Google Custom Search Engine MCP Server

by Richard-Weiss
Verified
  • src
#!/usr/bin/env node /** * Feature Discussion MCP Server * * This server facilitates intelligent feature discussions between developers and AI. * It provides: * - Interactive feature discussions * - AI-guided requirement gathering * - Development guidance * - Architectural recommendations */ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; /** * Types for feature discussions */ type FeatureDiscussion = { id: string; title: string; description: string; status: 'proposed' | 'in-discussion' | 'approved' | 'rejected'; requirements: string[]; businessValue?: string; targetUsers?: string[]; successCriteria?: string[]; technicalApproach?: string; architecturalDecisions?: string[]; dependencies?: string[]; risks?: string[]; timeline?: string; createdAt: string; updatedAt: string; currentPrompt?: string; }; // Type for feature discussion prompt fields type FeatureField = keyof Omit<FeatureDiscussion, 'id' | 'status' | 'createdAt' | 'updatedAt' | 'currentPrompt' | 'architecturalDecisions' | 'dependencies' >; type FeaturePrompt = { id: string; message: string; field: FeatureField; }; type DiscussionContext = { previousDecisions: string[]; relatedFeatures: string[]; technicalConstraints: string[]; conversationHistory: Array<{ prompt: string; response: string; timestamp: string; }>; }; /** * In-memory storage for feature discussions and context */ const featureDiscussions: { [id: string]: FeatureDiscussion } = {}; const discussionContexts: { [featureId: string]: DiscussionContext } = {}; // Define the sequence of prompts for feature discussion const FEATURE_DISCUSSION_PROMPTS: FeaturePrompt[] = [ { id: "initial_description", message: "Please provide a brief description of the feature you'd like to discuss.", field: "description" }, { id: "business_value", message: "What business value does this feature provide? How does it benefit users or stakeholders?", field: "businessValue" }, { id: "target_users", message: "Who are the target users for this feature?", field: "targetUsers" }, { id: "requirements", message: "What are the key requirements or constraints for this feature?", field: "requirements" }, { id: "success_criteria", message: "What are the success criteria for this feature? How will we know it's working as intended?", field: "successCriteria" }, { id: "technical_approach", message: "Do you have any specific technical approach in mind for implementing this feature?", field: "technicalApproach" }, { id: "risks", message: "Are there any potential risks or challenges we should consider?", field: "risks" }, { id: "timeline", message: "What's the desired timeline or priority for this feature?", field: "timeline" } ]; const server = new Server( { name: "feature-discussion", version: "0.1.0", }, { capabilities: { resources: {}, tools: {}, prompts: {}, }, } ); /** * Handler for listing available feature discussions as resources */ server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: Object.entries(featureDiscussions).map(([id, feature]) => ({ uri: `feature:///${id}`, mimeType: "application/json", name: feature.title, description: feature.description })) }; }); /** * Handler for reading a specific feature discussion */ server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const url = new URL(request.params.uri); const id = url.pathname.replace(/^\//, ''); const feature = featureDiscussions[id]; const context = discussionContexts[id]; if (!feature) { throw new Error(`Feature discussion ${id} not found`); } return { contents: [{ uri: request.params.uri, mimeType: "application/json", text: JSON.stringify({ ...feature, context }, null, 2) }] }; }); /** * Handler that lists available tools for feature management */ server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "begin_feature_discussion", description: "Start a new feature discussion", inputSchema: { type: "object", properties: { title: { type: "string", description: "Title or name of the feature" } }, required: ["title"] } }, { name: "provide_feature_input", description: "Provide information for the current feature discussion prompt", inputSchema: { type: "object", properties: { featureId: { type: "string", description: "ID of the feature being discussed" }, response: { type: "string", description: "Your response to the current prompt" } }, required: ["featureId", "response"] } } ] }; }); /** * Handler for feature management tools */ server.setRequestHandler(CallToolRequestSchema, async (request) => { switch (request.params.name) { case "begin_feature_discussion": { const { title } = request.params.arguments as any; const id = `f${Object.keys(featureDiscussions).length + 1}`; // Initialize new feature discussion featureDiscussions[id] = { id, title, description: "", requirements: [], status: 'in-discussion', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), currentPrompt: FEATURE_DISCUSSION_PROMPTS[0].id }; // Initialize discussion context discussionContexts[id] = { previousDecisions: [], relatedFeatures: [], technicalConstraints: [], conversationHistory: [] }; // Return the first prompt return { content: [ { type: "text", text: `Feature discussion started for: ${title}\n\nFeature ID: ${id}\n\nFirst question:\n${FEATURE_DISCUSSION_PROMPTS[0].message}` } ] }; } case "provide_feature_input": { const { featureId, response } = request.params.arguments as any; const feature = featureDiscussions[featureId]; const context = discussionContexts[featureId]; if (!feature) { throw new Error(`Feature ${featureId} not found`); } // Find current prompt const currentPromptIndex = FEATURE_DISCUSSION_PROMPTS.findIndex(p => p.id === feature.currentPrompt); const currentPrompt = FEATURE_DISCUSSION_PROMPTS[currentPromptIndex]; // Store the response if (currentPrompt.field === 'requirements' || currentPrompt.field === 'targetUsers' || currentPrompt.field === 'successCriteria' || currentPrompt.field === 'risks') { // Handle array fields feature[currentPrompt.field] = response.split('\n').map((r: string) => r.trim()).filter(Boolean); } else { // Handle string fields feature[currentPrompt.field] = response; } // Update conversation history context.conversationHistory.push({ prompt: currentPrompt.message, response, timestamp: new Date().toISOString() }); // Move to next prompt if available const nextPrompt = FEATURE_DISCUSSION_PROMPTS[currentPromptIndex + 1]; let responseMessage = "Response recorded. "; if (nextPrompt) { feature.currentPrompt = nextPrompt.id; responseMessage += `\n\nNext question:\n${nextPrompt.message}`; } else { feature.status = 'proposed'; feature.currentPrompt = undefined; responseMessage += "\n\nThank you! The feature has been fully documented. You can now use the 'analyze_feature' or 'suggest_architecture' prompts to get AI guidance on implementation."; } feature.updatedAt = new Date().toISOString(); return { content: [{ type: "text", text: responseMessage }] }; } default: throw new Error("Unknown tool"); } }); /** * Handler that lists available AI guidance prompts */ server.setRequestHandler(ListPromptsRequestSchema, async () => { return { prompts: [ { name: "analyze_feature", description: "Get AI analysis and recommendations for a feature", }, { name: "suggest_architecture", description: "Get architectural recommendations for a feature", } ] }; }); /** * Handler for AI guidance prompts */ server.setRequestHandler(GetPromptRequestSchema, async (request) => { switch (request.params.name) { case "analyze_feature": { return { messages: [ { role: "system", content: { type: "text", text: "You are an experienced technical lead helping analyze feature requirements and providing implementation guidance. Consider the full context of the feature discussion when providing recommendations." } }, { role: "user", content: { type: "text", text: "Please analyze this feature and provide detailed recommendations covering:\n" + "1. Implementation approach\n" + "2. Potential technical challenges\n" + "3. Required skills and expertise\n" + "4. Testing considerations\n" + "5. Integration points with existing systems" } } ] }; } case "suggest_architecture": { return { messages: [ { role: "system", content: { type: "text", text: "You are an experienced software architect helping design robust and scalable solutions. Consider the full feature context and existing system architecture." } }, { role: "user", content: { type: "text", text: "Please provide architectural recommendations covering:\n" + "1. Proposed system design\n" + "2. Key components and their interactions\n" + "3. Data flow and storage considerations\n" + "4. Scalability and performance factors\n" + "5. Security implications" } } ] }; } default: throw new Error("Unknown prompt"); } }); /** * Start the server using stdio transport */ async function main() { const transport = new StdioServerTransport(); await server.connect(transport); } main().catch((error) => { console.error("Server error:", error); process.exit(1); });