diff --git a/packages/core/src/adapters/mcp.ts b/packages/core/src/adapters/mcp.ts index 8c5130b8..13396196 100644 --- a/packages/core/src/adapters/mcp.ts +++ b/packages/core/src/adapters/mcp.ts @@ -5,10 +5,10 @@ * Uses camelCase naming and _meta.ui.* namespace. */ -import type { ToolDef, ToolAnnotations } from "../types/tools"; +import type { ToolDef } from "../types/tools"; import type { UIDef } from "../types/ui"; import type { ProtocolAdapter, ToolMetaResult, UIResourceMetaResult } from "./types"; -import { mapVisibilityToMcp } from "../utils/metadata"; +import { mapVisibilityToMcp, buildAnnotations } from "../utils/metadata"; import { generateMcpCSPMetadata } from "../utils/csp"; // ============================================================================= @@ -37,7 +37,7 @@ export class McpAdapter implements ProtocolAdapter { }; // Build annotations if specified - const annotations = this.buildAnnotations(toolDef.annotations); + const annotations = buildAnnotations(toolDef.annotations); // Compatibility notes: // - ext-apps / MCP Apps reference shape commonly uses nested `_meta.ui.resourceUri`. @@ -55,32 +55,6 @@ export class McpAdapter implements ProtocolAdapter { }; } - /** - * Build MCP annotations from tool annotations - */ - private buildAnnotations(annotations?: ToolAnnotations): Record | undefined { - if (!annotations) { - return undefined; - } - - const result: Record = {}; - - if (annotations.readOnlyHint !== undefined) { - result.readOnlyHint = annotations.readOnlyHint; - } - if (annotations.destructiveHint !== undefined) { - result.destructiveHint = annotations.destructiveHint; - } - if (annotations.openWorldHint !== undefined) { - result.openWorldHint = annotations.openWorldHint; - } - if (annotations.idempotentHint !== undefined) { - result.idempotentHint = annotations.idempotentHint; - } - - return Object.keys(result).length > 0 ? result : undefined; - } - /** * Build UI resource metadata for MCP protocol */ diff --git a/packages/core/src/adapters/openai.ts b/packages/core/src/adapters/openai.ts index bddf7e68..f6b88e2b 100644 --- a/packages/core/src/adapters/openai.ts +++ b/packages/core/src/adapters/openai.ts @@ -5,10 +5,10 @@ * Uses snake_case naming and openai/* prefixed keys. */ -import type { ToolDef, ToolAnnotations } from "../types/tools"; +import type { ToolDef } from "../types/tools"; import type { UIDef } from "../types/ui"; import type { ProtocolAdapter, ToolMetaResult, UIResourceMetaResult } from "./types"; -import { mapVisibilityToOpenAI } from "../utils/metadata"; +import { mapVisibilityToOpenAI, buildAnnotations } from "../utils/metadata"; import { generateOpenAICSPMetadata } from "../utils/csp"; // ============================================================================= @@ -60,7 +60,7 @@ export class OpenAIAdapter implements ProtocolAdapter { } // Build annotations if specified - const annotations = this.buildAnnotations(toolDef.annotations); + const annotations = buildAnnotations(toolDef.annotations); return { annotations, @@ -68,34 +68,6 @@ export class OpenAIAdapter implements ProtocolAdapter { }; } - /** - * Build OpenAI annotations from tool annotations - * - * OpenAI uses the same annotation names as MCP spec - */ - private buildAnnotations(annotations?: ToolAnnotations): Record | undefined { - if (!annotations) { - return undefined; - } - - const result: Record = {}; - - if (annotations.readOnlyHint !== undefined) { - result.readOnlyHint = annotations.readOnlyHint; - } - if (annotations.destructiveHint !== undefined) { - result.destructiveHint = annotations.destructiveHint; - } - if (annotations.openWorldHint !== undefined) { - result.openWorldHint = annotations.openWorldHint; - } - if (annotations.idempotentHint !== undefined) { - result.idempotentHint = annotations.idempotentHint; - } - - return Object.keys(result).length > 0 ? result : undefined; - } - /** * Build UI resource metadata for OpenAI protocol */ diff --git a/packages/core/src/events/types.ts b/packages/core/src/events/types.ts index 1b38be54..614dd029 100644 --- a/packages/core/src/events/types.ts +++ b/packages/core/src/events/types.ts @@ -4,7 +4,7 @@ * @module events/types */ -import type { ToolContext, ToolDefs as _ToolDefs } from "../types/tools"; +import type { ToolContext } from "../types/tools"; import type { AppConfig } from "../types/config"; // ============================================================================= diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 617840f0..4a5d9eda 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -151,6 +151,7 @@ export type { ErrorCodeType } from "./utils/errors"; export { mapVisibilityToMcp, mapVisibilityToOpenAI, + buildAnnotations, generateToolMetadata, generateAllToolsMetadata, } from "./utils/metadata"; diff --git a/packages/core/src/plugins/builtin/logging.ts b/packages/core/src/plugins/builtin/logging.ts index 448da0c4..11569e95 100644 --- a/packages/core/src/plugins/builtin/logging.ts +++ b/packages/core/src/plugins/builtin/logging.ts @@ -16,6 +16,7 @@ import { z } from "zod"; import { createPlugin } from "../types"; import type { ToolCallContext, PluginInitContext, PluginStartContext } from "../types"; +import { safeStringify } from "../../debug/logger"; /** * Log level enumeration @@ -59,37 +60,6 @@ function timestamp(): string { return new Date().toISOString(); } -/** - * Safely stringify objects, handling circular references - */ -function safeStringify(obj: unknown): string { - try { - return JSON.stringify(obj, null, 2); - } catch { - try { - return JSON.stringify(obj, getCircularReplacer(), 2); - } catch { - return String(obj); - } - } -} - -/** - * JSON replacer function that handles circular references - */ -function getCircularReplacer() { - const seen = new WeakSet(); - return (_key: string, value: unknown): unknown => { - if (typeof value === "object" && value !== null) { - if (seen.has(value)) { - return "[Circular]"; - } - seen.add(value); - } - return value; - }; -} - /** * Built-in logging plugin * diff --git a/packages/core/src/utils/csp.ts b/packages/core/src/utils/csp.ts index 218f796f..fccfe7c1 100644 --- a/packages/core/src/utils/csp.ts +++ b/packages/core/src/utils/csp.ts @@ -8,7 +8,7 @@ * @module utils/csp */ -import type { CSPConfig } from "../types/ui"; +import type { CSPConfig, UIDef } from "../types/ui"; // ============================================================================= // MCP CSP METADATA @@ -105,8 +105,6 @@ export function generateOpenAICSPMetadata(csp: CSPConfig): OpenAICSPMetadata { // UI RESOURCE METADATA GENERATION // ============================================================================= -import type { UIDef } from "../types/ui"; - /** * MCP UI resource metadata format * diff --git a/packages/core/src/utils/metadata.ts b/packages/core/src/utils/metadata.ts index 7a6c3087..f4c36a94 100644 --- a/packages/core/src/utils/metadata.ts +++ b/packages/core/src/utils/metadata.ts @@ -8,7 +8,7 @@ * @module utils/metadata */ -import type { ToolDef, Visibility } from "../types/tools"; +import type { ToolDef, Visibility, ToolAnnotations } from "../types/tools"; import { zodToJsonSchema } from "./schema"; // ============================================================================= @@ -102,6 +102,46 @@ export function mapVisibilityToOpenAI(visibility?: Visibility): OpenAIVisibility } } +// ============================================================================= +// TOOL ANNOTATIONS +// ============================================================================= + +/** + * Build protocol-agnostic annotations from tool annotations. + * + * Both MCP and OpenAI use the same annotation property names, + * so this function is shared between adapters. + * + * @param annotations - Tool annotations from tool definition + * @returns Record of annotation properties, or undefined if no annotations + * + * @internal + */ +export function buildAnnotations( + annotations?: ToolAnnotations +): Record | undefined { + if (!annotations) { + return undefined; + } + + const result: Record = {}; + + if (annotations.readOnlyHint !== undefined) { + result.readOnlyHint = annotations.readOnlyHint; + } + if (annotations.destructiveHint !== undefined) { + result.destructiveHint = annotations.destructiveHint; + } + if (annotations.openWorldHint !== undefined) { + result.openWorldHint = annotations.openWorldHint; + } + if (annotations.idempotentHint !== undefined) { + result.idempotentHint = annotations.idempotentHint; + } + + return Object.keys(result).length > 0 ? result : undefined; +} + // ============================================================================= // TOOL METADATA GENERATION // =============================================================================