Skip to content

Commit 0d01c5f

Browse files
ochafikclaude
andcommitted
Add zip tool to MCP server (demonstrates consuming and producing URIs, incl. data URIs)
This change ports the ZIP tool from PR #2830 in the modelcontextprotocol/servers repository to the example-remote-server codebase. The zip tool: - Takes a mapping of filenames to URLs (which can be data URIs) - Fetches files from those URLs - Compresses them into a zip archive - Returns the zip as a data URI resource link This demonstrates best practices for handling URIs in MCP tools, including both consuming input URIs and producing output data URIs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 45ffd17 commit 0d01c5f

File tree

2 files changed

+52
-0
lines changed

2 files changed

+52
-0
lines changed

mcp-server/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"dotenv": "^16.4.7",
2222
"express": "^4.21.2",
2323
"express-rate-limit": "^8.0.1",
24+
"jszip": "^3.10.1",
2425
"raw-body": "^3.0.0"
2526
},
2627
"devDependencies": {

mcp-server/src/services/mcp.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from "@modelcontextprotocol/sdk/types.js";
2020
import { z } from "zod";
2121
import { zodToJsonSchema } from "zod-to-json-schema";
22+
import JSZip from "jszip";
2223

2324
type ToolInput = {
2425
type: "object";
@@ -79,6 +80,12 @@ const GetResourceReferenceSchema = z.object({
7980
.describe("ID of the resource to reference (1-100)"),
8081
});
8182

83+
const ZipResourcesInputSchema = z.object({
84+
files: z
85+
.record(z.string().url().describe("URL of the file to include in the zip"))
86+
.describe("Mapping of file names to URLs to include in the zip"),
87+
});
88+
8289
enum ToolName {
8390
ECHO = "echo",
8491
ADD = "add",
@@ -87,6 +94,7 @@ enum ToolName {
8794
GET_TINY_IMAGE = "getTinyImage",
8895
ANNOTATED_MESSAGE = "annotatedMessage",
8996
GET_RESOURCE_REFERENCE = "getResourceReference",
97+
ZIP_RESOURCES = "zip",
9098
}
9199

92100
enum PromptName {
@@ -446,6 +454,12 @@ export const createMcpServer = (): McpServerWrapper => {
446454
"Returns a resource reference that can be used by MCP clients",
447455
inputSchema: zodToJsonSchema(GetResourceReferenceSchema) as ToolInput,
448456
},
457+
{
458+
name: ToolName.ZIP_RESOURCES,
459+
description:
460+
"Compresses the provided resource files (mapping of name to URI, which can be a data URI) to a zip file, which it returns as a data URI resource link",
461+
inputSchema: zodToJsonSchema(ZipResourcesInputSchema) as ToolInput,
462+
},
449463
];
450464

451465
return { tools };
@@ -624,6 +638,43 @@ export const createMcpServer = (): McpServerWrapper => {
624638
return { content };
625639
}
626640

641+
if (name === ToolName.ZIP_RESOURCES) {
642+
const { files } = ZipResourcesInputSchema.parse(args);
643+
644+
const zip = new JSZip();
645+
646+
for (const [fileName, fileUrl] of Object.entries(files)) {
647+
try {
648+
const response = await fetch(fileUrl);
649+
if (!response.ok) {
650+
throw new Error(
651+
`Failed to fetch ${fileUrl}: ${response.statusText}`
652+
);
653+
}
654+
const arrayBuffer = await response.arrayBuffer();
655+
zip.file(fileName, arrayBuffer);
656+
} catch (error) {
657+
throw new Error(
658+
`Error fetching file ${fileUrl}: ${error instanceof Error ? error.message : String(error)}`
659+
);
660+
}
661+
}
662+
663+
const uri = `data:application/zip;base64,${await zip.generateAsync({
664+
type: "base64",
665+
})}`;
666+
667+
return {
668+
content: [
669+
{
670+
type: "resource_link",
671+
mimeType: "application/zip",
672+
uri,
673+
},
674+
],
675+
};
676+
}
677+
627678
throw new Error(`Unknown tool: ${name}`);
628679
});
629680

0 commit comments

Comments
 (0)