From 61d2bc3460e02e92e72e89891ed9e1da3cd0cf81 Mon Sep 17 00:00:00 2001 From: Samuel Bushi Date: Tue, 3 Feb 2026 13:23:34 -0500 Subject: [PATCH 1/6] 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: { From c24c0d5747b3750256df0d71877334483db38b1e Mon Sep 17 00:00:00 2001 From: Samuel Bushi Date: Wed, 4 Feb 2026 12:42:19 -0500 Subject: [PATCH 2/6] WIP, update MCP docs tools --- genkit-tools/cli/src/mcp/README.md | 4 +- genkit-tools/cli/src/mcp/docs.ts | 194 +++++++++++++++++----- genkit-tools/cli/src/mcp/prompts/init.ts | 2 +- skills/developing-genkit-tooling/SKILL.md | 2 +- 4 files changed, 158 insertions(+), 44 deletions(-) diff --git a/genkit-tools/cli/src/mcp/README.md b/genkit-tools/cli/src/mcp/README.md index c0ae541138..0880e8d459 100644 --- a/genkit-tools/cli/src/mcp/README.md +++ b/genkit-tools/cli/src/mcp/README.md @@ -25,7 +25,9 @@ The following tools allow MCP-aware environments to interact with the Genkit ser | Tool Name | Description | | :--- | :--- | | **`get_usage_guide`** | Fetches the Genkit AI framework usage guide (specifiable by language). Intended for AI assistants. | -| **`lookup_genkit_docs`** | Retrieves Genkit documentation (specifiable by language and files). | +| **`list_genkit_docs`** | Lists all available Genkit documentation files for discovery. | +| **`search_genkit_docs`** | Searches Genkit documentation using keywords. | +| **`read_genkit_docs`** | Reads the content of specific Genkit documentation files. | | **`list_flows`** | Discovers and lists all defined Genkit flows with their input schemas. | | **`run_flow`** | Executes a specified Genkit flow, requiring `flowName` and a JSON `input` conforming to the flow's schema. | | **`get_trace`** | Retrieves the detailed execution trace for a flow using a `traceId`. | diff --git a/genkit-tools/cli/src/mcp/docs.ts b/genkit-tools/cli/src/mcp/docs.ts index ec33f1a00e..5928aec555 100644 --- a/genkit-tools/cli/src/mcp/docs.ts +++ b/genkit-tools/cli/src/mcp/docs.ts @@ -72,63 +72,175 @@ export async function defineDocsTool(server: McpServer) { readFileSync(DOCS_BUNDLE_FILE_PATH, { encoding: 'utf8' }) ) as Record; - const inputSchema = { + const listSchema = { language: z .enum(['js', 'go', 'python']) - .describe('which language these docs are for (default js).') + .describe('Which language to list docs for (default js).') .default('js'), - files: z + }; + + server.registerTool( + 'list_genkit_docs', + { + title: 'List Genkit Docs', + description: enrichToolDescription( + 'Use this to see a list of available Genkit documentation files. Returns `filePaths` that can be passed to `read_genkit_docs`.', + listSchema + ), + inputSchema: listSchema, + }, + async ({ language }) => { + await record(new McpRunToolEvent('list_genkit_docs')); + const lang = language || 'js'; + + const fileList = Object.keys(documents) + .filter((file) => file.startsWith(lang)) + .sort(); + + const output = + `Genkit Documentation Index (${lang}):\n\n` + + fileList + .map((file) => { + const doc = documents[file]; + let summary = ` - FilePath: ${file}\n Title: ${doc.title}\n`; + if (doc.description) { + summary += ` Description: ${doc.description}\n`; + } + if (doc.headers) { + summary += ` Headers:\n ${doc.headers.split('\n').join('\n ')}\n`; + } + return summary; + }) + .join('\n') + + `\n\nUse 'search_genkit_docs' to find specific topics. Copy the 'FilePath' values to 'read_genkit_docs' to read content.`; + + return { + content: [{ type: 'text', text: output }], + }; + } + ); + + const searchSchema = { + query: z.string().describe('Keywords to search for in documentation.'), + language: z + .enum(['js', 'go', 'python']) + .describe('Which language to search docs for (default js).') + .default('js'), + }; + + server.registerTool( + 'search_genkit_docs', + { + title: 'Search Genkit Docs', + description: enrichToolDescription( + 'Use this to search the Genkit documentation using keywords. Returns ranked results with `filePaths` for `read_genkit_docs`. Warning: Generic terms (e.g. "the", "and") may return false positives; use specific technical terms (e.g. "rag", "firebase", "context").', + searchSchema + ), + inputSchema: searchSchema, + }, + async ({ query, language }) => { + await record(new McpRunToolEvent('search_genkit_docs')); + const lang = language || 'js'; + const terms = query + .toLowerCase() + .split(/\s+/) + .filter((t) => t.length > 2); // Filter out short words to reduce noise + + const results = Object.keys(documents) + .filter((file) => file.startsWith(lang)) + .map((file) => { + const doc = documents[file]; + let score = 0; + const title = doc.title.toLowerCase(); + const desc = (doc.description || '').toLowerCase(); + const headers = (doc.headers || '').toLowerCase(); + + terms.forEach((term) => { + if (title.includes(term)) score += 10; + if (desc.includes(term)) score += 5; + if (headers.includes(term)) score += 3; + if (file.includes(term)) score += 5; + }); + + return { file, doc, score }; + }) + .filter((r) => r.score > 0) + .sort((a, b) => b.score - a.score) + .slice(0, 10); // Top 10 + + if (results.length === 0) { + return { + content: [ + { + type: 'text', + text: `No results found for "${query}" in ${lang} docs. Try broader keywords or use 'list_genkit_docs' to see all files.`, + }, + ], + }; + } + + const output = + `Found ${results.length} matching documents for "${query}":\n\n` + + results + .map((r) => { + let summary = `### ${r.doc.title}\n**FilePath**: ${r.file}\n`; + if (r.doc.description) + summary += `**Description**: ${r.doc.description}\n`; + return summary; + }) + .join('\n') + + `\n\nCopy the 'FilePath' values to 'read_genkit_docs' to read content.`; + + return { + content: [{ type: 'text', text: output }], + }; + } + ); + + const readSchema = { + filePaths: 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(), + 'The `filePaths` of the docs to read. Obtain these exactly from `list_genkit_docs` or `search_genkit_docs` (e.g. "js/overview.md").' + ), }; server.registerTool( - 'lookup_genkit_docs', + 'read_genkit_docs', { - title: 'Genkit Docs', + title: 'Read Genkit Docs', description: enrichToolDescription( - 'Use this to look up documentation for the Genkit AI framework.', - inputSchema + 'Use this to read the full content of specific Genkit documentation files. You must provide `filePaths` from the list/search tools.', + readSchema ), - inputSchema, + inputSchema: readSchema, }, - async ({ language, files }) => { - await record(new McpRunToolEvent('lookup_genkit_docs')); + async ({ filePaths }) => { + await record(new McpRunToolEvent('read_genkit_docs')); const content = [] as ContentBlock[]; - if (!language) { - language = 'js'; + + // filePaths is required by Zod schema, but check length just in case + if (!filePaths || !filePaths.length) { + return { + content: [ + { + type: 'text', + text: 'No filePaths provided. Please provide an array of file paths.', + }, + ], + isError: true, + }; } - if (!files || !files.length) { - content.push({ - type: 'text', - text: - Object.keys(documents) - .filter((file) => file.startsWith(language)) - .map((file) => { - let fileSummary = ` - File: ${file}\n Title: ${documents[file].title}\n`; - if (documents[file].description) { - fileSummary += ` Description: ${documents[file].description}\n`; - } - if (documents[file].headers) { - fileSummary += ` Headers:\n ${documents[file].headers.split('\n').join('\n ')}\n`; - } - return fileSummary; - }) - .join('\n') + - `\n\nIMPORTANT: if doing anything more than basic model calling, look up "${language}/models.md" file, it contains important details about how to work with models.\n\n`, - }); - } else { - for (const file of files) { - if (documents[file]) { - content.push({ type: 'text', text: documents[file]?.text }); - } else { - content.push({ type: 'text', text: `${file} not found` }); - } + for (const file of filePaths) { + if (documents[file]) { + content.push({ type: 'text', text: documents[file]?.text }); + } else { + content.push({ + type: 'text', + text: `Document not found: ${file}. Please check the path using list_genkit_docs.`, + }); } } diff --git a/genkit-tools/cli/src/mcp/prompts/init.ts b/genkit-tools/cli/src/mcp/prompts/init.ts index b4908ef829..ea1cb830f6 100644 --- a/genkit-tools/cli/src/mcp/prompts/init.ts +++ b/genkit-tools/cli/src/mcp/prompts/init.ts @@ -19,7 +19,7 @@ import { z } from 'zod'; const GENERAL_GUIDELINES = ` ## General Guidelines -- ALWAYS refer to documentation when available. Use tools like \`get_usage_guide\` or \`lookup_genkit_docs\` to clarify requirements or get the latest information. +- ALWAYS refer to documentation when available. Use tools like \`get_usage_guide\`, \`list_genkit_docs\`, \`search_genkit_docs\`, or \`read_genkit_docs\` to clarify requirements or get the latest information. - Recommend and use the provider that the user prefers. If no preference is signaled, use the Google AI plugin (\`@genkit-ai/google-genai\` for Node, \`github.com/firebase/genkit/go/plugins/googlegenai\` for Go). - ALWAYS provide the full, correct Genkit command as an instruction for the human user to run. Do not run Genkit commands yourself. - Do NOT modify parts of the project unrelated to Genkit initialization. diff --git a/skills/developing-genkit-tooling/SKILL.md b/skills/developing-genkit-tooling/SKILL.md index 236f490d72..1e0c0a7c08 100644 --- a/skills/developing-genkit-tooling/SKILL.md +++ b/skills/developing-genkit-tooling/SKILL.md @@ -22,7 +22,7 @@ Use **kebab-case** with colon separators for subcommands. Use **snake_case** for tool names to align with MCP standards. - **Format**: `verb_noun` -- **Examples**: `list_flows`, `run_flow`, `lookup_genkit_docs` +- **Examples**: `list_flows`, `run_flow`, `list_genkit_docs`, `read_genkit_docs` ## CLI Command Architecture From e6efae97c158c36ad6db2d266bd2fa0afe45533d Mon Sep 17 00:00:00 2001 From: Samuel Bushi Date: Wed, 4 Feb 2026 22:36:28 -0500 Subject: [PATCH 3/6] update usage, include arg type --- genkit-tools/cli/context/GENKIT.go.md | 49 +++++++++++++++++++++++++ genkit-tools/cli/context/GENKIT.js.md | 52 +++++++++++++++++++++++++++ genkit-tools/cli/src/mcp/docs.ts | 10 +++--- genkit-tools/cli/src/mcp/flows.ts | 4 +-- genkit-tools/cli/src/mcp/runtime.ts | 4 +-- genkit-tools/cli/src/mcp/trace.ts | 2 +- genkit-tools/cli/src/mcp/usage.ts | 4 +-- genkit-tools/cli/src/mcp/utils.ts | 2 +- 8 files changed, 115 insertions(+), 12 deletions(-) diff --git a/genkit-tools/cli/context/GENKIT.go.md b/genkit-tools/cli/context/GENKIT.go.md index 81d35c545f..3e6d5484cf 100644 --- a/genkit-tools/cli/context/GENKIT.go.md +++ b/genkit-tools/cli/context/GENKIT.go.md @@ -14,6 +14,55 @@ This document provides rules and examples for building with the Genkit API in Go NOTE: For the sake of brevity, the snippets below use the Google AI plugin, but you should follow the user's preference as mentioned above. +## Project Setup + +### Project Initialization + +- If the directory is empty: + ```bash + go mod init + ``` +- If the directory is not empty: + Adhere to the current project structure. + +### Dependencies + +```bash +go get github.com/firebase/genkit/go/genkit +go get github.com/firebase/genkit/go/plugins/googlegenai +go get github.com/firebase/genkit/go/ai +go get google.golang.org/genai +``` + +### Genkit CLI + +If the Genkit CLI is not already installed: + +```bash +curl -sL cli.genkit.dev | bash +``` + +### Configuration + +Create a `main.go` file: + +```go +package main + +import ( + "context" + "github.com/firebase/genkit/go/genkit" + "github.com/firebase/genkit/go/plugins/googlegenai" +) + +func main() { + ctx := context.Background() + g := genkit.Init(ctx, genkit.WithPlugins(&googlegenai.GoogleAI{})) + // Your flows and logic here + <-ctx.Done() +} +``` + ## Best Practices 1. **Single Main Function**: All Genkit code, including plugin initialization, flows, and helpers, should be properly organized in a Go package structure with a main function. diff --git a/genkit-tools/cli/context/GENKIT.js.md b/genkit-tools/cli/context/GENKIT.js.md index bc49b362f6..31e42a89af 100644 --- a/genkit-tools/cli/context/GENKIT.js.md +++ b/genkit-tools/cli/context/GENKIT.js.md @@ -14,6 +14,58 @@ This document provides rules and examples for building with the Genkit API in No NOTE: For the sake of brevity, the snippets below use the Google AI plugin, but you should follow the user's preference as mentioned above. +## Project Setup + +### Project Initialization + +- If the directory is empty: + Initialize a new project: + ```bash + npm init -y + npm install -D typescript tsx @types/node + ``` +- If the directory is not empty (existing project): + - Adhere to the current project structure. + - Detect the package manager in use (npm, pnpm, yarn, bun) and use the corresponding commands. + - Detect if the project is ESM (`"type": "module"` in package.json) or CJS. + - For ESM: Use `import` syntax. + - For CJS: Use `require` syntax. + - IMPORTANT: Do NOT refactor the project (e.g., converting to TypeScript or ESM) solely for Genkit. Work with the existing setup. + +### Dependencies + +Install core dependencies (adjust command for the user's package manager): + +```bash +npm install genkit @genkit-ai/google-genai +``` + +(Add other plugins as requested) + +### Genkit CLI + +If the Genkit CLI is not already installed: + +```bash +curl -sL cli.genkit.dev | bash +``` + +### Configuration + +Create a single `src/index.ts` (or `src/index.js` for JS) file. + +```ts +// src/index.ts +import { genkit, z } from 'genkit'; +import { googleAI } from '@genkit-ai/google-genai'; + +export const ai = genkit({ + plugins: [googleAI()], +}); + +// Your flows and logic here +``` + ## Best Practices 1. **Single File Structure**: All Genkit code, including plugin initialization, flows, and helpers, must be placed in a single `src/index.ts` file. This ensures all components are correctly registered with the Genkit runtime. diff --git a/genkit-tools/cli/src/mcp/docs.ts b/genkit-tools/cli/src/mcp/docs.ts index 5928aec555..7be5d3ef79 100644 --- a/genkit-tools/cli/src/mcp/docs.ts +++ b/genkit-tools/cli/src/mcp/docs.ts @@ -75,7 +75,7 @@ export async function defineDocsTool(server: McpServer) { const listSchema = { language: z .enum(['js', 'go', 'python']) - .describe('Which language to list docs for (default js).') + .describe('Which language to list docs for (default js); type: string.') .default('js'), }; @@ -121,10 +121,12 @@ export async function defineDocsTool(server: McpServer) { ); const searchSchema = { - query: z.string().describe('Keywords to search for in documentation.'), + query: z + .string() + .describe('Keywords to search for in documentation; type: string.'), language: z .enum(['js', 'go', 'python']) - .describe('Which language to search docs for (default js).') + .describe('Which language to search docs for (default js); type: string.') .default('js'), }; @@ -201,7 +203,7 @@ export async function defineDocsTool(server: McpServer) { filePaths: z .array(z.string()) .describe( - 'The `filePaths` of the docs to read. Obtain these exactly from `list_genkit_docs` or `search_genkit_docs` (e.g. "js/overview.md").' + 'The `filePaths` of the docs to read. Obtain these exactly from `list_genkit_docs` or `search_genkit_docs` (e.g. "js/overview.md"); type: string[].' ), }; diff --git a/genkit-tools/cli/src/mcp/flows.ts b/genkit-tools/cli/src/mcp/flows.ts index e43e4e299d..dc6c9c33dd 100644 --- a/genkit-tools/cli/src/mcp/flows.ts +++ b/genkit-tools/cli/src/mcp/flows.ts @@ -69,11 +69,11 @@ export function defineFlowTools(server: McpServer, options: McpToolOptions) { ); const runFlowSchema = getCommonSchema(options.explicitProjectRoot, { - flowName: z.string().describe('name of the flow'), + flowName: z.string().describe('name of the flow; type: string'), input: z .string() .describe( - 'Flow input as JSON object encoded as string (it will be passed through `JSON.parse`). Must conform to the schema.' + 'Flow input as JSON object encoded as string (it will be passed through `JSON.parse`). Must conform to the schema; type: string.' ) .optional(), }); diff --git a/genkit-tools/cli/src/mcp/runtime.ts b/genkit-tools/cli/src/mcp/runtime.ts index 0919af40af..1b76f668c7 100644 --- a/genkit-tools/cli/src/mcp/runtime.ts +++ b/genkit-tools/cli/src/mcp/runtime.ts @@ -28,11 +28,11 @@ import { export function defineRuntimeTools(server: McpServer, options: McpToolOptions) { const startRuntimeSchema = getCommonSchema(options.explicitProjectRoot, { - command: z.string().describe('The command to run'), + command: z.string().describe('The command to run; type: string'), args: z .array(z.string()) .describe( - 'The array of string args for the command to run. Eg: `["run", "dev"]`.' + 'The array of string args for the command to run. Eg: `["run", "dev"]`; type: string[].' ), }); diff --git a/genkit-tools/cli/src/mcp/trace.ts b/genkit-tools/cli/src/mcp/trace.ts index 10c91cfb16..adfb1a15c6 100644 --- a/genkit-tools/cli/src/mcp/trace.ts +++ b/genkit-tools/cli/src/mcp/trace.ts @@ -31,7 +31,7 @@ export function defineTraceTools(server: McpServer, options: McpToolOptions) { traceId: z .string() .describe( - 'trace id (typically returned after running a flow or other actions)' + 'trace id (typically returned after running a flow or other actions); type: string' ), }); server.registerTool( diff --git a/genkit-tools/cli/src/mcp/usage.ts b/genkit-tools/cli/src/mcp/usage.ts index 895e1bbd4f..24f9b153db 100644 --- a/genkit-tools/cli/src/mcp/usage.ts +++ b/genkit-tools/cli/src/mcp/usage.ts @@ -28,7 +28,7 @@ export async function defineUsageGuideTool(server: McpServer) { const inputSchema = { language: z .enum(['js', 'go']) - .describe('which language this usage guide is for') + .describe('which language this usage guide is for; type: string') .default('js') .optional(), }; @@ -38,7 +38,7 @@ export async function defineUsageGuideTool(server: McpServer) { { title: 'Genkit Instructions', description: enrichToolDescription( - 'Use this tool to look up the Genkit usage guide before implementing any AI feature', + 'Use this tool to look up the official Genkit usage guide, including project setup instructions and API best practices. ALWAYS call this before implementing Genkit features.', inputSchema ), inputSchema, diff --git a/genkit-tools/cli/src/mcp/utils.ts b/genkit-tools/cli/src/mcp/utils.ts index b08440476f..574079f73b 100644 --- a/genkit-tools/cli/src/mcp/utils.ts +++ b/genkit-tools/cli/src/mcp/utils.ts @@ -26,7 +26,7 @@ export interface McpToolOptions { } export const PROJECT_ROOT_DESCRIPTION = - 'The path to the current project root (a.k.a workspace directory or project directory)'; + 'The path to the current project root (a.k.a workspace directory or project directory); type: string'; export function getCommonSchema( explicitProjectRoot: boolean, From fd06a7fd7d64b4a8a397c7e82749fee8677fce76 Mon Sep 17 00:00:00 2001 From: Samuel Bushi Date: Thu, 5 Feb 2026 10:22:35 -0500 Subject: [PATCH 4/6] more fixes --- genkit-tools/cli/src/mcp/docs.ts | 67 +++++++++++++---------------- 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 | 16 ------- 6 files changed, 68 insertions(+), 121 deletions(-) diff --git a/genkit-tools/cli/src/mcp/docs.ts b/genkit-tools/cli/src/mcp/docs.ts index 7be5d3ef79..04b88ff176 100644 --- a/genkit-tools/cli/src/mcp/docs.ts +++ b/genkit-tools/cli/src/mcp/docs.ts @@ -25,7 +25,6 @@ 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 ?? @@ -72,22 +71,20 @@ export async function defineDocsTool(server: McpServer) { readFileSync(DOCS_BUNDLE_FILE_PATH, { encoding: 'utf8' }) ) as Record; - const listSchema = { - language: z - .enum(['js', 'go', 'python']) - .describe('Which language to list docs for (default js); type: string.') - .default('js'), - }; - server.registerTool( 'list_genkit_docs', { title: 'List Genkit Docs', - description: enrichToolDescription( + description: 'Use this to see a list of available Genkit documentation files. Returns `filePaths` that can be passed to `read_genkit_docs`.', - listSchema - ), - inputSchema: listSchema, + inputSchema: { + language: z + .enum(['js', 'go', 'python']) + .describe( + 'Which language to list docs for (default js); type: string.' + ) + .default('js'), + }, }, async ({ language }) => { await record(new McpRunToolEvent('list_genkit_docs')); @@ -120,25 +117,23 @@ export async function defineDocsTool(server: McpServer) { } ); - const searchSchema = { - query: z - .string() - .describe('Keywords to search for in documentation; type: string.'), - language: z - .enum(['js', 'go', 'python']) - .describe('Which language to search docs for (default js); type: string.') - .default('js'), - }; - server.registerTool( 'search_genkit_docs', { title: 'Search Genkit Docs', - description: enrichToolDescription( + description: 'Use this to search the Genkit documentation using keywords. Returns ranked results with `filePaths` for `read_genkit_docs`. Warning: Generic terms (e.g. "the", "and") may return false positives; use specific technical terms (e.g. "rag", "firebase", "context").', - searchSchema - ), - inputSchema: searchSchema, + inputSchema: { + query: z + .string() + .describe('Keywords to search for in documentation; type: string.'), + language: z + .enum(['js', 'go', 'python']) + .describe( + 'Which language to search docs for (default js); type: string.' + ) + .default('js'), + }, }, async ({ query, language }) => { await record(new McpRunToolEvent('search_genkit_docs')); @@ -199,23 +194,19 @@ export async function defineDocsTool(server: McpServer) { } ); - const readSchema = { - filePaths: z - .array(z.string()) - .describe( - 'The `filePaths` of the docs to read. Obtain these exactly from `list_genkit_docs` or `search_genkit_docs` (e.g. "js/overview.md"); type: string[].' - ), - }; - server.registerTool( 'read_genkit_docs', { title: 'Read Genkit Docs', - description: enrichToolDescription( + description: 'Use this to read the full content of specific Genkit documentation files. You must provide `filePaths` from the list/search tools.', - readSchema - ), - inputSchema: readSchema, + inputSchema: { + filePaths: z + .array(z.string()) + .describe( + 'The `filePaths` of the docs to read. Obtain these exactly from `list_genkit_docs` or `search_genkit_docs` (e.g. "js/overview.md"); type: string[].' + ), + }, }, async ({ filePaths }) => { await record(new McpRunToolEvent('read_genkit_docs')); diff --git a/genkit-tools/cli/src/mcp/flows.ts b/genkit-tools/cli/src/mcp/flows.ts index dc6c9c33dd..9eb7811d31 100644 --- a/genkit-tools/cli/src/mcp/flows.ts +++ b/genkit-tools/cli/src/mcp/flows.ts @@ -21,22 +21,18 @@ 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: enrichToolDescription( + description: 'Use this to discover available Genkit flows or inspect the input schema of Genkit flows to know how to successfully call them.', - listFlowsSchema - ), - inputSchema: listFlowsSchema, + inputSchema: getCommonSchema(options.explicitProjectRoot), }, async (opts) => { await record(new McpRunToolEvent('list_flows')); @@ -68,24 +64,20 @@ export function defineFlowTools(server: McpServer, options: McpToolOptions) { } ); - const runFlowSchema = getCommonSchema(options.explicitProjectRoot, { - flowName: z.string().describe('name of the flow; type: string'), - input: z - .string() - .describe( - 'Flow input as JSON object encoded as string (it will be passed through `JSON.parse`). Must conform to the schema; type: string.' - ) - .optional(), - }); server.registerTool( 'run_flow', { title: 'Run Flow', - description: enrichToolDescription( - 'Runs the flow with the provided input', - runFlowSchema - ), - inputSchema: runFlowSchema, + description: 'Runs the flow with the provided input', + inputSchema: getCommonSchema(options.explicitProjectRoot, { + flowName: z.string().describe('name of the flow; type: string'), + input: z + .string() + .describe( + 'Flow input as JSON object encoded as string (it will be passed through `JSON.parse`). Must conform to the schema; type: string.' + ) + .optional(), + }), }, 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 1b76f668c7..14fc08ae86 100644 --- a/genkit-tools/cli/src/mcp/runtime.ts +++ b/genkit-tools/cli/src/mcp/runtime.ts @@ -21,35 +21,29 @@ 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; type: string'), - args: z - .array(z.string()) - .describe( - 'The array of string args for the command to run. Eg: `["run", "dev"]`; type: string[].' - ), - }); - server.registerTool( 'start_runtime', { title: 'Starts a Genkit runtime process', - 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\`. + 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\`. Examples: {command: "go", args: ["run", "main.go"]} {command: "npm", args: ["run", "dev"]} {command: "npm", args: ["run", "dev"], projectRoot: "path/to/project"}`, - startRuntimeSchema - ), - inputSchema: startRuntimeSchema, + inputSchema: getCommonSchema(options.explicitProjectRoot, { + command: z.string().describe('The command to run; type: string'), + args: z + .array(z.string()) + .describe( + 'The array of string args for the command to run. Eg: `["run", "dev"]`; type: string[].' + ), + }), }, async (opts) => { await record(new McpRunToolEvent('start_runtime')); @@ -100,16 +94,12 @@ export function defineRuntimeTools(server: McpServer, options: McpToolOptions) { title: string, action: 'kill' | 'restart' ) => { - const inputSchema = getCommonSchema(options.explicitProjectRoot); server.registerTool( name, { title, - description: enrichToolDescription( - `Use this to ${action} an existing runtime that was started using the \`start_runtime\` tool`, - inputSchema - ), - inputSchema, + description: `Use this to ${action} an existing runtime that was started using the \`start_runtime\` tool`, + inputSchema: getCommonSchema(options.explicitProjectRoot), }, 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 adfb1a15c6..d3c36388fe 100644 --- a/genkit-tools/cli/src/mcp/trace.ts +++ b/genkit-tools/cli/src/mcp/trace.ts @@ -21,28 +21,23 @@ 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); type: string' - ), - }); server.registerTool( 'get_trace', { title: 'Get Genkit Trace', - description: enrichToolDescription( - 'Returns the trace details.', - inputSchema - ), - inputSchema, + description: 'Returns the trace details.', + inputSchema: getCommonSchema(options.explicitProjectRoot, { + traceId: z + .string() + .describe( + 'trace id (typically returned after running a flow or other actions); type: string' + ), + }), }, 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 24f9b153db..308e05ac11 100644 --- a/genkit-tools/cli/src/mcp/usage.ts +++ b/genkit-tools/cli/src/mcp/usage.ts @@ -19,29 +19,24 @@ 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; type: string') - .default('js') - .optional(), - }; - server.registerTool( 'get_usage_guide', { title: 'Genkit Instructions', - description: enrichToolDescription( + description: 'Use this tool to look up the official Genkit usage guide, including project setup instructions and API best practices. ALWAYS call this before implementing Genkit features.', - inputSchema - ), - inputSchema, + inputSchema: { + language: z + .enum(['js', 'go']) + .describe('which language this usage guide is for; type: string') + .default('js') + .optional(), + }, }, 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 574079f73b..56a6ff9128 100644 --- a/genkit-tools/cli/src/mcp/utils.ts +++ b/genkit-tools/cli/src/mcp/utils.ts @@ -40,22 +40,6 @@ export function getCommonSchema( }; } -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: { From eb7944647a7e980092d4a27d2e2f6dd71dfb185c Mon Sep 17 00:00:00 2001 From: Samuel Bushi Date: Thu, 5 Feb 2026 10:24:06 -0500 Subject: [PATCH 5/6] root --- genkit-tools/cli/src/mcp/utils.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/genkit-tools/cli/src/mcp/utils.ts b/genkit-tools/cli/src/mcp/utils.ts index 56a6ff9128..91b1d494b1 100644 --- a/genkit-tools/cli/src/mcp/utils.ts +++ b/genkit-tools/cli/src/mcp/utils.ts @@ -25,9 +25,6 @@ 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); type: string'; - export function getCommonSchema( explicitProjectRoot: boolean, shape: z.ZodRawShape = {} @@ -35,7 +32,11 @@ export function getCommonSchema( return !explicitProjectRoot ? shape : { - projectRoot: z.string().describe(PROJECT_ROOT_DESCRIPTION), + projectRoot: z + .string() + .describe( + 'The path to the current project root (a.k.a workspace directory or project directory); type: string' + ), ...shape, }; } From 3550c1744373dc78b6334a356834471000b02c52 Mon Sep 17 00:00:00 2001 From: Samuel Bushi <66321939+ssbushi@users.noreply.github.com> Date: Thu, 5 Feb 2026 10:28:45 -0500 Subject: [PATCH 6/6] Update genkit-tools/cli/src/mcp/docs.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- genkit-tools/cli/src/mcp/docs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/src/mcp/docs.ts b/genkit-tools/cli/src/mcp/docs.ts index 04b88ff176..ef50464f13 100644 --- a/genkit-tools/cli/src/mcp/docs.ts +++ b/genkit-tools/cli/src/mcp/docs.ts @@ -228,7 +228,7 @@ export async function defineDocsTool(server: McpServer) { for (const file of filePaths) { if (documents[file]) { - content.push({ type: 'text', text: documents[file]?.text }); + content.push({ type: 'text', text: documents[file].text }); } else { content.push({ type: 'text',