Skip to content

MCP integration

Talon connects external Model Context Protocol servers to personas. Each persona gets its own set of MCP servers. A server bound to one persona is invisible to another.

Terminal window
npx talonctl add-mcp --persona assistant --name brave-search --command "npx @modelcontextprotocol/server-brave-search"

This adds the server to the persona’s config in talond.yaml:

personas:
- name: assistant
mcp:
- name: brave-search
command: npx @modelcontextprotocol/server-brave-search
env:
BRAVE_API_KEY: ${BRAVE_API_KEY}

The server starts when the persona’s agent runner initialises and stops when it shuts down.

Talon supports three MCP transport types:

TransportDescription
stdioSpawn a child process, communicate over stdin/stdout (default)
sseConnect to a remote server via Server-Sent Events
httpConnect to a remote server via HTTP
mcp:
- name: my-server
command: node /path/to/server/index.js
args:
- --verbose
env:
API_KEY: ${MY_SERVER_API_KEY}

For remote MCP servers, use url instead of command. Custom headers support environment variable substitution:

mcp:
- name: remote-server
transport: sse
url: https://mcp.example.com/sse
headers:
Authorization: Bearer ${MCP_API_KEY}
X-Custom-Header: my-value
Config fieldRequiredDescription
nameYesIdentifier used in logs and capability labels
commandYes (stdio)Command to spawn the server process
argsNoAdditional arguments (stdio only)
transportNostdio, sse, or http (default: stdio)
urlYes (sse/http)Remote server URL
headersNoCustom HTTP headers — values support ${ENV_VAR} substitution
envNoEnvironment variables passed to the server process (stdio only)
authNoDynamic auth config — resolved by the daemon before reaching providers (see below)

Remote MCP servers that require authentication can use Talon-managed OAuth 2.0. The daemon handles token acquisition, storage, and refresh — providers never see the auth field.

Add an auth block with kind: 'oauth2' and a tokenStore identifier:

mcp:
- name: glean
transport: http
url: https://contentful-be.glean.com/mcp/default
auth:
kind: oauth2
tokenStore: glean/default
FieldDescription
kind'oauth2' (future schemes like basic or mtls may be added)
tokenStoreFilesystem-safe identifier for the cached token bundle. Resolves to <dataDir>/mcp-auth/<tokenStore>.json

The resolveMcpServers() step runs once per agent run, just before the server list reaches the provider. It materializes the access token into an Authorization: Bearer <token> header, merges it with any caller-set headers, and strips the auth field entirely.

Run the one-time OAuth dance:

Terminal window
# Interactive — opens your local browser
npx talonctl auth-mcp <skill>:<server>
# Headless — prints URL and callback instructions (useful over SSH)
npx talonctl auth-mcp <skill>:<server> --headless

The CLI performs RFC 9728 discovery → RFC 7591 dynamic client registration → PKCE authorization code flow → token exchange. The callback listener binds 127.0.0.1.

  • Tokens persist at <dataDir>/mcp-auth/<skill>/<server>.json with mode 0600
  • Atomic temp+rename writes prevent corruption
  • Access tokens are refreshed within 60 seconds of expiry using the cached refresh_token
  • Concurrent refresh attempts for the same token store are coalesced — the refresh token is never burned by parallel exchanges
  • Token files are owner-read/write only (0600)
  • The auth field is resolved and stripped inside the daemon before reaching any provider CLI
  • Unsupported auth.kind values are rejected at startup with a clear error

Each persona declares its own mcp list. The servers are loaded during persona initialization and are only available to that persona’s agents.

personas:
- name: researcher
mcp:
- name: brave-search
command: npx @modelcontextprotocol/server-brave-search
- name: coder
mcp:
- name: github
command: npx @modelcontextprotocol/server-github

researcher sees brave-search tools. coder sees github tools. Neither sees the other’s servers.

Each MCP server has a token-bucket rate limiter. The daemon checks the bucket before forwarding any tool call. If the bucket is empty, the call is held until tokens are available.

mcp:
- name: brave-search
command: npx @modelcontextprotocol/server-brave-search
rateLimit:
requestsPerMinute: 60

This prevents runaway agents from burning through API quotas on expensive external services.

Talon uses the __talond_ prefix for its internal MCP servers:

ServerPurpose
__talond_host_toolsThe 9 built-in host tools (schedule, memory, channel, persona_send, persona_list, etc.)
__talond_skill_loaderLazy skill loading via skill_load

User-defined servers with the __talond_ prefix are rejected at startup. Don’t use it.

MCP tool calls pass through the same capability filter as host tools. The persona must have a matching capability grant before an MCP tool is forwarded to the external server.

Skills can declare MCP server definitions in skills/<name>/mcp/*.json. When a skill is loaded, its servers are automatically bound to the persona.