UNPKG

mcp-lockdown

Version:

Strict TypeScript library for Model Context Protocol (MCP) tool manifest pinning, schema enforcement, and policy vetos.

118 lines 5.84 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LockdownServer = void 0; const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js"); const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/client/stdio.js"); const zod_from_json_schema_1 = require("zod-from-json-schema"); const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/client/streamableHttp.js"); const readDownstreamMcpServersJson = (mcpServersJson) => { const servers = Object.keys(mcpServersJson.servers).map((key) => (Object.assign({ name: key }, mcpServersJson.servers[key]))); console.log("Connecting Lockdown to the following MCP servers..."); console.log(servers.map((server) => `- ${server.name} (${server.type})`).join("\n")); return servers; }; // Call a given tool on a server const callTool = (server, tool, args) => __awaiter(void 0, void 0, void 0, function* () { const client = new index_js_1.Client({ name: "MCP Lockdown Client", version: "1.0.0", }); console.log(`Using: ${server.name} - ${tool.name}`); if (server.type === "stdio") { const transport = new stdio_js_1.StdioClientTransport(server); yield client.connect(transport); } else { const transport = new streamableHttp_js_1.StreamableHTTPClientTransport(new URL(server.url), { requestInit: server.requestInit }); yield client.connect(transport); } return client.callTool({ name: tool.name, arguments: args }); }); const toolIsValid = (pm, tool) => __awaiter(void 0, void 0, void 0, function* () { const result = yield pm.evaluate(tool); if (result) { console.log(`\t✅ Tool validation passed for ${tool.name}`); } else { console.error(`\t❌ Tool validation failed, ${tool.name} will not be registered\n`); } return result; }); // given server connection details // return a list of tools for that server that lockdown allows const fetchValidToolsManifest = (pm, server) => __awaiter(void 0, void 0, void 0, function* () { const client = new index_js_1.Client({ name: "MCP Lockdown Client", version: "1.0.0", }); console.log(`Registering MCP Server: ${server.name}`); if (server.type === "stdio") { const transport = new stdio_js_1.StdioClientTransport(server); yield client.connect(transport); } else { const transport = new streamableHttp_js_1.StreamableHTTPClientTransport(new URL(server.url), { requestInit: server.requestInit }); yield client.connect(transport); } const tools = yield client.listTools(); return Object.assign(Object.assign({}, server), { tools: (yield Promise.all(tools.tools.map((t) => __awaiter(void 0, void 0, void 0, function* () { return (Object.assign(Object.assign({}, t), { isValid: yield toolIsValid(pm, t) })); })))).filter((tool) => tool.isValid) }); }); const LockdownServer = (_a) => __awaiter(void 0, [_a], void 0, function* ({ policyManager, lockdownServerJson, }) { // Loop through all servers requested from lockdown // remove tools that don't pass policies const validatedServers = yield Promise.all(readDownstreamMcpServersJson(lockdownServerJson).map((server) => fetchValidToolsManifest(policyManager, server))); // Create an MCP server to return with the proxy tools registered const lockdownServer = new mcp_js_1.McpServer({ name: "Lockdown MCP Server", version: "1.0.0", }); // Create a tool for each for tool in the MCP servers validatedServers.forEach((server) => __awaiter(void 0, void 0, void 0, function* () { server.tools.forEach((tool) => { // convert JSON schema back to Zod schema const zodSchema = (0, zod_from_json_schema_1.convertJsonSchemaToZod)(tool.inputSchema); const keys = zodSchema.keyof().options; let inputSchema = {}; for (const key of keys) { inputSchema[key] = zodSchema.shape[key]; } lockdownServer.registerTool(`${tool.name}_using_${server.name}`, Object.assign(Object.assign({}, tool), { inputSchema: inputSchema }), (args) => { return callTool(server, tool, args); }); }); })); // Register Lockdown internal tools lockdownServer.registerTool("explain_missing_tools", { title: "Lockdown Explain Missing Tools", description: "Returns a list of reasons that tools were removed from the lockdown MCP server", annotations: { title: "Lockdown Explain Missing Tools", description: "Returns a list of reasons that tools were removed from the lockdown MCP server", }, }, () => __awaiter(void 0, void 0, void 0, function* () { return { content: [ { type: "text", text: policyManager.listRejections().join(", ") }, ], }; })); return lockdownServer; }); exports.LockdownServer = LockdownServer; //# sourceMappingURL=server.js.map