@modelcontextprotocol/server-everything 2025.12.18 → 2026.1.26
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 +9 -158
- package/dist/docs/architecture.md +44 -0
- package/dist/docs/extension.md +23 -0
- package/dist/docs/features.md +103 -0
- package/dist/docs/how-it-works.md +45 -0
- package/dist/docs/instructions.md +28 -0
- package/dist/docs/startup.md +73 -0
- package/dist/docs/structure.md +182 -0
- package/dist/index.js +19 -14
- package/dist/prompts/args.js +34 -0
- package/dist/prompts/completions.js +52 -0
- package/dist/prompts/index.js +15 -0
- package/dist/prompts/resource.js +60 -0
- package/dist/prompts/simple.js +23 -0
- package/dist/resources/files.js +83 -0
- package/dist/resources/index.js +33 -0
- package/dist/resources/session.js +44 -0
- package/dist/resources/subscriptions.js +125 -0
- package/dist/resources/templates.js +171 -0
- package/dist/server/index.js +93 -0
- package/dist/server/logging.js +64 -0
- package/dist/server/roots.js +65 -0
- package/dist/tools/echo.js +29 -0
- package/dist/tools/get-annotated-message.js +81 -0
- package/dist/tools/get-env.js +28 -0
- package/dist/tools/get-resource-links.js +62 -0
- package/dist/tools/get-resource-reference.js +74 -0
- package/dist/tools/get-roots-list.js +71 -0
- package/dist/tools/get-structured-content.js +72 -0
- package/dist/tools/get-sum.js +40 -0
- package/dist/tools/get-tiny-image.js +41 -0
- package/dist/tools/gzip-file-as-resource.js +182 -0
- package/dist/tools/index.js +50 -0
- package/dist/tools/simulate-research-query.js +249 -0
- package/dist/tools/toggle-simulated-logging.js +41 -0
- package/dist/tools/toggle-subscriber-updates.js +44 -0
- package/dist/tools/trigger-elicitation-request-async.js +202 -0
- package/dist/tools/trigger-elicitation-request.js +210 -0
- package/dist/tools/trigger-long-running-operation.js +59 -0
- package/dist/tools/trigger-sampling-request-async.js +168 -0
- package/dist/tools/trigger-sampling-request.js +71 -0
- package/dist/{sse.js → transports/sse.js} +25 -17
- package/dist/transports/stdio.js +27 -0
- package/dist/transports/streamableHttp.js +206 -0
- package/package.json +10 -7
- package/dist/everything.js +0 -978
- package/dist/instructions.md +0 -23
- package/dist/stdio.js +0 -23
- package/dist/streamableHttp.js +0 -174
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Everything Server - Project Structure
|
|
2
|
+
|
|
3
|
+
**[Architecture](architecture.md)
|
|
4
|
+
| Project Structure
|
|
5
|
+
| [Startup Process](startup.md)
|
|
6
|
+
| [Server Features](features.md)
|
|
7
|
+
| [Extension Points](extension.md)
|
|
8
|
+
| [How It Works](how-it-works.md)**
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
src/everything
|
|
12
|
+
├── index.ts
|
|
13
|
+
├── AGENTS.md
|
|
14
|
+
├── package.json
|
|
15
|
+
├── docs
|
|
16
|
+
│ ├── architecture.md
|
|
17
|
+
│ ├── extension.md
|
|
18
|
+
│ ├── features.md
|
|
19
|
+
│ ├── how-it-works.md
|
|
20
|
+
│ ├── instructions.md
|
|
21
|
+
│ ├── startup.md
|
|
22
|
+
│ └── structure.md
|
|
23
|
+
├── prompts
|
|
24
|
+
│ ├── index.ts
|
|
25
|
+
│ ├── args.ts
|
|
26
|
+
│ ├── completions.ts
|
|
27
|
+
│ ├── simple.ts
|
|
28
|
+
│ └── resource.ts
|
|
29
|
+
├── resources
|
|
30
|
+
│ ├── index.ts
|
|
31
|
+
│ ├── files.ts
|
|
32
|
+
│ ├── session.ts
|
|
33
|
+
│ ├── subscriptions.ts
|
|
34
|
+
│ └── templates.ts
|
|
35
|
+
├── server
|
|
36
|
+
│ ├── index.ts
|
|
37
|
+
│ ├── logging.ts
|
|
38
|
+
│ └── roots.ts
|
|
39
|
+
├── tools
|
|
40
|
+
│ ├── index.ts
|
|
41
|
+
│ ├── echo.ts
|
|
42
|
+
│ ├── get-annotated-message.ts
|
|
43
|
+
│ ├── get-env.ts
|
|
44
|
+
│ ├── get-resource-links.ts
|
|
45
|
+
│ ├── get-resource-reference.ts
|
|
46
|
+
│ ├── get-roots-list.ts
|
|
47
|
+
│ ├── get-structured-content.ts
|
|
48
|
+
│ ├── get-sum.ts
|
|
49
|
+
│ ├── get-tiny-image.ts
|
|
50
|
+
│ ├── gzip-file-as-resource.ts
|
|
51
|
+
│ ├── toggle-simulated-logging.ts
|
|
52
|
+
│ ├── toggle-subscriber-updates.ts
|
|
53
|
+
│ ├── trigger-elicitation-request.ts
|
|
54
|
+
│ ├── trigger-long-running-operation.ts
|
|
55
|
+
│ └── trigger-sampling-request.ts
|
|
56
|
+
└── transports
|
|
57
|
+
├── sse.ts
|
|
58
|
+
├── stdio.ts
|
|
59
|
+
└── streamableHttp.ts
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
# Project Contents
|
|
63
|
+
|
|
64
|
+
## `src/everything`:
|
|
65
|
+
|
|
66
|
+
### `index.ts`
|
|
67
|
+
|
|
68
|
+
- CLI entry point that selects and runs a specific transport module based on the first CLI argument: `stdio`, `sse`, or `streamableHttp`.
|
|
69
|
+
|
|
70
|
+
### `AGENTS.md`
|
|
71
|
+
|
|
72
|
+
- Directions for Agents/LLMs explaining coding guidelines and how to appropriately extend the server.
|
|
73
|
+
|
|
74
|
+
### `package.json`
|
|
75
|
+
|
|
76
|
+
- Package metadata and scripts:
|
|
77
|
+
- `build`: TypeScript compile to `dist/`, copies `docs/` into `dist/` and marks the compiled entry scripts as executable.
|
|
78
|
+
- `start:stdio`, `start:sse`, `start:streamableHttp`: Run built transports from `dist/`.
|
|
79
|
+
- Declares dependencies on `@modelcontextprotocol/sdk`, `express`, `cors`, `zod`, etc.
|
|
80
|
+
|
|
81
|
+
### `docs/`
|
|
82
|
+
|
|
83
|
+
- `architecture.md`
|
|
84
|
+
- This document.
|
|
85
|
+
- `server-instructions.md`
|
|
86
|
+
- Human‑readable instructions intended to be passed to the client/LLM as for guidance on server use. Loaded by the server at startup and returned in the "initialize" exchange.
|
|
87
|
+
|
|
88
|
+
### `prompts/`
|
|
89
|
+
|
|
90
|
+
- `index.ts`
|
|
91
|
+
- `registerPrompts(server)` orchestrator; delegates to prompt factory/registration methods from in individual prompt files.
|
|
92
|
+
- `simple.ts`
|
|
93
|
+
- Registers `simple-prompt`: a prompt with no arguments that returns a single user message.
|
|
94
|
+
- `args.ts`
|
|
95
|
+
- Registers `args-prompt`: a prompt with two arguments (`city` required, `state` optional) used to compose a message.
|
|
96
|
+
- `completions.ts`
|
|
97
|
+
- Registers `completable-prompt`: a prompt whose arguments support server-driven completions using the SDK’s `completable(...)` helper (e.g., completing `department` and context-aware `name`).
|
|
98
|
+
- `resource.ts`
|
|
99
|
+
- Exposes `registerEmbeddedResourcePrompt(server)` which registers `resource-prompt` — a prompt that accepts `resourceType` ("Text" or "Blob") and `resourceId` (integer), and embeds a dynamically generated resource of the requested type within the returned messages. Internally reuses helpers from `resources/templates.ts`.
|
|
100
|
+
|
|
101
|
+
### `resources/`
|
|
102
|
+
|
|
103
|
+
- `index.ts`
|
|
104
|
+
- `registerResources(server)` orchestrator; delegates to resource factory/registration methods from individual resource files.
|
|
105
|
+
- `templates.ts`
|
|
106
|
+
- Registers two dynamic, template‑driven resources using `ResourceTemplate`:
|
|
107
|
+
- Text: `demo://resource/dynamic/text/{index}` (MIME: `text/plain`)
|
|
108
|
+
- Blob: `demo://resource/dynamic/blob/{index}` (MIME: `application/octet-stream`, Base64 payload)
|
|
109
|
+
- The `{index}` path variable must be a finite positive integer. Content is generated on demand with a timestamp.
|
|
110
|
+
- Exposes helpers `textResource(uri, index)`, `textResourceUri(index)`, `blobResource(uri, index)`, and `blobResourceUri(index)` so other modules can construct and embed dynamic resources directly (e.g., from prompts).
|
|
111
|
+
- `files.ts`
|
|
112
|
+
- Registers static file-based resources for each file in the `docs/` folder.
|
|
113
|
+
- URIs follow the pattern: `demo://resource/static/document/<filename>`.
|
|
114
|
+
- Serves markdown files as `text/markdown`, `.txt` as `text/plain`, `.json` as `application/json`, others default to `text/plain`.
|
|
115
|
+
|
|
116
|
+
### `server/`
|
|
117
|
+
|
|
118
|
+
- `index.ts`
|
|
119
|
+
- Server factory that creates an `McpServer` with declared capabilities, loads server instructions, and registers tools, prompts, and resources.
|
|
120
|
+
- Sets resource subscription handlers via `setSubscriptionHandlers(server)`.
|
|
121
|
+
- Exposes `{ server, cleanup }` to the chosen transport. Cleanup stops any running intervals in the server when the transport disconnects.
|
|
122
|
+
- `logging.ts`
|
|
123
|
+
- Implements simulated logging. Periodically sends randomized log messages at various levels to the connected client session. Started/stopped on demand via a dedicated tool.
|
|
124
|
+
|
|
125
|
+
### `tools/`
|
|
126
|
+
|
|
127
|
+
- `index.ts`
|
|
128
|
+
- `registerTools(server)` orchestrator; delegates to tool factory/registration methods in individual tool files.
|
|
129
|
+
- `echo.ts`
|
|
130
|
+
- Registers an `echo` tool that takes a message and returns `Echo: {message}`.
|
|
131
|
+
- `get-annotated-message.ts`
|
|
132
|
+
- Registers an `annotated-message` tool which demonstrates annotated content items by emitting a primary `text` message with `annotations` that vary by `messageType` (`"error" | "success" | "debug"`), and optionally includes an annotated `image` (tiny PNG) when `includeImage` is true.
|
|
133
|
+
- `get-env.ts`
|
|
134
|
+
- Registers a `get-env` tool that returns the current process environment variables as formatted JSON text; useful for debugging configuration.
|
|
135
|
+
- `get-resource-links.ts`
|
|
136
|
+
- Registers a `get-resource-links` tool that returns an intro `text` block followed by multiple `resource_link` items.
|
|
137
|
+
- `get-resource-reference.ts`
|
|
138
|
+
- Registers a `get-resource-reference` tool that returns a reference for a selected dynamic resource.
|
|
139
|
+
- `get-roots-list.ts`
|
|
140
|
+
- Registers a `get-roots-list` tool that returns the last list of roots sent by the client.
|
|
141
|
+
- `gzip-file-as-resource.ts`
|
|
142
|
+
- Registers a `gzip-file-as-resource` tool that fetches content from a URL or data URI, compresses it, and then either:
|
|
143
|
+
- returns a `resource_link` to a session-scoped resource (default), or
|
|
144
|
+
- returns an inline `resource` with the gzipped data. The resource will be still discoverable for the duration of the session via `resources/list`.
|
|
145
|
+
- Uses `resources/session.ts` to register the gzipped blob as a per-session resource at a URI like `demo://resource/session/<name>` with `mimeType: application/gzip`.
|
|
146
|
+
- Environment controls:
|
|
147
|
+
- `GZIP_MAX_FETCH_SIZE` (bytes, default 10 MiB)
|
|
148
|
+
- `GZIP_MAX_FETCH_TIME_MILLIS` (ms, default 30000)
|
|
149
|
+
- `GZIP_ALLOWED_DOMAINS` (comma-separated allowlist; empty means all domains allowed)
|
|
150
|
+
- `trigger-elicitation-request.ts`
|
|
151
|
+
- Registers a `trigger-elicitation-request` tool that sends an `elicitation/create` request to the client/LLM and returns the elicitation result.
|
|
152
|
+
- `trigger-sampling-request.ts`
|
|
153
|
+
- Registers a `trigger-sampling-request` tool that sends a `sampling/createMessage` request to the client/LLM and returns the sampling result.
|
|
154
|
+
- `get-structured-content.ts`
|
|
155
|
+
- Registers a `get-structured-content` tool that demonstrates structuredContent block responses.
|
|
156
|
+
- `get-sum.ts`
|
|
157
|
+
- Registers an `get-sum` tool with a Zod input schema that sums two numbers `a` and `b` and returns the result.
|
|
158
|
+
- `get-tiny-image.ts`
|
|
159
|
+
- Registers a `get-tiny-image` tool, which returns a tiny PNG MCP logo as an `image` content item, along with surrounding descriptive `text` items.
|
|
160
|
+
- `trigger-long-running-operation.ts`
|
|
161
|
+
- Registers a `long-running-operation` tool that simulates a long-running task over a specified `duration` (seconds) and number of `steps`; emits `notifications/progress` updates when the client supplies a `progressToken`.
|
|
162
|
+
- `toggle-simulated-logging.ts`
|
|
163
|
+
- Registers a `toggle-simulated-logging` tool, which starts or stops simulated logging for the invoking session.
|
|
164
|
+
- `toggle-subscriber-updates.ts`
|
|
165
|
+
- Registers a `toggle-subscriber-updates` tool, which starts or stops simulated resource subscription update checks for the invoking session.
|
|
166
|
+
|
|
167
|
+
### `transports/`
|
|
168
|
+
|
|
169
|
+
- `stdio.ts`
|
|
170
|
+
- Starts a `StdioServerTransport`, created the server via `createServer()`, and connects it.
|
|
171
|
+
- Handles `SIGINT` to close cleanly and calls `cleanup()` to remove any live intervals.
|
|
172
|
+
- `sse.ts`
|
|
173
|
+
- Express server exposing:
|
|
174
|
+
- `GET /sse` to establish an SSE connection per session.
|
|
175
|
+
- `POST /message` for client messages.
|
|
176
|
+
- Manages multiple connected clients via a transport map.
|
|
177
|
+
- Starts an `SSEServerTransport`, created the server via `createServer()`, and connects it to a new transport.
|
|
178
|
+
- On server disconnect, calls `cleanup()` to remove any live intervals.
|
|
179
|
+
- `streamableHttp.ts`
|
|
180
|
+
- Express server exposing a single `/mcp` endpoint for POST (JSON‑RPC), GET (SSE stream), and DELETE (session termination) using `StreamableHTTPServerTransport`.
|
|
181
|
+
- Uses an `InMemoryEventStore` for resumable sessions and tracks transports by `sessionId`.
|
|
182
|
+
- Connects a fresh server instance on initialization POST and reuses the transport for subsequent requests.
|
package/dist/index.js
CHANGED
|
@@ -1,36 +1,41 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Parse command line arguments first
|
|
3
3
|
const args = process.argv.slice(2);
|
|
4
|
-
const scriptName = args[0] ||
|
|
4
|
+
const scriptName = args[0] || "stdio";
|
|
5
5
|
async function run() {
|
|
6
6
|
try {
|
|
7
7
|
// Dynamically import only the requested module to prevent all modules from initializing
|
|
8
8
|
switch (scriptName) {
|
|
9
|
-
case
|
|
9
|
+
case "stdio":
|
|
10
10
|
// Import and run the default server
|
|
11
|
-
await import(
|
|
11
|
+
await import("./transports/stdio.js");
|
|
12
12
|
break;
|
|
13
|
-
case
|
|
13
|
+
case "sse":
|
|
14
14
|
// Import and run the SSE server
|
|
15
|
-
await import(
|
|
15
|
+
await import("./transports/sse.js");
|
|
16
16
|
break;
|
|
17
|
-
case
|
|
17
|
+
case "streamableHttp":
|
|
18
18
|
// Import and run the streamable HTTP server
|
|
19
|
-
await import(
|
|
19
|
+
await import("./transports/streamableHttp.js");
|
|
20
20
|
break;
|
|
21
21
|
default:
|
|
22
|
-
console.error(
|
|
23
|
-
console.
|
|
24
|
-
console.
|
|
25
|
-
console.
|
|
26
|
-
console.
|
|
22
|
+
console.error(`-`.repeat(53));
|
|
23
|
+
console.error(` Everything Server Launcher`);
|
|
24
|
+
console.error(` Usage: node ./index.js [stdio|sse|streamableHttp]`);
|
|
25
|
+
console.error(` Default transport: stdio`);
|
|
26
|
+
console.error(`-`.repeat(53));
|
|
27
|
+
console.error(`Unknown transport: ${scriptName}`);
|
|
28
|
+
console.log("Available transports:");
|
|
29
|
+
console.log("- stdio");
|
|
30
|
+
console.log("- sse");
|
|
31
|
+
console.log("- streamableHttp");
|
|
27
32
|
process.exit(1);
|
|
28
33
|
}
|
|
29
34
|
}
|
|
30
35
|
catch (error) {
|
|
31
|
-
console.error(
|
|
36
|
+
console.error("Error running script:", error);
|
|
32
37
|
process.exit(1);
|
|
33
38
|
}
|
|
34
39
|
}
|
|
35
|
-
run();
|
|
40
|
+
await run();
|
|
36
41
|
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Register a prompt with arguments
|
|
4
|
+
* - Two arguments, one required and one optional
|
|
5
|
+
* - Combines argument values in the returned prompt
|
|
6
|
+
*
|
|
7
|
+
* @param server
|
|
8
|
+
*/
|
|
9
|
+
export const registerArgumentsPrompt = (server) => {
|
|
10
|
+
// Prompt arguments
|
|
11
|
+
const promptArgsSchema = {
|
|
12
|
+
city: z.string().describe("Name of the city"),
|
|
13
|
+
state: z.string().describe("Name of the state").optional(),
|
|
14
|
+
};
|
|
15
|
+
// Register the prompt
|
|
16
|
+
server.registerPrompt("args-prompt", {
|
|
17
|
+
title: "Arguments Prompt",
|
|
18
|
+
description: "A prompt with two arguments, one required and one optional",
|
|
19
|
+
argsSchema: promptArgsSchema,
|
|
20
|
+
}, (args) => {
|
|
21
|
+
const location = `${args?.city}${args?.state ? `, ${args?.state}` : ""}`;
|
|
22
|
+
return {
|
|
23
|
+
messages: [
|
|
24
|
+
{
|
|
25
|
+
role: "user",
|
|
26
|
+
content: {
|
|
27
|
+
type: "text",
|
|
28
|
+
text: `What's weather in ${location}?`,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { completable } from "@modelcontextprotocol/sdk/server/completable.js";
|
|
3
|
+
/**
|
|
4
|
+
* Register a prompt with completable arguments
|
|
5
|
+
* - Two required arguments, both with completion handlers
|
|
6
|
+
* - First argument value will be included in context for second argument
|
|
7
|
+
* - Allows second argument to depend on the first argument value
|
|
8
|
+
*
|
|
9
|
+
* @param server
|
|
10
|
+
*/
|
|
11
|
+
export const registerPromptWithCompletions = (server) => {
|
|
12
|
+
// Prompt arguments
|
|
13
|
+
const promptArgsSchema = {
|
|
14
|
+
department: completable(z.string().describe("Choose the department."), (value) => {
|
|
15
|
+
return ["Engineering", "Sales", "Marketing", "Support"].filter((d) => d.startsWith(value));
|
|
16
|
+
}),
|
|
17
|
+
name: completable(z
|
|
18
|
+
.string()
|
|
19
|
+
.describe("Choose a team member to lead the selected department."), (value, context) => {
|
|
20
|
+
const department = context?.arguments?.["department"];
|
|
21
|
+
if (department === "Engineering") {
|
|
22
|
+
return ["Alice", "Bob", "Charlie"].filter((n) => n.startsWith(value));
|
|
23
|
+
}
|
|
24
|
+
else if (department === "Sales") {
|
|
25
|
+
return ["David", "Eve", "Frank"].filter((n) => n.startsWith(value));
|
|
26
|
+
}
|
|
27
|
+
else if (department === "Marketing") {
|
|
28
|
+
return ["Grace", "Henry", "Iris"].filter((n) => n.startsWith(value));
|
|
29
|
+
}
|
|
30
|
+
else if (department === "Support") {
|
|
31
|
+
return ["John", "Kim", "Lee"].filter((n) => n.startsWith(value));
|
|
32
|
+
}
|
|
33
|
+
return [];
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
// Register the prompt
|
|
37
|
+
server.registerPrompt("completable-prompt", {
|
|
38
|
+
title: "Team Management",
|
|
39
|
+
description: "First argument choice narrows values for second argument.",
|
|
40
|
+
argsSchema: promptArgsSchema,
|
|
41
|
+
}, ({ department, name }) => ({
|
|
42
|
+
messages: [
|
|
43
|
+
{
|
|
44
|
+
role: "user",
|
|
45
|
+
content: {
|
|
46
|
+
type: "text",
|
|
47
|
+
text: `Please promote ${name} to the head of the ${department} team.`,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
}));
|
|
52
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { registerSimplePrompt } from "./simple.js";
|
|
2
|
+
import { registerArgumentsPrompt } from "./args.js";
|
|
3
|
+
import { registerPromptWithCompletions } from "./completions.js";
|
|
4
|
+
import { registerEmbeddedResourcePrompt } from "./resource.js";
|
|
5
|
+
/**
|
|
6
|
+
* Register the prompts with the MCP server.
|
|
7
|
+
*
|
|
8
|
+
* @param server
|
|
9
|
+
*/
|
|
10
|
+
export const registerPrompts = (server) => {
|
|
11
|
+
registerSimplePrompt(server);
|
|
12
|
+
registerArgumentsPrompt(server);
|
|
13
|
+
registerPromptWithCompletions(server);
|
|
14
|
+
registerEmbeddedResourcePrompt(server);
|
|
15
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { resourceTypeCompleter, resourceIdForPromptCompleter, } from "../resources/templates.js";
|
|
2
|
+
import { textResource, textResourceUri, blobResourceUri, blobResource, RESOURCE_TYPE_BLOB, RESOURCE_TYPE_TEXT, RESOURCE_TYPES, } from "../resources/templates.js";
|
|
3
|
+
/**
|
|
4
|
+
* Register a prompt with an embedded resource reference
|
|
5
|
+
* - Takes a resource type and id
|
|
6
|
+
* - Returns the corresponding dynamically created resource
|
|
7
|
+
*
|
|
8
|
+
* @param server
|
|
9
|
+
*/
|
|
10
|
+
export const registerEmbeddedResourcePrompt = (server) => {
|
|
11
|
+
// Prompt arguments
|
|
12
|
+
const promptArgsSchema = {
|
|
13
|
+
resourceType: resourceTypeCompleter,
|
|
14
|
+
resourceId: resourceIdForPromptCompleter,
|
|
15
|
+
};
|
|
16
|
+
// Register the prompt
|
|
17
|
+
server.registerPrompt("resource-prompt", {
|
|
18
|
+
title: "Resource Prompt",
|
|
19
|
+
description: "A prompt that includes an embedded resource reference",
|
|
20
|
+
argsSchema: promptArgsSchema,
|
|
21
|
+
}, (args) => {
|
|
22
|
+
// Validate resource type argument
|
|
23
|
+
const resourceType = args.resourceType;
|
|
24
|
+
if (!RESOURCE_TYPES.includes(resourceType)) {
|
|
25
|
+
throw new Error(`Invalid resourceType: ${args?.resourceType}. Must be ${RESOURCE_TYPE_TEXT} or ${RESOURCE_TYPE_BLOB}.`);
|
|
26
|
+
}
|
|
27
|
+
// Validate resourceId argument
|
|
28
|
+
const resourceId = Number(args?.resourceId);
|
|
29
|
+
if (!Number.isFinite(resourceId) ||
|
|
30
|
+
!Number.isInteger(resourceId) ||
|
|
31
|
+
resourceId < 1) {
|
|
32
|
+
throw new Error(`Invalid resourceId: ${args?.resourceId}. Must be a finite positive integer.`);
|
|
33
|
+
}
|
|
34
|
+
// Get resource based on the resource type
|
|
35
|
+
const uri = resourceType === RESOURCE_TYPE_TEXT
|
|
36
|
+
? textResourceUri(resourceId)
|
|
37
|
+
: blobResourceUri(resourceId);
|
|
38
|
+
const resource = resourceType === RESOURCE_TYPE_TEXT
|
|
39
|
+
? textResource(uri, resourceId)
|
|
40
|
+
: blobResource(uri, resourceId);
|
|
41
|
+
return {
|
|
42
|
+
messages: [
|
|
43
|
+
{
|
|
44
|
+
role: "user",
|
|
45
|
+
content: {
|
|
46
|
+
type: "text",
|
|
47
|
+
text: `This prompt includes the ${resourceType} resource with id: ${resourceId}. Please analyze the following resource:`,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
role: "user",
|
|
52
|
+
content: {
|
|
53
|
+
type: "resource",
|
|
54
|
+
resource: resource,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register a simple prompt with no arguments
|
|
3
|
+
* - Returns the fixed text of the prompt with no modifications
|
|
4
|
+
*
|
|
5
|
+
* @param server
|
|
6
|
+
*/
|
|
7
|
+
export const registerSimplePrompt = (server) => {
|
|
8
|
+
// Register the prompt
|
|
9
|
+
server.registerPrompt("simple-prompt", {
|
|
10
|
+
title: "Simple Prompt",
|
|
11
|
+
description: "A prompt with no arguments",
|
|
12
|
+
}, () => ({
|
|
13
|
+
messages: [
|
|
14
|
+
{
|
|
15
|
+
role: "user",
|
|
16
|
+
content: {
|
|
17
|
+
type: "text",
|
|
18
|
+
text: "This is a simple prompt without arguments.",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
}));
|
|
23
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { dirname, join } from "path";
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import { readdirSync, readFileSync, statSync } from "fs";
|
|
4
|
+
/**
|
|
5
|
+
* Register static file resources
|
|
6
|
+
* - Each file in src/everything/docs is exposed as an individual static resource
|
|
7
|
+
* - URIs follow the pattern: "demo://static/docs/<filename>"
|
|
8
|
+
* - Markdown (.md) files are served as mime type "text/markdown"
|
|
9
|
+
* - Text (.txt) files are served as mime type "text/plain"
|
|
10
|
+
* - JSON (.json) files are served as mime type "application/json"
|
|
11
|
+
*
|
|
12
|
+
* @param server
|
|
13
|
+
*/
|
|
14
|
+
export const registerFileResources = (server) => {
|
|
15
|
+
// Read the entries in the docs directory
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = dirname(__filename);
|
|
18
|
+
const docsDir = join(__dirname, "..", "docs");
|
|
19
|
+
let entries = [];
|
|
20
|
+
try {
|
|
21
|
+
entries = readdirSync(docsDir);
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
// If docs/ folder is missing or unreadable, just skip registration
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// Register each file as a static resource
|
|
28
|
+
for (const name of entries) {
|
|
29
|
+
// Only process files, not directories
|
|
30
|
+
const fullPath = join(docsDir, name);
|
|
31
|
+
try {
|
|
32
|
+
const st = statSync(fullPath);
|
|
33
|
+
if (!st.isFile())
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
// Prepare file resource info
|
|
40
|
+
const uri = `demo://resource/static/document/${encodeURIComponent(name)}`;
|
|
41
|
+
const mimeType = getMimeType(name);
|
|
42
|
+
const description = `Static document file exposed from /docs: ${name}`;
|
|
43
|
+
// Register file resource
|
|
44
|
+
server.registerResource(name, uri, { mimeType, description }, async (uri) => {
|
|
45
|
+
const text = readFileSafe(fullPath);
|
|
46
|
+
return {
|
|
47
|
+
contents: [
|
|
48
|
+
{
|
|
49
|
+
uri: uri.toString(),
|
|
50
|
+
mimeType,
|
|
51
|
+
text,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Get the mimetype based on filename
|
|
60
|
+
* @param fileName
|
|
61
|
+
*/
|
|
62
|
+
function getMimeType(fileName) {
|
|
63
|
+
const lower = fileName.toLowerCase();
|
|
64
|
+
if (lower.endsWith(".md") || lower.endsWith(".markdown"))
|
|
65
|
+
return "text/markdown";
|
|
66
|
+
if (lower.endsWith(".txt"))
|
|
67
|
+
return "text/plain";
|
|
68
|
+
if (lower.endsWith(".json"))
|
|
69
|
+
return "application/json";
|
|
70
|
+
return "text/plain";
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Read a file or return an error message if it fails
|
|
74
|
+
* @param path
|
|
75
|
+
*/
|
|
76
|
+
function readFileSafe(path) {
|
|
77
|
+
try {
|
|
78
|
+
return readFileSync(path, "utf-8");
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
return `Error reading file: ${path}. ${e}`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { registerResourceTemplates } from "./templates.js";
|
|
2
|
+
import { registerFileResources } from "./files.js";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { dirname, join } from "path";
|
|
5
|
+
import { readFileSync } from "fs";
|
|
6
|
+
/**
|
|
7
|
+
* Register the resources with the MCP server.
|
|
8
|
+
* @param server
|
|
9
|
+
*/
|
|
10
|
+
export const registerResources = (server) => {
|
|
11
|
+
registerResourceTemplates(server);
|
|
12
|
+
registerFileResources(server);
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Reads the server instructions from the corresponding markdown file.
|
|
16
|
+
* Attempts to load the content of the file located in the `docs` directory.
|
|
17
|
+
* If the file cannot be loaded, an error message is returned instead.
|
|
18
|
+
*
|
|
19
|
+
* @return {string} The content of the server instructions file, or an error message if reading fails.
|
|
20
|
+
*/
|
|
21
|
+
export function readInstructions() {
|
|
22
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
23
|
+
const __dirname = dirname(__filename);
|
|
24
|
+
const filePath = join(__dirname, "..", "docs", "instructions.md");
|
|
25
|
+
let instructions;
|
|
26
|
+
try {
|
|
27
|
+
instructions = readFileSync(filePath, "utf-8");
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
instructions = "Server instructions not loaded: " + e;
|
|
31
|
+
}
|
|
32
|
+
return instructions;
|
|
33
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a session-scoped resource URI string based on the provided resource name.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} name - The name of the resource to create a URI for.
|
|
5
|
+
* @returns {string} The formatted session resource URI.
|
|
6
|
+
*/
|
|
7
|
+
export const getSessionResourceURI = (name) => {
|
|
8
|
+
return `demo://resource/session/${name}`;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Registers a session-scoped resource with the provided server and returns a resource link.
|
|
12
|
+
*
|
|
13
|
+
* The registered resource is available during the life of the session only; it is not otherwise persisted.
|
|
14
|
+
*
|
|
15
|
+
* @param {McpServer} server - The server instance responsible for handling the resource registration.
|
|
16
|
+
* @param {Resource} resource - The resource object containing metadata such as URI, name, description, and mimeType.
|
|
17
|
+
* @param {"text"|"blob"} type
|
|
18
|
+
* @param payload
|
|
19
|
+
* @returns {ResourceLink} An object representing the resource link, with associated metadata.
|
|
20
|
+
*/
|
|
21
|
+
export const registerSessionResource = (server, resource, type, payload) => {
|
|
22
|
+
// Destructure resource
|
|
23
|
+
const { uri, name, mimeType, description, title, annotations, icons, _meta } = resource;
|
|
24
|
+
// Prepare the resource content to return
|
|
25
|
+
// See https://modelcontextprotocol.io/specification/2025-11-25/server/resources#resource-contents
|
|
26
|
+
const resourceContent = type === "text"
|
|
27
|
+
? {
|
|
28
|
+
uri: uri.toString(),
|
|
29
|
+
mimeType,
|
|
30
|
+
text: payload,
|
|
31
|
+
}
|
|
32
|
+
: {
|
|
33
|
+
uri: uri.toString(),
|
|
34
|
+
mimeType,
|
|
35
|
+
blob: payload,
|
|
36
|
+
};
|
|
37
|
+
// Register file resource
|
|
38
|
+
server.registerResource(name, uri, { mimeType, description, title, annotations, icons, _meta }, async (uri) => {
|
|
39
|
+
return {
|
|
40
|
+
contents: [resourceContent],
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
return { type: "resource_link", ...resource };
|
|
44
|
+
};
|