From 9dbac521498f6e4478c2c896eae6954e51fa042e Mon Sep 17 00:00:00 2001 From: jxom <7336481+jxom@users.noreply.github.com> Date: Fri, 10 Jul 2026 10:18:12 +1000 Subject: [PATCH] feat(openapi): include operation descriptions in MCP tool metadata --- .changeset/openapi-mcp-descriptions.md | 5 +++++ src/Openapi.test.ts | 13 +++++++++++++ src/Openapi.ts | 5 +++++ test/fixtures/openapi-spec.ts | 1 + 4 files changed, 24 insertions(+) create mode 100644 .changeset/openapi-mcp-descriptions.md diff --git a/.changeset/openapi-mcp-descriptions.md b/.changeset/openapi-mcp-descriptions.md new file mode 100644 index 0000000..69da0d0 --- /dev/null +++ b/.changeset/openapi-mcp-descriptions.md @@ -0,0 +1,5 @@ +--- +'incur': patch +--- + +Included OpenAPI operation descriptions in MCP tool descriptions by concatenating summary and description; CLI help keeps the short summary. diff --git a/src/Openapi.test.ts b/src/Openapi.test.ts index 1477295..ee8240c 100644 --- a/src/Openapi.test.ts +++ b/src/Openapi.test.ts @@ -154,6 +154,19 @@ describe('generateCommands', () => { expect(cmd.description).toBe('List users') }) + test('command concatenates summary and description for MCP', async () => { + const commands = await Openapi.generateCommands(spec, app.fetch) + const cmd = commands.get('listUsers')! + if ('_group' in cmd) throw new Error('expected listUsers command') + expect(cmd.mcp?.description).toBe( + 'List users\n\nReturns users ordered by creation date. Use `limit` to cap the page size.', + ) + // Summary-only operations get no MCP override. + const summaryOnly = commands.get('createUser')! + if ('_group' in summaryOnly) throw new Error('expected createUser command') + expect(summaryOnly.mcp).toBeUndefined() + }) + test('coerced number params preserve description', async () => { const commands = await Openapi.generateCommands(spec, app.fetch) const cmd = commands.get('listUsers')! diff --git a/src/Openapi.ts b/src/Openapi.ts index 79dd635..fba99e1 100644 --- a/src/Openapi.ts +++ b/src/Openapi.ts @@ -122,6 +122,7 @@ type FetchHandler = (req: Request) => Response | Promise type GeneratedCommand = { args?: z.ZodObject | undefined description?: string | undefined + mcp?: { description: string } | undefined options?: z.ZodObject | undefined run: (context: any) => any } @@ -434,6 +435,10 @@ export async function generateCommands( setCommand(commands, segments, { description: op.summary ?? op.description, + // Help keeps the short summary; MCP tools surface the full prose. + ...(op.summary && op.description && op.summary !== op.description + ? { mcp: { description: `${op.summary}\n\n${op.description}` } } + : undefined), args: argsSchema, options: optionsSchema, run: createHandler({ diff --git a/test/fixtures/openapi-spec.ts b/test/fixtures/openapi-spec.ts index db4c584..fd444cb 100644 --- a/test/fixtures/openapi-spec.ts +++ b/test/fixtures/openapi-spec.ts @@ -7,6 +7,7 @@ export const spec = { get: { operationId: 'listUsers', summary: 'List users', + description: 'Returns users ordered by creation date. Use `limit` to cap the page size.', parameters: [ { name: 'limit',