-
-
Notifications
You must be signed in to change notification settings - Fork 2
MCP Servers
MCP servers are essentially APIs designed specifically for LLMs that work like a remote repository of tools for the model to access and extend its capabilities.
So think of it like this: Instead of having to write all your own custom tools to interact with different services, those services can expose their functionality through an MCP server.
Coyote has first-class support for MCP servers.
As mentioned in the Coyote Vault documentation, Coyote can inject sensitive configuration data into your MCP configuration file to ensure that secrets are not hard-coded.
Be careful how many MCP servers you enable at one time, regardless of the context. When there is a significant number of configured MCP servers, enabling too many MCP servers may overwhelm the context length of a model, and quickly exceed token limits.
Coyote stores the MCP server configuration file, functions/mcp.json, in the functions directory. You can find
this directory using the following command:
coyote --info | grep functions_dir | awk '{print $2}'The syntax for the functions/mcp.json file matches Claude Code's .mcp.json configuration format,
with one small difference: the type field is always required in Coyote, even for stdio servers (Claude Code
allows it to be omitted and infers stdio from the presence of a command). So any time you're looking to add
a new server, look at its docs and find the Claude Code configuration example. You should be able to use the
exact same configuration in your functions/mcp.json file. Just make sure every entry has an explicit type.
Note: Coyote does not support Claude Code's "streamable-http" alias (use "http" instead) or envFile.
For secrets, use Coyote Vault interpolation rather than Claude Code's ${VAR} shell-style expansion.
OAuth-protected remote servers are supported natively (see OAuth Authentication below).
Every server entry must include a "type" field set to one of: "stdio", "http", or "sse".
Running inside a Docker Sandbox? MCP servers often need extra network allowances beyond what the base kit provides. See Sandbox Compatibility at the bottom of this page for details and common gotchas.
Coyote supports three MCP transport types:
| Type | Use Case |
|---|---|
stdio |
Spawns a local subprocess and communicates over stdin/stdout |
http |
Connects to a remote server via Streamable HTTP |
sse |
Connects to a remote server via the legacy HTTP+SSE transport (deprecated in the MCP spec; prefer http where the server supports it) |
Stdio is the standard transport for locally-installed MCP servers. Coyote spawns the process and communicates over stdin/stdout:
{
"mcpServers": {
"github": {
"type": "stdio",
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/github/github-mcp-server"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "YOUR_GITHUB_TOKEN"
}
}
}
}| Field | Required | Description |
|---|---|---|
type |
yes | Must be "stdio"
|
command |
yes | The executable to spawn |
args |
no | Arguments passed to the command |
env |
no | Environment variables for the subprocess |
cwd |
no | Working directory for the subprocess |
For remote MCP servers that support the Streamable HTTP transport:
{
"mcpServers": {
"datadog": {
"type": "http",
"url": "https://mcp.datadoghq.com/api/unstable/mcp-server/mcp"
}
}
}| Field | Required | Description |
|---|---|---|
type |
yes | Must be "http"
|
url |
yes | The server endpoint URL |
headers |
no | Custom HTTP headers to include with every request |
oauth_client_id |
no | OAuth client ID. Omit to use Dynamic Client Registration (auto-registers on first .mcp auth) |
For remote MCP servers that use the legacy HTTP+SSE transport (deprecated in the MCP spec;
prefer http where the server supports it):
{
"mcpServers": {
"my-sse-server": {
"type": "sse",
"url": "http://127.0.0.1:64342/sse",
"headers": {
"Authorization": "Bearer my-token"
}
}
}
}| Field | Required | Description |
|---|---|---|
type |
yes | Must be "sse"
|
url |
yes | The server SSE endpoint URL |
headers |
no | Custom HTTP headers to include with every request |
oauth_client_id |
no | OAuth client ID. Omit to use Dynamic Client Registration (auto-registers on first .mcp auth) |
Note: Both http and sse types use the same underlying transport, which auto-negotiates the
protocol with the server. The type field primarily serves as documentation of which protocol the
server speaks. Neither type supports command, args, or cwd fields.
Some remote MCP servers require OAuth 2.0 authentication (e.g. Notion, Jira). Coyote supports these natively, meaning no manual token management is required.
{
"mcpServers": {
"notion": {
"type": "http",
"url": "https://mcp.notion.com/mcp"
}
}
}That's it. Then in the REPL:
.mcp auth notion
Or from the command line:
coyote --auth-mcp notionYour browser opens, you log into Notion, and the token is saved. On subsequent startups Coyote injects the token automatically. When the token expires it is refreshed silently.
-
Discovery: Coyote fetches
/.well-known/oauth-protected-resourceand/.well-known/oauth-authorization-serverto find the server's authorization and token endpoints (per RFC 9728 / RFC 8414). No manual endpoint configuration needed. -
Dynamic Client Registration (RFC 7591): If the server supports it (the Notion MCP server used in this example does), Coyote
registers itself automatically and caches the client ID in
~/.cache/coyote/oauth/mcp_<name>_registration.json. Theoauth_client_idfield is only needed when DCR is unavailable. -
PKCE authorization code flow: A localhost callback server is bound on an ephemeral port. Your browser opens for login.
The token is exchanged and stored in
<cache_dir>/oauth/mcp_<name>_oauth_tokens.json. -
Token injection: On every connection to the server, Coyote loads the stored token (refreshing if expired) and injects
it as an
Authorization: Bearerheader. No changes tomcp.jsonrequired.
If Coyote tries to connect to an OAuth-protected server at startup and gets an auth challenge, it warns and skips the server rather than failing:
warn: MCP server 'notion' requires authentication. Run `.mcp auth notion` to authenticate.
Run .mcp auth notion (or coyote --auth-mcp notion) once to complete the flow, then restart Coyote.
If your organization pre-registers a client with the server (this is rare, as DCR handles this automatically for most servers):
{
"mcpServers": {
"my-server": {
"type": "http",
"url": "https://mcp.example.com/mcp",
"oauth_client_id": "your-registered-client-id"
}
}
}When oauth_client_id is set, DCR is skipped and the provided ID is used directly.
If the server issues long-lived tokens (e.g. Notion internal integrations), you can skip the OAuth flow
entirely and use a static Authorization header:
{
"mcpServers": {
"notion": {
"type": "http",
"url": "https://mcp.notion.com/mcp",
"headers": {
"Authorization": "Bearer <your-notion-integration-token>"
}
}
}
}Use Coyote Vault to avoid storing the token in plaintext:
{
"headers": {
"Authorization": "Bearer {{notion_token}}"
}
}As mentioned in the Coyote Vault documentation, you can use Coyote Vault to inject secrets into your MCP configuration file.
In fact, this is why you need to set up your vault before using Coyote at all: the built-in MCP configuration requires you set up some secrets to use it.
For more information about how to set up your vault and inject secrets, please refer to the Coyote Vault documentation.
Coyote ships with a functions/mcp.json file that includes some useful MCP servers:
- atlassian - Interact with and manage Atlassian tools like Confluence and Jira.
- github - Interact with GitHub repositories, issues, pull requests, and more.
- docker - Manage your local Docker containers with natural language
- slack - Interact with Slack
- ddg-search - Perform web searches with the DuckDuckGo search engine
The mcp.json file is created from a bundled template on first run and is never overwritten afterward. It is your own
configuration to edit freely. To discard your changes and restore the bundled template (for example, to pick up new
default servers after a Coyote update), run coyote --install mcp_config (or .install mcp_config in the REPL). This is
destructive: it replaces your entire mcp.json, including your configured servers and any secret references in them,
with the bundled template.
MCP servers, like tools, can be used in a handful of contexts:
- Inside a session
- Inside a role
- Inside an agent
- Globally (i.e. outside a session, role, or agent)
Each of these has a different configuration and interaction with the global configuration.
*Note: The names of each MCP server referenced in the below configuration properties directly corresponds
to the names given in the functions/mcp.json configuration file. So if you change the name of an MCP server
from slack to lucem-slack, then you need to also update your Coyote configuration accordingly.
The global configuration is essentially what settings you want to have on by default when
you just invoke coyote. (Don't worry about agents, roles, or sessions yet. We'll get to them in a bit).
The following settings are available in the global configuration for MCP servers:
mcp_server_support: true # Enables or disables MCP server support (globally).
mapping_mcp_servers: # Alias for an MCP server or set of servers
git: github,gitmcp
enabled_mcp_servers: null # Which MCP servers to enable by default.
# Accepts either a YAML list or a comma-separated string. Examples:
# enabled_mcp_servers: github,slack
# enabled_mcp_servers:
# - github
# - slackA special note about enabled_mcp_servers: a user can set this to all (or include all in the list) to enable all
configured MCP servers in the functions/mcp.json configuration.
(See the Configuration Example file for an example global configuration with all options.)
When running in REPL-mode, the mcp_server_support and enabled_mcp_servers settings can be overridden using the
.set command:

When you create a role, you have the following MCP-related configuration options available to you:
enabled_mcp_servers: # Which MCP servers the role uses. Accepts either a YAML list (as shown)
- github # or a comma-separated string (e.g. `enabled_mcp_servers: github,slack`).The values for mapping_mcp_servers are inherited from the [global configuration](#global-configuration).
For more information about roles, refer to the Roles documentation.
When you create an agent, you have the following MCP-related configuration options available to you:
mcp_servers: # Which MCP servers the agent uses
- github
- dockerThe values for mapping_mcp_servers are inherited from the global configuration.
For more information about agents, refer to the Agents documentation.
For a full example configuration for an agent, see the Agent Configuration Example file.
If you run Coyote inside a Sandbox, MCP servers often need extra setup beyond what works on your host because the sandbox's network proxy denies all unlisted domains.
The bundled assets/functions/sbx-mixin.yaml (installed via coyote --install functions) already allowlists the
defaults shipped in the built-in mcp.json: github MCP (api.githubcopilot.com), atlassian MCP (mcp.atlassian.com),
ddg-search MCP (duckduckgo.com + subdomains), plus the npm registry and Docker registries that npx/uvx-based MCP
servers pull from.
For your own MCP servers, add the relevant domains to a user-level sbx-mixin.yaml. Brief example:
# ~/.config/coyote/sbx-mixin.yaml
schemaVersion: "1"
kind: mixin
name: my-mcp-domains
network:
allowedDomains:
- "api.example-mcp.com:443"
- "auth.example-mcp.com:443"See Sandboxes: Extending the Sandbox for the full discovery path table.
-
Containerized MCP servers (
docker run -i ...) require their image registry to be inallowedDomains. The sandbox's nested Docker daemon needs to pull the image. Declareghcr.io,registry-1.docker.io, andauth.docker.io(or whichever registry you pull from) explicitly. The built-infunctions/sbx-mixin.yamlalready covers these for the defaults. -
OAuth-based remote MCP servers (e.g. Atlassian via
mcp-remote) need the OAuth host's domain too. Failures here often look like indefinite hangs at "Authorizing..." with no useful error. -
Host file mounts do not survive into the sandbox. Only the project workspace is mounted. If your MCP server
reads secrets from a file on the host (e.g.
~/.config/<service>/credentials), copy it in viasbx cpbefore starting Coyote, or inject the secret as an env var via the Vault instead. -
The
npx/uvxMCP server install pulls from public registries on first run. That can take 30+ seconds inside a fresh sandbox while it downloads and compiles. This is normal. Subsequent attaches reuse the cached install.