MCP
Languages: English · 中文
MCP (Model Context Protocol) exposes external tools to AI agents. Agently wires MCP servers into the action runtime via MCPActionExecutor so the model sees MCP tools and your own @agent.action_func actions through the same interface.
Use URL / Streamable HTTP MCP endpoints for service integrations, and stdio command configs for local development, desktop clients, or single-user local servers. SSE endpoints remain a legacy compatibility path.
Minimal example
import os
import asyncio
from dotenv import load_dotenv, find_dotenv
from agently import Agently
load_dotenv(find_dotenv())
Agently.set_settings("OpenAICompatible", {
"base_url": "${ENV.OPENAI_BASE_URL}",
"api_key": "${ENV.OPENAI_API_KEY}",
"model": "${ENV.OPENAI_MODEL}",
})
agent = Agently.create_agent()
async def main():
result = (
await agent.use_mcp(f"https://mcp.amap.com/mcp?key={os.environ.get('AMAP_API_KEY')}")
.input("What's the weather like in Shanghai today?")
.async_start()
)
print(result)
asyncio.run(main())use_mcp(url) registers all tools the MCP server exposes. The agent then plans tool calls against the union of {@agent.action_func, use_tool, use_mcp tools} as if they were one set.
API
| Method | Behavior |
|---|---|
await agent.use_mcp(url) | connect to the server, list tools, register them; returns the agent for chaining |
await agent.use_mcp(url, headers={...}) | with custom HTTP headers (auth tokens, etc.) |
await agent.use_mcp({"mcpServers": {...}}) | use an MCP config with one or more HTTP or stdio servers |
For the default executor, headers= with a URL is normalized to an MCP config before FastMCP sees it.
await agent.use_mcp(
"https://example.com/mcp",
headers={"Authorization": f"Bearer {token}"},
)For local stdio servers, pass MCP config directly:
await agent.use_mcp({
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "./workspace"],
}
}
})Mixing MCP with custom actions
@agent.action_func
async def lookup_internal(id: str):
"""Look up a record in the internal database."""
...
await agent.use_mcp("https://example-mcp/server")
agent.use_actions(lookup_internal)
# The model now sees MCP tools + lookup_internal in the same plan
result = await agent.input(question).async_start()There's no precedence between MCP-provided tools and locally-defined actions. The model picks based on names, descriptions, and the prompt context.
Inspecting what was called
For a request-scoped turn, pass the turn prompt into the action loop to inspect what tools the model actually invoked:
turn = agent.input("Use the MCP server to answer this question.")
records = agent.get_action_result(prompt=turn.prompt)
for r in records:
print(r)Action records are also written to extra.action_logs (or extra.tool_logs on the compat surface).
Common pitfalls
- Forgetting
await:use_mcp(...)is async because it lists tools from the server. Forgettingawaitreturns a coroutine and the registration silently doesn't happen. - Passing secrets in URLs: prefer headers and env vars. URL query params end up in logs.
- Treating MCP as identical to local actions: hosted MCP servers can be slow or rate-limited. For latency-sensitive or high-volume calls, prefer local action functions.
See also
- Action Runtime —
MCPActionExecutoris one of the bundled executors - Tools —
use_mcp(...)is the same on the compat surface