@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,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
|
+
};
|