MCP tools
Connect tools from a remote MCP server, with the same tenant checks, validation, and limits as HTTP tools.
The Model Context Protocol (MCP) is an open protocol for serving tools to AI applications. Brain connects to remote MCP servers over Streamable HTTP with the official MCP Go SDK (pinned in go.mod), so you can give sessions tools from an MCP server you already run. MCP tools go through the same tenant allowlist, input validation, timeout, and output limits as HTTP tools.
What Brain supports
- Transport: Remote MCP servers over Streamable HTTP, or installed stdio commands through the Go application SDK.
- Authentication: API keys sent as bearer tokens from the environment variable named in
token_env. - Results: Tool calls that return text, images, or structured content.
- Progress: Progress notifications streamed during a call as
tool.progressevents.
Discover tools
Run cmd/mcp-tools to turn tools from an MCP server into Brain tool bindings:
go run ./cmd/mcp-tools -url https://YOUR_SERVER/mcp -token-env MCP_API_KEY -tenant local -tools search,fetch| Flag | Description |
|---|---|
-url | The server's Streamable HTTP endpoint. |
-token-env | The environment variable that holds the server's API key. The command uses it to connect, and the bindings tell Brain to send it as a bearer token. |
-tenant | The tenant allowed to use the tools. |
-tools | A comma-separated list of 1 to 128 remote tool names to include. |
The command connects to the server, reads its tool list, and prints a JSON array with one binding for each named tool. It fails if a named tool doesn't exist or if discovery takes longer than 30 seconds. The output looks like this:
[
{
"name": "search",
"description": "Search the knowledge base.",
"input_schema": { "type": "object", "properties": { "query": { "type": "string" } }, "required": ["query"] },
"url": "https://YOUR_SERVER/mcp",
"mcp_tool": "search",
"token_env": "MCP_API_KEY",
"tenants": ["local"],
"timeout_seconds": 300,
"max_output_bytes": 1048576
}
]Review the descriptions and schemas, then copy the bindings into the tools array of your host config. Bindings are static: a tool's description and schema change only when you edit the config and restart Brain, and discovery never changes the tools of a running session.
Configure an MCP binding
An MCP binding is a regular tool binding with one extra field, mcp_tool:
nameis the name the model sees. Rename tools to avoid collisions, for exampledocs_searchfor asearchtool.mcp_toolis the tool's name on the MCP server.urlis the server's MCP endpoint.token_envnames the environment variable with the server's API key. Brain sends it as a bearer token on every request to the server.
tenants, timeout_seconds, max_output_bytes, and output_schema work as they do for HTTP tools. inspect_session applies to HTTP and SSE tools; Brain rejects a host config that sets it on an MCP binding.
How Brain calls an MCP tool
- Check the call. Brain confirms that the tool is allowed for the session's tenant and validates the arguments against
input_schema. - Open an MCP session. Brain starts a new MCP session with the server for this call, over pooled HTTP connections. A separate MCP session per call keeps server-side session state from crossing tenants or calls, at the cost of one initialization round trip per call.
- Call the tool. Brain sends the arguments and puts the session's identity in
_meta["brain.dev/scope"]astenant_id,session_id, andcall_id. The call ID is also the progress token. - Stream progress. Each progress notification from the server becomes a live-only
tool.progressevent. - Return the result. Brain converts the result into a
tool.resultevent, returns it to the harness, and closes the MCP session.
Scope metadata is for attribution
The _meta["brain.dev/scope"] values tell your server which tenant and session a call belongs to. Your server must still authenticate Brain's API key and enforce access to its own resources. Brain never sends session inspection tokens to MCP servers.
Results and errors
Brain converts MCP results into Brain content blocks:
| MCP result | Brain result |
|---|---|
| Text content | A text block. |
| Image content | An image block with base64 data and mime_type. |
| Structured content | structured_content plus a text block containing the JSON. Successful output schemas validate the structured value. |
isError: true | is_error: true. The model sees the error as a tool result. |
| Any other content type | The call fails. |
When a call fails, the tool.result has is_error: true:
| What happened | unknown_outcome |
|---|---|
| Brain refused the call or couldn't connect to the server, so the tool was never called | No |
| The call failed after Brain sent it: the connection dropped, the call timed out, or the server returned a protocol error | Yes |
| The server asked for user input | Yes. Handle approval inside the tool instead. |
The result had an unsupported content type, exceeded max_output_bytes, or didn't match output_schema | Yes |
Brain sends each MCP call at most once. With an unknown outcome, the model can use another tool to check the resource. Interrupting the session or reaching the timeout cancels the request; whether the work stops on the server depends on the server.