Define a Standard MCP Configuration Schema
Is your feature request related to a problem? Please describe.
I'm developing an MCP host. Existing MCP hosts read their configuration from a config JSON file, but I couldn’t find a standard configuration schema.
Currently, each implementation appears to handle configuration independently in some popular projects/products. There are concerns with this situation from multiple perspectives:
From the MCP host implementer's perspective: Each implementation has its own design and validation mechanism. As new requirements—such as additional validation rules or authentication methods—arise, implementers must address these changes on a case-by-case basis. This fragmentation increases maintenance overhead and complicates keeping up with evolving specifications.
From the end-user's perspective: Configuration options differ across products. Users must consult various product documents to understand each specific implementation's requirements, and if multiple products are used, configurations may vary (with different setting names). This inconsistency negatively impacts the user experience.
Here are some implementations in popular projects/products:
-
Claude Desktop:
[Quickstart for Claude Desktop](https://modelcontextprotocol.io/quickstart/user)
Example configuration locations:- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{ "mcpServers": { "filesystem": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "/Users/username/Desktop", "/Users/username/Downloads" ] } } }This configuration is minimal but no documentation for transport types like
stdinorsse. - macOS:
-
VSCode's GitHub Copilot:
[MCP Servers Documentation](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server){ // 💡 Inputs are prompted on first server start, then stored securely by VS Code. "inputs": [ { "type": "promptString", "id": "perplexity-key", "description": "Perplexity API Key", "password": true } ], "servers": { // https://github.com/ppl-ai/modelcontextprotocol/ "Perplexity": { "type": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-perplexity-ask"], "env": { "PERPLEXITY_API_KEY": "${input:perplexity-key}" } } } }The
inputsfield is more advanced—it defines how to obtain authentication information and pass it to the command. Also, GitHub Copilot can readclaude_desktop_config.json:VS Code can automatically detect and reuse MCP servers defined in other tools, such as Claude Desktop. Enable auto-discovery with the
chat.mcp.discovery.enabledsetting. -
Cline:
The schema is defined using Zod.
[Cline's MCP Schema in McpHub.ts](https://github.com/cline/cline/blob/807a4b36dfe14a18a3e6a0dc39e05594e2fbff4c/src/services/mcp/McpHub.ts#L48-L72)const AutoApproveSchema = z.array(z.string()).default([]) const BaseConfigSchema = z.object({ autoApprove: AutoApproveSchema.optional(), disabled: z.boolean().optional(), timeout: z.number().min(MIN_MCP_TIMEOUT_SECONDS).optional().default(DEFAULT_MCP_TIMEOUT_SECONDS), }) const SseConfigSchema = BaseConfigSchema.extend({ url: z.string().url(), }).transform((config) => ({ ...config, transportType: "sse" as const, })) const StdioConfigSchema = BaseConfigSchema.extend({ command: z.string(), args: z.array(z.string()).optional(), env: z.record(z.string()).optional(), }).transform((config) => ({ ...config, transportType: "stdio" as const, })) const ServerConfigSchema = z.union([StdioConfigSchema, SseConfigSchema]) const McpSettingsSchema = z.object({ mcpServers: z.record(ServerConfigSchema), })Cline extends the schema with additional properties like
autoApproveanddisabled. It usestransportTypeto indicate whether the configuration is forstdinorsse, whereas GitHub Copilot uses a property calledtype. -
Continue:
[Continue MCP Documentation](https://docs.continue.dev/customize/deep-dives/mcp){ "experimental": { "modelContextProtocolServers": [ { "transport": { "type": "stdio", "command": "uvx", "args": ["mcp-server-sqlite", "--db-path", "/Users/NAME/test.db"] } } ] } }
Describe the solution you'd like
Define a standard MCP configuration schema. I propose using JSON Schema because it is language-independent and is already employed in MCP (e.g., in the ListTools response).
Here is an example of a server schema.
{
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["stdio", "sse"]
},
"command": {
"type": "string"
},
"args": {
"type": "array",
"items": { "type": "string" }
},
"env": {
"type": "object",
"additionalProperties": { "type": "string" }
},
"url": {
"type": "string",
"format": "uri"
}
}
}
While this is merely a basic schema common to all current implementations, defining it could provide a base for future enhancements such as various authentication methods and other transport-specific configurations(e.g. the input field in GitHub Copilot)
Describe alternatives you've considered
Adding another reference point: LibreChat uses a similarish YAML config to Claude Desktop (example). Bringing it up here as it's notably not JSON; YAML is a superset of JSON but just showing that clients may not use the same config formats at all (Cline also uses JSON on-disk).
Different host applications will have different capabilities on different platforms, won't they? It would be useful for all stdio servers to share the same configuration schema, for example, but if someone (hypothetically) made a mobile app that only supported remote servers, it wouldn't be possible to have a compliant configuration file, since the application's capabilities would break the type contract. That's a relatively trivial example (just define that as a SHOULD instead of a MUST), but more complex limitations or enhancements may not be so easy to represent.
GitHub Copilot is actually a notable example, as its inputs field changes what the env field means - that's a significant semantic difference that isn't captured well in a schema. If I wanted to move from Copilot to something else, I wouldn't be able to just copy the config object over, since env may not contain raw environment variables, only references to them.
I think there's value in community-adopted conventions or documented implementation recommendations so that equivalent MCP support in different client applications can be used in the same way between them, but making application config spec-defined might be too restrictive.
Thanks, @LucaButBoring
Yes, that's why I'm proposing a JSON Schema rather than a configuration file specification.
With a JSON Schema, you are not limited to a specific format—whether the actual settings are in JSON, YAML, or another format—and it can be used within any part of an application-specific configuration.
In addition, by setting additionalProperties to true (default), you allow for custom, application-specific properties while still validating the base configuration against the same specification.
The intention here is to standardize the basic configuration schema and establish a foundation that can accommodate additional specification. (Also it can simplify the implementation of new MCP Hosts.)
I was glad to find this issue @jlandowner! I've been using the following as I didn't see even one hosted on schemastore.org or anywhere...
I have been using a local one and just put it on a gist for adding to this discussion:
https://gist.github.com/timheuer/7b092645f01fe15fad2a96677d0b34e6
I was glad to find this issue @jlandowner!
Same!
've been using the following as I didn't see even one hosted on schemastore.org or anywhere...
https://gist.github.com/timheuer/7b092645f01fe15fad2a96677d0b34e6
Thanks for sharing @timheuer, this is super useful!
If you're encountering an ENOENT error when trying to run an MCP server using npm within your Cline configuration, try changing the command from npm to node.
Here's an example of how your configuration might look:
{
"mcpServers": {
"filesystem-mcp": {
"command": "node",
"args": [
"c:\\Users\\onlyo\\Documents\\Cline\\MCP\\filesystem\\node_modules\\@modelcontextprotocol\\server-filesystem\\dist\\index.js",
"c:\\Users"
],
"autoApprove": [],
"disabled": false
}
}
}
Key Insights:
- The
commandshould benode. - The
argsarray should contain:-
The first argument is always the full path to the installed NPM package's main script (e.g.,
...\\<name of your mcp>\\@modelcontextprotocol\\server-<name of your mcp>\\dist\\index.js). - Subsequent arguments are optional and specific to the particular MCP server you are running.
-
The first argument is always the full path to the installed NPM package's main script (e.g.,
Thank you @timheuer -- this is great. This is something that I think would be good published as part of the CWG site as well as the schema store.
I don't know if there is a shareable update for specifying http servers, or the rules around url only configuration?
This is the JSON schema that Visual Studio 17.14 uses: https://gist.github.com/AArnott/ea481eeb07fb78b4b6bf4b7436d88009
I don't know if there is a shareable update for specifying
httpservers, or the rules around url only configuration?
@evalstate I just updated my gist to include http -- I think they are intended to be backward compatible actually so one schemaDef actually could be fine (like @AARnott has above) but just separated in this 'just in case' ;-)
That's great, thanks @AArnott and @timheuer -- I think since SSE is being deprecated, the merged one from @AArnott is probably easier to work with....
I just had posted a discussion about a very similar idea because I didn't find this:
https://github.com/modelcontextprotocol/modelcontextprotocol/discussions/681
So obviously I'm in favor of this.
There are a few aspects in my discussion that don't seem to be mentioned here, so I'll mention them:
Permitting a full shell command to be put in command and not having to split out an args list
I have been told that Cursor at least will happily work with a config for a stdio server where the entire command is specified in command and no splitting of the args into a separate args list. E.g.:
"adobe_firefly": {
"type": "stdio",
"env": {
"FIREFLY_CLIENT_ID": "xyz",
"FIREFLY_CLIENT_SECRET": "123"
},
"command": "uvx --from git+https://github.com/msabramo/python-firefly[mcp-server] mcp-server"
},
So I wonder if that is something that should be in the schema? I like this because pasting a command verbatim is much easier than massaging it into separate command and args which feels unnecessary. Parsing a shell command isn't that hard to do in code - I think there should be an MCP library that does this so users don't have to worry about it. Especially since MCP is a technology that attracts a lot of folks who aren't necessarily versed in UNIX conventions that came from the 1970s and probably find this to just be a hurdle.
Supporting an (optional) YAML variant of the current typical config
While JSON is the current convention, and one I think should absolutely continue to be supported, some users might prefer YAML for its flexibility and readability. YAML offers:
- Support for Comments: Useful for documentation or temporarily disabling configurations.
- Forgiving Syntax: YAML is less error-prone, especially for manual edits. For example, JSON parsers get upset if you include a comma at the end of the last item in a list. This tends to lead to annoying errors when copying and pasting JSON snippet.
- Compactness: YAML files are often more concise and easier to read. And the compactness leaves more room for comments, already mentioned above, which could be very helpful for explaining things.
If we combine the idea of YAML with the above idea of permitting full command in command without args, then configs can look quite a bit nicer:
adobe_firefly:
type: stdio
env:
FIREFLY_CLIENT_ID: REPLACE_ME # Adobe Firefly client ID for authentication - get from https://developer.adobe.com/
FIREFLY_CLIENT_SECRET: REPLACE_ME # Adobe Firefly client secret for authentication - get from https://developer.adobe.com/
command: > # Command to execute (using folded style for readability)
uvx --from git+https://github.com/msabramo/python-firefly[mcp-server] mcp-server
To be clear, I am not advocating removing the JSON format - I think it should absolutely be kept for backwards compatibility and I don't want to force YAML on anyone as I know a lot of people dislike it. In fact, since YAML is a superset of JSON, it seems it would be pretty easy for clients to support YAML and continue to support the JSON format with little or no changes. I would advocate that any development of this include testing to ensure that current mcp.json files still work.
GitHub Copilot is actually a notable example, as its inputs field changes what the env field means - that's a significant semantic difference that isn't captured well in a schema. If I wanted to move from Copilot to something else, I wouldn't be able to just copy the config object over, since env may not contain raw environment variables, only references to them.
I think there's value in community-adopted conventions or documented implementation recommendations so that equivalent MCP support in different client applications can be used in the same way between them, but making application config spec-defined might be too restrictive.
This is interesting. I love the idea of the inputs key in GitHub Copilot and as @LucaButBoring points out, they've extended the semantics of env to support it in a way that isn't probably possible to express in a simple schema. A schema only gets you so far in specifying how things are supposed to work.
I wonder if what would be even more valuable than a spec or a schema would be Python and TypeScript functions that provide apps with a view of what MCP servers are configured while abstracting away the details of the underlying configuration files? Then apps don't need to care about a lot of the details like whether it's JSON or YAML or maybe in the future something entirely else like:
- Some mechanism that is provided in operating systems like Windows, Mac OS, Linux, etc. that may or may not use such files but provides a global way in the OS for users to specify MCP servers to all apps on their system
- Some mechanism that automatically discovers MCP servers from the OS or a company-wide or global registry - not thought through in depth because I'm mainly trying to focus on abstracting away the details of configuration from the apps that want to use MCP servers.
- Would such libraries (or perhaps functions in existing libraries) be valuable?
- Is it worthwhile to create a JSON schema first to help guide and ensure the correctness of such functions?
It's understandable that different tools might have somewhat different config file formats for mcp.json. But inexcusable that they don't publish a JSON Schema for their config file.
Shouldn't we also declare the runtime dependencies in the schema? E.g., to answer questions like: where do these npx, uvx, node, etc come from? what (min/max) version is requested?
As long as command is an unconstrained string, we aren't helping the server to install the required dependencies, we're probably assuming they're already there. Shouldn't we be more opinionated instead?
In alternative, since I'm thinking of how to automate building the container image, do you have an idea on how to effectively build the Dockerfile starting from the server configuration?
If the command can be anything, then you can wrap everything is sh -c and call many commands, including build steps:
sh -c 'docker build -t my-mcp . && docker run my-mcp'
Very much agree on the premise here, I think having a unified official standard would be great, as it can also move together with the MCP changes. E.g. when new features are added, the JSON Schema should be updated.
This way we can also produce nice SDKs for reading the config and then it benefits everybody and we don't get the fragmentation of the configurations.
As some alluded here, I think the file format itself can be anything, even YAML. But JSON Schema is quite universal and most languages have great support for it in terms of both parsing and validating the configuration.
While JSON is the current convention, and one I think should absolutely continue to be supported, some users might prefer YAML for its flexibility and readability. YAML offers:
I'm surprised you left out another huge benefit: no need to escape backslashes. At least for Windows users, that's huge.
Permitting a full shell command to be put in command and not having to split out an args list
No schema can preclude that as far as validation goes. So unless there is documentation to forbid it, and clients that enforce it, it may Just Work. So maybe this is more of a call for clients to support it than asking for a schema change, yes?
To add to the examples, recently Claude Code added support for env var expansion in .mcp.json.
Limited to specific key values, and with a defined syntax (${VAR:-default}), documentation.
So I'm sorry If I'm posting in the wrong place, but I can't come up with a better one. But since this issue is about creating a standard for MCP config schema I thought why not.
Feature
I want to support both having url and command/args. Both claude and vscode today only allow url or command/args.
$ claude mcp add --help
Usage: claude mcp add [options] <name> <commandOrUrl> [args...]
Background
I have created a remote MCP server, in this case in GCP cloud run.
To setup a secure connection to my MCP server I need to do gcloud run services proxy mcp-server --region=us-central1.
Then you configure your MCP server to point to localhost.
I don't want to tell all my developers: "before you connect to this mcp server you have to manually write this command before using it"
+1
Excellent start. In addition there may be a need to configure client-id when a dynamic OAuth 2.0 based authentication is in use, but there is no support for dynamic client registration. BTW the MCP client SDK should absorb the consumption of such standard configuration and also the corresponding discovery and authentication flow that needs to happen to complete the authentication.