MCP Design Specification 1.0 (MCPDS)
A declarative, design-time specification format for Model Context Protocol servers.
MCPDS is to MCP servers what OpenAPI is to REST APIs: a single human-authored,
tool-friendly document that fully describes a server — its identity, transport,
authentication, and every capability it exposes (tools, resources, prompts) —
before any server code exists. It is the source of truth from which servers are
scaffolded, clients are configured, documentation is generated, and registry
entries (server.json) are derived.
- Document revision: 1.0.0.
- Format: YAML (canonical). JSON is an allowed equivalent serialization.
- Schema dialect: JSON Schema 2020-12 by default. Another dialect MAY be
selected per-schema via an explicit
$schemakeyword. - Target MCP protocol revision:
2025-11-25(declare per-document via the optionalx-mcpProtocolVersionkey; see §2). - Filename convention:
mcp.yamlor*.mcp.yaml. - Machine-readable schema: the canonical JSON Schema for this format is
published at
https://mcpdesign.org/schemas/mcpds-1.0.schema.json. It defines the structural contract; the semantic rules in §12 are enforced by tooling in addition to the schema.
1. Design rationale
MCP exposes two layers of information that no single existing artifact covers:
- Distribution / runtime layer — how a server is packaged, transported and
launched. This is what the registry
server.jsondescribes (packages,remotes,transport,environmentVariables, …). - Capability / contract layer — what tools, resources and prompts the server
exposes, with full input/output JSON Schemas. In live MCP this exists only at
runtime via
tools/list,resources/list,prompts/list; there is no standard design-time file for it.
MCPDS unifies both layers. The server, transports, auth and packaging
sections stay compatible with server.json; the tools, resources and prompts sections are the new part.
A conforming generator MUST be able to project an MCPDS document down to a valid
registry server.json (lossily — the capability layer is dropped; see §10).
2. Top-level structure
mcpds: "1.0" # REQUIRED. Spec version string; MUST be "1.0".
x-mcpProtocolVersion: "2025-11-25" # OPTIONAL. MCP protocol revision targeted.
server: {} # REQUIRED. Identity & metadata.
instructions: "" # OPTIONAL. Server-wide guidance for the LLM.
capabilities: {} # OPTIONAL. Declared protocol feature flags.
requiresClientCapabilities: [] # OPTIONAL. Client capabilities the server depends on.
transports: [] # REQUIRED. Transport AND remote-endpoint definitions.
auth: {} # OPTIONAL. Authentication & authorization.
packaging: {} # OPTIONAL. Local distribution packages.
tools: [] # OPTIONAL. Tool definitions.
resources: [] # OPTIONAL. Static resource definitions.
resourceTemplates: [] # OPTIONAL. URI-templated resource definitions.
prompts: [] # OPTIONAL. Prompt template definitions.
x-*: {} # OPTIONAL. Vendor extensions (any top-level x- key).
x-mcpProtocolVersion declares which MCP protocol revision the document targets.
It lets generators pick the correct schema surface — newer revisions add fields
(such as tasks and tool execution). When omitted, tools SHOULD assume the
latest revision they support.
Every object MAY carry a meta field (mapping to MCP's reserved _meta) and any
number of x-* extension keys.
Key ordering. YAML/JSON mappings are unordered, so mapping key order is not semantically significant and MUST NOT affect validation. For readability, authors SHOULD follow the top-level order shown above.
3. server — identity & metadata
Mirrors the registry server.json identity fields.
server:
name: "org.example/my-server" # REQUIRED. Reverse-DNS namespaced ID.
# MUST begin/end alphanumeric; may contain - _ .
title: "My Server" # OPTIONAL. Human display name.
description: "What the server does." # REQUIRED.
version: "1.0" # REQUIRED.
websiteUrl: "https://example.org" # OPTIONAL.
repository: # OPTIONAL. When present, url + source are REQUIRED.
url: "https://github.com/org/repo"
source: "github" # github | gitlab | ...
id: "optional-vcs-id"
icons: # OPTIONAL. Per MCP icon spec. src REQUIRED URI per entry.
- src: "https://example.org/icon.svg"
mimeType: "image/svg+xml"
sizes: ["any"]
theme: "dark" # light | dark
authors: # OPTIONAL. (MCPDS extension.)
- name: "Author Name"
url: "https://example.org"
license: "MIT" # OPTIONAL. SPDX identifier.
meta: {} # OPTIONAL. -> _meta
Name rules (inherited from MCP): reverse-DNS namespace + / + name. The
namespace determines publishing authority (io.github.<user>/*,
com.<domain>/*). Labels such as io.modelcontextprotocol and dev.mcp are
reserved.
server.name, server.description and server.version are REQUIRED and MUST be
non-empty (matching the registry server.json Server object). repository,
when present, REQUIRES both url and source. Every icons[] entry REQUIRES
src formatted as a URI.
4. instructions & capabilities
instructions: |
Server-wide guidance injected into the model's system prompt.
capabilities:
tools:
listChanged: true # Server may emit notifications/tools/list_changed
resources:
subscribe: true # Clients may subscribe to resource updates
listChanged: true
prompts:
listChanged: true
logging: true # Supports logging/setLevel + log notifications
completions: true # Supports argument completion
tasks: # Task-augmented execution (rev. 2025-11-25)
list: true # Supports tasks/list
cancel: true # Supports tasks/cancel
requests: # Which request kinds may be run as tasks
tools:
call: true # tools/call may be task-augmented
experimental: # Named experimental flags; each value is a boolean.
myFeature: true
instructions maps to InitializeResult.instructions. capabilities maps to
ServerCapabilities declared during initialize; generators use these flags to
wire up the correct notification handlers.
The tasks capability was introduced in protocol revision 2025-11-25. A server
declares it when one or more requests (currently tools/call) may run
asynchronously as a task; individual tools opt in via tools[].execution (§7).
4.1 requiresClientCapabilities
requiresClientCapabilities: ["elicitation", "sampling"]
An optional array declaring which client-side MCP capabilities the server actively calls. Known values:
| Value | Server calls | Purpose |
|---|---|---|
sampling |
sampling/createMessage |
Server asks the client/LLM to generate text |
elicitation |
elicitation/create |
Server asks the client to collect structured user input |
roots |
roots/list |
Server queries the client's file-system roots |
When non-empty, clients SHOULD verify the declared capabilities are supported before connecting. Unknown values MUST be tolerated.
5. transports
A server MAY support more than one transport. Each entry declares both how a
client talks to the server and where it runs — there is no separate
remotes[] section. A stdio transport runs locally and is distributed via
packaging.packages[]; a streamable-http/sse transport runs remotely at its
own url and carries its headers and variables inline. On export to
server.json, each remote transport projects to one remotes[] entry (§10).
transports:
- type: stdio # stdio | streamable-http | sse(legacy)
- type: streamable-http
url: "https://api.example.org/mcp/{tenant}" # may use {var} templates
variables:
tenant:
description: "Tenant identifier"
isRequired: true
default: "public"
sessions: true # Stateful sessions via Mcp-Session-Id
sse: true # Server may stream via SSE on this transport
cors: # OPTIONAL, HTTP only
allowedOrigins: ["https://app.example.org"]
headers: # OPTIONAL static/required request headers
- name: "MCP-Protocol-Version"
isRequired: true
- name: "X-API-Key"
description: "API key header for deployments that require one."
isRequired: true
isSecret: true
| Field | Applies to | Notes |
|---|---|---|
type |
all | REQUIRED. stdio local; streamable-http recommended remote; sse legacy |
url |
http/sse | REQUIRED for streamable-http and sse; supports {variable} templating |
variables |
http/sse | declared template vars (description, isRequired, default, choices) |
sessions |
http | whether server keeps session state |
sse |
http | whether the server may stream responses via SSE |
cors |
http | allowed browser origins |
headers |
http | declared request headers (name, description, isRequired, isSecret, default, choices) |
Every transport REQUIRES type. A streamable-http or sse transport
additionally REQUIRES a non-empty url. Static API keys or bearer headers
required by a remote deployment are modeled here as headers, not as MCP auth
schemes (§6).
6. auth
Authentication is declared once, at the server level — it describes how a
client authenticates to the server as a whole. MCP authorizes the transport
connection/session, not individual requests, so MCPDS has no per-tool or
per-resource security field and no default-scheme mechanism. Supported
surfaces are those defined by MCP itself: OAuth 2.1 for HTTP transports,
environment variables for stdio/local execution, and an explicit none scheme.
auth:
schemes:
none:
type: none # No auth (typical for stdio local)
oauth:
type: oauth2 # OAuth 2.1 / Protected Resource Metadata
flows:
authorizationCode:
authorizationUrl: "https://auth.example.org/authorize"
tokenUrl: "https://auth.example.org/token"
scopes:
"tools:read": "List and read tool metadata"
"tools:write": "Create and modify tools"
resourceMetadataUrl: "https://api.example.org/.well-known/oauth-protected-resource"
env:
type: env # Secrets supplied as environment variables
variables:
- name: "API_TOKEN"
description: "Personal access token"
isSecret: true
isRequired: true
Supported type values: none, oauth2, env. All schemes are server-wide.
Every env scheme variable REQUIRES name; description, default,
isSecret and isRequired are OPTIONAL.
API keys, basic auth and static bearer-token headers are outside MCP core
authorization; describe them as transports[].headers (§5).
7. tools
The primary design surface. Each tool mirrors the MCP Tool object: name,
title, description, inputSchema, optional outputSchema, annotations,
execution, icons.
MCPDS adds design-time conveniences (examples).
Only name and inputSchema are REQUIRED. description is strongly recommended
(it is the prompt context the model sees) but optional; tooling SHOULD emit a
warning, not an error, when it is missing.
tools:
- name: "create_tool" # REQUIRED. Unique. 1-128 chars, [A-Za-z0-9_.-].
title: "Create Tool" # OPTIONAL. Human label.
description: | # RECOMMENDED. Prompt context for the LLM.
Add a new tool definition to the working spec. Use when the user wants to
expose a new capability. Returns the updated tool list.
inputSchema: # REQUIRED. JSON Schema 2020-12, object.
type: object
properties:
name:
type: string
pattern: "^[a-z0-9_]+$"
description: "Unique tool identifier."
description:
type: string
description: "Human + model facing description."
inputSchema:
type: object
description: "JSON Schema for the tool's parameters."
required: ["name", "description"]
additionalProperties: false
outputSchema: # OPTIONAL. JSON Schema for structured result.
type: object
properties:
toolCount: { type: integer }
tool:
type: object
properties:
name: { type: string }
title: { type: string }
required: ["name"]
required: ["toolCount"]
annotations: # OPTIONAL. Behavioral hints for clients.
title: "Create Tool"
readOnlyHint: false
destructiveHint: false
idempotentHint: true
openWorldHint: false
execution: # OPTIONAL. Task-augmented execution (rev. 2025-11-25).
taskSupport: "optional" # "forbidden" (default) | "optional" | "required"
icons: # OPTIONAL. src REQUIRED URI per entry.
- src: "https://example.org/tools/create.svg"
mimeType: "image/svg+xml"
examples: # OPTIONAL. Sample calls for docs/tests.
- name: "Add an echo tool"
input:
name: "echo"
description: "Echo the input back."
output:
toolCount: 1
tool: { name: "echo" }
meta: {} # OPTIONAL -> _meta
7.1 annotations reference
| Hint | Meaning | Default |
|---|---|---|
readOnlyHint |
Tool does not modify its environment | false |
destructiveHint |
Tool may perform irreversible updates (only meaningful if not read-only) | true |
idempotentHint |
Repeated identical calls have no additional effect | false |
openWorldHint |
Tool touches external/open systems | true |
Annotations are hints, not guarantees; clients MUST NOT rely on them for security decisions.
7.2 execution reference
| Field | Meaning | Values | Default |
|---|---|---|---|
taskSupport |
Whether tools/call for this tool may be task-augmented |
forbidden | optional | required |
forbidden |
A value other than forbidden requires the server to declare capabilities.tasks
(§4). required means the tool MUST be invoked as a task.
Name rules: a tool name MUST be 1–128 characters, case-sensitive, using
only A–Z a–z 0–9 _ - . (pattern ^[A-Za-z0-9_.-]+$). snake_case is
recommended.
8. resources & resourceTemplates
Static resources have a concrete uri; templated ones use RFC 6570 URI templates.
resources:
- uri: "spec://current" # REQUIRED.
name: "current-spec" # REQUIRED. Programmatic id.
title: "Current Working Spec" # OPTIONAL.
description: "The MCPDS document being edited."
mimeType: "application/yaml" # OPTIONAL.
size: null # OPTIONAL. Bytes, if known.
icons: # OPTIONAL. src REQUIRED URI per entry.
- src: "https://example.org/resources/spec.svg"
mimeType: "image/svg+xml"
annotations:
audience: ["assistant", "user"] # Who is this for?
priority: 0.8 # 0..1 importance.
lastModified: "2025-11-25T10:00:00Z"
resourceTemplates:
- uriTemplate: "spec://tools/{toolName}" # REQUIRED.
name: "tool-by-name"
title: "Tool Definition"
description: "Fetch a single tool definition by name."
mimeType: "application/json"
icons:
- src: "https://example.org/resources/tool.svg"
mimeType: "image/svg+xml"
annotations:
audience: ["assistant"]
priority: 0.5
lastModified: "2025-11-25T10:00:00Z"
parameters: # MCPDS: documents template vars.
- name: "toolName"
description: "The tool's unique name."
required: true
completion: true # Server offers completion for this arg.
Every resources[] entry REQUIRES uri and name; every resourceTemplates[]
entry REQUIRES uriTemplate and name. annotations.priority, when present,
MUST be a number between 0 and 1 inclusive. Every parameters[] entry
REQUIRES name.
subscribe and listChanged behavior is governed by capabilities.resources.
9. prompts
Reusable, parameterized prompt templates (MCP Prompt objects).
prompts:
- name: "scaffold_server" # REQUIRED.
title: "Scaffold MCP Server" # OPTIONAL.
description: "Generate server code from the current spec."
arguments:
- name: "language" # REQUIRED per argument.
title: "Language" # OPTIONAL.
description: "Target language." # OPTIONAL.
required: true # OPTIONAL.
- name: "includeTests"
title: "Include tests"
description: "Emit a test suite too."
required: false
icons:
- src: "https://example.org/prompts/scaffold.svg"
meta: {}
Every prompt REQUIRES name. Every arguments[] entry REQUIRES name;
title, description and required are OPTIONAL.
10. packaging — distribution (server.json projection)
Carries the local, installable distribution artifacts (the registry
packages[]) so a generator can emit a publishable registry entry. Remote
endpoints are not repeated here — they live in transports[] (§5) and project
to server.json remotes[] on export.
Each packages[] entry REQUIRES registryType, identifier and transport.
registryType is an open set: npm, pypi, cargo, nuget, oci and mcpb
are the known values, and any other non-empty registry type is permitted.
version is OPTIONAL at the schema layer because OCI versions are part of the
image reference and MCPB versions may be omitted; publishers SHOULD provide a
specific package version when the selected registry uses one. When present,
version MUST be 1–255 characters and MUST NOT be the string latest.
registryBaseUrl, when present, MUST be formatted as a URI. A package with
registryType: mcpb additionally REQUIRES fileSha256, which MUST be a
lowercase 64-character SHA-256 hex digest.
packaging:
packages:
- registryType: "npm" # npm | pypi | cargo | nuget | oci | mcpb | ... (open set)
registryBaseUrl: "https://registry.npmjs.org"
identifier: "@org/my-server"
version: "1.0.0" # OPTIONAL. 1-255 chars; MUST NOT be "latest".
transport: { type: "stdio" } # REQUIRED. Local runtime transport (see 10.3).
runtimeHint: "npx" # OPTIONAL launcher hint
runtimeArguments: # args to the runtime
- type: "named"
name: "-y"
packageArguments: # args to the package itself
- type: "positional"
valueHint: "target_dir"
value: "/project"
environmentVariables:
- name: "LOG_LEVEL"
description: "debug | info | warn | error"
default: "info"
isSecret: false
isRequired: false
meta:
"io.modelcontextprotocol.registry/publisher-provided":
tool: "mcp-designer"
version: "1.0.0"
10.1 Input objects
environmentVariables[] entries, transport.headers[] entries and the argument
kinds in 10.2 share a common input object shape (mirroring the registry
server.json input model):
| Field | Type | Notes |
|---|---|---|
description |
string | Human-readable purpose of the input |
isRequired |
boolean | Whether the user must supply a value |
format |
string | string (default) | number | boolean | filepath |
value |
string | Fixed or templated value; may embed {variable} placeholders |
isSecret |
boolean | Value is sensitive; tooling MUST mask it |
default |
string | Default value when the user supplies none |
placeholder |
string | UI hint shown when no value is set |
choices |
string[] | Allowed values |
variables |
map | Declares each {variable} used in value; every entry is itself an input object |
environmentVariables[] entries and transport.headers[] entries additionally
REQUIRE name (a named input).
10.2 runtimeArguments & packageArguments
Both argument lists use the input-object fields from 10.1 plus:
| Field | Type | Notes |
|---|---|---|
type |
string | REQUIRED. positional | named |
name |
string | REQUIRED when type: named (e.g. -y, --port) |
valueHint |
string | Placeholder name of a positional value |
isRepeated |
boolean | Argument may be passed multiple times |
A named argument REQUIRES name. A positional argument REQUIRES at least
one of valueHint or value.
10.3 Package transport
Declares the local runtime transport of the installed package (registry
Package.transport parity):
transport:
type: "stdio" # REQUIRED. stdio | streamable-http | sse
# For streamable-http / sse packages:
# url: "http://localhost:{port}/mcp" # REQUIRED. http(s) URL or {variable} template.
# headers: # OPTIONAL. Named inputs (10.1).
# - name: "X-API-Key"
# isSecret: true
# isRequired: true
type is REQUIRED and MUST be one of stdio, streamable-http, sse. When
type is streamable-http or sse, url is REQUIRED and MUST be either an
absolute http(s) URL or a template beginning with a {variable} reference
(for example {baseUrl}/mcp); each such variable is declared as an input
object. headers, when present, is an array of named inputs (10.1).
Projection rule: to produce server.json, take server.name,
server.description, server.version, server.repository,
packaging.packages and packaging.meta as _meta, and derive remotes[] from the
transports[] whose type is streamable-http or sse (each contributes one
remotes[] entry carrying its url, headers and variables). The capability
layer (tools/resources/prompts) has no server.json equivalent and is
omitted.
11. Minimal conforming document
mcpds: "1.0"
server:
name: "org.example/hello"
description: "Minimal MCP server."
version: "1.0.0"
transports:
- type: stdio
tools:
- name: "echo"
description: "Echo a message back to the caller."
inputSchema:
type: object
properties:
message: { type: string }
required: ["message"]
additionalProperties: false
12. Validation rules (normative summary)
mcpdsMUST be present and equal the string"1.0".server.name,server.descriptionandserver.versionMUST be present and non-empty.transportsMUST contain at least one entry.server.nameMUST match the reverse-DNS + name grammar (§3).- Every
tools[].name,resources[].name,resourceTemplates[].nameandprompts[].nameMUST be unique within its collection. - Every
tools[]entry MUST declarenameandinputSchema. A toolnameMUST be 1–128 characters and match^[A-Za-z0-9_.-]+$. A missingtools[].descriptionSHOULD raise a warning, not an error. - Every
inputSchemaandoutputSchemaMUST be a valid JSON Schema object with top-leveltype: object. The dialect defaults to JSON Schema 2020-12 when$schemais absent; a different dialect MAY be selected via$schema. - Every
$refMUST resolve within the document. - Every
transports[]entry MUST declaretype;typeMUST be one ofstdio,streamable-http,sse. Astreamable-httporssetransport MUST declare a non-emptyurl. tools[].execution.taskSupport, if present, MUST be one offorbidden,optional,required; a non-forbiddenvalue SHOULD be accompanied by a declaredcapabilities.tasks.- Every
packaging.packages[]entry MUST declareregistryType,identifierandtransport.version, when present, MUST be 1–255 characters and MUST NOT be the stringlatest.registryType: mcpbREQUIRESfileSha256; anyfileSha256value MUST match^[a-f0-9]{64}$. - Every package
transportMUST declaretypeas one ofstdio,streamable-http,sse; whentypeisstreamable-httporsse,urlis REQUIRED and MUST be an absolutehttp(s)URL or begin with a{variable}template reference. - Every
runtimeArguments[]/packageArguments[]entry MUST declaretypeaspositionalornamed. Anamedargument MUST declarename; apositionalargument MUST declarevalueHintorvalue. EveryenvironmentVariables[]entry and every named-input header MUST declarename. - Every
icons[].srcMUST be present and formatted as a URI. Everyicons[].sizes[]value MUST be eitheranyorWxH(for example,48x48). Everyicons[].theme, when present, MUST belightordark. server.repository, when present, MUST declareurlandsource.requiresClientCapabilities, if present, MUST be an array of strings; known values aresampling,elicitation,roots. Unknown values MUST be tolerated.- Unknown
x-*keys MUST be ignored by validators (forward compatibility).
12.1 Conformance & machine validation
Validation happens at two layers:
- Structural layer — the canonical JSON Schema published at
https://mcpdesign.org/schemas/mcpds-1.0.schema.json. It checks document shape and every constraint expressible in JSON Schema: rule 1 (required keys, themcpds: "1.0"constant, at least one transport), thename/inputSchemapresence and the tool-name pattern of rule 4, theurlrequirement of rule 7, thetaskSupportenumeration of rule 8, and rules 9–14 except as noted below. The schema is intentionally permissive (additionalProperties: true) sox-*extensions pass. - Semantic layer — rules 2, 3, 5 and 6, the allowed transport
typevalues of rule 7, thecapabilities.taskscorrelation of rule 8, the recommended-descriptionwarning of rule 4, the tolerance requirements of rule 14 and the extension handling of rule 15 (reverse-DNS naming, cross-collection uniqueness,$refresolution, JSON Schema validity of embedded schemas, and validator behavior). These cannot be expressed in plain JSON Schema and MUST be enforced by tooling on top of structural validation.
A document is conforming only when it passes both layers.
13. Relationship to MCP artifacts
MCPDS sits alongside two artifacts already defined by the MCP ecosystem and overlaps with each only partially:
- The MCP protocol (the
2025-11-25revision) defines the runtime capability layer. A live server exposes its tools, resources and prompts only on demand viatools/list,resources/list,prompts/listandinitialize; there is no standard design-time document for it. - The registry
server.jsondefines the distribution layer — how a server is packaged, transported and launched (packages,remotes,transport,environmentVariables, …). It says nothing about the capabilities a server exposes.
MCPDS unifies both into a single, human-authored design-time file:
| Concern | MCP protocol (runtime) | registry server.json |
MCPDS 1.0 |
|---|---|---|---|
| Service identity | InitializeResult / Implementation |
top-level fields | server |
| Transport | negotiated per connection | packages[].transport / remotes[] |
transports (single section) |
| Auth | OAuth 2.1 / env (per connection) | remotes[].headers for static headers |
auth (server-level) + transports[].headers |
| Capabilities / contract | tools/list, resources/list, prompts/list (runtime only) |
— | tools / resources / prompts |
| Distribution | — | packages / remotes |
packaging.packages + transports (remotes) |
| Schema dialect | JSON Schema 2020-12 | JSON Schema 2020-12 | JSON Schema 2020-12 |
MCPDS is the superset: the only artifact that carries both the distribution
layer (projectable to server.json, §10) and the full capability contract in one
design-time file — before any server code exists.