Skip to content

feat: Add MCP server configuration parsing (e.g.: mcp.json) to the SDK #968

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 42 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
7d20254
First cut at config API
msabramo Jun 16, 2025
c92c12f
Add effective_command and effective_args
msabramo Jun 16, 2025
444960c
Make infer_server_types typed; fix warnings
msabramo Jun 16, 2025
2bf423d
test_explicit_types_are_respected
msabramo Jun 16, 2025
8ce1221
More descriptive server names in tests
msabramo Jun 16, 2025
8dfa25b
test_streamable_http_server => test_streamable_http_server_with_headers
msabramo Jun 16, 2025
25b3a7b
Test refactoring
msabramo Jun 16, 2025
37a6bb0
Move test fixtures from Python to mcp.json
msabramo Jun 16, 2025
7d63c26
ruff format
msabramo Jun 16, 2025
38eff49
Add JSONC support
msabramo Jun 16, 2025
71adf90
Revert "Add JSONC support"
msabramo Jun 16, 2025
68b1a2e
Add YAML support
msabramo Jun 16, 2025
a707aeb
Use shlex.split to parse command
msabramo Jun 16, 2025
729c0db
More YAML tests
msabramo Jun 16, 2025
651d504
Handle multiline YAML commands with backslashes
msabramo Jun 16, 2025
54d173e
Fix pre-commit failures
msabramo Jun 16, 2025
76ee90b
ruff format tests/client/config/test_yaml_functionality.py
msabramo Jun 16, 2025
e2e16f4
ruff format tests/client/config/__init__.py
msabramo Jun 16, 2025
8b3593c
Remove unnecessary `import json`
msabramo Jun 16, 2025
f9d48d6
Allow config_path: Path | str
msabramo Jun 16, 2025
4af7fe4
Allow config_path to use ~ for home dir
msabramo Jun 16, 2025
6574cfc
Expand environment variables in config_path
msabramo Jun 16, 2025
dd7a77d
Add as_dict method
msabramo Jun 16, 2025
66e0e0e
Support both "servers" and "mcpServers" keys
msabramo Jun 16, 2025
8c8e657
Support VS Code "inputs" key
msabramo Jun 16, 2025
85036e0
Strip out // comments (JSONC support)
msabramo Jun 16, 2025
27e5b12
Fix lint issues
msabramo Jun 16, 2025
70e91b9
Add tests for when yaml is not importable
msabramo Jun 16, 2025
cf2b2be
Add support for name, description, isActive optional fields
msabramo Jun 16, 2025
6493c57
Smarter detection of SSE servers
msabramo Jun 16, 2025
27c63e6
Lowercase fields before checking for "sse" in them
msabramo Jun 16, 2025
d5aeca6
Remove as_dict method
msabramo Jun 16, 2025
d6d3809
ruff format tests/client/config/test_mcp_servers_config.py
msabramo Jun 16, 2025
fbfa594
Add docs
msabramo Jun 16, 2025
eec3c2b
Documentation tweaks
msabramo Jun 17, 2025
31eb3e1
Docs tweaks
msabramo Jun 17, 2025
9753da8
Docs tweaks
msabramo Jun 17, 2025
02fc66f
Docs tweaks
msabramo Jun 17, 2025
47e92d7
Move input substitution to new `server` method
msabramo Jun 17, 2025
71e20d1
Doc updates
msabramo Jun 17, 2025
0c3bde3
Emit warning when `servers` attribute accessed
msabramo Jun 17, 2025
1ac383e
Add tests/client/config/test_warning_functionality.py
msabramo Jun 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
- [Advanced Usage](#advanced-usage)
- [Low-Level Server](#low-level-server)
- [Writing MCP Clients](#writing-mcp-clients)
- [API for Client Configuration](#api-for-client-configuration)
- [MCP Primitives](#mcp-primitives)
- [Server Capabilities](#server-capabilities)
- [Documentation](#documentation)
Expand Down Expand Up @@ -862,6 +863,11 @@ async def main():

For a complete working example, see [`examples/clients/simple-auth-client/`](examples/clients/simple-auth-client/).

### API for Client Configuration

The MCP Python SDK provides an API for client configuration. This lets client applications easily load MCP servers from configuration files in a variety of formats and with some useful, built-in features.

See the [Client Configuration Guide](docs/client-configuration.md) for complete details.

### MCP Primitives

Expand Down
348 changes: 348 additions & 0 deletions docs/client-configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,348 @@
# MCP Client Configuration (NEW)

This guide, for client application developers, covers a new API for client
configuration. Client applications can use this API to get info about configured
MCP servers from configuration files

## Why should my application use this API?

- Eliminate the need to write and maintain code to parse configuration files
- Your application can easily benefit from bug fixes and new features related to configuration
- Allows your application to support features that other applications may have
and which your application does not. E.g.,

- Allow specifying the entire command in the `command` field (not having to
specify an `args` list), which makes it easier for users to manage
- Allow comments in JSON configuration files
- Input variables (as supported by VS Code), plus validation of required inputs
and interpolation of input values
- YAML configuration files, which are more readable and easier to write than JSON

- If every application that uses MCP supported this API, it would lead to
greater consistency in how MCP servers are configured and used, which is a
tremendous win for users and a benefit to the MCP ecosystem. Note: This is the
Python SDK, but hopefully this can be ported to the SDKs for other languages.

## Loading Configuration Files

```python
from mcp.client.config.mcp_servers_config import MCPServersConfig

# Load JSON
config = MCPServersConfig.from_file("~/.cursor/mcp.json")
config = MCPServersConfig.from_file("~/Library/Application\ Support/Claude/claude_desktop_config.json")

# Load YAML (auto-detected by extension)
config = MCPServersConfig.from_file("~/.cursor/mcp.yaml") # Not yet supported in Cursor but maybe soon...?!
config = MCPServersConfig.from_file("~/Library/Application\ Support/Claude/claude_desktop_config.yaml") # Maybe someday...?!

config = MCPServersConfig.from_file(".vscode/mcp.json")

mcp_server = config.server("time")
print(mcp_server.command)
print(mcp_server.args)
print(mcp_server.env)
print(mcp_server.headers)
print(mcp_server.inputs)
print(mcp_server.isActive)
print(mcp_server.effective_command)
print(mcp_server.effective_args)
```

## Configuration File Formats

MCP supports multiple configuration file formats for maximum flexibility.

### JSON Configuration

```json
{
"mcpServers": {
"time": {
"command": "uvx",
"args": ["mcp-server-time"]
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/username/Desktop"]
}
}
}
```

This is a typical JSON configuration file for an MCP server in that it has
`command` and `args` (as a list) fields.

Users can also specify the entire command in the `command` field, which
makes it easier to read and write. Internally, the library splits the command
into `command` and `args` fields, so the result is a nicer user experience and
no application code needs to change.

```json
{
"mcpServers": {
"time": {
"command": "uvx mcp-server-time"
},
"filesystem": {
"command": "npx -y @modelcontextprotocol/server-filesystem /Users/username/Desktop"
}
}
}
```

JSON is the most commonly used format for MCP servers, but it has some
limitations, which is why subsequent sections cover other formats, such as JSONC
and YAML.

### JSON with Comments (JSONC)

The API supports JSON files with `//` comments (JSONC), which is very commonly
used in the VS Code ecosystem:

```jsonc
{
"mcpServers": {
// Can get current time in various timezones
"time": {
"command": "uvx mcp-server-time"
},

// Can get the contents of the user's desktop
"filesystem": {
"command": "npx -y @modelcontextprotocol/server-filesystem /Users/username/Desktop"
}
}
}
```

### YAML Configuration

The API supports YAML configuration files, which offer improved readability,
comments, and the ability to completely sidestep issues with commas, that are
common when working with JSON.

```yaml
mcpServers:
# Can get current time in various timezones
time:
command: uvx mcp-server-time

# Can get the contents of the user's desktop
filesystem:
command: npx -y @modelcontextprotocol/server-filesystem /Users/username/Desktop
```

**Installation**: YAML support requires the optional dependency:

```bash
pip install "mcp[yaml]"
```

## Server Types and Auto-Detection

MCP automatically infers server types based on configuration fields when the
`type` field is omitted:

### Stdio Servers

Servers with a `command` field are automatically detected as `stdio` type:

```yaml
mcpServers:
python-server:
command: python -m my_server
# type: stdio (auto-inferred)
```

### Streamable HTTP Servers

Servers with a `url` field (without SSE keywords) are detected as
`streamable_http` type:

```yaml
mcpServers:
api-server:
url: https://api.example.com/mcp
# type: streamable_http (auto-inferred)
```

### SSE Servers

Servers with a `url` field containing "sse" in the URL, name, or description are
detected as `sse` type:

```yaml
mcpServers:
sse-server:
url: https://api.example.com/sse
# type: sse (auto-inferred due to "sse" in URL)

event-server:
url: https://api.example.com/events
description: "SSE-based event server"
# type: sse (auto-inferred due to "SSE" in description)
```

## Input Variables and Substitution

MCP supports dynamic configuration using input variables, which is a feature
that VS Code supports. This works in both JSON and YAML configurations.

### Declaring Inputs (JSON)

```json
{
"inputs": [
{
"id": "api-key",
"type": "promptString",
"description": "Your API key",
"password": true
},
{
"id": "server-host",
"type": "promptString",
"description": "Server hostname"
}
],
"servers": {
"dynamic-server": {
"url": "https://${input:server-host}/mcp",
"headers": {
"Authorization": "Bearer ${input:api-key}"
}
}
}
}
```

### Declaring Inputs (YAML)

```yaml
inputs:
- id: api-key
type: promptString
description: "Your API key"
password: true
- id: server-host
type: promptString
description: "Server hostname"

servers:
dynamic-server:
url: https://${input:server-host}/mcp
headers:
Authorization: Bearer ${input:api-key}
```

### Getting Declared Inputs

The application can use the `inputs` field to get the declared inputs and
prompt the user for the values or otherwise allow them to be specified.

The application gets the declared inputs by doing:

```python
config = MCPServersConfig.from_file(".vscode/mcp.json")
for input in config.inputs:
# Prompt the user for the value
...
```

### Using Inputs

When loading the configuration, provide input values:

```python
from mcp.client.config.mcp_servers_config import MCPServersConfig

config = MCPServersConfig.from_file("config.yaml")

# Substitute input values into the configuration
server = config.server(
"dynamic-server",
input_values={"api-key": "secret-key-123", "server-host": "api.example.com"},
)
```

### Input Validation

MCP validates that all required inputs are provided:

```python
# Check required inputs
required_inputs = config.get_required_inputs()
print(f"Required inputs: {required_inputs}")

# Validate provided inputs
missing_inputs = config.validate_inputs(provided_inputs)
if missing_inputs:
print(f"Missing required inputs: {missing_inputs}")
```

## Configuration Schema

### Server Configuration Base Fields

All server types support these common optionalfields:

- `name` (string, optional): Display name for the server
- `description` (string, optional): Server description
- `isActive` (boolean, default: true): Whether the server is active

### Stdio Server Configuration

```yaml
mcpServers:
stdio-server:
type: stdio # Optional if 'command' is present
command: python -m my_server
args: # Optional additional arguments
- --debug
- --port=8080
env: # Optional environment variables
DEBUG: "true"
API_KEY: secret123
```

### Streamable HTTP Server Configuration

```yaml
mcpServers:
http-server:
type: streamable_http # Optional if 'url' is present
url: https://api.example.com/mcp
headers: # Optional HTTP headers
Authorization: Bearer token123
X-Custom-Header: value
```

### SSE Server Configuration

```yaml
mcpServers:
sse-server:
type: sse
url: https://api.example.com/sse
headers: # Optional HTTP headers
Authorization: Bearer token123
```

## Field Aliases

MCP supports both traditional and modern field names:

- `mcpServers` (most common) or `servers` (VS Code)

```yaml
# More common format
mcpServers:
my-server:
command: python -m server

# VS Code format (equivalent)
servers:
my-server:
command: python -m server
```
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ site_url: https://modelcontextprotocol.github.io/python-sdk

nav:
- Home: index.md
- Client Configuration: client-configuration.md
- API Reference: api.md

theme:
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ dependencies = [
rich = ["rich>=13.9.4"]
cli = ["typer>=0.12.4", "python-dotenv>=1.0.0"]
ws = ["websockets>=15.0.1"]
yaml = ["pyyaml>=6.0.2"]

[project.scripts]
mcp = "mcp.cli:app [cli]"
Expand Down
Loading
Loading