@mcp-layer/test-server 0.0.0 → 1.0.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 +130 -1
- package/package.json +11 -6
- package/src/data/manual.js +4 -0
- package/src/index.js +12 -4
- package/src/prompts/index.js +1 -1
- package/src/prompts/welcome.js +2 -2
- package/src/resources/index.js +4 -2
- package/src/resources/manual.js +3 -3
- package/src/resources/notes.js +6 -6
- package/src/resources/templates.js +42 -0
- package/src/resources/ui.js +2 -2
- package/src/shared/capabilities.js +2 -2
- package/src/subscriptions/resources.js +4 -6
- package/src/tools/add.js +2 -2
- package/src/tools/annotated.js +2 -2
- package/src/tools/batch.js +2 -2
- package/src/tools/booking.js +3 -3
- package/src/tools/dashboard.js +2 -2
- package/src/tools/echo.js +2 -2
- package/src/tools/files.js +3 -3
- package/src/tools/index.js +9 -3
- package/src/tools/logs.js +2 -2
- package/src/tools/note-update.js +4 -4
- package/src/tools/present.js +2 -2
- package/src/tools/progress.js +14 -5
- package/src/tools/rebalance.js +3 -3
- package/src/tools/roots.js +3 -3
- package/src/tools/summaries.js +3 -3
- package/src/tools/testing.js +142 -0
- package/src/transport/http.js +1 -1
package/README.md
CHANGED
|
@@ -2,13 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
`@mcp-layer/test-server` is a feature-complete MCP server used for integration tests and local experiments. It mirrors the tool, resource, prompt, sampling, elicitation, notification, and MCP Apps features expected by the official SDK so clients can validate end-to-end behavior against a single, real server.
|
|
4
4
|
|
|
5
|
-
## Why this exists
|
|
6
5
|
|
|
7
6
|
The rest of this workspace depends on a real MCP server to validate protocols. This server:
|
|
8
7
|
- Runs over stdio (and optional HTTP/SSE transports)
|
|
9
8
|
- Exercises all major MCP capabilities
|
|
10
9
|
- Exposes stable fixtures for tests and demos
|
|
11
10
|
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Installation](#installation)
|
|
14
|
+
- [Quick start](#quick-start)
|
|
15
|
+
- [What it provides](#what-it-provides)
|
|
16
|
+
- [Structure](#structure)
|
|
17
|
+
- [Spec support matrix](#spec-support-matrix)
|
|
18
|
+
- [Usage in tests](#usage-in-tests)
|
|
19
|
+
- [Testing](#testing)
|
|
20
|
+
- [Extending the server](#extending-the-server)
|
|
21
|
+
- [Runtime Error Reference](#runtime-error-reference)
|
|
22
|
+
|
|
12
23
|
## Installation
|
|
13
24
|
|
|
14
25
|
```bash
|
|
@@ -133,6 +144,124 @@ When adding capabilities:
|
|
|
133
144
|
2) Add or update a test in `test/index.test.js`.
|
|
134
145
|
3) Document it in this README.
|
|
135
146
|
|
|
147
|
+
## Runtime Error Reference
|
|
148
|
+
|
|
149
|
+
This section is written for high-pressure debugging moments. These errors are intentionally emitted by the test server to exercise client and REST error-handling paths.
|
|
150
|
+
|
|
151
|
+
<a id="error-56fd38"></a>
|
|
152
|
+
### Protocol failure
|
|
153
|
+
|
|
154
|
+
Thrown from: `registerCrash.crashTool`
|
|
155
|
+
|
|
156
|
+
This error is intentionally thrown by the `crash` tool. It simulates an MCP tool that fails at protocol/runtime level so downstream layers can validate error mapping and HTTP problem responses.
|
|
157
|
+
|
|
158
|
+
Step-by-step resolution:
|
|
159
|
+
1. Confirm you actually invoked the `crash` tool (`tools crash exec`) and not a production tool.
|
|
160
|
+
2. If this appeared unexpectedly, verify test fixture/tool name selection.
|
|
161
|
+
3. In integration tests, assert the expected failure shape (`isError`/problem details) instead of treating it as an infra bug.
|
|
162
|
+
4. Use a non-crashing test tool when validating success paths.
|
|
163
|
+
|
|
164
|
+
<details>
|
|
165
|
+
<summary>Fix Example: separate crash-path tests from success-path tests</summary>
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
await t.test('error mapping', async function errorCase() {
|
|
169
|
+
await assert.rejects(client.callTool({ name: 'crash', arguments: {} }));
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
await t.test('success path', async function successCase() {
|
|
173
|
+
const result = await client.callTool({ name: 'slow', arguments: {} });
|
|
174
|
+
assert.equal(result.isError, false);
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
</details>
|
|
179
|
+
|
|
180
|
+
<a id="error-990eaa"></a>
|
|
181
|
+
### first failure
|
|
182
|
+
|
|
183
|
+
Thrown from: `registerFlap.flapTool`
|
|
184
|
+
|
|
185
|
+
This error is intentionally thrown on the first invocation of the `flap` tool. The second and later calls succeed, which is useful for circuit-breaker recovery tests.
|
|
186
|
+
|
|
187
|
+
Step-by-step resolution:
|
|
188
|
+
1. Confirm this is the first call in the process lifetime; first-call failure is expected behavior.
|
|
189
|
+
2. If you need deterministic success, warm up the tool once before the measured assertion.
|
|
190
|
+
3. For resilience tests, assert both phases: failure first, success next.
|
|
191
|
+
4. Reset server state between test cases when first-call semantics matter.
|
|
192
|
+
|
|
193
|
+
<details>
|
|
194
|
+
<summary>Fix Example: account for flap tool's first-call failure contract</summary>
|
|
195
|
+
|
|
196
|
+
```js
|
|
197
|
+
await assert.rejects(client.callTool({ name: 'flap', arguments: {} }));
|
|
198
|
+
const ok = await client.callTool({ name: 'flap', arguments: {} });
|
|
199
|
+
assert.equal(ok.isError, false);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
</details>
|
|
203
|
+
|
|
204
|
+
<a id="error-d1ff05"></a>
|
|
205
|
+
### progress tool aborted
|
|
206
|
+
|
|
207
|
+
Thrown from: `registerProgress.progressTool`
|
|
208
|
+
|
|
209
|
+
This error is raised when the progress tool sees `extra.signal.aborted` before or during loop processing. It represents explicit cancellation of long-running MCP work.
|
|
210
|
+
|
|
211
|
+
Step-by-step resolution:
|
|
212
|
+
1. Check client/request timeout and cancellation behavior for this call.
|
|
213
|
+
2. Confirm you did not pass an already-aborted signal into the tool execution context.
|
|
214
|
+
3. Increase timeout or delay cancellation if you expect completion.
|
|
215
|
+
4. For cancellation tests, assert abort behavior explicitly instead of treating this as unexpected.
|
|
216
|
+
|
|
217
|
+
<details>
|
|
218
|
+
<summary>Fix Example: avoid premature cancellation for progress runs</summary>
|
|
219
|
+
|
|
220
|
+
```js
|
|
221
|
+
const controller = new AbortController();
|
|
222
|
+
const result = await client.callTool({
|
|
223
|
+
name: 'progress',
|
|
224
|
+
arguments: { steps: 3, delayMs: 20 },
|
|
225
|
+
signal: controller.signal
|
|
226
|
+
});
|
|
227
|
+
assert.equal(result.isError, false);
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
</details>
|
|
231
|
+
|
|
232
|
+
<a id="error-c9d511"></a>
|
|
233
|
+
### progress tool aborted
|
|
234
|
+
|
|
235
|
+
Thrown from: `registerProgress.progressTool.sleep`
|
|
236
|
+
|
|
237
|
+
This error is raised when cancellation occurs while `progress` is sleeping between step notifications. It is the mid-flight cancellation branch of the same tool.
|
|
238
|
+
|
|
239
|
+
Step-by-step resolution:
|
|
240
|
+
1. Correlate abort timing with `delayMs` and `steps` in the progress request.
|
|
241
|
+
2. If completion is required, avoid cancelling during the sleep interval.
|
|
242
|
+
3. If cancellation is expected, assert this specific branch so tests remain intentional.
|
|
243
|
+
4. Tune `delayMs` in tests to make cancel timing deterministic.
|
|
244
|
+
|
|
245
|
+
<details>
|
|
246
|
+
<summary>Fix Example: deterministic cancellation test for sleep-abort path</summary>
|
|
247
|
+
|
|
248
|
+
```js
|
|
249
|
+
const controller = new AbortController();
|
|
250
|
+
const pending = client.callTool({
|
|
251
|
+
name: 'progress',
|
|
252
|
+
arguments: { steps: 5, delayMs: 100 },
|
|
253
|
+
signal: controller.signal
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
setTimeout(function abortLater() {
|
|
257
|
+
controller.abort();
|
|
258
|
+
}, 30);
|
|
259
|
+
|
|
260
|
+
await assert.rejects(pending);
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
</details>
|
|
264
|
+
|
|
136
265
|
## License
|
|
137
266
|
|
|
138
267
|
MIT
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-layer/test-server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Feature-rich MCP server for integration testing and local development.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/3rd-Eden/mcp-layer"
|
|
8
|
+
},
|
|
5
9
|
"type": "module",
|
|
6
10
|
"keywords": [
|
|
7
11
|
"mcp",
|
|
@@ -10,9 +14,6 @@
|
|
|
10
14
|
"integration",
|
|
11
15
|
"model-context-protocol"
|
|
12
16
|
],
|
|
13
|
-
"scripts": {
|
|
14
|
-
"test": "node --test --experimental-test-coverage ./test/**/*.js"
|
|
15
|
-
},
|
|
16
17
|
"bin": {
|
|
17
18
|
"mcp-test-server": "src/bin.js",
|
|
18
19
|
"mcp-test-server-http": "src/http.js"
|
|
@@ -35,6 +36,10 @@
|
|
|
35
36
|
"dependencies": {
|
|
36
37
|
"@modelcontextprotocol/sdk": "^1.20.2",
|
|
37
38
|
"express": "^5.0.1",
|
|
38
|
-
"zod": "^3.25.76"
|
|
39
|
+
"zod": "^3.25.76",
|
|
40
|
+
"@mcp-layer/error": "0.2.0"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"test": "node --test --experimental-test-coverage ./test/**/*.js"
|
|
39
44
|
}
|
|
40
|
-
}
|
|
45
|
+
}
|
package/src/data/manual.js
CHANGED
|
@@ -12,6 +12,10 @@ export const manual = [
|
|
|
12
12
|
'- progress: emits notifications/progress updates tied to request tokens.',
|
|
13
13
|
'- note-update: mutates note:// resources and emits notifications/resources/updated.',
|
|
14
14
|
'- rebalance: toggles tool registrations to showcase notification debouncing.',
|
|
15
|
+
'- fail-gracefully: returns isError true for REST error handling tests.',
|
|
16
|
+
'- crash: throws a protocol error to exercise error mapping.',
|
|
17
|
+
'- slow: delays responses to trigger circuit breaker timeouts.',
|
|
18
|
+
'- flap: fails once then succeeds to validate breaker recovery.',
|
|
15
19
|
'',
|
|
16
20
|
'## Resources',
|
|
17
21
|
'- resource://manual exposes this manual.',
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import { debouncedNotifications, info, instructions } from './config.js';
|
|
3
|
+
import { debouncedNotifications, info as defaultInfo, instructions } from './config.js';
|
|
4
4
|
import { manual } from './data/manual.js';
|
|
5
5
|
import { notes } from './data/notes.js';
|
|
6
6
|
import { references } from './data/references.js';
|
|
@@ -12,9 +12,14 @@ import { registerTools } from './tools/index.js';
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Build an MCP server instance pre-configured with tools, resources, and prompts.
|
|
15
|
+
*
|
|
16
|
+
* targeted metadata overrides (name/version) without custom servers.
|
|
17
|
+
*
|
|
18
|
+
* @param {{ info?: { name?: string, version?: string } }} [opts] - Optional metadata overrides.
|
|
15
19
|
* @returns {McpServer}
|
|
16
20
|
*/
|
|
17
|
-
export function build() {
|
|
21
|
+
export function build(opts = {}) {
|
|
22
|
+
const info = opts.info ? { ...defaultInfo, ...opts.info } : defaultInfo;
|
|
18
23
|
const server = new McpServer(info, {
|
|
19
24
|
instructions,
|
|
20
25
|
debouncedNotificationMethods: debouncedNotifications,
|
|
@@ -40,11 +45,14 @@ export function build() {
|
|
|
40
45
|
|
|
41
46
|
/**
|
|
42
47
|
* Start the test server using stdio transport for integration scenarios.
|
|
43
|
-
*
|
|
48
|
+
*
|
|
49
|
+
* exercising the standard test-server surface.
|
|
50
|
+
*
|
|
51
|
+
* @param {{ server?: McpServer, transport?: StdioServerTransport, info?: { name?: string, version?: string } }} [opts] - Optional overrides for the server instance, transport, or metadata.
|
|
44
52
|
* @returns {Promise<{ server: McpServer, transport: StdioServerTransport }>}
|
|
45
53
|
*/
|
|
46
54
|
export async function start(opts = {}) {
|
|
47
|
-
const server = opts.server ?? build();
|
|
55
|
+
const server = opts.server ?? build(opts);
|
|
48
56
|
const transport = opts.transport ?? new StdioServerTransport();
|
|
49
57
|
|
|
50
58
|
// Use stdio transport so callers can spawn this server as a subprocess during tests.
|
package/src/prompts/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { registerWelcomePrompt } from './welcome.js';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Register all prompts exposed by the server.
|
|
5
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
5
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register prompts on.
|
|
6
6
|
*/
|
|
7
7
|
export function registerPrompts(server) {
|
|
8
8
|
registerWelcomePrompt(server);
|
package/src/prompts/welcome.js
CHANGED
|
@@ -6,7 +6,7 @@ const tones = ['casual', 'formal', 'cheerful'];
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Register the welcome prompt with completable arguments.
|
|
9
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
9
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the prompt on.
|
|
10
10
|
*/
|
|
11
11
|
export function registerWelcomePrompt(server) {
|
|
12
12
|
const argsSchema = {
|
|
@@ -27,7 +27,7 @@ export function registerWelcomePrompt(server) {
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Produce a greeting message tailored to the provided tone.
|
|
30
|
-
* @param {{ name: string, tone: 'casual' | 'formal' | 'cheerful' }} args
|
|
30
|
+
* @param {{ name: string, tone: 'casual' | 'formal' | 'cheerful' }} args - Prompt arguments provided by the caller.
|
|
31
31
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').GetPromptResult>}
|
|
32
32
|
*/
|
|
33
33
|
async function welcomePrompt(args) {
|
package/src/resources/index.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { registerManualResource } from './manual.js';
|
|
2
2
|
import { registerNotesResource } from './notes.js';
|
|
3
|
+
import { registerTemplateResource } from './templates.js';
|
|
3
4
|
import { registerUi } from './ui.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Register all resources exposed by the server.
|
|
7
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
8
|
-
* @param {{ manual: string, notes: Map<string, Record<string, string>> }} context
|
|
8
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register resources on.
|
|
9
|
+
* @param {{ manual: string, notes: Map<string, Record<string, string>> }} context - Shared data backing the resources.
|
|
9
10
|
*/
|
|
10
11
|
export function registerResources(server, context) {
|
|
11
12
|
registerManualResource(server, context);
|
|
12
13
|
registerNotesResource(server, context);
|
|
14
|
+
registerTemplateResource(server);
|
|
13
15
|
registerUi(server);
|
|
14
16
|
}
|
package/src/resources/manual.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Register the markdown manual resource.
|
|
3
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
4
|
-
* @param {{ manual: string }} context
|
|
3
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the resource on.
|
|
4
|
+
* @param {{ manual: string }} context - Manual content backing the resource.
|
|
5
5
|
*/
|
|
6
6
|
export function registerManualResource(server, context) {
|
|
7
7
|
/**
|
|
8
8
|
* Return a markdown manual describing server features.
|
|
9
|
-
* @param {URL} uri
|
|
9
|
+
* @param {URL} uri - Resource URI for the request.
|
|
10
10
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').ReadResourceResult>}
|
|
11
11
|
*/
|
|
12
12
|
async function manualRead(uri) {
|
package/src/resources/notes.js
CHANGED
|
@@ -2,8 +2,8 @@ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Register the dynamic notes resource template with completions.
|
|
5
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
6
|
-
* @param {{ notes: Map<string, Record<string, string>> }} context
|
|
5
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the resource on.
|
|
6
|
+
* @param {{ notes: Map<string, Record<string, string>> }} context - Notes data keyed by topic/detail.
|
|
7
7
|
*/
|
|
8
8
|
export function registerNotesResource(server, context) {
|
|
9
9
|
const template = new ResourceTemplate('note://{topic}/{detail}', {
|
|
@@ -39,8 +39,8 @@ export function registerNotesResource(server, context) {
|
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* Derive note topic and detail for a URI or template variable set.
|
|
42
|
-
* @param {URL} uri
|
|
43
|
-
* @param {{ topic?: string, detail?: string }} [variables]
|
|
42
|
+
* @param {URL} uri - Requested resource URI.
|
|
43
|
+
* @param {{ topic?: string, detail?: string }} [variables] - Template variables resolved from the request.
|
|
44
44
|
* @returns {{ topic: string, detail: string }}
|
|
45
45
|
*/
|
|
46
46
|
function noteParts(uri, variables = {}) {
|
|
@@ -52,8 +52,8 @@ export function registerNotesResource(server, context) {
|
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
* Read a dynamic note resource for the requested topic/detail pair.
|
|
55
|
-
* @param {URL} uri
|
|
56
|
-
* @param {{ topic?: string, detail?: string }} [variables]
|
|
55
|
+
* @param {URL} uri - Requested resource URI.
|
|
56
|
+
* @param {{ topic?: string, detail?: string }} [variables] - Template variables resolved from the request.
|
|
57
57
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').ReadResourceResult>}
|
|
58
58
|
*/
|
|
59
59
|
async function noteRead(uri, variables = {}) {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Register a templated resource that expands a name into a note URI.
|
|
5
|
+
*
|
|
6
|
+
* surface to exercise dynamic routes.
|
|
7
|
+
*
|
|
8
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register resources on.
|
|
9
|
+
*/
|
|
10
|
+
export function registerTemplateResource(server) {
|
|
11
|
+
const template = new ResourceTemplate('template://note/{name}', {});
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Read a templated note resource.
|
|
15
|
+
* @param {URL} uri - Expanded resource URI.
|
|
16
|
+
* @param {{ name?: string }} variables - Template variables.
|
|
17
|
+
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').ReadResourceResult>}
|
|
18
|
+
*/
|
|
19
|
+
async function templateRead(uri, variables) {
|
|
20
|
+
const name = variables && typeof variables.name === 'string' ? variables.name : 'unknown';
|
|
21
|
+
return {
|
|
22
|
+
contents: [
|
|
23
|
+
{
|
|
24
|
+
uri: uri.href,
|
|
25
|
+
text: `Template note for ${name}.`,
|
|
26
|
+
mimeType: 'text/plain'
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
server.registerResource(
|
|
33
|
+
'note-template',
|
|
34
|
+
template,
|
|
35
|
+
{
|
|
36
|
+
title: 'Template Note',
|
|
37
|
+
description: 'Dynamic note template resource.',
|
|
38
|
+
mimeType: 'text/plain'
|
|
39
|
+
},
|
|
40
|
+
templateRead
|
|
41
|
+
);
|
|
42
|
+
}
|
package/src/resources/ui.js
CHANGED
|
@@ -19,12 +19,12 @@ const uri = 'ui://dashboard/app.html';
|
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Register the MCP App UI resource.
|
|
22
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
22
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the resource on.
|
|
23
23
|
*/
|
|
24
24
|
export function registerUi(server) {
|
|
25
25
|
/**
|
|
26
26
|
* Return HTML for the dashboard UI.
|
|
27
|
-
* @param {URL} url
|
|
27
|
+
* @param {URL} url - Requested resource URL.
|
|
28
28
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').ReadResourceResult>}
|
|
29
29
|
*/
|
|
30
30
|
async function ui(url) {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Build a capability checker bound to the current server connection.
|
|
3
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
3
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server instance to read client capabilities from.
|
|
4
4
|
* @returns {(capability: 'sampling' | 'elicitation' | 'roots') => boolean}
|
|
5
5
|
*/
|
|
6
6
|
export function createCapabilityChecker(server) {
|
|
7
7
|
/**
|
|
8
8
|
* Determine whether the connected client declared the requested capability.
|
|
9
|
-
* @param {'sampling' | 'elicitation' | 'roots'} capability
|
|
9
|
+
* @param {'sampling' | 'elicitation' | 'roots'} capability - Capability name to check.
|
|
10
10
|
* @returns {boolean}
|
|
11
11
|
*/
|
|
12
12
|
function hasCapability(capability) {
|
|
@@ -2,8 +2,8 @@ import { ErrorCode, McpError, SubscribeRequestSchema, UnsubscribeRequestSchema }
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Configure basic resource subscriptions on the underlying server transport.
|
|
5
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
6
|
-
* @param {{ isSubscribable: (uri: string) => boolean }} options
|
|
5
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register subscription handlers on.
|
|
6
|
+
* @param {{ isSubscribable: (uri: string) => boolean }} options - Subscription filters for allowed URIs.
|
|
7
7
|
* @returns {{ notifyResourceUpdated: (uri: string) => Promise<void> }}
|
|
8
8
|
*/
|
|
9
9
|
export function registerResourceSubscriptions(server, options) {
|
|
@@ -37,9 +37,7 @@ export function registerResourceSubscriptions(server, options) {
|
|
|
37
37
|
const set = subscribers.get(sessionId);
|
|
38
38
|
if (set) {
|
|
39
39
|
set.delete(request.params.uri);
|
|
40
|
-
if (set.size === 0)
|
|
41
|
-
subscribers.delete(sessionId);
|
|
42
|
-
}
|
|
40
|
+
if (set.size === 0) subscribers.delete(sessionId);
|
|
43
41
|
}
|
|
44
42
|
return {};
|
|
45
43
|
});
|
|
@@ -50,7 +48,7 @@ export function registerResourceSubscriptions(server, options) {
|
|
|
50
48
|
|
|
51
49
|
/**
|
|
52
50
|
* Notify subscribed clients that a resource has been updated.
|
|
53
|
-
* @param {string} uri
|
|
51
|
+
* @param {string} uri - Resource URI that changed.
|
|
54
52
|
* @returns {Promise<void>}
|
|
55
53
|
*/
|
|
56
54
|
async function notifyResourceUpdated(uri) {
|
package/src/tools/add.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Register the add tool returning structured totals.
|
|
5
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
5
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
6
6
|
* @returns {ReturnType<import('@modelcontextprotocol/sdk/server/mcp.js').McpServer['registerTool']>}
|
|
7
7
|
*/
|
|
8
8
|
export function registerAdd(server) {
|
|
@@ -13,7 +13,7 @@ export function registerAdd(server) {
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Sum two numbers to demonstrate structured responses.
|
|
16
|
-
* @param {{ first: number, second: number }} args
|
|
16
|
+
* @param {{ first: number, second: number }} args - Numeric operands provided by the caller.
|
|
17
17
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
18
18
|
*/
|
|
19
19
|
async function addTool(args) {
|
package/src/tools/annotated.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Register a tool that exercises annotations and _meta without output schema.
|
|
5
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
5
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
6
6
|
* @returns {ReturnType<import('@modelcontextprotocol/sdk/server/mcp.js').McpServer['registerTool']>}
|
|
7
7
|
*/
|
|
8
8
|
export function registerAnnotated(server) {
|
|
@@ -12,7 +12,7 @@ export function registerAnnotated(server) {
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Return a plain text response for annotation coverage.
|
|
15
|
-
* @param {{ label: string }} args
|
|
15
|
+
* @param {{ label: string }} args - Label input used in the response text.
|
|
16
16
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
17
17
|
*/
|
|
18
18
|
async function annotatedTool(args) {
|
package/src/tools/batch.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Register a tool that accepts array/object inputs.
|
|
5
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
5
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
6
6
|
* @returns {ReturnType<import('@modelcontextprotocol/sdk/server/mcp.js').McpServer['registerTool']>}
|
|
7
7
|
*/
|
|
8
8
|
export function registerBatch(server) {
|
|
@@ -13,7 +13,7 @@ export function registerBatch(server) {
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Summarize array/object inputs.
|
|
16
|
-
* @param {{ items: string[], meta: { tag: string } }} args
|
|
16
|
+
* @param {{ items: string[], meta: { tag: string } }} args - Batch input values.
|
|
17
17
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
18
18
|
*/
|
|
19
19
|
async function batchTool(args) {
|
package/src/tools/booking.js
CHANGED
|
@@ -2,8 +2,8 @@ import { z } from 'zod';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Register the booking tool that triggers elicitation workflows.
|
|
5
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
6
|
-
* @param {{ hasCapability: (capability: 'elicitation') => boolean }} capabilities
|
|
5
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
6
|
+
* @param {{ hasCapability: (capability: 'elicitation') => boolean }} capabilities - Capability checker for elicitation support.
|
|
7
7
|
*/
|
|
8
8
|
export function registerBooking(server, capabilities) {
|
|
9
9
|
const inputSchema = {
|
|
@@ -14,7 +14,7 @@ export function registerBooking(server, capabilities) {
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Demonstrate elicitation by asking for alternate booking dates.
|
|
17
|
-
* @param {{ restaurant: string, date: string, guests: number }} args
|
|
17
|
+
* @param {{ restaurant: string, date: string, guests: number }} args - Booking request inputs.
|
|
18
18
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
19
19
|
*/
|
|
20
20
|
async function bookingTool(args) {
|
package/src/tools/dashboard.js
CHANGED
|
@@ -6,7 +6,7 @@ const permissions = ['clipboard-read'];
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Register a tool that declares an MCP App UI resource.
|
|
9
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
9
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
10
10
|
* @returns {ReturnType<import('@modelcontextprotocol/sdk/server/mcp.js').McpServer['registerTool']>}
|
|
11
11
|
*/
|
|
12
12
|
export function registerDashboard(server) {
|
|
@@ -16,7 +16,7 @@ export function registerDashboard(server) {
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Return a simple payload while the host renders the UI resource.
|
|
19
|
-
* @param {{ name: string }} args
|
|
19
|
+
* @param {{ name: string }} args - Request input used for the response payload.
|
|
20
20
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
21
21
|
*/
|
|
22
22
|
async function dashboardTool(args) {
|
package/src/tools/echo.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Register the echo tool that optionally uppercases responses.
|
|
5
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
5
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
6
6
|
* @returns {ReturnType<import('@modelcontextprotocol/sdk/server/mcp.js').McpServer['registerTool']>}
|
|
7
7
|
*/
|
|
8
8
|
export function registerEcho(server) {
|
|
@@ -13,7 +13,7 @@ export function registerEcho(server) {
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Repeat supplied text and optionally uppercase the response.
|
|
16
|
-
* @param {{ text: string, loud: boolean }} args
|
|
16
|
+
* @param {{ text: string, loud: boolean }} args - Request input used to build the response.
|
|
17
17
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
18
18
|
*/
|
|
19
19
|
async function echoTool(args) {
|
package/src/tools/files.js
CHANGED
|
@@ -2,8 +2,8 @@ import { z } from 'zod';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Register the files tool that emits ResourceLinks referencing shared assets.
|
|
5
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
6
|
-
* @param {{ references: Array<{ uri: string, name: string, description: string, mimeType: string }> }} context
|
|
5
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
6
|
+
* @param {{ references: Array<{ uri: string, name: string, description: string, mimeType: string }> }} context - Reference data backing the responses.
|
|
7
7
|
*/
|
|
8
8
|
export function registerFiles(server, context) {
|
|
9
9
|
const inputSchema = {
|
|
@@ -13,7 +13,7 @@ export function registerFiles(server, context) {
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Emit ResourceLinks referencing manual and note resources.
|
|
16
|
-
* @param {{ filter: string, limit: number }} args
|
|
16
|
+
* @param {{ filter: string, limit: number }} args - Filtering and limit inputs for the listing.
|
|
17
17
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
18
18
|
*/
|
|
19
19
|
async function filesTool(args) {
|
package/src/tools/index.js
CHANGED
|
@@ -12,12 +12,14 @@ import { registerPresent } from './present.js';
|
|
|
12
12
|
import { registerRebalance } from './rebalance.js';
|
|
13
13
|
import { registerRoots } from './roots.js';
|
|
14
14
|
import { registerSummaries } from './summaries.js';
|
|
15
|
+
import { registerCrash, registerFailGracefully, registerFlap, registerSlow } from './testing.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Register all server tools.
|
|
18
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
19
|
-
* @param {{ references: Array<{ uri: string, name: string, description: string, mimeType: string }> }} context
|
|
20
|
-
* @param {{ hasCapability: (capability: 'sampling' | 'elicitation' | 'roots') => boolean }} capabilities
|
|
19
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register tools on.
|
|
20
|
+
* @param {{ references: Array<{ uri: string, name: string, description: string, mimeType: string }> }} context - Shared reference data for tools.
|
|
21
|
+
* @param {{ hasCapability: (capability: 'sampling' | 'elicitation' | 'roots') => boolean }} capabilities - Capability checker for tool behavior.
|
|
22
|
+
* @param {{ notifyResourceUpdated: (uri: string) => Promise<void> }} notifier - Notifier for resource update events.
|
|
21
23
|
*/
|
|
22
24
|
export function registerTools(server, context, capabilities, notifier) {
|
|
23
25
|
const echoHandle = registerEcho(server);
|
|
@@ -34,4 +36,8 @@ export function registerTools(server, context, capabilities, notifier) {
|
|
|
34
36
|
registerPresent(server);
|
|
35
37
|
registerProgress(server);
|
|
36
38
|
registerRebalance(server, { echo: echoHandle, add: addHandle });
|
|
39
|
+
registerFailGracefully(server);
|
|
40
|
+
registerCrash(server);
|
|
41
|
+
registerSlow(server);
|
|
42
|
+
registerFlap(server);
|
|
37
43
|
}
|
package/src/tools/logs.js
CHANGED
|
@@ -5,7 +5,7 @@ const levels = LoggingLevelSchema.options;
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Register the logs tool that emits notifications/message entries.
|
|
8
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
8
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
9
9
|
*/
|
|
10
10
|
export function registerLogs(server) {
|
|
11
11
|
const inputSchema = {
|
|
@@ -16,7 +16,7 @@ export function registerLogs(server) {
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Emit a logging notification honoring the client's configured level filter.
|
|
19
|
-
* @param {{ level: typeof levels[number], message: string, logger: string }} args
|
|
19
|
+
* @param {{ level: typeof levels[number], message: string, logger: string }} args - Logging payload inputs.
|
|
20
20
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
21
21
|
*/
|
|
22
22
|
async function logsTool(args) {
|
package/src/tools/note-update.js
CHANGED
|
@@ -2,9 +2,9 @@ import { z } from 'zod';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Register a tool that mutates note resources and emits update notifications.
|
|
5
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
6
|
-
* @param {{ notes: Map<string, Record<string, string>> }} context
|
|
7
|
-
* @param {{ notifyResourceUpdated: (uri: string) => Promise<void> }} notifier
|
|
5
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
6
|
+
* @param {{ notes: Map<string, Record<string, string>> }} context - Notes data store to mutate.
|
|
7
|
+
* @param {{ notifyResourceUpdated: (uri: string) => Promise<void> }} notifier - Resource update notifier.
|
|
8
8
|
*/
|
|
9
9
|
export function registerNoteUpdate(server, context, notifier) {
|
|
10
10
|
const inputSchema = {
|
|
@@ -15,7 +15,7 @@ export function registerNoteUpdate(server, context, notifier) {
|
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Update the referenced note and broadcast notifications/resources/updated.
|
|
18
|
-
* @param {{ topic: string, detail: string, text: string }} args
|
|
18
|
+
* @param {{ topic: string, detail: string, text: string }} args - Update inputs for the note entry.
|
|
19
19
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
20
20
|
*/
|
|
21
21
|
async function updateNoteTool(args) {
|
package/src/tools/present.js
CHANGED
|
@@ -5,7 +5,7 @@ const WAV_BASE64 = 'UklGRiQAAABXQVZFZm10IBAAAAABAAEAESsAACJWAAACABAAZGF0YQAAAAA=
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Register the present tool to emit mixed content types.
|
|
8
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
8
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
9
9
|
* @returns {ReturnType<import('@modelcontextprotocol/sdk/server/mcp.js').McpServer['registerTool']>}
|
|
10
10
|
*/
|
|
11
11
|
export function registerPresent(server) {
|
|
@@ -15,7 +15,7 @@ export function registerPresent(server) {
|
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Emit markdown, image, audio, and resource link content items.
|
|
18
|
-
* @param {{ title: string }} args
|
|
18
|
+
* @param {{ title: string }} args - Presentation title supplied by the caller.
|
|
19
19
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
20
20
|
*/
|
|
21
21
|
async function presentTool(args) {
|
package/src/tools/progress.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { LayerError } from '@mcp-layer/error';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Register the progress tool that emits notifications/progress updates.
|
|
5
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
6
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
6
7
|
*/
|
|
7
8
|
export function registerProgress(server) {
|
|
8
9
|
const inputSchema = {
|
|
@@ -12,8 +13,8 @@ export function registerProgress(server) {
|
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Wait between steps and emit progress notifications when requested.
|
|
15
|
-
* @param {{ steps: number, delayMs: number }} args
|
|
16
|
-
* @param {{ _meta?: { progressToken?: string | number }, signal: AbortSignal, sendNotification: (notification: any) => Promise<void> }} extra
|
|
16
|
+
* @param {{ steps: number, delayMs: number }} args - Progress configuration inputs.
|
|
17
|
+
* @param {{ _meta?: { progressToken?: string | number }, signal: AbortSignal, sendNotification: (notification: any) => Promise<void> }} extra - Tool execution context and notification helper.
|
|
17
18
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
18
19
|
*/
|
|
19
20
|
async function progressTool(args, extra) {
|
|
@@ -30,7 +31,11 @@ export function registerProgress(server) {
|
|
|
30
31
|
'abort',
|
|
31
32
|
() => {
|
|
32
33
|
clearTimeout(timer);
|
|
33
|
-
reject(new
|
|
34
|
+
reject(new LayerError({
|
|
35
|
+
name: 'test-server',
|
|
36
|
+
method: 'registerProgress.progressTool.sleep',
|
|
37
|
+
message: 'progress tool aborted',
|
|
38
|
+
}));
|
|
34
39
|
},
|
|
35
40
|
{ once: true }
|
|
36
41
|
);
|
|
@@ -39,7 +44,11 @@ export function registerProgress(server) {
|
|
|
39
44
|
|
|
40
45
|
for (let index = 1; index <= args.steps; index += 1) {
|
|
41
46
|
if (extra.signal.aborted) {
|
|
42
|
-
throw new
|
|
47
|
+
throw new LayerError({
|
|
48
|
+
name: 'test-server',
|
|
49
|
+
method: 'registerProgress.progressTool',
|
|
50
|
+
message: 'progress tool aborted',
|
|
51
|
+
});
|
|
43
52
|
}
|
|
44
53
|
await sleep();
|
|
45
54
|
if (token) {
|
package/src/tools/rebalance.js
CHANGED
|
@@ -2,8 +2,8 @@ import { z } from 'zod';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Register the rebalance tool that toggles core tool registrations to exercise debounced notifications.
|
|
5
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
6
|
-
* @param {{ echo: ReturnType<import('@modelcontextprotocol/sdk/server/mcp.js').McpServer['registerTool']>, add: ReturnType<import('@modelcontextprotocol/sdk/server/mcp.js').McpServer['registerTool']> }} handles
|
|
5
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
6
|
+
* @param {{ echo: ReturnType<import('@modelcontextprotocol/sdk/server/mcp.js').McpServer['registerTool']>, add: ReturnType<import('@modelcontextprotocol/sdk/server/mcp.js').McpServer['registerTool']> }} handles - Tool handles to enable/disable.
|
|
7
7
|
*/
|
|
8
8
|
export function registerRebalance(server, handles) {
|
|
9
9
|
const inputSchema = {
|
|
@@ -12,7 +12,7 @@ export function registerRebalance(server, handles) {
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Toggle core tools repeatedly to demonstrate notification debouncing.
|
|
15
|
-
* @param {{ cycles: number }} args
|
|
15
|
+
* @param {{ cycles: number }} args - Number of enable/disable cycles to run.
|
|
16
16
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
17
17
|
*/
|
|
18
18
|
async function rebalanceTool(args) {
|
package/src/tools/roots.js
CHANGED
|
@@ -2,8 +2,8 @@ import { z } from 'zod';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Register the roots tool that calls roots/list on capable clients.
|
|
5
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
6
|
-
* @param {{ hasCapability: (capability: 'roots') => boolean }} capabilities
|
|
5
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
6
|
+
* @param {{ hasCapability: (capability: 'roots') => boolean }} capabilities - Capability checker for roots support.
|
|
7
7
|
*/
|
|
8
8
|
export function registerRoots(server, capabilities) {
|
|
9
9
|
const inputSchema = {
|
|
@@ -12,7 +12,7 @@ export function registerRoots(server, capabilities) {
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Request roots/list from the client to expose workspace locations.
|
|
15
|
-
* @param {{ includeMeta: boolean }} args
|
|
15
|
+
* @param {{ includeMeta: boolean }} args - Whether to include client root metadata.
|
|
16
16
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
17
17
|
*/
|
|
18
18
|
async function rootsTool(args) {
|
package/src/tools/summaries.js
CHANGED
|
@@ -2,8 +2,8 @@ import { z } from 'zod';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Register the summaries tool that delegates to sampling/createMessage.
|
|
5
|
-
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
6
|
-
* @param {{ hasCapability: (capability: 'sampling') => boolean }} capabilities
|
|
5
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
6
|
+
* @param {{ hasCapability: (capability: 'sampling') => boolean }} capabilities - Capability checker for sampling support.
|
|
7
7
|
*/
|
|
8
8
|
export function registerSummaries(server, capabilities) {
|
|
9
9
|
const inputSchema = {
|
|
@@ -13,7 +13,7 @@ export function registerSummaries(server, capabilities) {
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Request a summary via sampling/createMessage when supported.
|
|
16
|
-
* @param {{ text: string, maxTokens: number }} args
|
|
16
|
+
* @param {{ text: string, maxTokens: number }} args - Summary request inputs.
|
|
17
17
|
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
18
18
|
*/
|
|
19
19
|
async function summaryTool(args) {
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { LayerError } from '@mcp-layer/error';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Register a tool that always returns isError true.
|
|
6
|
+
*
|
|
7
|
+
* relying on custom servers.
|
|
8
|
+
*
|
|
9
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
10
|
+
* @returns {ReturnType<import('@modelcontextprotocol/sdk/server/mcp.js').McpServer['registerTool']>}
|
|
11
|
+
*/
|
|
12
|
+
export function registerFailGracefully(server) {
|
|
13
|
+
const inputSchema = { reason: z.string().optional() };
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Return a tool error payload.
|
|
17
|
+
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
18
|
+
*/
|
|
19
|
+
async function failGracefullyTool() {
|
|
20
|
+
return {
|
|
21
|
+
content: [{ type: 'text', text: 'Something went wrong' }],
|
|
22
|
+
isError: true
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return server.registerTool(
|
|
27
|
+
'fail-gracefully',
|
|
28
|
+
{
|
|
29
|
+
title: 'Fail Gracefully',
|
|
30
|
+
description: 'Always returns isError true for REST error handling tests.',
|
|
31
|
+
inputSchema
|
|
32
|
+
},
|
|
33
|
+
failGracefullyTool
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Register a tool that throws.
|
|
39
|
+
*
|
|
40
|
+
* exercises JSON-RPC error mapping.
|
|
41
|
+
*
|
|
42
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
43
|
+
* @returns {ReturnType<import('@modelcontextprotocol/sdk/server/mcp.js').McpServer['registerTool']>}
|
|
44
|
+
*/
|
|
45
|
+
export function registerCrash(server) {
|
|
46
|
+
const inputSchema = { reason: z.string().optional() };
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Throw a protocol-level error.
|
|
50
|
+
* @throws {LayerError}
|
|
51
|
+
*/
|
|
52
|
+
async function crashTool() {
|
|
53
|
+
throw new LayerError({
|
|
54
|
+
name: 'test-server',
|
|
55
|
+
method: 'registerCrash.crashTool',
|
|
56
|
+
message: 'Protocol failure',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return server.registerTool(
|
|
61
|
+
'crash',
|
|
62
|
+
{
|
|
63
|
+
title: 'Crash',
|
|
64
|
+
description: 'Throws an error to simulate protocol failures.',
|
|
65
|
+
inputSchema
|
|
66
|
+
},
|
|
67
|
+
crashTool
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Register a tool that intentionally exceeds short timeouts.
|
|
73
|
+
*
|
|
74
|
+
*
|
|
75
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
76
|
+
* @returns {ReturnType<import('@modelcontextprotocol/sdk/server/mcp.js').McpServer['registerTool']>}
|
|
77
|
+
*/
|
|
78
|
+
export function registerSlow(server) {
|
|
79
|
+
const inputSchema = { reason: z.string().optional() };
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Resolve after a fixed delay.
|
|
83
|
+
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
84
|
+
*/
|
|
85
|
+
async function slowTool() {
|
|
86
|
+
return new Promise(function wait(resolve) {
|
|
87
|
+
const timer = setTimeout(function done() {
|
|
88
|
+
resolve({ content: [{ type: 'text', text: 'late' }] });
|
|
89
|
+
}, 50);
|
|
90
|
+
timer.unref();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return server.registerTool(
|
|
95
|
+
'slow',
|
|
96
|
+
{
|
|
97
|
+
title: 'Slow',
|
|
98
|
+
description: 'Delays to trigger timeout behavior in clients.',
|
|
99
|
+
inputSchema
|
|
100
|
+
},
|
|
101
|
+
slowTool
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Register a tool that fails once then succeeds.
|
|
107
|
+
*
|
|
108
|
+
* transition from failure to success.
|
|
109
|
+
*
|
|
110
|
+
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server - MCP server to register the tool on.
|
|
111
|
+
* @returns {ReturnType<import('@modelcontextprotocol/sdk/server/mcp.js').McpServer['registerTool']>}
|
|
112
|
+
*/
|
|
113
|
+
export function registerFlap(server) {
|
|
114
|
+
const inputSchema = { reason: z.string().optional() };
|
|
115
|
+
let count = 0;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Throw on the first call, then return success.
|
|
119
|
+
* @returns {Promise<import('@modelcontextprotocol/sdk/types.js').CallToolResult>}
|
|
120
|
+
*/
|
|
121
|
+
async function flapTool() {
|
|
122
|
+
count += 1;
|
|
123
|
+
if (count === 1) {
|
|
124
|
+
throw new LayerError({
|
|
125
|
+
name: 'test-server',
|
|
126
|
+
method: 'registerFlap.flapTool',
|
|
127
|
+
message: 'first failure',
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return { content: [{ type: 'text', text: 'ok' }] };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return server.registerTool(
|
|
134
|
+
'flap',
|
|
135
|
+
{
|
|
136
|
+
title: 'Flap',
|
|
137
|
+
description: 'Fails once, then succeeds to exercise breaker recovery.',
|
|
138
|
+
inputSchema
|
|
139
|
+
},
|
|
140
|
+
flapTool
|
|
141
|
+
);
|
|
142
|
+
}
|
package/src/transport/http.js
CHANGED
|
@@ -5,7 +5,7 @@ import { build } from '../index.js';
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Start an HTTP server that exposes Streamable HTTP and SSE transports.
|
|
8
|
-
* @param {{ port?: number }} [opts]
|
|
8
|
+
* @param {{ port?: number }} [opts] - Optional port override for the HTTP server.
|
|
9
9
|
* @returns {Promise<{ close: () => Promise<void> }>}
|
|
10
10
|
*/
|
|
11
11
|
export async function startHttpServer(opts = {}) {
|