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_idis 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;typerequired 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:
- Test tool directly with upstream server
- Check tool name and arguments match schema
- Verify group name is correct
- 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
- TypeScript implementation: modular-mcp
- MCP Specification: Model Context Protocol
- Rust MCP Ecosystem: rust-mcp-stack
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
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b384ccfe6553648eaf9612bd291a429ea16550ecff5ed77fc0c02ae40e014239
|
|
| MD5 |
90a4f719388824270640cdba7f31c146
|
|
| BLAKE2b-256 |
3d092b05bb09a6b834117370a01ffb7eedabcbe4311c3ec769f227295a7ae879
|
Provenance
The following attestation bundles were made for dmcp-1.1.0-py3-none-win_arm64.whl:
Publisher:
release.yml on asyrjasalo/dynamic-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dmcp-1.1.0-py3-none-win_arm64.whl -
Subject digest:
b384ccfe6553648eaf9612bd291a429ea16550ecff5ed77fc0c02ae40e014239 - Sigstore transparency entry: 800425593
- Sigstore integration time:
-
Permalink:
asyrjasalo/dynamic-mcp@1fe21de5c82bcd01c05498323768d439d5f59e86 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/asyrjasalo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1fe21de5c82bcd01c05498323768d439d5f59e86 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3d409e95664da087daf289c1d4a83c13a55f2df97a94f03f345ce9749273cd2e
|
|
| MD5 |
a4c14c1954b9074d99068d09b0114b67
|
|
| BLAKE2b-256 |
1b86af85646bd2639acc5f4c6306c378049ee977ba3087fcd4d797e6175f896a
|
Provenance
The following attestation bundles were made for dmcp-1.1.0-py3-none-win_amd64.whl:
Publisher:
release.yml on asyrjasalo/dynamic-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dmcp-1.1.0-py3-none-win_amd64.whl -
Subject digest:
3d409e95664da087daf289c1d4a83c13a55f2df97a94f03f345ce9749273cd2e - Sigstore transparency entry: 800425563
- Sigstore integration time:
-
Permalink:
asyrjasalo/dynamic-mcp@1fe21de5c82bcd01c05498323768d439d5f59e86 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/asyrjasalo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1fe21de5c82bcd01c05498323768d439d5f59e86 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2192453b70da81e45fc2a41378a3bdcdcb7b0135ecb3de5ac2d9a3348877ddd0
|
|
| MD5 |
a21a5e9415243673690fdca7db0f4b6b
|
|
| BLAKE2b-256 |
9f8e6115b76a6f63468b7f1baa4ec687eb2a82b67d9f6dcc66c4402fb0ea2a99
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dmcp-1.1.0-py3-none-manylinux_2_24_x86_64.whl -
Subject digest:
2192453b70da81e45fc2a41378a3bdcdcb7b0135ecb3de5ac2d9a3348877ddd0 - Sigstore transparency entry: 800425623
- Sigstore integration time:
-
Permalink:
asyrjasalo/dynamic-mcp@1fe21de5c82bcd01c05498323768d439d5f59e86 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/asyrjasalo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1fe21de5c82bcd01c05498323768d439d5f59e86 -
Trigger Event:
push
-
Statement type:
File details
Details for the file dmcp-1.1.0-py3-none-manylinux_2_24_aarch64.whl.
File metadata
- Download URL: dmcp-1.1.0-py3-none-manylinux_2_24_aarch64.whl
- Upload date:
- Size: 3.5 MB
- Tags: Python 3, manylinux: glibc 2.24+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32bda958ec018e0c0a6e94808d3a6138c09f40b47d29a41a900dac61c373ee64
|
|
| MD5 |
d86faf8a35213294f1b8040f88ba71a2
|
|
| BLAKE2b-256 |
9ea11d430ed0a61c0d0f802d22bab7e0fef520db98d39ead459e8faaa8d6f636
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dmcp-1.1.0-py3-none-manylinux_2_24_aarch64.whl -
Subject digest:
32bda958ec018e0c0a6e94808d3a6138c09f40b47d29a41a900dac61c373ee64 - Sigstore transparency entry: 800425540
- Sigstore integration time:
-
Permalink:
asyrjasalo/dynamic-mcp@1fe21de5c82bcd01c05498323768d439d5f59e86 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/asyrjasalo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1fe21de5c82bcd01c05498323768d439d5f59e86 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0136622ae4dbbc99838629e44e3edcc91b7df319381af1500fdfd44efad44d34
|
|
| MD5 |
0b3228024701c6facee9aa8168718c7d
|
|
| BLAKE2b-256 |
dfda0cdd3905a02c1d3bd22d5f645adae1c7b550153692472dc92beee5197c9e
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dmcp-1.1.0-py3-none-macosx_11_0_arm64.whl -
Subject digest:
0136622ae4dbbc99838629e44e3edcc91b7df319381af1500fdfd44efad44d34 - Sigstore transparency entry: 800425513
- Sigstore integration time:
-
Permalink:
asyrjasalo/dynamic-mcp@1fe21de5c82bcd01c05498323768d439d5f59e86 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/asyrjasalo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1fe21de5c82bcd01c05498323768d439d5f59e86 -
Trigger Event:
push
-
Statement type: