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/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 50fc84a0da..ef50464f13 100644 --- a/genkit-tools/cli/src/mcp/docs.ts +++ b/genkit-tools/cli/src/mcp/docs.ts @@ -72,58 +72,168 @@ export async function defineDocsTool(server: McpServer) { ) as Record; server.registerTool( - 'lookup_genkit_docs', + 'list_genkit_docs', { - title: 'Genkit Docs', + title: 'List Genkit Docs', description: - 'Use this to look up documentation for the Genkit AI framework.', + 'Use this to see a list of available Genkit documentation files. Returns `filePaths` that can be passed to `read_genkit_docs`.', inputSchema: { language: z .enum(['js', 'go', 'python']) - .describe('which language these docs are for (default js).') + .describe( + 'Which language to list docs for (default js); type: string.' + ) .default('js'), - files: z - .array(z.string()) + }, + }, + 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 }], + }; + } + ); + + server.registerTool( + 'search_genkit_docs', + { + title: 'Search Genkit Docs', + 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").', + inputSchema: { + query: z + .string() + .describe('Keywords to search for in documentation; type: string.'), + language: z + .enum(['js', 'go', 'python']) .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.' + 'Which language to search docs for (default js); type: string.' ) - .optional(), + .default('js'), }, }, - async ({ language, files }) => { - await record(new McpRunToolEvent('lookup_genkit_docs')); + 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 }], + }; + } + ); + + server.registerTool( + 'read_genkit_docs', + { + title: 'Read Genkit Docs', + description: + 'Use this to read the full content of specific Genkit documentation files. You must provide `filePaths` from the list/search tools.', + 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')); 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/flows.ts b/genkit-tools/cli/src/mcp/flows.ts index 16374e655f..9eb7811d31 100644 --- a/genkit-tools/cli/src/mcp/flows.ts +++ b/genkit-tools/cli/src/mcp/flows.ts @@ -70,11 +70,11 @@ export function defineFlowTools(server: McpServer, options: McpToolOptions) { title: 'Run Flow', description: 'Runs the flow with the provided input', inputSchema: 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/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/genkit-tools/cli/src/mcp/runtime.ts b/genkit-tools/cli/src/mcp/runtime.ts index 1318b621fc..14fc08ae86 100644 --- a/genkit-tools/cli/src/mcp/runtime.ts +++ b/genkit-tools/cli/src/mcp/runtime.ts @@ -37,11 +37,11 @@ export function defineRuntimeTools(server: McpServer, options: McpToolOptions) { {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'), + 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 a97fe4cf1c..d3c36388fe 100644 --- a/genkit-tools/cli/src/mcp/trace.ts +++ b/genkit-tools/cli/src/mcp/trace.ts @@ -30,12 +30,12 @@ export function defineTraceTools(server: McpServer, options: McpToolOptions) { 'get_trace', { title: 'Get Genkit Trace', - description: 'Returns the trace details', + description: 'Returns the trace details.', inputSchema: getCommonSchema(options.explicitProjectRoot, { 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' ), }), }, diff --git a/genkit-tools/cli/src/mcp/usage.ts b/genkit-tools/cli/src/mcp/usage.ts index 20bc1b3a0c..308e05ac11 100644 --- a/genkit-tools/cli/src/mcp/usage.ts +++ b/genkit-tools/cli/src/mcp/usage.ts @@ -29,11 +29,11 @@ export async function defineUsageGuideTool(server: McpServer) { { title: 'Genkit Instructions', description: - '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: { 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(), }, diff --git a/genkit-tools/cli/src/mcp/utils.ts b/genkit-tools/cli/src/mcp/utils.ts index c0c5a20192..91b1d494b1 100644 --- a/genkit-tools/cli/src/mcp/utils.ts +++ b/genkit-tools/cli/src/mcp/utils.ts @@ -35,7 +35,7 @@ export function getCommonSchema( projectRoot: z .string() .describe( - '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' ), ...shape, }; 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