dynamic-mcp 1.0.0

MCP proxy server that reduces LLM context overhead with on-demand tool loading from multiple upstream servers
# 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

Download the binary for your operating system from the
[releases page](https://github.com/asyrjasalo/dynamic-mcp/releases)
and put it in your `PATH`.

Alternatively, you may install from [crates.io](https://crates.io/crates/dynamic-mcp):

    cargo install dynamic-mcp

Then the binary will be available at `~/.cargo/bin/dynamic-mcp`.

### Setup

Once `dynamic-mcp` is in `PATH`, take it into use in your agent's MCP settings:

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

Or you can set `DYNAMIC_MCP_CONFIG` environment variable and not give arguments.

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

    dynamic-mcp 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
```

## Config File

### Descriptions

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

```json
{
  "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:

```json
{
  "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)

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

#### http

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

#### sse

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

#### OAuth Authentication (HTTP/SSE)

```json
{
  "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

**Debug mode**:
```bash
RUST_LOG=debug dynamic-mcp 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

To build `dynamic-mcp` from source:

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

The binary will be available at `./target/release/dynamic-mcp`.

For more details on development setup, testing, and contributing, see [CONTRIBUTING.md](CONTRIBUTING.md).

## Acknowledgments

- TypeScript implementation: [modular-mcp]https://github.com/d-kimuson/modular-mcp
- MCP Specification: [Model Context Protocol]https://modelcontextprotocol.io/
- Rust MCP Ecosystem: [rust-mcp-stack]https://github.com/rust-mcp-stack