Skip to main content
An agent on AgentOS can use any MCP server’s tools. The agent runs the MCP client itself, the model is called through the AgentOS gateway, and each tool the model picks is called through MCP. So every model call is checked by the environment’s guardrails and recorded on the run, a remote MCP server is reachable only if the agent’s network rules allow it, and a risky tool can wait for a person’s approval first.
Tested with the MCP Python SDK (mcp) 2.3.0 and Python 3.12, using the conformance kit: 24 checks passed, including approve, edit and reject of a tool call. The remote part was also run against the real DeepWiki server from a machine with internet access.

The bundle

The example has two capabilities:
  • support uses a small MCP server bundled in the container. The agent starts it as a subprocess and talks to it over stdio. Its cancel_order tool waits for a person.
  • ask_docs uses a remote MCP server over Streamable HTTP: DeepWiki, which answers questions about public GitHub repositories. Its host is in network.allow.
The SDK wheel goes into vendor/, as described in SDKs.

How it works

1

List the MCP tools

The agent connects to the MCP server and turns each tool’s input_schema into an OpenAI function tool.
2

Call the model through the gateway

inv.llm.achat(messages, tools=…) sends the conversation and the tools to the model in config.models.main, with the run token.
3

Run the tools the model picks

Each tool call goes to the MCP server and its result goes back to the model, until the model answers in text.

Approve a tool call before it runs

Tools in NEEDS_APPROVAL don’t run until a person decides. When the model asks for one, the agent saves the conversation and the tool calls not run yet in run state, then asks with the tool’s arguments as the item’s data:
  • approve runs the tool with the model’s arguments,
  • edit runs it with the arguments the reviewer changed,
  • reject doesn’t run it. The model is told that a person declined it and answers accordingly.
The resume loads the saved conversation, runs the tool (or not), and continues the loop. Nothing before the decision runs again, so no tool runs twice.
Use mcp_client() from the example, not a bare Client. The MCP client’s async with block wraps anything raised inside it in an ExceptionGroup: the SDK’s ask(), an agentos.Fail or a guardrail block. The SDK can’t read the group, so the agent closes the connection without answering and the run fails with agent_interrupted, even after a successful ask. mcp_client() in the example unwraps it.

Remote MCP servers and network access

Agents reach nothing outside the platform by default. To use a remote MCP server, add its host to network.allow in agentos.yaml, or ask an admin to allow it on the agent’s Guardrails tab (see Network access). The MCP client honours the egress proxy settings the container gets, so no code is needed for that. When the host isn’t allowed or the server is down, the connection fails and the run fails with the connection error. If an MCP server needs a token, keep it as a secret, not in params, and send it in the MCP client’s HTTP headers. In the Python SDK that means an httpx2.AsyncClient with the header, passed to the Streamable HTTP transport as http_client.
Run MCP tools in the agent, as here, not as provider-hosted tools (Anthropic mcp_servers, the Responses API’s mcp tool). When a guardrail blocks or masks at tool_call or tool_result, the gateway refuses requests that ask the provider to call tools itself, because it couldn’t check the call or its result. See Guardrails.

Run it

Testing it with the conformance kit

The kit’s mock model calls a tool when the message says call:<tool> {arguments}, so a conformance.yaml can drive the tool loop and the approval without a real model. The kit’s container has no internet, so the remote capability is expected to fail there:
conformance.yaml

Next steps

Network access

Which hosts an agent may reach, and how to allow more.

Framework examples

The same patterns in other frameworks.