| @@ -0,0 +1,104 @@ |
| | 1 | import { describe, expect, it } from "@effect/vitest"; |
| | 2 | import { projectCatalogForAgentSdk } from "@executor/ir/catalog"; |
| | 3 | import type { Source } from "@executor/source-core"; |
| | 4 | |
| | 5 | import { |
| | 6 | createMcpCatalogSnapshot, |
| | 7 | type McpCatalogOperationInput, |
| | 8 | } from "./catalog"; |
| | 9 | |
| | 10 | const createSource = (): Source => ({ |
| | 11 | id: "axiom-mcp", |
| | 12 | scopeId: "scope_local", |
| | 13 | name: "Axiom MCP", |
| | 14 | kind: "mcp", |
| | 15 | status: "connected", |
| | 16 | enabled: true, |
| | 17 | namespace: "axiom.mcp", |
| | 18 | createdAt: 0, |
| | 19 | updatedAt: 0, |
| | 20 | }); |
| | 21 | |
| | 22 | const createDashboardInputSchema = { |
| | 23 | type: "object", |
| | 24 | properties: { |
| | 25 | dashboardJson: { |
| | 26 | type: "string", |
| | 27 | }, |
| | 28 | }, |
| | 29 | required: ["dashboardJson"], |
| | 30 | additionalProperties: false, |
| | 31 | } as const; |
| | 32 | |
| | 33 | const createOperation = ( |
| | 34 | input: { outputSchema?: unknown } = {}, |
| | 35 | ): McpCatalogOperationInput => ({ |
| | 36 | toolId: "createdashboard", |
| | 37 | title: "createDashboard", |
| | 38 | description: "Create a new dashboard from a JSON document.", |
| | 39 | effect: "write", |
| | 40 | inputSchema: createDashboardInputSchema, |
| | 41 | ...(input.outputSchema !== undefined |
| | 42 | ? { outputSchema: input.outputSchema } |
| | 43 | : {}), |
| | 44 | providerData: { |
| | 45 | toolId: "createdashboard", |
| | 46 | toolName: "createDashboard", |
| | 47 | displayTitle: "createDashboard", |
| | 48 | title: null, |
| | 49 | description: "Create a new dashboard from a JSON document.", |
| | 50 | annotations: null, |
| | 51 | execution: { |
| | 52 | taskSupport: "forbidden", |
| | 53 | }, |
| | 54 | icons: null, |
| | 55 | meta: null, |
| | 56 | rawTool: { |
| | 57 | name: "createDashboard", |
| | 58 | description: "Create a new dashboard from a JSON document.", |
| | 59 | inputSchema: createDashboardInputSchema, |
| | 60 | ...(input.outputSchema !== undefined |
| | 61 | ? { outputSchema: input.outputSchema } |
| | 62 | : {}), |
| | 63 | }, |
| | 64 | server: null, |
| | 65 | }, |
| | 66 | }); |
| | 67 | |
| | 68 | describe("MCP catalog", () => { |
| | 69 | it("projects missing MCP output schemas as unknown result data", () => { |
| | 70 | const snapshot = createMcpCatalogSnapshot({ |
| | 71 | source: createSource(), |
| | 72 | documents: [{ |
| | 73 | documentKind: "mcp_manifest", |
| | 74 | documentKey: "https://mcp.axiom.co/mcp", |
| | 75 | contentText: JSON.stringify({ version: 2, tools: [] }), |
| | 76 | }], |
| | 77 | operations: [createOperation()], |
| | 78 | }); |
| | 79 | const projected = projectCatalogForAgentSdk({ |
| | 80 | catalog: snapshot.catalog, |
| | 81 | }); |
| | 82 | const descriptor = Object.values(projected.toolDescriptors).find((entry) => |
| | 83 | entry.toolPath.join(".") === "axiom.mcp.createdashboard"); |
| | 84 | |
| | 85 | if (!descriptor?.resultShapeId) { |
| | 86 | throw new Error("Expected createdashboard to have a projected result shape"); |
| | 87 | } |
| | 88 | |
| | 89 | const resultShape = projected.catalog.symbols[descriptor.resultShapeId]; |
| | 90 | if (resultShape?.kind !== "shape" || resultShape.node.type !== "object") { |
| | 91 | throw new Error("Expected MCP result envelope"); |
| | 92 | } |
| | 93 | |
| | 94 | const dataShape = projected.catalog.symbols[resultShape.node.fields.data.shapeId]; |
| | 95 | if (dataShape?.kind !== "shape" || dataShape.node.type !== "anyOf") { |
| | 96 | throw new Error("Expected nullable MCP result data"); |
| | 97 | } |
| | 98 | |
| | 99 | const dataVariants = dataShape.node.items.map((shapeId) => projected.catalog.symbols[shapeId]); |
| | 100 | expect( |
| | 101 | dataVariants.some((shape) => shape?.kind === "shape" && shape.node.type === "unknown"), |
| | 102 | ).toBe(true); |
| | 103 | }); |
| | 104 | }); |