From 61d2bc3460e02e92e72e89891ed9e1da3cd0cf81 Mon Sep 17 00:00:00 2001 From: Samuel Bushi Date: Tue, 3 Feb 2026 13:23:34 -0500 Subject: [PATCH] fix(genkit-tools/mcp): Add docs to top-level tool descriptions --- genkit-tools/cli/src/mcp/docs.ts | 31 ++++++++++++++++------------ genkit-tools/cli/src/mcp/flows.ts | 32 ++++++++++++++++++----------- genkit-tools/cli/src/mcp/runtime.ts | 32 +++++++++++++++++++---------- genkit-tools/cli/src/mcp/trace.ts | 21 +++++++++++-------- genkit-tools/cli/src/mcp/usage.ts | 21 +++++++++++-------- genkit-tools/cli/src/mcp/utils.ts | 25 +++++++++++++++++----- 6 files changed, 105 insertions(+), 57 deletions(-) diff --git a/genkit-tools/cli/src/mcp/docs.ts b/genkit-tools/cli/src/mcp/docs.ts index 50fc84a0da..ec33f1a00e 100644 --- a/genkit-tools/cli/src/mcp/docs.ts +++ b/genkit-tools/cli/src/mcp/docs.ts @@ -25,6 +25,7 @@ import path from 'path'; import z from 'zod'; import { version } from '../utils/version'; import { McpRunToolEvent } from './analytics.js'; +import { enrichToolDescription } from './utils.js'; const DOCS_URL = process.env.GENKIT_DOCS_BUNDLE_URL ?? @@ -71,24 +72,28 @@ export async function defineDocsTool(server: McpServer) { readFileSync(DOCS_BUNDLE_FILE_PATH, { encoding: 'utf8' }) ) as Record; + const inputSchema = { + language: z + .enum(['js', 'go', 'python']) + .describe('which language these docs are for (default js).') + .default('js'), + files: z + .array(z.string()) + .describe( + 'Specific docs files to look up. If empty or not specified an index will be returned. Always lookup index first for exact file names.' + ) + .optional(), + }; + server.registerTool( 'lookup_genkit_docs', { title: 'Genkit Docs', - description: + description: enrichToolDescription( 'Use this to look up documentation for the Genkit AI framework.', - inputSchema: { - language: z - .enum(['js', 'go', 'python']) - .describe('which language these docs are for (default js).') - .default('js'), - files: z - .array(z.string()) - .describe( - 'Specific docs files to look up. If empty or not specified an index will be returned. Always lookup index first for exact file names.' - ) - .optional(), - }, + inputSchema + ), + inputSchema, }, async ({ language, files }) => { await record(new McpRunToolEvent('lookup_genkit_docs')); diff --git a/genkit-tools/cli/src/mcp/flows.ts b/genkit-tools/cli/src/mcp/flows.ts index 16374e655f..e43e4e299d 100644 --- a/genkit-tools/cli/src/mcp/flows.ts +++ b/genkit-tools/cli/src/mcp/flows.ts @@ -21,18 +21,22 @@ import z from 'zod'; import { McpRunToolEvent } from './analytics.js'; import { McpToolOptions, + enrichToolDescription, getCommonSchema, resolveProjectRoot, } from './utils.js'; export function defineFlowTools(server: McpServer, options: McpToolOptions) { + const listFlowsSchema = getCommonSchema(options.explicitProjectRoot); server.registerTool( 'list_flows', { title: 'List Genkit Flows', - description: + description: enrichToolDescription( 'Use this to discover available Genkit flows or inspect the input schema of Genkit flows to know how to successfully call them.', - inputSchema: getCommonSchema(options.explicitProjectRoot), + listFlowsSchema + ), + inputSchema: listFlowsSchema, }, async (opts) => { await record(new McpRunToolEvent('list_flows')); @@ -64,20 +68,24 @@ export function defineFlowTools(server: McpServer, options: McpToolOptions) { } ); + const runFlowSchema = getCommonSchema(options.explicitProjectRoot, { + flowName: z.string().describe('name of the flow'), + input: z + .string() + .describe( + 'Flow input as JSON object encoded as string (it will be passed through `JSON.parse`). Must conform to the schema.' + ) + .optional(), + }); server.registerTool( 'run_flow', { title: 'Run Flow', - description: 'Runs the flow with the provided input', - inputSchema: getCommonSchema(options.explicitProjectRoot, { - flowName: z.string().describe('name of the flow'), - input: z - .string() - .describe( - 'Flow input as JSON object encoded as string (it will be passed through `JSON.parse`). Must conform to the schema.' - ) - .optional(), - }), + description: enrichToolDescription( + 'Runs the flow with the provided input', + runFlowSchema + ), + inputSchema: runFlowSchema, }, async (opts) => { await record(new McpRunToolEvent('run_flow')); diff --git a/genkit-tools/cli/src/mcp/runtime.ts b/genkit-tools/cli/src/mcp/runtime.ts index 1318b621fc..0919af40af 100644 --- a/genkit-tools/cli/src/mcp/runtime.ts +++ b/genkit-tools/cli/src/mcp/runtime.ts @@ -21,29 +21,35 @@ import { z } from 'zod'; import { McpRunToolEvent } from './analytics.js'; import { McpToolOptions, + enrichToolDescription, getCommonSchema, resolveProjectRoot, } from './utils.js'; export function defineRuntimeTools(server: McpServer, options: McpToolOptions) { + const startRuntimeSchema = getCommonSchema(options.explicitProjectRoot, { + command: z.string().describe('The command to run'), + args: z + .array(z.string()) + .describe( + 'The array of string args for the command to run. Eg: `["run", "dev"]`.' + ), + }); + server.registerTool( 'start_runtime', { title: 'Starts a Genkit runtime process', - description: `Use this to start a Genkit runtime process (This is typically the entry point to the users app). Once started, the runtime will be picked up by the \`genkit start\` command to power the Dev UI features like model and flow playgrounds. The inputSchema for this tool matches the function prototype for \`NodeJS.child_process.spawn\`. + description: enrichToolDescription( + `Use this to start a Genkit runtime process (This is typically the entry point to the users app). Once started, the runtime will be picked up by the \`genkit start\` command to power the Dev UI features like model and flow playgrounds. The inputSchema for this tool matches the function prototype for \`NodeJS.child_process.spawn\`. Examples: {command: "go", args: ["run", "main.go"]} {command: "npm", args: ["run", "dev"]} {command: "npm", args: ["run", "dev"], projectRoot: "path/to/project"}`, - inputSchema: getCommonSchema(options.explicitProjectRoot, { - command: z.string().describe('The command to run'), - args: z - .array(z.string()) - .describe( - 'The array of string args for the command to run. Eg: `["run", "dev"]`.' - ), - }), + startRuntimeSchema + ), + inputSchema: startRuntimeSchema, }, async (opts) => { await record(new McpRunToolEvent('start_runtime')); @@ -94,12 +100,16 @@ export function defineRuntimeTools(server: McpServer, options: McpToolOptions) { title: string, action: 'kill' | 'restart' ) => { + const inputSchema = getCommonSchema(options.explicitProjectRoot); server.registerTool( name, { title, - description: `Use this to ${action} an existing runtime that was started using the \`start_runtime\` tool`, - inputSchema: getCommonSchema(options.explicitProjectRoot), + description: enrichToolDescription( + `Use this to ${action} an existing runtime that was started using the \`start_runtime\` tool`, + inputSchema + ), + inputSchema, }, async (opts) => { await record(new McpRunToolEvent(name)); diff --git a/genkit-tools/cli/src/mcp/trace.ts b/genkit-tools/cli/src/mcp/trace.ts index a97fe4cf1c..10c91cfb16 100644 --- a/genkit-tools/cli/src/mcp/trace.ts +++ b/genkit-tools/cli/src/mcp/trace.ts @@ -21,23 +21,28 @@ import z from 'zod'; import { McpRunToolEvent } from './analytics.js'; import { McpToolOptions, + enrichToolDescription, getCommonSchema, resolveProjectRoot, } from './utils.js'; export function defineTraceTools(server: McpServer, options: McpToolOptions) { + const inputSchema = getCommonSchema(options.explicitProjectRoot, { + traceId: z + .string() + .describe( + 'trace id (typically returned after running a flow or other actions)' + ), + }); server.registerTool( 'get_trace', { title: 'Get Genkit Trace', - description: 'Returns the trace details', - inputSchema: getCommonSchema(options.explicitProjectRoot, { - traceId: z - .string() - .describe( - 'trace id (typically returned after running a flow or other actions)' - ), - }), + description: enrichToolDescription( + 'Returns the trace details.', + inputSchema + ), + inputSchema, }, async (opts) => { await record(new McpRunToolEvent('get_trace')); diff --git a/genkit-tools/cli/src/mcp/usage.ts b/genkit-tools/cli/src/mcp/usage.ts index 20bc1b3a0c..895e1bbd4f 100644 --- a/genkit-tools/cli/src/mcp/usage.ts +++ b/genkit-tools/cli/src/mcp/usage.ts @@ -19,24 +19,29 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp'; import { ContentBlock } from '@modelcontextprotocol/sdk/types'; import z from 'zod'; import { McpRunToolEvent } from './analytics.js'; +import { enrichToolDescription } from './utils.js'; import { GENKIT_CONTEXT as GoContext } from '../commands/init-ai-tools/context/go.js'; import { GENKIT_CONTEXT as JsContext } from '../commands/init-ai-tools/context/nodejs.js'; export async function defineUsageGuideTool(server: McpServer) { + const inputSchema = { + language: z + .enum(['js', 'go']) + .describe('which language this usage guide is for') + .default('js') + .optional(), + }; + server.registerTool( 'get_usage_guide', { title: 'Genkit Instructions', - description: + description: enrichToolDescription( 'Use this tool to look up the Genkit usage guide before implementing any AI feature', - inputSchema: { - language: z - .enum(['js', 'go']) - .describe('which language this usage guide is for') - .default('js') - .optional(), - }, + inputSchema + ), + inputSchema, }, async ({ language }) => { await record(new McpRunToolEvent('get_usage_guide')); diff --git a/genkit-tools/cli/src/mcp/utils.ts b/genkit-tools/cli/src/mcp/utils.ts index c0c5a20192..b08440476f 100644 --- a/genkit-tools/cli/src/mcp/utils.ts +++ b/genkit-tools/cli/src/mcp/utils.ts @@ -25,6 +25,9 @@ export interface McpToolOptions { manager: McpRuntimeManager; } +export const PROJECT_ROOT_DESCRIPTION = + 'The path to the current project root (a.k.a workspace directory or project directory)'; + export function getCommonSchema( explicitProjectRoot: boolean, shape: z.ZodRawShape = {} @@ -32,15 +35,27 @@ export function getCommonSchema( return !explicitProjectRoot ? shape : { - projectRoot: z - .string() - .describe( - 'The path to the current project root (a.k.a workspace directory or project directory)' - ), + projectRoot: z.string().describe(PROJECT_ROOT_DESCRIPTION), ...shape, }; } +export function enrichToolDescription( + baseDescription: string, + schema: z.ZodRawShape +): string { + const args = Object.entries(schema) + .map(([key, value]) => { + const desc = value.description; + return desc ? `${key}: ${desc}` : key; + }) + .join('; '); + + if (args.length === 0) return baseDescription; + + return `${baseDescription} Arguments: ${args}`; +} + export function resolveProjectRoot( explicitProjectRoot: boolean, opts: {