MCP servers
Connect local and remote MCP servers with built-in tools, resources, OAuth, and exposure controls.
zot includes Model Context Protocol (MCP) support in its binary. No bridge installation or separate zot helper executable is required. Configured servers connect at CLI session startup, including interactive, print, stream, JSON, RPC, swarm, and continuous worker sessions.
Quick setup
Run /mcp in the interactive TUI and press a, or run /mcp add, to open the add wizard. Choose the user or project configuration file, local command or remote URL, a server name, optional bearer-token environment variable, and tool exposure. Review the entry, then save and connect.
For manual configuration, create $ZOT_HOME/mcp.json for personal servers or .zot/mcp.json in the session working directory for project servers. When ZOT_HOME is unset, zot uses its normal state directory.
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
},
"docs": {
"url": "https://example.com/mcp",
"headers": { "Authorization": "Bearer ${DOCS_TOKEN}" }
}
}
}The filesystem example requires Node.js and npx for that server, not for zot itself. Set DOCS_TOKEN before starting zot if you configure the example remote server. Run /mcp reload after editing configuration.
Configuration fields
A command selects stdio. A url selects streamable HTTP. transport, or its alias type, can explicitly select stdio, streamable-http, http (an alias for streamable HTTP), or legacy sse.
Servers connect in parallel. Startup waits for discovery, bounded by each server's connection timeout. Invalid entries and failed servers are reported independently without discarding healthy connections. Continuous workers also report MCP failures without refusing to start.
Server names accept ASCII letters, digits, underscores, and hyphens. Names that normalize to the same namespace, such as my-server and my_server, conflict. The later name in lexical order is skipped. Tool names become mcp__<server>__<tool>, replacing other characters with underscores. Names longer than 64 characters get a stable hash suffix. Ambiguous normalized tool names are disabled rather than routed to the wrong tool.
Project overrides and environment variables
A project entry with its own command, url, type, or transport replaces the user entry of the same name. Without those fields, it overrides only enabled, exposure, and toolExposure, retaining the user's connection and credentials. For example, {"enabled": false} disables a user server in one project.
${NAME} and ${NAME:-fallback} expand once in command, args, env values, cwd, url, headers values, and oauth.clientId, oauth.clientSecret, and oauth.authServerMetadataUrl. Fallbacks apply only to unset variables. Explicitly empty values remain empty. Missing required variables disable the affected server and report the variable name, not its value.
Bare $NAME, shell commands, and references to sibling env entries are not expanded. Configuration files are not rewritten during expansion.
Tool discovery and exposure
By default, the model sees mcp__search_tools. Searching by server name, tool name, or description loads matching deferred definitions for subsequent model calls. Search is local and never executes a remote tool. It loads up to 8 tools by default, with a maximum of 20.
toolExposure keys are exact remote tool names or patterns where * matches any characters. Exact names win, then the longest matching pattern, with lexical order breaking ties. A hidden server can expose only selected tools:
{
"mcpServers": {
"github": {
"url": "https://api.example.com/mcp",
"exposure": "hidden",
"toolExposure": { "issue_*": "deferred", "search_code": "direct" }
}
}
}The tools view in /mcp shows effective exposure. Tool calls use the normal agent execution path, including confirmation and extension interception. Cancellation and request deadlines propagate to MCP requests. Text, images, structured content, and MCP error results are preserved. Server annotations do not grant permissions or replay rights. Input schemas that do not describe an object are advertised with an empty object schema because providers reject other types.
Resources
Servers can offer resources such as files, documentation, or database schemas without implementing tools. When a connected, non-hidden server offers resources, zot registers:
mcp__list_resources: list resource metadata with its server and URI. Withserver, return one page and an optionalnextCursor. Without it, list all servers and pages, reporting individual failures without hiding healthy results.mcp__list_resource_templates: list URI templates for resources that servers do not list directly.mcp__read_resource: read content byserveranduri. Text arrives as text. PNG, JPEG, GIF, and WebP images up to 512 KiB arrive inline. Other binary content is saved to a private temporary file and its path is returned.
Text from tool results and resource reads is limited to 64 KiB per content block. Longer text is saved whole to a temporary file, with a preview and path returned to the model. This is a per-block limit, not a total response or memory limit. Temporary files are removed when the MCP manager closes.
Resource tools reach only connected servers that offer resources. Hidden servers remain unreachable. They use the widest exposure among eligible servers, direct when any is direct and deferred otherwise. Interactive app resources with ui:// URIs are left out because zot does not render them.
Sign in with OAuth
HTTP servers that require OAuth need no credentials in mcp.json. A server answering 401 appears as sign-in required in /mcp. Select it and press l. zot opens the browser, waits for a local callback, stores the token, and reconnects. The dialog also shows the authorization URL. Press esc to cancel.
Over SSH or with a browser on another machine, the final redirect to 127.0.0.1 cannot reach zot. Copy the address from the browser and paste it into the dialog's redirect field, then press enter. A bare authorization code or code#state also works. A URL from another sign-in attempt is refused. Text-mode /mcp login <server> reads pasted redirects from the controlling terminal, never standard input.
zot discovers authorization-server metadata, supports dynamic client registration, uses PKCE, and refreshes access tokens automatically. Authorization failures during tool calls, resource requests, or discovery withdraw capabilities and offer sign-in again, even if a rejected token is still stored.
For servers requiring a pre-registered client:
{
"mcpServers": {
"example": {
"url": "https://mcp.example.com/mcp",
"oauth": {
"clientId": "my-client",
"clientSecret": "${EXAMPLE_SECRET}",
"callbackPort": 8765,
"scopes": ["read"]
}
}
}
}callbackPort fixes the redirect URI to http://127.0.0.1:<port>/callback. Without it, zot selects a free port and registers a client for that URI. authServerMetadataUrl can override authorization-server discovery. A static Authorization header disables OAuth for that server.
Credentials and registered clients are stored in $ZOT_HOME/mcp-auth.json, keyed by server name and URL, with private file permissions where supported. Advertised resource-metadata URLs are checked against the resource origin before reuse. Concurrent requests within one runtime share token refreshes. Separate zot processes do not coordinate refreshes or logout.
Press o to sign out and delete stored credentials. Sign-out invalidates older connections and pending sign-ins within the runtime, so a stale refresh or authorization exchange cannot restore deleted credentials. It does not revoke tokens at the authorization server.
Manage servers
The interactive /mcp manager lists server state, tool count, exposure, and defining file. Failed and invalid servers come first. Diagnostics omit raw transport details that could reveal URLs, headers, or arguments.
Edits update the file defining the server, preserving unrelated fields and servers. Symlinked configurations are written through the link and existing file permissions are retained. Partial project overrides keep explicit enabled and deferred values when needed to override user settings.
Enabling, disabling, changing exposure, and signing in reconnect only the affected server. Adding or removing a server reloads MCP. The active turn is cancelled before reconnecting. The add wizard stores bearer tokens as environment-variable references, not pasted token values. In the wizard, esc steps back without losing input.
Commands and opt-out flags
Changing directory with /cd reconnects MCP for the new directory, applying its .zot/mcp.json and closing the previous project's servers.
Connection behavior
Dropped stdio or SSE connections reconnect on the next tool call. Closing stderr alone does not disconnect a stdio server. Ended streamable HTTP sessions are reinitialized and the unaccepted request is sent once more. Resource calls recover ended sessions the same way. After reconnecting, zot checks the current catalog before invoking a tool or reading resources.
Network errors and HTTP 408, 429, and 5xx responses get up to two retries for initialization and discovery, with bounded backoff and Retry-After support. Tool calls are not replayed after network failures because they might already have run. Concurrent calls share a reconnect, while a waiting caller can cancel independently.
Servers announcing tools.listChanged update the catalog without a reload. Streamable HTTP also needs an open event stream. Without usable notifications, zot refreshes lists before a new prompt when they are older than 30 seconds. Cancellation stops this pre-turn discovery. Withdrawn tools are no longer callable.
Connections close at shutdown or MCP reload. For stdio, zot closes stdin, waits up to two seconds, sends SIGTERM to the process group, waits again, then kills the process group. Windows kills only the direct server process after the first wait, without an interrupt signal. Descendant cleanup is not guaranteed on Windows.
Existing bridge installations
An enabled extension named mcp takes precedence over built-in support, avoiding duplicate connections. Remove it and restart zot, or run /reload-ext followed by /mcp reload, to use the built-in manager. Existing transport, command, environment, header, and request-timeout configuration works without conversion.
The optional bridge still provides setup templates described in Extensions. /mcp setup, /mcp start, /mcp stop, /mcp restart, and /mcp refresh are bridge-only commands. Built-in support discovers tools at startup and keeps connections open until shutdown or reload. It does not use the bridge's tool cache or idle reaper, and ignores idleTimeout.
Limitations
Jail mode does not confine an MCP server's filesystem or network access. MCP servers run with the permissions of their process, regardless of local tool guardrails. Per-tool confirmation remains separate from configuration loading and server startup.
MCP prompts, server-initiated sampling, and interactive app rendering are not supported. The Go SDK does not automatically load MCP configuration. Extension and RPC wire formats are unchanged.