@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.
Files changed (49) hide show
  1. package/README.md +9 -158
  2. package/dist/docs/architecture.md +44 -0
  3. package/dist/docs/extension.md +23 -0
  4. package/dist/docs/features.md +103 -0
  5. package/dist/docs/how-it-works.md +45 -0
  6. package/dist/docs/instructions.md +28 -0
  7. package/dist/docs/startup.md +73 -0
  8. package/dist/docs/structure.md +182 -0
  9. package/dist/index.js +19 -14
  10. package/dist/prompts/args.js +34 -0
  11. package/dist/prompts/completions.js +52 -0
  12. package/dist/prompts/index.js +15 -0
  13. package/dist/prompts/resource.js +60 -0
  14. package/dist/prompts/simple.js +23 -0
  15. package/dist/resources/files.js +83 -0
  16. package/dist/resources/index.js +33 -0
  17. package/dist/resources/session.js +44 -0
  18. package/dist/resources/subscriptions.js +125 -0
  19. package/dist/resources/templates.js +171 -0
  20. package/dist/server/index.js +93 -0
  21. package/dist/server/logging.js +64 -0
  22. package/dist/server/roots.js +65 -0
  23. package/dist/tools/echo.js +29 -0
  24. package/dist/tools/get-annotated-message.js +81 -0
  25. package/dist/tools/get-env.js +28 -0
  26. package/dist/tools/get-resource-links.js +62 -0
  27. package/dist/tools/get-resource-reference.js +74 -0
  28. package/dist/tools/get-roots-list.js +71 -0
  29. package/dist/tools/get-structured-content.js +72 -0
  30. package/dist/tools/get-sum.js +40 -0
  31. package/dist/tools/get-tiny-image.js +41 -0
  32. package/dist/tools/gzip-file-as-resource.js +182 -0
  33. package/dist/tools/index.js +50 -0
  34. package/dist/tools/simulate-research-query.js +249 -0
  35. package/dist/tools/toggle-simulated-logging.js +41 -0
  36. package/dist/tools/toggle-subscriber-updates.js +44 -0
  37. package/dist/tools/trigger-elicitation-request-async.js +202 -0
  38. package/dist/tools/trigger-elicitation-request.js +210 -0
  39. package/dist/tools/trigger-long-running-operation.js +59 -0
  40. package/dist/tools/trigger-sampling-request-async.js +168 -0
  41. package/dist/tools/trigger-sampling-request.js +71 -0
  42. package/dist/{sse.js → transports/sse.js} +25 -17
  43. package/dist/transports/stdio.js +27 -0
  44. package/dist/transports/streamableHttp.js +206 -0
  45. package/package.json +10 -7
  46. package/dist/everything.js +0 -978
  47. package/dist/instructions.md +0 -23
  48. package/dist/stdio.js +0 -23
  49. package/dist/streamableHttp.js +0 -174
@@ -0,0 +1,62 @@
1
+ import { z } from "zod";
2
+ import { textResource, textResourceUri, blobResourceUri, blobResource, } from "../resources/templates.js";
3
+ // Tool input schema
4
+ const GetResourceLinksSchema = z.object({
5
+ count: z
6
+ .number()
7
+ .min(1)
8
+ .max(10)
9
+ .default(3)
10
+ .describe("Number of resource links to return (1-10)"),
11
+ });
12
+ // Tool configuration
13
+ const name = "get-resource-links";
14
+ const config = {
15
+ title: "Get Resource Links Tool",
16
+ description: "Returns up to ten resource links that reference different types of resources",
17
+ inputSchema: GetResourceLinksSchema,
18
+ };
19
+ /**
20
+ * Registers the 'get-resource-reference' tool.
21
+ *
22
+ * The registered tool retrieves a specified number of resource links and their metadata.
23
+ * Resource links are dynamically generated as either text or binary blob resources,
24
+ * based on their ID being even or odd.
25
+
26
+ * The response contains a "text" introductory block and multiple "resource_link" blocks.
27
+ *
28
+ * @param {McpServer} server - The McpServer instance where the tool will be registered.
29
+ */
30
+ export const registerGetResourceLinksTool = (server) => {
31
+ server.registerTool(name, config, async (args) => {
32
+ const { count } = GetResourceLinksSchema.parse(args);
33
+ // Add intro text content block
34
+ const content = [];
35
+ content.push({
36
+ type: "text",
37
+ text: `Here are ${count} resource links to resources available in this server:`,
38
+ });
39
+ // Create resource link content blocks
40
+ for (let resourceId = 1; resourceId <= count; resourceId++) {
41
+ // Get resource uri for text or blob resource based on odd/even resourceId
42
+ const isOdd = resourceId % 2 === 0;
43
+ const uri = isOdd
44
+ ? textResourceUri(resourceId)
45
+ : blobResourceUri(resourceId);
46
+ // Get resource based on the resource type
47
+ const resource = isOdd
48
+ ? textResource(uri, resourceId)
49
+ : blobResource(uri, resourceId);
50
+ content.push({
51
+ type: "resource_link",
52
+ uri: resource.uri,
53
+ name: `${isOdd ? "Text" : "Blob"} Resource ${resourceId}`,
54
+ description: `Resource ${resourceId}: ${resource.mimeType === "text/plain"
55
+ ? "plaintext resource"
56
+ : "binary blob resource"}`,
57
+ mimeType: resource.mimeType,
58
+ });
59
+ }
60
+ return { content };
61
+ });
62
+ };
@@ -0,0 +1,74 @@
1
+ import { z } from "zod";
2
+ import { textResource, textResourceUri, blobResourceUri, blobResource, RESOURCE_TYPE_BLOB, RESOURCE_TYPE_TEXT, RESOURCE_TYPES, } from "../resources/templates.js";
3
+ // Tool input schema
4
+ const GetResourceReferenceSchema = z.object({
5
+ resourceType: z
6
+ .enum([RESOURCE_TYPE_TEXT, RESOURCE_TYPE_BLOB])
7
+ .default(RESOURCE_TYPE_TEXT),
8
+ resourceId: z
9
+ .number()
10
+ .default(1)
11
+ .describe("ID of the text resource to fetch"),
12
+ });
13
+ // Tool configuration
14
+ const name = "get-resource-reference";
15
+ const config = {
16
+ title: "Get Resource Reference Tool",
17
+ description: "Returns a resource reference that can be used by MCP clients",
18
+ inputSchema: GetResourceReferenceSchema,
19
+ };
20
+ /**
21
+ * Registers the 'get-resource-reference' tool.
22
+ *
23
+ * The registered tool validates and processes arguments for retrieving a resource
24
+ * reference. Supported resource types include predefined `RESOURCE_TYPE_TEXT` and
25
+ * `RESOURCE_TYPE_BLOB`. The retrieved resource's reference will include the resource
26
+ * ID, type, and its associated URI.
27
+ *
28
+ * The tool performs the following operations:
29
+ * 1. Validates the `resourceType` argument to ensure it matches a supported type.
30
+ * 2. Validates the `resourceId` argument to ensure it is a finite positive integer.
31
+ * 3. Constructs a URI for the resource based on its type (text or blob).
32
+ * 4. Retrieves the resource and returns it in a content block.
33
+ *
34
+ * @param {McpServer} server - The McpServer instance where the tool will be registered.
35
+ */
36
+ export const registerGetResourceReferenceTool = (server) => {
37
+ server.registerTool(name, config, async (args) => {
38
+ // Validate resource type argument
39
+ const { resourceType } = args;
40
+ if (!RESOURCE_TYPES.includes(resourceType)) {
41
+ throw new Error(`Invalid resourceType: ${args?.resourceType}. Must be ${RESOURCE_TYPE_TEXT} or ${RESOURCE_TYPE_BLOB}.`);
42
+ }
43
+ // Validate resourceId argument
44
+ const resourceId = Number(args?.resourceId);
45
+ if (!Number.isFinite(resourceId) ||
46
+ !Number.isInteger(resourceId) ||
47
+ resourceId < 1) {
48
+ throw new Error(`Invalid resourceId: ${args?.resourceId}. Must be a finite positive integer.`);
49
+ }
50
+ // Get resource based on the resource type
51
+ const uri = resourceType === RESOURCE_TYPE_TEXT
52
+ ? textResourceUri(resourceId)
53
+ : blobResourceUri(resourceId);
54
+ const resource = resourceType === RESOURCE_TYPE_TEXT
55
+ ? textResource(uri, resourceId)
56
+ : blobResource(uri, resourceId);
57
+ return {
58
+ content: [
59
+ {
60
+ type: "text",
61
+ text: `Returning resource reference for Resource ${resourceId}:`,
62
+ },
63
+ {
64
+ type: "resource",
65
+ resource: resource,
66
+ },
67
+ {
68
+ type: "text",
69
+ text: `You can access this resource using the URI: ${resource.uri}`,
70
+ },
71
+ ],
72
+ };
73
+ });
74
+ };
@@ -0,0 +1,71 @@
1
+ import { syncRoots } from "../server/roots.js";
2
+ // Tool configuration
3
+ const name = "get-roots-list";
4
+ const config = {
5
+ title: "Get Roots List Tool",
6
+ description: "Lists the current MCP roots provided by the client. Demonstrates the roots protocol capability even though this server doesn't access files.",
7
+ inputSchema: {},
8
+ };
9
+ /**
10
+ * Registers the 'get-roots-list' tool.
11
+ *
12
+ * If the client does not support the roots capability, the tool is not registered.
13
+ *
14
+ * The registered tool interacts with the MCP roots capability, which enables the server to access
15
+ * information about the client's workspace directories or file system roots.
16
+ *
17
+ * When supported, the server automatically retrieves and formats the current list of roots from the
18
+ * client upon connection and whenever the client sends a `roots/list_changed` notification.
19
+ *
20
+ * Therefore, this tool displays the roots that the server currently knows about for the connected
21
+ * client. If for some reason the server never got the initial roots list, the tool will request the
22
+ * list from the client again.
23
+ *
24
+ * @param {McpServer} server - The McpServer instance where the tool will be registered.
25
+ */
26
+ export const registerGetRootsListTool = (server) => {
27
+ // Does client support roots?
28
+ const clientCapabilities = server.server.getClientCapabilities() || {};
29
+ const clientSupportsRoots = clientCapabilities.roots !== undefined;
30
+ // If so, register tool
31
+ if (clientSupportsRoots) {
32
+ server.registerTool(name, config, async (args, extra) => {
33
+ // Get the current rootsFetch the current roots list from the client if need be
34
+ const currentRoots = await syncRoots(server, extra.sessionId);
35
+ // Respond if client supports roots but doesn't have any configured
36
+ if (clientSupportsRoots &&
37
+ (!currentRoots || currentRoots.length === 0)) {
38
+ return {
39
+ content: [
40
+ {
41
+ type: "text",
42
+ text: "The client supports roots but no roots are currently configured.\n\n" +
43
+ "This could mean:\n" +
44
+ "1. The client hasn't provided any roots yet\n" +
45
+ "2. The client provided an empty roots list\n" +
46
+ "3. The roots configuration is still being loaded",
47
+ },
48
+ ],
49
+ };
50
+ }
51
+ // Create formatted response if there is a list of roots
52
+ const rootsList = currentRoots
53
+ ? currentRoots
54
+ .map((root, index) => {
55
+ return `${index + 1}. ${root.name || "Unnamed Root"}\n URI: ${root.uri}`;
56
+ })
57
+ .join("\n\n")
58
+ : "No roots found";
59
+ return {
60
+ content: [
61
+ {
62
+ type: "text",
63
+ text: `Current MCP Roots (${currentRoots.length} total):\n\n${rootsList}\n\n` +
64
+ "Note: This server demonstrates the roots protocol capability but doesn't actually access files. " +
65
+ "The roots are provided by the MCP client and can be used by servers that need file system access.",
66
+ },
67
+ ],
68
+ };
69
+ });
70
+ }
71
+ };
@@ -0,0 +1,72 @@
1
+ import { z } from "zod";
2
+ // Tool input schema
3
+ const GetStructuredContentInputSchema = {
4
+ location: z
5
+ .enum(["New York", "Chicago", "Los Angeles"])
6
+ .describe("Choose city"),
7
+ };
8
+ // Tool output schema
9
+ const GetStructuredContentOutputSchema = z.object({
10
+ temperature: z.number().describe("Temperature in celsius"),
11
+ conditions: z.string().describe("Weather conditions description"),
12
+ humidity: z.number().describe("Humidity percentage"),
13
+ });
14
+ // Tool configuration
15
+ const name = "get-structured-content";
16
+ const config = {
17
+ title: "Get Structured Content Tool",
18
+ description: "Returns structured content along with an output schema for client data validation",
19
+ inputSchema: GetStructuredContentInputSchema,
20
+ outputSchema: GetStructuredContentOutputSchema,
21
+ };
22
+ /**
23
+ * Registers the 'get-structured-content' tool.
24
+ *
25
+ * The registered tool processes incoming arguments using a predefined input schema,
26
+ * generates structured content with weather information including temperature,
27
+ * conditions, and humidity, and returns both backward-compatible content blocks
28
+ * and structured content in the response.
29
+ *
30
+ * The response contains:
31
+ * - `content`: An array of content blocks, presented as JSON stringified objects.
32
+ * - `structuredContent`: A JSON structured representation of the weather data.
33
+ *
34
+ * @param {McpServer} server - The McpServer instance where the tool will be registered.
35
+ */
36
+ export const registerGetStructuredContentTool = (server) => {
37
+ server.registerTool(name, config, async (args) => {
38
+ // Get simulated weather for the chosen city
39
+ let weather;
40
+ switch (args.location) {
41
+ case "New York":
42
+ weather = {
43
+ temperature: 33,
44
+ conditions: "Cloudy",
45
+ humidity: 82,
46
+ };
47
+ break;
48
+ case "Chicago":
49
+ weather = {
50
+ temperature: 36,
51
+ conditions: "Light rain / drizzle",
52
+ humidity: 82,
53
+ };
54
+ break;
55
+ case "Los Angeles":
56
+ weather = {
57
+ temperature: 73,
58
+ conditions: "Sunny / Clear",
59
+ humidity: 48,
60
+ };
61
+ break;
62
+ }
63
+ const backwardCompatibleContentBlock = {
64
+ type: "text",
65
+ text: JSON.stringify(weather),
66
+ };
67
+ return {
68
+ content: [backwardCompatibleContentBlock],
69
+ structuredContent: weather,
70
+ };
71
+ });
72
+ };
@@ -0,0 +1,40 @@
1
+ import { z } from "zod";
2
+ // Tool input schema
3
+ const GetSumSchema = z.object({
4
+ a: z.number().describe("First number"),
5
+ b: z.number().describe("Second number"),
6
+ });
7
+ // Tool configuration
8
+ const name = "get-sum";
9
+ const config = {
10
+ title: "Get Sum Tool",
11
+ description: "Returns the sum of two numbers",
12
+ inputSchema: GetSumSchema,
13
+ };
14
+ /**
15
+ * Registers the 'get-sum' tool.
16
+ **
17
+ * The registered tool processes input arguments, validates them using a predefined schema,
18
+ * calculates the sum of two numeric values, and returns the result in a content block.
19
+ *
20
+ * Expects input arguments to conform to a specific schema that includes two numeric properties, `a` and `b`.
21
+ * Validation is performed to ensure the input adheres to the expected structure before calculating the sum.
22
+ *
23
+ * The result is returned as a Promise resolving to an object containing the computed sum in a text format.
24
+ *
25
+ * @param {McpServer} server - The McpServer instance where the tool will be registered.
26
+ */
27
+ export const registerGetSumTool = (server) => {
28
+ server.registerTool(name, config, async (args) => {
29
+ const validatedArgs = GetSumSchema.parse(args);
30
+ const sum = validatedArgs.a + validatedArgs.b;
31
+ return {
32
+ content: [
33
+ {
34
+ type: "text",
35
+ text: `The sum of ${validatedArgs.a} and ${validatedArgs.b} is ${sum}.`,
36
+ },
37
+ ],
38
+ };
39
+ });
40
+ };
@@ -0,0 +1,41 @@
1
+ // A tiny encoded MCP logo image
2
+ export const MCP_TINY_IMAGE = "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";
3
+ // Tool configuration
4
+ const name = "get-tiny-image";
5
+ const config = {
6
+ title: "Get Tiny Image Tool",
7
+ description: "Returns a tiny MCP logo image.",
8
+ inputSchema: {},
9
+ };
10
+ /**
11
+ * Registers the "get-tiny-image" tool.
12
+ *
13
+ * The registered tool returns a response containing a small image alongside some
14
+ * descriptive text.
15
+ *
16
+ * The response structure includes textual content before and after the image.
17
+ * The image is served as a PNG data type and represents the default MCP tiny image.
18
+ *
19
+ * @param server - The McpServer instance where the tool will be registered.
20
+ */
21
+ export const registerGetTinyImageTool = (server) => {
22
+ server.registerTool(name, config, async (args) => {
23
+ return {
24
+ content: [
25
+ {
26
+ type: "text",
27
+ text: "Here's the image you requested:",
28
+ },
29
+ {
30
+ type: "image",
31
+ data: MCP_TINY_IMAGE,
32
+ mimeType: "image/png",
33
+ },
34
+ {
35
+ type: "text",
36
+ text: "The image above is the MCP logo.",
37
+ },
38
+ ],
39
+ };
40
+ });
41
+ };
@@ -0,0 +1,182 @@
1
+ import { z } from "zod";
2
+ import { gzipSync } from "node:zlib";
3
+ import { getSessionResourceURI, registerSessionResource, } from "../resources/session.js";
4
+ // Maximum input file size - 10 MB default
5
+ const GZIP_MAX_FETCH_SIZE = Number(process.env.GZIP_MAX_FETCH_SIZE ?? String(10 * 1024 * 1024));
6
+ // Maximum fetch time - 30 seconds default.
7
+ const GZIP_MAX_FETCH_TIME_MILLIS = Number(process.env.GZIP_MAX_FETCH_TIME_MILLIS ?? String(30 * 1000));
8
+ // Comma-separated list of allowed domains. Empty means all domains are allowed.
9
+ const GZIP_ALLOWED_DOMAINS = (process.env.GZIP_ALLOWED_DOMAINS ?? "")
10
+ .split(",")
11
+ .map((d) => d.trim().toLowerCase())
12
+ .filter((d) => d.length > 0);
13
+ // Tool input schema
14
+ const GZipFileAsResourceSchema = z.object({
15
+ name: z.string().describe("Name of the output file").default("README.md.gz"),
16
+ data: z
17
+ .string()
18
+ .url()
19
+ .describe("URL or data URI of the file content to compress")
20
+ .default("https://raw.githubusercontent.com/modelcontextprotocol/servers/refs/heads/main/README.md"),
21
+ outputType: z
22
+ .enum(["resourceLink", "resource"])
23
+ .default("resourceLink")
24
+ .describe("How the resulting gzipped file should be returned. 'resourceLink' returns a link to a resource that can be read later, 'resource' returns a full resource object."),
25
+ });
26
+ // Tool configuration
27
+ const name = "gzip-file-as-resource";
28
+ const config = {
29
+ title: "GZip File as Resource Tool",
30
+ description: "Compresses a single file using gzip compression. Depending upon the selected output type, returns either the compressed data as a gzipped resource or a resource link, allowing it to be downloaded in a subsequent request during the current session.",
31
+ inputSchema: GZipFileAsResourceSchema,
32
+ };
33
+ /**
34
+ * Registers the `gzip-file-as-resource` tool.
35
+ *
36
+ * The registered tool compresses input data using gzip, and makes the resulting file accessible
37
+ * as a resource for the duration of the session.
38
+ *
39
+ * The tool supports two output types:
40
+ * - "resource": Returns the resource directly, including its URI, MIME type, and base64-encoded content.
41
+ * - "resourceLink": Returns a link to access the resource later.
42
+ *
43
+ * If an unrecognized `outputType` is provided, the tool throws an error.
44
+ *
45
+ * @param {McpServer} server - The McpServer instance where the tool will be registered.
46
+ * @throws {Error} Throws an error if an unknown output type is specified.
47
+ */
48
+ export const registerGZipFileAsResourceTool = (server) => {
49
+ server.registerTool(name, config, async (args) => {
50
+ const { name, data: dataUri, outputType, } = GZipFileAsResourceSchema.parse(args);
51
+ // Validate data uri
52
+ const url = validateDataURI(dataUri);
53
+ // Fetch the data
54
+ const response = await fetchSafely(url, {
55
+ maxBytes: GZIP_MAX_FETCH_SIZE,
56
+ timeoutMillis: GZIP_MAX_FETCH_TIME_MILLIS,
57
+ });
58
+ // Compress the data using gzip
59
+ const inputBuffer = Buffer.from(response);
60
+ const compressedBuffer = gzipSync(inputBuffer);
61
+ // Create resource
62
+ const uri = getSessionResourceURI(name);
63
+ const blob = compressedBuffer.toString("base64");
64
+ const mimeType = "application/gzip";
65
+ const resource = { uri, name, mimeType };
66
+ // Register resource, get resource link in return
67
+ const resourceLink = registerSessionResource(server, resource, "blob", blob);
68
+ // Return the resource or a resource link that can be used to access this resource later
69
+ if (outputType === "resource") {
70
+ return {
71
+ content: [
72
+ {
73
+ type: "resource",
74
+ resource: { uri, mimeType, blob },
75
+ },
76
+ ],
77
+ };
78
+ }
79
+ else if (outputType === "resourceLink") {
80
+ return {
81
+ content: [resourceLink],
82
+ };
83
+ }
84
+ else {
85
+ throw new Error(`Unknown outputType: ${outputType}`);
86
+ }
87
+ });
88
+ };
89
+ /**
90
+ * Validates a given data URI to ensure it follows the appropriate protocols and rules.
91
+ *
92
+ * @param {string} dataUri - The data URI to validate. Must be an HTTP, HTTPS, or data protocol URL. If a domain is provided, it must match the allowed domains list if applicable.
93
+ * @return {URL} The validated and parsed URL object.
94
+ * @throws {Error} If the data URI does not use a supported protocol or does not meet allowed domains criteria.
95
+ */
96
+ function validateDataURI(dataUri) {
97
+ // Validate Inputs
98
+ const url = new URL(dataUri);
99
+ try {
100
+ if (url.protocol !== "http:" &&
101
+ url.protocol !== "https:" &&
102
+ url.protocol !== "data:") {
103
+ throw new Error(`Unsupported URL protocol for ${dataUri}. Only http, https, and data URLs are supported.`);
104
+ }
105
+ if (GZIP_ALLOWED_DOMAINS.length > 0 &&
106
+ (url.protocol === "http:" || url.protocol === "https:")) {
107
+ const domain = url.hostname;
108
+ const domainAllowed = GZIP_ALLOWED_DOMAINS.some((allowedDomain) => {
109
+ return domain === allowedDomain || domain.endsWith(`.${allowedDomain}`);
110
+ });
111
+ if (!domainAllowed) {
112
+ throw new Error(`Domain ${domain} is not in the allowed domains list.`);
113
+ }
114
+ }
115
+ }
116
+ catch (error) {
117
+ throw new Error(`Error processing file ${dataUri}: ${error instanceof Error ? error.message : String(error)}`);
118
+ }
119
+ return url;
120
+ }
121
+ /**
122
+ * Fetches data safely from a given URL while ensuring constraints on maximum byte size and timeout duration.
123
+ *
124
+ * @param {URL} url The URL to fetch data from.
125
+ * @param {Object} options An object containing options for the fetch operation.
126
+ * @param {number} options.maxBytes The maximum allowed size (in bytes) of the response. If the response exceeds this size, the operation will be aborted.
127
+ * @param {number} options.timeoutMillis The timeout duration (in milliseconds) for the fetch operation. If the fetch takes longer, it will be aborted.
128
+ * @return {Promise<ArrayBuffer>} A promise that resolves with the response as an ArrayBuffer if successful.
129
+ * @throws {Error} Throws an error if the response size exceeds the defined limit, the fetch times out, or the response is otherwise invalid.
130
+ */
131
+ async function fetchSafely(url, { maxBytes, timeoutMillis }) {
132
+ const controller = new AbortController();
133
+ const timeout = setTimeout(() => controller.abort(`Fetching ${url} took more than ${timeoutMillis} ms and was aborted.`), timeoutMillis);
134
+ try {
135
+ // Fetch the data
136
+ const response = await fetch(url, { signal: controller.signal });
137
+ if (!response.body) {
138
+ throw new Error("No response body");
139
+ }
140
+ // Note: we can't trust the Content-Length header: a malicious or clumsy server could return much more data than advertised.
141
+ // We check it here for early bail-out, but we still need to monitor actual bytes read below.
142
+ const contentLengthHeader = response.headers.get("content-length");
143
+ if (contentLengthHeader != null) {
144
+ const contentLength = parseInt(contentLengthHeader, 10);
145
+ if (contentLength > maxBytes) {
146
+ throw new Error(`Content-Length for ${url} exceeds max of ${maxBytes}: ${contentLength}`);
147
+ }
148
+ }
149
+ // Read the fetched data from the response body
150
+ const reader = response.body.getReader();
151
+ const chunks = [];
152
+ let totalSize = 0;
153
+ // Read chunks until done
154
+ try {
155
+ while (true) {
156
+ const { done, value } = await reader.read();
157
+ if (done)
158
+ break;
159
+ totalSize += value.length;
160
+ if (totalSize > maxBytes) {
161
+ reader.cancel();
162
+ throw new Error(`Response from ${url} exceeds ${maxBytes} bytes`);
163
+ }
164
+ chunks.push(value);
165
+ }
166
+ }
167
+ finally {
168
+ reader.releaseLock();
169
+ }
170
+ // Combine chunks into a single buffer
171
+ const buffer = new Uint8Array(totalSize);
172
+ let offset = 0;
173
+ for (const chunk of chunks) {
174
+ buffer.set(chunk, offset);
175
+ offset += chunk.length;
176
+ }
177
+ return buffer.buffer;
178
+ }
179
+ finally {
180
+ clearTimeout(timeout);
181
+ }
182
+ }
@@ -0,0 +1,50 @@
1
+ import { registerGetAnnotatedMessageTool } from "./get-annotated-message.js";
2
+ import { registerEchoTool } from "./echo.js";
3
+ import { registerGetEnvTool } from "./get-env.js";
4
+ import { registerGetResourceLinksTool } from "./get-resource-links.js";
5
+ import { registerGetResourceReferenceTool } from "./get-resource-reference.js";
6
+ import { registerGetRootsListTool } from "./get-roots-list.js";
7
+ import { registerGetStructuredContentTool } from "./get-structured-content.js";
8
+ import { registerGetSumTool } from "./get-sum.js";
9
+ import { registerGetTinyImageTool } from "./get-tiny-image.js";
10
+ import { registerGZipFileAsResourceTool } from "./gzip-file-as-resource.js";
11
+ import { registerToggleSimulatedLoggingTool } from "./toggle-simulated-logging.js";
12
+ import { registerToggleSubscriberUpdatesTool } from "./toggle-subscriber-updates.js";
13
+ import { registerTriggerElicitationRequestTool } from "./trigger-elicitation-request.js";
14
+ import { registerTriggerLongRunningOperationTool } from "./trigger-long-running-operation.js";
15
+ import { registerTriggerSamplingRequestTool } from "./trigger-sampling-request.js";
16
+ import { registerTriggerSamplingRequestAsyncTool } from "./trigger-sampling-request-async.js";
17
+ import { registerTriggerElicitationRequestAsyncTool } from "./trigger-elicitation-request-async.js";
18
+ import { registerSimulateResearchQueryTool } from "./simulate-research-query.js";
19
+ /**
20
+ * Register the tools with the MCP server.
21
+ * @param server
22
+ */
23
+ export const registerTools = (server) => {
24
+ registerEchoTool(server);
25
+ registerGetAnnotatedMessageTool(server);
26
+ registerGetEnvTool(server);
27
+ registerGetResourceLinksTool(server);
28
+ registerGetResourceReferenceTool(server);
29
+ registerGetStructuredContentTool(server);
30
+ registerGetSumTool(server);
31
+ registerGetTinyImageTool(server);
32
+ registerGZipFileAsResourceTool(server);
33
+ registerToggleSimulatedLoggingTool(server);
34
+ registerToggleSubscriberUpdatesTool(server);
35
+ registerTriggerLongRunningOperationTool(server);
36
+ };
37
+ /**
38
+ * Register the tools that are conditional upon client capabilities.
39
+ * These must be registered conditionally, after initialization.
40
+ */
41
+ export const registerConditionalTools = (server) => {
42
+ registerGetRootsListTool(server);
43
+ registerTriggerElicitationRequestTool(server);
44
+ registerTriggerSamplingRequestTool(server);
45
+ // Task-based research tool (uses experimental tasks API)
46
+ registerSimulateResearchQueryTool(server);
47
+ // Bidirectional task tools - server sends requests that client executes as tasks
48
+ registerTriggerSamplingRequestAsyncTool(server);
49
+ registerTriggerElicitationRequestAsyncTool(server);
50
+ };