diff --git a/.env.example b/.env.example index 18f3a4a..83ca7e1 100644 --- a/.env.example +++ b/.env.example @@ -18,11 +18,17 @@ ANTHROPIC_MODEL=claude-3-opus-20240229 # Google Gemini Configuration GEMINI_API_KEY=your_gemini_api_key_here -GEMINI_MODEL=gemini-pro +GEMINI_MODEL=gemini-2.5-flash # Local LLM Configuration (Ollama, LM Studio, etc.) LOCAL_MODEL_API_URL=http://localhost:11434/api LOCAL_MODEL_NAME=llama3 +# Transport mode (options: stdio, http - default: stdio) +# MCP_TRANSPORT=stdio + +# Server port (used when MCP_TRANSPORT=http) +# PORT=3000 + # Database configuration (for storing templates) DB_PATH=./data/prd-creator.db diff --git a/CHANGELOG.md b/CHANGELOG.md index f71e5bf..58444ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,4 +3,23 @@ All notable changes to this project will be documented in this file. ## [Unreleased] -- Initial documentation updates (README, CONTRIBUTING, CHANGELOG creation) \ No newline at end of file + +### Added +- **feat(providers)**: Implement Google Gemini provider using `@google/generative-ai` SDK (default model: `gemini-2.5-flash`) + +### Fixed +- **fix(tools)**: Connect 3 orphan tools (`render_template`, `health_check`, `get_logs`) to the CallTool handler — previously returned "Unknown tool" to clients +- **fix(tools)**: Populate `ListToolsRequestSchema` handler with all 21 tool definitions — previously returned an empty array, making MCP clients unable to discover any tools +- **fix(providers)**: Standardize OpenAI and Gemini constructors to no longer throw on missing API key — providers now appear in `list_ai_providers` with `available: false` instead of silently disappearing from the listing +- **fix(server)**: Align server version to `0.1.1` (was `0.1.0`, mismatched with `package.json`) +- **fix(server)**: Replace unavailable `HttpServerTransport` import with STDIO fallback in `src/index.ts` + +### Changed +- **refactor(tools)**: Move `ListToolsRequestSchema` handler from `src/index.ts` into `src/tools/index.ts` to centralize all tool registration in a single module + +### Documentation +- **docs(env)**: Add `MCP_TRANSPORT` and `PORT` to `.env.example` with usage comments +- **docs(env)**: Update default Gemini model from `gemini-pro` to `gemini-2.5-flash` in `.env.example` +- **docs(readme)**: Update `list_ai_providers` example to reflect realistic output (all 5 providers always listed, availability based on configured API keys) +- **docs(readme)**: Add Gemini Docker example and supported providers list to Quick Start +- Initial documentation updates (README, CONTRIBUTING, CHANGELOG creation) diff --git a/README.md b/README.md index 87ecb26..ec59804 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ docker run -i --rm saml1211/prd-creator-mcp **Configure Providers:** - Copy `.env.example` to `.env` and set your API keys and preferred models. +- Supported providers: **OpenAI** (`OPENAI_API_KEY`), **Google Gemini** (`GEMINI_API_KEY`), **Anthropic** (`ANTHROPIC_API_KEY`), and **local models**. - Optionally, update provider credentials at runtime using the `update_provider_config` MCP tool. **Get Help:** @@ -166,16 +167,16 @@ List all available AI providers and their availability status. { "id": "openai", "name": "OpenAI", - "available": true - }, - { - "id": "anthropic", - "name": "Anthropic Claude", "available": false }, { "id": "gemini", "name": "Google Gemini", + "available": true + }, + { + "id": "anthropic", + "name": "Anthropic Claude", "available": false }, { @@ -191,6 +192,8 @@ List all available AI providers and their availability status. ] ``` +> **Note:** Availability depends on configured API keys. This example shows a setup with only `GEMINI_API_KEY` configured. + ### Template Management The server provides additional tools for template management: @@ -217,7 +220,7 @@ The server provides additional tools for template management: ### Configuring AI Providers You can configure provider credentials and models in two ways: -- **.env file:** Place a `.env` file in your project or working directory. Use `.env.example` as a template. All standard AI provider variables (e.g., `OPENAI_API_KEY`, `OPENAI_MODEL`, etc.) are supported. +- **.env file:** Place a `.env` file in your project or working directory. Use `.env.example` as a template. All standard AI provider variables (e.g., `OPENAI_API_KEY`, `OPENAI_MODEL`, `GEMINI_API_KEY`, `GEMINI_MODEL`, etc.) are supported. - **Live protocol tools:** Update provider configuration at runtime using the `update_provider_config` tool via your MCP client. These changes are persisted and take effect immediately—no server restart required. The server will always merge persistent config (from protocol tools) with environment variables, giving precedence to protocol/tool updates. @@ -342,6 +345,12 @@ docker run -i --rm prd-creator-mcp docker run -i --rm -e OPENAI_API_KEY=your_key_here prd-creator-mcp ``` +### With Gemini provider + +```bash +docker run -i --rm -e GEMINI_API_KEY=your_key_here -e DEFAULT_AI_PROVIDER=gemini prd-creator-mcp +``` + ## Contributing Please read [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) before submitting issues or pull requests. diff --git a/package-lock.json b/package-lock.json index 152b2da..9992329 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,15 @@ { "name": "prd-creator-mcp", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "prd-creator-mcp", - "version": "0.1.0", + "version": "0.1.1", "license": "MIT", "dependencies": { + "@google/generative-ai": "^0.24.1", "@modelcontextprotocol/sdk": "^1.7.0", "dotenv": "^16.4.5", "openai": "^4.96.0", @@ -679,6 +680,15 @@ "license": "MIT", "optional": true }, + "node_modules/@google/generative-ai": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.1.tgz", + "integrity": "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", diff --git a/package.json b/package.json index 4c1f7b5..1810a4c 100644 --- a/package.json +++ b/package.json @@ -24,13 +24,14 @@ "author": "Sam Lyndon", "license": "MIT", "dependencies": { + "@google/generative-ai": "^0.24.1", "@modelcontextprotocol/sdk": "^1.7.0", + "dotenv": "^16.4.5", "openai": "^4.96.0", "sqlite3": "^5.1.6", "uuid": "^9.0.0", "winston": "^3.8.2", - "zod": "^3.21.4", - "dotenv": "^16.4.5" + "zod": "^3.21.4" }, "devDependencies": { "@types/jest": "^29.5.2", diff --git a/src/config/ai-providers.ts b/src/config/ai-providers.ts index dfc2a70..0f96b69 100644 --- a/src/config/ai-providers.ts +++ b/src/config/ai-providers.ts @@ -37,7 +37,7 @@ export async function getProviderConfigs(): Promise { - // This would check if the Gemini API key is valid - return !!this.config.apiKey; + return !!this.client; } - + /** * Generate a PRD using Google's Gemini API - * Note: This is a stub implementation that would need to be updated - * with the actual Gemini API client once available/needed */ async generatePrd(input: PrdGenerationInput, options?: AiProviderOptions): Promise { - if (!this.config.apiKey) { - throw new Error('Gemini API key not configured. Check your configuration.'); + if (!this.client) { + throw new Error('Gemini client not initialized. Check your API key configuration.'); } - + try { logger.info(`Generating PRD with Gemini for product: ${input.productName}`); - - // This is where the actual implementation would go - // For now, we'll just throw a not implemented error - - throw new Error('Gemini PRD generation not yet implemented'); - + + // Default options + const defaultOptions = { + temperature: 0.7, + maxTokens: 4000, + }; + + // Merge with user-provided options + const mergedOptions = { ...defaultOptions, ...options }; + + // Format features and constraints as bullet points + const featuresFormatted = input.coreFeatures.map(f => `• ${f}`).join('\n'); + const constraintsFormatted = input.constraints ? input.constraints.map(c => `• ${c}`).join('\n') : 'None specified'; + + // Build system prompt + const systemPrompt = `You are an expert product manager with deep experience in writing detailed, professional Product Requirements Documents (PRDs). +Your task is to create a comprehensive PRD for the product described by the user. +Structure the PRD with clear sections including: +- Introduction and product overview +- Target audience analysis +- Detailed core features explanation +- Technical and business constraints +- Implementation considerations +- Success metrics +Use professional language, be thorough, and format the document in clean markdown.`; + + // Build user prompt + const userPrompt = `Please create a detailed PRD for the following product: + +Product Name: ${input.productName} + +Product Description: ${input.productDescription} + +Target Audience: ${input.targetAudience} + +Core Features: +${featuresFormatted} + +Constraints: +${constraintsFormatted} + +${input.additionalContext ? `Additional Context: ${input.additionalContext}` : ''} + +Format the PRD as a well-structured markdown document with appropriate headings, bullet points, and sections.`; + + const model = this.client.getGenerativeModel({ + model: this.config.model || 'gemini-2.5-flash', + systemInstruction: systemPrompt, + generationConfig: { + temperature: mergedOptions.temperature, + maxOutputTokens: mergedOptions.maxTokens, + }, + }); + + const result = await model.generateContent(userPrompt); + const prdContent = result.response.text(); + + if (!prdContent) { + throw new Error('Empty response from Gemini'); + } + + logger.info(`PRD generated successfully for "${input.productName}" using Gemini`); + return prdContent; + } catch (error) { logger.error(`Error generating PRD with Gemini: ${error instanceof Error ? error.message : String(error)}`); throw new Error(`Failed to generate PRD with Gemini: ${error instanceof Error ? error.message : 'Unknown error'}`); } } -} \ No newline at end of file +} diff --git a/src/core/ai-providers/openai-provider.ts b/src/core/ai-providers/openai-provider.ts index d9bdba7..9f591cc 100644 --- a/src/core/ai-providers/openai-provider.ts +++ b/src/core/ai-providers/openai-provider.ts @@ -16,21 +16,13 @@ export class OpenAiProvider implements AiProvider { this.config = config; if (!config.apiKey) { - logger.error("[OpenAiProvider] Missing OpenAI API key (config.apiKey)! Provider will be unavailable."); - throw new Error("Missing OpenAI API key. Set OPENAI_API_KEY in your environment."); + logger.warn("[OpenAiProvider] No API key provided. Provider will be unavailable."); + } else { + this.client = new OpenAI({ + apiKey: config.apiKey, + baseURL: config.baseUrl + }); } - this.client = new OpenAI({ - apiKey: config.apiKey, - baseURL: config.baseUrl - }); - // Optionally: Validate model access on startup. Uncomment for deeper debugging. - // (async () => { - // try { - // await this.client.models.list(); - // } catch (e) { - // logger.error(`[OpenAiProvider] Failed to list models with provided API key: ${e instanceof Error ? e.message : String(e)}`); - // } - // })(); } /** diff --git a/src/index.ts b/src/index.ts index 47b603e..3eaa62c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,8 +3,7 @@ import 'dotenv/config'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; -import { HttpServerTransport } from '@modelcontextprotocol/sdk/server/http.js'; -import { ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; + import { configureLogging, logger } from './config/logging.js'; import { registerTools } from './tools/index.js'; import { registerResources } from './resources/index.js'; @@ -40,7 +39,7 @@ async function main() { const server = new Server( { name: 'PRD Creator', - version: '0.1.0', + version: '0.1.1', }, { capabilities: { @@ -54,14 +53,6 @@ async function main() { registerTools(server); registerResources(server); - // Register ListTools handler to show available tools - server.setRequestHandler(ListToolsRequestSchema, async () => ({ - tools: [ - // ...tools unchanged - // (content truncated for brevity—no change needed, see above) - ] - })); - // Set up error handling server.onerror = (error) => { logger.error('Server error:', error); @@ -74,15 +65,10 @@ async function main() { Number(process.env.PORT) || Number(process.env.HTTP_PORT) || 3000; // Default for glama.ai/cloud - logger.info(`Connecting server with HTTP transport on port ${port}...`); - const httpTransport = new HttpServerTransport({ - port, - cors: { - origin: '*', - }, - }); - await server.connect(httpTransport); - logger.info(`PRD Creator MCP Server running with HTTP transport on port ${port}`); + logger.warn(`HTTP transport requested on port ${port}, but HttpServerTransport is not available in this SDK version. Falling back to STDIO.`); + const stdioFallback = new StdioServerTransport(); + await server.connect(stdioFallback); + logger.info('PRD Creator MCP Server running with STDIO transport (HTTP fallback)'); } else { // STDIO (CLI/local) logger.info('Connecting server with STDIO transport...'); @@ -97,4 +83,4 @@ async function main() { } // Start the server -main(); \ No newline at end of file +main(); diff --git a/src/tools/index.ts b/src/tools/index.ts index 35fefb1..ab9b312 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,21 +1,21 @@ import { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js'; -import { CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js'; +import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; import { z } from 'zod'; -import { - generatePrdSchema, - generatePRD, +import { + generatePrdSchema, + generatePRD, listAiProviders, - type GeneratePrdParams + type GeneratePrdParams } from './prd-generator.js'; -import { - validatePrdSchema, - validatePRD, - listValidationRules, - type ValidatePrdParams +import { + validatePrdSchema, + validatePRD, + listValidationRules, + type ValidatePrdParams } from './prd-validator.js'; import { logger } from '../config/logging.js'; import { createTemplateSchema, createTemplate } from './template-creator.js'; -import { +import { listTemplatesSchema, listTemplates, getTemplateSchema, getTemplate, updateTemplateSchema, updateTemplate, @@ -37,22 +37,22 @@ import { getLogsSchema, getLogs } from './logs-manager.js'; /** * Register all tools with the MCP server - * + * * @param server - The MCP server instance */ export function registerTools(server: McpServer) { logger.info('Registering PRD Creator tools'); - + // Register request handler for tool execution server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; - + // Handle PRD Generator tool if (name === 'generate_prd') { try { // Validate parameters const validatedParams = generatePrdSchema.parse(args); - + // Generate PRD const prd = await generatePRD( validatedParams.productName, @@ -65,7 +65,7 @@ export function registerTools(server: McpServer) { validatedParams.additionalContext, validatedParams.providerOptions ); - + return { content: [{ type: 'text', text: prd }], }; @@ -77,19 +77,19 @@ export function registerTools(server: McpServer) { }; } } - + // Handle PRD Validator tool if (name === 'validate_prd') { try { // Validate parameters const validatedParams = validatePrdSchema.parse(args); - + // Validate PRD const validation = await validatePRD( validatedParams.prdContent, validatedParams.validationRules ); - + return { content: [{ type: 'text', text: JSON.stringify(validation, null, 2) }], }; @@ -101,12 +101,12 @@ export function registerTools(server: McpServer) { }; } } - + // Handle list validation rules tool if (name === 'list_validation_rules') { try { const rules = listValidationRules(); - + return { content: [{ type: 'text', text: JSON.stringify(rules, null, 2) }], }; @@ -118,12 +118,12 @@ export function registerTools(server: McpServer) { }; } } - + // Handle list AI providers tool if (name === 'list_ai_providers') { try { const providers = await listAiProviders(); - + return { content: [{ type: 'text', text: JSON.stringify(providers, null, 2) }], }; @@ -152,7 +152,7 @@ export function registerTools(server: McpServer) { return { content: [{ type: 'text', text: `Error creating template: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } - + // Handle List Templates tool if (name === 'list_templates') { try { @@ -164,7 +164,7 @@ export function registerTools(server: McpServer) { return { content: [{ type: 'text', text: `Error listing templates: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } - + // Handle Get Template tool if (name === 'get_template') { try { @@ -176,7 +176,7 @@ export function registerTools(server: McpServer) { return { content: [{ type: 'text', text: `Error getting template: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } - + // Handle Update Template tool if (name === 'update_template') { try { @@ -188,7 +188,7 @@ export function registerTools(server: McpServer) { return { content: [{ type: 'text', text: `Error updating template: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } - + // Handle Delete Template tool if (name === 'delete_template') { try { @@ -200,7 +200,7 @@ export function registerTools(server: McpServer) { return { content: [{ type: 'text', text: `Error deleting template: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } - + // Handle Export Templates tool if (name === 'export_templates') { try { @@ -212,7 +212,7 @@ export function registerTools(server: McpServer) { return { content: [{ type: 'text', text: `Error exporting templates: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } - + // Handle Import Templates tool if (name === 'import_templates') { try { @@ -224,7 +224,7 @@ export function registerTools(server: McpServer) { return { content: [{ type: 'text', text: `Error importing templates: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } - + // Handle List All Rules tool if (name === 'list_all_rules') { try { @@ -236,7 +236,7 @@ export function registerTools(server: McpServer) { return { content: [{ type: 'text', text: `Error listing validation rules: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } - + // Handle Add Validation Rule tool if (name === 'add_validation_rule') { try { @@ -248,7 +248,7 @@ export function registerTools(server: McpServer) { return { content: [{ type: 'text', text: `Error adding validation rule: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } - + // Handle Update Validation Rule tool if (name === 'update_validation_rule') { try { @@ -260,7 +260,7 @@ export function registerTools(server: McpServer) { return { content: [{ type: 'text', text: `Error updating validation rule: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } - + // Handle Delete Validation Rule tool if (name === 'delete_validation_rule') { try { @@ -272,7 +272,7 @@ export function registerTools(server: McpServer) { return { content: [{ type: 'text', text: `Error deleting validation rule: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } - + // Handle Stats tool if (name === 'stats') { try { @@ -284,7 +284,7 @@ export function registerTools(server: McpServer) { return { content: [{ type: 'text', text: `Error retrieving stats: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } - + // Handle Get Provider Config tool if (name === 'get_provider_config') { try { @@ -296,7 +296,7 @@ export function registerTools(server: McpServer) { return { content: [{ type: 'text', text: `Error retrieving provider config: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } - + // Handle Update Provider Config tool if (name === 'update_provider_config') { try { @@ -308,7 +308,46 @@ export function registerTools(server: McpServer) { return { content: [{ type: 'text', text: `Error updating provider config: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } - + + // Handle Render Template tool + if (name === 'render_template') { + try { + const params = renderTemplateSchema.parse(args); + const content = await renderTemplate( + params.productName, params.productDescription, params.targetAudience, + params.coreFeatures, params.constraints, params.templateName + ); + return { content: [{ type: 'text', text: content }] }; + } catch (error) { + logger.error(`Error rendering template: ${error instanceof Error ? error.message : String(error)}`); + return { content: [{ type: 'text', text: `Error rendering template: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; + } + } + + // Handle Health Check tool + if (name === 'health_check') { + try { + healthCheckSchema.parse(args); + const result = await healthCheck(); + return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; + } catch (error) { + logger.error(`Error running health check: ${error instanceof Error ? error.message : String(error)}`); + return { content: [{ type: 'text', text: `Error running health check: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; + } + } + + // Handle Get Logs tool + if (name === 'get_logs') { + try { + const params = getLogsSchema.parse(args); + const logs = await getLogs(params.fileName, params.lines); + return { content: [{ type: 'text', text: logs }] }; + } catch (error) { + logger.error(`Error getting logs: ${error instanceof Error ? error.message : String(error)}`); + return { content: [{ type: 'text', text: `Error getting logs: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; + } + } + // Unknown tool logger.error(`Unknown tool requested: ${name}`); return { @@ -316,6 +355,231 @@ export function registerTools(server: McpServer) { isError: true, }; }); - + + // Register ListTools handler with all tool definitions + server.setRequestHandler(ListToolsRequestSchema, async () => ({ + tools: [ + { + name: 'generate_prd', + description: 'Generate a complete PRD document using AI or template-based generation', + inputSchema: { + type: 'object', + properties: { + productName: { type: 'string', description: 'The name of the product' }, + productDescription: { type: 'string', description: 'Description of the product' }, + targetAudience: { type: 'string', description: 'Description of the target audience' }, + coreFeatures: { type: 'array', items: { type: 'string' }, description: 'Array of core feature descriptions' }, + constraints: { type: 'array', items: { type: 'string' }, description: 'Array of constraints or limitations' }, + templateName: { type: 'string', description: 'Template name to use (defaults to "standard")' }, + providerId: { type: 'string', description: 'Specific AI provider to use (openai, anthropic, gemini, local, template)' }, + additionalContext: { type: 'string', description: 'Additional context or instructions for the AI provider' }, + providerOptions: { type: 'object', description: 'Provider-specific options like temperature, maxTokens, etc.' }, + }, + required: ['productName', 'productDescription', 'targetAudience', 'coreFeatures'], + }, + }, + { + name: 'validate_prd', + description: 'Validate a PRD document against best practices and customizable rules', + inputSchema: { + type: 'object', + properties: { + prdContent: { type: 'string', description: 'The PRD content to validate' }, + validationRules: { type: 'array', items: { type: 'string' }, description: 'Array of validation rule IDs to check' }, + }, + required: ['prdContent'], + }, + }, + { + name: 'list_validation_rules', + description: 'List all available built-in validation rules', + inputSchema: { type: 'object', properties: {} }, + }, + { + name: 'list_ai_providers', + description: 'List all AI providers and their availability status', + inputSchema: { type: 'object', properties: {} }, + }, + { + name: 'create_template', + description: 'Create a new PRD template', + inputSchema: { + type: 'object', + properties: { + name: { type: 'string', description: 'Template name' }, + description: { type: 'string', description: 'Template description' }, + content: { type: 'string', description: 'Markdown content of the template' }, + tags: { type: 'array', items: { type: 'string' }, description: 'Array of tags' }, + }, + required: ['name', 'content'], + }, + }, + { + name: 'list_templates', + description: 'List all available PRD templates', + inputSchema: { type: 'object', properties: {} }, + }, + { + name: 'get_template', + description: 'Get a specific template by ID or name', + inputSchema: { + type: 'object', + properties: { + idOrName: { type: 'string', description: 'Template ID or name' }, + }, + required: ['idOrName'], + }, + }, + { + name: 'update_template', + description: 'Update an existing PRD template', + inputSchema: { + type: 'object', + properties: { + id: { type: 'string', description: 'Template ID' }, + name: { type: 'string', description: 'New template name' }, + description: { type: 'string', description: 'New template description' }, + content: { type: 'string', description: 'New template content' }, + tags: { type: 'array', items: { type: 'string' }, description: 'New tags' }, + }, + required: ['id'], + }, + }, + { + name: 'delete_template', + description: 'Delete a PRD template', + inputSchema: { + type: 'object', + properties: { + id: { type: 'string', description: 'Template ID' }, + }, + required: ['id'], + }, + }, + { + name: 'export_templates', + description: 'Export all templates to a JSON file', + inputSchema: { + type: 'object', + properties: { + filePath: { type: 'string', description: 'File path to export to' }, + }, + required: ['filePath'], + }, + }, + { + name: 'import_templates', + description: 'Import templates from a JSON file', + inputSchema: { + type: 'object', + properties: { + filePath: { type: 'string', description: 'File path to import from' }, + }, + required: ['filePath'], + }, + }, + { + name: 'list_all_rules', + description: 'List all validation rules (built-in and custom)', + inputSchema: { type: 'object', properties: {} }, + }, + { + name: 'add_validation_rule', + description: 'Add a custom validation rule', + inputSchema: { + type: 'object', + properties: { + id: { type: 'string', description: 'Rule ID' }, + name: { type: 'string', description: 'Rule name' }, + description: { type: 'string', description: 'Rule description' }, + pattern: { type: 'string', description: 'Regex pattern for the rule' }, + }, + required: ['id', 'name', 'pattern'], + }, + }, + { + name: 'update_validation_rule', + description: 'Update a custom validation rule', + inputSchema: { + type: 'object', + properties: { + id: { type: 'string', description: 'Rule ID' }, + name: { type: 'string', description: 'New rule name' }, + description: { type: 'string', description: 'New rule description' }, + pattern: { type: 'string', description: 'New regex pattern' }, + }, + required: ['id'], + }, + }, + { + name: 'delete_validation_rule', + description: 'Delete a custom validation rule', + inputSchema: { + type: 'object', + properties: { + id: { type: 'string', description: 'Rule ID' }, + }, + required: ['id'], + }, + }, + { + name: 'stats', + description: 'Get usage statistics and metrics', + inputSchema: { type: 'object', properties: {} }, + }, + { + name: 'get_provider_config', + description: 'Get current AI provider configurations', + inputSchema: { type: 'object', properties: {} }, + }, + { + name: 'update_provider_config', + description: 'Update AI provider configuration (API key, base URL, model)', + inputSchema: { + type: 'object', + properties: { + providerId: { type: 'string', description: 'Provider ID (openai, anthropic, gemini, local)' }, + apiKey: { type: 'string', description: 'New API key' }, + baseUrl: { type: 'string', description: 'New base URL' }, + model: { type: 'string', description: 'New model name' }, + }, + required: ['providerId'], + }, + }, + { + name: 'render_template', + description: 'Render a PRD template with placeholder substitution (no AI)', + inputSchema: { + type: 'object', + properties: { + productName: { type: 'string', description: 'The name of the product' }, + productDescription: { type: 'string', description: 'Description of the product' }, + targetAudience: { type: 'string', description: 'Description of the target audience' }, + coreFeatures: { type: 'array', items: { type: 'string' }, description: 'Array of core feature descriptions' }, + constraints: { type: 'array', items: { type: 'string' }, description: 'Array of constraints or limitations' }, + templateName: { type: 'string', description: 'Template name to use' }, + }, + required: ['productName', 'productDescription', 'targetAudience', 'coreFeatures'], + }, + }, + { + name: 'health_check', + description: 'Check system health: database connectivity and AI provider availability', + inputSchema: { type: 'object', properties: {} }, + }, + { + name: 'get_logs', + description: 'Get recent system log entries', + inputSchema: { + type: 'object', + properties: { + fileName: { type: 'string', description: 'Log file name (default: combined.log)' }, + lines: { type: 'number', description: 'Number of lines to return (default: 100)' }, + }, + }, + }, + ], + })); + logger.info('All PRD Creator tools registered successfully'); -} \ No newline at end of file +}