From 4a6275da6cfc9e5a1cdab6243d9a0fd6c7aa7af6 Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Thu, 12 Jun 2025 03:29:26 +0700 Subject: [PATCH] feat: improve mcp edge things --- apps/e2e/src/mcp-e2e.test.ts | 48 +++++++++++++++- apps/e2e/src/mcp-fixtures.ts | 26 +++++++-- apps/gateway/package.json | 1 + apps/gateway/src/lib/durable-mcp-server.ts | 29 +++++----- .../src/lib/handle-mcp-tool-call-error.ts | 12 +++- apps/gateway/src/lib/utils.test.ts | 16 ++++++ apps/gateway/src/lib/utils.ts | 56 +++++++++++++++++++ .../get-tools-from-openapi-spec.test.ts.snap | 35 ++++++++++++ .../validate-openapi-spec.test.ts.snap | 43 ++++++++++++++ .../load-agentic-config.test.ts.snap | 21 ++++++- pnpm-lock.yaml | 27 ++------- 11 files changed, 263 insertions(+), 51 deletions(-) diff --git a/apps/e2e/src/mcp-e2e.test.ts b/apps/e2e/src/mcp-e2e.test.ts index dc3fdffc..447b9bd4 100644 --- a/apps/e2e/src/mcp-e2e.test.ts +++ b/apps/e2e/src/mcp-e2e.test.ts @@ -1,3 +1,4 @@ +import { pick } from '@agentic/platform-core' import { Client as McpClient } from '@modelcontextprotocol/sdk/client/index.js' import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js' import { afterAll, beforeAll, describe, expect, test } from 'vitest' @@ -36,10 +37,16 @@ for (const [i, fixtureSuite] of fixtureSuites.entries()) { content: expectedContent, structuredContent: expectedStructuredContent, _meta: expectedMeta, + _agenticMeta: expectedAgenticMeta, + _agenticMetaHeaders: expectedAgenticMetaHeaders, validate } = fixture.response ?? {} - const snapshot = - fixture.response?.snapshot ?? fixtureSuite.snapshot ?? !isError + const expectedSnapshot = + fixture.response?.snapshot ?? fixtureSuite.snapshot ?? false + const expectedStableSnapshot = + fixture.response?.stableSnapshot ?? + fixtureSuite.stableSnapshot ?? + !isError const debugFixture = !!( fixture.debug ?? fixtureSuite.debug ?? @@ -93,15 +100,50 @@ for (const [i, fixtureSuite] of fixtureSuites.entries()) { if (expectedMeta) { expect(result._meta).toBeDefined() + expect(typeof result._meta).toEqual('object') + expect(!Array.isArray(result._meta)).toBeTruthy() for (const [key, value] of Object.entries(expectedMeta)) { expect(result._meta![key]).toEqual(value) } } + if (expectedAgenticMeta) { + expect(result._meta).toBeDefined() + expect(result._meta?.agentic).toBeDefined() + expect(typeof result._meta?.agentic).toEqual('object') + expect(!Array.isArray(result._meta?.agentic)).toBeTruthy() + for (const [key, value] of Object.entries(expectedAgenticMeta)) { + expect((result._meta!.agentic as any)[key]).toEqual(value) + } + } - if (snapshot) { + if (expectedAgenticMetaHeaders) { + expect(result._meta).toBeDefined() + expect(result._meta?.agentic).toBeDefined() + expect(typeof result._meta?.agentic).toEqual('object') + expect(!Array.isArray(result._meta?.agentic)).toBeTruthy() + expect(typeof (result._meta?.agentic as any)?.headers).toEqual( + 'object' + ) + expect( + !Array.isArray((result._meta?.agentic as any)?.headers) + ).toBeTruthy() + for (const [key, value] of Object.entries( + expectedAgenticMetaHeaders + )) { + expect((result._meta!.agentic as any).headers[key]).toEqual(value) + } + } + + if (expectedSnapshot) { expect(result).toMatchSnapshot() } + if (expectedStableSnapshot) { + expect( + pick(result, 'content', 'structuredContent', 'isError') + ).toMatchSnapshot() + } + if (validate) { await Promise.resolve(validate(result)) } diff --git a/apps/e2e/src/mcp-fixtures.ts b/apps/e2e/src/mcp-fixtures.ts index 9412e7bf..31f07a0e 100644 --- a/apps/e2e/src/mcp-fixtures.ts +++ b/apps/e2e/src/mcp-fixtures.ts @@ -19,9 +19,13 @@ export type MCPE2ETestFixture = { content?: Array> structuredContent?: any _meta?: Record + _agenticMeta?: Record + _agenticMetaHeaders?: Record validate?: (result: any) => void | Promise - /** @default true */ + /** @default undefined */ snapshot?: boolean + /** @default true */ + stableSnapshot?: boolean } } @@ -42,8 +46,17 @@ export type MCPE2ETestFixtureSuite = { /** @default false */ debug?: boolean - /** @default undefined */ + /** + * Not used by default because the result `_meta.agentic` contains some + * metadata which may not be stable across test runs such as `cacheStatus` + * and `headers`. + * + * @default false + */ snapshot?: boolean + + /** @default undefined */ + stableSnapshot?: boolean } const now = Date.now() @@ -94,7 +107,7 @@ export const fixtureSuites: MCPE2ETestFixtureSuite[] = [ { title: 'Basic MCP => MCP "echo" tool call success', path: '@dev/test-basic-mcp/mcp', - snapshot: false, + stableSnapshot: false, fixtures: [ { request: { @@ -141,8 +154,6 @@ export const fixtureSuites: MCPE2ETestFixtureSuite[] = [ { title: 'Basic MCP => MCP "echo" tool call errors', path: '@dev/test-basic-openapi/mcp', - snapshot: false, - only: true, fixtures: [ { request: { @@ -154,7 +165,10 @@ export const fixtureSuites: MCPE2ETestFixtureSuite[] = [ } }, response: { - isError: true + isError: true, + _agenticMeta: { + status: 400 + } } } ] diff --git a/apps/gateway/package.json b/apps/gateway/package.json index f02a401a..711181c0 100644 --- a/apps/gateway/package.json +++ b/apps/gateway/package.json @@ -45,6 +45,7 @@ "hono": "catalog:", "ky": "catalog:", "plur": "catalog:", + "sort-keys": "^5.1.0", "stripe": "catalog:", "type-fest": "catalog:" }, diff --git a/apps/gateway/src/lib/durable-mcp-server.ts b/apps/gateway/src/lib/durable-mcp-server.ts index 5b754684..0f5d1d3f 100644 --- a/apps/gateway/src/lib/durable-mcp-server.ts +++ b/apps/gateway/src/lib/durable-mcp-server.ts @@ -1,5 +1,5 @@ import type { AdminDeployment, PricingPlan } from '@agentic/platform-types' -import { assert, getRateLimitHeaders, pruneEmpty } from '@agentic/platform-core' +import { assert, getRateLimitHeaders } from '@agentic/platform-core' import { parseDeploymentIdentifier } from '@agentic/platform-validators' import { Server } from '@modelcontextprotocol/sdk/server/index.js' import { @@ -19,6 +19,7 @@ import { handleMcpToolCallError } from './handle-mcp-tool-call-error' import { recordToolCallUsage } from './record-tool-call-usage' import { resolveOriginToolCall } from './resolve-origin-tool-call' import { transformHttpResponseToMcpToolCallResponse } from './transform-http-response-to-mcp-tool-call-response' +import { createAgenticMcpMetadata } from './utils' export class DurableMcpServerBase extends McpAgent< RawEnv, @@ -119,8 +120,8 @@ export class DurableMcpServerBase extends McpAgent< return toolCallResponse } catch (err: unknown) { - // Gracefully handle tool call exceptions, whether they're thrown by the - // origin or internally by the gateway. + // Gracefully handle tool call exceptions, whether they were thrown by + // the origin server or internally by the gateway. toolCallResponse = handleMcpToolCallError(err, { toolName, env: this.env @@ -133,22 +134,18 @@ export class DurableMcpServerBase extends McpAgent< // Augment the MCP tool call response with agentic metadata, which // makes it easier to debug tool calls and adds some much-needed HTTP // header-like functionality to tool call responses. - toolCallResponse._meta = { - ...toolCallResponse._meta, - agentic: pruneEmpty({ - ...(toolCallResponse._meta?.agentic as any), + toolCallResponse._meta = createAgenticMcpMetadata( + { deploymentId: deployment.id, consumerId: consumer?.id, - cacheStatus: resolvedOriginToolCallResult?.cacheStatus, toolName, - headers: { - ...(toolCallResponse._meta?.agentic as any)?.headers, - ...getRateLimitHeaders( - resolvedOriginToolCallResult?.rateLimitResult - ) - } - }) - } + cacheStatus: resolvedOriginToolCallResult?.cacheStatus, + headers: getRateLimitHeaders( + resolvedOriginToolCallResult?.rateLimitResult + ) + }, + toolCallResponse._meta + ) // Record tool call usage, whether the call was successful or not. recordToolCallUsage({ diff --git a/apps/gateway/src/lib/handle-mcp-tool-call-error.ts b/apps/gateway/src/lib/handle-mcp-tool-call-error.ts index 964a5fd1..bda12ac4 100644 --- a/apps/gateway/src/lib/handle-mcp-tool-call-error.ts +++ b/apps/gateway/src/lib/handle-mcp-tool-call-error.ts @@ -28,6 +28,12 @@ export function handleMcpToolCallError( let status: ContentfulStatusCode = 500 const res: McpToolCallResponse = { + _meta: { + agentic: { + toolName, + headers: {} + } + }, isError: true, content: [ { @@ -45,7 +51,7 @@ export function handleMcpToolCallError( // is a subclass of `HttpError`. if (err.headers) { for (const [key, value] of Object.entries(err.headers)) { - res._meta![key] = value + ;(res._meta!.agentic as any).headers[key] = value } } } else if (err instanceof HTTPException) { @@ -76,10 +82,10 @@ export function handleMcpToolCallError( } } else { // eslint-disable-next-line no-console - console.warn(`mcp tool call "${toolName}" warning`, status, message, err) + console.warn(`mcp tool call "${toolName}" warning`, status, err) } - res._meta!.status = status + ;(res._meta!.agentic as any).status = status res.content = [ { type: 'text', diff --git a/apps/gateway/src/lib/utils.test.ts b/apps/gateway/src/lib/utils.test.ts index 9e79945d..a1623557 100644 --- a/apps/gateway/src/lib/utils.test.ts +++ b/apps/gateway/src/lib/utils.test.ts @@ -1,6 +1,7 @@ import { expect, test } from 'vitest' import { + createAgenticMcpMetadata, isCacheControlPubliclyCacheable, isRequestPubliclyCacheable } from './utils' @@ -77,3 +78,18 @@ test('isCacheControlPubliclyCacheable false', () => { isCacheControlPubliclyCacheable('private, max-age=3600, must-revalidate') ).toBe(false) }) + +test('createAgenticMcpMetadata', () => { + expect( + // Test the stringified version because we want to test the order of the + // keys. + JSON.stringify( + createAgenticMcpMetadata({ + deploymentId: '123', + consumerId: '456', + toolName: 'test', + cacheStatus: 'HIT' + }) + ) + ).toMatchSnapshot() +}) diff --git a/apps/gateway/src/lib/utils.ts b/apps/gateway/src/lib/utils.ts index 7510c665..33afdbe4 100644 --- a/apps/gateway/src/lib/utils.ts +++ b/apps/gateway/src/lib/utils.ts @@ -1,3 +1,6 @@ +import { pruneEmpty } from '@agentic/platform-core' +import sortKeys from 'sort-keys' + export function isRequestPubliclyCacheable(request: Request): boolean { const pragma = request.headers.get('pragma') if (pragma === 'no-cache') { @@ -36,3 +39,56 @@ export function isCacheControlPubliclyCacheable( return true } + +const agenticMcpMetadataFieldOrder: string[] = [ + 'deploymentId', + 'consumerId', + 'toolName', + 'status', + 'cacheStatus', + 'headers' +] + +const agenticMcpMetadataFieldsOrderMap = Object.fromEntries( + agenticMcpMetadataFieldOrder.map((f, i) => [f, i]) +) + +function agenticMcpMetadataFieldComparator(a: string, b: string): number { + const aIndex = agenticMcpMetadataFieldsOrderMap[a] ?? Infinity + const bIndex = agenticMcpMetadataFieldsOrderMap[b] ?? Infinity + + return aIndex - bIndex +} + +/** + * Sanitizes agentic MCP metadata by sorting the keys and pruning empty values. + */ +export function createAgenticMcpMetadata( + metadata: { + deploymentId: string + consumerId?: string + toolName?: string + status?: number + cacheStatus?: string + headers?: Record + }, + existingMetadata?: Record +): Record { + const rawAgenticMcpMetadata = pruneEmpty({ + ...existingMetadata?.agentic, + ...metadata, + headers: { + ...existingMetadata?.agentic?.headers, + ...metadata.headers + } + }) + + const agentic = sortKeys(rawAgenticMcpMetadata, { + compare: agenticMcpMetadataFieldComparator + }) + + return { + ...existingMetadata, + agentic + } +} diff --git a/packages/openapi-utils/src/__snapshots__/get-tools-from-openapi-spec.test.ts.snap b/packages/openapi-utils/src/__snapshots__/get-tools-from-openapi-spec.test.ts.snap index f3e50301..143b32c2 100644 --- a/packages/openapi-utils/src/__snapshots__/get-tools-from-openapi-spec.test.ts.snap +++ b/packages/openapi-utils/src/__snapshots__/get-tools-from-openapi-spec.test.ts.snap @@ -1693,6 +1693,15 @@ exports[`getToolsFromOpenAPISpec > remote spec https://agentic-platform-fixtures "path": "/pure", "tags": undefined, }, + "strict_additional_properties": { + "method": "post", + "operationId": "strictAdditionalProperties", + "parameterSources": { + "foo": "body", + }, + "path": "/strict-additional-properties", + "tags": undefined, + }, "unpure_marked_pure": { "method": "post", "operationId": "unpure_marked_pure", @@ -1908,6 +1917,32 @@ exports[`getToolsFromOpenAPISpec > remote spec https://agentic-platform-fixtures "type": "object", }, }, + { + "description": "Echoes the request body only allowing a single "foo" field.", + "inputSchema": { + "properties": { + "foo": { + "type": "string", + }, + }, + "required": [ + "foo", + ], + "type": "object", + }, + "name": "strict_additional_properties", + "outputSchema": { + "properties": { + "foo": { + "type": "string", + }, + }, + "required": [ + "foo", + ], + "type": "object", + }, + }, ], } `; diff --git a/packages/openapi-utils/src/__snapshots__/validate-openapi-spec.test.ts.snap b/packages/openapi-utils/src/__snapshots__/validate-openapi-spec.test.ts.snap index d0d89470..caa25b61 100644 --- a/packages/openapi-utils/src/__snapshots__/validate-openapi-spec.test.ts.snap +++ b/packages/openapi-utils/src/__snapshots__/validate-openapi-spec.test.ts.snap @@ -22820,6 +22820,49 @@ exports[`validateOpenAPISpec > remote spec https://agentic-platform-fixtures-eve }, }, }, + "/strict-additional-properties": { + "post": { + "description": "Echoes the request body only allowing a single "foo" field.", + "operationId": "strictAdditionalProperties", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "foo": { + "type": "string", + }, + }, + "required": [ + "foo", + ], + "type": "object", + }, + }, + }, + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "foo": { + "type": "string", + }, + }, + "required": [ + "foo", + ], + "type": "object", + }, + }, + }, + "description": "Echoed request body", + }, + }, + }, + }, "/unpure-marked-pure": { "post": { "description": "Unpure tool marked pure", diff --git a/packages/platform/src/__snapshots__/load-agentic-config.test.ts.snap b/packages/platform/src/__snapshots__/load-agentic-config.test.ts.snap index 8f982010..0b56df82 100644 --- a/packages/platform/src/__snapshots__/load-agentic-config.test.ts.snap +++ b/packages/platform/src/__snapshots__/load-agentic-config.test.ts.snap @@ -56,12 +56,14 @@ exports[`loadAgenticConfig > basic-openapi 1`] = ` ], "toolConfigs": [ { + "additionalProperties": true, "enabled": true, "name": "get_posts", "pure": true, "reportUsage": true, }, { + "additionalProperties": true, "enabled": true, "name": "get_post", "pure": true, @@ -135,7 +137,7 @@ exports[`loadAgenticConfig > everything-openapi 1`] = ` "name": "test-everything-openapi", "originAdapter": { "location": "external", - "spec": "{"openapi":"3.1.0","info":{"title":"OpenAPI server everything","description":"OpenAPI kitchen sink server meant for testing Agentic's origin OpenAPI adapter and ToolConfig features.","version":"0.1.0"},"components":{"schemas":{"User":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"}},"required":["id","name","email"]}},"parameters":{}},"paths":{"/health":{"get":{"description":"Check if the server is healthy","operationId":"healthCheck","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}}}}}}},"/users/{userId}":{"get":{"description":"Gets a user","tags":["users"],"operationId":"getUser","parameters":[{"schema":{"type":"string"},"required":true,"description":"User ID","name":"userId","in":"path"}],"responses":{"200":{"description":"A user object","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}}}}},"/disabled-tool":{"get":{"description":"Disabled tool","operationId":"disabledTool","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}}}}}}},"/disabled-for-free-plan-tool":{"get":{"description":"Disabled for free plan tool","operationId":"disabledForFreePlanTool","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}}}}}}},"/echo":{"post":{"description":"Echoes the request body","operationId":"echo","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/pure":{"post":{"description":"Pure tool","operationId":"pure","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/unpure-marked-pure":{"post":{"description":"Unpure tool marked pure","operationId":"unpure_marked_pure","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body with current timestamp to not be pure","content":{"application/json":{"schema":{"type":"object","properties":{"now":{"type":"number"}},"required":["now"]}}}}}}},"/custom-cache-control-tool":{"post":{"description":"Custom cache control tool","operationId":"customCacheControlTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/no-store-cache-control-tool":{"post":{"description":"No store cache control tool","operationId":"noStoreCacheControlTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/no-cache-cache-control-tool":{"post":{"description":"No cache cache control tool","operationId":"noCacheCacheControlTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/custom-rate-limit-tool":{"post":{"description":"Custom rate limit tool","operationId":"customRateLimitTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/disabled-rate-limit-tool":{"post":{"description":"Disabled rate limit tool","operationId":"disabledRateLimitTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}}},"webhooks":{}}", + "spec": "{"openapi":"3.1.0","info":{"title":"OpenAPI server everything","description":"OpenAPI kitchen sink server meant for testing Agentic's origin OpenAPI adapter and ToolConfig features.","version":"0.1.0"},"components":{"schemas":{"User":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"}},"required":["id","name","email"]}},"parameters":{}},"paths":{"/health":{"get":{"description":"Check if the server is healthy","operationId":"healthCheck","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}}}}}}},"/users/{userId}":{"get":{"description":"Gets a user","tags":["users"],"operationId":"getUser","parameters":[{"schema":{"type":"string"},"required":true,"description":"User ID","name":"userId","in":"path"}],"responses":{"200":{"description":"A user object","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}}}}},"/disabled-tool":{"get":{"description":"Disabled tool","operationId":"disabledTool","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}}}}}}},"/disabled-for-free-plan-tool":{"get":{"description":"Disabled for free plan tool","operationId":"disabledForFreePlanTool","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}}}}}}},"/echo":{"post":{"description":"Echoes the request body","operationId":"echo","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/pure":{"post":{"description":"Pure tool","operationId":"pure","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/unpure-marked-pure":{"post":{"description":"Unpure tool marked pure","operationId":"unpure_marked_pure","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body with current timestamp to not be pure","content":{"application/json":{"schema":{"type":"object","properties":{"now":{"type":"number"}},"required":["now"]}}}}}}},"/custom-cache-control-tool":{"post":{"description":"Custom cache control tool","operationId":"customCacheControlTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/no-store-cache-control-tool":{"post":{"description":"No store cache control tool","operationId":"noStoreCacheControlTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/no-cache-cache-control-tool":{"post":{"description":"No cache cache control tool","operationId":"noCacheCacheControlTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/custom-rate-limit-tool":{"post":{"description":"Custom rate limit tool","operationId":"customRateLimitTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/disabled-rate-limit-tool":{"post":{"description":"Disabled rate limit tool","operationId":"disabledRateLimitTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/strict-additional-properties":{"post":{"description":"Echoes the request body only allowing a single \\"foo\\" field.","operationId":"strictAdditionalProperties","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"foo":{"type":"string"}},"required":["foo"]}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{"foo":{"type":"string"}},"required":["foo"]}}}}}}}},"webhooks":{}}", "type": "openapi", }, "originUrl": "https://agentic-platform-fixtures-everything.onrender.com", @@ -157,6 +159,7 @@ exports[`loadAgenticConfig > everything-openapi 1`] = ` ], "toolConfigs": [ { + "additionalProperties": true, "enabled": true, "name": "get_user", "pricingPlanOverridesMap": { @@ -170,12 +173,14 @@ exports[`loadAgenticConfig > everything-openapi 1`] = ` "reportUsage": true, }, { + "additionalProperties": true, "enabled": false, "name": "disabled_tool", "pure": false, "reportUsage": true, }, { + "additionalProperties": true, "enabled": true, "name": "disabled_for_free_plan_tool", "pricingPlanOverridesMap": { @@ -187,18 +192,21 @@ exports[`loadAgenticConfig > everything-openapi 1`] = ` "reportUsage": true, }, { + "additionalProperties": true, "enabled": true, "name": "pure", "pure": true, "reportUsage": true, }, { + "additionalProperties": true, "enabled": true, "name": "unpure_marked_pure", "pure": true, "reportUsage": true, }, { + "additionalProperties": true, "cacheControl": "public, max-age=7200, s-maxage=7200, stale-while-revalidate=3600", "enabled": true, "name": "custom_cache_control_tool", @@ -206,6 +214,7 @@ exports[`loadAgenticConfig > everything-openapi 1`] = ` "reportUsage": true, }, { + "additionalProperties": true, "cacheControl": "no-cache", "enabled": true, "name": "no_cache_cache_control_tool", @@ -213,6 +222,7 @@ exports[`loadAgenticConfig > everything-openapi 1`] = ` "reportUsage": true, }, { + "additionalProperties": true, "cacheControl": "no-store", "enabled": true, "name": "no_store_cache_control_tool", @@ -220,6 +230,7 @@ exports[`loadAgenticConfig > everything-openapi 1`] = ` "reportUsage": true, }, { + "additionalProperties": true, "enabled": true, "name": "custom_rate_limit_tool", "pure": false, @@ -231,12 +242,20 @@ exports[`loadAgenticConfig > everything-openapi 1`] = ` "reportUsage": true, }, { + "additionalProperties": true, "enabled": true, "name": "disabled_rate_limit_tool", "pure": false, "rateLimit": null, "reportUsage": true, }, + { + "additionalProperties": false, + "enabled": true, + "name": "strict_additional_properties", + "pure": false, + "reportUsage": true, + }, ], "version": undefined, } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b8119a22..3e048619 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -451,6 +451,9 @@ importers: plur: specifier: 'catalog:' version: 5.1.0 + sort-keys: + specifier: ^5.1.0 + version: 5.1.0 stripe: specifier: 'catalog:' version: 18.2.1(@types/node@22.15.29) @@ -1260,12 +1263,6 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.6.1': - resolution: {integrity: sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/eslint-utils@4.7.0': resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -5286,10 +5283,6 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyglobby@0.2.13: - resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} - engines: {node: '>=12.0.0'} - tinyglobby@0.2.14: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} @@ -6124,11 +6117,6 @@ snapshots: '@esbuild/win32-x64@0.25.4': optional: true - '@eslint-community/eslint-utils@4.6.1(eslint@9.28.0(jiti@2.4.2))': - dependencies: - eslint: 9.28.0(jiti@2.4.2) - eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.7.0(eslint@9.28.0(jiti@2.4.2))': dependencies: eslint: 9.28.0(jiti@2.4.2) @@ -8452,7 +8440,7 @@ snapshots: eslint@9.28.0(jiti@2.4.2): dependencies: - '@eslint-community/eslint-utils': 4.6.1(eslint@9.28.0(jiti@2.4.2)) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0(jiti@2.4.2)) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.20.0 '@eslint/config-helpers': 0.2.1 @@ -10373,11 +10361,6 @@ snapshots: tinyexec@0.3.2: {} - tinyglobby@0.2.13: - dependencies: - fdir: 6.4.4(picomatch@4.0.2) - picomatch: 4.0.2 - tinyglobby@0.2.14: dependencies: fdir: 6.4.4(picomatch@4.0.2) @@ -10452,7 +10435,7 @@ snapshots: source-map: 0.8.0-beta.0 sucrase: 3.35.0 tinyexec: 0.3.2 - tinyglobby: 0.2.13 + tinyglobby: 0.2.14 tree-kill: 1.2.2 optionalDependencies: postcss: 8.5.3