Add options field for static value lists and options_from for dynamic file-based options. options_from uses JSONPath to extract values from local JSON files (e.g., account names from config). Update schema with validation rules. Add --manifest flag as preferred discovery method (offline, version-matched). Reorder discovery priority: installed tool → well-known URL → HTML link → direct URL → local file. Update examples and documentation
17 KiB
mcp-manifest.json Specification v0.1
Status: Draft Author: David H Friedel Jr Date: 2026-03-29
Abstract
mcp-manifest.json is a machine-readable manifest format for Model Context Protocol (MCP) servers. It describes how to install, configure, and connect to an MCP server — the information that today lives only in README files and requires manual copy-paste into client configuration.
By shipping an mcp-manifest.json, server authors enable MCP clients to automate discovery, installation, configuration, and connection setup.
Motivation
The MCP ecosystem is in its "curl the install script" era. Every server is a snowflake:
- Installation varies:
npm install -g,dotnet tool install,pip install, binary download, Docker - Configuration varies: environment variables, CLI args, config files, hardcoded
- Transport varies: stdio, SSE, streamable HTTP
- Client wiring varies: every README has a different JSON blob to paste
The MCP protocol defines the handshake after connection, but is agnostic about everything upstream — how to get the server running and how to wire a client to it. This gap forces every user to manually translate README instructions into client-specific configuration.
mcp-manifest.json fills this gap with a single, standardized, machine-readable file.
Spec
File Location
The manifest MUST be named mcp-manifest.json and SHOULD be placed at the root of the server's repository or package.
Schema
{
"$schema": "https://mcp-manifest.dev/schema/v0.1.json",
"version": "0.1",
"server": {
"name": "string (required) — unique identifier, lowercase, hyphens allowed",
"displayName": "string (required) — human-readable name",
"description": "string (required) — one-line description",
"version": "string (required) — semver",
"author": "string (optional) — author or organization",
"homepage": "string (optional) — URL to project homepage",
"repository": "string (optional) — URL to source repository",
"license": "string (optional) — SPDX license identifier",
"icon": "string (optional) — URL to the server's icon (square, PNG or SVG)",
"keywords": ["string (optional) — discovery tags"]
},
"install": [
{
"method": "string (required) — dotnet-tool | npm | pip | cargo | binary | docker",
"package": "string (required) — package name or image",
"source": "string (optional) — custom registry URL",
"command": "string (required) — the command name after installation",
"priority": "number (optional, default 0) — lower = preferred"
}
],
"transport": "string (required) — stdio | sse | streamable-http",
"endpoint": "string (optional) — URL for sse/streamable-http transports",
"config": [
{
"key": "string (required) — parameter name",
"description": "string (required) — what this parameter does",
"type": "string (required) — string | boolean | number | path | url | secret",
"required": "boolean (default false)",
"default": "any (optional) — default value",
"env_var": "string (optional) — environment variable name that supplies this value",
"arg": "string (optional) — CLI argument name (e.g. '--api-key')",
"prompt": "string (optional) — human-readable prompt for interactive setup",
"options": ["string (optional) — static list of valid values"],
"options_from": {
"file": "string — path to a local JSON file (~ expanded to home directory)",
"path": "string — JSONPath expression to extract values from the file"
}
}
],
"scopes": ["string (optional) — where this server makes sense: 'global', 'project', 'both'"],
"settings_template": {
"description": "The exact JSON object to merge into the client's mcpServers config. Variables use ${config.key} syntax.",
"example": {
"command": "ironlicensing-mcp",
"args": ["--profile", "${profile}"]
}
}
}
Field Details
server (required)
Metadata about the MCP server. The name field is the canonical identifier used in client configuration (e.g., the key in mcpServers).
The icon field is a URL to the server's icon (square, PNG or SVG recommended, minimum 64x64). Clients SHOULD display this icon alongside the server name. When absent, clients SHOULD fall back to a default MCP icon.
install (required, array)
One or more installation methods, ordered by priority (lower = preferred). Clients SHOULD present the highest-priority method that matches the user's environment.
| Method | Package Format | Example |
|---|---|---|
dotnet-tool |
NuGet package ID | IronLicensing.Mcp |
npm |
npm package name | @anthropic/mcp-server-sqlite |
pip |
PyPI package name | mcp-server-fetch |
cargo |
Crate name | mcp-server-rs |
binary |
Download URL template | https://github.com/.../releases/download/v${version}/server-${os}-${arch} |
docker |
Docker image | ghcr.io/org/mcp-server:latest |
The source field allows custom registries (e.g., private NuGet feeds, npm registries).
The command field is the CLI command available after installation (e.g., ironlicensing-mcp).
transport (required)
How the client connects to this server:
| Transport | Description | Requires endpoint |
|---|---|---|
stdio |
Client spawns process, communicates via stdin/stdout | No |
sse |
Client connects to server-sent events endpoint | Yes |
streamable-http |
Client uses HTTP streaming | Yes |
config (optional, array)
Parameters the server accepts. Each entry describes one configuration value.
Type values:
string— free-form textboolean— true/falsenumber— numeric valuepath— filesystem path (clients may show a file picker)url— URL (clients may validate format)secret— sensitive value like API key (clients SHOULD mask display, SHOULD NOT log)
Available values: Config parameters can declare their valid values:
options— a static list of valid values. Clients SHOULD render these as a dropdown/picker.options_from— dynamically read from a local file. Thefilefield is a path (with~expanded to the user's home directory) andpathis a JSONPath expression to extract values. Clients SHOULD resolve this at display time and render as a dropdown. If the file doesn't exist or the path yields no results, clients SHOULD fall back to free-text input.
Resolution order: Clients SHOULD resolve config values in this order:
- User-provided value (via UI prompt or manual entry)
- Environment variable (
env_varfield) - CLI argument (
argfield) - Default value (
defaultfield)
scopes (optional)
Hints for where the server is typically configured:
global— user-wide (e.g., personal tools, account management)project— per-project (e.g., database access, project-specific APIs)both— reasonable in either scope
settings_template (optional)
A pre-built template for the client's MCP server configuration entry. Variables use ${key} syntax where key matches a config[].key value. Clients can substitute user-provided values to generate the final configuration.
Example: Complete Manifest
{
"$schema": "https://mcp-manifest.dev/schema/v0.1.json",
"version": "0.1",
"server": {
"name": "ironlicensing",
"displayName": "IronLicensing",
"description": "Manage IronLicensing products, tiers, features, licenses, and analytics",
"version": "1.0.0",
"author": "IronServices",
"homepage": "https://www.ironlicensing.com",
"repository": "https://git.marketally.com/IronServices/ironlicensing-mcp",
"license": "MIT",
"icon": "https://www.ironlicensing.com/favicon.svg",
"keywords": ["licensing", "saas", "product-management", "analytics"]
},
"install": [
{
"method": "dotnet-tool",
"package": "IronLicensing.Mcp",
"source": "https://git.marketally.com/api/packages/ironservices/nuget/index.json",
"command": "ironlicensing-mcp",
"priority": 0
}
],
"transport": "stdio",
"config": [
{
"key": "profile",
"description": "Named account profile from ~/.ironlicensing/config.json",
"type": "string",
"required": false,
"arg": "--profile",
"prompt": "Account profile (leave empty for default)",
"options_from": {
"file": "~/.ironlicensing/config.json",
"path": "$.accounts[*].name"
}
},
{
"key": "api-key",
"description": "IronLicensing API key (sk_live_xxx) from /app/settings/api-keys",
"type": "secret",
"required": false,
"env_var": "IRONLICENSING_API_KEY",
"arg": "--api-key",
"prompt": "API key (or configure via add_account tool after connecting)"
},
{
"key": "base-url",
"description": "IronLicensing API base URL",
"type": "url",
"required": false,
"default": "http://localhost:5000",
"env_var": "IRONLICENSING_BASE_URL",
"arg": "--base-url",
"prompt": "API base URL"
}
],
"scopes": ["global"],
"settings_template": {
"command": "ironlicensing-mcp",
"args": ["--profile", "${profile}"]
}
}
Example: Minimal Manifest (npm, no config)
{
"$schema": "https://mcp-manifest.dev/schema/v0.1.json",
"version": "0.1",
"server": {
"name": "sqlite",
"displayName": "SQLite Explorer",
"description": "Query and explore SQLite databases",
"version": "0.5.0"
},
"install": [
{
"method": "npm",
"package": "@anthropic/mcp-server-sqlite",
"command": "mcp-server-sqlite",
"priority": 0
}
],
"transport": "stdio",
"config": [
{
"key": "db-path",
"description": "Path to SQLite database file",
"type": "path",
"required": true,
"prompt": "Database file path"
}
],
"scopes": ["project"],
"settings_template": {
"command": "mcp-server-sqlite",
"args": ["${db-path}"]
}
}
Autodiscovery
MCP manifest autodiscovery follows the same pattern as RSS feed discovery — server authors publish a pointer to their manifest from their website, and clients resolve it automatically from a domain name.
Discovery Methods
Clients SHOULD support the following discovery methods, in priority order:
1. Installed Tool (--manifest flag)
If the server command is already installed on the system, clients SHOULD try running it with the --manifest flag:
ironlicensing-mcp --manifest
The command MUST output the manifest JSON to stdout and exit with code 0. This is the preferred discovery method because:
- It works offline — no network request needed
- It works for private/internal servers with no website
- The manifest can include runtime-resolved values (e.g.,
options_fromwith local files) - It guarantees the manifest matches the installed version
Server authors SHOULD implement this flag. The output MUST be valid mcp-manifest.json content.
2. Well-Known URL
Serve the manifest at a well-known path:
GET https://example.com/.well-known/mcp-manifest.json
This is the preferred method for API servers and headless services that don't serve HTML. The response MUST be application/json with the manifest content.
Server setup:
- Serve the
mcp-manifest.jsonfile at/.well-known/mcp-manifest.json - Return
Content-Type: application/json - CORS headers SHOULD allow cross-origin requests (
Access-Control-Allow-Origin: *)
3. HTML Link Tag
Add a <link> tag to the <head> of any page on the server author's website:
<link rel="mcp-manifest" type="application/json" href="/mcp-manifest.json" />
The href MAY be:
- A relative path:
href="/mcp-manifest.json" - An absolute URL:
href="https://cdn.example.com/mcp-manifest.json" - A path to a different domain:
href="https://raw.github.com/org/repo/main/mcp-manifest.json"
Multiple manifests: A page MAY contain multiple <link rel="mcp-manifest"> tags for different MCP servers. Clients SHOULD present all discovered manifests and let the user choose.
<link rel="mcp-manifest" type="application/json" href="/mcp-manifests/analytics.json" title="Analytics Server" />
<link rel="mcp-manifest" type="application/json" href="/mcp-manifests/licensing.json" title="Licensing Server" />
4. Direct URL
A direct URL to the manifest file:
https://git.example.com/org/mcp-server/raw/branch/main/mcp-manifest.json
5. Local File
A local file path to a manifest:
C:\path\to\mcp-manifest.json
/home/user/projects/mcp-server/mcp-manifest.json
Client Resolution Algorithm
Clients SHOULD resolve manifests using this algorithm. The input may be a command name (for installed servers), a domain, URL, or file path.
INPUT: user_input (could be command, domain, URL, or file path)
1. If user_input is an installed command (on PATH):
→ Run: {user_input} --manifest
- If exit code 0 with valid JSON manifest: Done.
2. If user_input is a local file path and the file exists:
→ Parse as manifest JSON. Done.
3. If user_input looks like a direct manifest URL (ends with .json):
→ Fetch and parse as manifest JSON. Done.
4. Normalize to a base URL:
- If no scheme, prepend "https://"
- If no path, use root "/"
→ base_url
5. Try well-known URL:
→ GET {base_url}/.well-known/mcp-manifest.json
- If 200 with valid JSON manifest: Done.
6. Fetch the HTML page:
→ GET {base_url}
- Parse HTML for <link rel="mcp-manifest"> tags
- If found: fetch the href URL, parse as manifest. Done.
7. Discovery failed.
→ Inform user that no manifest was found at the given location.
Example: Full Discovery Flow
Scenario A: Server already installed
A developer has ironlicensing-mcp installed. Their MCP client detects the command and runs:
ironlicensing-mcp --manifest→ outputs manifest JSON- Parses manifest: 3 config params (profile, api-key, base-url)
profilehasoptions_from: { file: "~/.ironlicensing/config.json", path: "$.accounts[*].name" }- Client reads
~/.ironlicensing/config.json→ finds["marketally_llc", "marketally_pte"] - Shows dropdown for profile with the two options
- User selects
marketally_pte→ generates args:["--profile", "marketally_pte"]
Total user effort: select from dropdown. Everything else is automatic.
Scenario B: Server not installed
A developer types ironlicensing.com into their MCP client:
ironlicensing-mcpnot on PATH → skip--manifest- Client tries
https://ironlicensing.com/.well-known/mcp-manifest.json→ 200 OK - Parses the manifest: name="ironlicensing", install via dotnet-tool
- Shows: "Install IronLicensing MCP? Run:
dotnet tool install -g IronLicensing.Mcp" - User confirms → tool installed
- Prompts for config: "API key (sk_live_xxx)" → user enters key
- Writes to
~/.claude/.mcp.json:{ "mcpServers": { "ironlicensing": { "command": "ironlicensing-mcp" } } }
Total user effort: type domain name, enter API key. Everything else is automated.
Client Behavior
Installation Flow
When a client encounters a manifest:
- Check if the
commandis already available on PATH - If not, present the install methods (filtered by available runtimes) ordered by priority
- Execute the installation command
- Prompt for required
configvalues (usingprompttext, respectingtypefor UI) - Generate the
mcpServersentry usingsettings_templatewith substituted values - Write to the appropriate settings file based on
scopes
Validation
After configuration, clients SHOULD:
- Verify the
commandexists on PATH - Attempt an MCP
initializehandshake - Report success or failure to the user
Design Principles
- The format is the standard, the UI is an implementation. Any client can consume
mcp-manifest.json. - Discoverable like RSS. A
<link>tag or well-known URL lets any client find the manifest from a domain name. - Progressive complexity. A minimal manifest is 10 lines. Advanced features are optional.
- No runtime dependency. The manifest is static JSON — no code execution, no scripting.
- Transport-aware. The manifest captures how to connect, not just how to install.
- Secret-aware. Config values typed as
secretget special handling (masking, no logging). - No central registry required. Discovery is decentralized — each server author publishes their own manifest.
Versioning
The version field at the manifest root tracks the spec version. Clients SHOULD check this field and gracefully handle unknown versions.
Future Considerations
- Registry protocol — an optional central index that crawls and aggregates manifests for search/browsing
- Dependency declaration — servers that require other servers or system tools
- Health check — a standard way to verify a configured server is working
- Update notifications — signaling when a newer version is available
- Signed manifests — cryptographic signatures for manifest authenticity