{"version":3,"sources":["../src/server.ts","../src/util.ts"],"names":["assertValidUri","uri"],"mappings":"AAAA,qvBAAuB,6DAehB,4BACW,SCXFA,CAAAA,CAAeC,CAAAA,CAAa,CAC1C,GAAI,CACF,OAAA,IAAI,GAAA,CAAIA,CAAG,CAAA,CACJA,CACT,CAAA,UAAQ,CACN,MAAM,IAAI,KAAA,CAAM,CAAA,aAAA,EAAgBA,CAAG,CAAA,CAAA","file":"/Users/matt/Developer/supabase-org/supabase-mcp/packages/mcp-utils/dist/index.cjs","sourcesContent":["import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n  CallToolRequestSchema,\n  ListResourcesRequestSchema,\n  ListResourceTemplatesRequestSchema,\n  ListToolsRequestSchema,\n  ReadResourceRequestSchema,\n  type ClientCapabilities,\n  type Implementation,\n  type ListResourcesResult,\n  type ListResourceTemplatesResult,\n  type ReadResourceResult,\n  type ServerCapabilities,\n  type ListToolsResult,\n  type Tool as McpTool,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod/v4';\nimport type {\n  ExpandRecursively,\n  ExtractNotification,\n  ExtractParams,\n  ExtractRequest,\n  ExtractResult,\n} from './types.js';\nimport { assertValidUri, compareUris, matchUriTemplate } from './util.js';\n\nexport type Scheme = string;\nexport type Annotations = NonNullable<\n  ListToolsResult['tools'][number]['annotations']\n>;\n\nexport type Resource<Uri extends string = string, Result = unknown> = {\n  uri: Uri;\n  name: string;\n  description?: string;\n  mimeType?: string;\n  read(uri: `${Scheme}://${Uri}`): Promise<Result>;\n};\n\nexport type ResourceTemplate<Uri extends string = string, Result = unknown> = {\n  uriTemplate: Uri;\n  name: string;\n  description?: string;\n  mimeType?: string;\n  read(\n    uri: `${Scheme}://${Uri}`,\n    params: {\n      [Param in ExtractParams<Uri>]: string;\n    }\n  ): Promise<Result>;\n};\n\ntype RecordSchema = z.ZodObject<any> | z.ZodRecord<any, any>;\n\nexport type Tool<\n  Params extends z.ZodObject<any> = z.ZodObject<any>,\n  OutputSchema extends RecordSchema = RecordSchema,\n> = {\n  description: Prop<string>;\n  annotations?: Annotations;\n  parameters: Params;\n  outputSchema: OutputSchema;\n  execute(params: z.infer<Params>): Promise<z.infer<OutputSchema>>;\n};\n\n/**\n * Helper function to define an MCP resource while preserving type information.\n */\nexport function resource<Uri extends string, Result>(\n  uri: Uri,\n  resource: Omit<Resource<Uri, Result>, 'uri'>\n): Resource<Uri, Result> {\n  return {\n    uri,\n    ...resource,\n  };\n}\n\n/**\n * Helper function to define an MCP resource with a URI template while preserving type information.\n */\nexport function resourceTemplate<Uri extends string, Result>(\n  uriTemplate: Uri,\n  resource: Omit<ResourceTemplate<Uri, Result>, 'uriTemplate'>\n): ResourceTemplate<Uri, Result> {\n  return {\n    uriTemplate,\n    ...resource,\n  };\n}\n\n/**\n * Helper function to define a JSON resource while preserving type information.\n */\nexport function jsonResource<Uri extends string, Result>(\n  uri: Uri,\n  resource: Omit<Resource<Uri, Result>, 'uri' | 'mimeType'>\n): Resource<Uri, Result> {\n  return {\n    uri,\n    mimeType: 'application/json' as const,\n    ...resource,\n  };\n}\n\n/**\n * Helper function to define a JSON resource with a URI template while preserving type information.\n */\nexport function jsonResourceTemplate<Uri extends string, Result>(\n  uriTemplate: Uri,\n  resource: Omit<ResourceTemplate<Uri, Result>, 'uriTemplate' | 'mimeType'>\n): ResourceTemplate<Uri, Result> {\n  return {\n    uriTemplate,\n    mimeType: 'application/json' as const,\n    ...resource,\n  };\n}\n\n/**\n * Helper function to define a list of resources that share a common URI scheme.\n */\nexport function resources<Scheme extends string>(\n  scheme: Scheme,\n  resources: (Resource | ResourceTemplate)[]\n): (\n  | Resource<`${Scheme}://${string}`>\n  | ResourceTemplate<`${Scheme}://${string}`>\n)[] {\n  return resources.map((resource) => {\n    if ('uri' in resource) {\n      const url = new URL(resource.uri, `${scheme}://`);\n      const uri = decodeURI(url.href) as `${Scheme}://${typeof resource.uri}`;\n\n      return {\n        ...resource,\n        uri,\n      };\n    }\n\n    const url = new URL(resource.uriTemplate, `${scheme}://`);\n    const uriTemplate = decodeURI(\n      url.href\n    ) as `${Scheme}://${typeof resource.uriTemplate}`;\n\n    return {\n      ...resource,\n      uriTemplate,\n    };\n  });\n}\n\n/**\n * Helper function to create a JSON resource response.\n */\nexport function jsonResourceResponse<Uri extends string, Response>(\n  uri: Uri,\n  response: Response\n) {\n  return {\n    uri,\n    mimeType: 'application/json',\n    text: JSON.stringify(response),\n  };\n}\n\n/**\n * Helper function to define an MCP tool while preserving type information.\n */\nexport function tool<\n  Params extends z.ZodObject<any>,\n  OutputSchema extends RecordSchema,\n>(tool: Tool<Params, OutputSchema>) {\n  return tool;\n}\n\nexport type InitData = {\n  clientInfo: Implementation;\n  clientCapabilities: ClientCapabilities;\n};\n\ntype ToolCallBaseDetails = {\n  name: string;\n  arguments: Record<string, unknown>;\n  annotations?: Annotations;\n};\n\ntype ToolCallSuccessDetails = ToolCallBaseDetails & {\n  success: true;\n  data: unknown;\n};\n\ntype ToolCallErrorDetails = ToolCallBaseDetails & {\n  success: false;\n  error: unknown;\n};\n\nexport type ToolCallDetails = ToolCallSuccessDetails | ToolCallErrorDetails;\n\nexport type InitCallback = (initData: InitData) => void | Promise<void>;\nexport type ToolCallCallback = (details: ToolCallDetails) => void;\nexport type PropCallback<T> = () => T | Promise<T>;\nexport type Prop<T> = T | PropCallback<T>;\n\nexport type McpServerOptions = {\n  /**\n   * The name of the MCP server. This will be sent to the client as part of\n   * the initialization process.\n   */\n  name: string;\n\n  /**\n   * The title of the MCP server. This is a human-readable name that can be\n   * displayed in the client UI.\n   *\n   * If not provided, the name will be used as the title.\n   */\n  title?: string;\n\n  /**\n   * The version of the MCP server. This will be sent to the client as part of\n   * the initialization process.\n   */\n  version: string;\n\n  /**\n   * Callback for when initialization has fully completed with the client.\n   */\n  onInitialize?: InitCallback;\n\n  /**\n   * Callback for after a tool is called.\n   */\n  onToolCall?: ToolCallCallback;\n\n  /**\n   * Resources to be served by the server. These can be defined as a static\n   * object or as a function that dynamically returns the object synchronously\n   * or asynchronously.\n   *\n   * If defined as a function, the function will be called whenever the client\n   * asks for the list of resources or reads a resource. This allows for dynamic\n   * resources that can change after the server has started.\n   */\n  resources?: Prop<\n    (Resource<string, unknown> | ResourceTemplate<string, unknown>)[]\n  >;\n\n  /**\n   * Tools to be served by the server. These can be defined as a static object\n   * or as a function that dynamically returns the object synchronously or\n   * asynchronously.\n   *\n   * If defined as a function, the function will be called whenever the client\n   * asks for the list of tools or invokes a tool. This allows for dynamic tools\n   * that can change after the server has started.\n   */\n  tools?: Prop<Record<string, Tool>>;\n};\n\n/**\n * Creates an MCP server with the given options.\n *\n * Simplifies the process of creating an MCP server by providing a high-level\n * API for defining resources and tools.\n */\nexport function createMcpServer(options: McpServerOptions) {\n  const capabilities: ServerCapabilities = {};\n\n  if (options.resources) {\n    capabilities.resources = {};\n  }\n\n  if (options.tools) {\n    capabilities.tools = {};\n  }\n\n  const server = new Server(\n    {\n      name: options.name,\n      title: options.title,\n      version: options.version,\n    },\n    {\n      capabilities,\n    }\n  );\n\n  async function getResources() {\n    if (!options.resources) {\n      throw new Error('resources not available');\n    }\n\n    return typeof options.resources === 'function'\n      ? await options.resources()\n      : options.resources;\n  }\n\n  async function getTools() {\n    if (!options.tools) {\n      throw new Error('tools not available');\n    }\n\n    return typeof options.tools === 'function'\n      ? await options.tools()\n      : options.tools;\n  }\n\n  server.oninitialized = async () => {\n    const clientInfo = server.getClientVersion();\n    const clientCapabilities = server.getClientCapabilities();\n\n    if (!clientInfo) {\n      throw new Error('client info not available after initialization');\n    }\n\n    if (!clientCapabilities) {\n      throw new Error('client capabilities not available after initialization');\n    }\n\n    const initData: InitData = {\n      clientInfo,\n      clientCapabilities,\n    };\n\n    await options.onInitialize?.(initData);\n  };\n\n  if (options.resources) {\n    server.setRequestHandler(\n      ListResourcesRequestSchema,\n      async (): Promise<ListResourcesResult> => {\n        const allResources = await getResources();\n        return {\n          resources: allResources\n            .filter((resource) => 'uri' in resource)\n            .map(({ uri, name, description, mimeType }) => {\n              return {\n                uri,\n                name,\n                description,\n                mimeType,\n              };\n            }),\n        };\n      }\n    );\n\n    server.setRequestHandler(\n      ListResourceTemplatesRequestSchema,\n      async (): Promise<ListResourceTemplatesResult> => {\n        const allResources = await getResources();\n        return {\n          resourceTemplates: allResources\n            .filter((resource) => 'uriTemplate' in resource)\n            .map(({ uriTemplate, name, description, mimeType }) => {\n              return {\n                uriTemplate,\n                name,\n                description,\n                mimeType,\n              };\n            }),\n        };\n      }\n    );\n\n    server.setRequestHandler(\n      ReadResourceRequestSchema,\n      async (request): Promise<ReadResourceResult> => {\n        try {\n          const allResources = await getResources();\n          const { uri } = request.params;\n\n          const resources = allResources.filter(\n            (resource) => 'uri' in resource\n          );\n          const resource = resources.find((resource) =>\n            compareUris(resource.uri, uri)\n          );\n\n          if (resource) {\n            const result = await resource.read(uri as `${string}://${string}`);\n\n            const contents = Array.isArray(result) ? result : [result];\n\n            return {\n              contents,\n            };\n          }\n\n          const resourceTemplates = allResources.filter(\n            (resource) => 'uriTemplate' in resource\n          );\n          const resourceTemplateUris = resourceTemplates.map(\n            ({ uriTemplate }) => assertValidUri(uriTemplate)\n          );\n\n          const templateMatch = matchUriTemplate(uri, resourceTemplateUris);\n\n          if (!templateMatch) {\n            throw new Error('resource not found');\n          }\n\n          const resourceTemplate = resourceTemplates.find(\n            (r) => r.uriTemplate === templateMatch.uri\n          );\n\n          if (!resourceTemplate) {\n            throw new Error('resource not found');\n          }\n\n          const result = await resourceTemplate.read(\n            uri as `${string}://${string}`,\n            templateMatch.params\n          );\n\n          const contents = Array.isArray(result) ? result : [result];\n\n          return {\n            contents,\n          };\n        } catch (error) {\n          return {\n            isError: true,\n            content: [\n              {\n                type: 'text',\n                text: JSON.stringify({ error: enumerateError(error) }),\n              },\n            ],\n          } as any;\n        }\n      }\n    );\n  }\n\n  if (options.tools) {\n    server.setRequestHandler(\n      ListToolsRequestSchema,\n      async (): Promise<ListToolsResult> => {\n        const tools = await getTools();\n\n        return {\n          tools: await Promise.all(\n            Object.entries(tools).map(\n              async ([name, { description, annotations, parameters }]) => {\n                const inputSchema = z.toJSONSchema(parameters, {\n                  target: 'draft-7',\n                });\n\n                return {\n                  name,\n                  description:\n                    typeof description === 'function'\n                      ? await description()\n                      : description,\n                  annotations,\n                  // Casting the same as the SDK does:\n                  // https://github.com/modelcontextprotocol/typescript-sdk/blob/fb07af810b51003c338dc4885a9e42f54519f9af/src/server/mcp.ts#L154\n                  inputSchema: inputSchema as McpTool['inputSchema'],\n                };\n              }\n            )\n          ),\n        } satisfies ListToolsResult;\n      }\n    );\n\n    server.setRequestHandler(CallToolRequestSchema, async (request) => {\n      try {\n        const tools = await getTools();\n        const toolName = request.params.name;\n\n        if (!(toolName in tools)) {\n          throw new Error('tool not found');\n        }\n\n        const tool = tools[toolName];\n\n        if (!tool) {\n          throw new Error('tool not found');\n        }\n        const args = tool.parameters\n          .strict()\n          .parse(request.params.arguments ?? {});\n\n        const executeWithCallback = async (tool: Tool) => {\n          // Wrap success or error in a result value\n          const res = await tool\n            .execute(args)\n            .then((data: unknown) => ({ success: true as const, data }))\n            .catch((error) => ({ success: false as const, error }));\n\n          try {\n            options.onToolCall?.({\n              name: toolName,\n              arguments: args,\n              annotations: tool.annotations,\n              ...res,\n            });\n          } catch (error) {\n            // Don't fail the tool call if the callback fails\n            console.error('Failed to run tool callback', error);\n          }\n\n          // Unwrap result\n          if (!res.success) {\n            throw res.error;\n          }\n          return res.data;\n        };\n\n        const result = await executeWithCallback(tool);\n\n        const content =\n          result != null\n            ? [{ type: 'text', text: JSON.stringify(result) }]\n            : [];\n\n        return {\n          content,\n        };\n      } catch (error) {\n        return {\n          isError: true,\n          content: [\n            {\n              type: 'text',\n              text: JSON.stringify({ error: enumerateError(error) }),\n            },\n          ],\n        };\n      }\n    });\n  }\n\n  // Expand types recursively for better intellisense\n  type Request = ExpandRecursively<ExtractRequest<typeof server>>;\n  type Notification = ExpandRecursively<ExtractNotification<typeof server>>;\n  type Result = ExpandRecursively<ExtractResult<typeof server>>;\n\n  return server as Server<Request, Notification, Result>;\n}\n\nfunction enumerateError(error: unknown) {\n  if (!error) {\n    return error;\n  }\n\n  if (typeof error !== 'object') {\n    return error;\n  }\n\n  const newError: Record<string, unknown> = {};\n\n  const errorProps = ['name', 'message'] as const;\n\n  for (const prop of errorProps) {\n    if (prop in error) {\n      newError[prop] = (error as Record<string, unknown>)[prop];\n    }\n  }\n\n  return newError;\n}\n","import type { ExtractParams } from './types.js';\n\n/**\n * Asserts that a URI is valid.\n */\nexport function assertValidUri(uri: string) {\n  try {\n    new URL(uri);\n    return uri;\n  } catch {\n    throw new Error(`invalid uri: ${uri}`);\n  }\n}\n\n/**\n * Compares two URIs.\n */\nexport function compareUris(uriA: string, uriB: string): boolean {\n  const urlA = new URL(uriA);\n  const urlB = new URL(uriB);\n\n  return urlA.href === urlB.href;\n}\n\n/**\n * Matches a URI to a RFC 6570 URI Template (resourceUris) and extracts\n * the parameters.\n *\n * Currently only supports simple string parameters.\n */\nexport function matchUriTemplate<Templates extends string[]>(\n  uri: string,\n  uriTemplates: Templates\n):\n  | {\n      uri: Templates[number];\n      params: { [Param in ExtractParams<Templates[number]>]: string };\n    }\n  | undefined {\n  const url = new URL(uri);\n  const segments = url.pathname.split('/').slice(1);\n\n  for (const resourceUri of uriTemplates) {\n    const resourceUrl = new URL(resourceUri);\n    const resourceSegments = decodeURIComponent(resourceUrl.pathname)\n      .split('/')\n      .slice(1);\n\n    if (segments.length !== resourceSegments.length) {\n      continue;\n    }\n\n    const params: Record<string, string> = {};\n    let isMatch = true;\n\n    for (let i = 0; i < segments.length; i++) {\n      const resourceSegment = resourceSegments[i];\n      const segment = segments[i];\n\n      if (!resourceSegment || !segment) {\n        break;\n      }\n\n      if (resourceSegment.startsWith('{') && resourceSegment.endsWith('}')) {\n        const paramKey = resourceSegment.slice(1, -1);\n\n        if (!paramKey) {\n          break;\n        }\n\n        params[paramKey] = segment;\n      } else if (segments[i] !== resourceSegments[i]) {\n        isMatch = false;\n        break;\n      }\n    }\n\n    if (isMatch) {\n      return {\n        uri: resourceUri,\n        params: params as {\n          [Param in ExtractParams<Templates[number]>]: string;\n        },\n      };\n    }\n  }\n}\n"]}