Skip to main content

MCP proxy server that reduces LLM context overhead with on-demand tool loading from multiple upstream servers.

Project description

dynamic-mcp

MCP proxy server that reduces LLM context overhead by grouping tools from multiple upstream MCP servers and loading tool schemas on-demand.

Instead of you exposing all MCP servers upfront (which can consume thousands of tokens), dynamic-mcp exposes only two MCP tools initially.

It maintains full functionality for upstream MCP servers and supports stdio, HTTP (and SSE) transports, handles OAuth, and automatically retries failed connections.

Quick Start

Installation

Option 1: Python package

It is available in PyPI.

Use uvx to install and run that package in your agent's MCP settings:

{
  "mcpServers": {
    "dynamic-mcp": {
      "command": "uvx",
      "args": ["dmcp", "/path/to/your/dynamic-mcp.json"]
    }
  }
}

You can also set DYNAMIC_MCP_CONFIG= environment variable and omit the path.

Option 2: Native binary

Download the binary for your operating system from the releases page and put it in your PATH:

{
  "mcpServers": {
    "dynamic-mcp": {
      "command": "dmcp"
    }
  }
}

Set DYNAMIC_MCP_CONFIG= environment variable and omit the args altogether.

Option 3: Compile from source

Install it from crates.io:

cargo install dynamic-mcp

The binary will be available at ~/.cargo/bin/dmcp.

Migrate from an existing MCP config

If you have an existing MCP config without descriptions, use migrate command.

Note: There is no standard MCP json format. Not all formats are supported.

Migrate from an existing mcp config to dynamic-mcp format:

uvx dmcp migrate mcp.json -o dynamic-mcp.json

The command will interactively prompt for descriptions for each server.

Example migration session:

🔄 Starting migration from standard MCP config to dynamic-mcp format
📖 Reading config from: mcp.json

✅ Found 2 MCP server(s) to migrate

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Server: filesystem
Type: stdio

Config details:
  command: "npx"
  args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]

💬 Enter description for 'filesystem' (what this server does): File operations on /tmp directory

[... prompts for other servers ...]

✅ Migration complete!
📝 Output saved to: dynamic-mcp.json

Note: The migrate command respects RUST_LOG for controlling verbosity (same as server mode).

Config File

Descriptions

Create a dynamic-mcp.json file with description field for each server:

{
  "mcpServers": {
    "filesystem": {
      "description": "Use when you need to read, write, or search files.",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
    }
  }
}

Environment Variables

It supports ${VAR} syntax for environment variable interpolation:

{
  "mcpServers": {
    "example": {
      "description": "Example with env vars",
      "command": "node",
      "args": ["${HOME}/.local/bin/server.js"],
      "env": {
        "API_KEY": "${MY_API_KEY}"
      }
    }
  }
}

Server Types

stdio (Default)

{
  "description": "Server description for LLM",
  "command": "npx",
  "args": ["-y", "package-name"],
  "env": {
    "KEY": "value"
  }
}

http

{
  "type": "http",
  "description": "HTTP server",
  "url": "https://api.example.com",
  "headers": {
    "Authorization": "Bearer ${TOKEN}"
  }
}

sse

{
  "type": "sse",
  "description": "SSE server",
  "url": "https://api.example.com/sse",
  "headers": {
    "Authorization": "Bearer ${TOKEN}"
  }
}

OAuth Authentication (HTTP/SSE)

{
  "type": "http",
  "description": "OAuth-protected MCP server",
  "url": "https://api.example.com/mcp",
  "oauth_client_id": "your-client-id",
  "oauth_scopes": ["read", "write"]
}

OAuth Flow:

  • On first connect, browser opens for authorization
  • Access token stored in ~/.dynamic-mcp/oauth-servers/<server-name>.json
  • Automatic token refresh before expiry (with RFC 6749 token rotation support)
  • Token injected as Authorization: Bearer <token> header

Troubleshooting

Server Connection Issues

Problem: ❌ Failed to connect to <server>

Solutions:

  • Automatic retry: System retries up to 3 times with exponential backoff (2s, 4s, 8s)
  • Periodic retry: Failed servers are retried every 30 seconds in the background
  • Stdio servers: Verify command exists (which <command>)
  • HTTP/SSE servers: Check server is running and URL is correct
  • Environment variables: Ensure all ${VAR} references are defined
  • OAuth servers: Complete OAuth flow when prompted

Logging:

By default, errors and warnings are logged to terminal. For more verbose output:

# Debug mode (all logs including debug-level details)
RUST_LOG=debug uvx dmcp config.json

# Info mode (includes informational messages)
RUST_LOG=info uvx dmcp config.json

# Default mode (errors and warnings only, no RUST_LOG needed)
uvx dmcp config.json

OAuth Authentication Problems

Problem: Browser doesn't open for OAuth

Solutions:

  • Manually open the URL shown in console
  • Check firewall allows localhost connections
  • Verify oauth_client_id is correct for the server

Problem: Token refresh fails

Solutions:

  • Delete cached token: rm ~/.dynamic-mcp/oauth-servers/<server-name>.json
  • Re-authenticate on next connection

Environment Variable Not Substituted

Problem: Config shows ${VAR} instead of value

Solutions:

  • Use ${VAR} syntax, not $VAR
  • Export variable: export VAR=value
  • Variable names are case-sensitive
  • Check for typos in variable name

Configuration Errors

Problem: Invalid JSON in config file

Solutions:

  • Validate JSON syntax (use jq . config.json)
  • Check for trailing commas
  • Ensure all required fields present (description; type required only for http/sse, optional for stdio)

Problem: Failed to resolve config path

Solutions:

  • Use absolute path or path relative to working directory
  • Check file exists and has read permissions
  • Try: ls -la <config-path>

Tool Call Failures

Problem: Tool call returns error

Debugging:

  1. Test tool directly with upstream server
  2. Check tool name and arguments match schema
  3. Verify group name is correct
  4. Enable debug logging to see JSON-RPC messages

Performance Issues

Problem: Slow startup

Solutions:

  • Parallel connections already enabled
  • Check network latency for HTTP/SSE servers
  • Some servers may be slow to initialize (normal)

Problem: High memory usage

Solutions:

  • Tools are cached in memory (expected)
  • Failed groups use minimal memory
  • Large tool schemas contribute to memory usage

Building from source

Rust Binary

To build the Rust binary directly:

git clone https://github.com/asyrjasalo/dynamic-mcp.git
cd dynamic-mcp
cargo build --release

The binary will be available at ./target/release/dmcp.

Python Package

To build the Python package (wheel):

# Install maturin
pip install maturin

# Build wheel
maturin build --release

# Install locally
pip install target/wheels/dmcp-*.whl

The Python package uses maturin with bindings = "bin" to compile the Rust binary directly into the wheel.

For more details on development setup, testing, and contributing, see CONTRIBUTING.md.

Release History

See CHANGELOG.md for version history and release notes.

Acknowledgments

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

dmcp-1.1.0-py3-none-win_arm64.whl (3.6 MB view details)

Uploaded Python 3Windows ARM64

dmcp-1.1.0-py3-none-win_amd64.whl (3.8 MB view details)

Uploaded Python 3Windows x86-64

dmcp-1.1.0-py3-none-manylinux_2_24_x86_64.whl (3.7 MB view details)

Uploaded Python 3manylinux: glibc 2.24+ x86-64

dmcp-1.1.0-py3-none-manylinux_2_24_aarch64.whl (3.5 MB view details)

Uploaded Python 3manylinux: glibc 2.24+ ARM64

dmcp-1.1.0-py3-none-macosx_11_0_arm64.whl (3.3 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

File details

Details for the file dmcp-1.1.0-py3-none-win_arm64.whl.

File metadata

  • Download URL: dmcp-1.1.0-py3-none-win_arm64.whl
  • Upload date:
  • Size: 3.6 MB
  • Tags: Python 3, Windows ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dmcp-1.1.0-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 b384ccfe6553648eaf9612bd291a429ea16550ecff5ed77fc0c02ae40e014239
MD5 90a4f719388824270640cdba7f31c146
BLAKE2b-256 3d092b05bb09a6b834117370a01ffb7eedabcbe4311c3ec769f227295a7ae879

See more details on using hashes here.

Provenance

The following attestation bundles were made for dmcp-1.1.0-py3-none-win_arm64.whl:

Publisher: release.yml on asyrjasalo/dynamic-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dmcp-1.1.0-py3-none-win_amd64.whl.

File metadata

  • Download URL: dmcp-1.1.0-py3-none-win_amd64.whl
  • Upload date:
  • Size: 3.8 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dmcp-1.1.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 3d409e95664da087daf289c1d4a83c13a55f2df97a94f03f345ce9749273cd2e
MD5 a4c14c1954b9074d99068d09b0114b67
BLAKE2b-256 1b86af85646bd2639acc5f4c6306c378049ee977ba3087fcd4d797e6175f896a

See more details on using hashes here.

Provenance

The following attestation bundles were made for dmcp-1.1.0-py3-none-win_amd64.whl:

Publisher: release.yml on asyrjasalo/dynamic-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dmcp-1.1.0-py3-none-manylinux_2_24_x86_64.whl.

File metadata

  • Download URL: dmcp-1.1.0-py3-none-manylinux_2_24_x86_64.whl
  • Upload date:
  • Size: 3.7 MB
  • Tags: Python 3, manylinux: glibc 2.24+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dmcp-1.1.0-py3-none-manylinux_2_24_x86_64.whl
Algorithm Hash digest
SHA256 2192453b70da81e45fc2a41378a3bdcdcb7b0135ecb3de5ac2d9a3348877ddd0
MD5 a21a5e9415243673690fdca7db0f4b6b
BLAKE2b-256 9f8e6115b76a6f63468b7f1baa4ec687eb2a82b67d9f6dcc66c4402fb0ea2a99

See more details on using hashes here.

Provenance

The following attestation bundles were made for dmcp-1.1.0-py3-none-manylinux_2_24_x86_64.whl:

Publisher: release.yml on asyrjasalo/dynamic-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dmcp-1.1.0-py3-none-manylinux_2_24_aarch64.whl.

File metadata

File hashes

Hashes for dmcp-1.1.0-py3-none-manylinux_2_24_aarch64.whl
Algorithm Hash digest
SHA256 32bda958ec018e0c0a6e94808d3a6138c09f40b47d29a41a900dac61c373ee64
MD5 d86faf8a35213294f1b8040f88ba71a2
BLAKE2b-256 9ea11d430ed0a61c0d0f802d22bab7e0fef520db98d39ead459e8faaa8d6f636

See more details on using hashes here.

Provenance

The following attestation bundles were made for dmcp-1.1.0-py3-none-manylinux_2_24_aarch64.whl:

Publisher: release.yml on asyrjasalo/dynamic-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dmcp-1.1.0-py3-none-macosx_11_0_arm64.whl.

File metadata

  • Download URL: dmcp-1.1.0-py3-none-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 3.3 MB
  • Tags: Python 3, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dmcp-1.1.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0136622ae4dbbc99838629e44e3edcc91b7df319381af1500fdfd44efad44d34
MD5 0b3228024701c6facee9aa8168718c7d
BLAKE2b-256 dfda0cdd3905a02c1d3bd22d5f645adae1c7b550153692472dc92beee5197c9e

See more details on using hashes here.

Provenance

The following attestation bundles were made for dmcp-1.1.0-py3-none-macosx_11_0_arm64.whl:

Publisher: release.yml on asyrjasalo/dynamic-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page