import { createMCPServer, InMemoryStore, PromptDefinition, ResourceDefinition, CompletionRef, AuthContext } from '../src'; /** * Example: Completion Support for Prompts and Resources * * This example demonstrates how to implement autocomplete/completion * for prompt arguments and resource URI parameters in MCP. */ // Mock data for completion suggestions const availableLanguages = ['python', 'javascript', 'typescript', 'java', 'go', 'rust']; const availableFrameworks = { python: ['django', 'flask', 'fastapi'], javascript: ['express', 'nestjs', 'koa'], typescript: ['express', 'nestjs', 'nest'], java: ['spring', 'quarkus', 'micronaut'], go: ['gin', 'echo', 'fiber'], rust: ['actix', 'rocket', 'axum'], }; const repositories = [ { owner: 'facebook', name: 'react', description: 'A JavaScript library for building user interfaces' }, { owner: 'microsoft', name: 'typescript', description: 'TypeScript is a superset of JavaScript' }, { owner: 'nodejs', name: 'node', description: 'Node.js JavaScript runtime' }, { owner: 'denoland', name: 'deno', description: 'A modern runtime for JavaScript and TypeScript' }, ]; // Example 1: Prompt with Completion Support const codeGeneratorPrompt: PromptDefinition = { name: 'generate-code', description: 'Generate code based on language and framework', arguments: [ { name: 'language', description: 'Programming language', required: true, }, { name: 'framework', description: 'Framework to use', required: false, }, { name: 'feature', description: 'Feature to implement', required: true, }, ], handler: async (args: Record, context: AuthContext) => { const { language, framework, feature } = args; return { messages: [ { role: 'user' as const, content: `Generate ${language}${framework ? ` ${framework}` : ''} code for: ${feature}`, }, { role: 'assistant' as const, content: `Here's a ${language}${framework ? ` ${framework}` : ''} implementation for ${feature}...`, }, ], }; }, // Completion handler for prompt arguments completion: async (ref: CompletionRef, argument: string, context: AuthContext) => { if (ref.type !== 'ref/prompt') { return []; } console.log(`Providing completions for argument: ${argument}`); // Complete 'language' argument if (argument === 'language') { return availableLanguages.map(lang => ({ value: lang, label: lang.charAt(0).toUpperCase() + lang.slice(1), description: `Use ${lang} for code generation`, type: 'value' as const, })); } // Complete 'framework' argument (context-aware based on language) if (argument === 'framework') { const selectedLanguage = ref.arguments?.language; if (selectedLanguage && selectedLanguage in availableFrameworks) { const frameworks = availableFrameworks[selectedLanguage as keyof typeof availableFrameworks]; return frameworks.map(fw => ({ value: fw, label: fw.charAt(0).toUpperCase() + fw.slice(1), description: `${fw} framework for ${selectedLanguage}`, type: 'value' as const, })); } // If no language selected, show all frameworks return Object.values(availableFrameworks) .flat() .filter((v, i, a) => a.indexOf(v) === i) // unique .map(fw => ({ value: fw, label: fw.charAt(0).toUpperCase() + fw.slice(1), type: 'value' as const, })); } // Complete 'feature' argument with common features if (argument === 'feature') { const commonFeatures = [ 'REST API', 'Authentication', 'Database CRUD', 'File Upload', 'Websockets', 'Caching', ]; return commonFeatures.map(feat => ({ value: feat, label: feat, description: `Implement ${feat}`, type: 'value' as const, })); } return []; }, }; // Example 2: Resource with Completion for URI Parameters const githubResource: ResourceDefinition = { uri: 'github://repos/{owner}/{repo}/issues', name: 'github-issues', description: 'GitHub repository issues', mimeType: 'application/json', list: async (context: AuthContext) => { return { resources: repositories.map(repo => ({ uri: `github://repos/${repo.owner}/${repo.name}/issues`, name: `${repo.owner}/${repo.name} Issues`, description: repo.description, })), }; }, read: async (uri: string, context: AuthContext) => { const match = uri.match(/github:\/\/repos\/([^/]+)\/([^/]+)\/issues/); if (!match) { throw new Error('Invalid GitHub URI'); } const [, owner, repo] = match; return { contents: { repository: `${owner}/${repo}`, issues: [ { id: 1, title: 'Bug fix needed', state: 'open' }, { id: 2, title: 'Feature request', state: 'open' }, ], }, }; }, // Completion handler for URI parameters completion: async (ref: CompletionRef, argument: string, context: AuthContext) => { if (ref.type !== 'ref/resource') { return []; } console.log(`Providing resource completions for: ${ref.uri}, argument: ${argument}`); // Extract current values from URI const match = ref.uri.match(/github:\/\/repos\/([^/]*)\/([^/]*)/); const currentOwner = match?.[1] || ''; const currentRepo = match?.[2] || ''; // Complete 'owner' parameter if (argument === 'owner' || !currentOwner) { const owners = [...new Set(repositories.map(r => r.owner))]; return owners.map(owner => ({ value: owner, label: owner, description: `GitHub user/organization: ${owner}`, type: 'value' as const, })); } // Complete 'repo' parameter (filtered by owner) if (argument === 'repo' || !currentRepo) { const filtered = currentOwner ? repositories.filter(r => r.owner === currentOwner) : repositories; return filtered.map(repo => ({ value: repo.name, label: repo.name, description: repo.description, type: 'value' as const, })); } return []; }, }; // Example 3: Global Completion Handler // This provides completions for any prompt/resource that doesn't have its own handler async function globalCompletionHandler( ref: CompletionRef, argument: string, context: AuthContext ) { console.log('Global completion handler called', { ref, argument }); // Provide generic suggestions return [ { value: 'default', label: 'Default Value', description: 'Use default value', type: 'value' as const, }, { value: 'custom', label: 'Custom Value', description: 'Enter custom value', type: 'value' as const, }, ]; } // Create and start the server async function main() { const server = createMCPServer({ name: 'completion-example', version: '1.0.0', publicUrl: 'http://localhost:3000', port: 3000, store: new InMemoryStore(), tools: [], prompts: [codeGeneratorPrompt], resources: [githubResource], // Global completion handler (optional, used as fallback) completions: globalCompletionHandler, }); await server.start(); console.log('🚀 Completion example server started!'); console.log('\nCompletion features:'); console.log('\n1. Prompt Argument Completion:'); console.log(' - Try typing in "language" field → suggests: python, javascript, typescript, etc.'); console.log(' - Try typing in "framework" field → context-aware suggestions based on selected language'); console.log(' - Try typing in "feature" field → suggests common features'); console.log('\n2. Resource URI Completion:'); console.log(' - Type github://repos/ → suggests owners: facebook, microsoft, nodejs, etc.'); console.log(' - Type github://repos/facebook/ → suggests repos: react'); console.log(' - Type github://repos/microsoft/ → suggests repos: typescript'); console.log('\n3. Global Fallback:'); console.log(' - Any argument without specific handler gets default suggestions'); } /** * USAGE NOTES: * * CLIENT REQUEST FORMAT: * * To request completions, clients send: * { * "jsonrpc": "2.0", * "method": "completion/complete", * "params": { * "ref": { * "type": "ref/prompt", // or "ref/resource" * "name": "generate-code", // prompt name * "arguments": { // current argument values (for context) * "language": "python" * } * }, * "argument": { * "name": "framework", // which argument to complete * "value": "fla" // partial value typed so far * } * } * } * * COMPLETION RESPONSE: * * Server responds with: * { * "jsonrpc": "2.0", * "result": { * "completion": { * "values": [ * "flask", * "fastapi" * ], * "total": 2, * "hasMore": false * } * } * } * * IMPLEMENTATION TIPS: * * 1. **Context-Aware Completions**: Use ref.arguments to provide context-aware suggestions * (e.g., frameworks filtered by selected language) * * 2. **Fuzzy Matching**: Implement fuzzy search for better user experience * (e.g., "ts" matches "typescript", "nestjs") * * 3. **Async Data**: Completion handlers can be async, so you can fetch suggestions * from databases, APIs, or other sources * * 4. **Caching**: Cache frequent completions to improve performance * * 5. **Hierarchical**: For resources with nested parameters (like GitHub repos), * provide completions in order (owner first, then repos for that owner) * * 6. **Performance**: Keep completion handlers fast (<100ms) for good UX * * 7. **Fallback**: Use global completion handler for arguments without * specific handlers * * 8. **Type Safety**: Use TypeScript types to ensure completion items * match the expected format */ if (require.main === module) { main().catch(console.error); } export { codeGeneratorPrompt, githubResource, globalCompletionHandler };