Skip to content

MCP Servers

Alex Clarke edited this page Jul 2, 2026 · 8 revisions

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.


Important Note

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.

MCP Server Configuration

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.

Transport Types

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 Servers

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

HTTP (Streamable HTTP) Servers

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)

SSE Servers

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.

OAuth Authentication

Some remote MCP servers require OAuth 2.0 authentication (e.g. Notion, Jira). Coyote supports these natively, meaning no manual token management is required.

Example: Notion

{
  "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 notion

Your 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.

How it works

  1. Discovery: Coyote fetches /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server to find the server's authorization and token endpoints (per RFC 9728 / RFC 8414). No manual endpoint configuration needed.
  2. 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. The oauth_client_id field is only needed when DCR is unavailable.
  3. 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.
  4. Token injection: On every connection to the server, Coyote loads the stored token (refreshing if expired) and injects it as an Authorization: Bearer header. No changes to mcp.json required.

If authentication hasn't been run yet

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.

Using a pre-existing client ID

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.

Using a static token instead of OAuth

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}}"
  }
}

Secret Injection

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.

Default MCP Servers

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.

Coyote Configuration

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.

Global Configuration

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
                                 #     - slack

A 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:

REPL set MCP servers

Role Configuration

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.

Agent Configuration

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
  - docker

The 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.

Sandbox Compatibility

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.

Sandbox caveats

  • Containerized MCP servers (docker run -i ...) require their image registry to be in allowedDomains. The sandbox's nested Docker daemon needs to pull the image. Declare ghcr.io, registry-1.docker.io, and auth.docker.io (or whichever registry you pull from) explicitly. The built-in functions/sbx-mixin.yaml already 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 via sbx cp before starting Coyote, or inject the secret as an env var via the Vault instead.
  • The npx/uvx MCP 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.

Clone this wiki locally