@zigrivers/scaffold 3.32.1 → 3.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -18
- package/content/guides/knowledge-freshness/.diagrams/diagram-0.svg +1 -1
- package/content/guides/knowledge-freshness/.diagrams/manifest.json +1 -1
- package/content/guides/knowledge-freshness/index.html +9 -5
- package/content/guides/knowledge-freshness/index.md +5 -4
- package/content/guides/pipeline/index.html +2 -2
- package/content/guides/pipeline/index.md +2 -2
- package/content/knowledge/VERSION +1 -1
- package/content/knowledge/backend/backend-api-design.md +21 -3
- package/content/knowledge/backend/backend-architecture.md +21 -3
- package/content/knowledge/backend/backend-async-patterns.md +16 -2
- package/content/knowledge/backend/backend-auth-patterns.md +19 -2
- package/content/knowledge/backend/backend-conventions.md +15 -3
- package/content/knowledge/backend/backend-data-modeling.md +19 -3
- package/content/knowledge/backend/backend-deployment.md +21 -3
- package/content/knowledge/backend/backend-dev-environment.md +13 -3
- package/content/knowledge/backend/backend-fintech-broker-integration.md +14 -2
- package/content/knowledge/backend/backend-fintech-compliance.md +23 -4
- package/content/knowledge/mcp-server/mcp-authentication.md +100 -0
- package/content/knowledge/mcp-server/mcp-deployment-patterns.md +119 -0
- package/content/knowledge/mcp-server/mcp-error-handling.md +131 -0
- package/content/knowledge/mcp-server/mcp-observability.md +125 -0
- package/content/knowledge/mcp-server/mcp-prompt-primitives.md +119 -0
- package/content/knowledge/mcp-server/mcp-protocol-fundamentals.md +130 -0
- package/content/knowledge/mcp-server/mcp-resource-design.md +111 -0
- package/content/knowledge/mcp-server/mcp-sdk-selection.md +136 -0
- package/content/knowledge/mcp-server/mcp-testing-strategies.md +127 -0
- package/content/knowledge/mcp-server/mcp-tool-design.md +125 -0
- package/content/knowledge/mcp-server/mcp-transport-patterns.md +122 -0
- package/content/knowledge/mcp-server/mcp-versioning.md +115 -0
- package/content/methodology/custom-defaults.yml +2 -0
- package/content/methodology/deep.yml +2 -0
- package/content/methodology/mcp-server-overlay.yml +88 -0
- package/content/methodology/mvp.yml +2 -0
- package/content/pipeline/build/multi-agent-resume.md +107 -11
- package/content/pipeline/build/multi-agent-start.md +104 -11
- package/content/pipeline/build/single-agent-resume.md +74 -8
- package/content/pipeline/build/single-agent-start.md +69 -12
- package/content/pipeline/finalization/materialize-plan-to-beads.md +473 -0
- package/content/pipeline/foundation/beads.md +6 -0
- package/content/pipeline/planning/implementation-plan-review.md +25 -0
- package/content/pipeline/planning/implementation-plan.md +75 -1
- package/content/pipeline/specification/mcp-tool-resource-contract.md +77 -0
- package/dist/cli/commands/adopt.d.ts.map +1 -1
- package/dist/cli/commands/adopt.js +33 -1
- package/dist/cli/commands/adopt.js.map +1 -1
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +46 -3
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/init-flag-families.d.ts +6 -1
- package/dist/cli/init-flag-families.d.ts.map +1 -1
- package/dist/cli/init-flag-families.js +59 -2
- package/dist/cli/init-flag-families.js.map +1 -1
- package/dist/cli/init-flag-families.test.js +86 -1
- package/dist/cli/init-flag-families.test.js.map +1 -1
- package/dist/config/schema.d.ts +2310 -126
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +26 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/config/schema.test.js +75 -2
- package/dist/config/schema.test.js.map +1 -1
- package/dist/config/validators/index.d.ts.map +1 -1
- package/dist/config/validators/index.js +2 -0
- package/dist/config/validators/index.js.map +1 -1
- package/dist/config/validators/mcp-server.d.ts +4 -0
- package/dist/config/validators/mcp-server.d.ts.map +1 -0
- package/dist/config/validators/mcp-server.js +37 -0
- package/dist/config/validators/mcp-server.js.map +1 -0
- package/dist/config/validators/mcp-server.test.d.ts +2 -0
- package/dist/config/validators/mcp-server.test.d.ts.map +1 -0
- package/dist/config/validators/mcp-server.test.js +47 -0
- package/dist/config/validators/mcp-server.test.js.map +1 -0
- package/dist/core/assembly/materialize-plan-to-beads-assembly.test.d.ts +2 -0
- package/dist/core/assembly/materialize-plan-to-beads-assembly.test.d.ts.map +1 -0
- package/dist/core/assembly/materialize-plan-to-beads-assembly.test.js +75 -0
- package/dist/core/assembly/materialize-plan-to-beads-assembly.test.js.map +1 -0
- package/dist/e2e/project-type-overlays.test.js +83 -0
- package/dist/e2e/project-type-overlays.test.js.map +1 -1
- package/dist/project/adopt.d.ts.map +1 -1
- package/dist/project/adopt.js +3 -1
- package/dist/project/adopt.js.map +1 -1
- package/dist/project/detectors/coverage.test.js +1 -0
- package/dist/project/detectors/coverage.test.js.map +1 -1
- package/dist/project/detectors/disambiguate.d.ts.map +1 -1
- package/dist/project/detectors/disambiguate.js +6 -1
- package/dist/project/detectors/disambiguate.js.map +1 -1
- package/dist/project/detectors/disambiguate.test.js +18 -0
- package/dist/project/detectors/disambiguate.test.js.map +1 -1
- package/dist/project/detectors/index.d.ts.map +1 -1
- package/dist/project/detectors/index.js +2 -1
- package/dist/project/detectors/index.js.map +1 -1
- package/dist/project/detectors/mcp-server.d.ts +4 -0
- package/dist/project/detectors/mcp-server.d.ts.map +1 -0
- package/dist/project/detectors/mcp-server.js +91 -0
- package/dist/project/detectors/mcp-server.js.map +1 -0
- package/dist/project/detectors/mcp-server.test.d.ts +2 -0
- package/dist/project/detectors/mcp-server.test.d.ts.map +1 -0
- package/dist/project/detectors/mcp-server.test.js +115 -0
- package/dist/project/detectors/mcp-server.test.js.map +1 -0
- package/dist/project/detectors/types.d.ts +6 -2
- package/dist/project/detectors/types.d.ts.map +1 -1
- package/dist/project/detectors/types.js.map +1 -1
- package/dist/types/config.d.ts +8 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/wizard/copy/core.d.ts.map +1 -1
- package/dist/wizard/copy/core.js +4 -0
- package/dist/wizard/copy/core.js.map +1 -1
- package/dist/wizard/copy/index.d.ts.map +1 -1
- package/dist/wizard/copy/index.js +2 -0
- package/dist/wizard/copy/index.js.map +1 -1
- package/dist/wizard/copy/mcp-server.d.ts +3 -0
- package/dist/wizard/copy/mcp-server.d.ts.map +1 -0
- package/dist/wizard/copy/mcp-server.js +40 -0
- package/dist/wizard/copy/mcp-server.js.map +1 -0
- package/dist/wizard/copy/types.d.ts +5 -1
- package/dist/wizard/copy/types.d.ts.map +1 -1
- package/dist/wizard/flags.d.ts +9 -1
- package/dist/wizard/flags.d.ts.map +1 -1
- package/dist/wizard/questions.d.ts +4 -2
- package/dist/wizard/questions.d.ts.map +1 -1
- package/dist/wizard/questions.js +37 -0
- package/dist/wizard/questions.js.map +1 -1
- package/dist/wizard/questions.test.js +107 -0
- package/dist/wizard/questions.test.js.map +1 -1
- package/dist/wizard/wizard.d.ts +3 -2
- package/dist/wizard/wizard.d.ts.map +1 -1
- package/dist/wizard/wizard.js +3 -1
- package/dist/wizard/wizard.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mcp-prompt-primitives
|
|
3
|
+
description: MCP prompts as user-controlled primitives, prompts/list and prompts/get methods, arguments, message roles, and when to use prompts vs tools
|
|
4
|
+
topics: [mcp, prompts, ux, slash-commands, prompt-templates]
|
|
5
|
+
volatility: evolving
|
|
6
|
+
last-reviewed: null
|
|
7
|
+
version-pin: 'MCP spec 2025-11-25'
|
|
8
|
+
sources:
|
|
9
|
+
- url: https://modelcontextprotocol.io/specification/2025-11-25/server/prompts
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Prompts in MCP are structured, reusable message templates that users explicitly select. They are different from tools (model-controlled, automatic) and resources (application-controlled, passive context). Understanding the distinction shapes how you expose server capabilities.
|
|
13
|
+
|
|
14
|
+
## Summary
|
|
15
|
+
|
|
16
|
+
Servers declare prompts via `prompts/list`; clients retrieve a populated prompt via `prompts/get` with filled-in arguments. Each prompt returns a `messages` array with `role`/`content` pairs ready to inject into a conversation. Prompts are **user-controlled** — they appear as slash commands or UI-selectable templates. Declare `prompts: { listChanged: true }` if the available prompts can change at runtime. Use prompts when a user needs to explicitly invoke a repeatable interaction pattern; use tools when the LLM should autonomously execute an action.
|
|
17
|
+
|
|
18
|
+
## Deep Guidance
|
|
19
|
+
|
|
20
|
+
### Prompts as user-controlled primitives
|
|
21
|
+
|
|
22
|
+
The defining characteristic of MCP prompts is that they are **user-initiated**, not model-initiated. The host surfaces them as discoverable, selectable commands — typically slash commands in a chat UI, menu items in an IDE, or shortcut buttons in a desktop app. The user explicitly chooses a prompt, fills in any arguments, and the resulting message sequence is injected into the conversation.
|
|
23
|
+
|
|
24
|
+
This contrasts sharply with tools (which the LLM invokes automatically based on context) and resources (which the host includes in context based on application logic). Prompts are neither automatic nor passive — they are deliberate user actions.
|
|
25
|
+
|
|
26
|
+
### prompts/list — discovery
|
|
27
|
+
|
|
28
|
+
`prompts/list` returns available prompt templates. Each entry includes `name`, optional `title` (display name), optional `description`, and an optional `arguments` array:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"prompts": [
|
|
33
|
+
{
|
|
34
|
+
"name": "code_review",
|
|
35
|
+
"title": "Request Code Review",
|
|
36
|
+
"description": "Ask the model to review code for quality, bugs, and improvements",
|
|
37
|
+
"arguments": [
|
|
38
|
+
{
|
|
39
|
+
"name": "code",
|
|
40
|
+
"description": "The source code to review",
|
|
41
|
+
"required": true
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "language",
|
|
45
|
+
"description": "Programming language, e.g. TypeScript",
|
|
46
|
+
"required": false
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`prompts/list` supports cursor-based pagination. If the server can add or remove prompts dynamically, declare `prompts: { listChanged: true }` and send `notifications/prompts/list_changed` when the set changes.
|
|
55
|
+
|
|
56
|
+
### prompts/get — retrieval and argument injection
|
|
57
|
+
|
|
58
|
+
`prompts/get` takes a `name` and an `arguments` map (matching the declared argument names to concrete values). The server returns a `messages` array — fully formed conversation messages ready to send to an LLM:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"method": "prompts/get",
|
|
63
|
+
"params": {
|
|
64
|
+
"name": "code_review",
|
|
65
|
+
"arguments": {
|
|
66
|
+
"code": "function add(a, b) { return a + b }",
|
|
67
|
+
"language": "JavaScript"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Response:
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"result": {
|
|
77
|
+
"description": "Code review prompt for JavaScript",
|
|
78
|
+
"messages": [
|
|
79
|
+
{
|
|
80
|
+
"role": "user",
|
|
81
|
+
"content": {
|
|
82
|
+
"type": "text",
|
|
83
|
+
"text": "Please review this JavaScript code for quality, potential bugs, and improvements:\n\nfunction add(a, b) { return a + b }"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The server does the argument substitution and structures the messages — the client does not template-expand arguments itself. This lets the server implement sophisticated logic: injecting relevant resources, fetching live data to embed in the prompt, or constructing multi-turn conversation starters.
|
|
92
|
+
|
|
93
|
+
### Message roles and content types
|
|
94
|
+
|
|
95
|
+
Prompt messages support `role: "user"` and `role: "assistant"`. Use `"user"` for the human turn and `"assistant"` for pre-seeded model responses (useful for few-shot examples or to prime a specific response style).
|
|
96
|
+
|
|
97
|
+
Content types within messages match tool result content types: `text`, `image`, `audio`, and `resource` (embedded resource). Use embedded resources to include relevant file contents, API data, or database records directly in the prompt without requiring a separate `resources/read` call.
|
|
98
|
+
|
|
99
|
+
### When to use prompts vs tools vs resources
|
|
100
|
+
|
|
101
|
+
These three primitives answer different questions:
|
|
102
|
+
|
|
103
|
+
| Primitive | Controlled by | When to use |
|
|
104
|
+
|-----------|--------------|-------------|
|
|
105
|
+
| **Prompt** | User (explicit selection) | Repeatable interaction patterns a user deliberately invokes: code review, explain concept, generate commit message |
|
|
106
|
+
| **Tool** | LLM (autonomous invocation) | Actions the LLM should take as part of its reasoning: fetch data, write file, call API |
|
|
107
|
+
| **Resource** | Application (contextual inclusion) | Background context the LLM should be aware of: current file, open database schema, active configuration |
|
|
108
|
+
|
|
109
|
+
A prompt for "generate a commit message" is appropriate — the user explicitly decides to invoke this pattern. A tool for "get_current_file" is appropriate — the LLM decides when fetching the current file is relevant to its task. A resource for "current_project_schema" is appropriate — the host automatically includes the schema as background context.
|
|
110
|
+
|
|
111
|
+
Avoid turning every capability into a prompt. Prompts that should be tools (because the LLM should invoke them automatically) create friction. Prompts that should be resources (because they're background context) waste a user action.
|
|
112
|
+
|
|
113
|
+
### Argument design
|
|
114
|
+
|
|
115
|
+
Keep prompt arguments minimal. Each required argument is a burden on the user. Design prompts that work well with few arguments and use the server's access to context (active files, project config, authenticated user state) to fill in the rest.
|
|
116
|
+
|
|
117
|
+
Use required arguments for inputs the server genuinely cannot infer: the code to review, the ticket ID to reference. Use optional arguments with sensible defaults for refinements: language hint, verbosity level, output format.
|
|
118
|
+
|
|
119
|
+
Validate all arguments before building the messages. Return a JSON-RPC `-32602` (invalid params) error for missing required arguments or invalid values, with a clear message identifying which argument is problematic and what a valid value looks like.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mcp-protocol-fundamentals
|
|
3
|
+
description: MCP client/server model, JSON-RPC 2.0 message format, capability negotiation, initialize handshake, connection lifecycle, and host/client/server roles
|
|
4
|
+
topics: [mcp, protocol, json-rpc, lifecycle, capability-negotiation]
|
|
5
|
+
volatility: fast-moving
|
|
6
|
+
last-reviewed: null
|
|
7
|
+
version-pin: 'MCP spec 2025-11-25'
|
|
8
|
+
sources:
|
|
9
|
+
- url: https://modelcontextprotocol.io/specification/2025-11-25/
|
|
10
|
+
- url: https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle
|
|
11
|
+
- url: https://www.jsonrpc.org/specification
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
The Model Context Protocol (MCP) is a JSON-RPC 2.0 based protocol that standardizes how LLM applications connect to external data sources and tools. Understanding the three-role model and the initialize handshake is prerequisite knowledge for every other MCP concept.
|
|
15
|
+
|
|
16
|
+
## Summary
|
|
17
|
+
|
|
18
|
+
MCP uses JSON-RPC 2.0 over a stateful transport. Three roles: **hosts** (LLM applications that initiate connections), **clients** (connectors within the host), and **servers** (services that expose capabilities). The connection lifecycle has three phases — initialization, operation, and shutdown — and always begins with an `initialize` request/response exchange followed by an `initialized` notification. Capabilities are declared during initialization; neither side may use a capability it did not declare.
|
|
19
|
+
|
|
20
|
+
## Deep Guidance
|
|
21
|
+
|
|
22
|
+
### Three-role model
|
|
23
|
+
|
|
24
|
+
The MCP architecture separates concerns into three distinct roles:
|
|
25
|
+
|
|
26
|
+
- **Host**: The LLM application (e.g., Claude Desktop, an IDE plugin) that orchestrates connections. The host owns the user interface and decides which servers to connect to.
|
|
27
|
+
- **Client**: A connector embedded in the host that manages a single server connection, handles the protocol lifecycle, and mediates capability negotiation.
|
|
28
|
+
- **Server**: An independent process or service that exposes tools, resources, or prompts. A server has no knowledge of other servers the client may be connected to.
|
|
29
|
+
|
|
30
|
+
One host typically manages multiple client-server pairs. Each server connection is isolated — servers do not communicate with each other.
|
|
31
|
+
|
|
32
|
+
### JSON-RPC 2.0 message format
|
|
33
|
+
|
|
34
|
+
All MCP messages are JSON-RPC 2.0 objects, UTF-8 encoded. Three message types:
|
|
35
|
+
|
|
36
|
+
**Requests** (expect a response):
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"jsonrpc": "2.0",
|
|
40
|
+
"id": 1,
|
|
41
|
+
"method": "tools/list",
|
|
42
|
+
"params": {}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Responses** (reply to a request):
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"jsonrpc": "2.0",
|
|
50
|
+
"id": 1,
|
|
51
|
+
"result": { "tools": [] }
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Notifications** (no response expected, no `id` field):
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"jsonrpc": "2.0",
|
|
59
|
+
"method": "notifications/initialized"
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Error responses use a standard error object with `code` (integer) and `message` (string). Standard JSON-RPC error codes apply: `-32700` (parse error), `-32600` (invalid request), `-32601` (method not found), `-32602` (invalid params), `-32603` (internal error).
|
|
64
|
+
|
|
65
|
+
### The initialize handshake
|
|
66
|
+
|
|
67
|
+
The `initialize` request MUST be the first message sent by the client. It carries the protocol version the client supports (should be the latest), the client's capabilities, and client implementation information. The server responds with its own protocol version, capabilities, and an optional `instructions` string for the client.
|
|
68
|
+
|
|
69
|
+
After a successful response, the client MUST send an `initialized` notification (`notifications/initialized`) to signal readiness. Neither side should send substantive requests before this exchange completes — the only exception is ping messages.
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"jsonrpc": "2.0",
|
|
74
|
+
"id": 1,
|
|
75
|
+
"method": "initialize",
|
|
76
|
+
"params": {
|
|
77
|
+
"protocolVersion": "2025-11-25",
|
|
78
|
+
"capabilities": {
|
|
79
|
+
"roots": { "listChanged": true },
|
|
80
|
+
"sampling": {},
|
|
81
|
+
"elicitation": {}
|
|
82
|
+
},
|
|
83
|
+
"clientInfo": { "name": "MyClient", "version": "1.0.0" }
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The server responds with its capabilities. Example server capability declaration:
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"protocolVersion": "2025-11-25",
|
|
92
|
+
"capabilities": {
|
|
93
|
+
"tools": { "listChanged": true },
|
|
94
|
+
"resources": { "subscribe": true, "listChanged": true },
|
|
95
|
+
"prompts": { "listChanged": true },
|
|
96
|
+
"logging": {}
|
|
97
|
+
},
|
|
98
|
+
"serverInfo": { "name": "MyServer", "version": "1.0.0" }
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Version negotiation
|
|
103
|
+
|
|
104
|
+
The client sends the latest protocol version it supports. If the server supports it, it echoes the same version. If the server does not support the requested version, it responds with the latest version it does support. If the client cannot handle the server's version, it should disconnect. The currently active spec version is `2025-11-25`; clients sending this should receive it back from any compliant modern server.
|
|
105
|
+
|
|
106
|
+
### Capability negotiation
|
|
107
|
+
|
|
108
|
+
Capabilities declared during `initialize` govern which protocol features are available for the session. Key capability categories:
|
|
109
|
+
|
|
110
|
+
| Side | Capability | Enables |
|
|
111
|
+
|--------|---------------|-------------------------------------------------|
|
|
112
|
+
| Server | `tools` | `tools/list`, `tools/call` |
|
|
113
|
+
| Server | `resources` | `resources/list`, `resources/read`, subscriptions |
|
|
114
|
+
| Server | `prompts` | `prompts/list`, `prompts/get` |
|
|
115
|
+
| Server | `logging` | Log message notifications to client |
|
|
116
|
+
| Client | `sampling` | Server-initiated LLM sampling requests |
|
|
117
|
+
| Client | `roots` | Server can query filesystem root boundaries |
|
|
118
|
+
| Client | `elicitation` | Server can request additional info from user |
|
|
119
|
+
|
|
120
|
+
Sub-capabilities like `listChanged` (support for list-change notifications) and `subscribe` (resources only, per-resource change subscriptions) are nested within their parent capability. A server that declares `tools: { listChanged: true }` MUST send `notifications/tools/list_changed` when its tool set changes.
|
|
121
|
+
|
|
122
|
+
### Lifecycle phases
|
|
123
|
+
|
|
124
|
+
1. **Initialization**: `initialize` request → server response → `initialized` notification. No tool/resource/prompt calls before this completes.
|
|
125
|
+
2. **Operation**: Normal message exchange. Both sides respect negotiated capabilities.
|
|
126
|
+
3. **Shutdown**: For stdio, the client closes the server's stdin and waits for the process to exit (sending `SIGTERM` then `SIGKILL` if needed). For HTTP, closing connections signals shutdown. No formal shutdown request message exists — transport-level signals are used.
|
|
127
|
+
|
|
128
|
+
### Timeouts and error handling
|
|
129
|
+
|
|
130
|
+
Implementations should set timeouts on all sent requests to prevent hung connections. When a timeout fires, send a `CancelledNotification` for the pending request ID. Progress notifications can optionally reset a timeout clock, but a hard maximum timeout should always be enforced regardless. Protocol version mismatch, required capability negotiation failure, and request timeouts are the standard error cases to handle at startup.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mcp-resource-design
|
|
3
|
+
description: MCP resource URIs, URI templates (RFC 6570), MIME types, resources/list + resources/read + resources/subscribe, listChanged notifications, annotations, pagination
|
|
4
|
+
topics: [mcp, resources, uri-templates, mime-types, subscriptions]
|
|
5
|
+
volatility: evolving
|
|
6
|
+
last-reviewed: null
|
|
7
|
+
version-pin: 'MCP spec 2025-11-25'
|
|
8
|
+
sources:
|
|
9
|
+
- url: https://modelcontextprotocol.io/specification/2025-11-25/server/resources
|
|
10
|
+
- url: https://datatracker.ietf.org/doc/html/rfc6570
|
|
11
|
+
- url: https://datatracker.ietf.org/doc/html/rfc3986
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
Resources are the MCP primitive for exposing data that provides context to LLMs — files, database records, API responses, or any addressable content. Unlike tools, resources are application-driven (the host decides what to expose), not model-driven.
|
|
15
|
+
|
|
16
|
+
## Summary
|
|
17
|
+
|
|
18
|
+
Each resource has a `URI` as its unique identifier, a `name`, optional `description` and `mimeType`. Clients discover resources via `resources/list` and fetch content via `resources/read`. Parameterized resources use URI templates (RFC 6570) via `resources/templates/list`. Servers declare `resources: { subscribe: true }` to support `resources/subscribe` for change notifications. Both list and content endpoints support cursor-based pagination.
|
|
19
|
+
|
|
20
|
+
## Deep Guidance
|
|
21
|
+
|
|
22
|
+
### Resource URI design
|
|
23
|
+
|
|
24
|
+
Every resource MUST have a globally unique URI conforming to RFC 3986. The URI scheme signals the resource's nature:
|
|
25
|
+
|
|
26
|
+
- `file:///absolute/path/to/file.txt` — filesystem-like resources (the resource need not be a real file; use this for any content that behaves like a file).
|
|
27
|
+
- `https://example.com/api/data` — web-fetchable resources. Use only when the client can fetch the URL directly; otherwise use a custom scheme.
|
|
28
|
+
- `git://repo/path/to/file@main` — git-versioned content.
|
|
29
|
+
- Custom schemes (e.g., `db://`, `github://`, `slack://`) — use freely for domain-specific resources. Custom schemes MUST conform to RFC 3986.
|
|
30
|
+
|
|
31
|
+
Avoid embedding credentials, tokens, or session state in URIs — URIs are logged and passed between systems. Keep URIs stable across server restarts for resources that represent persistent entities.
|
|
32
|
+
|
|
33
|
+
### Static resources vs URI templates
|
|
34
|
+
|
|
35
|
+
**Static resources** have fully-specified URIs and appear in `resources/list`. Use for a bounded, known set of resources (a fixed set of config files, a fixed list of database tables, etc.).
|
|
36
|
+
|
|
37
|
+
**URI templates** (RFC 6570) use `resources/templates/list` and allow parameterized resource access. The `uriTemplate` field contains an RFC 6570 Level 1 template:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"uriTemplate": "github://repos/{owner}/{repo}/issues/{number}",
|
|
42
|
+
"name": "GitHub Issue",
|
|
43
|
+
"description": "A specific GitHub issue by owner, repo, and number",
|
|
44
|
+
"mimeType": "application/json"
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The template parameters (`owner`, `repo`, `number`) can be auto-completed by the server via the completions API if declared as server capability. Clients expand the template with concrete values and use the result as the URI in a `resources/read` request. Use templates for unbounded or large collections (user files, database rows, API records) where listing all resources statically is impractical.
|
|
49
|
+
|
|
50
|
+
### resources/list and pagination
|
|
51
|
+
|
|
52
|
+
`resources/list` returns available static resources. The response includes a `resources` array and an optional `nextCursor`. If `nextCursor` is present, pass it as `cursor` in the next request to get the next page. Always handle pagination — a server serving a filesystem may return thousands of entries.
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"method": "resources/list",
|
|
57
|
+
"params": { "cursor": "eyJwYWdlIjogMn0=" }
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Response resources include `uri`, `name`, optional `title`, optional `description`, optional `mimeType`, and optional `size` (bytes). The `mimeType` is advisory — always re-check the `mimeType` in the actual content returned by `resources/read`.
|
|
62
|
+
|
|
63
|
+
### resources/read and content types
|
|
64
|
+
|
|
65
|
+
`resources/read` takes a `uri` and returns a `contents` array. Each content item carries:
|
|
66
|
+
|
|
67
|
+
- For text: `{ "uri": "...", "mimeType": "text/plain", "text": "..." }`
|
|
68
|
+
- For binary: `{ "uri": "...", "mimeType": "image/png", "blob": "<base64>" }`
|
|
69
|
+
|
|
70
|
+
A single read can return multiple content items if the URI expands to multiple files (e.g., a directory URI). The `blob` field contains standard base64-encoded binary data; the `text` field is a UTF-8 string.
|
|
71
|
+
|
|
72
|
+
Standard MIME type conventions: `text/plain`, `text/markdown`, `application/json`, `application/octet-stream` for unknown binary, `image/png`, `image/jpeg`, `text/html`. Use `inode/directory` (XDG MIME) for directory resources without a standard MIME type.
|
|
73
|
+
|
|
74
|
+
### Subscriptions
|
|
75
|
+
|
|
76
|
+
Declare `resources: { subscribe: true }` to support per-resource change notifications. The flow:
|
|
77
|
+
|
|
78
|
+
1. Client sends `resources/subscribe` with a specific `uri`.
|
|
79
|
+
2. Server responds with success (empty result).
|
|
80
|
+
3. When the resource changes, server sends `notifications/resources/updated` with the `uri`.
|
|
81
|
+
4. Client re-fetches the resource with `resources/read`.
|
|
82
|
+
|
|
83
|
+
Subscriptions are stateful — track them in the server. If a client disconnects and reconnects, subscriptions are lost and must be re-established. Implement subscriptions when resources represent live data (open files in an editor, real-time database records, live API state).
|
|
84
|
+
|
|
85
|
+
### listChanged notifications
|
|
86
|
+
|
|
87
|
+
If the set of available resources can change (files added/removed, database tables created/dropped), declare `resources: { listChanged: true }` and send `notifications/resources/list_changed` when the list changes. The client re-issues `resources/list` to refresh. This is separate from subscriptions: `listChanged` is for the catalog, subscriptions are for individual resource content.
|
|
88
|
+
|
|
89
|
+
### Annotations
|
|
90
|
+
|
|
91
|
+
Resources support optional `annotations` that hint at audience and priority:
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"annotations": {
|
|
96
|
+
"audience": ["assistant"],
|
|
97
|
+
"priority": 0.9,
|
|
98
|
+
"lastModified": "2025-06-18T10:00:00Z"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- `audience`: `["user"]`, `["assistant"]`, or `["user", "assistant"]`. Use `["assistant"]` for machine-readable data the LLM should process but not display to users. Use `["user"]` for human-readable content.
|
|
104
|
+
- `priority`: 0.0 to 1.0. Higher values indicate more important context to include when token budgets are limited.
|
|
105
|
+
- `lastModified`: ISO 8601 timestamp. Enables clients to sort by recency or skip stale resources.
|
|
106
|
+
|
|
107
|
+
Annotations appear on the resource list entry, the content item, or both. They are hints — clients may ignore them.
|
|
108
|
+
|
|
109
|
+
### Error handling
|
|
110
|
+
|
|
111
|
+
Standard JSON-RPC error codes for resource operations: `-32002` for resource not found, `-32603` for internal server errors. Always return a proper JSON-RPC error when a `resources/read` URI does not exist — do not return an empty `contents` array. Validate all incoming URIs for scheme and path safety before accessing the underlying data source.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mcp-sdk-selection
|
|
3
|
+
description: TypeScript @modelcontextprotocol/sdk vs Python SDK and FastMCP — trade-offs, SDK patterns, McpServer class, decorator-based registration, and when to use each
|
|
4
|
+
topics: [mcp, sdk, typescript, python, fastmcp]
|
|
5
|
+
volatility: fast-moving
|
|
6
|
+
last-reviewed: null
|
|
7
|
+
version-pin: 'MCP spec 2025-11-25; @modelcontextprotocol/sdk 1.x; mcp[cli] Python 1.2.0+'
|
|
8
|
+
sources:
|
|
9
|
+
- url: https://modelcontextprotocol.io/docs/develop/build-server
|
|
10
|
+
- url: https://github.com/modelcontextprotocol/typescript-sdk
|
|
11
|
+
- url: https://github.com/modelcontextprotocol/python-sdk
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
Three SDK options cover the majority of MCP server implementations: the official TypeScript SDK (`@modelcontextprotocol/sdk`), the official Python SDK (`mcp`), and FastMCP (a higher-level Python wrapper bundled with the Python SDK). Choose based on your team's language, the server's complexity, and how much boilerplate you want to manage.
|
|
15
|
+
|
|
16
|
+
## Summary
|
|
17
|
+
|
|
18
|
+
**TypeScript SDK**: use for Node.js servers, teams with TypeScript expertise, or when integrating tightly with the JS/TS ecosystem. The `McpServer` high-level API covers most use cases; the lower-level `Server` class gives full control for edge cases. **Python SDK with FastMCP**: use for Python teams; FastMCP uses decorators and type hints to auto-generate tool/resource/prompt definitions, requiring minimal boilerplate. **FastMCP** is the recommended starting point for Python — it is bundled in the official `mcp[cli]` package as `mcp.server.fastmcp`. Both SDKs handle transport setup, JSON-RPC framing, and capability negotiation automatically.
|
|
19
|
+
|
|
20
|
+
## Deep Guidance
|
|
21
|
+
|
|
22
|
+
### TypeScript SDK (@modelcontextprotocol/sdk)
|
|
23
|
+
|
|
24
|
+
Install: `npm install @modelcontextprotocol/sdk`
|
|
25
|
+
|
|
26
|
+
The TypeScript SDK offers two server APIs:
|
|
27
|
+
|
|
28
|
+
**McpServer (high-level, recommended)**: Handles transport setup, capability negotiation, and message routing. Register tools, resources, and prompts with typed handler functions:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
32
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
33
|
+
import { z } from 'zod'
|
|
34
|
+
|
|
35
|
+
const server = new McpServer({
|
|
36
|
+
name: 'my-server',
|
|
37
|
+
version: '1.0.0',
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
server.tool('get_weather', 'Get current weather for a location', {
|
|
41
|
+
location: z.string().describe('City name or zip code'),
|
|
42
|
+
}, async ({ location }) => {
|
|
43
|
+
// fetch weather ...
|
|
44
|
+
return {
|
|
45
|
+
content: [{ type: 'text', text: `Weather for ${location}: Sunny, 72°F` }],
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const transport = new StdioServerTransport()
|
|
50
|
+
await server.connect(transport)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
For Streamable HTTP, use `StreamableHTTPServerTransport` from `@modelcontextprotocol/sdk/server/streamableHttp.js`.
|
|
54
|
+
|
|
55
|
+
**Server (low-level)**: Use when you need fine-grained control over request handling, custom middleware, or capabilities not yet abstracted by McpServer. Requires manually registering handlers for each method (`setRequestHandler`, `setNotificationHandler`). Useful for implementing servers that proxy or aggregate other MCP servers.
|
|
56
|
+
|
|
57
|
+
The TypeScript SDK uses Zod schemas for input validation — the `z.object({ ... })` schema passed to `server.tool()` becomes the JSON Schema `inputSchema` automatically.
|
|
58
|
+
|
|
59
|
+
### Python SDK with FastMCP
|
|
60
|
+
|
|
61
|
+
Install: `uv add "mcp[cli]"` (includes FastMCP) or `pip install "mcp[cli]"`
|
|
62
|
+
|
|
63
|
+
**FastMCP** is the high-level Python API. It derives tool definitions from function signatures, type hints, and docstrings — eliminating most boilerplate:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from mcp.server.fastmcp import FastMCP
|
|
67
|
+
|
|
68
|
+
mcp = FastMCP("my-server")
|
|
69
|
+
|
|
70
|
+
@mcp.tool()
|
|
71
|
+
async def get_weather(location: str) -> str:
|
|
72
|
+
"""Get current weather for a location.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
location: City name or zip code, e.g. 'New York' or '10001'
|
|
76
|
+
"""
|
|
77
|
+
# fetch weather ...
|
|
78
|
+
return f"Weather for {location}: Sunny, 72°F"
|
|
79
|
+
|
|
80
|
+
if __name__ == "__main__":
|
|
81
|
+
mcp.run() # defaults to stdio transport
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
FastMCP generates the JSON Schema `inputSchema` from the function's type annotations. The docstring becomes the tool's `description`. Argument descriptions come from the `Args:` section of the docstring. This convention-over-configuration approach makes FastMCP the fastest way to get a Python MCP server running.
|
|
85
|
+
|
|
86
|
+
For resources:
|
|
87
|
+
```python
|
|
88
|
+
@mcp.resource("file://config/{name}")
|
|
89
|
+
async def get_config(name: str) -> str:
|
|
90
|
+
"""Read a configuration file by name."""
|
|
91
|
+
return Path(f"/etc/myapp/{name}.json").read_text()
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
For prompts:
|
|
95
|
+
```python
|
|
96
|
+
@mcp.prompt()
|
|
97
|
+
async def review_code(code: str, language: str = "Python") -> list[dict]:
|
|
98
|
+
"""Generate a code review prompt."""
|
|
99
|
+
return [{"role": "user", "content": f"Review this {language} code:\n{code}"}]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The lower-level Python SDK (`mcp.server.Server`) exists for the same fine-grained use cases as the TypeScript `Server` class.
|
|
103
|
+
|
|
104
|
+
### SDK comparison
|
|
105
|
+
|
|
106
|
+
| Dimension | TypeScript SDK | Python + FastMCP |
|
|
107
|
+
|-----------|---------------|------------------|
|
|
108
|
+
| Boilerplate | Low (McpServer) | Minimal (decorators) |
|
|
109
|
+
| Type safety | Strong (Zod + TS) | Good (type hints) |
|
|
110
|
+
| Schema generation | Zod → JSON Schema | Type hints → JSON Schema |
|
|
111
|
+
| Ecosystem fit | Node.js, VS Code, Electron | Data science, scripting, ML |
|
|
112
|
+
| Async model | Promise/async-await | asyncio (FastMCP is async-first) |
|
|
113
|
+
| Deployment | npm package or compiled JS | uvx, pip, Docker |
|
|
114
|
+
| Community servers | Most reference servers in TS | Growing Python ecosystem |
|
|
115
|
+
|
|
116
|
+
### Choosing between SDKs
|
|
117
|
+
|
|
118
|
+
**Choose TypeScript** when: your team is primarily TypeScript/JavaScript, you're building a VS Code extension or Electron app that embeds an MCP server, you need deep integration with npm ecosystem tooling, or the official reference implementation is your guide.
|
|
119
|
+
|
|
120
|
+
**Choose Python + FastMCP** when: your team is primarily Python, you're wrapping existing Python services or data science tools, you want the fastest prototyping path, or you need to integrate with Python-specific libraries (pandas, SQLAlchemy, LangChain, etc.).
|
|
121
|
+
|
|
122
|
+
**Avoid rolling your own transport layer** regardless of language — the SDKs handle the nuances of JSON-RPC framing, keep-alive, session management, and error formatting correctly. The only reason to implement transport handling manually is when targeting a runtime or language for which no SDK exists yet (C#, Rust, Go all have community SDKs; check the MCP GitHub org before writing your own).
|
|
123
|
+
|
|
124
|
+
### Logging caution in Python stdio servers
|
|
125
|
+
|
|
126
|
+
FastMCP and the Python SDK's stdio transport are sensitive to stdout pollution. Never use `print()` in a stdio server without redirecting to stderr:
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
import sys
|
|
130
|
+
# WRONG — corrupts protocol stream:
|
|
131
|
+
print("Debug: processing request")
|
|
132
|
+
# CORRECT:
|
|
133
|
+
print("Debug: processing request", file=sys.stderr)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Use Python's `logging` module configured with a `StreamHandler(sys.stderr)` — it defaults to stderr and is safe for stdio servers.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mcp-testing-strategies
|
|
3
|
+
description: MCP server testing — MCP Inspector for interactive testing, client mocks, unit tests for handlers, protocol compliance tests, and integration testing patterns
|
|
4
|
+
topics: [mcp, testing, mcp-inspector, protocol-compliance, integration-testing]
|
|
5
|
+
volatility: stable
|
|
6
|
+
last-reviewed: null
|
|
7
|
+
version-pin: null
|
|
8
|
+
sources:
|
|
9
|
+
- url: https://modelcontextprotocol.io/docs/tools/inspector
|
|
10
|
+
- url: https://github.com/modelcontextprotocol/inspector
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
Testing MCP servers requires verifying two distinct layers: the protocol layer (correct JSON-RPC framing, capability negotiation, lifecycle) and the domain layer (tool logic, resource content, prompt rendering). The MCP Inspector handles the protocol layer interactively; unit and integration tests handle the domain layer.
|
|
14
|
+
|
|
15
|
+
## Summary
|
|
16
|
+
|
|
17
|
+
The **MCP Inspector** (`npx @modelcontextprotocol/inspector`) is the primary interactive testing tool — it connects to your server, shows all protocol messages, and lets you invoke tools, browse resources, and test prompts. For automated testing: unit-test individual tool/resource/prompt handler functions directly (no transport); write integration tests using an in-process SDK client against the server; use protocol compliance tests to verify capability negotiation and error responses. Test `isError` paths explicitly — they are easy to overlook.
|
|
18
|
+
|
|
19
|
+
## Deep Guidance
|
|
20
|
+
|
|
21
|
+
### MCP Inspector for interactive testing
|
|
22
|
+
|
|
23
|
+
The MCP Inspector is an interactive browser-based UI that connects to an MCP server and exposes all three capability types. Run it without installation:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Test a local stdio server
|
|
27
|
+
npx @modelcontextprotocol/inspector node path/to/server/index.js
|
|
28
|
+
|
|
29
|
+
# Test a server installed via npm
|
|
30
|
+
npx -y @modelcontextprotocol/inspector npx @modelcontextprotocol/server-filesystem /tmp
|
|
31
|
+
|
|
32
|
+
# Test a Python server
|
|
33
|
+
npx @modelcontextprotocol/inspector uvx mcp-server-myapp --config myapp.json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The Inspector opens in your browser and provides:
|
|
37
|
+
|
|
38
|
+
- **Connection pane**: Select transport, customize command-line arguments and environment variables.
|
|
39
|
+
- **Tools tab**: Lists all declared tools, shows schemas, lets you invoke tools with custom input, displays results including `isError` responses.
|
|
40
|
+
- **Resources tab**: Lists static resources and templates, shows metadata, lets you read resource content, tests subscriptions.
|
|
41
|
+
- **Prompts tab**: Lists prompt templates, shows arguments, lets you invoke `prompts/get` with custom argument values, previews generated messages.
|
|
42
|
+
- **Notifications pane**: Shows all server log messages and notifications in real time.
|
|
43
|
+
|
|
44
|
+
**Development workflow with Inspector:**
|
|
45
|
+
1. Start development → launch Inspector with your server → verify connectivity and capability negotiation.
|
|
46
|
+
2. Add a tool → verify it appears in the Tools tab with the correct schema → test with valid and invalid inputs.
|
|
47
|
+
3. Implement error paths → verify `isError: true` responses display correctly.
|
|
48
|
+
4. Test edge cases: empty inputs, maximum-size inputs, concurrent calls, subscription behavior.
|
|
49
|
+
|
|
50
|
+
Use the Inspector as your first debugging stop before writing any automated tests — it shows the full protocol exchange and makes misconfigurations immediately visible.
|
|
51
|
+
|
|
52
|
+
### Unit testing handler functions
|
|
53
|
+
|
|
54
|
+
The best structure for testability isolates tool/resource/prompt logic from transport concerns. Extract handler functions that take typed inputs and return typed outputs, then test them without any MCP transport:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// handlers/weather.ts — testable in isolation
|
|
58
|
+
export async function getWeatherHandler(location: string): Promise<string> {
|
|
59
|
+
const data = await fetchWeatherApi(location)
|
|
60
|
+
return formatWeatherResponse(data)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// weather.test.ts
|
|
64
|
+
import { getWeatherHandler } from './handlers/weather.js'
|
|
65
|
+
it('returns formatted weather', async () => {
|
|
66
|
+
const result = await getWeatherHandler('New York')
|
|
67
|
+
expect(result).toContain('Temperature')
|
|
68
|
+
})
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The MCP registration is a thin wrapper:
|
|
72
|
+
```typescript
|
|
73
|
+
server.tool('get_weather', 'Get current weather', { location: z.string() },
|
|
74
|
+
async ({ location }) => ({
|
|
75
|
+
content: [{ type: 'text', text: await getWeatherHandler(location) }],
|
|
76
|
+
})
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This pattern keeps the bulk of your logic in ordinary functions that are fast to test without spinning up a transport.
|
|
81
|
+
|
|
82
|
+
### Protocol compliance tests
|
|
83
|
+
|
|
84
|
+
Write tests that verify correct JSON-RPC behavior at the protocol level. Use the SDK's in-process transport (TypeScript: `InMemoryTransport`; Python: available via the SDK's testing utilities) to connect a test client directly to the server:
|
|
85
|
+
|
|
86
|
+
Key protocol behaviors to test:
|
|
87
|
+
- `initialize` returns declared capabilities matching what the server actually supports.
|
|
88
|
+
- Calling `tools/call` with an unknown tool name returns a protocol error `-32602` (not `isError`) — the request could not be dispatched at all.
|
|
89
|
+
- Calling `tools/call` with arguments that fail the tool's `inputSchema` or business validation returns `isError: true` (SEP-1303), NOT `-32602` — the tool dispatched but the inputs were invalid, so the model can self-correct.
|
|
90
|
+
- Tools that fail domain-level (API errors, rate limits, resource not found) return `isError: true`, not a JSON-RPC error.
|
|
91
|
+
- `resources/read` with a nonexistent URI returns `-32002`.
|
|
92
|
+
- `prompts/get` with a missing required argument returns `-32602` (structural dispatch failure, not a tool execution).
|
|
93
|
+
- `notifications/tools/list_changed` is sent when the tool list changes (if `listChanged: true` was declared).
|
|
94
|
+
|
|
95
|
+
### Testing isError paths
|
|
96
|
+
|
|
97
|
+
`isError` paths are frequently untested because they require mocking external failures. Make them explicit:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
it('returns isError when API rate limited', async () => {
|
|
101
|
+
mockWeatherApi.mockRejectedValue(new RateLimitError('rate limited'))
|
|
102
|
+
const result = await callTool(server, 'get_weather', { location: 'NYC' })
|
|
103
|
+
expect(result.isError).toBe(true)
|
|
104
|
+
expect(result.content[0].text).toContain('rate limit')
|
|
105
|
+
})
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Test both `isError: true` AND that the error message is actionable (mentions what failed, not just "error occurred").
|
|
109
|
+
|
|
110
|
+
### Integration testing
|
|
111
|
+
|
|
112
|
+
Integration tests connect a real client (or SDK client) to the server over the actual transport:
|
|
113
|
+
|
|
114
|
+
**For stdio servers**: spawn the server process in the test, connect via stdio, run the initialization handshake, invoke tools/resources/prompts, and verify results. Tear down the process after each test suite.
|
|
115
|
+
|
|
116
|
+
**For HTTP servers**: start the server on a random port, make HTTP requests using the SDK client or raw HTTP, verify both successful responses and error cases. Use `supertest` (Node.js) or `httpx` (Python) for HTTP-level assertions alongside the MCP client for protocol-level assertions.
|
|
117
|
+
|
|
118
|
+
**For resource subscriptions**: test that `notifications/resources/updated` is sent when the underlying data changes, and that a subsequent `resources/read` returns the new content.
|
|
119
|
+
|
|
120
|
+
### Test environment setup
|
|
121
|
+
|
|
122
|
+
Avoid relying on real external APIs in unit or integration tests. Mock or stub all external calls. For testing servers that wrap external services (GitHub, databases, cloud APIs), use:
|
|
123
|
+
- Test doubles (mocks) at the HTTP level (e.g., `nock` for Node.js, `responses` for Python).
|
|
124
|
+
- Sandbox/test environments of the external service if a mock is too complex.
|
|
125
|
+
- Recorded HTTP cassettes (e.g., VCR-style) for stable third-party APIs.
|
|
126
|
+
|
|
127
|
+
Run the Inspector on your test fixtures to visually confirm that schemas, descriptions, and error messages read well from an LLM client's perspective — the Inspector is also a schema review tool.
|