From 3823d813192777438be1351f142489575844ffc0 Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Thu, 23 Oct 2025 17:15:46 -0400 Subject: [PATCH 1/8] feat: support lazy tool definitions, disable client schema validation by default --- .../src/content-api/graphql.ts | 2 +- .../src/content-api/index.ts | 17 +++---- packages/mcp-server-supabase/src/server.ts | 5 +- .../src/tools/docs-tools.ts | 49 ++++++++++--------- .../src/transports/stdio.ts | 15 ++++++ packages/mcp-utils/src/server.ts | 39 ++++++++------- 6 files changed, 76 insertions(+), 51 deletions(-) diff --git a/packages/mcp-server-supabase/src/content-api/graphql.ts b/packages/mcp-server-supabase/src/content-api/graphql.ts index b286cfb..7925a13 100644 --- a/packages/mcp-server-supabase/src/content-api/graphql.ts +++ b/packages/mcp-server-supabase/src/content-api/graphql.ts @@ -121,7 +121,7 @@ export class GraphQLClient { */ async query( request: GraphQLRequest, - options: QueryOptions = { validateSchema: true } + options: QueryOptions = { validateSchema: false } ) { try { // Check that this is a valid GraphQL query diff --git a/packages/mcp-server-supabase/src/content-api/index.ts b/packages/mcp-server-supabase/src/content-api/index.ts index 937bcc8..500e2fb 100644 --- a/packages/mcp-server-supabase/src/content-api/index.ts +++ b/packages/mcp-server-supabase/src/content-api/index.ts @@ -6,7 +6,7 @@ const contentApiSchemaResponseSchema = z.object({ }); export type ContentApiClient = { - schema: string; + loadSchema: () => Promise; query: QueryFn; setUserAgent: (userAgent: string) => void; }; @@ -18,18 +18,15 @@ export async function createContentApiClient( const graphqlClient = new GraphQLClient({ url, headers, - // Content API provides schema string via `schema` query - loadSchema: async ({ query }) => { - const response = await query({ query: '{ schema }' }); - const { schema } = contentApiSchemaResponseSchema.parse(response); - return schema; - }, }); - const { source } = await graphqlClient.schemaLoaded; - return { - schema: source, + loadSchema: async () => { + console.error('[MATT] loading graphql schema'); + const response = await graphqlClient.query({ query: '{ schema }' }); + const { schema } = contentApiSchemaResponseSchema.parse(response); + return schema; + }, async query(request: GraphQLRequest) { return graphqlClient.query(request); }, diff --git a/packages/mcp-server-supabase/src/server.ts b/packages/mcp-server-supabase/src/server.ts index ab8fe0b..d584573 100644 --- a/packages/mcp-server-supabase/src/server.ts +++ b/packages/mcp-server-supabase/src/server.ts @@ -1,5 +1,6 @@ import { createMcpServer, + type Prop, type Tool, type ToolCallCallback, } from '@supabase/mcp-utils'; @@ -117,7 +118,9 @@ export function createSupabaseMcpServer(options: SupabaseMcpServerOptions) { onToolCall, tools: async () => { const contentApiClient = await contentApiClientPromise; - const tools: Record = {}; + const tools: Record> = {}; + + console.error('[MATT] in tools() callback'); const { account, diff --git a/packages/mcp-server-supabase/src/tools/docs-tools.ts b/packages/mcp-server-supabase/src/tools/docs-tools.ts index adb04d8..e3fb0be 100644 --- a/packages/mcp-server-supabase/src/tools/docs-tools.ts +++ b/packages/mcp-server-supabase/src/tools/docs-tools.ts @@ -9,29 +9,34 @@ export type DocsToolsOptions = { export function getDocsTools({ contentApiClient }: DocsToolsOptions) { return { - search_docs: tool({ - description: source` - Search the Supabase documentation using GraphQL. Must be a valid GraphQL query. + search_docs: tool(async () => { + console.error('[MATT] creating search_docs tool'); + const schema = await contentApiClient.loadSchema(); - You should default to calling this even if you think you already know the answer, since the documentation is always being updated. - - Below is the GraphQL schema for the Supabase docs endpoint: - ${contentApiClient.schema} - `, - annotations: { - title: 'Search docs', - readOnlyHint: true, - destructiveHint: false, - idempotentHint: true, - openWorldHint: false, - }, - parameters: z.object({ - // Intentionally use a verbose param name for the LLM - graphql_query: z.string().describe('GraphQL query string'), - }), - execute: async ({ graphql_query }) => { - return await contentApiClient.query({ query: graphql_query }); - }, + return { + description: source` + Search the Supabase documentation using GraphQL. Must be a valid GraphQL query. + + You should default to calling this even if you think you already know the answer, since the documentation is always being updated. + + Below is the GraphQL schema for the Supabase docs endpoint: + ${schema} + `, + annotations: { + title: 'Search docs', + readOnlyHint: true, + destructiveHint: false, + idempotentHint: true, + openWorldHint: false, + }, + parameters: z.object({ + // Intentionally use a verbose param name for the LLM + graphql_query: z.string().describe('GraphQL query string'), + }), + execute: async ({ graphql_query }) => { + return await contentApiClient.query({ query: graphql_query }); + }, + }; }), }; } diff --git a/packages/mcp-server-supabase/src/transports/stdio.ts b/packages/mcp-server-supabase/src/transports/stdio.ts index e974296..9d54f3c 100644 --- a/packages/mcp-server-supabase/src/transports/stdio.ts +++ b/packages/mcp-server-supabase/src/transports/stdio.ts @@ -6,6 +6,10 @@ import packageJson from '../../package.json' with { type: 'json' }; import { createSupabaseApiPlatform } from '../platform/api-platform.js'; import { createSupabaseMcpServer } from '../server.js'; import { parseList } from './util.js'; +import { + isJSONRPCNotification, + isJSONRPCRequest, +} from '@modelcontextprotocol/sdk/types.js'; const { version } = packageJson; @@ -73,6 +77,17 @@ async function main() { const transport = new StdioServerTransport(); + transport.onmessage = (message) => { + if (isJSONRPCRequest(message)) { + console.error(`[MATT] received JSON-RPC request "${message.method}"`); + } + if (isJSONRPCNotification(message)) { + console.error( + `[MATT] received JSON-RPC notification "${message.method}"` + ); + } + }; + await server.connect(transport); } diff --git a/packages/mcp-utils/src/server.ts b/packages/mcp-utils/src/server.ts index cf834c3..8ebe8de 100644 --- a/packages/mcp-utils/src/server.ts +++ b/packages/mcp-utils/src/server.ts @@ -165,7 +165,7 @@ export function jsonResourceResponse( * Helper function to define an MCP tool while preserving type information. */ export function tool, Result>( - tool: Tool + tool: Prop> ) { return tool; } @@ -251,7 +251,7 @@ export type McpServerOptions = { * asks for the list of tools or invokes a tool. This allows for dynamic tools * that can change after the server has started. */ - tools?: Prop>; + tools?: Prop>>; }; /** @@ -293,6 +293,7 @@ export function createMcpServer(options: McpServerOptions) { } async function getTools() { + console.error('[MATT] in getTools()'); if (!options.tools) { throw new Error('tools not available'); } @@ -302,6 +303,16 @@ export function createMcpServer(options: McpServerOptions) { : options.tools; } + async function getTool(name: string) { + console.error('[MATT] in getTool()', name); + const tools = await getTools(); + const toolProp = tools[name]; + if (!toolProp) { + throw new Error('tool not found'); + } + return typeof toolProp === 'function' ? await toolProp() : toolProp; + } + server.oninitialized = async () => { const clientInfo = server.getClientVersion(); const clientCapabilities = server.getClientCapabilities(); @@ -436,9 +447,13 @@ export function createMcpServer(options: McpServerOptions) { ListToolsRequestSchema, async (): Promise => { const tools = await getTools(); + return { - tools: Object.entries(tools).map( - ([name, { description, annotations, parameters }]) => { + tools: await Promise.all( + Object.entries(tools).map(async ([name, toolProp]) => { + const tool = + typeof toolProp === 'function' ? await toolProp() : toolProp; + const { description, annotations, parameters } = tool; const inputSchema = zodToJsonSchema(parameters); if (!('properties' in inputSchema)) { @@ -451,26 +466,16 @@ export function createMcpServer(options: McpServerOptions) { annotations, inputSchema, }; - } + }) ), - }; + } satisfies ListToolsResult; } ); server.setRequestHandler(CallToolRequestSchema, async (request) => { try { - const tools = await getTools(); const toolName = request.params.name; - - if (!(toolName in tools)) { - throw new Error('tool not found'); - } - - const tool = tools[toolName]; - - if (!tool) { - throw new Error('tool not found'); - } + const tool = await getTool(toolName); const args = tool.parameters .strict() From bca52f0c0125332e66a16d85861891d06d3c1e02 Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Thu, 23 Oct 2025 17:19:17 -0400 Subject: [PATCH 2/8] feat: lazy tool descriptions --- .../src/tools/docs-tools.ts | 43 +++++++++---------- packages/mcp-utils/src/server.ts | 7 ++- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/mcp-server-supabase/src/tools/docs-tools.ts b/packages/mcp-server-supabase/src/tools/docs-tools.ts index e3fb0be..c03a7e7 100644 --- a/packages/mcp-server-supabase/src/tools/docs-tools.ts +++ b/packages/mcp-server-supabase/src/tools/docs-tools.ts @@ -9,34 +9,31 @@ export type DocsToolsOptions = { export function getDocsTools({ contentApiClient }: DocsToolsOptions) { return { - search_docs: tool(async () => { - console.error('[MATT] creating search_docs tool'); - const schema = await contentApiClient.loadSchema(); + search_docs: tool({ + description: async () => { + const schema = await contentApiClient.loadSchema(); - return { - description: source` + return source` Search the Supabase documentation using GraphQL. Must be a valid GraphQL query. - You should default to calling this even if you think you already know the answer, since the documentation is always being updated. - Below is the GraphQL schema for the Supabase docs endpoint: ${schema} - `, - annotations: { - title: 'Search docs', - readOnlyHint: true, - destructiveHint: false, - idempotentHint: true, - openWorldHint: false, - }, - parameters: z.object({ - // Intentionally use a verbose param name for the LLM - graphql_query: z.string().describe('GraphQL query string'), - }), - execute: async ({ graphql_query }) => { - return await contentApiClient.query({ query: graphql_query }); - }, - }; + `; + }, + annotations: { + title: 'Search docs', + readOnlyHint: true, + destructiveHint: false, + idempotentHint: true, + openWorldHint: false, + }, + parameters: z.object({ + // Intentionally use a verbose param name for the LLM + graphql_query: z.string().describe('GraphQL query string'), + }), + execute: async ({ graphql_query }) => { + return await contentApiClient.query({ query: graphql_query }); + }, }), }; } diff --git a/packages/mcp-utils/src/server.ts b/packages/mcp-utils/src/server.ts index 8ebe8de..ea1c330 100644 --- a/packages/mcp-utils/src/server.ts +++ b/packages/mcp-utils/src/server.ts @@ -54,7 +54,7 @@ export type Tool< Params extends z.ZodObject = z.ZodObject, Result = unknown, > = { - description: string; + description: Prop; annotations?: Annotations; parameters: Params; execute(params: z.infer): Promise; @@ -462,7 +462,10 @@ export function createMcpServer(options: McpServerOptions) { return { name, - description, + description: + typeof description === 'function' + ? await description() + : description, annotations, inputSchema, }; From 092bfd7dfc13ae608300410e5f69dc39bedd9126 Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Thu, 23 Oct 2025 17:21:40 -0400 Subject: [PATCH 3/8] chore: cleanup logs --- packages/mcp-server-supabase/src/content-api/index.ts | 1 - packages/mcp-server-supabase/src/server.ts | 2 -- packages/mcp-server-supabase/src/transports/stdio.ts | 11 ----------- packages/mcp-utils/src/server.ts | 2 -- 4 files changed, 16 deletions(-) diff --git a/packages/mcp-server-supabase/src/content-api/index.ts b/packages/mcp-server-supabase/src/content-api/index.ts index 500e2fb..3dfaee3 100644 --- a/packages/mcp-server-supabase/src/content-api/index.ts +++ b/packages/mcp-server-supabase/src/content-api/index.ts @@ -22,7 +22,6 @@ export async function createContentApiClient( return { loadSchema: async () => { - console.error('[MATT] loading graphql schema'); const response = await graphqlClient.query({ query: '{ schema }' }); const { schema } = contentApiSchemaResponseSchema.parse(response); return schema; diff --git a/packages/mcp-server-supabase/src/server.ts b/packages/mcp-server-supabase/src/server.ts index d584573..b310503 100644 --- a/packages/mcp-server-supabase/src/server.ts +++ b/packages/mcp-server-supabase/src/server.ts @@ -120,8 +120,6 @@ export function createSupabaseMcpServer(options: SupabaseMcpServerOptions) { const contentApiClient = await contentApiClientPromise; const tools: Record> = {}; - console.error('[MATT] in tools() callback'); - const { account, database, diff --git a/packages/mcp-server-supabase/src/transports/stdio.ts b/packages/mcp-server-supabase/src/transports/stdio.ts index 9d54f3c..9f7c1ae 100644 --- a/packages/mcp-server-supabase/src/transports/stdio.ts +++ b/packages/mcp-server-supabase/src/transports/stdio.ts @@ -77,17 +77,6 @@ async function main() { const transport = new StdioServerTransport(); - transport.onmessage = (message) => { - if (isJSONRPCRequest(message)) { - console.error(`[MATT] received JSON-RPC request "${message.method}"`); - } - if (isJSONRPCNotification(message)) { - console.error( - `[MATT] received JSON-RPC notification "${message.method}"` - ); - } - }; - await server.connect(transport); } diff --git a/packages/mcp-utils/src/server.ts b/packages/mcp-utils/src/server.ts index ea1c330..ca6d21e 100644 --- a/packages/mcp-utils/src/server.ts +++ b/packages/mcp-utils/src/server.ts @@ -293,7 +293,6 @@ export function createMcpServer(options: McpServerOptions) { } async function getTools() { - console.error('[MATT] in getTools()'); if (!options.tools) { throw new Error('tools not available'); } @@ -304,7 +303,6 @@ export function createMcpServer(options: McpServerOptions) { } async function getTool(name: string) { - console.error('[MATT] in getTool()', name); const tools = await getTools(); const toolProp = tools[name]; if (!toolProp) { From 6839848170cab3e5fb5fc42c890b96800376cf9f Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Thu, 23 Oct 2025 17:22:27 -0400 Subject: [PATCH 4/8] docs: restore comment --- packages/mcp-server-supabase/src/content-api/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/mcp-server-supabase/src/content-api/index.ts b/packages/mcp-server-supabase/src/content-api/index.ts index 3dfaee3..b788024 100644 --- a/packages/mcp-server-supabase/src/content-api/index.ts +++ b/packages/mcp-server-supabase/src/content-api/index.ts @@ -21,6 +21,7 @@ export async function createContentApiClient( }); return { + // Content API provides schema string via `schema` query loadSchema: async () => { const response = await graphqlClient.query({ query: '{ schema }' }); const { schema } = contentApiSchemaResponseSchema.parse(response); From 46efa2d38133c40fadcfde0582466f06d8576a0a Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Thu, 23 Oct 2025 17:27:07 -0400 Subject: [PATCH 5/8] feat: revert lazy tool definitions, just use lazy description --- .../src/content-api/index.ts | 1 + packages/mcp-server-supabase/src/server.ts | 3 +- packages/mcp-utils/src/server.ts | 61 +++++++++---------- 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/packages/mcp-server-supabase/src/content-api/index.ts b/packages/mcp-server-supabase/src/content-api/index.ts index b788024..b3b1c31 100644 --- a/packages/mcp-server-supabase/src/content-api/index.ts +++ b/packages/mcp-server-supabase/src/content-api/index.ts @@ -23,6 +23,7 @@ export async function createContentApiClient( return { // Content API provides schema string via `schema` query loadSchema: async () => { + console.error('[MATT] loading graphql schema'); const response = await graphqlClient.query({ query: '{ schema }' }); const { schema } = contentApiSchemaResponseSchema.parse(response); return schema; diff --git a/packages/mcp-server-supabase/src/server.ts b/packages/mcp-server-supabase/src/server.ts index b310503..ab8fe0b 100644 --- a/packages/mcp-server-supabase/src/server.ts +++ b/packages/mcp-server-supabase/src/server.ts @@ -1,6 +1,5 @@ import { createMcpServer, - type Prop, type Tool, type ToolCallCallback, } from '@supabase/mcp-utils'; @@ -118,7 +117,7 @@ export function createSupabaseMcpServer(options: SupabaseMcpServerOptions) { onToolCall, tools: async () => { const contentApiClient = await contentApiClientPromise; - const tools: Record> = {}; + const tools: Record = {}; const { account, diff --git a/packages/mcp-utils/src/server.ts b/packages/mcp-utils/src/server.ts index ca6d21e..b42e773 100644 --- a/packages/mcp-utils/src/server.ts +++ b/packages/mcp-utils/src/server.ts @@ -165,7 +165,7 @@ export function jsonResourceResponse( * Helper function to define an MCP tool while preserving type information. */ export function tool, Result>( - tool: Prop> + tool: Tool ) { return tool; } @@ -251,7 +251,7 @@ export type McpServerOptions = { * asks for the list of tools or invokes a tool. This allows for dynamic tools * that can change after the server has started. */ - tools?: Prop>>; + tools?: Prop>; }; /** @@ -302,15 +302,6 @@ export function createMcpServer(options: McpServerOptions) { : options.tools; } - async function getTool(name: string) { - const tools = await getTools(); - const toolProp = tools[name]; - if (!toolProp) { - throw new Error('tool not found'); - } - return typeof toolProp === 'function' ? await toolProp() : toolProp; - } - server.oninitialized = async () => { const clientInfo = server.getClientVersion(); const clientCapabilities = server.getClientCapabilities(); @@ -448,26 +439,25 @@ export function createMcpServer(options: McpServerOptions) { return { tools: await Promise.all( - Object.entries(tools).map(async ([name, toolProp]) => { - const tool = - typeof toolProp === 'function' ? await toolProp() : toolProp; - const { description, annotations, parameters } = tool; - const inputSchema = zodToJsonSchema(parameters); - - if (!('properties' in inputSchema)) { - throw new Error('tool parameters must be a ZodObject'); + Object.entries(tools).map( + async ([name, { description, annotations, parameters }]) => { + const inputSchema = zodToJsonSchema(parameters); + + if (!('properties' in inputSchema)) { + throw new Error('tool parameters must be a ZodObject'); + } + + return { + name, + description: + typeof description === 'function' + ? await description() + : description, + annotations, + inputSchema, + }; } - - return { - name, - description: - typeof description === 'function' - ? await description() - : description, - annotations, - inputSchema, - }; - }) + ) ), } satisfies ListToolsResult; } @@ -475,9 +465,18 @@ export function createMcpServer(options: McpServerOptions) { server.setRequestHandler(CallToolRequestSchema, async (request) => { try { + const tools = await getTools(); const toolName = request.params.name; - const tool = await getTool(toolName); + if (!(toolName in tools)) { + throw new Error('tool not found'); + } + + const tool = tools[toolName]; + + if (!tool) { + throw new Error('tool not found'); + } const args = tool.parameters .strict() .parse(request.params.arguments ?? {}); From 92ae9ec5175ebfa8be3ddb9fe34d48d8d8430fed Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Thu, 23 Oct 2025 17:27:40 -0400 Subject: [PATCH 6/8] chore: cleanup --- packages/mcp-server-supabase/src/content-api/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/mcp-server-supabase/src/content-api/index.ts b/packages/mcp-server-supabase/src/content-api/index.ts index b3b1c31..b788024 100644 --- a/packages/mcp-server-supabase/src/content-api/index.ts +++ b/packages/mcp-server-supabase/src/content-api/index.ts @@ -23,7 +23,6 @@ export async function createContentApiClient( return { // Content API provides schema string via `schema` query loadSchema: async () => { - console.error('[MATT] loading graphql schema'); const response = await graphqlClient.query({ query: '{ schema }' }); const { schema } = contentApiSchemaResponseSchema.parse(response); return schema; From 9bd5e344123f78c465fab024333be8b64c25f98b Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Fri, 24 Oct 2025 14:29:47 -0400 Subject: [PATCH 7/8] chore: unused imports --- packages/mcp-server-supabase/src/transports/stdio.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/mcp-server-supabase/src/transports/stdio.ts b/packages/mcp-server-supabase/src/transports/stdio.ts index 9f7c1ae..e974296 100644 --- a/packages/mcp-server-supabase/src/transports/stdio.ts +++ b/packages/mcp-server-supabase/src/transports/stdio.ts @@ -6,10 +6,6 @@ import packageJson from '../../package.json' with { type: 'json' }; import { createSupabaseApiPlatform } from '../platform/api-platform.js'; import { createSupabaseMcpServer } from '../server.js'; import { parseList } from './util.js'; -import { - isJSONRPCNotification, - isJSONRPCRequest, -} from '@modelcontextprotocol/sdk/types.js'; const { version } = packageJson; From b33b46d81ede68986f029a76ef6b40abeee82e85 Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Fri, 24 Oct 2025 14:55:29 -0400 Subject: [PATCH 8/8] test: schema is only loaded when listing tools --- .../mcp-server-supabase/src/server.test.ts | 25 +++++++++++++++++++ packages/mcp-server-supabase/test/mocks.ts | 3 +++ 2 files changed, 28 insertions(+) diff --git a/packages/mcp-server-supabase/src/server.test.ts b/packages/mcp-server-supabase/src/server.test.ts index 5d44112..80b8f61 100644 --- a/packages/mcp-server-supabase/src/server.test.ts +++ b/packages/mcp-server-supabase/src/server.test.ts @@ -11,6 +11,7 @@ import { ACCESS_TOKEN, API_URL, contentApiMockSchema, + mockContentApiSchemaLoadCount, createOrganization, createProject, createBranch, @@ -31,6 +32,7 @@ beforeEach(async () => { mockOrgs.clear(); mockProjects.clear(); mockBranches.clear(); + mockContentApiSchemaLoadCount.value = 0; const server = setupServer(...mockContentApi, ...mockManagementApi); server.listen({ onUnhandledRequest: 'error' }); @@ -2943,4 +2945,27 @@ describe('docs tools', () => { expect(tool.description.includes(contentApiMockSchema)).toBe(true); }); + + test('schema is only loaded when listing tools', async () => { + const { client, callTool } = await setup(); + + expect(mockContentApiSchemaLoadCount.value).toBe(0); + + // "tools/list" requests fetch the schema + await client.listTools(); + expect(mockContentApiSchemaLoadCount.value).toBe(1); + + // "tools/call" should not fetch the schema again + await callTool({ + name: 'search_docs', + arguments: { + graphql_query: '{ searchDocs(query: "test") { nodes { title } } }', + }, + }); + expect(mockContentApiSchemaLoadCount.value).toBe(1); + + // Additional "tools/list" requests fetch the schema again + await client.listTools(); + expect(mockContentApiSchemaLoadCount.value).toBe(2); + }); }); diff --git a/packages/mcp-server-supabase/test/mocks.ts b/packages/mcp-server-supabase/test/mocks.ts index 5cc539f..6f45141 100644 --- a/packages/mcp-server-supabase/test/mocks.ts +++ b/packages/mcp-server-supabase/test/mocks.ts @@ -84,6 +84,8 @@ export const mockOrgs = new Map(); export const mockProjects = new Map(); export const mockBranches = new Map(); +export const mockContentApiSchemaLoadCount = { value: 0 }; + export const mockContentApi = [ http.post(CONTENT_API_URL, async ({ request }) => { const json = await request.json(); @@ -96,6 +98,7 @@ export const mockContentApi = [ const [queryName] = getQueryFields(document); if (queryName === 'schema') { + mockContentApiSchemaLoadCount.value++; return HttpResponse.json({ data: { schema: contentApiMockSchema,