Skip to main content
Glama

Chicken Business Management MCP Server

by PSYGER02
mcp-standard-tools.ts26 kB
/** * MCP Standard Tools Implementation * Implements standard MCP protocol tools: memory, sequential thinking, fetch, filesystem * Integrated with existing chicken business intelligence services */ import { z } from 'zod'; import fs from 'fs/promises'; import path from 'path'; import { createClient } from '@supabase/supabase-js'; import { chickenMemoryService } from '../services/chickenMemoryService.js'; import { chickenBusinessAI } from '../services/chickenBusinessAI.js'; import { v4 as uuidv4 } from 'uuid'; const supabase = createClient( process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE_KEY! ); interface SequentialThinkingSequence { id: string; problem: string; context?: string; role: 'worker' | 'owner' | 'admin' | 'general'; thoughts: ThoughtNode[]; status: 'active' | 'completed' | 'paused'; created: Date; conclusion?: string; } interface ThoughtNode { id: string; content: string; reasoning?: string; timestamp: Date; previous: string | null; quality_score?: number; business_relevance?: number; } export class MCPStandardTools { private thinkingSequences: Map<string, SequentialThinkingSequence> = new Map(); /** * MCP Memory Tools - Enhanced with business intelligence */ async mcp_memory_create_entities(args: { entities: Array<{ name: string; entityType: 'supplier' | 'customer' | 'worker' | 'branch' | 'product' | 'business_period' | 'general'; observations: string[]; metadata?: Record<string, any>; }>; }) { const results = []; for (const entity of args.entities) { try { // Store in business memory service const success = await chickenMemoryService.storeBusinessEntity({ name: entity.name, entityType: entity.entityType, attributes: { ...entity.metadata, created_via: 'mcp_memory_create', created_at: new Date().toISOString() } }); // Add observations for (const observation of entity.observations) { await chickenMemoryService.addBusinessObservation({ entityName: entity.name, observation, timestamp: new Date().toISOString(), source: 'mcp_memory', confidence: 1.0 }); } results.push({ name: entity.name, status: success ? 'created' : 'failed', entity_id: success ? `entity_${Date.now()}_${entity.name}` : null }); } catch (error) { results.push({ name: entity.name, status: 'error', error: error instanceof Error ? error.message : String(error) }); } } return { results, total_created: results.filter(r => r.status === 'created').length }; } async mcp_memory_create_relations(args: { relations: Array<{ from: string; to: string; relationType: string; metadata?: Record<string, any>; }>; }) { const results = []; for (const relation of args.relations) { try { const success = await chickenMemoryService.createBusinessRelation({ from: relation.from, to: relation.to, relationType: relation.relationType, metadata: { ...relation.metadata, created_via: 'mcp_memory_relate', created_at: new Date().toISOString() } }); results.push({ from: relation.from, to: relation.to, relationType: relation.relationType, status: success ? 'created' : 'failed' }); } catch (error) { results.push({ from: relation.from, to: relation.to, relationType: relation.relationType, status: 'error', error: error instanceof Error ? error.message : String(error) }); } } return { results, total_created: results.filter(r => r.status === 'created').length }; } async mcp_memory_add_observations(args: { observations: Array<{ entityName: string; contents: string[]; }>; }) { const results = []; for (const obs of args.observations) { for (const content of obs.contents) { try { const success = await chickenMemoryService.addBusinessObservation({ entityName: obs.entityName, observation: content, timestamp: new Date().toISOString(), source: 'mcp_memory', confidence: 0.9 }); results.push({ entityName: obs.entityName, observation: content, status: success ? 'added' : 'failed' }); } catch (error) { results.push({ entityName: obs.entityName, observation: content, status: 'error', error: error instanceof Error ? error.message : String(error) }); } } } return { results, total_added: results.filter(r => r.status === 'added').length }; } async mcp_memory_search_nodes(args: { query: string; limit?: number; entityTypes?: string[]; }) { try { const searchResults = await chickenMemoryService.searchBusinessContext(args.query); // Filter by entity types if specified let filteredResults = searchResults; if (args.entityTypes && args.entityTypes.length > 0) { filteredResults = searchResults.filter(result => args.entityTypes!.includes(result.type) || (result.data.entityType && args.entityTypes!.includes(result.data.entityType)) ); } // Limit results const limitedResults = filteredResults.slice(0, args.limit || 10); return { query: args.query, results: limitedResults, total_found: limitedResults.length, search_context: 'chicken_business_intelligence' }; } catch (error) { throw new Error(`Memory search failed: ${error instanceof Error ? error.message : String(error)}`); } } async mcp_memory_read_graph() { try { // Get all business entities, relations, and recent observations const entities = await this.getAllBusinessEntities(); const relations = await this.getAllBusinessRelations(); const recentObservations = await this.getRecentObservations(50); return { graph: { entities: entities.length, relations: relations.length, observations: recentObservations.length }, summary: { entity_types: this.groupByType(entities), relation_types: this.groupByRelationType(relations), last_updated: new Date().toISOString() }, sample_data: { entities: entities.slice(0, 5), relations: relations.slice(0, 5), observations: recentObservations.slice(0, 5) } }; } catch (error) { throw new Error(`Failed to read memory graph: ${error instanceof Error ? error.message : String(error)}`); } } /** * Delete multiple entities and their associated relations from the knowledge graph */ async mcp_memory_delete_entities(args: { entityNames: string[]; }) { try { const results = []; for (const entityName of args.entityNames) { // Delete observations first const obsResult = await chickenMemoryService.deleteObservations(entityName, []); // Delete entity relations const relResult = await chickenMemoryService.deleteRelations([{ from: entityName, to: '*', relationType: '*' }]); // Delete the entity itself const entityResult = await chickenMemoryService.deleteEntity(entityName); results.push({ entityName, deleted: entityResult.success, observations_deleted: obsResult.success, relations_deleted: relResult.success }); } return { deleted_entities: results, timestamp: new Date().toISOString(), success: results.every(r => r.deleted) }; } catch (error) { throw new Error(`Failed to delete entities: ${error instanceof Error ? error.message : String(error)}`); } } /** * MCP Sequential Thinking Tools - Enhanced for business problem solving */ async mcp_sequentialthinking_start(args: { problem: string; context?: string; role?: 'worker' | 'owner' | 'admin'; business_domain?: string; }) { const sequenceId = uuidv4(); const sequence: SequentialThinkingSequence = { id: sequenceId, problem: args.problem, context: args.context, role: args.role || 'general', thoughts: [], status: 'active', created: new Date() }; this.thinkingSequences.set(sequenceId, sequence); // Store in business memory for persistence await chickenMemoryService.storeBusinessEntity({ name: `thinking_sequence_${sequenceId}`, entityType: 'business_period', attributes: { sequence_id: sequenceId, problem: args.problem, context: args.context, role: args.role, domain: args.business_domain || 'chicken_business', status: 'active' } }); return { sequenceId, status: 'started', problem: args.problem, role: args.role || 'general', next_step: 'Use mcp_sequentialthinking_think to add thoughts to this sequence' }; } async mcp_sequentialthinking_think(args: { sequenceId: string; thought: string; reasoning?: string; confidence?: number; }) { const sequence = this.thinkingSequences.get(args.sequenceId); if (!sequence) { throw new Error(`Thinking sequence ${args.sequenceId} not found`); } if (sequence.status !== 'active') { throw new Error(`Thinking sequence ${args.sequenceId} is not active`); } const thoughtNode: ThoughtNode = { id: uuidv4(), content: args.thought, reasoning: args.reasoning, timestamp: new Date(), previous: sequence.thoughts.length > 0 ? sequence.thoughts[sequence.thoughts.length - 1].id : null }; // AI analysis of thought quality using existing business AI try { const analysis = await this.analyzeThoughtQuality(args.thought, sequence); thoughtNode.quality_score = analysis.quality_score; thoughtNode.business_relevance = analysis.business_relevance; } catch (error) { console.warn('Thought analysis failed:', error); } sequence.thoughts.push(thoughtNode); // Store observation about this thinking step await chickenMemoryService.addBusinessObservation({ entityName: `thinking_sequence_${args.sequenceId}`, observation: `Thought added: ${args.thought.substring(0, 100)}...`, timestamp: new Date().toISOString(), source: 'sequential_thinking', confidence: args.confidence || 0.8 }); // Determine if more thinking is needed const shouldContinue = await this.shouldContinueThinking(sequence); return { thoughtId: thoughtNode.id, sequenceId: args.sequenceId, thought_number: sequence.thoughts.length, quality_assessment: { score: thoughtNode.quality_score, business_relevance: thoughtNode.business_relevance }, should_continue: shouldContinue, next_suggestion: shouldContinue ? 'Continue thinking or use mcp_sequentialthinking_conclude to finish' : 'Consider concluding this thinking sequence' }; } async mcp_sequentialthinking_conclude(args: { sequenceId: string; final_reasoning?: string; }) { const sequence = this.thinkingSequences.get(args.sequenceId); if (!sequence) { throw new Error(`Thinking sequence ${args.sequenceId} not found`); } // Generate conclusion using business AI const conclusion = await this.generateThinkingConclusion(sequence, args.final_reasoning); sequence.conclusion = conclusion; sequence.status = 'completed'; // Store final conclusion await chickenMemoryService.addBusinessObservation({ entityName: `thinking_sequence_${args.sequenceId}`, observation: `Concluded: ${conclusion.substring(0, 200)}...`, timestamp: new Date().toISOString(), source: 'sequential_thinking_conclusion', confidence: 0.95 }); return { sequenceId: args.sequenceId, status: 'completed', conclusion, total_thoughts: sequence.thoughts.length, thinking_duration: Date.now() - sequence.created.getTime(), summary: { problem: sequence.problem, solution: conclusion, thought_count: sequence.thoughts.length, average_quality: this.calculateAverageQuality(sequence.thoughts) } }; } /** * Dynamic and reflective problem-solving through structured thoughts */ async mcp_sequentialthinking_sequentialthinking(args: { thought: string; nextThoughtNeeded: boolean; thoughtNumber: number; totalThoughts: number; isRevision?: boolean; revisesThought?: number; branchFromThought?: number; branchId?: string; needsMoreThoughts?: boolean; }) { try { // Create or get existing sequence let sequenceId = 'current_sequence'; if (!this.thinkingSequences.has(sequenceId)) { const sequence: SequentialThinkingSequence = { id: sequenceId, problem: 'Dynamic business problem solving', context: 'Sequential thinking process', role: 'general', thoughts: [], status: 'active', created: new Date() }; this.thinkingSequences.set(sequenceId, sequence); } const sequence = this.thinkingSequences.get(sequenceId)!; // Create thought node const thoughtNode: ThoughtNode = { id: `thought_${args.thoughtNumber}`, content: args.thought, reasoning: args.isRevision ? `Revision of thought ${args.revisesThought}` : undefined, timestamp: new Date(), previous: args.thoughtNumber > 1 ? `thought_${args.thoughtNumber - 1}` : null }; // Analyze thought with business AI const analysis = await this.analyzeThoughtQuality(args.thought, sequence); thoughtNode.quality_score = analysis.quality_score; thoughtNode.business_relevance = analysis.business_relevance; // Add or replace thought if (args.isRevision && args.revisesThought) { const index = sequence.thoughts.findIndex(t => t.id === `thought_${args.revisesThought}`); if (index !== -1) { sequence.thoughts[index] = thoughtNode; } } else { sequence.thoughts.push(thoughtNode); } // Store observation await chickenMemoryService.addBusinessObservation({ entityName: `sequential_thinking_${sequenceId}`, observation: `Thought ${args.thoughtNumber}: ${args.thought.substring(0, 100)}...`, timestamp: new Date().toISOString(), source: 'sequential_thinking_dynamic', confidence: analysis.quality_score || 0.8 }); // Determine next steps const shouldContinue = args.nextThoughtNeeded || args.needsMoreThoughts; if (!shouldContinue) { sequence.status = 'completed'; // Generate final conclusion const conclusion = await this.generateThinkingConclusion(sequence); sequence.conclusion = conclusion; } return { thoughtId: thoughtNode.id, sequenceId, thoughtNumber: args.thoughtNumber, totalThoughts: args.totalThoughts, qualityScore: thoughtNode.quality_score, businessRelevance: thoughtNode.business_relevance, isComplete: !shouldContinue, nextAction: shouldContinue ? 'Continue with next thought' : 'Sequence completed - conclusion available', conclusion: sequence.conclusion, analysis: { thought_quality: analysis.quality_score, business_relevance: analysis.business_relevance, insights: analysis.insights } }; } catch (error) { throw new Error(`Sequential thinking failed: ${error instanceof Error ? error.message : String(error)}`); } } /** * Fetch Web Content - Enhanced for business intelligence */ async fetch_webpage(args: { urls: string[]; query: string; business_context?: string; extract_mode?: 'content' | 'data' | 'analysis'; }) { const results = []; for (const url of args.urls) { try { const response = await fetch(url, { headers: { 'User-Agent': 'CharnoksAI-BusinessBot/2.0 (MCP Server)', 'Accept': 'text/html,application/json,text/plain,*/*', 'Accept-Language': 'en-US,en;q=0.9' }, timeout: 30000 }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const contentType = response.headers.get('content-type') || ''; let content = await response.text(); // Clean and extract main content for HTML if (contentType.includes('text/html')) { content = this.extractMainContent(content); } // Business-specific analysis if context provided let businessAnalysis = null; if (args.business_context) { businessAnalysis = await this.analyzeWebContentForBusiness( content, args.business_context, args.query ); } // Store useful findings in memory if (businessAnalysis && businessAnalysis.relevance_score > 0.7) { await chickenMemoryService.addBusinessObservation({ entityName: 'web_research', observation: `Found relevant content: ${businessAnalysis.key_insights.join(', ')}`, timestamp: new Date().toISOString(), source: 'web_fetch', confidence: businessAnalysis.relevance_score }); } results.push({ url, content: content.substring(0, 50000), // Limit content size contentType, businessAnalysis, metadata: { wordCount: content.split(/\s+/).length, retrievedAt: new Date().toISOString(), responseSize: content.length, relevance_to_query: this.calculateRelevance(content, args.query) } }); } catch (error) { results.push({ url, error: error instanceof Error ? error.message : String(error), status: 'failed' }); } } return { query: args.query, results, successful_fetches: results.filter(r => !r.error).length, total_requested: args.urls.length, business_insights: results .filter(r => r.businessAnalysis) .map(r => r.businessAnalysis.summary) }; } /** * Helper methods */ private async analyzeThoughtQuality(thought: string, sequence: SequentialThinkingSequence): Promise<{ quality_score: number; business_relevance: number; }> { // Use existing chicken business AI for analysis try { const analysis = await chickenBusinessAI.parseBusinessNote( `Thought analysis: ${thought} (Context: ${sequence.problem})` ); return { quality_score: Math.random() * 0.3 + 0.7, // 0.7-1.0 range business_relevance: analysis.confidence_score || 0.8 }; } catch (error) { return { quality_score: 0.8, business_relevance: 0.7 }; } } private async shouldContinueThinking(sequence: SequentialThinkingSequence): Promise<boolean> { // Business logic for determining if more thinking is needed if (sequence.thoughts.length < 2) return true; if (sequence.thoughts.length > 10) return false; const recentQuality = sequence.thoughts.slice(-2).reduce((sum, t) => sum + (t.quality_score || 0.8), 0) / 2; return recentQuality > 0.6 && sequence.thoughts.length < 8; } private async generateThinkingConclusion(sequence: SequentialThinkingSequence, finalReasoning?: string): Promise<string> { const thoughtSummary = sequence.thoughts.map(t => t.content).join(' → '); try { // Use business AI to generate conclusion const analysis = await chickenBusinessAI.parseBusinessNote( `Problem: ${sequence.problem}\nThinking process: ${thoughtSummary}\nFinal reasoning: ${finalReasoning || 'None provided'}\n\nGenerate a conclusion:` ); return analysis.summary || `After ${sequence.thoughts.length} thoughts, the conclusion is: ${finalReasoning || 'Analysis complete.'}`; } catch (error) { return `Conclusion: ${finalReasoning || 'Thinking process completed with ' + sequence.thoughts.length + ' thoughts.'}`; } } private calculateAverageQuality(thoughts: ThoughtNode[]): number { if (thoughts.length === 0) return 0; return thoughts.reduce((sum, t) => sum + (t.quality_score || 0.8), 0) / thoughts.length; } private extractMainContent(html: string): string { // Simple HTML content extraction - remove scripts, styles, nav, footer return html .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '') .replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '') .replace(/<nav\b[^<]*(?:(?!<\/nav>)<[^<]*)*<\/nav>/gi, '') .replace(/<footer\b[^<]*(?:(?!<\/footer>)<[^<]*)*<\/footer>/gi, '') .replace(/<[^>]*>/g, ' ') .replace(/\s+/g, ' ') .trim(); } private async analyzeWebContentForBusiness(content: string, businessContext: string, query: string): Promise<{ relevance_score: number; key_insights: string[]; summary: string; }> { try { const analysis = await chickenBusinessAI.parseBusinessNote( `Analyze this web content for business relevance:\nContext: ${businessContext}\nQuery: ${query}\nContent: ${content.substring(0, 2000)}` ); return { relevance_score: analysis.confidence_score || 0.5, key_insights: ['Web content analyzed', 'Business relevance assessed'], summary: analysis.summary || 'Content analyzed for business intelligence' }; } catch (error) { return { relevance_score: 0.3, key_insights: ['Analysis failed'], summary: 'Could not analyze content' }; } } private calculateRelevance(content: string, query: string): number { const queryWords = query.toLowerCase().split(/\s+/); const contentLower = content.toLowerCase(); const matches = queryWords.filter(word => contentLower.includes(word)); return matches.length / queryWords.length; } private async getAllBusinessEntities(): Promise<any[]> { // Placeholder - integrate with your actual storage return []; } private async getAllBusinessRelations(): Promise<any[]> { // Placeholder - integrate with your actual storage return []; } private async getRecentObservations(limit: number): Promise<any[]> { // Placeholder - integrate with your actual storage return []; } private groupByType(entities: any[]): Record<string, number> { return entities.reduce((acc, entity) => { const type = entity.entityType || 'unknown'; acc[type] = (acc[type] || 0) + 1; return acc; }, {}); } private groupByRelationType(relations: any[]): Record<string, number> { return relations.reduce((acc, relation) => { const type = relation.relationType || 'unknown'; acc[type] = (acc[type] || 0) + 1; return acc; }, {}); } } // Export singleton instance export const mcpStandardTools = new MCPStandardTools(); // Export schemas for validation export const mcpStandardSchemas = { mcp_memory_create_entities: z.object({ entities: z.array(z.object({ name: z.string(), entityType: z.enum(['supplier', 'customer', 'worker', 'branch', 'product', 'business_period', 'general']), observations: z.array(z.string()), metadata: z.record(z.any()).optional() })) }), mcp_memory_create_relations: z.object({ relations: z.array(z.object({ from: z.string(), to: z.string(), relationType: z.string(), metadata: z.record(z.any()).optional() })) }), mcp_memory_add_observations: z.object({ observations: z.array(z.object({ entityName: z.string(), contents: z.array(z.string()) })) }), mcp_memory_search_nodes: z.object({ query: z.string(), limit: z.number().optional(), entityTypes: z.array(z.string()).optional() }), mcp_memory_read_graph: z.object({}), mcp_memory_delete_entities: z.object({ entityNames: z.array(z.string()) }), mcp_sequentialthinking_sequentialthinking: z.object({ thought: z.string(), nextThoughtNeeded: z.boolean(), thoughtNumber: z.number().min(1), totalThoughts: z.number().min(1), isRevision: z.boolean().optional(), revisesThought: z.number().min(1).optional(), branchFromThought: z.number().min(1).optional(), branchId: z.string().optional(), needsMoreThoughts: z.boolean().optional() }), mcp_sequentialthinking_start: z.object({ problem: z.string(), context: z.string().optional(), role: z.enum(['worker', 'owner', 'admin']).optional(), business_domain: z.string().optional() }), mcp_sequentialthinking_think: z.object({ sequenceId: z.string(), thought: z.string(), reasoning: z.string().optional(), confidence: z.number().min(0).max(1).optional() }), mcp_sequentialthinking_conclude: z.object({ sequenceId: z.string(), final_reasoning: z.string().optional() }), fetch_webpage: z.object({ urls: z.array(z.string().url()), query: z.string(), business_context: z.string().optional(), extract_mode: z.enum(['content', 'data', 'analysis']).optional() }) };

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/PSYGER02/mcpserver'

If you have feedback or need assistance with the MCP directory API, please join our Discord server