/* * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. * @generated-id: 654d5d1f2062 */ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { ShapeOutput, ZodRawShapeCompat, } from "@modelcontextprotocol/sdk/server/zod-compat.js"; import { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js"; import { CallToolResult, ServerNotification, ServerRequest, } from "@modelcontextprotocol/sdk/types.js"; import * as z from "zod"; import { CloudinaryAssetMgmtCore } from "../core.js"; import { ServerRegion$zodSchema } from "../lib/config.js"; import { enabledAppUriPrefixes, type McpApp } from "./apps/config.js"; import { shouldForwardToolMeta } from "./apps/tool-hooks.js"; import { ConsoleLogger } from "./console-logger.js"; import { MCPServerFlags } from "./flags.js"; import { MCPScope, mcpScopes } from "./scopes.js"; import { valueToBase64 } from "./shared.js"; export type MCPToolAnnotationFilter = { readOnlyHint?: boolean; destructiveHint?: boolean; idempotentHint?: boolean; openWorldHint?: boolean; }; const VALID_ANNOTATIONS = [ "readOnly", "destructive", "idempotent", "openWorld", ] as const; type AnnotationName = typeof VALID_ANNOTATIONS[number]; const annotationToKey: Record = { readOnly: "readOnlyHint", destructive: "destructiveHint", idempotent: "idempotentHint", openWorld: "openWorldHint", }; export function buildAnnotationFilter( annotations: string[] | undefined, ): MCPToolAnnotationFilter { const filter: MCPToolAnnotationFilter = {}; if (!annotations || annotations.length === 0) { return filter; } for (const a of annotations) { if (!VALID_ANNOTATIONS.includes(a as AnnotationName)) { throw new Error( `Invalid annotation filter: "${a}". Valid values are: ${ VALID_ANNOTATIONS.join(", ") }`, ); } } for (const name of VALID_ANNOTATIONS) { filter[annotationToKey[name]] = annotations.includes(name); } return filter; } export type ToolDefinition< Args extends undefined | ZodRawShapeCompat = undefined, > = Args extends ZodRawShapeCompat ? { name: string; description: string; scopes?: MCPScope[]; args: Args; annotations: { title: string; destructiveHint: boolean; idempotentHint: boolean; openWorldHint: boolean; readOnlyHint: boolean; }; _meta?: Record; tool: ( client: CloudinaryAssetMgmtCore, args: ShapeOutput, extra: RequestHandlerExtra, ) => CallToolResult | Promise; } : { name: string; description: string; scopes?: MCPScope[]; args?: undefined; annotations: { title: string; destructiveHint: boolean; idempotentHint: boolean; openWorldHint: boolean; readOnlyHint: boolean; }; _meta?: Record; tool: ( client: CloudinaryAssetMgmtCore, extra: RequestHandlerExtra, ) => CallToolResult | Promise; }; // Optional function to assist with formatting tool results export async function formatResult( response: Response, ): Promise { let content: CallToolResult["content"] = []; const contentType = response?.headers.get("content-type") ?? ""; if (contentType.startsWith("image/")) { const data = await valueToBase64(await response.arrayBuffer()); content = data == null ? [] : [{ type: "image", data, mimeType: contentType }]; } else if (contentType.startsWith("audio/")) { const data = await valueToBase64(await response.arrayBuffer()); content = data == null ? [] : [{ type: "audio", data, mimeType: contentType }]; } else { const text = await response.text(); content = [{ type: "text", text }]; } return response.ok ? { content } : { content, isError: true }; } export function createRegisterTool( logger: ConsoleLogger, server: McpServer, getSDK: () => CloudinaryAssetMgmtCore, allowedScopes: Set, allowedTools?: Set, dynamic?: boolean, annotationFilter?: MCPToolAnnotationFilter, mcpApps?: McpApp[], ): [ (tool: ToolDefinition) => void, Array<{ name: string; description: string }>, Map>, ] { const enabledUris = enabledAppUriPrefixes(mcpApps ?? []); const tools: Array<{ name: string; description: string }> = []; const toolMap = new Map< string, ToolDefinition >(); const registerTool = ( tool: ToolDefinition, ): void => { if (allowedTools && !allowedTools.has(tool.name)) { return; } const scopes = tool.scopes ?? []; if (allowedScopes.size > 0 && scopes.length === 0) { return; } if ( allowedScopes.size > 0 && !scopes.every((s: MCPScope) => allowedScopes.has(s)) ) { return; } if (annotationFilter) { const a = tool.annotations; if ( annotationFilter.readOnlyHint !== undefined && a.readOnlyHint !== annotationFilter.readOnlyHint ) { return; } if ( annotationFilter.destructiveHint !== undefined && a.destructiveHint !== annotationFilter.destructiveHint ) { return; } if ( annotationFilter.idempotentHint !== undefined && a.idempotentHint !== annotationFilter.idempotentHint ) { return; } if ( annotationFilter.openWorldHint !== undefined && a.openWorldHint !== annotationFilter.openWorldHint ) { return; } } toolMap.set( tool.name, tool as ToolDefinition, ); if (dynamic) { logger.debug("Collected tool for dynamic mode", { name: tool.name }); tools.push({ name: tool.name, description: tool.description }); return; } if (tool.args) { server.registerTool( tool.name, { description: tool.description, inputSchema: tool.args, annotations: tool.annotations, ...(shouldForwardToolMeta(tool._meta, enabledUris) ? { _meta: tool._meta } : {}), }, async (args, ctx) => { return tool.tool(getSDK(), args, ctx); }, ); } else { server.registerTool( tool.name, { description: tool.description, annotations: tool.annotations, ...(shouldForwardToolMeta(tool._meta, enabledUris) ? { _meta: tool._meta } : {}), }, async (ctx) => { return tool.tool(getSDK(), ctx); }, ); } logger.debug("Registered tool", { name: tool.name }); tools.push({ name: tool.name, description: tool.description }); }; return [registerTool, tools, toolMap]; } function matchesSearchTerms( terms: string[], name: string, def: ToolDefinition, ): boolean { if (terms.length === 0) { return true; } const nameLower = name.toLowerCase(); const descLower = def.description.toLowerCase(); const scopesLower = def.scopes?.flatMap((s: unknown) => { return typeof s === "string" ? [s.toLowerCase()] : []; }) || []; return terms.some((term) => nameLower.includes(term) || descLower.includes(term) || scopesLower.some((s) => s.includes(term)) ); } export function registerDynamicTools( logger: ConsoleLogger, server: McpServer, getSDK: () => CloudinaryAssetMgmtCore, toolMap: Map>, allowedScopes: Set, ): void { // 1. list_tools server.registerTool("list_tools", { description: "List available tools. Optionally filter by search terms that match against tool name, description, and scopes.", inputSchema: { search_terms: z.array(z.string()).optional().describe( [ "Filter the list of tools.", "Each term is matched case-insensitively as a substring against tool name, description, and scopes.", "Multiple terms are combined with a logical OR: a tool is included if ANY term matches ANY field.", "If not provided, all tools are returned.", ].join("\n"), ), }, annotations: { title: "List Tools", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false, }, }, async (args) => { const terms = (args.search_terms ?? []).map((t) => t.toLowerCase()); const tools: Array< { name: string; description: string; scopes?: MCPScope[] | undefined } > = []; for (const [name, def] of toolMap) { if (!matchesSearchTerms(terms, name, def)) { continue; } tools.push({ name: name, description: def.description, scopes: def.scopes, }); } return { content: [{ type: "text", text: JSON.stringify(tools, null, 2) }], }; }); logger.debug("Registered dynamic meta-tool", { name: "list_tools" }); // 2. describe_tool server.registerTool("describe_tool_input", { description: "Get the input schema for one or more tools. It is a good idea to call this tool first to understand how to successfully call execute_tool.", inputSchema: { tool_names: z.array(z.string()).describe( "The names of the tools to describe", ), }, annotations: { title: "Describe Tool", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false, }, }, async (args) => { const parts: string[] = []; const unknown: string[] = []; if (args.tool_names.length === 0) { return { content: [{ type: "text", text: "No tool names provided." }] }; } for (const toolName of args.tool_names) { const def = toolMap.get(toolName); if (!def) { unknown.push(toolName); continue; } let schemaText = `\n\n`; if (def.args) { const jsonSchema = z.toJSONSchema(z.object(def.args), { target: "draft-2020-12", }); schemaText += JSON.stringify(jsonSchema, null, 2); } else { schemaText += "This tool takes no input parameters."; } schemaText += `\n\n`; parts.push(schemaText); } if (unknown.length > 0) { parts.push(`Unknown tools: ${unknown.join(", ")}`); } return { content: [{ type: "text", text: parts.join("\n\n") }] }; }); logger.debug("Registered dynamic meta-tool", { name: "describe_tool_input" }); // 3. execute_tool server.registerTool("execute_tool", { description: "Execute a tool by name with its arguments. If executing a given tool for the first time, it is recommended to call describe_tool_input first to understand the expected `arguments` shape.", inputSchema: { name: z.string().describe("The name of the tool to execute"), arguments: z.looseObject({}).optional().describe( "Arguments for the target tool as a JSON object, matching the schema returned by describe_tool_input.", ), }, annotations: { title: "Execute Tool", readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true, }, }, async (args, ctx) => { const def = toolMap.get(args.name); if (!def) { return { content: [{ type: "text", text: `Unknown tool: ${args.name}` }], isError: true, }; } let validatedInput: Record = {}; if (def.args) { const vres = z.object(def.args).safeParse(args.arguments ?? {}); if (vres.success) { validatedInput = vres.data; } else { const issues = z.prettifyError(vres.error); return { content: [{ type: "text", text: `Invalid input for tool ${args.name}:\n\n${issues}\n`, }], isError: true, }; } } try { if (def.args) { return await def.tool(getSDK(), validatedInput, ctx); } else { return await def.tool(getSDK(), ctx); } } catch (error) { const message = error instanceof Error ? error.message : String(error); return { content: [{ type: "text", text: `Error executing tool ${args.name}: ${message}`, }], isError: true, }; } }); logger.debug("Registered dynamic meta-tool", { name: "execute_tool" }); // 4. list_scopes (only when the server has scopes defined) if (mcpScopes.length > 0) { const scopes = allowedScopes.size > 0 ? [...allowedScopes].sort() : [...mcpScopes]; server.registerTool("list_scopes", { description: [ "List the scopes available on this server.", "Scopes are categories that group related tools together.", "They can be used as search terms when filtering the list of tools.", ].join("\n"), annotations: { title: "List Scopes", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false, }, }, async () => { const lines = scopes.map((s) => `- ${s}`); return { content: [{ type: "text", text: lines.join("\n") }] }; }); logger.debug("Registered dynamic meta-tool", { name: "list_scopes" }); } } function resolveHeader( headers: Headers, headerName: string, schema: z.ZodType, cliFlagValue: T | undefined, disableStaticAuth: boolean, ): T | undefined { const val = headers.get(headerName); if (val != null) { return schema.parse(val); } return disableStaticAuth ? undefined : schema.parse(cliFlagValue); } export function buildSDK( headers: Headers, cliFlags: MCPServerFlags, disableStaticAuth: boolean, logger: { level: string }, ) { const flags = { ...cliFlags, "api-key": resolveHeader( headers, "api-key", z.string(), cliFlags["api-key"], disableStaticAuth, ), "api-secret": resolveHeader( headers, "api-secret", z.string(), cliFlags["api-secret"], disableStaticAuth, ), "oauth2": resolveHeader( headers, "oauth2", z.string(), cliFlags["oauth2"], disableStaticAuth, ), }; return new CloudinaryAssetMgmtCore({ security: { cloudinaryAuth: flags["api-key"] != null && flags["api-secret"] != null ? { api_key: flags["api-key"] ?? "", api_secret: flags["api-secret"] ?? "", } : void 0, oauth2: flags.oauth2 ?? "", }, serverURL: cliFlags["server-url"], cloud_name: resolveHeader( headers, "cloud-name", z.string(), cliFlags["cloud-name"], disableStaticAuth, ), serverIdx: cliFlags["server-index"], region: resolveHeader( headers, "region", ServerRegion$zodSchema, cliFlags.region, disableStaticAuth, ), host: resolveHeader( headers, "api-host", z.string(), cliFlags["api-host"], disableStaticAuth, ), debugLogger: logger.level === "debug" ? { log: (...args) => console.log(...args), group: (...args) => console.group(...args), groupEnd: (...args) => console.groupEnd(...args), } : undefined, }); }