@kontext-dev/js-sdk 0.3.0 → 1.1.0
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/dist/adapters/ai/index.cjs +12 -2
- package/dist/adapters/ai/index.cjs.map +1 -1
- package/dist/adapters/ai/index.js +12 -2
- package/dist/adapters/ai/index.js.map +1 -1
- package/dist/adapters/cloudflare/index.cjs +13 -0
- package/dist/adapters/cloudflare/index.cjs.map +1 -1
- package/dist/adapters/cloudflare/index.js +13 -0
- package/dist/adapters/cloudflare/index.js.map +1 -1
- package/dist/adapters/cloudflare/react.cjs +12 -2
- package/dist/adapters/cloudflare/react.cjs.map +1 -1
- package/dist/adapters/cloudflare/react.js +12 -2
- package/dist/adapters/cloudflare/react.js.map +1 -1
- package/dist/adapters/react/index.cjs +12 -2
- package/dist/adapters/react/index.cjs.map +1 -1
- package/dist/adapters/react/index.js +12 -2
- package/dist/adapters/react/index.js.map +1 -1
- package/dist/client/index.cjs +108 -69
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +2 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +109 -71
- package/dist/client/index.js.map +1 -1
- package/dist/errors.cjs +78 -0
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +7 -1
- package/dist/errors.d.ts +7 -1
- package/dist/errors.js +78 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.cjs +151 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +152 -88
- package/dist/index.js.map +1 -1
- package/dist/{kontext-CgIBANFo.d.cts → kontext-CBPuE-hq.d.cts} +3 -0
- package/dist/{kontext-CgIBANFo.d.ts → kontext-CBPuE-hq.d.ts} +3 -0
- package/dist/management/index.cjs +15 -0
- package/dist/management/index.cjs.map +1 -1
- package/dist/management/index.d.cts +2 -2
- package/dist/management/index.d.ts +2 -2
- package/dist/management/index.js +15 -1
- package/dist/management/index.js.map +1 -1
- package/dist/mcp/index.cjs +32 -3
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.d.cts +7 -1
- package/dist/mcp/index.d.ts +7 -1
- package/dist/mcp/index.js +33 -4
- package/dist/mcp/index.js.map +1 -1
- package/dist/oauth/index.cjs +12 -2
- package/dist/oauth/index.cjs.map +1 -1
- package/dist/oauth/index.d.cts +1 -1
- package/dist/oauth/index.d.ts +1 -1
- package/dist/oauth/index.js +12 -2
- package/dist/oauth/index.js.map +1 -1
- package/dist/server/index.cjs +55 -20
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +56 -21
- package/dist/server/index.js.map +1 -1
- package/dist/{types-CzhnlJHW.d.cts → types-DicGI7ix.d.cts} +23 -1
- package/dist/{types-CzhnlJHW.d.ts → types-DicGI7ix.d.ts} +23 -1
- package/package.json +1 -1
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var ai = require('ai');
|
|
4
|
+
var types_js = require('@modelcontextprotocol/sdk/types.js');
|
|
4
5
|
|
|
5
6
|
// src/adapters/ai/index.ts
|
|
6
|
-
|
|
7
|
-
// src/errors.ts
|
|
8
7
|
var KontextError = class extends Error {
|
|
9
8
|
/** Brand field for type narrowing without instanceof */
|
|
10
9
|
kontextError = true;
|
|
@@ -52,6 +51,17 @@ var ConfigError = class extends KontextError {
|
|
|
52
51
|
this.name = "ConfigError";
|
|
53
52
|
}
|
|
54
53
|
};
|
|
54
|
+
({
|
|
55
|
+
[types_js.ErrorCode.ParseError]: { },
|
|
56
|
+
[types_js.ErrorCode.InvalidRequest]: { },
|
|
57
|
+
[types_js.ErrorCode.MethodNotFound]: { },
|
|
58
|
+
[types_js.ErrorCode.InvalidParams]: { },
|
|
59
|
+
[types_js.ErrorCode.InternalError]: {
|
|
60
|
+
},
|
|
61
|
+
[types_js.ErrorCode.RequestTimeout]: {
|
|
62
|
+
},
|
|
63
|
+
[types_js.ErrorCode.ConnectionClosed]: { }
|
|
64
|
+
});
|
|
55
65
|
|
|
56
66
|
// src/client/tool-utils.ts
|
|
57
67
|
function buildKontextSystemPrompt(toolNames, integrations) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/errors.ts","../../../src/client/tool-utils.ts","../../../src/adapters/ai/index.ts"],"names":["jsonSchema"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Typed error classes for the Kontext SDK.\n *\n * Every error has a `kontext_` prefixed code, an auto-generated docsUrl,\n * and a `kontextError` brand for type narrowing without instanceof.\n *\n * @packageDocumentation\n */\n\n// ============================================================================\n// Base\n// ============================================================================\n\n/**\n * Base error class for all Kontext SDK errors.\n *\n * @example\n * ```typescript\n * import { isKontextError } from '@kontext-dev/js-sdk';\n *\n * try {\n * await client.connect();\n * } catch (err) {\n * if (isKontextError(err)) {\n * console.log(err.code); // \"kontext_authorization_required\"\n * console.log(err.docsUrl); // \"https://docs.kontext.dev/errors/kontext_authorization_required\"\n * }\n * }\n * ```\n */\nexport class KontextError extends Error {\n /** Brand field for type narrowing without instanceof */\n readonly kontextError = true as const;\n\n /** Machine-readable error code, always prefixed with `kontext_` */\n readonly code: string;\n\n /** HTTP status code when applicable */\n readonly statusCode?: number;\n\n /** Auto-generated link to error documentation */\n readonly docsUrl: string;\n\n /** Server request ID for debugging / support escalation */\n readonly requestId?: string;\n\n /** Contextual metadata bag (integration IDs, param names, etc.) */\n readonly meta: Record<string, unknown>;\n\n constructor(\n message: string,\n code: string,\n options?: {\n statusCode?: number;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, { cause: options?.cause });\n this.name = \"KontextError\";\n this.code = code;\n this.statusCode = options?.statusCode;\n this.requestId = options?.requestId;\n this.meta = options?.meta ?? {};\n this.docsUrl = `https://docs.kontext.dev/errors/${code}`;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n statusCode: this.statusCode,\n docsUrl: this.docsUrl,\n requestId: this.requestId,\n meta: Object.keys(this.meta).length > 0 ? this.meta : undefined,\n };\n }\n\n override toString(): string {\n const parts = [`[${this.code}] ${this.message}`];\n if (this.docsUrl) parts.push(`Docs: ${this.docsUrl}`);\n if (this.requestId) parts.push(`Request ID: ${this.requestId}`);\n return parts.join(\"\\n\");\n }\n}\n\n// ============================================================================\n// Type guard\n// ============================================================================\n\n/**\n * Check if an error is a KontextError without instanceof.\n * Works across package versions and bundler deduplication.\n */\nexport function isKontextError(err: unknown): err is KontextError {\n return (\n typeof err === \"object\" &&\n err !== null &&\n (err as Record<string, unknown>).kontextError === true\n );\n}\n\n// ============================================================================\n// Auth errors\n// ============================================================================\n\n/**\n * Thrown when authentication is required but no valid credentials are available.\n */\nexport class AuthorizationRequiredError extends KontextError {\n readonly authorizationUrl?: string;\n\n constructor(\n message = \"Authorization required. Complete the OAuth flow to continue.\",\n options?: {\n authorizationUrl?: string;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, \"kontext_authorization_required\", {\n statusCode: 401,\n ...options,\n });\n this.name = \"AuthorizationRequiredError\";\n this.authorizationUrl = options?.authorizationUrl;\n }\n}\n\n// ============================================================================\n// OAuth errors\n// ============================================================================\n\n/**\n * Thrown when an OAuth flow fails — state validation, token exchange,\n * missing code verifier, or provider errors.\n */\nexport class OAuthError extends KontextError {\n readonly errorCode?: string;\n readonly errorDescription?: string;\n\n constructor(\n message: string,\n code: string,\n options?: {\n statusCode?: number;\n errorCode?: string;\n errorDescription?: string;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, code, {\n statusCode: options?.statusCode ?? 400,\n ...options,\n });\n this.name = \"OAuthError\";\n this.errorCode = options?.errorCode;\n this.errorDescription = options?.errorDescription;\n }\n}\n\n// ============================================================================\n// Integration errors\n// ============================================================================\n\n/**\n * Thrown when an integration connection is required before a tool can be used.\n */\nexport class IntegrationConnectionRequiredError extends KontextError {\n readonly integrationId: string;\n readonly integrationName?: string;\n readonly connectUrl?: string;\n\n constructor(\n integrationId: string,\n options?: {\n integrationName?: string;\n connectUrl?: string;\n message?: string;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(\n options?.message ??\n `Connection to integration \"${integrationId}\" is required. Visit the connect URL to authorize.`,\n \"kontext_integration_connection_required\",\n { statusCode: 403, ...options },\n );\n this.name = \"IntegrationConnectionRequiredError\";\n this.integrationId = integrationId;\n this.integrationName = options?.integrationName;\n this.connectUrl = options?.connectUrl;\n }\n}\n\n// ============================================================================\n// Config errors (NEW — replaces all plain Error config throws)\n// ============================================================================\n\n/**\n * Thrown when SDK configuration is invalid or missing.\n * These are deterministic errors caught at initialization time.\n */\nexport class ConfigError extends KontextError {\n constructor(\n message: string,\n code: string,\n options?: {\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, code, options);\n this.name = \"ConfigError\";\n }\n}\n\n// ============================================================================\n// Network errors\n// ============================================================================\n\n/**\n * Thrown when there is a network or connection error.\n */\nexport class NetworkError extends KontextError {\n constructor(\n message = \"Network error. Check your internet connection and that the server is reachable.\",\n options?: {\n cause?: unknown;\n requestId?: string;\n meta?: Record<string, unknown>;\n },\n ) {\n super(message, \"kontext_network_error\", options);\n this.name = \"NetworkError\";\n }\n}\n\n// ============================================================================\n// HTTP response errors (differentiated by code)\n// ============================================================================\n\n/**\n * Thrown when the server returns an HTTP error.\n * Use `error.code` to distinguish between specific error types.\n */\nexport class HttpError extends KontextError {\n readonly retryAfter?: number;\n readonly validationErrors?: Array<{ field: string; message: string }>;\n\n constructor(\n message: string,\n code: string,\n options?: {\n statusCode?: number;\n retryAfter?: number;\n validationErrors?: Array<{ field: string; message: string }>;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, code, {\n statusCode: options?.statusCode,\n ...options,\n });\n this.name = \"HttpError\";\n this.retryAfter = options?.retryAfter;\n this.validationErrors = options?.validationErrors;\n }\n}\n\n// ============================================================================\n// Network error detection (used by translateError)\n// ============================================================================\n\n/**\n * Safely access arbitrary properties on an error object.\n * Errors in JS frequently carry extra properties (code, statusCode, etc.)\n * that aren't part of the Error interface. This avoids `as unknown as` casts.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction errorProps(err: Error): Record<string, any> {\n return err;\n}\n\nconst NETWORK_ERROR_CODES = new Set([\n \"ECONNREFUSED\",\n \"ENOTFOUND\",\n \"ETIMEDOUT\",\n \"ECONNRESET\",\n \"ECONNABORTED\",\n \"EPIPE\",\n \"UND_ERR_CONNECT_TIMEOUT\",\n]);\n\n/**\n * Detect network errors structurally rather than by string matching.\n * Checks Node.js system error codes on the error and its cause.\n */\nexport function isNetworkError(err: Error): boolean {\n if (err.name === \"AbortError\") return true;\n\n const props = errorProps(err);\n const sysCode = props.code as string | undefined;\n if (typeof sysCode === \"string\" && NETWORK_ERROR_CODES.has(sysCode))\n return true;\n\n // fetch() throws TypeError — only classify as network error when cause\n // indicates a system-level failure\n if (err.name === \"TypeError\" && err.cause instanceof Error) {\n const causeCode = errorProps(err.cause).code;\n if (typeof causeCode === \"string\" && NETWORK_ERROR_CODES.has(causeCode))\n return true;\n }\n\n return false;\n}\n\n/**\n * Detect unauthorized errors structurally.\n * Checks status code and numeric code rather than string matching on name.\n */\nexport function isUnauthorizedError(err: Error): boolean {\n const props = errorProps(err);\n\n // Check HTTP status code (most reliable)\n if (props.statusCode === 401 || props.status === 401) return true;\n\n // Check MCP SDK UnauthorizedError by name (last resort, but needed for\n // MCP SDK errors which don't set statusCode)\n if (err.name === \"UnauthorizedError\") return true;\n if (err.message === \"Unauthorized\") return true;\n\n return false;\n}\n\n// ============================================================================\n// Elicitation types\n// ============================================================================\n\nexport interface ElicitationEntry {\n readonly url: string;\n readonly message: string;\n readonly elicitationId: string;\n readonly integrationId?: string;\n readonly integrationName?: string;\n}\n\n// ============================================================================\n// HTTP error parsing\n// ============================================================================\n\n/**\n * Parse an HTTP response into an appropriate error.\n */\nexport function parseHttpError(\n statusCode: number,\n body?: unknown,\n): KontextError {\n const message =\n typeof body === \"object\" && body !== null && \"message\" in body\n ? String((body as { message: unknown }).message)\n : `HTTP ${statusCode}`;\n\n const errorCode =\n typeof body === \"object\" && body !== null && \"code\" in body\n ? String((body as { code: unknown }).code)\n : undefined;\n\n switch (statusCode) {\n case 400:\n if (\n typeof body === \"object\" &&\n body !== null &&\n \"errors\" in body &&\n Array.isArray((body as { errors: unknown }).errors)\n ) {\n return new HttpError(message, \"kontext_validation_error\", {\n statusCode: 400,\n validationErrors: (\n body as { errors: Array<{ field: string; message: string }> }\n ).errors,\n });\n }\n return new KontextError(message, errorCode ?? \"kontext_bad_request\", {\n statusCode: 400,\n });\n\n case 401:\n return new AuthorizationRequiredError(message);\n\n case 403:\n if (errorCode === \"INTEGRATION_CONNECTION_REQUIRED\") {\n const details = body as {\n integrationId?: string;\n integrationName?: string;\n connectUrl?: string;\n };\n return new IntegrationConnectionRequiredError(\n details.integrationId ?? \"unknown\",\n {\n integrationName: details.integrationName,\n connectUrl: details.connectUrl,\n message,\n },\n );\n }\n return new HttpError(message, \"kontext_policy_denied\", {\n statusCode: 403,\n meta: { policy: (body as Record<string, unknown>)?.policy },\n });\n\n case 404:\n return new HttpError(message, \"kontext_not_found\", { statusCode: 404 });\n\n case 429: {\n const retryAfter =\n typeof body === \"object\" && body !== null && \"retryAfter\" in body\n ? Number((body as { retryAfter: unknown }).retryAfter)\n : undefined;\n return new HttpError(\n retryAfter\n ? `Rate limit exceeded. Retry after ${retryAfter} seconds.`\n : \"Rate limit exceeded. Wait and retry.\",\n \"kontext_rate_limited\",\n { statusCode: 429, retryAfter },\n );\n }\n\n default:\n if (statusCode >= 500) {\n return new HttpError(\n `Server error (HTTP ${statusCode}): ${message}`,\n \"kontext_server_error\",\n { statusCode },\n );\n }\n return new KontextError(message, errorCode ?? \"kontext_unknown_error\", {\n statusCode,\n });\n }\n}\n","/**\n * Shared utilities for Kontext tool handling.\n * Used by both KontextClient (core) and withKontext (Cloudflare adapter).\n */\n\nimport {\n ConfigError,\n IntegrationConnectionRequiredError,\n type ElicitationEntry,\n} from \"../errors.js\";\n\n// Re-export for consumers that import this type from tool-utils.\nexport type { ElicitationEntry } from \"../errors.js\";\n\n// ============================================================================\n// Shared types\n// ============================================================================\n\nexport interface IntegrationStatus {\n readonly id: string;\n readonly name: string;\n readonly connected: boolean;\n readonly connectUrl?: string;\n}\n\n// ============================================================================\n// System prompt\n// ============================================================================\n\n/**\n * Build an LLM system prompt based on available tool names and integration status.\n * Detects `*_SEARCH_TOOLS` / `*_EXECUTE_TOOL` suffixed names from the\n * Cloudflare Agents MCP proxy and generates appropriate guidance.\n */\nexport function buildKontextSystemPrompt(\n toolNames: readonly string[],\n integrations: readonly IntegrationStatus[],\n): string {\n if (toolNames.length === 0) return \"\";\n\n const searchTool = toolNames.find(\n (n) => n.endsWith(\"_SEARCH_TOOLS\") || n === \"SEARCH_TOOLS\",\n );\n const executeTool = toolNames.find(\n (n) => n.endsWith(\"_EXECUTE_TOOL\") || n === \"EXECUTE_TOOL\",\n );\n const requestCapabilityTool = toolNames.find(\n (n) => n.endsWith(\"_REQUEST_CAPABILITY\") || n === \"REQUEST_CAPABILITY\",\n );\n\n let prompt =\n \"You are a helpful assistant with access to various tools and integrations through Kontext.\\n\" +\n \"You can help users with tasks across their connected services (GitHub, Linear, Slack, etc.).\\n\" +\n \"When a user asks about their services, use the available MCP tools to help them.\\n\" +\n \"The user has already authenticated — tools run as the user with their permissions, including access to private repositories and org resources.\\n\" +\n \"If a Kontext tool can satisfy an integration-related request, use the Kontext tool and do not use shell or CLI commands for that request.\\n\" +\n \"Use shell or CLI only when no relevant Kontext tool exists or when the user explicitly requests local shell actions.\";\n\n if (searchTool && executeTool) {\n prompt +=\n `\\n\\nYou have access to external integrations through Kontext MCP tools:\\n` +\n `- Call \\`${searchTool}\\` first to discover what tools/integrations are available.\\n` +\n `- Then call \\`${executeTool}\\` with the tool_id and arguments to run a specific tool.\\n` +\n `- When the user asks about \"my repos\", \"my issues\", etc., use the appropriate tool to look up their data directly. Do not ask for their username — the tools already know who they are.\\n` +\n `Always start by searching for tools when the user asks about their connected services.`;\n }\n\n // Append integration status section when there are integrations to report\n if (integrations.length > 0) {\n const connected = integrations.filter((i) => i.connected);\n const disconnected = integrations.filter((i) => !i.connected);\n\n if (disconnected.length > 0) {\n prompt += \"\\n\\n## Integration Status\";\n\n if (connected.length > 0) {\n prompt += `\\n\\nYou already have access to: ${connected.map((i) => i.name).join(\", \")}`;\n }\n\n prompt += `\\n\\nThe following services require user authorization: ${disconnected.map((i) => i.name).join(\", \")}`;\n\n if (requestCapabilityTool) {\n prompt +=\n `\\n\\n**IMPORTANT:** When the user requests an action that requires one of these services:` +\n `\\n1. Call the \\`${requestCapabilityTool}\\` tool` +\n `\\n2. The tool returns a fresh integrations management link` +\n `\\n3. Include the link in your response so the user can connect or manage integrations` +\n `\\n4. Always call the tool for a fresh link and never reuse a previously returned URL`;\n } else {\n prompt +=\n \"\\n\\nWhen the user asks about a disconnected integration, let them know it needs to be connected first. Do not attempt to use tools from disconnected integrations without informing the user.\";\n }\n } else if (requestCapabilityTool && connected.length > 0) {\n prompt +=\n \"\\n\\n## Integration Management\" +\n `\\n\\nIf the user asks to manage, reconnect, or verify integrations, call \\`${requestCapabilityTool}\\` to get a fresh integrations management link.`;\n }\n }\n\n return prompt;\n}\n\n// ============================================================================\n// Auth-aware toolset orchestrator\n// ============================================================================\n\nexport interface AuthAwareToolset {\n /** System prompt with integration status and REQUEST_CAPABILITY instructions. */\n readonly systemPrompt: string;\n /** REQUEST_CAPABILITY tool to inject, or null if all integrations are connected. */\n readonly requestCapabilityTool: {\n readonly name: string;\n readonly description: string;\n readonly parameters: Record<string, unknown>;\n readonly execute: () => Promise<string>;\n } | null;\n}\n\n/**\n * Enrich a tool set with auth awareness.\n *\n * Given tool names and integration status, produces:\n * - A system prompt that tells the LLM about connected/disconnected integrations\n * - A REQUEST_CAPABILITY tool (if needed) that returns a fresh management URL as text\n *\n * Both `ai/` and `cloudflare/` adapters use this as their core auth layer.\n */\nexport function enrichToolsWithAuthAwareness(\n toolNames: readonly string[],\n integrations: readonly IntegrationStatus[],\n options?: {\n prefix?: string;\n requireServerRequestCapability?: boolean;\n requestCapabilityProxy?: () => Promise<string>;\n },\n): AuthAwareToolset {\n const serverRequestCapability = toolNames.find(\n (name) =>\n name.endsWith(\"_REQUEST_CAPABILITY\") || name === \"REQUEST_CAPABILITY\",\n );\n\n if (options?.requireServerRequestCapability && !serverRequestCapability) {\n throw new ConfigError(\n \"REQUEST_CAPABILITY tool is required but not exposed by the MCP server. Deploy API and SDK in lockstep.\",\n \"kontext_config_missing_request_capability_tool\",\n );\n }\n\n let requestCapabilityTool: AuthAwareToolset[\"requestCapabilityTool\"] = null;\n let effectiveToolNames = [...toolNames];\n\n if (options?.requestCapabilityProxy) {\n const prefix =\n options.prefix ??\n toolNames\n .find((n) => n.endsWith(\"_SEARCH_TOOLS\"))\n ?.replace(/_SEARCH_TOOLS$/, \"\") ??\n \"kontext\";\n const toolName = `${prefix}_REQUEST_CAPABILITY`;\n const tool = buildRequestCapabilityTool(\n integrations,\n options.requestCapabilityProxy!,\n );\n requestCapabilityTool = { name: toolName, ...tool };\n effectiveToolNames = [...effectiveToolNames, toolName];\n }\n\n return {\n systemPrompt: buildKontextSystemPrompt(effectiveToolNames, integrations),\n requestCapabilityTool,\n };\n}\n\n// ============================================================================\n// Integration status parsing\n// ============================================================================\n\n/**\n * Parse integration status from gateway tool search results.\n * Extracts connected integrations from tools and disconnected from errors.\n */\nexport function parseIntegrationStatus(\n tools: ReadonlyArray<{ server?: { id?: string; name?: string } }>,\n errors: ReadonlyArray<{ serverId: string; serverName?: string }>,\n elicitations?: ReadonlyArray<{\n url: string;\n integrationId?: string;\n integrationName?: string;\n }>,\n): IntegrationStatus[] {\n const seen = new Set<string>();\n const result: IntegrationStatus[] = [];\n\n for (const t of tools) {\n const sid = t.server?.id;\n if (sid && !seen.has(sid)) {\n seen.add(sid);\n result.push({\n id: sid,\n name: t.server?.name ?? sid,\n connected: true,\n });\n }\n }\n\n for (const e of errors) {\n if (!seen.has(e.serverId)) {\n seen.add(e.serverId);\n const elicitation = elicitations?.find(\n (el) => el.integrationId === e.serverId,\n );\n result.push({\n id: e.serverId,\n name: e.serverName ?? e.serverId,\n connected: false,\n connectUrl: elicitation?.url,\n });\n }\n }\n\n return result;\n}\n\n// ============================================================================\n// Request capability tool\n// ============================================================================\n\n/**\n * Build a tool that lets the LLM request a fresh integrations management URL.\n * Returns the URL as text so the LLM can present it to the user.\n * No popups — the user clicks the link in the chat.\n */\nexport function buildRequestCapabilityTool(\n capabilities: readonly IntegrationStatus[],\n executeRequestCapability: () => Promise<string>,\n): {\n description: string;\n parameters: Record<string, unknown>;\n execute: () => Promise<string>;\n} {\n const connected = capabilities.filter((c) => c.connected);\n const disconnected = capabilities.filter((c) => !c.connected);\n const connectedList =\n connected.length > 0 ? connected.map((c) => c.name).join(\", \") : \"none\";\n const disconnectedList =\n disconnected.length > 0\n ? disconnected.map((c) => c.name).join(\", \")\n : \"none\";\n\n return {\n description:\n \"Request a fresh integrations management link from the Kontext server. \" +\n `Connected integrations: ${connectedList}. ` +\n `Disconnected integrations: ${disconnectedList}. ` +\n \"Always call this tool for a fresh link and never reuse an older link.\",\n parameters: {\n type: \"object\",\n properties: {},\n },\n execute: async (): Promise<string> => {\n return executeRequestCapability();\n },\n };\n}\n\n/**\n * Pre-flight integration status detection.\n * Calls SEARCH_TOOLS on raw (unwrapped) tools to detect connected/disconnected\n * integrations without triggering any popups or broadcasts.\n */\nexport async function preflightIntegrationStatus(\n tools: ToolSetLike,\n): Promise<IntegrationStatus[]> {\n const searchToolKey = Object.keys(tools).find((k) =>\n k.endsWith(\"_SEARCH_TOOLS\"),\n );\n if (!searchToolKey || !tools[searchToolKey]?.execute) return [];\n\n try {\n const result = await tools[searchToolKey].execute!({ limit: 100 });\n\n // Cloudflare getAITools() returns MCP CallToolResult objects, not strings.\n // Extract JSON text from either a plain string or a CallToolResult shape.\n let json: string | undefined;\n if (typeof result === \"string\") {\n json = result;\n } else if (result && typeof result === \"object\") {\n const content = (\n result as {\n content?: Array<{\n type: string;\n resource?: { text?: string };\n text?: string;\n }>;\n }\n ).content;\n const first = content?.[0];\n json = first?.resource?.text ?? first?.text;\n }\n\n if (typeof json === \"string\") {\n const parsed = JSON.parse(json);\n if (parsed && typeof parsed === \"object\") {\n return parseIntegrationStatus(\n Array.isArray(parsed.items) ? parsed.items : [],\n Array.isArray(parsed.errors) ? parsed.errors : [],\n Array.isArray(parsed.elicitations) ? parsed.elicitations : [],\n );\n }\n }\n } catch (err: unknown) {\n // All integrations disconnected: -32042 error contains elicitations\n const elicitations = extractElicitations(err);\n if (elicitations?.length) {\n return elicitations\n .filter(\n (e): e is ElicitationEntry & { integrationId: string } =>\n !!e.integrationId,\n )\n .map((e) => ({\n id: e.integrationId,\n name: e.integrationName ?? e.integrationId,\n connected: false,\n connectUrl: e.url,\n }));\n }\n }\n\n return [];\n}\n\n// ============================================================================\n// Tool wrapping\n// ============================================================================\n\n/** A tool with an optional async execute method */\nexport interface ToolLike {\n execute?: (...args: unknown[]) => Promise<unknown>;\n [key: string]: unknown;\n}\n\n/** Record of named tools */\nexport type ToolSetLike = Record<string, ToolLike>;\n\n/**\n * Extract elicitation entries from an error thrown by the MCP SDK.\n * Handles two shapes: direct code/data and nested cause.\n */\nexport function extractElicitations(\n err: unknown,\n): ElicitationEntry[] | undefined {\n const error = err as {\n code?: number;\n data?: { elicitations?: ElicitationEntry[] };\n cause?: {\n code?: number;\n data?: { elicitations?: ElicitationEntry[] };\n };\n };\n\n // Primary: structured McpError with code -32042\n if (error?.code === -32042 && error?.data?.elicitations?.length) {\n return error.data.elicitations;\n }\n\n // Nested cause (some SDK wrappers nest the original error)\n if (\n error?.cause?.code === -32042 &&\n error?.cause?.data?.elicitations?.length\n ) {\n return error.cause.data.elicitations;\n }\n\n return undefined;\n}\n\n/**\n * Wrap tools to catch MCP error code -32042 (URL elicitation required)\n * and convert to IntegrationConnectionRequiredError.\n */\nexport function wrapToolsWithElicitation<T extends ToolSetLike>(tools: T): T {\n return Object.fromEntries(\n Object.entries(tools).map(([name, tool]) => [\n name,\n {\n ...tool,\n execute: tool.execute\n ? async (...args: unknown[]) => {\n try {\n return await tool.execute!(...args);\n } catch (err: unknown) {\n const elicitations = extractElicitations(err);\n const elicitation = elicitations?.[0];\n if (elicitation?.url) {\n const integrationId = elicitation.integrationId ?? \"unknown\";\n throw new IntegrationConnectionRequiredError(integrationId, {\n integrationName: elicitation.integrationName,\n connectUrl: elicitation.url,\n message: elicitation.message,\n });\n }\n throw err;\n }\n }\n : undefined,\n },\n ]),\n ) as T;\n}\n","/**\n * Vercel AI SDK adapter for Kontext.\n *\n * Pure format converter: maps KontextClient tools to AI SDK CoreTools.\n *\n * @example\n * ```typescript\n * import { createKontextClient } from '@kontext-dev/js-sdk';\n * import { toKontextTools } from '@kontext-dev/js-sdk/ai';\n * import { generateText } from 'ai';\n *\n * const client = createKontextClient({\n * clientId: process.env.KONTEXT_CLIENT_ID!,\n * redirectUri: 'http://localhost:3000/callback',\n * onAuthRequired: (url) => open(url.toString()),\n * });\n *\n * const { tools, systemPrompt } = await toKontextTools(client);\n * const result = await generateText({ model, system: systemPrompt, tools, prompt: '...', maxSteps: 5 });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { jsonSchema, type CoreTool } from \"ai\";\nimport type {\n KontextClient,\n KontextTool,\n IntegrationInfo,\n ToolResult,\n} from \"../../client/index.js\";\nimport type { KontextOrchestrator } from \"../../client/orchestrator/index.js\";\nimport { enrichToolsWithAuthAwareness } from \"../../client/tool-utils.js\";\nimport { ConfigError } from \"../../errors.js\";\n\nexport interface ToKontextToolsOptions {\n formatResult?: (result: ToolResult) => unknown;\n}\n\nexport interface KontextToolsResult {\n readonly tools: Record<string, CoreTool>;\n readonly systemPrompt: string;\n readonly integrations: readonly IntegrationInfo[];\n}\n\n/**\n * Convert a KontextClient's tools into an AI SDK ToolSet with system prompt.\n *\n * Calls `client.tools.list()` and `client.integrations.list()` to discover\n * tools and integration status, then wraps each tool as an AI SDK `CoreTool`\n * whose `execute` delegates to `client.tools.execute()`.\n */\nexport async function toKontextTools(\n client: KontextClient | KontextOrchestrator,\n options?: ToKontextToolsOptions,\n): Promise<KontextToolsResult> {\n const [tools, integrations, mcpTools] = await Promise.all([\n client.tools.list(),\n client.integrations.list(),\n client.mcp.listTools(),\n ]);\n\n const coreTools: Record<string, CoreTool> = {};\n const usedNames = new Set<string>();\n\n for (const tool of tools) {\n const name = buildToolName(tool, usedNames);\n coreTools[name] = {\n description: formatDescription(tool),\n parameters: tool.inputSchema\n ? jsonSchema(tool.inputSchema as Parameters<typeof jsonSchema>[0])\n : jsonSchema({ type: \"object\" as const, properties: {} }),\n execute: async (args: Record<string, unknown>) => {\n const r = await client.tools.execute(tool.id, args);\n return (options?.formatResult ?? defaultFormat)(r);\n },\n };\n }\n\n if (!mcpTools.some((tool) => tool.name === \"REQUEST_CAPABILITY\")) {\n throw new ConfigError(\n \"REQUEST_CAPABILITY tool is missing on the MCP server. Deploy API and SDK in lockstep.\",\n \"kontext_config_missing_request_capability_tool\",\n );\n }\n\n const { systemPrompt, requestCapabilityTool } = enrichToolsWithAuthAwareness(\n [...tools.map((t) => t.name), \"kontext_REQUEST_CAPABILITY\"],\n integrations,\n {\n prefix: \"kontext\",\n requireServerRequestCapability: true,\n requestCapabilityProxy: async () => {\n const result = await client.mcp.callTool(\"REQUEST_CAPABILITY\", {});\n return extractText(result);\n },\n },\n );\n\n if (requestCapabilityTool) {\n coreTools[requestCapabilityTool.name] = {\n description: requestCapabilityTool.description,\n parameters: jsonSchema(\n requestCapabilityTool.parameters as Parameters<typeof jsonSchema>[0],\n ),\n execute: async () => {\n return requestCapabilityTool.execute();\n },\n };\n }\n\n return { tools: coreTools, systemPrompt, integrations };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction defaultFormat(r: ToolResult): unknown {\n return r.content;\n}\n\nfunction formatDescription(tool: KontextTool): string {\n const base = tool.description ?? tool.name;\n const serverLabel = tool.server?.name ?? tool.server?.id;\n return serverLabel ? `[${serverLabel}] ${base}` : base;\n}\n\nfunction buildToolName(tool: KontextTool, usedNames: Set<string>): string {\n const rawName = tool.name || tool.id || \"tool\";\n const base = tool.server\n ? `${tool.server.name ?? tool.server.id ?? \"server\"}_${rawName}`\n : rawName;\n const sanitized = sanitizeToolName(base);\n const fallback = sanitized || `tool_${usedNames.size + 1}`;\n\n let name = fallback;\n let suffix = 1;\n while (usedNames.has(name)) {\n name = `${fallback}_${suffix}`;\n suffix += 1;\n }\n\n usedNames.add(name);\n return name;\n}\n\nfunction sanitizeToolName(name: string): string {\n return name\n .replace(/[^a-zA-Z0-9_-]/g, \"_\")\n .replace(/_+/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .slice(0, 64);\n}\n\nfunction extractText(result: unknown): string {\n if (typeof result === \"string\") return result;\n if (!result || typeof result !== \"object\") {\n return String(result ?? \"\");\n }\n\n const content = (\n result as {\n content?: Array<{\n type?: string;\n text?: string;\n resource?: { text?: string };\n }>;\n }\n ).content;\n if (Array.isArray(content)) {\n const textItem = content.find(\n (item) => item.type === \"text\" && typeof item.text === \"string\",\n );\n if (textItem?.text) return textItem.text;\n\n const resourceItem = content.find(\n (item) =>\n item.type === \"resource\" && typeof item.resource?.text === \"string\",\n );\n if (resourceItem?.resource?.text) return resourceItem.resource.text;\n }\n\n return JSON.stringify(result);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/errors.ts","../../../src/client/tool-utils.ts","../../../src/adapters/ai/index.ts"],"names":["ErrorCode","jsonSchema"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Typed error classes for the Kontext SDK.\n *\n * Every error has a `kontext_` prefixed code, an auto-generated docsUrl,\n * and a `kontextError` brand for type narrowing without instanceof.\n *\n * @packageDocumentation\n */\n\nimport { ErrorCode } from \"@modelcontextprotocol/sdk/types.js\";\n\n// ============================================================================\n// Base\n// ============================================================================\n\n/**\n * Base error class for all Kontext SDK errors.\n *\n * @example\n * ```typescript\n * import { isKontextError } from '@kontext-dev/js-sdk';\n *\n * try {\n * await client.connect();\n * } catch (err) {\n * if (isKontextError(err)) {\n * console.log(err.code); // \"kontext_authorization_required\"\n * console.log(err.docsUrl); // \"https://docs.kontext.dev/errors/kontext_authorization_required\"\n * }\n * }\n * ```\n */\nexport class KontextError extends Error {\n /** Brand field for type narrowing without instanceof */\n readonly kontextError = true as const;\n\n /** Machine-readable error code, always prefixed with `kontext_` */\n readonly code: string;\n\n /** HTTP status code when applicable */\n readonly statusCode?: number;\n\n /** Auto-generated link to error documentation */\n readonly docsUrl: string;\n\n /** Server request ID for debugging / support escalation */\n readonly requestId?: string;\n\n /** Contextual metadata bag (integration IDs, param names, etc.) */\n readonly meta: Record<string, unknown>;\n\n constructor(\n message: string,\n code: string,\n options?: {\n statusCode?: number;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, { cause: options?.cause });\n this.name = \"KontextError\";\n this.code = code;\n this.statusCode = options?.statusCode;\n this.requestId = options?.requestId;\n this.meta = options?.meta ?? {};\n this.docsUrl = `https://docs.kontext.dev/errors/${code}`;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n statusCode: this.statusCode,\n docsUrl: this.docsUrl,\n requestId: this.requestId,\n meta: Object.keys(this.meta).length > 0 ? this.meta : undefined,\n };\n }\n\n override toString(): string {\n const parts = [`[${this.code}] ${this.message}`];\n if (this.docsUrl) parts.push(`Docs: ${this.docsUrl}`);\n if (this.requestId) parts.push(`Request ID: ${this.requestId}`);\n return parts.join(\"\\n\");\n }\n}\n\n// ============================================================================\n// Type guard\n// ============================================================================\n\n/**\n * Check if an error is a KontextError without instanceof.\n * Works across package versions and bundler deduplication.\n */\nexport function isKontextError(err: unknown): err is KontextError {\n return (\n typeof err === \"object\" &&\n err !== null &&\n (err as Record<string, unknown>).kontextError === true\n );\n}\n\n// ============================================================================\n// Auth errors\n// ============================================================================\n\n/**\n * Thrown when authentication is required but no valid credentials are available.\n */\nexport class AuthorizationRequiredError extends KontextError {\n readonly authorizationUrl?: string;\n\n constructor(\n message = \"Authorization required. Complete the OAuth flow to continue.\",\n options?: {\n authorizationUrl?: string;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, \"kontext_authorization_required\", {\n statusCode: 401,\n ...options,\n });\n this.name = \"AuthorizationRequiredError\";\n this.authorizationUrl = options?.authorizationUrl;\n }\n}\n\n// ============================================================================\n// OAuth errors\n// ============================================================================\n\n/**\n * Thrown when an OAuth flow fails — state validation, token exchange,\n * missing code verifier, or provider errors.\n */\nexport class OAuthError extends KontextError {\n readonly errorCode?: string;\n readonly errorDescription?: string;\n\n constructor(\n message: string,\n code: string,\n options?: {\n statusCode?: number;\n errorCode?: string;\n errorDescription?: string;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, code, {\n statusCode: options?.statusCode ?? 400,\n ...options,\n });\n this.name = \"OAuthError\";\n this.errorCode = options?.errorCode;\n this.errorDescription = options?.errorDescription;\n }\n}\n\n// ============================================================================\n// Integration errors\n// ============================================================================\n\n/**\n * Thrown when an integration connection is required before a tool can be used.\n */\nexport class IntegrationConnectionRequiredError extends KontextError {\n readonly integrationId: string;\n readonly integrationName?: string;\n readonly connectUrl?: string;\n\n constructor(\n integrationId: string,\n options?: {\n integrationName?: string;\n connectUrl?: string;\n message?: string;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(\n options?.message ??\n `Connection to integration \"${integrationId}\" is required. Visit the connect URL to authorize.`,\n \"kontext_integration_connection_required\",\n { statusCode: 403, ...options },\n );\n this.name = \"IntegrationConnectionRequiredError\";\n this.integrationId = integrationId;\n this.integrationName = options?.integrationName;\n this.connectUrl = options?.connectUrl;\n }\n}\n\n// ============================================================================\n// Config errors (NEW — replaces all plain Error config throws)\n// ============================================================================\n\n/**\n * Thrown when SDK configuration is invalid or missing.\n * These are deterministic errors caught at initialization time.\n */\nexport class ConfigError extends KontextError {\n constructor(\n message: string,\n code: string,\n options?: {\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, code, options);\n this.name = \"ConfigError\";\n }\n}\n\n// ============================================================================\n// Network errors\n// ============================================================================\n\n/**\n * Thrown when there is a network or connection error.\n */\nexport class NetworkError extends KontextError {\n constructor(\n message = \"Network error. Check your internet connection and that the server is reachable.\",\n options?: {\n cause?: unknown;\n requestId?: string;\n meta?: Record<string, unknown>;\n },\n ) {\n super(message, \"kontext_network_error\", options);\n this.name = \"NetworkError\";\n }\n}\n\n// ============================================================================\n// HTTP response errors (differentiated by code)\n// ============================================================================\n\n/**\n * Thrown when the server returns an HTTP error.\n * Use `error.code` to distinguish between specific error types.\n */\nexport class HttpError extends KontextError {\n readonly retryAfter?: number;\n readonly validationErrors?: Array<{ field: string; message: string }>;\n\n constructor(\n message: string,\n code: string,\n options?: {\n statusCode?: number;\n retryAfter?: number;\n validationErrors?: Array<{ field: string; message: string }>;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, code, {\n statusCode: options?.statusCode,\n ...options,\n });\n this.name = \"HttpError\";\n this.retryAfter = options?.retryAfter;\n this.validationErrors = options?.validationErrors;\n }\n}\n\n// ============================================================================\n// Network error detection (used by translateError)\n// ============================================================================\n\n/**\n * Safely access arbitrary properties on an error object.\n * Errors in JS frequently carry extra properties (code, statusCode, etc.)\n * that aren't part of the Error interface. This avoids `as unknown as` casts.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction errorProps(err: Error): Record<string, any> {\n return err;\n}\n\nconst NETWORK_ERROR_CODES = new Set([\n \"ECONNREFUSED\",\n \"ENOTFOUND\",\n \"ETIMEDOUT\",\n \"ECONNRESET\",\n \"ECONNABORTED\",\n \"EPIPE\",\n \"UND_ERR_CONNECT_TIMEOUT\",\n]);\n\n/**\n * Detect network errors structurally rather than by string matching.\n * Checks Node.js system error codes on the error and its cause.\n */\nexport function isNetworkError(err: Error): boolean {\n if (err.name === \"AbortError\") return true;\n\n const props = errorProps(err);\n const sysCode = props.code as string | undefined;\n if (typeof sysCode === \"string\" && NETWORK_ERROR_CODES.has(sysCode))\n return true;\n\n // fetch() throws TypeError — only classify as network error when cause\n // indicates a system-level failure\n if (err.name === \"TypeError\" && err.cause instanceof Error) {\n const causeCode = errorProps(err.cause).code;\n if (typeof causeCode === \"string\" && NETWORK_ERROR_CODES.has(causeCode))\n return true;\n }\n\n // Browser fetch() failures: TypeError with known messages, no system error code\n if (err.name === \"TypeError\") {\n const msg = err.message.toLowerCase();\n if (\n msg === \"failed to fetch\" ||\n msg === \"load failed\" ||\n msg.includes(\"networkerror\")\n ) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Detect unauthorized errors structurally.\n * Checks status code and numeric code rather than string matching on name.\n */\nexport function isUnauthorizedError(err: Error): boolean {\n const props = errorProps(err);\n\n // Check HTTP status code (most reliable)\n if (props.statusCode === 401 || props.status === 401) return true;\n\n // StreamableHTTPError stores status as `.code`\n if (props.code === 401) return true;\n\n // MCP SDK UnauthorizedError extends Error without setting `.name`,\n // so `.name` is \"Error\". Use constructor name as fallback.\n if (err.name === \"UnauthorizedError\") return true;\n if (err.constructor?.name === \"UnauthorizedError\") return true;\n if (err.message === \"Unauthorized\") return true;\n\n return false;\n}\n\n// ============================================================================\n// Elicitation types\n// ============================================================================\n\nexport interface ElicitationEntry {\n readonly url: string;\n readonly message: string;\n readonly elicitationId: string;\n readonly integrationId?: string;\n readonly integrationName?: string;\n}\n\n// ============================================================================\n// HTTP error parsing\n// ============================================================================\n\n/**\n * Parse an HTTP response into an appropriate error.\n */\nexport function parseHttpError(\n statusCode: number,\n body?: unknown,\n): KontextError {\n const message =\n typeof body === \"object\" && body !== null && \"message\" in body\n ? String((body as { message: unknown }).message)\n : `HTTP ${statusCode}`;\n\n const errorCode =\n typeof body === \"object\" && body !== null && \"code\" in body\n ? String((body as { code: unknown }).code)\n : undefined;\n\n switch (statusCode) {\n case 400:\n if (\n typeof body === \"object\" &&\n body !== null &&\n \"errors\" in body &&\n Array.isArray((body as { errors: unknown }).errors)\n ) {\n return new HttpError(message, \"kontext_validation_error\", {\n statusCode: 400,\n validationErrors: (\n body as { errors: Array<{ field: string; message: string }> }\n ).errors,\n });\n }\n return new KontextError(message, errorCode ?? \"kontext_bad_request\", {\n statusCode: 400,\n });\n\n case 401:\n return new AuthorizationRequiredError(message);\n\n case 403:\n if (errorCode === \"INTEGRATION_CONNECTION_REQUIRED\") {\n const details = body as {\n integrationId?: string;\n integrationName?: string;\n connectUrl?: string;\n };\n return new IntegrationConnectionRequiredError(\n details.integrationId ?? \"unknown\",\n {\n integrationName: details.integrationName,\n connectUrl: details.connectUrl,\n message,\n },\n );\n }\n return new HttpError(message, \"kontext_policy_denied\", {\n statusCode: 403,\n meta: { policy: (body as Record<string, unknown>)?.policy },\n });\n\n case 404:\n return new HttpError(message, \"kontext_not_found\", { statusCode: 404 });\n\n case 429: {\n const retryAfter =\n typeof body === \"object\" && body !== null && \"retryAfter\" in body\n ? Number((body as { retryAfter: unknown }).retryAfter)\n : undefined;\n return new HttpError(\n retryAfter\n ? `Rate limit exceeded. Retry after ${retryAfter} seconds.`\n : \"Rate limit exceeded. Wait and retry.\",\n \"kontext_rate_limited\",\n { statusCode: 429, retryAfter },\n );\n }\n\n default:\n if (statusCode >= 500) {\n return new HttpError(\n `Server error (HTTP ${statusCode}): ${message}`,\n \"kontext_server_error\",\n { statusCode },\n );\n }\n return new KontextError(message, errorCode ?? \"kontext_unknown_error\", {\n statusCode,\n });\n }\n}\n\n// ============================================================================\n// MCP error translation\n// ============================================================================\n\nconst MCP_CODE_MAP: Record<number, { code: string; statusCode?: number }> = {\n [ErrorCode.ParseError]: { code: \"kontext_mcp_parse_error\" },\n [ErrorCode.InvalidRequest]: { code: \"kontext_mcp_invalid_request\" },\n [ErrorCode.MethodNotFound]: { code: \"kontext_mcp_method_not_found\" },\n [ErrorCode.InvalidParams]: { code: \"kontext_mcp_invalid_params\" },\n [ErrorCode.InternalError]: {\n code: \"kontext_mcp_internal_error\",\n statusCode: 500,\n },\n [ErrorCode.RequestTimeout]: {\n code: \"kontext_mcp_session_expired\",\n statusCode: 401,\n },\n [ErrorCode.ConnectionClosed]: { code: \"kontext_mcp_session_error\" },\n};\n\n/**\n * Translate external errors into KontextError instances.\n * Uses structural checks (numeric codes, status codes, system error codes)\n * rather than fragile string matching.\n */\nexport function translateError(err: unknown): KontextError {\n if (isKontextError(err)) return err as KontextError;\n\n if (!(err instanceof Error)) {\n return new KontextError(String(err), \"kontext_unknown_error\");\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const props: Record<string, any> = err;\n\n // 1. MCP protocol -32042 (URL elicitation required) — check numeric code\n if (props.code === ErrorCode.UrlElicitationRequired) {\n const elicitations = (props.elicitations ?? props.data?.elicitations) as\n | ElicitationEntry[]\n | undefined;\n const elicitation = elicitations?.[0];\n return new IntegrationConnectionRequiredError(\n elicitation?.integrationId ?? \"unknown\",\n {\n integrationName: elicitation?.integrationName,\n connectUrl: elicitation?.url,\n message: elicitation?.message,\n cause: err,\n },\n );\n }\n\n // 2. MCP JSON-RPC errors (standard and server-defined ranges)\n if (typeof props.code === \"number\" && props.code < 0) {\n const entry = MCP_CODE_MAP[props.code];\n if (entry) {\n return new KontextError(err.message, entry.code, {\n statusCode: entry.statusCode,\n cause: err,\n });\n }\n return new KontextError(err.message, \"kontext_mcp_error\", {\n cause: err,\n meta: { mcpCode: props.code },\n });\n }\n\n // 3. HTTP status on the error (from MCP SDK or fetch wrappers)\n // StreamableHTTPError stores HTTP status as `.code` (not `.statusCode`/`.status`),\n // so check `.code` as well when it's in the HTTP error range (400–599).\n const statusCode = (props.statusCode ??\n props.status ??\n (typeof props.code === \"number\" && props.code >= 400 && props.code < 600\n ? props.code\n : undefined)) as number | undefined;\n if (typeof statusCode === \"number\" && statusCode >= 400) {\n if (statusCode === 401) {\n return new AuthorizationRequiredError(err.message, { cause: err });\n }\n return new KontextError(err.message, \"kontext_server_error\", {\n statusCode,\n cause: err,\n });\n }\n\n // 4. Auth errors — structural check\n if (isUnauthorizedError(err)) {\n return new AuthorizationRequiredError(err.message, { cause: err });\n }\n\n // 5. Network errors — structural check (system error codes, not string matching)\n if (isNetworkError(err)) {\n return new NetworkError(err.message, { cause: err });\n }\n\n // 6. Fallback\n return new KontextError(err.message, \"kontext_unknown_error\", {\n cause: err,\n });\n}\n","/**\n * Shared utilities for Kontext tool handling.\n * Used by both KontextClient (core) and withKontext (Cloudflare adapter).\n */\n\nimport {\n ConfigError,\n IntegrationConnectionRequiredError,\n type ElicitationEntry,\n} from \"../errors.js\";\n\n// Re-export for consumers that import this type from tool-utils.\nexport type { ElicitationEntry } from \"../errors.js\";\n\n// ============================================================================\n// Shared types\n// ============================================================================\n\nexport interface IntegrationStatus {\n readonly id: string;\n readonly name: string;\n readonly connected: boolean;\n readonly connectUrl?: string;\n}\n\n// ============================================================================\n// System prompt\n// ============================================================================\n\n/**\n * Build an LLM system prompt based on available tool names and integration status.\n * Detects `*_SEARCH_TOOLS` / `*_EXECUTE_TOOL` suffixed names from the\n * Cloudflare Agents MCP proxy and generates appropriate guidance.\n */\nexport function buildKontextSystemPrompt(\n toolNames: readonly string[],\n integrations: readonly IntegrationStatus[],\n): string {\n if (toolNames.length === 0) return \"\";\n\n const searchTool = toolNames.find(\n (n) => n.endsWith(\"_SEARCH_TOOLS\") || n === \"SEARCH_TOOLS\",\n );\n const executeTool = toolNames.find(\n (n) => n.endsWith(\"_EXECUTE_TOOL\") || n === \"EXECUTE_TOOL\",\n );\n const requestCapabilityTool = toolNames.find(\n (n) => n.endsWith(\"_REQUEST_CAPABILITY\") || n === \"REQUEST_CAPABILITY\",\n );\n\n let prompt =\n \"You are a helpful assistant with access to various tools and integrations through Kontext.\\n\" +\n \"You can help users with tasks across their connected services (GitHub, Linear, Slack, etc.).\\n\" +\n \"When a user asks about their services, use the available MCP tools to help them.\\n\" +\n \"The user has already authenticated — tools run as the user with their permissions, including access to private repositories and org resources.\\n\" +\n \"If a Kontext tool can satisfy an integration-related request, use the Kontext tool and do not use shell or CLI commands for that request.\\n\" +\n \"Use shell or CLI only when no relevant Kontext tool exists or when the user explicitly requests local shell actions.\";\n\n if (searchTool && executeTool) {\n prompt +=\n `\\n\\nYou have access to external integrations through Kontext MCP tools:\\n` +\n `- Call \\`${searchTool}\\` first to discover what tools/integrations are available.\\n` +\n `- Then call \\`${executeTool}\\` with the tool_id and arguments to run a specific tool.\\n` +\n `- When the user asks about \"my repos\", \"my issues\", etc., use the appropriate tool to look up their data directly. Do not ask for their username — the tools already know who they are.\\n` +\n `Always start by searching for tools when the user asks about their connected services.`;\n }\n\n // Append integration status section when there are integrations to report\n if (integrations.length > 0) {\n const connected = integrations.filter((i) => i.connected);\n const disconnected = integrations.filter((i) => !i.connected);\n\n if (disconnected.length > 0) {\n prompt += \"\\n\\n## Integration Status\";\n\n if (connected.length > 0) {\n prompt += `\\n\\nYou already have access to: ${connected.map((i) => i.name).join(\", \")}`;\n }\n\n prompt += `\\n\\nThe following services require user authorization: ${disconnected.map((i) => i.name).join(\", \")}`;\n\n if (requestCapabilityTool) {\n prompt +=\n `\\n\\n**IMPORTANT:** When the user requests an action that requires one of these services:` +\n `\\n1. Call the \\`${requestCapabilityTool}\\` tool` +\n `\\n2. The tool returns a fresh integrations management link` +\n `\\n3. Include the link in your response so the user can connect or manage integrations` +\n `\\n4. Always call the tool for a fresh link and never reuse a previously returned URL`;\n } else {\n prompt +=\n \"\\n\\nWhen the user asks about a disconnected integration, let them know it needs to be connected first. Do not attempt to use tools from disconnected integrations without informing the user.\";\n }\n } else if (requestCapabilityTool && connected.length > 0) {\n prompt +=\n \"\\n\\n## Integration Management\" +\n `\\n\\nIf the user asks to manage, reconnect, or verify integrations, call \\`${requestCapabilityTool}\\` to get a fresh integrations management link.`;\n }\n }\n\n return prompt;\n}\n\n// ============================================================================\n// Auth-aware toolset orchestrator\n// ============================================================================\n\nexport interface AuthAwareToolset {\n /** System prompt with integration status and REQUEST_CAPABILITY instructions. */\n readonly systemPrompt: string;\n /** REQUEST_CAPABILITY tool to inject, or null if all integrations are connected. */\n readonly requestCapabilityTool: {\n readonly name: string;\n readonly description: string;\n readonly parameters: Record<string, unknown>;\n readonly execute: () => Promise<string>;\n } | null;\n}\n\n/**\n * Enrich a tool set with auth awareness.\n *\n * Given tool names and integration status, produces:\n * - A system prompt that tells the LLM about connected/disconnected integrations\n * - A REQUEST_CAPABILITY tool (if needed) that returns a fresh management URL as text\n *\n * Both `ai/` and `cloudflare/` adapters use this as their core auth layer.\n */\nexport function enrichToolsWithAuthAwareness(\n toolNames: readonly string[],\n integrations: readonly IntegrationStatus[],\n options?: {\n prefix?: string;\n requireServerRequestCapability?: boolean;\n requestCapabilityProxy?: () => Promise<string>;\n },\n): AuthAwareToolset {\n const serverRequestCapability = toolNames.find(\n (name) =>\n name.endsWith(\"_REQUEST_CAPABILITY\") || name === \"REQUEST_CAPABILITY\",\n );\n\n if (options?.requireServerRequestCapability && !serverRequestCapability) {\n throw new ConfigError(\n \"REQUEST_CAPABILITY tool is required but not exposed by the MCP server. Deploy API and SDK in lockstep.\",\n \"kontext_config_missing_request_capability_tool\",\n );\n }\n\n let requestCapabilityTool: AuthAwareToolset[\"requestCapabilityTool\"] = null;\n let effectiveToolNames = [...toolNames];\n\n if (options?.requestCapabilityProxy) {\n const prefix =\n options.prefix ??\n toolNames\n .find((n) => n.endsWith(\"_SEARCH_TOOLS\"))\n ?.replace(/_SEARCH_TOOLS$/, \"\") ??\n \"kontext\";\n const toolName = `${prefix}_REQUEST_CAPABILITY`;\n const tool = buildRequestCapabilityTool(\n integrations,\n options.requestCapabilityProxy!,\n );\n requestCapabilityTool = { name: toolName, ...tool };\n effectiveToolNames = [...effectiveToolNames, toolName];\n }\n\n return {\n systemPrompt: buildKontextSystemPrompt(effectiveToolNames, integrations),\n requestCapabilityTool,\n };\n}\n\n// ============================================================================\n// Integration status parsing\n// ============================================================================\n\n/**\n * Parse integration status from gateway tool search results.\n * Extracts connected integrations from tools and disconnected from errors.\n */\nexport function parseIntegrationStatus(\n tools: ReadonlyArray<{ server?: { id?: string; name?: string } }>,\n errors: ReadonlyArray<{ serverId: string; serverName?: string }>,\n elicitations?: ReadonlyArray<{\n url: string;\n integrationId?: string;\n integrationName?: string;\n }>,\n): IntegrationStatus[] {\n const seen = new Set<string>();\n const result: IntegrationStatus[] = [];\n\n for (const t of tools) {\n const sid = t.server?.id;\n if (sid && !seen.has(sid)) {\n seen.add(sid);\n result.push({\n id: sid,\n name: t.server?.name ?? sid,\n connected: true,\n });\n }\n }\n\n for (const e of errors) {\n if (!seen.has(e.serverId)) {\n seen.add(e.serverId);\n const elicitation = elicitations?.find(\n (el) => el.integrationId === e.serverId,\n );\n result.push({\n id: e.serverId,\n name: e.serverName ?? e.serverId,\n connected: false,\n connectUrl: elicitation?.url,\n });\n }\n }\n\n return result;\n}\n\n// ============================================================================\n// Request capability tool\n// ============================================================================\n\n/**\n * Build a tool that lets the LLM request a fresh integrations management URL.\n * Returns the URL as text so the LLM can present it to the user.\n * No popups — the user clicks the link in the chat.\n */\nexport function buildRequestCapabilityTool(\n capabilities: readonly IntegrationStatus[],\n executeRequestCapability: () => Promise<string>,\n): {\n description: string;\n parameters: Record<string, unknown>;\n execute: () => Promise<string>;\n} {\n const connected = capabilities.filter((c) => c.connected);\n const disconnected = capabilities.filter((c) => !c.connected);\n const connectedList =\n connected.length > 0 ? connected.map((c) => c.name).join(\", \") : \"none\";\n const disconnectedList =\n disconnected.length > 0\n ? disconnected.map((c) => c.name).join(\", \")\n : \"none\";\n\n return {\n description:\n \"Request a fresh integrations management link from the Kontext server. \" +\n `Connected integrations: ${connectedList}. ` +\n `Disconnected integrations: ${disconnectedList}. ` +\n \"Always call this tool for a fresh link and never reuse an older link.\",\n parameters: {\n type: \"object\",\n properties: {},\n },\n execute: async (): Promise<string> => {\n return executeRequestCapability();\n },\n };\n}\n\n/**\n * Pre-flight integration status detection.\n * Calls SEARCH_TOOLS on raw (unwrapped) tools to detect connected/disconnected\n * integrations without triggering any popups or broadcasts.\n */\nexport async function preflightIntegrationStatus(\n tools: ToolSetLike,\n): Promise<IntegrationStatus[]> {\n const searchToolKey = Object.keys(tools).find((k) =>\n k.endsWith(\"_SEARCH_TOOLS\"),\n );\n if (!searchToolKey || !tools[searchToolKey]?.execute) return [];\n\n try {\n const result = await tools[searchToolKey].execute!({ limit: 100 });\n\n // Cloudflare getAITools() returns MCP CallToolResult objects, not strings.\n // Extract JSON text from either a plain string or a CallToolResult shape.\n let json: string | undefined;\n if (typeof result === \"string\") {\n json = result;\n } else if (result && typeof result === \"object\") {\n const content = (\n result as {\n content?: Array<{\n type: string;\n resource?: { text?: string };\n text?: string;\n }>;\n }\n ).content;\n const first = content?.[0];\n json = first?.resource?.text ?? first?.text;\n }\n\n if (typeof json === \"string\") {\n const parsed = JSON.parse(json);\n if (parsed && typeof parsed === \"object\") {\n return parseIntegrationStatus(\n Array.isArray(parsed.items) ? parsed.items : [],\n Array.isArray(parsed.errors) ? parsed.errors : [],\n Array.isArray(parsed.elicitations) ? parsed.elicitations : [],\n );\n }\n }\n } catch (err: unknown) {\n // All integrations disconnected: -32042 error contains elicitations\n const elicitations = extractElicitations(err);\n if (elicitations?.length) {\n return elicitations\n .filter(\n (e): e is ElicitationEntry & { integrationId: string } =>\n !!e.integrationId,\n )\n .map((e) => ({\n id: e.integrationId,\n name: e.integrationName ?? e.integrationId,\n connected: false,\n connectUrl: e.url,\n }));\n }\n }\n\n return [];\n}\n\n// ============================================================================\n// Tool wrapping\n// ============================================================================\n\n/** A tool with an optional async execute method */\nexport interface ToolLike {\n execute?: (...args: unknown[]) => Promise<unknown>;\n [key: string]: unknown;\n}\n\n/** Record of named tools */\nexport type ToolSetLike = Record<string, ToolLike>;\n\n/**\n * Extract elicitation entries from an error thrown by the MCP SDK.\n * Handles two shapes: direct code/data and nested cause.\n */\nexport function extractElicitations(\n err: unknown,\n): ElicitationEntry[] | undefined {\n const error = err as {\n code?: number;\n data?: { elicitations?: ElicitationEntry[] };\n cause?: {\n code?: number;\n data?: { elicitations?: ElicitationEntry[] };\n };\n };\n\n // Primary: structured McpError with code -32042\n if (error?.code === -32042 && error?.data?.elicitations?.length) {\n return error.data.elicitations;\n }\n\n // Nested cause (some SDK wrappers nest the original error)\n if (\n error?.cause?.code === -32042 &&\n error?.cause?.data?.elicitations?.length\n ) {\n return error.cause.data.elicitations;\n }\n\n return undefined;\n}\n\n/**\n * Wrap tools to catch MCP error code -32042 (URL elicitation required)\n * and convert to IntegrationConnectionRequiredError.\n */\nexport function wrapToolsWithElicitation<T extends ToolSetLike>(tools: T): T {\n return Object.fromEntries(\n Object.entries(tools).map(([name, tool]) => [\n name,\n {\n ...tool,\n execute: tool.execute\n ? async (...args: unknown[]) => {\n try {\n return await tool.execute!(...args);\n } catch (err: unknown) {\n const elicitations = extractElicitations(err);\n const elicitation = elicitations?.[0];\n if (elicitation?.url) {\n const integrationId = elicitation.integrationId ?? \"unknown\";\n throw new IntegrationConnectionRequiredError(integrationId, {\n integrationName: elicitation.integrationName,\n connectUrl: elicitation.url,\n message: elicitation.message,\n });\n }\n throw err;\n }\n }\n : undefined,\n },\n ]),\n ) as T;\n}\n","/**\n * Vercel AI SDK adapter for Kontext.\n *\n * Pure format converter: maps KontextClient tools to AI SDK CoreTools.\n *\n * @example\n * ```typescript\n * import { createKontextClient } from '@kontext-dev/js-sdk';\n * import { toKontextTools } from '@kontext-dev/js-sdk/ai';\n * import { generateText } from 'ai';\n *\n * const client = createKontextClient({\n * clientId: process.env.KONTEXT_CLIENT_ID!,\n * redirectUri: 'http://localhost:3000/callback',\n * onAuthRequired: (url) => open(url.toString()),\n * });\n *\n * const { tools, systemPrompt } = await toKontextTools(client);\n * const result = await generateText({ model, system: systemPrompt, tools, prompt: '...', maxSteps: 5 });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { jsonSchema, type CoreTool } from \"ai\";\nimport type {\n KontextClient,\n KontextTool,\n IntegrationInfo,\n ToolResult,\n} from \"../../client/index.js\";\nimport type { KontextOrchestrator } from \"../../client/orchestrator/index.js\";\nimport { enrichToolsWithAuthAwareness } from \"../../client/tool-utils.js\";\nimport { ConfigError } from \"../../errors.js\";\n\nexport interface ToKontextToolsOptions {\n formatResult?: (result: ToolResult) => unknown;\n}\n\nexport interface KontextToolsResult {\n readonly tools: Record<string, CoreTool>;\n readonly systemPrompt: string;\n readonly integrations: readonly IntegrationInfo[];\n}\n\n/**\n * Convert a KontextClient's tools into an AI SDK ToolSet with system prompt.\n *\n * Calls `client.tools.list()` and `client.integrations.list()` to discover\n * tools and integration status, then wraps each tool as an AI SDK `CoreTool`\n * whose `execute` delegates to `client.tools.execute()`.\n */\nexport async function toKontextTools(\n client: KontextClient | KontextOrchestrator,\n options?: ToKontextToolsOptions,\n): Promise<KontextToolsResult> {\n const [tools, integrations, mcpTools] = await Promise.all([\n client.tools.list(),\n client.integrations.list(),\n client.mcp.listTools(),\n ]);\n\n const coreTools: Record<string, CoreTool> = {};\n const usedNames = new Set<string>();\n\n for (const tool of tools) {\n const name = buildToolName(tool, usedNames);\n coreTools[name] = {\n description: formatDescription(tool),\n parameters: tool.inputSchema\n ? jsonSchema(tool.inputSchema as Parameters<typeof jsonSchema>[0])\n : jsonSchema({ type: \"object\" as const, properties: {} }),\n execute: async (args: Record<string, unknown>) => {\n const r = await client.tools.execute(tool.id, args);\n return (options?.formatResult ?? defaultFormat)(r);\n },\n };\n }\n\n if (!mcpTools.some((tool) => tool.name === \"REQUEST_CAPABILITY\")) {\n throw new ConfigError(\n \"REQUEST_CAPABILITY tool is missing on the MCP server. Deploy API and SDK in lockstep.\",\n \"kontext_config_missing_request_capability_tool\",\n );\n }\n\n const { systemPrompt, requestCapabilityTool } = enrichToolsWithAuthAwareness(\n [...tools.map((t) => t.name), \"kontext_REQUEST_CAPABILITY\"],\n integrations,\n {\n prefix: \"kontext\",\n requireServerRequestCapability: true,\n requestCapabilityProxy: async () => {\n const result = await client.mcp.callTool(\"REQUEST_CAPABILITY\", {});\n return extractText(result);\n },\n },\n );\n\n if (requestCapabilityTool) {\n coreTools[requestCapabilityTool.name] = {\n description: requestCapabilityTool.description,\n parameters: jsonSchema(\n requestCapabilityTool.parameters as Parameters<typeof jsonSchema>[0],\n ),\n execute: async () => {\n return requestCapabilityTool.execute();\n },\n };\n }\n\n return { tools: coreTools, systemPrompt, integrations };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction defaultFormat(r: ToolResult): unknown {\n return r.content;\n}\n\nfunction formatDescription(tool: KontextTool): string {\n const base = tool.description ?? tool.name;\n const serverLabel = tool.server?.name ?? tool.server?.id;\n return serverLabel ? `[${serverLabel}] ${base}` : base;\n}\n\nfunction buildToolName(tool: KontextTool, usedNames: Set<string>): string {\n const rawName = tool.name || tool.id || \"tool\";\n const base = tool.server\n ? `${tool.server.name ?? tool.server.id ?? \"server\"}_${rawName}`\n : rawName;\n const sanitized = sanitizeToolName(base);\n const fallback = sanitized || `tool_${usedNames.size + 1}`;\n\n let name = fallback;\n let suffix = 1;\n while (usedNames.has(name)) {\n name = `${fallback}_${suffix}`;\n suffix += 1;\n }\n\n usedNames.add(name);\n return name;\n}\n\nfunction sanitizeToolName(name: string): string {\n return name\n .replace(/[^a-zA-Z0-9_-]/g, \"_\")\n .replace(/_+/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .slice(0, 64);\n}\n\nfunction extractText(result: unknown): string {\n if (typeof result === \"string\") return result;\n if (!result || typeof result !== \"object\") {\n return String(result ?? \"\");\n }\n\n const content = (\n result as {\n content?: Array<{\n type?: string;\n text?: string;\n resource?: { text?: string };\n }>;\n }\n ).content;\n if (Array.isArray(content)) {\n const textItem = content.find(\n (item) => item.type === \"text\" && typeof item.text === \"string\",\n );\n if (textItem?.text) return textItem.text;\n\n const resourceItem = content.find(\n (item) =>\n item.type === \"resource\" && typeof item.resource?.text === \"string\",\n );\n if (resourceItem?.resource?.text) return resourceItem.resource.text;\n }\n\n return JSON.stringify(result);\n}\n"]}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { jsonSchema } from 'ai';
|
|
2
|
+
import { ErrorCode } from '@modelcontextprotocol/sdk/types.js';
|
|
2
3
|
|
|
3
4
|
// src/adapters/ai/index.ts
|
|
4
|
-
|
|
5
|
-
// src/errors.ts
|
|
6
5
|
var KontextError = class extends Error {
|
|
7
6
|
/** Brand field for type narrowing without instanceof */
|
|
8
7
|
kontextError = true;
|
|
@@ -50,6 +49,17 @@ var ConfigError = class extends KontextError {
|
|
|
50
49
|
this.name = "ConfigError";
|
|
51
50
|
}
|
|
52
51
|
};
|
|
52
|
+
({
|
|
53
|
+
[ErrorCode.ParseError]: { },
|
|
54
|
+
[ErrorCode.InvalidRequest]: { },
|
|
55
|
+
[ErrorCode.MethodNotFound]: { },
|
|
56
|
+
[ErrorCode.InvalidParams]: { },
|
|
57
|
+
[ErrorCode.InternalError]: {
|
|
58
|
+
},
|
|
59
|
+
[ErrorCode.RequestTimeout]: {
|
|
60
|
+
},
|
|
61
|
+
[ErrorCode.ConnectionClosed]: { }
|
|
62
|
+
});
|
|
53
63
|
|
|
54
64
|
// src/client/tool-utils.ts
|
|
55
65
|
function buildKontextSystemPrompt(toolNames, integrations) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/errors.ts","../../../src/client/tool-utils.ts","../../../src/adapters/ai/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Typed error classes for the Kontext SDK.\n *\n * Every error has a `kontext_` prefixed code, an auto-generated docsUrl,\n * and a `kontextError` brand for type narrowing without instanceof.\n *\n * @packageDocumentation\n */\n\n// ============================================================================\n// Base\n// ============================================================================\n\n/**\n * Base error class for all Kontext SDK errors.\n *\n * @example\n * ```typescript\n * import { isKontextError } from '@kontext-dev/js-sdk';\n *\n * try {\n * await client.connect();\n * } catch (err) {\n * if (isKontextError(err)) {\n * console.log(err.code); // \"kontext_authorization_required\"\n * console.log(err.docsUrl); // \"https://docs.kontext.dev/errors/kontext_authorization_required\"\n * }\n * }\n * ```\n */\nexport class KontextError extends Error {\n /** Brand field for type narrowing without instanceof */\n readonly kontextError = true as const;\n\n /** Machine-readable error code, always prefixed with `kontext_` */\n readonly code: string;\n\n /** HTTP status code when applicable */\n readonly statusCode?: number;\n\n /** Auto-generated link to error documentation */\n readonly docsUrl: string;\n\n /** Server request ID for debugging / support escalation */\n readonly requestId?: string;\n\n /** Contextual metadata bag (integration IDs, param names, etc.) */\n readonly meta: Record<string, unknown>;\n\n constructor(\n message: string,\n code: string,\n options?: {\n statusCode?: number;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, { cause: options?.cause });\n this.name = \"KontextError\";\n this.code = code;\n this.statusCode = options?.statusCode;\n this.requestId = options?.requestId;\n this.meta = options?.meta ?? {};\n this.docsUrl = `https://docs.kontext.dev/errors/${code}`;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n statusCode: this.statusCode,\n docsUrl: this.docsUrl,\n requestId: this.requestId,\n meta: Object.keys(this.meta).length > 0 ? this.meta : undefined,\n };\n }\n\n override toString(): string {\n const parts = [`[${this.code}] ${this.message}`];\n if (this.docsUrl) parts.push(`Docs: ${this.docsUrl}`);\n if (this.requestId) parts.push(`Request ID: ${this.requestId}`);\n return parts.join(\"\\n\");\n }\n}\n\n// ============================================================================\n// Type guard\n// ============================================================================\n\n/**\n * Check if an error is a KontextError without instanceof.\n * Works across package versions and bundler deduplication.\n */\nexport function isKontextError(err: unknown): err is KontextError {\n return (\n typeof err === \"object\" &&\n err !== null &&\n (err as Record<string, unknown>).kontextError === true\n );\n}\n\n// ============================================================================\n// Auth errors\n// ============================================================================\n\n/**\n * Thrown when authentication is required but no valid credentials are available.\n */\nexport class AuthorizationRequiredError extends KontextError {\n readonly authorizationUrl?: string;\n\n constructor(\n message = \"Authorization required. Complete the OAuth flow to continue.\",\n options?: {\n authorizationUrl?: string;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, \"kontext_authorization_required\", {\n statusCode: 401,\n ...options,\n });\n this.name = \"AuthorizationRequiredError\";\n this.authorizationUrl = options?.authorizationUrl;\n }\n}\n\n// ============================================================================\n// OAuth errors\n// ============================================================================\n\n/**\n * Thrown when an OAuth flow fails — state validation, token exchange,\n * missing code verifier, or provider errors.\n */\nexport class OAuthError extends KontextError {\n readonly errorCode?: string;\n readonly errorDescription?: string;\n\n constructor(\n message: string,\n code: string,\n options?: {\n statusCode?: number;\n errorCode?: string;\n errorDescription?: string;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, code, {\n statusCode: options?.statusCode ?? 400,\n ...options,\n });\n this.name = \"OAuthError\";\n this.errorCode = options?.errorCode;\n this.errorDescription = options?.errorDescription;\n }\n}\n\n// ============================================================================\n// Integration errors\n// ============================================================================\n\n/**\n * Thrown when an integration connection is required before a tool can be used.\n */\nexport class IntegrationConnectionRequiredError extends KontextError {\n readonly integrationId: string;\n readonly integrationName?: string;\n readonly connectUrl?: string;\n\n constructor(\n integrationId: string,\n options?: {\n integrationName?: string;\n connectUrl?: string;\n message?: string;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(\n options?.message ??\n `Connection to integration \"${integrationId}\" is required. Visit the connect URL to authorize.`,\n \"kontext_integration_connection_required\",\n { statusCode: 403, ...options },\n );\n this.name = \"IntegrationConnectionRequiredError\";\n this.integrationId = integrationId;\n this.integrationName = options?.integrationName;\n this.connectUrl = options?.connectUrl;\n }\n}\n\n// ============================================================================\n// Config errors (NEW — replaces all plain Error config throws)\n// ============================================================================\n\n/**\n * Thrown when SDK configuration is invalid or missing.\n * These are deterministic errors caught at initialization time.\n */\nexport class ConfigError extends KontextError {\n constructor(\n message: string,\n code: string,\n options?: {\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, code, options);\n this.name = \"ConfigError\";\n }\n}\n\n// ============================================================================\n// Network errors\n// ============================================================================\n\n/**\n * Thrown when there is a network or connection error.\n */\nexport class NetworkError extends KontextError {\n constructor(\n message = \"Network error. Check your internet connection and that the server is reachable.\",\n options?: {\n cause?: unknown;\n requestId?: string;\n meta?: Record<string, unknown>;\n },\n ) {\n super(message, \"kontext_network_error\", options);\n this.name = \"NetworkError\";\n }\n}\n\n// ============================================================================\n// HTTP response errors (differentiated by code)\n// ============================================================================\n\n/**\n * Thrown when the server returns an HTTP error.\n * Use `error.code` to distinguish between specific error types.\n */\nexport class HttpError extends KontextError {\n readonly retryAfter?: number;\n readonly validationErrors?: Array<{ field: string; message: string }>;\n\n constructor(\n message: string,\n code: string,\n options?: {\n statusCode?: number;\n retryAfter?: number;\n validationErrors?: Array<{ field: string; message: string }>;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, code, {\n statusCode: options?.statusCode,\n ...options,\n });\n this.name = \"HttpError\";\n this.retryAfter = options?.retryAfter;\n this.validationErrors = options?.validationErrors;\n }\n}\n\n// ============================================================================\n// Network error detection (used by translateError)\n// ============================================================================\n\n/**\n * Safely access arbitrary properties on an error object.\n * Errors in JS frequently carry extra properties (code, statusCode, etc.)\n * that aren't part of the Error interface. This avoids `as unknown as` casts.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction errorProps(err: Error): Record<string, any> {\n return err;\n}\n\nconst NETWORK_ERROR_CODES = new Set([\n \"ECONNREFUSED\",\n \"ENOTFOUND\",\n \"ETIMEDOUT\",\n \"ECONNRESET\",\n \"ECONNABORTED\",\n \"EPIPE\",\n \"UND_ERR_CONNECT_TIMEOUT\",\n]);\n\n/**\n * Detect network errors structurally rather than by string matching.\n * Checks Node.js system error codes on the error and its cause.\n */\nexport function isNetworkError(err: Error): boolean {\n if (err.name === \"AbortError\") return true;\n\n const props = errorProps(err);\n const sysCode = props.code as string | undefined;\n if (typeof sysCode === \"string\" && NETWORK_ERROR_CODES.has(sysCode))\n return true;\n\n // fetch() throws TypeError — only classify as network error when cause\n // indicates a system-level failure\n if (err.name === \"TypeError\" && err.cause instanceof Error) {\n const causeCode = errorProps(err.cause).code;\n if (typeof causeCode === \"string\" && NETWORK_ERROR_CODES.has(causeCode))\n return true;\n }\n\n return false;\n}\n\n/**\n * Detect unauthorized errors structurally.\n * Checks status code and numeric code rather than string matching on name.\n */\nexport function isUnauthorizedError(err: Error): boolean {\n const props = errorProps(err);\n\n // Check HTTP status code (most reliable)\n if (props.statusCode === 401 || props.status === 401) return true;\n\n // Check MCP SDK UnauthorizedError by name (last resort, but needed for\n // MCP SDK errors which don't set statusCode)\n if (err.name === \"UnauthorizedError\") return true;\n if (err.message === \"Unauthorized\") return true;\n\n return false;\n}\n\n// ============================================================================\n// Elicitation types\n// ============================================================================\n\nexport interface ElicitationEntry {\n readonly url: string;\n readonly message: string;\n readonly elicitationId: string;\n readonly integrationId?: string;\n readonly integrationName?: string;\n}\n\n// ============================================================================\n// HTTP error parsing\n// ============================================================================\n\n/**\n * Parse an HTTP response into an appropriate error.\n */\nexport function parseHttpError(\n statusCode: number,\n body?: unknown,\n): KontextError {\n const message =\n typeof body === \"object\" && body !== null && \"message\" in body\n ? String((body as { message: unknown }).message)\n : `HTTP ${statusCode}`;\n\n const errorCode =\n typeof body === \"object\" && body !== null && \"code\" in body\n ? String((body as { code: unknown }).code)\n : undefined;\n\n switch (statusCode) {\n case 400:\n if (\n typeof body === \"object\" &&\n body !== null &&\n \"errors\" in body &&\n Array.isArray((body as { errors: unknown }).errors)\n ) {\n return new HttpError(message, \"kontext_validation_error\", {\n statusCode: 400,\n validationErrors: (\n body as { errors: Array<{ field: string; message: string }> }\n ).errors,\n });\n }\n return new KontextError(message, errorCode ?? \"kontext_bad_request\", {\n statusCode: 400,\n });\n\n case 401:\n return new AuthorizationRequiredError(message);\n\n case 403:\n if (errorCode === \"INTEGRATION_CONNECTION_REQUIRED\") {\n const details = body as {\n integrationId?: string;\n integrationName?: string;\n connectUrl?: string;\n };\n return new IntegrationConnectionRequiredError(\n details.integrationId ?? \"unknown\",\n {\n integrationName: details.integrationName,\n connectUrl: details.connectUrl,\n message,\n },\n );\n }\n return new HttpError(message, \"kontext_policy_denied\", {\n statusCode: 403,\n meta: { policy: (body as Record<string, unknown>)?.policy },\n });\n\n case 404:\n return new HttpError(message, \"kontext_not_found\", { statusCode: 404 });\n\n case 429: {\n const retryAfter =\n typeof body === \"object\" && body !== null && \"retryAfter\" in body\n ? Number((body as { retryAfter: unknown }).retryAfter)\n : undefined;\n return new HttpError(\n retryAfter\n ? `Rate limit exceeded. Retry after ${retryAfter} seconds.`\n : \"Rate limit exceeded. Wait and retry.\",\n \"kontext_rate_limited\",\n { statusCode: 429, retryAfter },\n );\n }\n\n default:\n if (statusCode >= 500) {\n return new HttpError(\n `Server error (HTTP ${statusCode}): ${message}`,\n \"kontext_server_error\",\n { statusCode },\n );\n }\n return new KontextError(message, errorCode ?? \"kontext_unknown_error\", {\n statusCode,\n });\n }\n}\n","/**\n * Shared utilities for Kontext tool handling.\n * Used by both KontextClient (core) and withKontext (Cloudflare adapter).\n */\n\nimport {\n ConfigError,\n IntegrationConnectionRequiredError,\n type ElicitationEntry,\n} from \"../errors.js\";\n\n// Re-export for consumers that import this type from tool-utils.\nexport type { ElicitationEntry } from \"../errors.js\";\n\n// ============================================================================\n// Shared types\n// ============================================================================\n\nexport interface IntegrationStatus {\n readonly id: string;\n readonly name: string;\n readonly connected: boolean;\n readonly connectUrl?: string;\n}\n\n// ============================================================================\n// System prompt\n// ============================================================================\n\n/**\n * Build an LLM system prompt based on available tool names and integration status.\n * Detects `*_SEARCH_TOOLS` / `*_EXECUTE_TOOL` suffixed names from the\n * Cloudflare Agents MCP proxy and generates appropriate guidance.\n */\nexport function buildKontextSystemPrompt(\n toolNames: readonly string[],\n integrations: readonly IntegrationStatus[],\n): string {\n if (toolNames.length === 0) return \"\";\n\n const searchTool = toolNames.find(\n (n) => n.endsWith(\"_SEARCH_TOOLS\") || n === \"SEARCH_TOOLS\",\n );\n const executeTool = toolNames.find(\n (n) => n.endsWith(\"_EXECUTE_TOOL\") || n === \"EXECUTE_TOOL\",\n );\n const requestCapabilityTool = toolNames.find(\n (n) => n.endsWith(\"_REQUEST_CAPABILITY\") || n === \"REQUEST_CAPABILITY\",\n );\n\n let prompt =\n \"You are a helpful assistant with access to various tools and integrations through Kontext.\\n\" +\n \"You can help users with tasks across their connected services (GitHub, Linear, Slack, etc.).\\n\" +\n \"When a user asks about their services, use the available MCP tools to help them.\\n\" +\n \"The user has already authenticated — tools run as the user with their permissions, including access to private repositories and org resources.\\n\" +\n \"If a Kontext tool can satisfy an integration-related request, use the Kontext tool and do not use shell or CLI commands for that request.\\n\" +\n \"Use shell or CLI only when no relevant Kontext tool exists or when the user explicitly requests local shell actions.\";\n\n if (searchTool && executeTool) {\n prompt +=\n `\\n\\nYou have access to external integrations through Kontext MCP tools:\\n` +\n `- Call \\`${searchTool}\\` first to discover what tools/integrations are available.\\n` +\n `- Then call \\`${executeTool}\\` with the tool_id and arguments to run a specific tool.\\n` +\n `- When the user asks about \"my repos\", \"my issues\", etc., use the appropriate tool to look up their data directly. Do not ask for their username — the tools already know who they are.\\n` +\n `Always start by searching for tools when the user asks about their connected services.`;\n }\n\n // Append integration status section when there are integrations to report\n if (integrations.length > 0) {\n const connected = integrations.filter((i) => i.connected);\n const disconnected = integrations.filter((i) => !i.connected);\n\n if (disconnected.length > 0) {\n prompt += \"\\n\\n## Integration Status\";\n\n if (connected.length > 0) {\n prompt += `\\n\\nYou already have access to: ${connected.map((i) => i.name).join(\", \")}`;\n }\n\n prompt += `\\n\\nThe following services require user authorization: ${disconnected.map((i) => i.name).join(\", \")}`;\n\n if (requestCapabilityTool) {\n prompt +=\n `\\n\\n**IMPORTANT:** When the user requests an action that requires one of these services:` +\n `\\n1. Call the \\`${requestCapabilityTool}\\` tool` +\n `\\n2. The tool returns a fresh integrations management link` +\n `\\n3. Include the link in your response so the user can connect or manage integrations` +\n `\\n4. Always call the tool for a fresh link and never reuse a previously returned URL`;\n } else {\n prompt +=\n \"\\n\\nWhen the user asks about a disconnected integration, let them know it needs to be connected first. Do not attempt to use tools from disconnected integrations without informing the user.\";\n }\n } else if (requestCapabilityTool && connected.length > 0) {\n prompt +=\n \"\\n\\n## Integration Management\" +\n `\\n\\nIf the user asks to manage, reconnect, or verify integrations, call \\`${requestCapabilityTool}\\` to get a fresh integrations management link.`;\n }\n }\n\n return prompt;\n}\n\n// ============================================================================\n// Auth-aware toolset orchestrator\n// ============================================================================\n\nexport interface AuthAwareToolset {\n /** System prompt with integration status and REQUEST_CAPABILITY instructions. */\n readonly systemPrompt: string;\n /** REQUEST_CAPABILITY tool to inject, or null if all integrations are connected. */\n readonly requestCapabilityTool: {\n readonly name: string;\n readonly description: string;\n readonly parameters: Record<string, unknown>;\n readonly execute: () => Promise<string>;\n } | null;\n}\n\n/**\n * Enrich a tool set with auth awareness.\n *\n * Given tool names and integration status, produces:\n * - A system prompt that tells the LLM about connected/disconnected integrations\n * - A REQUEST_CAPABILITY tool (if needed) that returns a fresh management URL as text\n *\n * Both `ai/` and `cloudflare/` adapters use this as their core auth layer.\n */\nexport function enrichToolsWithAuthAwareness(\n toolNames: readonly string[],\n integrations: readonly IntegrationStatus[],\n options?: {\n prefix?: string;\n requireServerRequestCapability?: boolean;\n requestCapabilityProxy?: () => Promise<string>;\n },\n): AuthAwareToolset {\n const serverRequestCapability = toolNames.find(\n (name) =>\n name.endsWith(\"_REQUEST_CAPABILITY\") || name === \"REQUEST_CAPABILITY\",\n );\n\n if (options?.requireServerRequestCapability && !serverRequestCapability) {\n throw new ConfigError(\n \"REQUEST_CAPABILITY tool is required but not exposed by the MCP server. Deploy API and SDK in lockstep.\",\n \"kontext_config_missing_request_capability_tool\",\n );\n }\n\n let requestCapabilityTool: AuthAwareToolset[\"requestCapabilityTool\"] = null;\n let effectiveToolNames = [...toolNames];\n\n if (options?.requestCapabilityProxy) {\n const prefix =\n options.prefix ??\n toolNames\n .find((n) => n.endsWith(\"_SEARCH_TOOLS\"))\n ?.replace(/_SEARCH_TOOLS$/, \"\") ??\n \"kontext\";\n const toolName = `${prefix}_REQUEST_CAPABILITY`;\n const tool = buildRequestCapabilityTool(\n integrations,\n options.requestCapabilityProxy!,\n );\n requestCapabilityTool = { name: toolName, ...tool };\n effectiveToolNames = [...effectiveToolNames, toolName];\n }\n\n return {\n systemPrompt: buildKontextSystemPrompt(effectiveToolNames, integrations),\n requestCapabilityTool,\n };\n}\n\n// ============================================================================\n// Integration status parsing\n// ============================================================================\n\n/**\n * Parse integration status from gateway tool search results.\n * Extracts connected integrations from tools and disconnected from errors.\n */\nexport function parseIntegrationStatus(\n tools: ReadonlyArray<{ server?: { id?: string; name?: string } }>,\n errors: ReadonlyArray<{ serverId: string; serverName?: string }>,\n elicitations?: ReadonlyArray<{\n url: string;\n integrationId?: string;\n integrationName?: string;\n }>,\n): IntegrationStatus[] {\n const seen = new Set<string>();\n const result: IntegrationStatus[] = [];\n\n for (const t of tools) {\n const sid = t.server?.id;\n if (sid && !seen.has(sid)) {\n seen.add(sid);\n result.push({\n id: sid,\n name: t.server?.name ?? sid,\n connected: true,\n });\n }\n }\n\n for (const e of errors) {\n if (!seen.has(e.serverId)) {\n seen.add(e.serverId);\n const elicitation = elicitations?.find(\n (el) => el.integrationId === e.serverId,\n );\n result.push({\n id: e.serverId,\n name: e.serverName ?? e.serverId,\n connected: false,\n connectUrl: elicitation?.url,\n });\n }\n }\n\n return result;\n}\n\n// ============================================================================\n// Request capability tool\n// ============================================================================\n\n/**\n * Build a tool that lets the LLM request a fresh integrations management URL.\n * Returns the URL as text so the LLM can present it to the user.\n * No popups — the user clicks the link in the chat.\n */\nexport function buildRequestCapabilityTool(\n capabilities: readonly IntegrationStatus[],\n executeRequestCapability: () => Promise<string>,\n): {\n description: string;\n parameters: Record<string, unknown>;\n execute: () => Promise<string>;\n} {\n const connected = capabilities.filter((c) => c.connected);\n const disconnected = capabilities.filter((c) => !c.connected);\n const connectedList =\n connected.length > 0 ? connected.map((c) => c.name).join(\", \") : \"none\";\n const disconnectedList =\n disconnected.length > 0\n ? disconnected.map((c) => c.name).join(\", \")\n : \"none\";\n\n return {\n description:\n \"Request a fresh integrations management link from the Kontext server. \" +\n `Connected integrations: ${connectedList}. ` +\n `Disconnected integrations: ${disconnectedList}. ` +\n \"Always call this tool for a fresh link and never reuse an older link.\",\n parameters: {\n type: \"object\",\n properties: {},\n },\n execute: async (): Promise<string> => {\n return executeRequestCapability();\n },\n };\n}\n\n/**\n * Pre-flight integration status detection.\n * Calls SEARCH_TOOLS on raw (unwrapped) tools to detect connected/disconnected\n * integrations without triggering any popups or broadcasts.\n */\nexport async function preflightIntegrationStatus(\n tools: ToolSetLike,\n): Promise<IntegrationStatus[]> {\n const searchToolKey = Object.keys(tools).find((k) =>\n k.endsWith(\"_SEARCH_TOOLS\"),\n );\n if (!searchToolKey || !tools[searchToolKey]?.execute) return [];\n\n try {\n const result = await tools[searchToolKey].execute!({ limit: 100 });\n\n // Cloudflare getAITools() returns MCP CallToolResult objects, not strings.\n // Extract JSON text from either a plain string or a CallToolResult shape.\n let json: string | undefined;\n if (typeof result === \"string\") {\n json = result;\n } else if (result && typeof result === \"object\") {\n const content = (\n result as {\n content?: Array<{\n type: string;\n resource?: { text?: string };\n text?: string;\n }>;\n }\n ).content;\n const first = content?.[0];\n json = first?.resource?.text ?? first?.text;\n }\n\n if (typeof json === \"string\") {\n const parsed = JSON.parse(json);\n if (parsed && typeof parsed === \"object\") {\n return parseIntegrationStatus(\n Array.isArray(parsed.items) ? parsed.items : [],\n Array.isArray(parsed.errors) ? parsed.errors : [],\n Array.isArray(parsed.elicitations) ? parsed.elicitations : [],\n );\n }\n }\n } catch (err: unknown) {\n // All integrations disconnected: -32042 error contains elicitations\n const elicitations = extractElicitations(err);\n if (elicitations?.length) {\n return elicitations\n .filter(\n (e): e is ElicitationEntry & { integrationId: string } =>\n !!e.integrationId,\n )\n .map((e) => ({\n id: e.integrationId,\n name: e.integrationName ?? e.integrationId,\n connected: false,\n connectUrl: e.url,\n }));\n }\n }\n\n return [];\n}\n\n// ============================================================================\n// Tool wrapping\n// ============================================================================\n\n/** A tool with an optional async execute method */\nexport interface ToolLike {\n execute?: (...args: unknown[]) => Promise<unknown>;\n [key: string]: unknown;\n}\n\n/** Record of named tools */\nexport type ToolSetLike = Record<string, ToolLike>;\n\n/**\n * Extract elicitation entries from an error thrown by the MCP SDK.\n * Handles two shapes: direct code/data and nested cause.\n */\nexport function extractElicitations(\n err: unknown,\n): ElicitationEntry[] | undefined {\n const error = err as {\n code?: number;\n data?: { elicitations?: ElicitationEntry[] };\n cause?: {\n code?: number;\n data?: { elicitations?: ElicitationEntry[] };\n };\n };\n\n // Primary: structured McpError with code -32042\n if (error?.code === -32042 && error?.data?.elicitations?.length) {\n return error.data.elicitations;\n }\n\n // Nested cause (some SDK wrappers nest the original error)\n if (\n error?.cause?.code === -32042 &&\n error?.cause?.data?.elicitations?.length\n ) {\n return error.cause.data.elicitations;\n }\n\n return undefined;\n}\n\n/**\n * Wrap tools to catch MCP error code -32042 (URL elicitation required)\n * and convert to IntegrationConnectionRequiredError.\n */\nexport function wrapToolsWithElicitation<T extends ToolSetLike>(tools: T): T {\n return Object.fromEntries(\n Object.entries(tools).map(([name, tool]) => [\n name,\n {\n ...tool,\n execute: tool.execute\n ? async (...args: unknown[]) => {\n try {\n return await tool.execute!(...args);\n } catch (err: unknown) {\n const elicitations = extractElicitations(err);\n const elicitation = elicitations?.[0];\n if (elicitation?.url) {\n const integrationId = elicitation.integrationId ?? \"unknown\";\n throw new IntegrationConnectionRequiredError(integrationId, {\n integrationName: elicitation.integrationName,\n connectUrl: elicitation.url,\n message: elicitation.message,\n });\n }\n throw err;\n }\n }\n : undefined,\n },\n ]),\n ) as T;\n}\n","/**\n * Vercel AI SDK adapter for Kontext.\n *\n * Pure format converter: maps KontextClient tools to AI SDK CoreTools.\n *\n * @example\n * ```typescript\n * import { createKontextClient } from '@kontext-dev/js-sdk';\n * import { toKontextTools } from '@kontext-dev/js-sdk/ai';\n * import { generateText } from 'ai';\n *\n * const client = createKontextClient({\n * clientId: process.env.KONTEXT_CLIENT_ID!,\n * redirectUri: 'http://localhost:3000/callback',\n * onAuthRequired: (url) => open(url.toString()),\n * });\n *\n * const { tools, systemPrompt } = await toKontextTools(client);\n * const result = await generateText({ model, system: systemPrompt, tools, prompt: '...', maxSteps: 5 });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { jsonSchema, type CoreTool } from \"ai\";\nimport type {\n KontextClient,\n KontextTool,\n IntegrationInfo,\n ToolResult,\n} from \"../../client/index.js\";\nimport type { KontextOrchestrator } from \"../../client/orchestrator/index.js\";\nimport { enrichToolsWithAuthAwareness } from \"../../client/tool-utils.js\";\nimport { ConfigError } from \"../../errors.js\";\n\nexport interface ToKontextToolsOptions {\n formatResult?: (result: ToolResult) => unknown;\n}\n\nexport interface KontextToolsResult {\n readonly tools: Record<string, CoreTool>;\n readonly systemPrompt: string;\n readonly integrations: readonly IntegrationInfo[];\n}\n\n/**\n * Convert a KontextClient's tools into an AI SDK ToolSet with system prompt.\n *\n * Calls `client.tools.list()` and `client.integrations.list()` to discover\n * tools and integration status, then wraps each tool as an AI SDK `CoreTool`\n * whose `execute` delegates to `client.tools.execute()`.\n */\nexport async function toKontextTools(\n client: KontextClient | KontextOrchestrator,\n options?: ToKontextToolsOptions,\n): Promise<KontextToolsResult> {\n const [tools, integrations, mcpTools] = await Promise.all([\n client.tools.list(),\n client.integrations.list(),\n client.mcp.listTools(),\n ]);\n\n const coreTools: Record<string, CoreTool> = {};\n const usedNames = new Set<string>();\n\n for (const tool of tools) {\n const name = buildToolName(tool, usedNames);\n coreTools[name] = {\n description: formatDescription(tool),\n parameters: tool.inputSchema\n ? jsonSchema(tool.inputSchema as Parameters<typeof jsonSchema>[0])\n : jsonSchema({ type: \"object\" as const, properties: {} }),\n execute: async (args: Record<string, unknown>) => {\n const r = await client.tools.execute(tool.id, args);\n return (options?.formatResult ?? defaultFormat)(r);\n },\n };\n }\n\n if (!mcpTools.some((tool) => tool.name === \"REQUEST_CAPABILITY\")) {\n throw new ConfigError(\n \"REQUEST_CAPABILITY tool is missing on the MCP server. Deploy API and SDK in lockstep.\",\n \"kontext_config_missing_request_capability_tool\",\n );\n }\n\n const { systemPrompt, requestCapabilityTool } = enrichToolsWithAuthAwareness(\n [...tools.map((t) => t.name), \"kontext_REQUEST_CAPABILITY\"],\n integrations,\n {\n prefix: \"kontext\",\n requireServerRequestCapability: true,\n requestCapabilityProxy: async () => {\n const result = await client.mcp.callTool(\"REQUEST_CAPABILITY\", {});\n return extractText(result);\n },\n },\n );\n\n if (requestCapabilityTool) {\n coreTools[requestCapabilityTool.name] = {\n description: requestCapabilityTool.description,\n parameters: jsonSchema(\n requestCapabilityTool.parameters as Parameters<typeof jsonSchema>[0],\n ),\n execute: async () => {\n return requestCapabilityTool.execute();\n },\n };\n }\n\n return { tools: coreTools, systemPrompt, integrations };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction defaultFormat(r: ToolResult): unknown {\n return r.content;\n}\n\nfunction formatDescription(tool: KontextTool): string {\n const base = tool.description ?? tool.name;\n const serverLabel = tool.server?.name ?? tool.server?.id;\n return serverLabel ? `[${serverLabel}] ${base}` : base;\n}\n\nfunction buildToolName(tool: KontextTool, usedNames: Set<string>): string {\n const rawName = tool.name || tool.id || \"tool\";\n const base = tool.server\n ? `${tool.server.name ?? tool.server.id ?? \"server\"}_${rawName}`\n : rawName;\n const sanitized = sanitizeToolName(base);\n const fallback = sanitized || `tool_${usedNames.size + 1}`;\n\n let name = fallback;\n let suffix = 1;\n while (usedNames.has(name)) {\n name = `${fallback}_${suffix}`;\n suffix += 1;\n }\n\n usedNames.add(name);\n return name;\n}\n\nfunction sanitizeToolName(name: string): string {\n return name\n .replace(/[^a-zA-Z0-9_-]/g, \"_\")\n .replace(/_+/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .slice(0, 64);\n}\n\nfunction extractText(result: unknown): string {\n if (typeof result === \"string\") return result;\n if (!result || typeof result !== \"object\") {\n return String(result ?? \"\");\n }\n\n const content = (\n result as {\n content?: Array<{\n type?: string;\n text?: string;\n resource?: { text?: string };\n }>;\n }\n ).content;\n if (Array.isArray(content)) {\n const textItem = content.find(\n (item) => item.type === \"text\" && typeof item.text === \"string\",\n );\n if (textItem?.text) return textItem.text;\n\n const resourceItem = content.find(\n (item) =>\n item.type === \"resource\" && typeof item.resource?.text === \"string\",\n );\n if (resourceItem?.resource?.text) return resourceItem.resource.text;\n }\n\n return JSON.stringify(result);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/errors.ts","../../../src/client/tool-utils.ts","../../../src/adapters/ai/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Typed error classes for the Kontext SDK.\n *\n * Every error has a `kontext_` prefixed code, an auto-generated docsUrl,\n * and a `kontextError` brand for type narrowing without instanceof.\n *\n * @packageDocumentation\n */\n\nimport { ErrorCode } from \"@modelcontextprotocol/sdk/types.js\";\n\n// ============================================================================\n// Base\n// ============================================================================\n\n/**\n * Base error class for all Kontext SDK errors.\n *\n * @example\n * ```typescript\n * import { isKontextError } from '@kontext-dev/js-sdk';\n *\n * try {\n * await client.connect();\n * } catch (err) {\n * if (isKontextError(err)) {\n * console.log(err.code); // \"kontext_authorization_required\"\n * console.log(err.docsUrl); // \"https://docs.kontext.dev/errors/kontext_authorization_required\"\n * }\n * }\n * ```\n */\nexport class KontextError extends Error {\n /** Brand field for type narrowing without instanceof */\n readonly kontextError = true as const;\n\n /** Machine-readable error code, always prefixed with `kontext_` */\n readonly code: string;\n\n /** HTTP status code when applicable */\n readonly statusCode?: number;\n\n /** Auto-generated link to error documentation */\n readonly docsUrl: string;\n\n /** Server request ID for debugging / support escalation */\n readonly requestId?: string;\n\n /** Contextual metadata bag (integration IDs, param names, etc.) */\n readonly meta: Record<string, unknown>;\n\n constructor(\n message: string,\n code: string,\n options?: {\n statusCode?: number;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, { cause: options?.cause });\n this.name = \"KontextError\";\n this.code = code;\n this.statusCode = options?.statusCode;\n this.requestId = options?.requestId;\n this.meta = options?.meta ?? {};\n this.docsUrl = `https://docs.kontext.dev/errors/${code}`;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n statusCode: this.statusCode,\n docsUrl: this.docsUrl,\n requestId: this.requestId,\n meta: Object.keys(this.meta).length > 0 ? this.meta : undefined,\n };\n }\n\n override toString(): string {\n const parts = [`[${this.code}] ${this.message}`];\n if (this.docsUrl) parts.push(`Docs: ${this.docsUrl}`);\n if (this.requestId) parts.push(`Request ID: ${this.requestId}`);\n return parts.join(\"\\n\");\n }\n}\n\n// ============================================================================\n// Type guard\n// ============================================================================\n\n/**\n * Check if an error is a KontextError without instanceof.\n * Works across package versions and bundler deduplication.\n */\nexport function isKontextError(err: unknown): err is KontextError {\n return (\n typeof err === \"object\" &&\n err !== null &&\n (err as Record<string, unknown>).kontextError === true\n );\n}\n\n// ============================================================================\n// Auth errors\n// ============================================================================\n\n/**\n * Thrown when authentication is required but no valid credentials are available.\n */\nexport class AuthorizationRequiredError extends KontextError {\n readonly authorizationUrl?: string;\n\n constructor(\n message = \"Authorization required. Complete the OAuth flow to continue.\",\n options?: {\n authorizationUrl?: string;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, \"kontext_authorization_required\", {\n statusCode: 401,\n ...options,\n });\n this.name = \"AuthorizationRequiredError\";\n this.authorizationUrl = options?.authorizationUrl;\n }\n}\n\n// ============================================================================\n// OAuth errors\n// ============================================================================\n\n/**\n * Thrown when an OAuth flow fails — state validation, token exchange,\n * missing code verifier, or provider errors.\n */\nexport class OAuthError extends KontextError {\n readonly errorCode?: string;\n readonly errorDescription?: string;\n\n constructor(\n message: string,\n code: string,\n options?: {\n statusCode?: number;\n errorCode?: string;\n errorDescription?: string;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, code, {\n statusCode: options?.statusCode ?? 400,\n ...options,\n });\n this.name = \"OAuthError\";\n this.errorCode = options?.errorCode;\n this.errorDescription = options?.errorDescription;\n }\n}\n\n// ============================================================================\n// Integration errors\n// ============================================================================\n\n/**\n * Thrown when an integration connection is required before a tool can be used.\n */\nexport class IntegrationConnectionRequiredError extends KontextError {\n readonly integrationId: string;\n readonly integrationName?: string;\n readonly connectUrl?: string;\n\n constructor(\n integrationId: string,\n options?: {\n integrationName?: string;\n connectUrl?: string;\n message?: string;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(\n options?.message ??\n `Connection to integration \"${integrationId}\" is required. Visit the connect URL to authorize.`,\n \"kontext_integration_connection_required\",\n { statusCode: 403, ...options },\n );\n this.name = \"IntegrationConnectionRequiredError\";\n this.integrationId = integrationId;\n this.integrationName = options?.integrationName;\n this.connectUrl = options?.connectUrl;\n }\n}\n\n// ============================================================================\n// Config errors (NEW — replaces all plain Error config throws)\n// ============================================================================\n\n/**\n * Thrown when SDK configuration is invalid or missing.\n * These are deterministic errors caught at initialization time.\n */\nexport class ConfigError extends KontextError {\n constructor(\n message: string,\n code: string,\n options?: {\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, code, options);\n this.name = \"ConfigError\";\n }\n}\n\n// ============================================================================\n// Network errors\n// ============================================================================\n\n/**\n * Thrown when there is a network or connection error.\n */\nexport class NetworkError extends KontextError {\n constructor(\n message = \"Network error. Check your internet connection and that the server is reachable.\",\n options?: {\n cause?: unknown;\n requestId?: string;\n meta?: Record<string, unknown>;\n },\n ) {\n super(message, \"kontext_network_error\", options);\n this.name = \"NetworkError\";\n }\n}\n\n// ============================================================================\n// HTTP response errors (differentiated by code)\n// ============================================================================\n\n/**\n * Thrown when the server returns an HTTP error.\n * Use `error.code` to distinguish between specific error types.\n */\nexport class HttpError extends KontextError {\n readonly retryAfter?: number;\n readonly validationErrors?: Array<{ field: string; message: string }>;\n\n constructor(\n message: string,\n code: string,\n options?: {\n statusCode?: number;\n retryAfter?: number;\n validationErrors?: Array<{ field: string; message: string }>;\n requestId?: string;\n meta?: Record<string, unknown>;\n cause?: unknown;\n },\n ) {\n super(message, code, {\n statusCode: options?.statusCode,\n ...options,\n });\n this.name = \"HttpError\";\n this.retryAfter = options?.retryAfter;\n this.validationErrors = options?.validationErrors;\n }\n}\n\n// ============================================================================\n// Network error detection (used by translateError)\n// ============================================================================\n\n/**\n * Safely access arbitrary properties on an error object.\n * Errors in JS frequently carry extra properties (code, statusCode, etc.)\n * that aren't part of the Error interface. This avoids `as unknown as` casts.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction errorProps(err: Error): Record<string, any> {\n return err;\n}\n\nconst NETWORK_ERROR_CODES = new Set([\n \"ECONNREFUSED\",\n \"ENOTFOUND\",\n \"ETIMEDOUT\",\n \"ECONNRESET\",\n \"ECONNABORTED\",\n \"EPIPE\",\n \"UND_ERR_CONNECT_TIMEOUT\",\n]);\n\n/**\n * Detect network errors structurally rather than by string matching.\n * Checks Node.js system error codes on the error and its cause.\n */\nexport function isNetworkError(err: Error): boolean {\n if (err.name === \"AbortError\") return true;\n\n const props = errorProps(err);\n const sysCode = props.code as string | undefined;\n if (typeof sysCode === \"string\" && NETWORK_ERROR_CODES.has(sysCode))\n return true;\n\n // fetch() throws TypeError — only classify as network error when cause\n // indicates a system-level failure\n if (err.name === \"TypeError\" && err.cause instanceof Error) {\n const causeCode = errorProps(err.cause).code;\n if (typeof causeCode === \"string\" && NETWORK_ERROR_CODES.has(causeCode))\n return true;\n }\n\n // Browser fetch() failures: TypeError with known messages, no system error code\n if (err.name === \"TypeError\") {\n const msg = err.message.toLowerCase();\n if (\n msg === \"failed to fetch\" ||\n msg === \"load failed\" ||\n msg.includes(\"networkerror\")\n ) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Detect unauthorized errors structurally.\n * Checks status code and numeric code rather than string matching on name.\n */\nexport function isUnauthorizedError(err: Error): boolean {\n const props = errorProps(err);\n\n // Check HTTP status code (most reliable)\n if (props.statusCode === 401 || props.status === 401) return true;\n\n // StreamableHTTPError stores status as `.code`\n if (props.code === 401) return true;\n\n // MCP SDK UnauthorizedError extends Error without setting `.name`,\n // so `.name` is \"Error\". Use constructor name as fallback.\n if (err.name === \"UnauthorizedError\") return true;\n if (err.constructor?.name === \"UnauthorizedError\") return true;\n if (err.message === \"Unauthorized\") return true;\n\n return false;\n}\n\n// ============================================================================\n// Elicitation types\n// ============================================================================\n\nexport interface ElicitationEntry {\n readonly url: string;\n readonly message: string;\n readonly elicitationId: string;\n readonly integrationId?: string;\n readonly integrationName?: string;\n}\n\n// ============================================================================\n// HTTP error parsing\n// ============================================================================\n\n/**\n * Parse an HTTP response into an appropriate error.\n */\nexport function parseHttpError(\n statusCode: number,\n body?: unknown,\n): KontextError {\n const message =\n typeof body === \"object\" && body !== null && \"message\" in body\n ? String((body as { message: unknown }).message)\n : `HTTP ${statusCode}`;\n\n const errorCode =\n typeof body === \"object\" && body !== null && \"code\" in body\n ? String((body as { code: unknown }).code)\n : undefined;\n\n switch (statusCode) {\n case 400:\n if (\n typeof body === \"object\" &&\n body !== null &&\n \"errors\" in body &&\n Array.isArray((body as { errors: unknown }).errors)\n ) {\n return new HttpError(message, \"kontext_validation_error\", {\n statusCode: 400,\n validationErrors: (\n body as { errors: Array<{ field: string; message: string }> }\n ).errors,\n });\n }\n return new KontextError(message, errorCode ?? \"kontext_bad_request\", {\n statusCode: 400,\n });\n\n case 401:\n return new AuthorizationRequiredError(message);\n\n case 403:\n if (errorCode === \"INTEGRATION_CONNECTION_REQUIRED\") {\n const details = body as {\n integrationId?: string;\n integrationName?: string;\n connectUrl?: string;\n };\n return new IntegrationConnectionRequiredError(\n details.integrationId ?? \"unknown\",\n {\n integrationName: details.integrationName,\n connectUrl: details.connectUrl,\n message,\n },\n );\n }\n return new HttpError(message, \"kontext_policy_denied\", {\n statusCode: 403,\n meta: { policy: (body as Record<string, unknown>)?.policy },\n });\n\n case 404:\n return new HttpError(message, \"kontext_not_found\", { statusCode: 404 });\n\n case 429: {\n const retryAfter =\n typeof body === \"object\" && body !== null && \"retryAfter\" in body\n ? Number((body as { retryAfter: unknown }).retryAfter)\n : undefined;\n return new HttpError(\n retryAfter\n ? `Rate limit exceeded. Retry after ${retryAfter} seconds.`\n : \"Rate limit exceeded. Wait and retry.\",\n \"kontext_rate_limited\",\n { statusCode: 429, retryAfter },\n );\n }\n\n default:\n if (statusCode >= 500) {\n return new HttpError(\n `Server error (HTTP ${statusCode}): ${message}`,\n \"kontext_server_error\",\n { statusCode },\n );\n }\n return new KontextError(message, errorCode ?? \"kontext_unknown_error\", {\n statusCode,\n });\n }\n}\n\n// ============================================================================\n// MCP error translation\n// ============================================================================\n\nconst MCP_CODE_MAP: Record<number, { code: string; statusCode?: number }> = {\n [ErrorCode.ParseError]: { code: \"kontext_mcp_parse_error\" },\n [ErrorCode.InvalidRequest]: { code: \"kontext_mcp_invalid_request\" },\n [ErrorCode.MethodNotFound]: { code: \"kontext_mcp_method_not_found\" },\n [ErrorCode.InvalidParams]: { code: \"kontext_mcp_invalid_params\" },\n [ErrorCode.InternalError]: {\n code: \"kontext_mcp_internal_error\",\n statusCode: 500,\n },\n [ErrorCode.RequestTimeout]: {\n code: \"kontext_mcp_session_expired\",\n statusCode: 401,\n },\n [ErrorCode.ConnectionClosed]: { code: \"kontext_mcp_session_error\" },\n};\n\n/**\n * Translate external errors into KontextError instances.\n * Uses structural checks (numeric codes, status codes, system error codes)\n * rather than fragile string matching.\n */\nexport function translateError(err: unknown): KontextError {\n if (isKontextError(err)) return err as KontextError;\n\n if (!(err instanceof Error)) {\n return new KontextError(String(err), \"kontext_unknown_error\");\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const props: Record<string, any> = err;\n\n // 1. MCP protocol -32042 (URL elicitation required) — check numeric code\n if (props.code === ErrorCode.UrlElicitationRequired) {\n const elicitations = (props.elicitations ?? props.data?.elicitations) as\n | ElicitationEntry[]\n | undefined;\n const elicitation = elicitations?.[0];\n return new IntegrationConnectionRequiredError(\n elicitation?.integrationId ?? \"unknown\",\n {\n integrationName: elicitation?.integrationName,\n connectUrl: elicitation?.url,\n message: elicitation?.message,\n cause: err,\n },\n );\n }\n\n // 2. MCP JSON-RPC errors (standard and server-defined ranges)\n if (typeof props.code === \"number\" && props.code < 0) {\n const entry = MCP_CODE_MAP[props.code];\n if (entry) {\n return new KontextError(err.message, entry.code, {\n statusCode: entry.statusCode,\n cause: err,\n });\n }\n return new KontextError(err.message, \"kontext_mcp_error\", {\n cause: err,\n meta: { mcpCode: props.code },\n });\n }\n\n // 3. HTTP status on the error (from MCP SDK or fetch wrappers)\n // StreamableHTTPError stores HTTP status as `.code` (not `.statusCode`/`.status`),\n // so check `.code` as well when it's in the HTTP error range (400–599).\n const statusCode = (props.statusCode ??\n props.status ??\n (typeof props.code === \"number\" && props.code >= 400 && props.code < 600\n ? props.code\n : undefined)) as number | undefined;\n if (typeof statusCode === \"number\" && statusCode >= 400) {\n if (statusCode === 401) {\n return new AuthorizationRequiredError(err.message, { cause: err });\n }\n return new KontextError(err.message, \"kontext_server_error\", {\n statusCode,\n cause: err,\n });\n }\n\n // 4. Auth errors — structural check\n if (isUnauthorizedError(err)) {\n return new AuthorizationRequiredError(err.message, { cause: err });\n }\n\n // 5. Network errors — structural check (system error codes, not string matching)\n if (isNetworkError(err)) {\n return new NetworkError(err.message, { cause: err });\n }\n\n // 6. Fallback\n return new KontextError(err.message, \"kontext_unknown_error\", {\n cause: err,\n });\n}\n","/**\n * Shared utilities for Kontext tool handling.\n * Used by both KontextClient (core) and withKontext (Cloudflare adapter).\n */\n\nimport {\n ConfigError,\n IntegrationConnectionRequiredError,\n type ElicitationEntry,\n} from \"../errors.js\";\n\n// Re-export for consumers that import this type from tool-utils.\nexport type { ElicitationEntry } from \"../errors.js\";\n\n// ============================================================================\n// Shared types\n// ============================================================================\n\nexport interface IntegrationStatus {\n readonly id: string;\n readonly name: string;\n readonly connected: boolean;\n readonly connectUrl?: string;\n}\n\n// ============================================================================\n// System prompt\n// ============================================================================\n\n/**\n * Build an LLM system prompt based on available tool names and integration status.\n * Detects `*_SEARCH_TOOLS` / `*_EXECUTE_TOOL` suffixed names from the\n * Cloudflare Agents MCP proxy and generates appropriate guidance.\n */\nexport function buildKontextSystemPrompt(\n toolNames: readonly string[],\n integrations: readonly IntegrationStatus[],\n): string {\n if (toolNames.length === 0) return \"\";\n\n const searchTool = toolNames.find(\n (n) => n.endsWith(\"_SEARCH_TOOLS\") || n === \"SEARCH_TOOLS\",\n );\n const executeTool = toolNames.find(\n (n) => n.endsWith(\"_EXECUTE_TOOL\") || n === \"EXECUTE_TOOL\",\n );\n const requestCapabilityTool = toolNames.find(\n (n) => n.endsWith(\"_REQUEST_CAPABILITY\") || n === \"REQUEST_CAPABILITY\",\n );\n\n let prompt =\n \"You are a helpful assistant with access to various tools and integrations through Kontext.\\n\" +\n \"You can help users with tasks across their connected services (GitHub, Linear, Slack, etc.).\\n\" +\n \"When a user asks about their services, use the available MCP tools to help them.\\n\" +\n \"The user has already authenticated — tools run as the user with their permissions, including access to private repositories and org resources.\\n\" +\n \"If a Kontext tool can satisfy an integration-related request, use the Kontext tool and do not use shell or CLI commands for that request.\\n\" +\n \"Use shell or CLI only when no relevant Kontext tool exists or when the user explicitly requests local shell actions.\";\n\n if (searchTool && executeTool) {\n prompt +=\n `\\n\\nYou have access to external integrations through Kontext MCP tools:\\n` +\n `- Call \\`${searchTool}\\` first to discover what tools/integrations are available.\\n` +\n `- Then call \\`${executeTool}\\` with the tool_id and arguments to run a specific tool.\\n` +\n `- When the user asks about \"my repos\", \"my issues\", etc., use the appropriate tool to look up their data directly. Do not ask for their username — the tools already know who they are.\\n` +\n `Always start by searching for tools when the user asks about their connected services.`;\n }\n\n // Append integration status section when there are integrations to report\n if (integrations.length > 0) {\n const connected = integrations.filter((i) => i.connected);\n const disconnected = integrations.filter((i) => !i.connected);\n\n if (disconnected.length > 0) {\n prompt += \"\\n\\n## Integration Status\";\n\n if (connected.length > 0) {\n prompt += `\\n\\nYou already have access to: ${connected.map((i) => i.name).join(\", \")}`;\n }\n\n prompt += `\\n\\nThe following services require user authorization: ${disconnected.map((i) => i.name).join(\", \")}`;\n\n if (requestCapabilityTool) {\n prompt +=\n `\\n\\n**IMPORTANT:** When the user requests an action that requires one of these services:` +\n `\\n1. Call the \\`${requestCapabilityTool}\\` tool` +\n `\\n2. The tool returns a fresh integrations management link` +\n `\\n3. Include the link in your response so the user can connect or manage integrations` +\n `\\n4. Always call the tool for a fresh link and never reuse a previously returned URL`;\n } else {\n prompt +=\n \"\\n\\nWhen the user asks about a disconnected integration, let them know it needs to be connected first. Do not attempt to use tools from disconnected integrations without informing the user.\";\n }\n } else if (requestCapabilityTool && connected.length > 0) {\n prompt +=\n \"\\n\\n## Integration Management\" +\n `\\n\\nIf the user asks to manage, reconnect, or verify integrations, call \\`${requestCapabilityTool}\\` to get a fresh integrations management link.`;\n }\n }\n\n return prompt;\n}\n\n// ============================================================================\n// Auth-aware toolset orchestrator\n// ============================================================================\n\nexport interface AuthAwareToolset {\n /** System prompt with integration status and REQUEST_CAPABILITY instructions. */\n readonly systemPrompt: string;\n /** REQUEST_CAPABILITY tool to inject, or null if all integrations are connected. */\n readonly requestCapabilityTool: {\n readonly name: string;\n readonly description: string;\n readonly parameters: Record<string, unknown>;\n readonly execute: () => Promise<string>;\n } | null;\n}\n\n/**\n * Enrich a tool set with auth awareness.\n *\n * Given tool names and integration status, produces:\n * - A system prompt that tells the LLM about connected/disconnected integrations\n * - A REQUEST_CAPABILITY tool (if needed) that returns a fresh management URL as text\n *\n * Both `ai/` and `cloudflare/` adapters use this as their core auth layer.\n */\nexport function enrichToolsWithAuthAwareness(\n toolNames: readonly string[],\n integrations: readonly IntegrationStatus[],\n options?: {\n prefix?: string;\n requireServerRequestCapability?: boolean;\n requestCapabilityProxy?: () => Promise<string>;\n },\n): AuthAwareToolset {\n const serverRequestCapability = toolNames.find(\n (name) =>\n name.endsWith(\"_REQUEST_CAPABILITY\") || name === \"REQUEST_CAPABILITY\",\n );\n\n if (options?.requireServerRequestCapability && !serverRequestCapability) {\n throw new ConfigError(\n \"REQUEST_CAPABILITY tool is required but not exposed by the MCP server. Deploy API and SDK in lockstep.\",\n \"kontext_config_missing_request_capability_tool\",\n );\n }\n\n let requestCapabilityTool: AuthAwareToolset[\"requestCapabilityTool\"] = null;\n let effectiveToolNames = [...toolNames];\n\n if (options?.requestCapabilityProxy) {\n const prefix =\n options.prefix ??\n toolNames\n .find((n) => n.endsWith(\"_SEARCH_TOOLS\"))\n ?.replace(/_SEARCH_TOOLS$/, \"\") ??\n \"kontext\";\n const toolName = `${prefix}_REQUEST_CAPABILITY`;\n const tool = buildRequestCapabilityTool(\n integrations,\n options.requestCapabilityProxy!,\n );\n requestCapabilityTool = { name: toolName, ...tool };\n effectiveToolNames = [...effectiveToolNames, toolName];\n }\n\n return {\n systemPrompt: buildKontextSystemPrompt(effectiveToolNames, integrations),\n requestCapabilityTool,\n };\n}\n\n// ============================================================================\n// Integration status parsing\n// ============================================================================\n\n/**\n * Parse integration status from gateway tool search results.\n * Extracts connected integrations from tools and disconnected from errors.\n */\nexport function parseIntegrationStatus(\n tools: ReadonlyArray<{ server?: { id?: string; name?: string } }>,\n errors: ReadonlyArray<{ serverId: string; serverName?: string }>,\n elicitations?: ReadonlyArray<{\n url: string;\n integrationId?: string;\n integrationName?: string;\n }>,\n): IntegrationStatus[] {\n const seen = new Set<string>();\n const result: IntegrationStatus[] = [];\n\n for (const t of tools) {\n const sid = t.server?.id;\n if (sid && !seen.has(sid)) {\n seen.add(sid);\n result.push({\n id: sid,\n name: t.server?.name ?? sid,\n connected: true,\n });\n }\n }\n\n for (const e of errors) {\n if (!seen.has(e.serverId)) {\n seen.add(e.serverId);\n const elicitation = elicitations?.find(\n (el) => el.integrationId === e.serverId,\n );\n result.push({\n id: e.serverId,\n name: e.serverName ?? e.serverId,\n connected: false,\n connectUrl: elicitation?.url,\n });\n }\n }\n\n return result;\n}\n\n// ============================================================================\n// Request capability tool\n// ============================================================================\n\n/**\n * Build a tool that lets the LLM request a fresh integrations management URL.\n * Returns the URL as text so the LLM can present it to the user.\n * No popups — the user clicks the link in the chat.\n */\nexport function buildRequestCapabilityTool(\n capabilities: readonly IntegrationStatus[],\n executeRequestCapability: () => Promise<string>,\n): {\n description: string;\n parameters: Record<string, unknown>;\n execute: () => Promise<string>;\n} {\n const connected = capabilities.filter((c) => c.connected);\n const disconnected = capabilities.filter((c) => !c.connected);\n const connectedList =\n connected.length > 0 ? connected.map((c) => c.name).join(\", \") : \"none\";\n const disconnectedList =\n disconnected.length > 0\n ? disconnected.map((c) => c.name).join(\", \")\n : \"none\";\n\n return {\n description:\n \"Request a fresh integrations management link from the Kontext server. \" +\n `Connected integrations: ${connectedList}. ` +\n `Disconnected integrations: ${disconnectedList}. ` +\n \"Always call this tool for a fresh link and never reuse an older link.\",\n parameters: {\n type: \"object\",\n properties: {},\n },\n execute: async (): Promise<string> => {\n return executeRequestCapability();\n },\n };\n}\n\n/**\n * Pre-flight integration status detection.\n * Calls SEARCH_TOOLS on raw (unwrapped) tools to detect connected/disconnected\n * integrations without triggering any popups or broadcasts.\n */\nexport async function preflightIntegrationStatus(\n tools: ToolSetLike,\n): Promise<IntegrationStatus[]> {\n const searchToolKey = Object.keys(tools).find((k) =>\n k.endsWith(\"_SEARCH_TOOLS\"),\n );\n if (!searchToolKey || !tools[searchToolKey]?.execute) return [];\n\n try {\n const result = await tools[searchToolKey].execute!({ limit: 100 });\n\n // Cloudflare getAITools() returns MCP CallToolResult objects, not strings.\n // Extract JSON text from either a plain string or a CallToolResult shape.\n let json: string | undefined;\n if (typeof result === \"string\") {\n json = result;\n } else if (result && typeof result === \"object\") {\n const content = (\n result as {\n content?: Array<{\n type: string;\n resource?: { text?: string };\n text?: string;\n }>;\n }\n ).content;\n const first = content?.[0];\n json = first?.resource?.text ?? first?.text;\n }\n\n if (typeof json === \"string\") {\n const parsed = JSON.parse(json);\n if (parsed && typeof parsed === \"object\") {\n return parseIntegrationStatus(\n Array.isArray(parsed.items) ? parsed.items : [],\n Array.isArray(parsed.errors) ? parsed.errors : [],\n Array.isArray(parsed.elicitations) ? parsed.elicitations : [],\n );\n }\n }\n } catch (err: unknown) {\n // All integrations disconnected: -32042 error contains elicitations\n const elicitations = extractElicitations(err);\n if (elicitations?.length) {\n return elicitations\n .filter(\n (e): e is ElicitationEntry & { integrationId: string } =>\n !!e.integrationId,\n )\n .map((e) => ({\n id: e.integrationId,\n name: e.integrationName ?? e.integrationId,\n connected: false,\n connectUrl: e.url,\n }));\n }\n }\n\n return [];\n}\n\n// ============================================================================\n// Tool wrapping\n// ============================================================================\n\n/** A tool with an optional async execute method */\nexport interface ToolLike {\n execute?: (...args: unknown[]) => Promise<unknown>;\n [key: string]: unknown;\n}\n\n/** Record of named tools */\nexport type ToolSetLike = Record<string, ToolLike>;\n\n/**\n * Extract elicitation entries from an error thrown by the MCP SDK.\n * Handles two shapes: direct code/data and nested cause.\n */\nexport function extractElicitations(\n err: unknown,\n): ElicitationEntry[] | undefined {\n const error = err as {\n code?: number;\n data?: { elicitations?: ElicitationEntry[] };\n cause?: {\n code?: number;\n data?: { elicitations?: ElicitationEntry[] };\n };\n };\n\n // Primary: structured McpError with code -32042\n if (error?.code === -32042 && error?.data?.elicitations?.length) {\n return error.data.elicitations;\n }\n\n // Nested cause (some SDK wrappers nest the original error)\n if (\n error?.cause?.code === -32042 &&\n error?.cause?.data?.elicitations?.length\n ) {\n return error.cause.data.elicitations;\n }\n\n return undefined;\n}\n\n/**\n * Wrap tools to catch MCP error code -32042 (URL elicitation required)\n * and convert to IntegrationConnectionRequiredError.\n */\nexport function wrapToolsWithElicitation<T extends ToolSetLike>(tools: T): T {\n return Object.fromEntries(\n Object.entries(tools).map(([name, tool]) => [\n name,\n {\n ...tool,\n execute: tool.execute\n ? async (...args: unknown[]) => {\n try {\n return await tool.execute!(...args);\n } catch (err: unknown) {\n const elicitations = extractElicitations(err);\n const elicitation = elicitations?.[0];\n if (elicitation?.url) {\n const integrationId = elicitation.integrationId ?? \"unknown\";\n throw new IntegrationConnectionRequiredError(integrationId, {\n integrationName: elicitation.integrationName,\n connectUrl: elicitation.url,\n message: elicitation.message,\n });\n }\n throw err;\n }\n }\n : undefined,\n },\n ]),\n ) as T;\n}\n","/**\n * Vercel AI SDK adapter for Kontext.\n *\n * Pure format converter: maps KontextClient tools to AI SDK CoreTools.\n *\n * @example\n * ```typescript\n * import { createKontextClient } from '@kontext-dev/js-sdk';\n * import { toKontextTools } from '@kontext-dev/js-sdk/ai';\n * import { generateText } from 'ai';\n *\n * const client = createKontextClient({\n * clientId: process.env.KONTEXT_CLIENT_ID!,\n * redirectUri: 'http://localhost:3000/callback',\n * onAuthRequired: (url) => open(url.toString()),\n * });\n *\n * const { tools, systemPrompt } = await toKontextTools(client);\n * const result = await generateText({ model, system: systemPrompt, tools, prompt: '...', maxSteps: 5 });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { jsonSchema, type CoreTool } from \"ai\";\nimport type {\n KontextClient,\n KontextTool,\n IntegrationInfo,\n ToolResult,\n} from \"../../client/index.js\";\nimport type { KontextOrchestrator } from \"../../client/orchestrator/index.js\";\nimport { enrichToolsWithAuthAwareness } from \"../../client/tool-utils.js\";\nimport { ConfigError } from \"../../errors.js\";\n\nexport interface ToKontextToolsOptions {\n formatResult?: (result: ToolResult) => unknown;\n}\n\nexport interface KontextToolsResult {\n readonly tools: Record<string, CoreTool>;\n readonly systemPrompt: string;\n readonly integrations: readonly IntegrationInfo[];\n}\n\n/**\n * Convert a KontextClient's tools into an AI SDK ToolSet with system prompt.\n *\n * Calls `client.tools.list()` and `client.integrations.list()` to discover\n * tools and integration status, then wraps each tool as an AI SDK `CoreTool`\n * whose `execute` delegates to `client.tools.execute()`.\n */\nexport async function toKontextTools(\n client: KontextClient | KontextOrchestrator,\n options?: ToKontextToolsOptions,\n): Promise<KontextToolsResult> {\n const [tools, integrations, mcpTools] = await Promise.all([\n client.tools.list(),\n client.integrations.list(),\n client.mcp.listTools(),\n ]);\n\n const coreTools: Record<string, CoreTool> = {};\n const usedNames = new Set<string>();\n\n for (const tool of tools) {\n const name = buildToolName(tool, usedNames);\n coreTools[name] = {\n description: formatDescription(tool),\n parameters: tool.inputSchema\n ? jsonSchema(tool.inputSchema as Parameters<typeof jsonSchema>[0])\n : jsonSchema({ type: \"object\" as const, properties: {} }),\n execute: async (args: Record<string, unknown>) => {\n const r = await client.tools.execute(tool.id, args);\n return (options?.formatResult ?? defaultFormat)(r);\n },\n };\n }\n\n if (!mcpTools.some((tool) => tool.name === \"REQUEST_CAPABILITY\")) {\n throw new ConfigError(\n \"REQUEST_CAPABILITY tool is missing on the MCP server. Deploy API and SDK in lockstep.\",\n \"kontext_config_missing_request_capability_tool\",\n );\n }\n\n const { systemPrompt, requestCapabilityTool } = enrichToolsWithAuthAwareness(\n [...tools.map((t) => t.name), \"kontext_REQUEST_CAPABILITY\"],\n integrations,\n {\n prefix: \"kontext\",\n requireServerRequestCapability: true,\n requestCapabilityProxy: async () => {\n const result = await client.mcp.callTool(\"REQUEST_CAPABILITY\", {});\n return extractText(result);\n },\n },\n );\n\n if (requestCapabilityTool) {\n coreTools[requestCapabilityTool.name] = {\n description: requestCapabilityTool.description,\n parameters: jsonSchema(\n requestCapabilityTool.parameters as Parameters<typeof jsonSchema>[0],\n ),\n execute: async () => {\n return requestCapabilityTool.execute();\n },\n };\n }\n\n return { tools: coreTools, systemPrompt, integrations };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction defaultFormat(r: ToolResult): unknown {\n return r.content;\n}\n\nfunction formatDescription(tool: KontextTool): string {\n const base = tool.description ?? tool.name;\n const serverLabel = tool.server?.name ?? tool.server?.id;\n return serverLabel ? `[${serverLabel}] ${base}` : base;\n}\n\nfunction buildToolName(tool: KontextTool, usedNames: Set<string>): string {\n const rawName = tool.name || tool.id || \"tool\";\n const base = tool.server\n ? `${tool.server.name ?? tool.server.id ?? \"server\"}_${rawName}`\n : rawName;\n const sanitized = sanitizeToolName(base);\n const fallback = sanitized || `tool_${usedNames.size + 1}`;\n\n let name = fallback;\n let suffix = 1;\n while (usedNames.has(name)) {\n name = `${fallback}_${suffix}`;\n suffix += 1;\n }\n\n usedNames.add(name);\n return name;\n}\n\nfunction sanitizeToolName(name: string): string {\n return name\n .replace(/[^a-zA-Z0-9_-]/g, \"_\")\n .replace(/_+/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .slice(0, 64);\n}\n\nfunction extractText(result: unknown): string {\n if (typeof result === \"string\") return result;\n if (!result || typeof result !== \"object\") {\n return String(result ?? \"\");\n }\n\n const content = (\n result as {\n content?: Array<{\n type?: string;\n text?: string;\n resource?: { text?: string };\n }>;\n }\n ).content;\n if (Array.isArray(content)) {\n const textItem = content.find(\n (item) => item.type === \"text\" && typeof item.text === \"string\",\n );\n if (textItem?.text) return textItem.text;\n\n const resourceItem = content.find(\n (item) =>\n item.type === \"resource\" && typeof item.resource?.text === \"string\",\n );\n if (resourceItem?.resource?.text) return resourceItem.resource.text;\n }\n\n return JSON.stringify(result);\n}\n"]}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var types_js = require('@modelcontextprotocol/sdk/types.js');
|
|
4
|
+
|
|
3
5
|
// src/errors.ts
|
|
4
6
|
var KontextError = class extends Error {
|
|
5
7
|
/** Brand field for type narrowing without instanceof */
|
|
@@ -77,6 +79,17 @@ var ConfigError = class extends KontextError {
|
|
|
77
79
|
this.name = "ConfigError";
|
|
78
80
|
}
|
|
79
81
|
};
|
|
82
|
+
({
|
|
83
|
+
[types_js.ErrorCode.ParseError]: { },
|
|
84
|
+
[types_js.ErrorCode.InvalidRequest]: { },
|
|
85
|
+
[types_js.ErrorCode.MethodNotFound]: { },
|
|
86
|
+
[types_js.ErrorCode.InvalidParams]: { },
|
|
87
|
+
[types_js.ErrorCode.InternalError]: {
|
|
88
|
+
},
|
|
89
|
+
[types_js.ErrorCode.RequestTimeout]: {
|
|
90
|
+
},
|
|
91
|
+
[types_js.ErrorCode.ConnectionClosed]: { }
|
|
92
|
+
});
|
|
80
93
|
|
|
81
94
|
// src/client/tool-utils.ts
|
|
82
95
|
function buildKontextSystemPrompt(toolNames, integrations) {
|