Skip to content

Transports

dynmcp can connect to upstream MCPs using one of three transports. You pick which per entry in your config file via the transport field. The host-facing side is always stdio.

TransportFieldsUse case
stdiocommand, args?, env?Local MCPs launched as child processes.
streamable-httpurl, headers?Remote MCPs reachable over HTTP.
sseurl, headers?Remote MCPs reachable over Server-Sent Events.

Spawns the upstream MCP as a child process. This is the most common transport; most MCPs on npm ship a stdio binary.

{
"filesystem": {
"transport": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
}
}
FieldRequiredDescription
commandYesExecutable to spawn.
argsNoString array. Default: [].
envNoMap of env vars to set for the spawned process.
  • The process spawns at startup for eager MCPs, or at load_mcp time for lazy MCPs.
  • The child’s stderr is forwarded to dynmcp’s own stderr so you can see what it says.
  • On shutdown, the process is terminated as part of the standard teardown.

Connects to a remote MCP over HTTP. Use this for hosted MCPs that expose an HTTP endpoint.

{
"aws-knowledge": {
"transport": "streamable-http",
"url": "https://knowledge-mcp.global.api.aws"
}
}
FieldRequiredDescription
urlYesFull URL of the MCP endpoint. Must be a valid http:// or https:// URL.
headersNoMap of HTTP headers included on every request.
authNoPre-registered OAuth client credentials. See Auth.

There are two ways to authenticate to a streamable-http (or sse) upstream, depending on what the server supports:

1. Static bearer token via headers. Use when the server accepts a long-lived API key or PAT in the Authorization header. Combine with environment variable interpolation to keep secrets out of the config file:

{
"remote": {
"transport": "streamable-http",
"url": "https://api.example.com/mcp",
"headers": {
"Authorization": "Bearer ${API_TOKEN}"
}
}
}

2. OAuth 2.1 with PKCE. Use when the server returns a 401 challenge with OAuth metadata. dynmcp handles the full authorization-code flow via the dynmcp login subcommand and persists tokens to your OS keychain. No config change is required for the auto-detected default; supply an optional auth block to override Dynamic Client Registration with pre-registered credentials:

{
"github": {
"transport": "streamable-http",
"url": "https://api.githubcopilot.com/mcp",
"auth": {
"client_id": "${GITHUB_OAUTH_CLIENT_ID}",
"client_secret": "${GITHUB_OAUTH_CLIENT_SECRET}",
"scope": "repo"
}
}
}

See the OAuth Authentication guide for the walkthrough and the OAuth reference for keychain storage details and runtime behavior.

Connects to a remote MCP over Server-Sent Events. Similar shape to streamable-http, just for endpoints that use the SSE transport variant.

{
"remote-sse": {
"transport": "sse",
"url": "https://example.com/sse",
"headers": {
"Authorization": "Bearer ${API_TOKEN}"
}
}
}
FieldRequiredDescription
urlYesFull URL of the SSE endpoint. Must be a valid http:// or https:// URL.
headersNoMap of HTTP headers included on the connection request.
authNoPre-registered OAuth client credentials. Identical semantics to the streamable-http auth block.

You can mix transports freely in a single config. Each entry’s transport discriminator is independent of the others, so a stdio MCP next to a streamable-http MCP next to an sse MCP is fine.

Each transport’s fields are mutually exclusive. The Zod schema rejects mixing them at startup:

  • stdio entries can’t include url, headers, or auth.
  • streamable-http and sse entries can’t include command, args, or env.

A misconfiguration produces a startup error before any connection is attempted.