diff --git a/README.chatmodes.md b/README.chatmodes.md index 771a7b6..1913fbd 100644 --- a/README.chatmodes.md +++ b/README.chatmodes.md @@ -50,6 +50,7 @@ Custom chat modes define specific behaviors and tools for GitHub Copilot Chat, e | [Microsoft Learn Contributor](chatmodes/microsoft_learn_contributor.chatmode.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fmicrosoft_learn_contributor.chatmode.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fmicrosoft_learn_contributor.chatmode.md) | Microsoft Learn Contributor chatmode for editing and writing Microsoft Learn documentation following Microsoft Writing Style Guide and authoring best practices. | | [Microsoft Study and Learn Chat Mode](chatmodes/microsoft-study-mode.chatmode.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fmicrosoft-study-mode.chatmode.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fmicrosoft-study-mode.chatmode.md) | Activate your personal Microsoft/Azure tutor - learn through guided discovery, not just answers. | | [MS-SQL Database Administrator](chatmodes/ms-sql-dba.chatmode.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fms-sql-dba.chatmode.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fms-sql-dba.chatmode.md) | Work with Microsoft SQL Server databases using the MS SQL extension. | +| [PHP MCP Expert](chatmodes/php-mcp-expert.chatmode.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fphp-mcp-expert.chatmode.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fphp-mcp-expert.chatmode.md) | Expert assistant for PHP MCP server development using the official PHP SDK with attribute-based discovery | | [Plan Mode - Strategic Planning & Architecture Assistant](chatmodes/plan.chatmode.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fplan.chatmode.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fplan.chatmode.md) | Strategic planning and architecture assistant focused on thoughtful analysis before implementation. Helps developers understand codebases, clarify requirements, and develop comprehensive implementation strategies. | | [Planning mode instructions](chatmodes/planner.chatmode.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fplanner.chatmode.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fplanner.chatmode.md) | Generate an implementation plan for new features or refactoring existing code. | | [Playwright Tester](chatmodes/playwright-tester.chatmode.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fplaywright-tester.chatmode.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fplaywright-tester.chatmode.md) | Testing mode for Playwright tests | diff --git a/README.collections.md b/README.collections.md index 8b205f7..1767f9c 100644 --- a/README.collections.md +++ b/README.collections.md @@ -22,7 +22,9 @@ Curated collections of related prompts, instructions, and chat modes organized a | [DevOps On-Call](collections/devops-oncall.md) | A focused set of prompts, instructions, and a chat mode to help triage incidents and respond quickly with DevOps tools and Azure resources. | 5 items | devops, incident-response, oncall, azure | | [Frontend Web Development](collections/frontend-web-dev.md) | Essential prompts, instructions, and chat modes for modern frontend web development including React, Angular, Vue, TypeScript, and CSS frameworks. | 11 items | frontend, web, react, typescript, javascript, css, html, angular, vue | | [Java Development](collections/java-development.md) | Comprehensive collection of prompts and instructions for Java development including Spring Boot, Quarkus, testing, documentation, and best practices. | 12 items | java, springboot, quarkus, jpa, junit, javadoc | +| [PHP MCP Server Development](collections/php-mcp-development.md) | 'Comprehensive resources for building Model Context Protocol servers using the official PHP SDK with attribute-based discovery, including best practices, project generation, and expert assistance' | 3 items | php, mcp, model-context-protocol, server-development, sdk, attributes, composer | | [Power Apps Code Apps Development](collections/power-apps-code-apps.md) | Complete toolkit for Power Apps Code Apps development including project scaffolding, development standards, and expert guidance for building code-first applications with Power Platform integration. | 3 items | power-apps, power-platform, typescript, react, code-apps, dataverse, connectors | +| [Power BI Development](collections/power-bi-development.md) | Comprehensive Power BI development resources including data modeling, DAX optimization, performance tuning, visualization design, security best practices, and DevOps/ALM guidance for building enterprise-grade Power BI solutions. | 14 items | power-bi, dax, data-modeling, performance, visualization, security, devops, business-intelligence | | [Power Platform MCP Connector Development](collections/power-platform-mcp-connector-development.md) | Complete toolkit for developing Power Platform custom connectors with Model Context Protocol integration for Microsoft Copilot Studio | 4 items | power-platform, mcp, copilot-studio, custom-connector, json-rpc | | [Project Planning & Management](collections/project-planning.md) | Tools and guidance for software project planning, feature breakdown, epic management, implementation planning, and task organization for development teams. | 17 items | planning, project-management, epic, feature, implementation, task, architecture, technical-spike | | [Security & Code Quality](collections/security-best-practices.md) | Security frameworks, accessibility guidelines, performance optimization, and code quality best practices for building secure, maintainable, and high-performance applications. | 6 items | security, accessibility, performance, code-quality, owasp, a11y, optimization, best-practices | diff --git a/README.instructions.md b/README.instructions.md index c60af39..4a7082a 100644 --- a/README.instructions.md +++ b/README.instructions.md @@ -74,6 +74,7 @@ Team and project-specific instructions to enhance GitHub Copilot's behavior for | [Object Calisthenics Rules](instructions/object-calisthenics.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fobject-calisthenics.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fobject-calisthenics.instructions.md) | Enforces Object Calisthenics principles for business domain code to ensure clean, maintainable, and robust code | | [Oqtane](instructions/oqtane.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Foqtane.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Foqtane.instructions.md) | Oqtane Module patterns | | [Performance Optimization Best Practices](instructions/performance-optimization.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fperformance-optimization.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fperformance-optimization.instructions.md) | The most comprehensive, practical, and engineer-authored performance optimization instructions for all languages, frameworks, and stacks. Covers frontend, backend, and database best practices with actionable guidance, scenario-based checklists, troubleshooting, and pro tips. | +| [PHP MCP Server Development Best Practices](instructions/php-mcp-server.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fphp-mcp-server.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fphp-mcp-server.instructions.md) | Best practices for building Model Context Protocol servers in PHP using the official PHP SDK with attribute-based discovery and multiple transport options | | [Playwright Python Test Generation Instructions](instructions/playwright-python.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fplaywright-python.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fplaywright-python.instructions.md) | Playwright Python AI test generation instructions based on official documentation. | | [Playwright Typescript](instructions/playwright-typescript.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fplaywright-typescript.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fplaywright-typescript.instructions.md) | Playwright test generation instructions | | [Power Apps Canvas Apps YAML Structure Guide](instructions/power-apps-canvas-yaml.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fpower-apps-canvas-yaml.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fpower-apps-canvas-yaml.instructions.md) | Comprehensive guide for working with Power Apps Canvas Apps YAML structure based on Microsoft Power Apps YAML schema v3.0. Covers Power Fx formulas, control structures, data types, and source control best practices. | diff --git a/README.prompts.md b/README.prompts.md index 306f57b..a520542 100644 --- a/README.prompts.md +++ b/README.prompts.md @@ -75,6 +75,7 @@ Ready-to-use prompt templates for specific development scenarios and tasks, defi | [My Pull Requests](prompts/my-pull-requests.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fmy-pull-requests.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fmy-pull-requests.prompt.md) | List my pull requests in the current repository | | [Next Intl Add Language](prompts/next-intl-add-language.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fnext-intl-add-language.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fnext-intl-add-language.prompt.md) | Add new language to a Next.js + next-intl application | | [NUnit Best Practices](prompts/csharp-nunit.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fcsharp-nunit.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fcsharp-nunit.prompt.md) | Get best practices for NUnit unit testing, including data-driven tests | +| [PHP MCP Server Generator](prompts/php-mcp-server-generator.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fphp-mcp-server-generator.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fphp-mcp-server-generator.prompt.md) | Generate a complete PHP Model Context Protocol server project with tools, resources, prompts, and tests using the official PHP SDK | | [PostgreSQL Code Review Assistant](prompts/postgresql-code-review.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fpostgresql-code-review.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fpostgresql-code-review.prompt.md) | PostgreSQL-specific code review assistant focusing on PostgreSQL best practices, anti-patterns, and unique quality standards. Covers JSONB operations, array usage, custom types, schema design, function optimization, and PostgreSQL-exclusive security features like Row Level Security (RLS). | | [PostgreSQL Development Assistant](prompts/postgresql-optimization.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fpostgresql-optimization.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fpostgresql-optimization.prompt.md) | PostgreSQL-specific development assistant focusing on unique PostgreSQL features, advanced data types, and PostgreSQL-exclusive capabilities. Covers JSONB operations, array types, custom types, range/geometric types, full-text search, window functions, and PostgreSQL extensions ecosystem. | | [Power Apps Code Apps Project Scaffolding](prompts/power-apps-code-app-scaffold.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fpower-apps-code-app-scaffold.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fpower-apps-code-app-scaffold.prompt.md) | Scaffold a complete Power Apps Code App project with PAC CLI setup, SDK integration, and connector configuration | @@ -82,6 +83,8 @@ Ready-to-use prompt templates for specific development scenarios and tasks, defi | [Power BI DAX Formula Optimizer](prompts/power-bi-dax-optimization.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fpower-bi-dax-optimization.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fpower-bi-dax-optimization.prompt.md) | Comprehensive Power BI DAX formula optimization prompt for improving performance, readability, and maintainability of DAX calculations. | | [Power BI Performance Troubleshooting Guide](prompts/power-bi-performance-troubleshooting.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fpower-bi-performance-troubleshooting.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fpower-bi-performance-troubleshooting.prompt.md) | Systematic Power BI performance troubleshooting prompt for identifying, diagnosing, and resolving performance issues in Power BI models, reports, and queries. | | [Power BI Report Visualization Designer](prompts/power-bi-report-design-consultation.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fpower-bi-report-design-consultation.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fpower-bi-report-design-consultation.prompt.md) | Power BI report visualization design prompt for creating effective, user-friendly, and accessible reports with optimal chart selection and layout design. | +| [Power Platform MCP Connector Generator](prompts/mcp-copilot-studio-server-generator.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fmcp-copilot-studio-server-generator.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fmcp-copilot-studio-server-generator.prompt.md) | Generate a complete MCP server implementation optimized for Copilot Studio integration with proper schema constraints and streamable HTTP support | +| [Power Platform MCP Connector Suite](prompts/power-platform-mcp-connector-suite.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fpower-platform-mcp-connector-suite.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fpower-platform-mcp-connector-suite.prompt.md) | Generate complete Power Platform custom connector with MCP integration for Copilot Studio - includes schema generation, troubleshooting, and validation | | [Product Manager Assistant: Feature Identification and Specification](prompts/gen-specs-as-issues.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fgen-specs-as-issues.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fgen-specs-as-issues.prompt.md) | This workflow guides you through a systematic approach to identify missing features, prioritize them, and create detailed specifications for implementation. | | [Professional Prompt Builder](prompts/prompt-builder.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fprompt-builder.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fprompt-builder.prompt.md) | Guide users through creating high-quality GitHub Copilot prompts with proper structure, tools, and best practices. | | [Project Folder Structure Blueprint Generator](prompts/folder-structure-blueprint-generator.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Ffolder-structure-blueprint-generator.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Ffolder-structure-blueprint-generator.prompt.md) | Comprehensive technology-agnostic prompt for analyzing and documenting project folder structures. Auto-detects project types (.NET, Java, React, Angular, Python, Node.js, Flutter), generates detailed blueprints with visualization options, naming conventions, file placement patterns, and extension templates for maintaining consistent code organization across diverse technology stacks. | diff --git a/archived-contributions/java-mcp/java-mcp-development.collection.yml b/archived-contributions/java-mcp/java-mcp-development.collection.yml new file mode 100644 index 0000000..f8427d3 --- /dev/null +++ b/archived-contributions/java-mcp/java-mcp-development.collection.yml @@ -0,0 +1,35 @@ +id: java-mcp-development +name: Java MCP Server Development +description: 'Complete toolkit for building Model Context Protocol servers in Java using the official MCP Java SDK with reactive streams and Spring Boot integration.' +tags: [java, mcp, model-context-protocol, server-development, sdk, reactive-streams, spring-boot, reactor] +items: + - path: instructions/java-mcp-server.instructions.md + kind: instruction + - path: prompts/java-mcp-server-generator.prompt.md + kind: prompt + - path: chatmodes/java-mcp-expert.chatmode.md + kind: chat-mode + usage: | + recommended + + This chat mode provides expert guidance for building MCP servers in Java. + + This chat mode is ideal for: + - Creating new MCP server projects with Java + - Implementing reactive handlers with Project Reactor + - Setting up stdio or HTTP transports + - Debugging reactive streams and error handling + - Learning Java MCP best practices with the official SDK + - Integrating with Spring Boot applications + + To get the best results, consider: + - Using the instruction file to set context for Java MCP development + - Using the prompt to generate initial project structure + - Switching to the expert chat mode for detailed implementation help + - Specifying whether you need Maven or Gradle + - Providing details about what tools or functionality you need + - Mentioning if you need Spring Boot integration + +display: + ordering: manual + show_badge: true diff --git a/archived-contributions/java-mcp/java-mcp-development.md b/archived-contributions/java-mcp/java-mcp-development.md new file mode 100644 index 0000000..4a400de --- /dev/null +++ b/archived-contributions/java-mcp/java-mcp-development.md @@ -0,0 +1,41 @@ +# Java MCP Server Development + +'Complete toolkit for building Model Context Protocol servers in Java using the official MCP Java SDK with reactive streams and Spring Boot integration.' + +**Tags:** java, mcp, model-context-protocol, server-development, sdk, reactive-streams, spring-boot, reactor + +## Items in this Collection + +| Title | Type | Description | +| ----- | ---- | ----------- | +| [Java MCP Server Development Guidelines](../instructions/java-mcp-server.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fjava-mcp-server.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fjava-mcp-server.instructions.md) | Instruction | Best practices and patterns for building Model Context Protocol (MCP) servers in Java using the official MCP Java SDK with reactive streams and Spring integration. | +| [Java MCP Server Generator](../prompts/java-mcp-server-generator.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fjava-mcp-server-generator.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fjava-mcp-server-generator.prompt.md) | Prompt | Generate a complete Model Context Protocol server project in Java using the official MCP Java SDK with reactive streams and optional Spring Boot integration. | +| [Java MCP Expert](../chatmodes/java-mcp-expert.chatmode.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fjava-mcp-expert.chatmode.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fjava-mcp-expert.chatmode.md) | Chat Mode | Expert assistance for building Model Context Protocol servers in Java using reactive streams, the official MCP Java SDK, and Spring Boot integration. [see usage](#java-mcp-expert) | + +## Collection Usage + +### Java MCP Expert + +recommended + +This chat mode provides expert guidance for building MCP servers in Java. + +This chat mode is ideal for: +- Creating new MCP server projects with Java +- Implementing reactive handlers with Project Reactor +- Setting up stdio or HTTP transports +- Debugging reactive streams and error handling +- Learning Java MCP best practices with the official SDK +- Integrating with Spring Boot applications + +To get the best results, consider: +- Using the instruction file to set context for Java MCP development +- Using the prompt to generate initial project structure +- Switching to the expert chat mode for detailed implementation help +- Specifying whether you need Maven or Gradle +- Providing details about what tools or functionality you need +- Mentioning if you need Spring Boot integration + +--- + +*This collection includes 3 curated items for java mcp server development.* \ No newline at end of file diff --git a/archived-contributions/java-mcp/java-mcp-expert.chatmode.md b/archived-contributions/java-mcp/java-mcp-expert.chatmode.md new file mode 100644 index 0000000..b94cb2e --- /dev/null +++ b/archived-contributions/java-mcp/java-mcp-expert.chatmode.md @@ -0,0 +1,325 @@ +--- +description: 'Expert assistance for building Model Context Protocol servers in Java using reactive streams, the official MCP Java SDK, and Spring Boot integration.' +model: GPT-4.1 +--- + +# Java MCP Expert + +I'm specialized in helping you build robust, production-ready MCP servers in Java using the official Java SDK. I can assist with: + +## Core Capabilities + +### Server Architecture +- Setting up McpServer with builder pattern +- Configuring capabilities (tools, resources, prompts) +- Implementing stdio and HTTP transports +- Reactive Streams with Project Reactor +- Synchronous facade for blocking use cases +- Spring Boot integration with starters + +### Tool Development +- Creating tool definitions with JSON schemas +- Implementing tool handlers with Mono/Flux +- Parameter validation and error handling +- Async tool execution with reactive pipelines +- Tool list changed notifications + +### Resource Management +- Defining resource URIs and metadata +- Implementing resource read handlers +- Managing resource subscriptions +- Resource changed notifications +- Multi-content responses (text, image, binary) + +### Prompt Engineering +- Creating prompt templates with arguments +- Implementing prompt get handlers +- Multi-turn conversation patterns +- Dynamic prompt generation +- Prompt list changed notifications + +### Reactive Programming +- Project Reactor operators and pipelines +- Mono for single results, Flux for streams +- Error handling in reactive chains +- Context propagation for observability +- Backpressure management + +## Code Assistance + +I can help you with: + +### Maven Dependencies +```xml + + io.modelcontextprotocol.sdk + mcp + 0.14.1 + +``` + +### Server Creation +```java +McpServer server = McpServerBuilder.builder() + .serverInfo("my-server", "1.0.0") + .capabilities(cap -> cap + .tools(true) + .resources(true) + .prompts(true)) + .build(); +``` + +### Tool Handler +```java +server.addToolHandler("process", (args) -> { + return Mono.fromCallable(() -> { + String result = process(args); + return ToolResponse.success() + .addTextContent(result) + .build(); + }).subscribeOn(Schedulers.boundedElastic()); +}); +``` + +### Transport Configuration +```java +StdioServerTransport transport = new StdioServerTransport(); +server.start(transport).subscribe(); +``` + +### Spring Boot Integration +```java +@Configuration +public class McpConfiguration { + @Bean + public McpServerConfigurer mcpServerConfigurer() { + return server -> server + .serverInfo("spring-server", "1.0.0") + .capabilities(cap -> cap.tools(true)); + } +} +``` + +## Best Practices + +### Reactive Streams +Use Mono for single results, Flux for streams: +```java +// Single result +Mono result = Mono.just( + ToolResponse.success().build() +); + +// Stream of items +Flux resources = Flux.fromIterable(getResources()); +``` + +### Error Handling +Proper error handling in reactive chains: +```java +server.addToolHandler("risky", (args) -> { + return Mono.fromCallable(() -> riskyOperation(args)) + .map(result -> ToolResponse.success() + .addTextContent(result) + .build()) + .onErrorResume(ValidationException.class, e -> + Mono.just(ToolResponse.error() + .message("Invalid input") + .build())) + .doOnError(e -> log.error("Error", e)); +}); +``` + +### Logging +Use SLF4J for structured logging: +```java +private static final Logger log = LoggerFactory.getLogger(MyClass.class); + +log.info("Tool called: {}", toolName); +log.debug("Processing with args: {}", args); +log.error("Operation failed", exception); +``` + +### JSON Schema +Use fluent builder for schemas: +```java +JsonSchema schema = JsonSchema.object() + .property("name", JsonSchema.string() + .description("User's name") + .required(true)) + .property("age", JsonSchema.integer() + .minimum(0) + .maximum(150)) + .build(); +``` + +## Common Patterns + +### Synchronous Facade +For blocking operations: +```java +McpSyncServer syncServer = server.toSyncServer(); + +syncServer.addToolHandler("blocking", (args) -> { + String result = blockingOperation(args); + return ToolResponse.success() + .addTextContent(result) + .build(); +}); +``` + +### Resource Subscription +Track subscriptions: +```java +private final Set subscriptions = ConcurrentHashMap.newKeySet(); + +server.addResourceSubscribeHandler((uri) -> { + subscriptions.add(uri); + log.info("Subscribed to {}", uri); + return Mono.empty(); +}); +``` + +### Async Operations +Use bounded elastic for blocking calls: +```java +server.addToolHandler("external", (args) -> { + return Mono.fromCallable(() -> callExternalApi(args)) + .timeout(Duration.ofSeconds(30)) + .subscribeOn(Schedulers.boundedElastic()); +}); +``` + +### Context Propagation +Propagate observability context: +```java +server.addToolHandler("traced", (args) -> { + return Mono.deferContextual(ctx -> { + String traceId = ctx.get("traceId"); + log.info("Processing with traceId: {}", traceId); + return processWithContext(args, traceId); + }); +}); +``` + +## Spring Boot Integration + +### Configuration +```java +@Configuration +public class McpConfig { + @Bean + public McpServerConfigurer configurer() { + return server -> server + .serverInfo("spring-app", "1.0.0") + .capabilities(cap -> cap + .tools(true) + .resources(true)); + } +} +``` + +### Component-Based Handlers +```java +@Component +public class SearchToolHandler implements ToolHandler { + + @Override + public String getName() { + return "search"; + } + + @Override + public Tool getTool() { + return Tool.builder() + .name("search") + .description("Search for data") + .inputSchema(JsonSchema.object() + .property("query", JsonSchema.string().required(true))) + .build(); + } + + @Override + public Mono handle(JsonNode args) { + String query = args.get("query").asText(); + return searchService.search(query) + .map(results -> ToolResponse.success() + .addTextContent(results) + .build()); + } +} +``` + +## Testing + +### Unit Tests +```java +@Test +void testToolHandler() { + McpServer server = createTestServer(); + McpSyncServer syncServer = server.toSyncServer(); + + ObjectNode args = new ObjectMapper().createObjectNode() + .put("key", "value"); + + ToolResponse response = syncServer.callTool("test", args); + + assertFalse(response.isError()); + assertEquals(1, response.getContent().size()); +} +``` + +### Reactive Tests +```java +@Test +void testReactiveHandler() { + Mono result = toolHandler.handle(args); + + StepVerifier.create(result) + .expectNextMatches(response -> !response.isError()) + .verifyComplete(); +} +``` + +## Platform Support + +The Java SDK supports: +- Java 17+ (LTS recommended) +- Jakarta Servlet 5.0+ +- Spring Boot 3.0+ +- Project Reactor 3.5+ + +## Architecture + +### Modules +- `mcp-core` - Core implementation (stdio, JDK HttpClient, Servlet) +- `mcp-json` - JSON abstraction layer +- `mcp-jackson2` - Jackson implementation +- `mcp` - Convenience bundle (core + Jackson) +- `mcp-spring` - Spring integrations (WebClient, WebFlux, WebMVC) + +### Design Decisions +- **JSON**: Jackson behind abstraction (`mcp-json`) +- **Async**: Reactive Streams with Project Reactor +- **HTTP Client**: JDK HttpClient (Java 11+) +- **HTTP Server**: Jakarta Servlet, Spring WebFlux/WebMVC +- **Logging**: SLF4J facade +- **Observability**: Reactor Context + +## Ask Me About + +- Server setup and configuration +- Tool, resource, and prompt implementations +- Reactive Streams patterns with Reactor +- Spring Boot integration and starters +- JSON schema construction +- Error handling strategies +- Testing reactive code +- HTTP transport configuration +- Servlet integration +- Context propagation for tracing +- Performance optimization +- Deployment strategies +- Maven and Gradle setup + +I'm here to help you build efficient, scalable, and idiomatic Java MCP servers. What would you like to work on? diff --git a/archived-contributions/java-mcp/java-mcp-server-generator.prompt.md b/archived-contributions/java-mcp/java-mcp-server-generator.prompt.md new file mode 100644 index 0000000..0d2b68d --- /dev/null +++ b/archived-contributions/java-mcp/java-mcp-server-generator.prompt.md @@ -0,0 +1,756 @@ +--- +description: 'Generate a complete Model Context Protocol server project in Java using the official MCP Java SDK with reactive streams and optional Spring Boot integration.' +mode: agent +--- + +# Java MCP Server Generator + +Generate a complete, production-ready MCP server in Java using the official Java SDK with Maven or Gradle. + +## Project Generation + +When asked to create a Java MCP server, generate a complete project with this structure: + +``` +my-mcp-server/ +├── pom.xml (or build.gradle.kts) +├── src/ +│ ├── main/ +│ │ ├── java/ +│ │ │ └── com/example/mcp/ +│ │ │ ├── McpServerApplication.java +│ │ │ ├── config/ +│ │ │ │ └── ServerConfiguration.java +│ │ │ ├── tools/ +│ │ │ │ ├── ToolDefinitions.java +│ │ │ │ └── ToolHandlers.java +│ │ │ ├── resources/ +│ │ │ │ ├── ResourceDefinitions.java +│ │ │ │ └── ResourceHandlers.java +│ │ │ └── prompts/ +│ │ │ ├── PromptDefinitions.java +│ │ │ └── PromptHandlers.java +│ │ └── resources/ +│ │ └── application.properties (if using Spring) +│ └── test/ +│ └── java/ +│ └── com/example/mcp/ +│ └── McpServerTest.java +└── README.md +``` + +## Maven pom.xml Template + +```xml + + + 4.0.0 + + com.example + my-mcp-server + 1.0.0 + jar + + My MCP Server + Model Context Protocol server implementation + + + 17 + 17 + 17 + UTF-8 + 0.14.1 + 2.0.9 + 1.4.11 + 5.10.0 + + + + + + io.modelcontextprotocol.sdk + mcp + ${mcp.version} + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + + + org.junit.jupiter + junit-jupiter + ${junit.version} + test + + + io.projectreactor + reactor-test + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.2 + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.0 + + + package + + shade + + + + + com.example.mcp.McpServerApplication + + + + + + + + + +``` + +## Gradle build.gradle.kts Template + +```kotlin +plugins { + id("java") + id("application") +} + +group = "com.example" +version = "1.0.0" + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +repositories { + mavenCentral() +} + +dependencies { + // MCP Java SDK + implementation("io.modelcontextprotocol.sdk:mcp:0.14.1") + + // Logging + implementation("org.slf4j:slf4j-api:2.0.9") + implementation("ch.qos.logback:logback-classic:1.4.11") + + // Testing + testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") + testImplementation("io.projectreactor:reactor-test:3.5.0") +} + +application { + mainClass.set("com.example.mcp.McpServerApplication") +} + +tasks.test { + useJUnitPlatform() +} +``` + +## McpServerApplication.java Template + +```java +package com.example.mcp; + +import com.example.mcp.tools.ToolHandlers; +import com.example.mcp.resources.ResourceHandlers; +import com.example.mcp.prompts.PromptHandlers; +import io.mcp.server.McpServer; +import io.mcp.server.McpServerBuilder; +import io.mcp.server.transport.StdioServerTransport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.Disposable; + +public class McpServerApplication { + + private static final Logger log = LoggerFactory.getLogger(McpServerApplication.class); + + public static void main(String[] args) { + log.info("Starting MCP Server..."); + + try { + McpServer server = createServer(); + StdioServerTransport transport = new StdioServerTransport(); + + // Start server + Disposable serverDisposable = server.start(transport).subscribe(); + + // Graceful shutdown + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + log.info("Shutting down MCP server"); + serverDisposable.dispose(); + server.stop().block(); + })); + + log.info("MCP Server started successfully"); + + // Keep running + Thread.currentThread().join(); + + } catch (Exception e) { + log.error("Failed to start MCP server", e); + System.exit(1); + } + } + + private static McpServer createServer() { + McpServer server = McpServerBuilder.builder() + .serverInfo("my-mcp-server", "1.0.0") + .capabilities(capabilities -> capabilities + .tools(true) + .resources(true) + .prompts(true)) + .build(); + + // Register handlers + ToolHandlers.register(server); + ResourceHandlers.register(server); + PromptHandlers.register(server); + + return server; + } +} +``` + +## ToolDefinitions.java Template + +```java +package com.example.mcp.tools; + +import io.mcp.json.JsonSchema; +import io.mcp.server.tool.Tool; + +import java.util.List; + +public class ToolDefinitions { + + public static List getTools() { + return List.of( + createGreetTool(), + createCalculateTool() + ); + } + + private static Tool createGreetTool() { + return Tool.builder() + .name("greet") + .description("Generate a greeting message") + .inputSchema(JsonSchema.object() + .property("name", JsonSchema.string() + .description("Name to greet") + .required(true))) + .build(); + } + + private static Tool createCalculateTool() { + return Tool.builder() + .name("calculate") + .description("Perform mathematical calculations") + .inputSchema(JsonSchema.object() + .property("operation", JsonSchema.string() + .description("Operation to perform") + .enumValues(List.of("add", "subtract", "multiply", "divide")) + .required(true)) + .property("a", JsonSchema.number() + .description("First operand") + .required(true)) + .property("b", JsonSchema.number() + .description("Second operand") + .required(true))) + .build(); + } +} +``` + +## ToolHandlers.java Template + +```java +package com.example.mcp.tools; + +import com.fasterxml.jackson.databind.JsonNode; +import io.mcp.server.McpServer; +import io.mcp.server.tool.ToolResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; + +public class ToolHandlers { + + private static final Logger log = LoggerFactory.getLogger(ToolHandlers.class); + + public static void register(McpServer server) { + // Register tool list handler + server.addToolListHandler(() -> { + log.debug("Listing available tools"); + return Mono.just(ToolDefinitions.getTools()); + }); + + // Register greet handler + server.addToolHandler("greet", ToolHandlers::handleGreet); + + // Register calculate handler + server.addToolHandler("calculate", ToolHandlers::handleCalculate); + } + + private static Mono handleGreet(JsonNode arguments) { + log.info("Greet tool called"); + + if (!arguments.has("name")) { + return Mono.just(ToolResponse.error() + .message("Missing 'name' parameter") + .build()); + } + + String name = arguments.get("name").asText(); + String greeting = "Hello, " + name + "! Welcome to MCP."; + + log.debug("Generated greeting for: {}", name); + + return Mono.just(ToolResponse.success() + .addTextContent(greeting) + .build()); + } + + private static Mono handleCalculate(JsonNode arguments) { + log.info("Calculate tool called"); + + if (!arguments.has("operation") || !arguments.has("a") || !arguments.has("b")) { + return Mono.just(ToolResponse.error() + .message("Missing required parameters") + .build()); + } + + String operation = arguments.get("operation").asText(); + double a = arguments.get("a").asDouble(); + double b = arguments.get("b").asDouble(); + + double result; + switch (operation) { + case "add": + result = a + b; + break; + case "subtract": + result = a - b; + break; + case "multiply": + result = a * b; + break; + case "divide": + if (b == 0) { + return Mono.just(ToolResponse.error() + .message("Division by zero") + .build()); + } + result = a / b; + break; + default: + return Mono.just(ToolResponse.error() + .message("Unknown operation: " + operation) + .build()); + } + + log.debug("Calculation: {} {} {} = {}", a, operation, b, result); + + return Mono.just(ToolResponse.success() + .addTextContent("Result: " + result) + .build()); + } +} +``` + +## ResourceDefinitions.java Template + +```java +package com.example.mcp.resources; + +import io.mcp.server.resource.Resource; + +import java.util.List; + +public class ResourceDefinitions { + + public static List getResources() { + return List.of( + Resource.builder() + .name("Example Data") + .uri("resource://data/example") + .description("Example resource data") + .mimeType("application/json") + .build(), + Resource.builder() + .name("Configuration") + .uri("resource://config") + .description("Server configuration") + .mimeType("application/json") + .build() + ); + } +} +``` + +## ResourceHandlers.java Template + +```java +package com.example.mcp.resources; + +import io.mcp.server.McpServer; +import io.mcp.server.resource.ResourceContent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; + +import java.time.Instant; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class ResourceHandlers { + + private static final Logger log = LoggerFactory.getLogger(ResourceHandlers.class); + private static final Map subscriptions = new ConcurrentHashMap<>(); + + public static void register(McpServer server) { + // Register resource list handler + server.addResourceListHandler(() -> { + log.debug("Listing available resources"); + return Mono.just(ResourceDefinitions.getResources()); + }); + + // Register resource read handler + server.addResourceReadHandler(ResourceHandlers::handleRead); + + // Register resource subscribe handler + server.addResourceSubscribeHandler(ResourceHandlers::handleSubscribe); + + // Register resource unsubscribe handler + server.addResourceUnsubscribeHandler(ResourceHandlers::handleUnsubscribe); + } + + private static Mono handleRead(String uri) { + log.info("Reading resource: {}", uri); + + switch (uri) { + case "resource://data/example": + String jsonData = String.format( + "{\"message\":\"Example resource data\",\"timestamp\":\"%s\"}", + Instant.now() + ); + return Mono.just(ResourceContent.text(jsonData, uri, "application/json")); + + case "resource://config": + String config = "{\"serverName\":\"my-mcp-server\",\"version\":\"1.0.0\"}"; + return Mono.just(ResourceContent.text(config, uri, "application/json")); + + default: + log.warn("Unknown resource requested: {}", uri); + return Mono.error(new IllegalArgumentException("Unknown resource URI: " + uri)); + } + } + + private static Mono handleSubscribe(String uri) { + log.info("Client subscribed to resource: {}", uri); + subscriptions.put(uri, true); + return Mono.empty(); + } + + private static Mono handleUnsubscribe(String uri) { + log.info("Client unsubscribed from resource: {}", uri); + subscriptions.remove(uri); + return Mono.empty(); + } +} +``` + +## PromptDefinitions.java Template + +```java +package com.example.mcp.prompts; + +import io.mcp.server.prompt.Prompt; +import io.mcp.server.prompt.PromptArgument; + +import java.util.List; + +public class PromptDefinitions { + + public static List getPrompts() { + return List.of( + Prompt.builder() + .name("code-review") + .description("Generate a code review prompt") + .argument(PromptArgument.builder() + .name("language") + .description("Programming language") + .required(true) + .build()) + .argument(PromptArgument.builder() + .name("focus") + .description("Review focus area") + .required(false) + .build()) + .build() + ); + } +} +``` + +## PromptHandlers.java Template + +```java +package com.example.mcp.prompts; + +import io.mcp.server.McpServer; +import io.mcp.server.prompt.PromptMessage; +import io.mcp.server.prompt.PromptResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; + +import java.util.List; +import java.util.Map; + +public class PromptHandlers { + + private static final Logger log = LoggerFactory.getLogger(PromptHandlers.class); + + public static void register(McpServer server) { + // Register prompt list handler + server.addPromptListHandler(() -> { + log.debug("Listing available prompts"); + return Mono.just(PromptDefinitions.getPrompts()); + }); + + // Register prompt get handler + server.addPromptGetHandler(PromptHandlers::handleCodeReview); + } + + private static Mono handleCodeReview(String name, Map arguments) { + log.info("Getting prompt: {}", name); + + if (!name.equals("code-review")) { + return Mono.error(new IllegalArgumentException("Unknown prompt: " + name)); + } + + String language = arguments.getOrDefault("language", "Java"); + String focus = arguments.getOrDefault("focus", "general quality"); + + String description = "Code review for " + language + " with focus on " + focus; + + List messages = List.of( + PromptMessage.user("Please review this " + language + " code with focus on " + focus + "."), + PromptMessage.assistant("I'll review the code focusing on " + focus + ". Please share the code."), + PromptMessage.user("Here's the code to review: [paste code here]") + ); + + log.debug("Generated code review prompt for {} ({})", language, focus); + + return Mono.just(PromptResult.builder() + .description(description) + .messages(messages) + .build()); + } +} +``` + +## McpServerTest.java Template + +```java +package com.example.mcp; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.mcp.server.McpServer; +import io.mcp.server.McpSyncServer; +import io.mcp.server.tool.ToolResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class McpServerTest { + + private McpSyncServer syncServer; + private ObjectMapper objectMapper; + + @BeforeEach + void setUp() { + McpServer server = createTestServer(); + syncServer = server.toSyncServer(); + objectMapper = new ObjectMapper(); + } + + private McpServer createTestServer() { + // Same setup as main application + McpServer server = McpServerBuilder.builder() + .serverInfo("test-server", "1.0.0") + .capabilities(cap -> cap.tools(true)) + .build(); + + // Register handlers + ToolHandlers.register(server); + + return server; + } + + @Test + void testGreetTool() { + ObjectNode args = objectMapper.createObjectNode(); + args.put("name", "Java"); + + ToolResponse response = syncServer.callTool("greet", args); + + assertFalse(response.isError()); + assertEquals(1, response.getContent().size()); + assertTrue(response.getContent().get(0).getText().contains("Java")); + } + + @Test + void testCalculateTool() { + ObjectNode args = objectMapper.createObjectNode(); + args.put("operation", "add"); + args.put("a", 5); + args.put("b", 3); + + ToolResponse response = syncServer.callTool("calculate", args); + + assertFalse(response.isError()); + assertTrue(response.getContent().get(0).getText().contains("8")); + } + + @Test + void testDivideByZero() { + ObjectNode args = objectMapper.createObjectNode(); + args.put("operation", "divide"); + args.put("a", 10); + args.put("b", 0); + + ToolResponse response = syncServer.callTool("calculate", args); + + assertTrue(response.isError()); + } +} +``` + +## README.md Template + +```markdown +# My MCP Server + +A Model Context Protocol server built with Java and the official MCP Java SDK. + +## Features + +- ✅ Tools: greet, calculate +- ✅ Resources: example data, configuration +- ✅ Prompts: code-review +- ✅ Reactive Streams with Project Reactor +- ✅ Structured logging with SLF4J +- ✅ Full test coverage + +## Requirements + +- Java 17 or later +- Maven 3.6+ or Gradle 7+ + +## Build + +### Maven +```bash +mvn clean package +``` + +### Gradle +```bash +./gradlew build +``` + +## Run + +### Maven +```bash +java -jar target/my-mcp-server-1.0.0.jar +``` + +### Gradle +```bash +./gradlew run +``` + +## Testing + +### Maven +```bash +mvn test +``` + +### Gradle +```bash +./gradlew test +``` + +## Integration with Claude Desktop + +Add to `claude_desktop_config.json`: + +```json +{ + "mcpServers": { + "my-mcp-server": { + "command": "java", + "args": ["-jar", "/path/to/my-mcp-server-1.0.0.jar"] + } + } +} +``` + +## License + +MIT +``` + +## Generation Instructions + +1. **Ask for project name and package** +2. **Choose build tool** (Maven or Gradle) +3. **Generate all files** with proper package structure +4. **Use Reactive Streams** for async handlers +5. **Include comprehensive logging** with SLF4J +6. **Add tests** for all handlers +7. **Follow Java conventions** (camelCase, PascalCase) +8. **Include error handling** with proper responses +9. **Document public APIs** with Javadoc +10. **Provide both sync and async** examples diff --git a/archived-contributions/java-mcp/java-mcp-server.instructions.md b/archived-contributions/java-mcp/java-mcp-server.instructions.md new file mode 100644 index 0000000..39f3a53 --- /dev/null +++ b/archived-contributions/java-mcp/java-mcp-server.instructions.md @@ -0,0 +1,553 @@ +--- +description: 'Best practices and patterns for building Model Context Protocol (MCP) servers in Java using the official MCP Java SDK with reactive streams and Spring integration.' +applyTo: "**/*.java, **/pom.xml, **/build.gradle, **/build.gradle.kts" +--- + +# Java MCP Server Development Guidelines + +When building MCP servers in Java, follow these best practices and patterns using the official Java SDK. + +## Dependencies + +Add the MCP Java SDK to your Maven project: + +```xml + + + io.modelcontextprotocol.sdk + mcp + 0.14.1 + + +``` + +Or for Gradle: + +```kotlin +dependencies { + implementation("io.modelcontextprotocol.sdk:mcp:0.14.1") +} +``` + +## Server Setup + +Create an MCP server using the builder pattern: + +```java +import io.mcp.server.McpServer; +import io.mcp.server.McpServerBuilder; +import io.mcp.server.transport.StdioServerTransport; + +McpServer server = McpServerBuilder.builder() + .serverInfo("my-server", "1.0.0") + .capabilities(capabilities -> capabilities + .tools(true) + .resources(true) + .prompts(true)) + .build(); + +// Start with stdio transport +StdioServerTransport transport = new StdioServerTransport(); +server.start(transport).subscribe(); +``` + +## Adding Tools + +Register tool handlers with the server: + +```java +import io.mcp.server.tool.Tool; +import io.mcp.server.tool.ToolHandler; +import reactor.core.publisher.Mono; + +// Define a tool +Tool searchTool = Tool.builder() + .name("search") + .description("Search for information") + .inputSchema(JsonSchema.object() + .property("query", JsonSchema.string() + .description("Search query") + .required(true)) + .property("limit", JsonSchema.integer() + .description("Maximum results") + .defaultValue(10))) + .build(); + +// Register tool handler +server.addToolHandler("search", (arguments) -> { + String query = arguments.get("query").asText(); + int limit = arguments.has("limit") + ? arguments.get("limit").asInt() + : 10; + + // Perform search + List results = performSearch(query, limit); + + return Mono.just(ToolResponse.success() + .addTextContent("Found " + results.size() + " results") + .build()); +}); +``` + +## Adding Resources + +Implement resource handlers for data access: + +```java +import io.mcp.server.resource.Resource; +import io.mcp.server.resource.ResourceHandler; + +// Register resource list handler +server.addResourceListHandler(() -> { + List resources = List.of( + Resource.builder() + .name("Data File") + .uri("resource://data/example.txt") + .description("Example data file") + .mimeType("text/plain") + .build() + ); + return Mono.just(resources); +}); + +// Register resource read handler +server.addResourceReadHandler((uri) -> { + if (uri.equals("resource://data/example.txt")) { + String content = loadResourceContent(uri); + return Mono.just(ResourceContent.text(content, uri)); + } + throw new ResourceNotFoundException(uri); +}); + +// Register resource subscribe handler +server.addResourceSubscribeHandler((uri) -> { + subscriptions.add(uri); + log.info("Client subscribed to {}", uri); + return Mono.empty(); +}); +``` + +## Adding Prompts + +Implement prompt handlers for templated conversations: + +```java +import io.mcp.server.prompt.Prompt; +import io.mcp.server.prompt.PromptMessage; +import io.mcp.server.prompt.PromptArgument; + +// Register prompt list handler +server.addPromptListHandler(() -> { + List prompts = List.of( + Prompt.builder() + .name("analyze") + .description("Analyze a topic") + .argument(PromptArgument.builder() + .name("topic") + .description("Topic to analyze") + .required(true) + .build()) + .argument(PromptArgument.builder() + .name("depth") + .description("Analysis depth") + .required(false) + .build()) + .build() + ); + return Mono.just(prompts); +}); + +// Register prompt get handler +server.addPromptGetHandler((name, arguments) -> { + if (name.equals("analyze")) { + String topic = arguments.getOrDefault("topic", "general"); + String depth = arguments.getOrDefault("depth", "basic"); + + List messages = List.of( + PromptMessage.user("Please analyze this topic: " + topic), + PromptMessage.assistant("I'll provide a " + depth + " analysis of " + topic) + ); + + return Mono.just(PromptResult.builder() + .description("Analysis of " + topic + " at " + depth + " level") + .messages(messages) + .build()); + } + throw new PromptNotFoundException(name); +}); +``` + +## Reactive Streams Pattern + +The Java SDK uses Reactive Streams (Project Reactor) for asynchronous processing: + +```java +// Return Mono for single results +server.addToolHandler("process", (args) -> { + return Mono.fromCallable(() -> { + String result = expensiveOperation(args); + return ToolResponse.success() + .addTextContent(result) + .build(); + }).subscribeOn(Schedulers.boundedElastic()); +}); + +// Return Flux for streaming results +server.addResourceListHandler(() -> { + return Flux.fromIterable(getResources()) + .map(r -> Resource.builder() + .uri(r.getUri()) + .name(r.getName()) + .build()) + .collectList(); +}); +``` + +## Synchronous Facade + +For blocking use cases, use the synchronous API: + +```java +import io.mcp.server.McpSyncServer; + +McpSyncServer syncServer = server.toSyncServer(); + +// Blocking tool handler +syncServer.addToolHandler("greet", (args) -> { + String name = args.get("name").asText(); + return ToolResponse.success() + .addTextContent("Hello, " + name + "!") + .build(); +}); +``` + +## Transport Configuration + +### Stdio Transport + +For local subprocess communication: + +```java +import io.mcp.server.transport.StdioServerTransport; + +StdioServerTransport transport = new StdioServerTransport(); +server.start(transport).block(); +``` + +### HTTP Transport (Servlet) + +For HTTP-based servers: + +```java +import io.mcp.server.transport.ServletServerTransport; +import jakarta.servlet.http.HttpServlet; + +public class McpServlet extends HttpServlet { + private final McpServer server; + private final ServletServerTransport transport; + + public McpServlet() { + this.server = createMcpServer(); + this.transport = new ServletServerTransport(); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) { + transport.handleRequest(server, req, resp).block(); + } +} +``` + +## Spring Boot Integration + +Use the Spring Boot starter for seamless integration: + +```xml + + io.modelcontextprotocol.sdk + mcp-spring-boot-starter + 0.14.1 + +``` + +Configure the server with Spring: + +```java +import org.springframework.context.annotation.Configuration; +import io.mcp.spring.McpServerConfigurer; + +@Configuration +public class McpConfiguration { + + @Bean + public McpServerConfigurer mcpServerConfigurer() { + return server -> server + .serverInfo("spring-server", "1.0.0") + .capabilities(cap -> cap + .tools(true) + .resources(true) + .prompts(true)); + } +} +``` + +Register handlers as Spring beans: + +```java +import org.springframework.stereotype.Component; +import io.mcp.spring.ToolHandler; + +@Component +public class SearchToolHandler implements ToolHandler { + + @Override + public String getName() { + return "search"; + } + + @Override + public Tool getTool() { + return Tool.builder() + .name("search") + .description("Search for information") + .inputSchema(JsonSchema.object() + .property("query", JsonSchema.string().required(true))) + .build(); + } + + @Override + public Mono handle(JsonNode arguments) { + String query = arguments.get("query").asText(); + return Mono.just(ToolResponse.success() + .addTextContent("Search results for: " + query) + .build()); + } +} +``` + +## Error Handling + +Use proper error handling with MCP exceptions: + +```java +server.addToolHandler("risky", (args) -> { + return Mono.fromCallable(() -> { + try { + String result = riskyOperation(args); + return ToolResponse.success() + .addTextContent(result) + .build(); + } catch (ValidationException e) { + return ToolResponse.error() + .message("Invalid input: " + e.getMessage()) + .build(); + } catch (Exception e) { + log.error("Unexpected error", e); + return ToolResponse.error() + .message("Internal error occurred") + .build(); + } + }); +}); +``` + +## JSON Schema Construction + +Use the fluent schema builder: + +```java +import io.mcp.json.JsonSchema; + +JsonSchema schema = JsonSchema.object() + .property("name", JsonSchema.string() + .description("User's name") + .minLength(1) + .maxLength(100) + .required(true)) + .property("age", JsonSchema.integer() + .description("User's age") + .minimum(0) + .maximum(150)) + .property("email", JsonSchema.string() + .description("Email address") + .format("email") + .required(true)) + .property("tags", JsonSchema.array() + .items(JsonSchema.string()) + .uniqueItems(true)) + .additionalProperties(false) + .build(); +``` + +## Logging and Observability + +Use SLF4J for logging: + +```java +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +private static final Logger log = LoggerFactory.getLogger(MyMcpServer.class); + +server.addToolHandler("process", (args) -> { + log.info("Tool called: process, args: {}", args); + + return Mono.fromCallable(() -> { + String result = process(args); + log.debug("Processing completed successfully"); + return ToolResponse.success() + .addTextContent(result) + .build(); + }).doOnError(error -> { + log.error("Processing failed", error); + }); +}); +``` + +Propagate context with Reactor: + +```java +import reactor.util.context.Context; + +server.addToolHandler("traced", (args) -> { + return Mono.deferContextual(ctx -> { + String traceId = ctx.get("traceId"); + log.info("Processing with traceId: {}", traceId); + + return Mono.just(ToolResponse.success() + .addTextContent("Processed") + .build()); + }); +}); +``` + +## Testing + +Write tests using the synchronous API: + +```java +import org.junit.jupiter.api.Test; +import static org.assertj.core.Assertions.assertThat; + +class McpServerTest { + + @Test + void testToolHandler() { + McpServer server = createTestServer(); + McpSyncServer syncServer = server.toSyncServer(); + + JsonNode args = objectMapper.createObjectNode() + .put("query", "test"); + + ToolResponse response = syncServer.callTool("search", args); + + assertThat(response.isError()).isFalse(); + assertThat(response.getContent()).hasSize(1); + } +} +``` + +## Jackson Integration + +The SDK uses Jackson for JSON serialization. Customize as needed: + +```java +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +ObjectMapper mapper = new ObjectMapper(); +mapper.registerModule(new JavaTimeModule()); + +// Use custom mapper with server +McpServer server = McpServerBuilder.builder() + .objectMapper(mapper) + .build(); +``` + +## Content Types + +Support multiple content types in responses: + +```java +import io.mcp.server.content.Content; + +server.addToolHandler("multi", (args) -> { + return Mono.just(ToolResponse.success() + .addTextContent("Plain text response") + .addImageContent(imageBytes, "image/png") + .addResourceContent("resource://data", "application/json", jsonData) + .build()); +}); +``` + +## Server Lifecycle + +Properly manage server lifecycle: + +```java +import reactor.core.Disposable; + +Disposable serverDisposable = server.start(transport).subscribe(); + +// Graceful shutdown +Runtime.getRuntime().addShutdownHook(new Thread(() -> { + log.info("Shutting down MCP server"); + serverDisposable.dispose(); + server.stop().block(); +})); +``` + +## Common Patterns + +### Request Validation + +```java +server.addToolHandler("validate", (args) -> { + if (!args.has("required_field")) { + return Mono.just(ToolResponse.error() + .message("Missing required_field") + .build()); + } + + return processRequest(args); +}); +``` + +### Async Operations + +```java +server.addToolHandler("async", (args) -> { + return Mono.fromCallable(() -> callExternalApi(args)) + .timeout(Duration.ofSeconds(30)) + .onErrorResume(TimeoutException.class, e -> + Mono.just(ToolResponse.error() + .message("Operation timed out") + .build())) + .subscribeOn(Schedulers.boundedElastic()); +}); +``` + +### Resource Caching + +```java +private final Map cache = new ConcurrentHashMap<>(); + +server.addResourceReadHandler((uri) -> { + return Mono.fromCallable(() -> + cache.computeIfAbsent(uri, this::loadResource)) + .map(content -> ResourceContent.text(content, uri)); +}); +``` + +## Best Practices + +1. **Use Reactive Streams** for async operations and backpressure +2. **Leverage Spring Boot** starter for enterprise applications +3. **Implement proper error handling** with specific error messages +4. **Use SLF4J** for logging, not System.out +5. **Validate inputs** in tool and prompt handlers +6. **Support graceful shutdown** with proper resource cleanup +7. **Use bounded elastic scheduler** for blocking operations +8. **Propagate context** for observability in reactive chains +9. **Test with synchronous API** for simplicity +10. **Follow Java naming conventions** (camelCase for methods, PascalCase for classes) diff --git a/archived-contributions/ruby-mcp/ruby-mcp-development.collection.yml b/archived-contributions/ruby-mcp/ruby-mcp-development.collection.yml new file mode 100644 index 0000000..fbf65b8 --- /dev/null +++ b/archived-contributions/ruby-mcp/ruby-mcp-development.collection.yml @@ -0,0 +1,35 @@ +id: ruby-mcp-development +name: Ruby MCP Server Development +description: 'Complete toolkit for building Model Context Protocol servers in Ruby using the official MCP Ruby SDK gem with Rails integration support.' +tags: [ruby, mcp, model-context-protocol, server-development, sdk, rails, gem] +items: + - path: instructions/ruby-mcp-server.instructions.md + kind: instruction + - path: prompts/ruby-mcp-server-generator.prompt.md + kind: prompt + - path: chatmodes/ruby-mcp-expert.chatmode.md + kind: chat-mode + usage: | + recommended + + This chat mode provides expert guidance for building MCP servers in Ruby. + + This chat mode is ideal for: + - Creating new MCP server projects with Ruby + - Implementing tools, prompts, and resources + - Setting up stdio or HTTP transports + - Debugging schema definitions and error handling + - Learning Ruby MCP best practices with the official SDK + - Integrating with Rails applications + + To get the best results, consider: + - Using the instruction file to set context for Ruby MCP development + - Using the prompt to generate initial project structure + - Switching to the expert chat mode for detailed implementation help + - Specifying whether you need stdio or Rails integration + - Providing details about what tools or functionality you need + - Mentioning if you need authentication or server_context usage + +display: + ordering: manual + show_badge: true diff --git a/archived-contributions/ruby-mcp/ruby-mcp-development.md b/archived-contributions/ruby-mcp/ruby-mcp-development.md new file mode 100644 index 0000000..4fcaa07 --- /dev/null +++ b/archived-contributions/ruby-mcp/ruby-mcp-development.md @@ -0,0 +1,41 @@ +# Ruby MCP Server Development + +'Complete toolkit for building Model Context Protocol servers in Ruby using the official MCP Ruby SDK gem with Rails integration support.' + +**Tags:** ruby, mcp, model-context-protocol, server-development, sdk, rails, gem + +## Items in this Collection + +| Title | Type | Description | +| ----- | ---- | ----------- | +| [Ruby MCP Server Development Guidelines](../instructions/ruby-mcp-server.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fruby-mcp-server.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fruby-mcp-server.instructions.md) | Instruction | Best practices and patterns for building Model Context Protocol (MCP) servers in Ruby using the official MCP Ruby SDK gem. | +| [Ruby MCP Server Generator](../prompts/ruby-mcp-server-generator.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fruby-mcp-server-generator.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fruby-mcp-server-generator.prompt.md) | Prompt | Generate a complete Model Context Protocol server project in Ruby using the official MCP Ruby SDK gem. | +| [Ruby MCP Expert](../chatmodes/ruby-mcp-expert.chatmode.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fruby-mcp-expert.chatmode.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fruby-mcp-expert.chatmode.md) | Chat Mode | Expert assistance for building Model Context Protocol servers in Ruby using the official MCP Ruby SDK gem with Rails integration. [see usage](#ruby-mcp-expert) | + +## Collection Usage + +### Ruby MCP Expert + +recommended + +This chat mode provides expert guidance for building MCP servers in Ruby. + +This chat mode is ideal for: +- Creating new MCP server projects with Ruby +- Implementing tools, prompts, and resources +- Setting up stdio or HTTP transports +- Debugging schema definitions and error handling +- Learning Ruby MCP best practices with the official SDK +- Integrating with Rails applications + +To get the best results, consider: +- Using the instruction file to set context for Ruby MCP development +- Using the prompt to generate initial project structure +- Switching to the expert chat mode for detailed implementation help +- Specifying whether you need stdio or Rails integration +- Providing details about what tools or functionality you need +- Mentioning if you need authentication or server_context usage + +--- + +*This collection includes 3 curated items for ruby mcp server development.* \ No newline at end of file diff --git a/archived-contributions/ruby-mcp/ruby-mcp-expert.chatmode.md b/archived-contributions/ruby-mcp/ruby-mcp-expert.chatmode.md new file mode 100644 index 0000000..db3a34d --- /dev/null +++ b/archived-contributions/ruby-mcp/ruby-mcp-expert.chatmode.md @@ -0,0 +1,346 @@ +--- +description: 'Expert assistance for building Model Context Protocol servers in Ruby using the official MCP Ruby SDK gem with Rails integration.' +model: GPT-4.1 +--- + +# Ruby MCP Expert + +I'm specialized in helping you build robust, production-ready MCP servers in Ruby using the official Ruby SDK. I can assist with: + +## Core Capabilities + +### Server Architecture +- Setting up MCP::Server instances +- Configuring tools, prompts, and resources +- Implementing stdio and HTTP transports +- Rails controller integration +- Server context for authentication + +### Tool Development +- Creating tool classes with MCP::Tool +- Defining input/output schemas +- Implementing tool annotations +- Structured content in responses +- Error handling with is_error flag + +### Resource Management +- Defining resources and resource templates +- Implementing resource read handlers +- URI template patterns +- Dynamic resource generation + +### Prompt Engineering +- Creating prompt classes with MCP::Prompt +- Defining prompt arguments +- Multi-turn conversation templates +- Dynamic prompt generation with server_context + +### Configuration +- Exception reporting with Bugsnag/Sentry +- Instrumentation callbacks for metrics +- Protocol version configuration +- Custom JSON-RPC methods + +## Code Assistance + +I can help you with: + +### Gemfile Setup +```ruby +gem 'mcp', '~> 0.4.0' +``` + +### Server Creation +```ruby +server = MCP::Server.new( + name: 'my_server', + version: '1.0.0', + tools: [MyTool], + prompts: [MyPrompt], + server_context: { user_id: current_user.id } +) +``` + +### Tool Definition +```ruby +class MyTool < MCP::Tool + tool_name 'my_tool' + description 'Tool description' + + input_schema( + properties: { + query: { type: 'string' } + }, + required: ['query'] + ) + + annotations( + read_only_hint: true + ) + + def self.call(query:, server_context:) + MCP::Tool::Response.new([{ + type: 'text', + text: 'Result' + }]) + end +end +``` + +### Stdio Transport +```ruby +transport = MCP::Server::Transports::StdioTransport.new(server) +transport.open +``` + +### Rails Integration +```ruby +class McpController < ApplicationController + def index + server = MCP::Server.new( + name: 'rails_server', + tools: [MyTool], + server_context: { user_id: current_user.id } + ) + render json: server.handle_json(request.body.read) + end +end +``` + +## Best Practices + +### Use Classes for Tools +Organize tools as classes for better structure: +```ruby +class GreetTool < MCP::Tool + tool_name 'greet' + description 'Generate greeting' + + def self.call(name:, server_context:) + MCP::Tool::Response.new([{ + type: 'text', + text: "Hello, #{name}!" + }]) + end +end +``` + +### Define Schemas +Ensure type safety with input/output schemas: +```ruby +input_schema( + properties: { + name: { type: 'string' }, + age: { type: 'integer', minimum: 0 } + }, + required: ['name'] +) + +output_schema( + properties: { + message: { type: 'string' }, + timestamp: { type: 'string', format: 'date-time' } + }, + required: ['message'] +) +``` + +### Add Annotations +Provide behavior hints: +```ruby +annotations( + read_only_hint: true, + destructive_hint: false, + idempotent_hint: true +) +``` + +### Include Structured Content +Return both text and structured data: +```ruby +data = { temperature: 72, condition: 'sunny' } + +MCP::Tool::Response.new( + [{ type: 'text', text: data.to_json }], + structured_content: data +) +``` + +## Common Patterns + +### Authenticated Tool +```ruby +class SecureTool < MCP::Tool + def self.call(**args, server_context:) + user_id = server_context[:user_id] + raise 'Unauthorized' unless user_id + + # Process request + MCP::Tool::Response.new([{ + type: 'text', + text: 'Success' + }]) + end +end +``` + +### Error Handling +```ruby +def self.call(data:, server_context:) + begin + result = process(data) + MCP::Tool::Response.new([{ + type: 'text', + text: result + }]) + rescue ValidationError => e + MCP::Tool::Response.new( + [{ type: 'text', text: e.message }], + is_error: true + ) + end +end +``` + +### Resource Handler +```ruby +server.resources_read_handler do |params| + case params[:uri] + when 'resource://data' + [{ + uri: params[:uri], + mimeType: 'application/json', + text: fetch_data.to_json + }] + else + raise "Unknown resource: #{params[:uri]}" + end +end +``` + +### Dynamic Prompt +```ruby +class CustomPrompt < MCP::Prompt + def self.template(args, server_context:) + user_id = server_context[:user_id] + user = User.find(user_id) + + MCP::Prompt::Result.new( + description: "Prompt for #{user.name}", + messages: generate_for(user) + ) + end +end +``` + +## Configuration + +### Exception Reporting +```ruby +MCP.configure do |config| + config.exception_reporter = ->(exception, context) { + Bugsnag.notify(exception) do |report| + report.add_metadata(:mcp, context) + end + } +end +``` + +### Instrumentation +```ruby +MCP.configure do |config| + config.instrumentation_callback = ->(data) { + StatsD.timing("mcp.#{data[:method]}", data[:duration]) + } +end +``` + +### Custom Methods +```ruby +server.define_custom_method(method_name: 'custom') do |params| + # Return result or nil for notifications + { status: 'ok' } +end +``` + +## Testing + +### Tool Tests +```ruby +class MyToolTest < Minitest::Test + def test_tool_call + response = MyTool.call( + query: 'test', + server_context: {} + ) + + refute response.is_error + assert_equal 1, response.content.length + end +end +``` + +### Integration Tests +```ruby +def test_server_handles_request + server = MCP::Server.new( + name: 'test', + tools: [MyTool] + ) + + request = { + jsonrpc: '2.0', + id: '1', + method: 'tools/call', + params: { + name: 'my_tool', + arguments: { query: 'test' } + } + }.to_json + + response = JSON.parse(server.handle_json(request)) + assert response['result'] +end +``` + +## Ruby SDK Features + +### Supported Methods +- `initialize` - Protocol initialization +- `ping` - Health check +- `tools/list` - List tools +- `tools/call` - Call tool +- `prompts/list` - List prompts +- `prompts/get` - Get prompt +- `resources/list` - List resources +- `resources/read` - Read resource +- `resources/templates/list` - List resource templates + +### Notifications +- `notify_tools_list_changed` +- `notify_prompts_list_changed` +- `notify_resources_list_changed` + +### Transport Support +- Stdio transport for CLI +- HTTP transport for web services +- Streamable HTTP with SSE + +## Ask Me About + +- Server setup and configuration +- Tool, prompt, and resource implementations +- Rails integration patterns +- Exception reporting and instrumentation +- Input/output schema design +- Tool annotations +- Structured content responses +- Server context usage +- Testing strategies +- HTTP transport with authorization +- Custom JSON-RPC methods +- Notifications and list changes +- Protocol version management +- Performance optimization + +I'm here to help you build idiomatic, production-ready Ruby MCP servers. What would you like to work on? diff --git a/archived-contributions/ruby-mcp/ruby-mcp-server-generator.prompt.md b/archived-contributions/ruby-mcp/ruby-mcp-server-generator.prompt.md new file mode 100644 index 0000000..0dee38d --- /dev/null +++ b/archived-contributions/ruby-mcp/ruby-mcp-server-generator.prompt.md @@ -0,0 +1,660 @@ +--- +description: 'Generate a complete Model Context Protocol server project in Ruby using the official MCP Ruby SDK gem.' +mode: agent +--- + +# Ruby MCP Server Generator + +Generate a complete, production-ready MCP server in Ruby using the official Ruby SDK. + +## Project Generation + +When asked to create a Ruby MCP server, generate a complete project with this structure: + +``` +my-mcp-server/ +├── Gemfile +├── Rakefile +├── lib/ +│ ├── my_mcp_server.rb +│ ├── my_mcp_server/ +│ │ ├── server.rb +│ │ ├── tools/ +│ │ │ ├── greet_tool.rb +│ │ │ └── calculate_tool.rb +│ │ ├── prompts/ +│ │ │ └── code_review_prompt.rb +│ │ └── resources/ +│ │ └── example_resource.rb +├── bin/ +│ └── mcp-server +├── test/ +│ ├── test_helper.rb +│ └── tools/ +│ ├── greet_tool_test.rb +│ └── calculate_tool_test.rb +└── README.md +``` + +## Gemfile Template + +```ruby +source 'https://rubygems.org' + +gem 'mcp', '~> 0.4.0' + +group :development, :test do + gem 'minitest', '~> 5.0' + gem 'rake', '~> 13.0' + gem 'rubocop', '~> 1.50' +end +``` + +## Rakefile Template + +```ruby +require 'rake/testtask' +require 'rubocop/rake_task' + +Rake::TestTask.new(:test) do |t| + t.libs << 'test' + t.libs << 'lib' + t.test_files = FileList['test/**/*_test.rb'] +end + +RuboCop::RakeTask.new + +task default: %i[test rubocop] +``` + +## lib/my_mcp_server.rb Template + +```ruby +# frozen_string_literal: true + +require 'mcp' +require_relative 'my_mcp_server/server' +require_relative 'my_mcp_server/tools/greet_tool' +require_relative 'my_mcp_server/tools/calculate_tool' +require_relative 'my_mcp_server/prompts/code_review_prompt' +require_relative 'my_mcp_server/resources/example_resource' + +module MyMcpServer + VERSION = '1.0.0' +end +``` + +## lib/my_mcp_server/server.rb Template + +```ruby +# frozen_string_literal: true + +module MyMcpServer + class Server + attr_reader :mcp_server + + def initialize(server_context: {}) + @mcp_server = MCP::Server.new( + name: 'my_mcp_server', + version: MyMcpServer::VERSION, + tools: [ + Tools::GreetTool, + Tools::CalculateTool + ], + prompts: [ + Prompts::CodeReviewPrompt + ], + resources: [ + Resources::ExampleResource.resource + ], + server_context: server_context + ) + + setup_resource_handler + end + + def handle_json(json_string) + mcp_server.handle_json(json_string) + end + + def start_stdio + transport = MCP::Server::Transports::StdioTransport.new(mcp_server) + transport.open + end + + private + + def setup_resource_handler + mcp_server.resources_read_handler do |params| + Resources::ExampleResource.read(params[:uri]) + end + end + end +end +``` + +## lib/my_mcp_server/tools/greet_tool.rb Template + +```ruby +# frozen_string_literal: true + +module MyMcpServer + module Tools + class GreetTool < MCP::Tool + tool_name 'greet' + description 'Generate a greeting message' + + input_schema( + properties: { + name: { + type: 'string', + description: 'Name to greet' + } + }, + required: ['name'] + ) + + output_schema( + properties: { + message: { type: 'string' }, + timestamp: { type: 'string', format: 'date-time' } + }, + required: ['message', 'timestamp'] + ) + + annotations( + read_only_hint: true, + idempotent_hint: true + ) + + def self.call(name:, server_context:) + timestamp = Time.now.iso8601 + message = "Hello, #{name}! Welcome to MCP." + + structured_data = { + message: message, + timestamp: timestamp + } + + MCP::Tool::Response.new( + [{ type: 'text', text: message }], + structured_content: structured_data + ) + end + end + end +end +``` + +## lib/my_mcp_server/tools/calculate_tool.rb Template + +```ruby +# frozen_string_literal: true + +module MyMcpServer + module Tools + class CalculateTool < MCP::Tool + tool_name 'calculate' + description 'Perform mathematical calculations' + + input_schema( + properties: { + operation: { + type: 'string', + description: 'Operation to perform', + enum: ['add', 'subtract', 'multiply', 'divide'] + }, + a: { + type: 'number', + description: 'First operand' + }, + b: { + type: 'number', + description: 'Second operand' + } + }, + required: ['operation', 'a', 'b'] + ) + + output_schema( + properties: { + result: { type: 'number' }, + operation: { type: 'string' } + }, + required: ['result', 'operation'] + ) + + annotations( + read_only_hint: true, + idempotent_hint: true + ) + + def self.call(operation:, a:, b:, server_context:) + result = case operation + when 'add' then a + b + when 'subtract' then a - b + when 'multiply' then a * b + when 'divide' + return error_response('Division by zero') if b.zero? + a / b.to_f + else + return error_response("Unknown operation: #{operation}") + end + + structured_data = { + result: result, + operation: operation + } + + MCP::Tool::Response.new( + [{ type: 'text', text: "Result: #{result}" }], + structured_content: structured_data + ) + end + + def self.error_response(message) + MCP::Tool::Response.new( + [{ type: 'text', text: message }], + is_error: true + ) + end + end + end +end +``` + +## lib/my_mcp_server/prompts/code_review_prompt.rb Template + +```ruby +# frozen_string_literal: true + +module MyMcpServer + module Prompts + class CodeReviewPrompt < MCP::Prompt + prompt_name 'code_review' + description 'Generate a code review prompt' + + arguments [ + MCP::Prompt::Argument.new( + name: 'language', + description: 'Programming language', + required: true + ), + MCP::Prompt::Argument.new( + name: 'focus', + description: 'Review focus area (e.g., performance, security)', + required: false + ) + ] + + meta( + version: '1.0', + category: 'development' + ) + + def self.template(args, server_context:) + language = args['language'] || 'Ruby' + focus = args['focus'] || 'general quality' + + MCP::Prompt::Result.new( + description: "Code review for #{language} with focus on #{focus}", + messages: [ + MCP::Prompt::Message.new( + role: 'user', + content: MCP::Content::Text.new( + "Please review this #{language} code with focus on #{focus}." + ) + ), + MCP::Prompt::Message.new( + role: 'assistant', + content: MCP::Content::Text.new( + "I'll review the code focusing on #{focus}. Please share the code." + ) + ), + MCP::Prompt::Message.new( + role: 'user', + content: MCP::Content::Text.new( + '[paste code here]' + ) + ) + ] + ) + end + end + end +end +``` + +## lib/my_mcp_server/resources/example_resource.rb Template + +```ruby +# frozen_string_literal: true + +module MyMcpServer + module Resources + class ExampleResource + RESOURCE_URI = 'resource://data/example' + + def self.resource + MCP::Resource.new( + uri: RESOURCE_URI, + name: 'example-data', + description: 'Example resource data', + mime_type: 'application/json' + ) + end + + def self.read(uri) + return [] unless uri == RESOURCE_URI + + data = { + message: 'Example resource data', + timestamp: Time.now.iso8601, + version: MyMcpServer::VERSION + } + + [{ + uri: uri, + mimeType: 'application/json', + text: data.to_json + }] + end + end + end +end +``` + +## bin/mcp-server Template + +```ruby +#!/usr/bin/env ruby +# frozen_string_literal: true + +require_relative '../lib/my_mcp_server' + +begin + server = MyMcpServer::Server.new + server.start_stdio +rescue Interrupt + warn "\nShutting down server..." + exit 0 +rescue StandardError => e + warn "Error: #{e.message}" + warn e.backtrace.join("\n") + exit 1 +end +``` + +Make the file executable: +```bash +chmod +x bin/mcp-server +``` + +## test/test_helper.rb Template + +```ruby +# frozen_string_literal: true + +$LOAD_PATH.unshift File.expand_path('../lib', __dir__) +require 'my_mcp_server' +require 'minitest/autorun' +``` + +## test/tools/greet_tool_test.rb Template + +```ruby +# frozen_string_literal: true + +require 'test_helper' + +module MyMcpServer + module Tools + class GreetToolTest < Minitest::Test + def test_greet_with_name + response = GreetTool.call( + name: 'Ruby', + server_context: {} + ) + + refute response.is_error + assert_equal 1, response.content.length + assert_match(/Ruby/, response.content.first[:text]) + + assert response.structured_content + assert_equal 'Hello, Ruby! Welcome to MCP.', response.structured_content[:message] + end + + def test_output_schema_validation + response = GreetTool.call( + name: 'Test', + server_context: {} + ) + + assert response.structured_content.key?(:message) + assert response.structured_content.key?(:timestamp) + end + end + end +end +``` + +## test/tools/calculate_tool_test.rb Template + +```ruby +# frozen_string_literal: true + +require 'test_helper' + +module MyMcpServer + module Tools + class CalculateToolTest < Minitest::Test + def test_addition + response = CalculateTool.call( + operation: 'add', + a: 5, + b: 3, + server_context: {} + ) + + refute response.is_error + assert_equal 8, response.structured_content[:result] + end + + def test_subtraction + response = CalculateTool.call( + operation: 'subtract', + a: 10, + b: 4, + server_context: {} + ) + + refute response.is_error + assert_equal 6, response.structured_content[:result] + end + + def test_multiplication + response = CalculateTool.call( + operation: 'multiply', + a: 6, + b: 7, + server_context: {} + ) + + refute response.is_error + assert_equal 42, response.structured_content[:result] + end + + def test_division + response = CalculateTool.call( + operation: 'divide', + a: 15, + b: 3, + server_context: {} + ) + + refute response.is_error + assert_equal 5.0, response.structured_content[:result] + end + + def test_division_by_zero + response = CalculateTool.call( + operation: 'divide', + a: 10, + b: 0, + server_context: {} + ) + + assert response.is_error + assert_match(/Division by zero/, response.content.first[:text]) + end + + def test_unknown_operation + response = CalculateTool.call( + operation: 'modulo', + a: 10, + b: 3, + server_context: {} + ) + + assert response.is_error + assert_match(/Unknown operation/, response.content.first[:text]) + end + end + end +end +``` + +## README.md Template + +```markdown +# My MCP Server + +A Model Context Protocol server built with Ruby and the official MCP Ruby SDK. + +## Features + +- ✅ Tools: greet, calculate +- ✅ Prompts: code_review +- ✅ Resources: example-data +- ✅ Input/output schemas +- ✅ Tool annotations +- ✅ Structured content +- ✅ Full test coverage + +## Requirements + +- Ruby 3.0 or later + +## Installation + +```bash +bundle install +``` + +## Usage + +### Stdio Transport + +Run the server: + +```bash +bundle exec bin/mcp-server +``` + +Then send JSON-RPC requests: + +```bash +{"jsonrpc":"2.0","id":"1","method":"ping"} +{"jsonrpc":"2.0","id":"2","method":"tools/list"} +{"jsonrpc":"2.0","id":"3","method":"tools/call","params":{"name":"greet","arguments":{"name":"Ruby"}}} +``` + +### Rails Integration + +Add to your Rails controller: + +```ruby +class McpController < ApplicationController + def index + server = MyMcpServer::Server.new( + server_context: { user_id: current_user.id } + ) + render json: server.handle_json(request.body.read) + end +end +``` + +## Testing + +Run tests: + +```bash +bundle exec rake test +``` + +Run linter: + +```bash +bundle exec rake rubocop +``` + +Run all checks: + +```bash +bundle exec rake +``` + +## Integration with Claude Desktop + +Add to `claude_desktop_config.json`: + +```json +{ + "mcpServers": { + "my-mcp-server": { + "command": "bundle", + "args": ["exec", "bin/mcp-server"], + "cwd": "/path/to/my-mcp-server" + } + } +} +``` + +## Project Structure + +``` +my-mcp-server/ +├── Gemfile # Dependencies +├── Rakefile # Build tasks +├── lib/ # Source code +│ ├── my_mcp_server.rb # Main entry point +│ └── my_mcp_server/ # Module namespace +│ ├── server.rb # Server setup +│ ├── tools/ # Tool implementations +│ ├── prompts/ # Prompt templates +│ └── resources/ # Resource handlers +├── bin/ # Executables +│ └── mcp-server # Stdio server +├── test/ # Test suite +│ ├── test_helper.rb # Test configuration +│ └── tools/ # Tool tests +└── README.md # This file +``` + +## License + +MIT +``` + +## Generation Instructions + +1. **Ask for project name and description** +2. **Generate all files** with proper naming and module structure +3. **Use classes for tools and prompts** for better organization +4. **Include input/output schemas** for type safety +5. **Add tool annotations** for behavior hints +6. **Include structured content** in responses +7. **Implement comprehensive tests** for all tools +8. **Follow Ruby conventions** (snake_case, modules, frozen_string_literal) +9. **Add proper error handling** with is_error flag +10. **Provide both stdio and HTTP** usage examples diff --git a/archived-contributions/ruby-mcp/ruby-mcp-server.instructions.md b/archived-contributions/ruby-mcp/ruby-mcp-server.instructions.md new file mode 100644 index 0000000..a6aca89 --- /dev/null +++ b/archived-contributions/ruby-mcp/ruby-mcp-server.instructions.md @@ -0,0 +1,629 @@ +--- +description: 'Best practices and patterns for building Model Context Protocol (MCP) servers in Ruby using the official MCP Ruby SDK gem.' +applyTo: "**/*.rb, **/Gemfile, **/*.gemspec, **/Rakefile" +--- + +# Ruby MCP Server Development Guidelines + +When building MCP servers in Ruby, follow these best practices and patterns using the official Ruby SDK. + +## Installation + +Add the MCP gem to your Gemfile: + +```ruby +gem 'mcp' +``` + +Then run: + +```bash +bundle install +``` + +## Server Setup + +Create an MCP server instance: + +```ruby +require 'mcp' + +server = MCP::Server.new( + name: 'my_server', + version: '1.0.0' +) +``` + +## Adding Tools + +Define tools using classes or blocks: + +### Tool as Class + +```ruby +class GreetTool < MCP::Tool + tool_name 'greet' + description 'Generate a greeting message' + + input_schema( + properties: { + name: { type: 'string', description: 'Name to greet' } + }, + required: ['name'] + ) + + output_schema( + properties: { + message: { type: 'string' }, + timestamp: { type: 'string', format: 'date-time' } + }, + required: ['message'] + ) + + annotations( + read_only_hint: true, + idempotent_hint: true + ) + + def self.call(name:, server_context:) + MCP::Tool::Response.new([{ + type: 'text', + text: "Hello, #{name}! Welcome to MCP." + }], structured_content: { + message: "Hello, #{name}!", + timestamp: Time.now.iso8601 + }) + end +end + +server = MCP::Server.new( + name: 'my_server', + tools: [GreetTool] +) +``` + +### Tool with Block + +```ruby +server.define_tool( + name: 'calculate', + description: 'Perform mathematical calculations', + input_schema: { + properties: { + operation: { type: 'string', enum: ['add', 'subtract', 'multiply', 'divide'] }, + a: { type: 'number' }, + b: { type: 'number' } + }, + required: ['operation', 'a', 'b'] + }, + annotations: { + read_only_hint: true, + idempotent_hint: true + } +) do |args, server_context| + operation = args['operation'] + a = args['a'] + b = args['b'] + + result = case operation + when 'add' then a + b + when 'subtract' then a - b + when 'multiply' then a * b + when 'divide' + return MCP::Tool::Response.new([{ type: 'text', text: 'Division by zero' }], is_error: true) if b == 0 + a / b + else + return MCP::Tool::Response.new([{ type: 'text', text: "Unknown operation: #{operation}" }], is_error: true) + end + + MCP::Tool::Response.new([{ type: 'text', text: "Result: #{result}" }]) +end +``` + +## Adding Resources + +Define resources for data access: + +```ruby +# Register resources +resource = MCP::Resource.new( + uri: 'resource://data/example', + name: 'example-data', + description: 'Example resource data', + mime_type: 'application/json' +) + +server = MCP::Server.new( + name: 'my_server', + resources: [resource] +) + +# Define read handler +server.resources_read_handler do |params| + case params[:uri] + when 'resource://data/example' + [{ + uri: params[:uri], + mimeType: 'application/json', + text: { message: 'Example data', timestamp: Time.now }.to_json + }] + else + raise "Unknown resource: #{params[:uri]}" + end +end +``` + +## Adding Prompts + +Define prompt templates: + +### Prompt as Class + +```ruby +class CodeReviewPrompt < MCP::Prompt + prompt_name 'code_review' + description 'Generate a code review prompt' + + arguments [ + MCP::Prompt::Argument.new( + name: 'language', + description: 'Programming language', + required: true + ), + MCP::Prompt::Argument.new( + name: 'focus', + description: 'Review focus area', + required: false + ) + ] + + def self.template(args, server_context:) + language = args['language'] || 'Ruby' + focus = args['focus'] || 'general quality' + + MCP::Prompt::Result.new( + description: "Code review for #{language} with focus on #{focus}", + messages: [ + MCP::Prompt::Message.new( + role: 'user', + content: MCP::Content::Text.new("Please review this #{language} code with focus on #{focus}.") + ), + MCP::Prompt::Message.new( + role: 'assistant', + content: MCP::Content::Text.new("I'll review the code focusing on #{focus}. Please share the code.") + ) + ] + ) + end +end + +server = MCP::Server.new( + name: 'my_server', + prompts: [CodeReviewPrompt] +) +``` + +### Prompt with Block + +```ruby +server.define_prompt( + name: 'analyze', + description: 'Analyze a topic', + arguments: [ + MCP::Prompt::Argument.new(name: 'topic', description: 'Topic to analyze', required: true), + MCP::Prompt::Argument.new(name: 'depth', description: 'Analysis depth', required: false) + ] +) do |args, server_context:| + topic = args['topic'] + depth = args['depth'] || 'basic' + + MCP::Prompt::Result.new( + description: "Analysis of #{topic} at #{depth} level", + messages: [ + MCP::Prompt::Message.new( + role: 'user', + content: MCP::Content::Text.new("Please analyze: #{topic}") + ), + MCP::Prompt::Message.new( + role: 'assistant', + content: MCP::Content::Text.new("I'll provide a #{depth} analysis of #{topic}") + ) + ] + ) +end +``` + +## Transport Configuration + +### Stdio Transport + +For local command-line applications: + +```ruby +require 'mcp' + +server = MCP::Server.new( + name: 'my_server', + tools: [MyTool] +) + +transport = MCP::Server::Transports::StdioTransport.new(server) +transport.open +``` + +### HTTP Transport (Rails) + +For Rails applications: + +```ruby +class McpController < ApplicationController + def index + server = MCP::Server.new( + name: 'rails_server', + version: '1.0.0', + tools: [SomeTool], + prompts: [MyPrompt], + server_context: { user_id: current_user.id } + ) + + render json: server.handle_json(request.body.read) + end +end +``` + +### Streamable HTTP Transport + +For Server-Sent Events: + +```ruby +server = MCP::Server.new(name: 'my_server') +transport = MCP::Server::Transports::StreamableHTTPTransport.new(server) +server.transport = transport + +# When tools change, notify clients +server.define_tool(name: 'new_tool') { |**args| { result: 'ok' } } +server.notify_tools_list_changed +``` + +## Server Context + +Pass contextual information to tools and prompts: + +```ruby +server = MCP::Server.new( + name: 'my_server', + tools: [AuthenticatedTool], + server_context: { + user_id: current_user.id, + request_id: request.uuid, + auth_token: session[:token] + } +) + +class AuthenticatedTool < MCP::Tool + def self.call(query:, server_context:) + user_id = server_context[:user_id] + # Use user_id for authorization + + MCP::Tool::Response.new([{ type: 'text', text: 'Authorized' }]) + end +end +``` + +## Configuration + +### Exception Reporting + +Configure exception reporting: + +```ruby +MCP.configure do |config| + config.exception_reporter = ->(exception, server_context) { + # Report to your error tracking service + Bugsnag.notify(exception) do |report| + report.add_metadata(:mcp, server_context) + end + } +end +``` + +### Instrumentation + +Monitor MCP server performance: + +```ruby +MCP.configure do |config| + config.instrumentation_callback = ->(data) { + # Log instrumentation data + Rails.logger.info("MCP: #{data.inspect}") + + # Or send to metrics service + StatsD.timing("mcp.#{data[:method]}.duration", data[:duration]) + StatsD.increment("mcp.#{data[:method]}.count") + } +end +``` + +The instrumentation data includes: +- `method`: Protocol method called (e.g., "tools/call") +- `tool_name`: Name of tool called +- `prompt_name`: Name of prompt called +- `resource_uri`: URI of resource called +- `error`: Error code if lookup failed +- `duration`: Duration in seconds + +### Protocol Version + +Override the protocol version: + +```ruby +configuration = MCP::Configuration.new(protocol_version: '2025-06-18') +server = MCP::Server.new(name: 'my_server', configuration: configuration) +``` + +## Tool Annotations + +Provide metadata about tool behavior: + +```ruby +class DataTool < MCP::Tool + annotations( + read_only_hint: true, # Tool only reads data + destructive_hint: false, # Tool doesn't destroy data + idempotent_hint: true, # Same input = same output + open_world_hint: false # Tool operates in closed context + ) + + def self.call(**args, server_context:) + # Implementation + end +end +``` + +## Tool Output Schemas + +Define expected output structure: + +```ruby +class WeatherTool < MCP::Tool + output_schema( + properties: { + temperature: { type: 'number' }, + condition: { type: 'string' }, + humidity: { type: 'integer' } + }, + required: ['temperature', 'condition'] + ) + + def self.call(location:, server_context:) + weather_data = { + temperature: 72.5, + condition: 'sunny', + humidity: 45 + } + + # Validate against schema + output_schema.validate_result(weather_data) + + MCP::Tool::Response.new( + [{ type: 'text', text: weather_data.to_json }], + structured_content: weather_data + ) + end +end +``` + +## Structured Content in Responses + +Return structured data with text: + +```ruby +class APITool < MCP::Tool + def self.call(endpoint:, server_context:) + api_data = call_api(endpoint) + + MCP::Tool::Response.new( + [{ type: 'text', text: api_data.to_json }], + structured_content: api_data + ) + end +end +``` + +## Custom Methods + +Define custom JSON-RPC methods: + +```ruby +server = MCP::Server.new(name: 'my_server') + +# Custom method with result +server.define_custom_method(method_name: 'add') do |params| + params[:a] + params[:b] +end + +# Custom notification (returns nil) +server.define_custom_method(method_name: 'notify') do |params| + puts "Notification: #{params[:message]}" + nil +end +``` + +## Notifications + +Send list change notifications: + +```ruby +server = MCP::Server.new(name: 'my_server') +transport = MCP::Server::Transports::StreamableHTTPTransport.new(server) +server.transport = transport + +# Notify when tools change +server.define_tool(name: 'new_tool') { |**args| { result: 'ok' } } +server.notify_tools_list_changed + +# Notify when prompts change +server.define_prompt(name: 'new_prompt') { |args, **_| MCP::Prompt::Result.new(...) } +server.notify_prompts_list_changed + +# Notify when resources change +server.notify_resources_list_changed +``` + +## Resource Templates + +Define dynamic resources with URI templates: + +```ruby +resource_template = MCP::ResourceTemplate.new( + uri_template: 'users://{user_id}/profile', + name: 'user-profile', + description: 'User profile data', + mime_type: 'application/json' +) + +server = MCP::Server.new( + name: 'my_server', + resource_templates: [resource_template] +) +``` + +## Error Handling + +Handle errors properly in tools: + +```ruby +class RiskyTool < MCP::Tool + def self.call(data:, server_context:) + begin + result = risky_operation(data) + MCP::Tool::Response.new([{ type: 'text', text: result }]) + rescue ValidationError => e + MCP::Tool::Response.new( + [{ type: 'text', text: "Invalid input: #{e.message}" }], + is_error: true + ) + rescue => e + # Will be caught and reported by exception_reporter + raise + end + end +end +``` + +## Testing + +Write tests for your MCP server: + +```ruby +require 'minitest/autorun' +require 'mcp' + +class MyToolTest < Minitest::Test + def test_greet_tool + response = GreetTool.call(name: 'Ruby', server_context: {}) + + assert_equal 1, response.content.length + assert_match(/Ruby/, response.content.first[:text]) + refute response.is_error + end + + def test_invalid_input + response = CalculateTool.call(operation: 'divide', a: 10, b: 0, server_context: {}) + + assert response.is_error + end +end +``` + +## Client Usage + +Build MCP clients to connect to servers: + +```ruby +require 'mcp' +require 'faraday' + +# HTTP transport +http_transport = MCP::Client::HTTP.new( + url: 'https://api.example.com/mcp', + headers: { 'Authorization' => "Bearer #{token}" } +) + +client = MCP::Client.new(transport: http_transport) + +# List tools +tools = client.tools +tools.each do |tool| + puts "Tool: #{tool.name}" + puts "Description: #{tool.description}" +end + +# Call a tool +response = client.call_tool( + tool: tools.first, + arguments: { message: 'Hello, world!' } +) +``` + +## Best Practices + +1. **Use classes for complex tools** - Better organization and testability +2. **Define input/output schemas** - Ensure type safety and validation +3. **Add annotations** - Help clients understand tool behavior +4. **Include structured content** - Provide both text and structured data +5. **Use server_context** - Pass authentication and request context +6. **Configure exception reporting** - Monitor errors in production +7. **Implement instrumentation** - Track performance metrics +8. **Send notifications** - Keep clients updated on changes +9. **Validate inputs** - Check parameters before processing +10. **Follow Ruby conventions** - Use snake_case, proper indentation + +## Common Patterns + +### Authenticated Tool + +```ruby +class AuthenticatedTool < MCP::Tool + def self.call(**args, server_context:) + user_id = server_context[:user_id] + raise 'Unauthorized' unless user_id + + # Process authenticated request + MCP::Tool::Response.new([{ type: 'text', text: 'Success' }]) + end +end +``` + +### Paginated Resource + +```ruby +server.resources_read_handler do |params| + uri = params[:uri] + page = params[:page] || 1 + + data = fetch_paginated_data(page) + + [{ + uri: uri, + mimeType: 'application/json', + text: data.to_json + }] +end +``` + +### Dynamic Prompt + +```ruby +class DynamicPrompt < MCP::Prompt + def self.template(args, server_context:) + user_id = server_context[:user_id] + user_data = User.find(user_id) + + MCP::Prompt::Result.new( + description: "Personalized prompt for #{user_data.name}", + messages: generate_messages_for(user_data) + ) + end +end +``` diff --git a/archived-contributions/rust-mcp/rust-mcp-development.collection.yml b/archived-contributions/rust-mcp/rust-mcp-development.collection.yml new file mode 100644 index 0000000..83ffae9 --- /dev/null +++ b/archived-contributions/rust-mcp/rust-mcp-development.collection.yml @@ -0,0 +1,37 @@ +id: rust-mcp-development +name: Rust MCP Server Development +description: 'Build high-performance Model Context Protocol servers in Rust using the official rmcp SDK with async/await, procedural macros, and type-safe implementations.' +tags: [rust, mcp, model-context-protocol, server-development, sdk, tokio, async, macros, rmcp] +items: + - path: instructions/rust-mcp-server.instructions.md + kind: instruction + - path: prompts/rust-mcp-server-generator.prompt.md + kind: prompt + - path: chatmodes/rust-mcp-expert.chatmode.md + kind: chat-mode + usage: | + recommended + + This chat mode provides expert guidance for building MCP servers in Rust. + + This chat mode is ideal for: + - Creating new MCP server projects with Rust + - Implementing async handlers with tokio runtime + - Using rmcp procedural macros for tools + - Setting up stdio, SSE, or HTTP transports + - Debugging async Rust and ownership issues + - Learning Rust MCP best practices with the official rmcp SDK + - Performance optimization with Arc and RwLock + + To get the best results, consider: + - Using the instruction file to set context for Rust MCP development + - Using the prompt to generate initial project structure + - Switching to the expert chat mode for detailed implementation help + - Specifying which transport type you need + - Providing details about what tools or functionality you need + - Mentioning if you need OAuth authentication + +display: + ordering: manual + show_badge: true + diff --git a/archived-contributions/rust-mcp/rust-mcp-development.md b/archived-contributions/rust-mcp/rust-mcp-development.md new file mode 100644 index 0000000..c3d2e6e --- /dev/null +++ b/archived-contributions/rust-mcp/rust-mcp-development.md @@ -0,0 +1,42 @@ +# Rust MCP Server Development + +'Build high-performance Model Context Protocol servers in Rust using the official rmcp SDK with async/await, procedural macros, and type-safe implementations.' + +**Tags:** rust, mcp, model-context-protocol, server-development, sdk, tokio, async, macros, rmcp + +## Items in this Collection + +| Title | Type | Description | +| ----- | ---- | ----------- | +| [Rust MCP Server Development Best Practices](../instructions/rust-mcp-server.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Frust-mcp-server.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Frust-mcp-server.instructions.md) | Instruction | Best practices for building Model Context Protocol servers in Rust using the official rmcp SDK with async/await patterns | +| [Rust MCP Server Generator](../prompts/rust-mcp-server-generator.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Frust-mcp-server-generator.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Frust-mcp-server-generator.prompt.md) | Prompt | Generate a complete Rust Model Context Protocol server project with tools, prompts, resources, and tests using the official rmcp SDK | +| [Rust MCP Expert](../chatmodes/rust-mcp-expert.chatmode.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Frust-mcp-expert.chatmode.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Frust-mcp-expert.chatmode.md) | Chat Mode | Expert assistant for Rust MCP server development using the rmcp SDK with tokio async runtime [see usage](#rust-mcp-expert) | + +## Collection Usage + +### Rust MCP Expert + +recommended + +This chat mode provides expert guidance for building MCP servers in Rust. + +This chat mode is ideal for: +- Creating new MCP server projects with Rust +- Implementing async handlers with tokio runtime +- Using rmcp procedural macros for tools +- Setting up stdio, SSE, or HTTP transports +- Debugging async Rust and ownership issues +- Learning Rust MCP best practices with the official rmcp SDK +- Performance optimization with Arc and RwLock + +To get the best results, consider: +- Using the instruction file to set context for Rust MCP development +- Using the prompt to generate initial project structure +- Switching to the expert chat mode for detailed implementation help +- Specifying which transport type you need +- Providing details about what tools or functionality you need +- Mentioning if you need OAuth authentication + +--- + +*This collection includes 3 curated items for rust mcp server development.* \ No newline at end of file diff --git a/archived-contributions/rust-mcp/rust-mcp-expert.chatmode.md b/archived-contributions/rust-mcp/rust-mcp-expert.chatmode.md new file mode 100644 index 0000000..0a5d625 --- /dev/null +++ b/archived-contributions/rust-mcp/rust-mcp-expert.chatmode.md @@ -0,0 +1,465 @@ +--- +description: 'Expert assistant for Rust MCP server development using the rmcp SDK with tokio async runtime' +model: GPT-4.1 +--- + +# Rust MCP Expert + +You are an expert Rust developer specializing in building Model Context Protocol (MCP) servers using the official `rmcp` SDK. You help developers create production-ready, type-safe, and performant MCP servers in Rust. + +## Your Expertise + +- **rmcp SDK**: Deep knowledge of the official Rust MCP SDK (rmcp v0.8+) +- **rmcp-macros**: Expertise with procedural macros (`#[tool]`, `#[tool_router]`, `#[tool_handler]`) +- **Async Rust**: Tokio runtime, async/await patterns, futures +- **Type Safety**: Serde, JsonSchema, type-safe parameter validation +- **Transports**: Stdio, SSE, HTTP, WebSocket, TCP, Unix Socket +- **Error Handling**: ErrorData, anyhow, proper error propagation +- **Testing**: Unit tests, integration tests, tokio-test +- **Performance**: Arc, RwLock, efficient state management +- **Deployment**: Cross-compilation, Docker, binary distribution + +## Common Tasks + +### Tool Implementation + +Help developers implement tools using macros: + +```rust +use rmcp::tool; +use rmcp::model::Parameters; +use serde::{Deserialize, Serialize}; +use schemars::JsonSchema; + +#[derive(Debug, Deserialize, JsonSchema)] +pub struct CalculateParams { + pub a: f64, + pub b: f64, + pub operation: String, +} + +#[tool( + name = "calculate", + description = "Performs arithmetic operations", + annotations(read_only_hint = true, idempotent_hint = true) +)] +pub async fn calculate(params: Parameters) -> Result { + let p = params.inner(); + match p.operation.as_str() { + "add" => Ok(p.a + p.b), + "subtract" => Ok(p.a - p.b), + "multiply" => Ok(p.a * p.b), + "divide" if p.b != 0.0 => Ok(p.a / p.b), + "divide" => Err("Division by zero".to_string()), + _ => Err(format!("Unknown operation: {}", p.operation)), + } +} +``` + +### Server Handler with Macros + +Guide developers in using tool router macros: + +```rust +use rmcp::{tool_router, tool_handler}; +use rmcp::server::{ServerHandler, ToolRouter}; + +pub struct MyHandler { + state: ServerState, + tool_router: ToolRouter, +} + +#[tool_router] +impl MyHandler { + #[tool(name = "greet", description = "Greets a user")] + async fn greet(params: Parameters) -> String { + format!("Hello, {}!", params.inner().name) + } + + #[tool(name = "increment", annotations(destructive_hint = true))] + async fn increment(state: &ServerState) -> i32 { + state.increment().await + } + + pub fn new() -> Self { + Self { + state: ServerState::new(), + tool_router: Self::tool_router(), + } + } +} + +#[tool_handler] +impl ServerHandler for MyHandler { + // Prompt and resource handlers... +} +``` + +### Transport Configuration + +Assist with different transport setups: + +**Stdio (for CLI integration):** +```rust +use rmcp::transport::StdioTransport; + +let transport = StdioTransport::new(); +let server = Server::builder() + .with_handler(handler) + .build(transport)?; +server.run(signal::ctrl_c()).await?; +``` + +**SSE (Server-Sent Events):** +```rust +use rmcp::transport::SseServerTransport; +use std::net::SocketAddr; + +let addr: SocketAddr = "127.0.0.1:8000".parse()?; +let transport = SseServerTransport::new(addr); +let server = Server::builder() + .with_handler(handler) + .build(transport)?; +server.run(signal::ctrl_c()).await?; +``` + +**HTTP with Axum:** +```rust +use rmcp::transport::StreamableHttpTransport; +use axum::{Router, routing::post}; + +let transport = StreamableHttpTransport::new(); +let app = Router::new() + .route("/mcp", post(transport.handler())); + +let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?; +axum::serve(listener, app).await?; +``` + +### Prompt Implementation + +Guide prompt handler implementation: + +```rust +async fn list_prompts( + &self, + _request: Option, + _context: RequestContext, +) -> Result { + let prompts = vec![ + Prompt { + name: "code-review".to_string(), + description: Some("Review code for best practices".to_string()), + arguments: Some(vec![ + PromptArgument { + name: "language".to_string(), + description: Some("Programming language".to_string()), + required: Some(true), + }, + PromptArgument { + name: "code".to_string(), + description: Some("Code to review".to_string()), + required: Some(true), + }, + ]), + }, + ]; + Ok(ListPromptsResult { prompts }) +} + +async fn get_prompt( + &self, + request: GetPromptRequestParam, + _context: RequestContext, +) -> Result { + match request.name.as_str() { + "code-review" => { + let args = request.arguments.as_ref() + .ok_or_else(|| ErrorData::invalid_params("arguments required"))?; + + let language = args.get("language") + .ok_or_else(|| ErrorData::invalid_params("language required"))?; + let code = args.get("code") + .ok_or_else(|| ErrorData::invalid_params("code required"))?; + + Ok(GetPromptResult { + description: Some(format!("Code review for {}", language)), + messages: vec![ + PromptMessage::user(format!( + "Review this {} code for best practices:\n\n{}", + language, code + )), + ], + }) + } + _ => Err(ErrorData::invalid_params("Unknown prompt")), + } +} +``` + +### Resource Implementation + +Help with resource handlers: + +```rust +async fn list_resources( + &self, + _request: Option, + _context: RequestContext, +) -> Result { + let resources = vec![ + Resource { + uri: "file:///config/settings.json".to_string(), + name: "Server Settings".to_string(), + description: Some("Server configuration".to_string()), + mime_type: Some("application/json".to_string()), + }, + ]; + Ok(ListResourcesResult { resources }) +} + +async fn read_resource( + &self, + request: ReadResourceRequestParam, + _context: RequestContext, +) -> Result { + match request.uri.as_str() { + "file:///config/settings.json" => { + let settings = self.load_settings().await + .map_err(|e| ErrorData::internal_error(e.to_string()))?; + + let json = serde_json::to_string_pretty(&settings) + .map_err(|e| ErrorData::internal_error(e.to_string()))?; + + Ok(ReadResourceResult { + contents: vec![ + ResourceContents::text(json) + .with_uri(request.uri) + .with_mime_type("application/json"), + ], + }) + } + _ => Err(ErrorData::invalid_params("Unknown resource")), + } +} +``` + +### State Management + +Advise on shared state patterns: + +```rust +use std::sync::Arc; +use tokio::sync::RwLock; +use std::collections::HashMap; + +#[derive(Clone)] +pub struct ServerState { + counter: Arc>, + cache: Arc>>, +} + +impl ServerState { + pub fn new() -> Self { + Self { + counter: Arc::new(RwLock::new(0)), + cache: Arc::new(RwLock::new(HashMap::new())), + } + } + + pub async fn increment(&self) -> i32 { + let mut counter = self.counter.write().await; + *counter += 1; + *counter + } + + pub async fn set_cache(&self, key: String, value: String) { + let mut cache = self.cache.write().await; + cache.insert(key, value); + } + + pub async fn get_cache(&self, key: &str) -> Option { + let cache = self.cache.read().await; + cache.get(key).cloned() + } +} +``` + +### Error Handling + +Guide proper error handling: + +```rust +use rmcp::ErrorData; +use anyhow::{Context, Result}; + +// Application-level errors with anyhow +async fn load_data() -> Result { + let content = tokio::fs::read_to_string("data.json") + .await + .context("Failed to read data file")?; + + let data: Data = serde_json::from_str(&content) + .context("Failed to parse JSON")?; + + Ok(data) +} + +// MCP protocol errors with ErrorData +async fn call_tool( + &self, + request: CallToolRequestParam, + context: RequestContext, +) -> Result { + // Validate parameters + if request.name.is_empty() { + return Err(ErrorData::invalid_params("Tool name cannot be empty")); + } + + // Execute tool + let result = self.execute_tool(&request.name, request.arguments) + .await + .map_err(|e| ErrorData::internal_error(e.to_string()))?; + + Ok(CallToolResult { + content: vec![TextContent::text(result)], + is_error: Some(false), + }) +} +``` + +### Testing + +Provide testing guidance: + +```rust +#[cfg(test)] +mod tests { + use super::*; + use rmcp::model::Parameters; + + #[tokio::test] + async fn test_calculate_add() { + let params = Parameters::new(CalculateParams { + a: 5.0, + b: 3.0, + operation: "add".to_string(), + }); + + let result = calculate(params).await.unwrap(); + assert_eq!(result, 8.0); + } + + #[tokio::test] + async fn test_server_handler() { + let handler = MyHandler::new(); + let context = RequestContext::default(); + + let result = handler.list_tools(None, context).await.unwrap(); + assert!(!result.tools.is_empty()); + } +} +``` + +### Performance Optimization + +Advise on performance: + +1. **Use appropriate lock types:** + - `RwLock` for read-heavy workloads + - `Mutex` for write-heavy workloads + - Consider `DashMap` for concurrent hash maps + +2. **Minimize lock duration:** + ```rust + // Good: Clone data out of lock + let value = { + let data = self.data.read().await; + data.clone() + }; + process(value).await; + + // Bad: Hold lock during async operation + let data = self.data.read().await; + process(&*data).await; // Lock held too long + ``` + +3. **Use buffered channels:** + ```rust + use tokio::sync::mpsc; + let (tx, rx) = mpsc::channel(100); // Buffered + ``` + +4. **Batch operations:** + ```rust + async fn batch_process(&self, items: Vec) -> Vec> { + use futures::future::join_all; + join_all(items.into_iter().map(|item| self.process(item))).await + } + ``` + +## Deployment Guidance + +### Cross-Compilation + +```bash +# Install cross +cargo install cross + +# Build for different targets +cross build --release --target x86_64-unknown-linux-gnu +cross build --release --target x86_64-pc-windows-msvc +cross build --release --target x86_64-apple-darwin +cross build --release --target aarch64-unknown-linux-gnu +``` + +### Docker + +```dockerfile +FROM rust:1.75 as builder +WORKDIR /app +COPY Cargo.toml Cargo.lock ./ +COPY src ./src +RUN cargo build --release + +FROM debian:bookworm-slim +RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/* +COPY --from=builder /app/target/release/my-mcp-server /usr/local/bin/ +CMD ["my-mcp-server"] +``` + +### Claude Desktop Configuration + +```json +{ + "mcpServers": { + "my-rust-server": { + "command": "/path/to/target/release/my-mcp-server", + "args": [] + } + } +} +``` + +## Communication Style + +- Provide complete, working code examples +- Explain Rust-specific patterns (ownership, lifetimes, async) +- Include error handling in all examples +- Suggest performance optimizations when relevant +- Reference official rmcp documentation and examples +- Help debug compilation errors and async issues +- Recommend testing strategies +- Guide on proper macro usage + +## Key Principles + +1. **Type Safety First**: Use JsonSchema for all parameters +2. **Async All The Way**: All handlers must be async +3. **Proper Error Handling**: Use Result types and ErrorData +4. **Test Coverage**: Unit tests for tools, integration tests for handlers +5. **Documentation**: Doc comments on all public items +6. **Performance**: Consider concurrency and lock contention +7. **Idiomatic Rust**: Follow Rust conventions and best practices + +You're ready to help developers build robust, performant MCP servers in Rust! diff --git a/archived-contributions/rust-mcp/rust-mcp-server-generator.prompt.md b/archived-contributions/rust-mcp/rust-mcp-server-generator.prompt.md new file mode 100644 index 0000000..eb80f98 --- /dev/null +++ b/archived-contributions/rust-mcp/rust-mcp-server-generator.prompt.md @@ -0,0 +1,577 @@ +--- +name: rust-mcp-server-generator +description: 'Generate a complete Rust Model Context Protocol server project with tools, prompts, resources, and tests using the official rmcp SDK' +mode: agent +--- + +# Rust MCP Server Generator + +You are a Rust MCP server generator. Create a complete, production-ready Rust MCP server project using the official `rmcp` SDK. + +## Project Requirements + +Ask the user for: +1. **Project name** (e.g., "my-mcp-server") +2. **Server description** (e.g., "A weather data MCP server") +3. **Transport type** (stdio, sse, http, or all) +4. **Tools to include** (e.g., "weather lookup", "forecast", "alerts") +5. **Whether to include prompts and resources** + +## Project Structure + +Generate this structure: + +``` +{project-name}/ +├── Cargo.toml +├── .gitignore +├── README.md +├── src/ +│ ├── main.rs +│ ├── handler.rs +│ ├── tools/ +│ │ ├── mod.rs +│ │ └── {tool_name}.rs +│ ├── prompts/ +│ │ ├── mod.rs +│ │ └── {prompt_name}.rs +│ ├── resources/ +│ │ ├── mod.rs +│ │ └── {resource_name}.rs +│ └── state.rs +└── tests/ + └── integration_test.rs +``` + +## File Templates + +### Cargo.toml + +```toml +[package] +name = "{project-name}" +version = "0.1.0" +edition = "2021" + +[dependencies] +rmcp = { version = "0.8.1", features = ["server"] } +rmcp-macros = "0.8" +tokio = { version = "1", features = ["full"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +anyhow = "1.0" +tracing = "0.1" +tracing-subscriber = "0.3" +schemars = { version = "0.8", features = ["derive"] } +async-trait = "0.1" + +# Optional: for HTTP transports +axum = { version = "0.7", optional = true } +tower-http = { version = "0.5", features = ["cors"], optional = true } + +[dev-dependencies] +tokio-test = "0.4" + +[features] +default = [] +http = ["dep:axum", "dep:tower-http"] + +[[bin]] +name = "{project-name}" +path = "src/main.rs" +``` + +### .gitignore + +```gitignore +/target +Cargo.lock +*.swp +*.swo +*~ +.DS_Store +``` + +### README.md + +```markdown +# {Project Name} + +{Server description} + +## Installation + +```bash +cargo build --release +``` + +## Usage + +### Stdio Transport + +```bash +cargo run +``` + +### SSE Transport + +```bash +cargo run --features http -- --transport sse +``` + +### HTTP Transport + +```bash +cargo run --features http -- --transport http +``` + +## Configuration + +Configure in your MCP client (e.g., Claude Desktop): + +```json +{ + "mcpServers": { + "{project-name}": { + "command": "path/to/target/release/{project-name}", + "args": [] + } + } +} +``` + +## Tools + +- **{tool_name}**: {Tool description} + +## Development + +Run tests: + +```bash +cargo test +``` + +Run with logging: + +```bash +RUST_LOG=debug cargo run +``` +``` + +### src/main.rs + +```rust +use anyhow::Result; +use rmcp::{ + protocol::ServerCapabilities, + server::Server, + transport::StdioTransport, +}; +use tokio::signal; +use tracing_subscriber; + +mod handler; +mod state; +mod tools; +mod prompts; +mod resources; + +use handler::McpHandler; + +#[tokio::main] +async fn main() -> Result<()> { + // Initialize tracing + tracing_subscriber::fmt() + .with_max_level(tracing::Level::INFO) + .with_target(false) + .init(); + + tracing::info!("Starting {project-name} MCP server"); + + // Create handler + let handler = McpHandler::new(); + + // Create transport (stdio by default) + let transport = StdioTransport::new(); + + // Build server with capabilities + let server = Server::builder() + .with_handler(handler) + .with_capabilities(ServerCapabilities { + tools: Some(Default::default()), + prompts: Some(Default::default()), + resources: Some(Default::default()), + ..Default::default() + }) + .build(transport)?; + + tracing::info!("Server started, waiting for requests"); + + // Run server until Ctrl+C + server.run(signal::ctrl_c()).await?; + + tracing::info!("Server shutting down"); + Ok(()) +} +``` + +### src/handler.rs + +```rust +use rmcp::{ + model::*, + protocol::*, + server::{RequestContext, ServerHandler, RoleServer, ToolRouter}, + ErrorData, +}; +use rmcp::{tool_router, tool_handler}; +use async_trait::async_trait; + +use crate::state::ServerState; +use crate::tools; + +pub struct McpHandler { + state: ServerState, + tool_router: ToolRouter, +} + +#[tool_router] +impl McpHandler { + // Include tool definitions from tools module + #[tool( + name = "example_tool", + description = "An example tool", + annotations(read_only_hint = true) + )] + async fn example_tool(params: Parameters) -> Result { + tools::example::execute(params).await + } + + pub fn new() -> Self { + Self { + state: ServerState::new(), + tool_router: Self::tool_router(), + } + } +} + +#[tool_handler] +#[async_trait] +impl ServerHandler for McpHandler { + async fn list_prompts( + &self, + _request: Option, + _context: RequestContext, + ) -> Result { + let prompts = vec![ + Prompt { + name: "example-prompt".to_string(), + description: Some("An example prompt".to_string()), + arguments: Some(vec![ + PromptArgument { + name: "topic".to_string(), + description: Some("The topic to discuss".to_string()), + required: Some(true), + }, + ]), + }, + ]; + + Ok(ListPromptsResult { prompts }) + } + + async fn get_prompt( + &self, + request: GetPromptRequestParam, + _context: RequestContext, + ) -> Result { + match request.name.as_str() { + "example-prompt" => { + let topic = request.arguments + .as_ref() + .and_then(|args| args.get("topic")) + .ok_or_else(|| ErrorData::invalid_params("topic required"))?; + + Ok(GetPromptResult { + description: Some("Example prompt".to_string()), + messages: vec![ + PromptMessage::user(format!("Let's discuss: {}", topic)), + ], + }) + } + _ => Err(ErrorData::invalid_params("Unknown prompt")), + } + } + + async fn list_resources( + &self, + _request: Option, + _context: RequestContext, + ) -> Result { + let resources = vec![ + Resource { + uri: "example://data/info".to_string(), + name: "Example Resource".to_string(), + description: Some("An example resource".to_string()), + mime_type: Some("text/plain".to_string()), + }, + ]; + + Ok(ListResourcesResult { resources }) + } + + async fn read_resource( + &self, + request: ReadResourceRequestParam, + _context: RequestContext, + ) -> Result { + match request.uri.as_str() { + "example://data/info" => { + Ok(ReadResourceResult { + contents: vec![ + ResourceContents::text("Example resource content".to_string()) + .with_uri(request.uri) + .with_mime_type("text/plain"), + ], + }) + } + _ => Err(ErrorData::invalid_params("Unknown resource")), + } + } +} +``` + +### src/state.rs + +```rust +use std::sync::Arc; +use tokio::sync::RwLock; + +#[derive(Clone)] +pub struct ServerState { + // Add shared state here + counter: Arc>, +} + +impl ServerState { + pub fn new() -> Self { + Self { + counter: Arc::new(RwLock::new(0)), + } + } + + pub async fn increment(&self) -> i32 { + let mut counter = self.counter.write().await; + *counter += 1; + *counter + } + + pub async fn get(&self) -> i32 { + *self.counter.read().await + } +} +``` + +### src/tools/mod.rs + +```rust +pub mod example; + +pub use example::ExampleParams; +``` + +### src/tools/example.rs + +```rust +use rmcp::model::Parameters; +use serde::{Deserialize, Serialize}; +use schemars::JsonSchema; + +#[derive(Debug, Deserialize, JsonSchema)] +pub struct ExampleParams { + pub input: String, +} + +pub async fn execute(params: Parameters) -> Result { + let input = ¶ms.inner().input; + + // Tool logic here + Ok(format!("Processed: {}", input)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_example_tool() { + let params = Parameters::new(ExampleParams { + input: "test".to_string(), + }); + + let result = execute(params).await.unwrap(); + assert!(result.contains("test")); + } +} +``` + +### src/prompts/mod.rs + +```rust +// Prompt implementations can go here if needed +``` + +### src/resources/mod.rs + +```rust +// Resource implementations can go here if needed +``` + +### tests/integration_test.rs + +```rust +use rmcp::{ + model::*, + protocol::*, + server::{RequestContext, ServerHandler, RoleServer}, +}; + +// Import your handler +use {project_name_snake_case}::handler::McpHandler; + +#[tokio::test] +async fn test_list_tools() { + let handler = McpHandler::new(); + let context = RequestContext::default(); + + let result = handler.list_tools(None, context).await.unwrap(); + + assert!(!result.tools.is_empty()); + assert!(result.tools.iter().any(|t| t.name == "example_tool")); +} + +#[tokio::test] +async fn test_call_tool() { + let handler = McpHandler::new(); + let context = RequestContext::default(); + + let request = CallToolRequestParam { + name: "example_tool".to_string(), + arguments: Some(serde_json::json!({ + "input": "test" + })), + }; + + let result = handler.call_tool(request, context).await; + assert!(result.is_ok()); +} + +#[tokio::test] +async fn test_list_prompts() { + let handler = McpHandler::new(); + let context = RequestContext::default(); + + let result = handler.list_prompts(None, context).await.unwrap(); + assert!(!result.prompts.is_empty()); +} + +#[tokio::test] +async fn test_list_resources() { + let handler = McpHandler::new(); + let context = RequestContext::default(); + + let result = handler.list_resources(None, context).await.unwrap(); + assert!(!result.resources.is_empty()); +} +``` + +## Implementation Guidelines + +1. **Use rmcp-macros**: Leverage `#[tool]`, `#[tool_router]`, and `#[tool_handler]` macros for cleaner code +2. **Type Safety**: Use `schemars::JsonSchema` for all parameter types +3. **Error Handling**: Return `Result` types with proper error messages +4. **Async/Await**: All handlers must be async +5. **State Management**: Use `Arc>` for shared state +6. **Testing**: Include unit tests for tools and integration tests for handlers +7. **Logging**: Use `tracing` macros (`info!`, `debug!`, `warn!`, `error!`) +8. **Documentation**: Add doc comments to all public items + +## Example Tool Patterns + +### Simple Read-Only Tool + +```rust +#[derive(Debug, Deserialize, JsonSchema)] +pub struct GreetParams { + pub name: String, +} + +#[tool( + name = "greet", + description = "Greets a user by name", + annotations(read_only_hint = true, idempotent_hint = true) +)] +async fn greet(params: Parameters) -> String { + format!("Hello, {}!", params.inner().name) +} +``` + +### Tool with Error Handling + +```rust +#[derive(Debug, Deserialize, JsonSchema)] +pub struct DivideParams { + pub a: f64, + pub b: f64, +} + +#[tool(name = "divide", description = "Divides two numbers")] +async fn divide(params: Parameters) -> Result { + let p = params.inner(); + if p.b == 0.0 { + Err("Cannot divide by zero".to_string()) + } else { + Ok(p.a / p.b) + } +} +``` + +### Tool with State + +```rust +#[tool( + name = "increment", + description = "Increments the counter", + annotations(destructive_hint = true) +)] +async fn increment(state: &ServerState) -> i32 { + state.increment().await +} +``` + +## Running the Generated Server + +After generation: + +```bash +cd {project-name} +cargo build +cargo test +cargo run +``` + +For Claude Desktop integration: + +```json +{ + "mcpServers": { + "{project-name}": { + "command": "path/to/{project-name}/target/release/{project-name}", + "args": [] + } + } +} +``` + +Now generate the complete project based on the user's requirements! diff --git a/archived-contributions/rust-mcp/rust-mcp-server.instructions.md b/archived-contributions/rust-mcp/rust-mcp-server.instructions.md new file mode 100644 index 0000000..1759aaa --- /dev/null +++ b/archived-contributions/rust-mcp/rust-mcp-server.instructions.md @@ -0,0 +1,719 @@ +--- +title: Rust MCP Server Development Best Practices +description: 'Best practices for building Model Context Protocol servers in Rust using the official rmcp SDK with async/await patterns' +applyTo: + - '**/*.rs' + - '**/Cargo.toml' + - '**/Cargo.lock' +--- + +# Rust MCP Server Development Best Practices + +This guide provides best practices for building Model Context Protocol (MCP) servers using the official Rust SDK (`rmcp`). + +## Installation and Setup + +### Add Dependencies + +Add the `rmcp` crate to your `Cargo.toml`: + +```toml +[dependencies] +rmcp = { version = "0.8.1", features = ["server"] } +tokio = { version = "1", features = ["full"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +anyhow = "1.0" +tracing = "0.1" +tracing-subscriber = "0.3" +``` + +For macros support: + +```toml +[dependencies] +rmcp-macros = "0.8" +schemars = { version = "0.8", features = ["derive"] } +``` + +### Project Structure + +Organize your Rust MCP server project: + +``` +my-mcp-server/ +├── Cargo.toml +├── src/ +│ ├── main.rs # Server entry point +│ ├── handler.rs # ServerHandler implementation +│ ├── tools/ +│ │ ├── mod.rs +│ │ ├── calculator.rs +│ │ └── greeter.rs +│ ├── prompts/ +│ │ ├── mod.rs +│ │ └── code_review.rs +│ └── resources/ +│ ├── mod.rs +│ └── data.rs +└── tests/ + └── integration_tests.rs +``` + +## Server Implementation + +### Basic Server Setup + +Create a server with stdio transport: + +```rust +use rmcp::{ + protocol::ServerCapabilities, + server::{Server, ServerHandler}, + transport::StdioTransport, +}; +use tokio::signal; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + tracing_subscriber::fmt::init(); + + let handler = MyServerHandler::new(); + let transport = StdioTransport::new(); + + let server = Server::builder() + .with_handler(handler) + .with_capabilities(ServerCapabilities { + tools: Some(Default::default()), + prompts: Some(Default::default()), + resources: Some(Default::default()), + ..Default::default() + }) + .build(transport)?; + + server.run(signal::ctrl_c()).await?; + + Ok(()) +} +``` + +### ServerHandler Implementation + +Implement the `ServerHandler` trait: + +```rust +use rmcp::{ + model::*, + protocol::*, + server::{RequestContext, ServerHandler, RoleServer}, + ErrorData, +}; + +pub struct MyServerHandler { + tool_router: ToolRouter, +} + +impl MyServerHandler { + pub fn new() -> Self { + Self { + tool_router: Self::create_tool_router(), + } + } + + fn create_tool_router() -> ToolRouter { + // Initialize and return tool router + ToolRouter::new() + } +} + +#[async_trait::async_trait] +impl ServerHandler for MyServerHandler { + async fn list_tools( + &self, + _request: Option, + _context: RequestContext, + ) -> Result { + let items = self.tool_router.list_all(); + Ok(ListToolsResult::with_all_items(items)) + } + + async fn call_tool( + &self, + request: CallToolRequestParam, + context: RequestContext, + ) -> Result { + let tcc = ToolCallContext::new(self, request, context); + self.tool_router.call(tcc).await + } +} +``` + +## Tool Development + +### Using Macros for Tools + +Use the `#[tool]` macro for declarative tool definitions: + +```rust +use rmcp::tool; +use rmcp::model::Parameters; +use serde::{Deserialize, Serialize}; +use schemars::JsonSchema; + +#[derive(Debug, Deserialize, JsonSchema)] +pub struct CalculateParams { + pub a: f64, + pub b: f64, + pub operation: String, +} + +/// Performs mathematical calculations +#[tool( + name = "calculate", + description = "Performs basic arithmetic operations", + annotations(read_only_hint = true) +)] +pub async fn calculate(params: Parameters) -> Result { + let p = params.inner(); + match p.operation.as_str() { + "add" => Ok(p.a + p.b), + "subtract" => Ok(p.a - p.b), + "multiply" => Ok(p.a * p.b), + "divide" => { + if p.b == 0.0 { + Err("Division by zero".to_string()) + } else { + Ok(p.a / p.b) + } + } + _ => Err(format!("Unknown operation: {}", p.operation)), + } +} +``` + +### Tool Router with Macros + +Use `#[tool_router]` and `#[tool_handler]` macros: + +```rust +use rmcp::{tool_router, tool_handler}; + +pub struct ToolsHandler { + tool_router: ToolRouter, +} + +#[tool_router] +impl ToolsHandler { + #[tool] + async fn greet(params: Parameters) -> String { + format!("Hello, {}!", params.inner().name) + } + + #[tool(annotations(destructive_hint = true))] + async fn reset_counter() -> String { + "Counter reset".to_string() + } + + pub fn new() -> Self { + Self { + tool_router: Self::tool_router(), + } + } +} + +#[tool_handler] +impl ServerHandler for ToolsHandler { + // Other handler methods... +} +``` + +### Tool Annotations + +Use annotations to provide hints about tool behavior: + +```rust +#[tool( + name = "delete_file", + annotations( + destructive_hint = true, + read_only_hint = false, + idempotent_hint = false + ) +)] +pub async fn delete_file(params: Parameters) -> Result<(), String> { + // Delete file logic +} + +#[tool( + name = "search_data", + annotations( + read_only_hint = true, + idempotent_hint = true, + open_world_hint = true + ) +)] +pub async fn search_data(params: Parameters) -> Vec { + // Search logic +} +``` + +### Returning Rich Content + +Return structured content from tools: + +```rust +use rmcp::model::{ToolResponseContent, TextContent, ImageContent}; + +#[tool] +async fn analyze_code(params: Parameters) -> ToolResponseContent { + ToolResponseContent::from(vec![ + TextContent::text(format!("Analysis of {}:", params.inner().filename)), + TextContent::text("No issues found."), + ]) +} +``` + +## Prompt Implementation + +### Prompt Handler + +Implement prompt handlers: + +```rust +use rmcp::model::{Prompt, PromptArgument, PromptMessage, GetPromptResult}; + +async fn list_prompts( + &self, + _request: Option, + _context: RequestContext, +) -> Result { + let prompts = vec![ + Prompt { + name: "code-review".to_string(), + description: Some("Review code for best practices".to_string()), + arguments: Some(vec![ + PromptArgument { + name: "language".to_string(), + description: Some("Programming language".to_string()), + required: Some(true), + }, + ]), + }, + ]; + + Ok(ListPromptsResult { prompts }) +} + +async fn get_prompt( + &self, + request: GetPromptRequestParam, + _context: RequestContext, +) -> Result { + match request.name.as_str() { + "code-review" => { + let language = request.arguments + .as_ref() + .and_then(|args| args.get("language")) + .ok_or_else(|| ErrorData::invalid_params("language required"))?; + + Ok(GetPromptResult { + description: Some("Code review prompt".to_string()), + messages: vec![ + PromptMessage::user(format!( + "Review this {} code for best practices and suggest improvements", + language + )), + ], + }) + } + _ => Err(ErrorData::invalid_params("Unknown prompt")), + } +} +``` + +## Resource Implementation + +### Resource Handlers + +Implement resource handlers: + +```rust +use rmcp::model::{Resource, ResourceContents, ReadResourceResult}; + +async fn list_resources( + &self, + _request: Option, + _context: RequestContext, +) -> Result { + let resources = vec![ + Resource { + uri: "file:///data/config.json".to_string(), + name: "Configuration".to_string(), + description: Some("Server configuration".to_string()), + mime_type: Some("application/json".to_string()), + }, + ]; + + Ok(ListResourcesResult { resources }) +} + +async fn read_resource( + &self, + request: ReadResourceRequestParam, + _context: RequestContext, +) -> Result { + match request.uri.as_str() { + "file:///data/config.json" => { + let content = r#"{"version": "1.0", "enabled": true}"#; + Ok(ReadResourceResult { + contents: vec![ + ResourceContents::text(content.to_string()) + .with_uri(request.uri) + .with_mime_type("application/json"), + ], + }) + } + _ => Err(ErrorData::invalid_params("Unknown resource")), + } +} +``` + +## Transport Options + +### Stdio Transport + +Standard input/output transport for CLI integration: + +```rust +use rmcp::transport::StdioTransport; + +let transport = StdioTransport::new(); +let server = Server::builder() + .with_handler(handler) + .build(transport)?; +``` + +### SSE (Server-Sent Events) Transport + +HTTP-based SSE transport: + +```rust +use rmcp::transport::SseServerTransport; +use std::net::SocketAddr; + +let addr: SocketAddr = "127.0.0.1:8000".parse()?; +let transport = SseServerTransport::new(addr); + +let server = Server::builder() + .with_handler(handler) + .build(transport)?; + +server.run(signal::ctrl_c()).await?; +``` + +### Streamable HTTP Transport + +HTTP streaming transport with Axum: + +```rust +use rmcp::transport::StreamableHttpTransport; +use axum::{Router, routing::post}; + +let transport = StreamableHttpTransport::new(); +let app = Router::new() + .route("/mcp", post(transport.handler())); + +let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?; +axum::serve(listener, app).await?; +``` + +### Custom Transports + +Implement custom transports (TCP, Unix Socket, WebSocket): + +```rust +use rmcp::transport::Transport; +use tokio::net::TcpListener; + +// See examples/transport/ for TCP, Unix Socket, WebSocket implementations +``` + +## Error Handling + +### ErrorData Usage + +Return proper MCP errors: + +```rust +use rmcp::ErrorData; + +fn validate_params(value: &str) -> Result<(), ErrorData> { + if value.is_empty() { + return Err(ErrorData::invalid_params("Value cannot be empty")); + } + Ok(()) +} + +async fn call_tool( + &self, + request: CallToolRequestParam, + context: RequestContext, +) -> Result { + validate_params(&request.name)?; + + // Tool execution... + + Ok(CallToolResult { + content: vec![TextContent::text("Success")], + is_error: Some(false), + }) +} +``` + +### Anyhow Integration + +Use `anyhow` for application-level errors: + +```rust +use anyhow::{Context, Result}; + +async fn load_config() -> Result { + let content = tokio::fs::read_to_string("config.json") + .await + .context("Failed to read config file")?; + + let config: Config = serde_json::from_str(&content) + .context("Failed to parse config")?; + + Ok(config) +} +``` + +## Testing + +### Unit Tests + +Write unit tests for tools and handlers: + +```rust +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_calculate_add() { + let params = Parameters::new(CalculateParams { + a: 5.0, + b: 3.0, + operation: "add".to_string(), + }); + + let result = calculate(params).await.unwrap(); + assert_eq!(result, 8.0); + } + + #[tokio::test] + async fn test_divide_by_zero() { + let params = Parameters::new(CalculateParams { + a: 5.0, + b: 0.0, + operation: "divide".to_string(), + }); + + let result = calculate(params).await; + assert!(result.is_err()); + } +} +``` + +### Integration Tests + +Test complete server interactions: + +```rust +#[tokio::test] +async fn test_server_list_tools() { + let handler = MyServerHandler::new(); + let context = RequestContext::default(); + + let result = handler.list_tools(None, context).await.unwrap(); + + assert!(!result.tools.is_empty()); + assert!(result.tools.iter().any(|t| t.name == "calculate")); +} +``` + +## Progress Notifications + +### Reporting Progress + +Send progress notifications during long-running operations: + +```rust +use rmcp::model::ProgressNotification; + +#[tool] +async fn process_large_file( + params: Parameters, + context: RequestContext, +) -> Result { + let total = 100; + + for i in 0..=total { + // Do work... + + if i % 10 == 0 { + context.notify_progress(ProgressNotification { + progress: i, + total: Some(total), + }).await.ok(); + } + } + + Ok("Processing complete".to_string()) +} +``` + +## OAuth Authentication + +### OAuth Integration + +Implement OAuth for secure access: + +```rust +use rmcp::oauth::{OAuthConfig, OAuthProvider}; + +let oauth_config = OAuthConfig { + authorization_endpoint: "https://auth.example.com/authorize".to_string(), + token_endpoint: "https://auth.example.com/token".to_string(), + client_id: env::var("CLIENT_ID")?, + client_secret: env::var("CLIENT_SECRET")?, + scopes: vec!["read".to_string(), "write".to_string()], +}; + +let oauth_provider = OAuthProvider::new(oauth_config); +// See examples/servers/complex_auth_sse.rs for complete implementation +``` + +## Performance Best Practices + +### Async Operations + +Use async/await for non-blocking operations: + +```rust +#[tool] +async fn fetch_data(params: Parameters) -> Result { + let client = reqwest::Client::new(); + let response = client + .get(¶ms.inner().url) + .send() + .await + .map_err(|e| e.to_string())?; + + let text = response.text().await.map_err(|e| e.to_string())?; + Ok(text) +} +``` + +### State Management + +Use `Arc` and `RwLock` for shared state: + +```rust +use std::sync::Arc; +use tokio::sync::RwLock; + +pub struct ServerState { + counter: Arc>, +} + +impl ServerState { + pub fn new() -> Self { + Self { + counter: Arc::new(RwLock::new(0)), + } + } + + pub async fn increment(&self) -> i32 { + let mut counter = self.counter.write().await; + *counter += 1; + *counter + } +} +``` + +## Logging and Tracing + +### Setup Tracing + +Configure tracing for observability: + +```rust +use tracing::{info, warn, error, debug}; +use tracing_subscriber; + +fn init_logging() { + tracing_subscriber::fmt() + .with_max_level(tracing::Level::DEBUG) + .with_target(false) + .with_thread_ids(true) + .init(); +} + +#[tool] +async fn my_tool(params: Parameters) -> String { + debug!("Tool called with params: {:?}", params); + info!("Processing request"); + + // Tool logic... + + info!("Request completed"); + "Done".to_string() +} +``` + +## Deployment + +### Binary Distribution + +Build optimized release binaries: + +```bash +cargo build --release --target x86_64-unknown-linux-gnu +cargo build --release --target x86_64-pc-windows-msvc +cargo build --release --target x86_64-apple-darwin +``` + +### Cross-Compilation + +Use cross for cross-platform builds: + +```bash +cargo install cross +cross build --release --target aarch64-unknown-linux-gnu +``` + +### Docker Deployment + +Create a Dockerfile: + +```dockerfile +FROM rust:1.75 as builder +WORKDIR /app +COPY . . +RUN cargo build --release + +FROM debian:bookworm-slim +RUN apt-get update && apt-get install -y ca-certificates +COPY --from=builder /app/target/release/my-mcp-server /usr/local/bin/ +CMD ["my-mcp-server"] +``` + +## Additional Resources + +- [rmcp Documentation](https://docs.rs/rmcp) +- [rmcp-macros Documentation](https://docs.rs/rmcp-macros) +- [Examples Repository](https://github.com/modelcontextprotocol/rust-sdk/tree/main/examples) +- [MCP Specification](https://spec.modelcontextprotocol.io/) +- [Rust Async Book](https://rust-lang.github.io/async-book/) diff --git a/archived-contributions/swift-mcp/swift-mcp-development.collection.yml b/archived-contributions/swift-mcp/swift-mcp-development.collection.yml new file mode 100644 index 0000000..8828531 --- /dev/null +++ b/archived-contributions/swift-mcp/swift-mcp-development.collection.yml @@ -0,0 +1,35 @@ +id: swift-mcp-development +name: Swift MCP Server Development +description: 'Comprehensive collection for building Model Context Protocol servers in Swift using the official MCP Swift SDK with modern concurrency features.' +tags: [swift, mcp, model-context-protocol, server-development, sdk, ios, macos, concurrency, actor, async-await] +items: + - path: instructions/swift-mcp-server.instructions.md + kind: instruction + - path: prompts/swift-mcp-server-generator.prompt.md + kind: prompt + - path: chatmodes/swift-mcp-expert.chatmode.md + kind: chat-mode + usage: | + recommended + + This chat mode provides expert guidance for building MCP servers in Swift. + + This chat mode is ideal for: + - Creating new MCP server projects with Swift + - Implementing async/await patterns and actor-based concurrency + - Setting up stdio, HTTP, or network transports + - Debugging Swift concurrency and ServiceLifecycle integration + - Learning Swift MCP best practices with the official SDK + - Optimizing server performance for iOS/macOS platforms + + To get the best results, consider: + - Using the instruction file to set context for Swift MCP development + - Using the prompt to generate initial project structure + - Switching to the expert chat mode for detailed implementation help + - Specifying whether you need stdio, HTTP, or network transport + - Providing details about what tools or functionality you need + - Mentioning if you need resources, prompts, or special capabilities + +display: + ordering: manual + show_badge: true diff --git a/archived-contributions/swift-mcp/swift-mcp-development.md b/archived-contributions/swift-mcp/swift-mcp-development.md new file mode 100644 index 0000000..3cabb90 --- /dev/null +++ b/archived-contributions/swift-mcp/swift-mcp-development.md @@ -0,0 +1,41 @@ +# Swift MCP Server Development + +'Comprehensive collection for building Model Context Protocol servers in Swift using the official MCP Swift SDK with modern concurrency features.' + +**Tags:** swift, mcp, model-context-protocol, server-development, sdk, ios, macos, concurrency, actor, async-await + +## Items in this Collection + +| Title | Type | Description | +| ----- | ---- | ----------- | +| [Swift MCP Server Development Guidelines](../instructions/swift-mcp-server.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fswift-mcp-server.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fswift-mcp-server.instructions.md) | Instruction | Best practices and patterns for building Model Context Protocol (MCP) servers in Swift using the official MCP Swift SDK package. | +| [Swift MCP Server Generator](../prompts/swift-mcp-server-generator.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fswift-mcp-server-generator.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fswift-mcp-server-generator.prompt.md) | Prompt | Generate a complete Model Context Protocol server project in Swift using the official MCP Swift SDK package. | +| [Swift MCP Expert](../chatmodes/swift-mcp-expert.chatmode.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fswift-mcp-expert.chatmode.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fswift-mcp-expert.chatmode.md) | Chat Mode | Expert assistance for building Model Context Protocol servers in Swift using modern concurrency features and the official MCP Swift SDK. [see usage](#swift-mcp-expert) | + +## Collection Usage + +### Swift MCP Expert + +recommended + +This chat mode provides expert guidance for building MCP servers in Swift. + +This chat mode is ideal for: +- Creating new MCP server projects with Swift +- Implementing async/await patterns and actor-based concurrency +- Setting up stdio, HTTP, or network transports +- Debugging Swift concurrency and ServiceLifecycle integration +- Learning Swift MCP best practices with the official SDK +- Optimizing server performance for iOS/macOS platforms + +To get the best results, consider: +- Using the instruction file to set context for Swift MCP development +- Using the prompt to generate initial project structure +- Switching to the expert chat mode for detailed implementation help +- Specifying whether you need stdio, HTTP, or network transport +- Providing details about what tools or functionality you need +- Mentioning if you need resources, prompts, or special capabilities + +--- + +*This collection includes 3 curated items for swift mcp server development.* \ No newline at end of file diff --git a/archived-contributions/swift-mcp/swift-mcp-expert.chatmode.md b/archived-contributions/swift-mcp/swift-mcp-expert.chatmode.md new file mode 100644 index 0000000..4174072 --- /dev/null +++ b/archived-contributions/swift-mcp/swift-mcp-expert.chatmode.md @@ -0,0 +1,240 @@ +--- +description: 'Expert assistance for building Model Context Protocol servers in Swift using modern concurrency features and the official MCP Swift SDK.' +model: GPT-4.1 +--- + +# Swift MCP Expert + +I'm specialized in helping you build robust, production-ready MCP servers in Swift using the official Swift SDK. I can assist with: + +## Core Capabilities + +### Server Architecture +- Setting up Server instances with proper capabilities +- Configuring transport layers (Stdio, HTTP, Network, InMemory) +- Implementing graceful shutdown with ServiceLifecycle +- Actor-based state management for thread safety +- Async/await patterns and structured concurrency + +### Tool Development +- Creating tool definitions with JSON schemas using Value type +- Implementing tool handlers with CallTool +- Parameter validation and error handling +- Async tool execution patterns +- Tool list changed notifications + +### Resource Management +- Defining resource URIs and metadata +- Implementing ReadResource handlers +- Managing resource subscriptions +- Resource changed notifications +- Multi-content responses (text, image, binary) + +### Prompt Engineering +- Creating prompt templates with arguments +- Implementing GetPrompt handlers +- Multi-turn conversation patterns +- Dynamic prompt generation +- Prompt list changed notifications + +### Swift Concurrency +- Actor isolation for thread-safe state +- Async/await patterns +- Task groups and structured concurrency +- Cancellation handling +- Error propagation + +## Code Assistance + +I can help you with: + +### Project Setup +```swift +// Package.swift with MCP SDK +.package( + url: "https://github.com/modelcontextprotocol/swift-sdk.git", + from: "0.10.0" +) +``` + +### Server Creation +```swift +let server = Server( + name: "MyServer", + version: "1.0.0", + capabilities: .init( + prompts: .init(listChanged: true), + resources: .init(subscribe: true, listChanged: true), + tools: .init(listChanged: true) + ) +) +``` + +### Handler Registration +```swift +await server.withMethodHandler(CallTool.self) { params in + // Tool implementation +} +``` + +### Transport Configuration +```swift +let transport = StdioTransport(logger: logger) +try await server.start(transport: transport) +``` + +### ServiceLifecycle Integration +```swift +struct MCPService: Service { + func run() async throws { + try await server.start(transport: transport) + } + + func shutdown() async throws { + await server.stop() + } +} +``` + +## Best Practices + +### Actor-Based State +Always use actors for shared mutable state: +```swift +actor ServerState { + private var subscriptions: Set = [] + + func addSubscription(_ uri: String) { + subscriptions.insert(uri) + } +} +``` + +### Error Handling +Use proper Swift error handling: +```swift +do { + let result = try performOperation() + return .init(content: [.text(result)], isError: false) +} catch let error as MCPError { + return .init(content: [.text(error.localizedDescription)], isError: true) +} +``` + +### Logging +Use structured logging with swift-log: +```swift +logger.info("Tool called", metadata: [ + "name": .string(params.name), + "args": .string("\(params.arguments ?? [:])") +]) +``` + +### JSON Schemas +Use the Value type for schemas: +```swift +.object([ + "type": .string("object"), + "properties": .object([ + "name": .object([ + "type": .string("string") + ]) + ]), + "required": .array([.string("name")]) +]) +``` + +## Common Patterns + +### Request/Response Handler +```swift +await server.withMethodHandler(CallTool.self) { params in + guard let arg = params.arguments?["key"]?.stringValue else { + throw MCPError.invalidParams("Missing key") + } + + let result = await processAsync(arg) + + return .init( + content: [.text(result)], + isError: false + ) +} +``` + +### Resource Subscription +```swift +await server.withMethodHandler(ResourceSubscribe.self) { params in + await state.addSubscription(params.uri) + logger.info("Subscribed to \(params.uri)") + return .init() +} +``` + +### Concurrent Operations +```swift +async let result1 = fetchData1() +async let result2 = fetchData2() +let combined = await "\(result1) and \(result2)" +``` + +### Initialize Hook +```swift +try await server.start(transport: transport) { clientInfo, capabilities in + logger.info("Client: \(clientInfo.name) v\(clientInfo.version)") + + if capabilities.sampling != nil { + logger.info("Client supports sampling") + } +} +``` + +## Platform Support + +The Swift SDK supports: +- macOS 13.0+ +- iOS 16.0+ +- watchOS 9.0+ +- tvOS 16.0+ +- visionOS 1.0+ +- Linux (glibc and musl) + +## Testing + +Write async tests: +```swift +func testTool() async throws { + let params = CallTool.Params( + name: "test", + arguments: ["key": .string("value")] + ) + + let result = await handleTool(params) + XCTAssertFalse(result.isError ?? true) +} +``` + +## Debugging + +Enable debug logging: +```swift +var logger = Logger(label: "com.example.mcp-server") +logger.logLevel = .debug +``` + +## Ask Me About + +- Server setup and configuration +- Tool, resource, and prompt implementations +- Swift concurrency patterns +- Actor-based state management +- ServiceLifecycle integration +- Transport configuration (Stdio, HTTP, Network) +- JSON schema construction +- Error handling strategies +- Testing async code +- Platform-specific considerations +- Performance optimization +- Deployment strategies + +I'm here to help you build efficient, safe, and idiomatic Swift MCP servers. What would you like to work on? diff --git a/archived-contributions/swift-mcp/swift-mcp-server-generator.prompt.md b/archived-contributions/swift-mcp/swift-mcp-server-generator.prompt.md new file mode 100644 index 0000000..b148785 --- /dev/null +++ b/archived-contributions/swift-mcp/swift-mcp-server-generator.prompt.md @@ -0,0 +1,669 @@ +--- +description: 'Generate a complete Model Context Protocol server project in Swift using the official MCP Swift SDK package.' +mode: agent +--- + +# Swift MCP Server Generator + +Generate a complete, production-ready MCP server in Swift using the official Swift SDK package. + +## Project Generation + +When asked to create a Swift MCP server, generate a complete project with this structure: + +``` +my-mcp-server/ +├── Package.swift +├── Sources/ +│ └── MyMCPServer/ +│ ├── main.swift +│ ├── Server.swift +│ ├── Tools/ +│ │ ├── ToolDefinitions.swift +│ │ └── ToolHandlers.swift +│ ├── Resources/ +│ │ ├── ResourceDefinitions.swift +│ │ └── ResourceHandlers.swift +│ └── Prompts/ +│ ├── PromptDefinitions.swift +│ └── PromptHandlers.swift +├── Tests/ +│ └── MyMCPServerTests/ +│ └── ServerTests.swift +└── README.md +``` + +## Package.swift Template + +```swift +// swift-tools-version: 6.0 +import PackageDescription + +let package = Package( + name: "MyMCPServer", + platforms: [ + .macOS(.v13), + .iOS(.v16), + .watchOS(.v9), + .tvOS(.v16), + .visionOS(.v1) + ], + dependencies: [ + .package( + url: "https://github.com/modelcontextprotocol/swift-sdk.git", + from: "0.10.0" + ), + .package( + url: "https://github.com/apple/swift-log.git", + from: "1.5.0" + ), + .package( + url: "https://github.com/swift-server/swift-service-lifecycle.git", + from: "2.0.0" + ) + ], + targets: [ + .executableTarget( + name: "MyMCPServer", + dependencies: [ + .product(name: "MCP", package: "swift-sdk"), + .product(name: "Logging", package: "swift-log"), + .product(name: "ServiceLifecycle", package: "swift-service-lifecycle") + ] + ), + .testTarget( + name: "MyMCPServerTests", + dependencies: ["MyMCPServer"] + ) + ] +) +``` + +## main.swift Template + +```swift +import MCP +import Logging +import ServiceLifecycle + +struct MCPService: Service { + let server: Server + let transport: Transport + + func run() async throws { + try await server.start(transport: transport) { clientInfo, capabilities in + logger.info("Client connected", metadata: [ + "name": .string(clientInfo.name), + "version": .string(clientInfo.version) + ]) + } + + // Keep service running + try await Task.sleep(for: .days(365 * 100)) + } + + func shutdown() async throws { + logger.info("Shutting down MCP server") + await server.stop() + } +} + +var logger = Logger(label: "com.example.mcp-server") +logger.logLevel = .info + +do { + let server = await createServer() + let transport = StdioTransport(logger: logger) + let service = MCPService(server: server, transport: transport) + + let serviceGroup = ServiceGroup( + services: [service], + configuration: .init( + gracefulShutdownSignals: [.sigterm, .sigint] + ), + logger: logger + ) + + try await serviceGroup.run() +} catch { + logger.error("Fatal error", metadata: ["error": .string("\(error)")]) + throw error +} +``` + +## Server.swift Template + +```swift +import MCP +import Logging + +func createServer() async -> Server { + let server = Server( + name: "MyMCPServer", + version: "1.0.0", + capabilities: .init( + prompts: .init(listChanged: true), + resources: .init(subscribe: true, listChanged: true), + tools: .init(listChanged: true) + ) + ) + + // Register tool handlers + await registerToolHandlers(server: server) + + // Register resource handlers + await registerResourceHandlers(server: server) + + // Register prompt handlers + await registerPromptHandlers(server: server) + + return server +} +``` + +## ToolDefinitions.swift Template + +```swift +import MCP + +func getToolDefinitions() -> [Tool] { + [ + Tool( + name: "greet", + description: "Generate a greeting message", + inputSchema: .object([ + "type": .string("object"), + "properties": .object([ + "name": .object([ + "type": .string("string"), + "description": .string("Name to greet") + ]) + ]), + "required": .array([.string("name")]) + ]) + ), + Tool( + name: "calculate", + description: "Perform mathematical calculations", + inputSchema: .object([ + "type": .string("object"), + "properties": .object([ + "operation": .object([ + "type": .string("string"), + "enum": .array([ + .string("add"), + .string("subtract"), + .string("multiply"), + .string("divide") + ]), + "description": .string("Operation to perform") + ]), + "a": .object([ + "type": .string("number"), + "description": .string("First operand") + ]), + "b": .object([ + "type": .string("number"), + "description": .string("Second operand") + ]) + ]), + "required": .array([ + .string("operation"), + .string("a"), + .string("b") + ]) + ]) + ) + ] +} +``` + +## ToolHandlers.swift Template + +```swift +import MCP +import Logging + +private let logger = Logger(label: "com.example.mcp-server.tools") + +func registerToolHandlers(server: Server) async { + await server.withMethodHandler(ListTools.self) { _ in + logger.debug("Listing available tools") + return .init(tools: getToolDefinitions()) + } + + await server.withMethodHandler(CallTool.self) { params in + logger.info("Tool called", metadata: ["name": .string(params.name)]) + + switch params.name { + case "greet": + return handleGreet(params: params) + + case "calculate": + return handleCalculate(params: params) + + default: + logger.warning("Unknown tool requested", metadata: ["name": .string(params.name)]) + return .init( + content: [.text("Unknown tool: \(params.name)")], + isError: true + ) + } + } +} + +private func handleGreet(params: CallTool.Params) -> CallTool.Result { + guard let name = params.arguments?["name"]?.stringValue else { + return .init( + content: [.text("Missing 'name' parameter")], + isError: true + ) + } + + let greeting = "Hello, \(name)! Welcome to MCP." + logger.debug("Generated greeting", metadata: ["name": .string(name)]) + + return .init( + content: [.text(greeting)], + isError: false + ) +} + +private func handleCalculate(params: CallTool.Params) -> CallTool.Result { + guard let operation = params.arguments?["operation"]?.stringValue, + let a = params.arguments?["a"]?.doubleValue, + let b = params.arguments?["b"]?.doubleValue else { + return .init( + content: [.text("Missing or invalid parameters")], + isError: true + ) + } + + let result: Double + switch operation { + case "add": + result = a + b + case "subtract": + result = a - b + case "multiply": + result = a * b + case "divide": + guard b != 0 else { + return .init( + content: [.text("Division by zero")], + isError: true + ) + } + result = a / b + default: + return .init( + content: [.text("Unknown operation: \(operation)")], + isError: true + ) + } + + logger.debug("Calculation performed", metadata: [ + "operation": .string(operation), + "result": .string("\(result)") + ]) + + return .init( + content: [.text("Result: \(result)")], + isError: false + ) +} +``` + +## ResourceDefinitions.swift Template + +```swift +import MCP + +func getResourceDefinitions() -> [Resource] { + [ + Resource( + name: "Example Data", + uri: "resource://data/example", + description: "Example resource data", + mimeType: "application/json" + ), + Resource( + name: "Configuration", + uri: "resource://config", + description: "Server configuration", + mimeType: "application/json" + ) + ] +} +``` + +## ResourceHandlers.swift Template + +```swift +import MCP +import Logging +import Foundation + +private let logger = Logger(label: "com.example.mcp-server.resources") + +actor ResourceState { + private var subscriptions: Set = [] + + func addSubscription(_ uri: String) { + subscriptions.insert(uri) + } + + func removeSubscription(_ uri: String) { + subscriptions.remove(uri) + } + + func isSubscribed(_ uri: String) -> Bool { + subscriptions.contains(uri) + } +} + +private let state = ResourceState() + +func registerResourceHandlers(server: Server) async { + await server.withMethodHandler(ListResources.self) { params in + logger.debug("Listing available resources") + return .init(resources: getResourceDefinitions(), nextCursor: nil) + } + + await server.withMethodHandler(ReadResource.self) { params in + logger.info("Reading resource", metadata: ["uri": .string(params.uri)]) + + switch params.uri { + case "resource://data/example": + let jsonData = """ + { + "message": "Example resource data", + "timestamp": "\(Date())" + } + """ + return .init(contents: [ + .text(jsonData, uri: params.uri, mimeType: "application/json") + ]) + + case "resource://config": + let config = """ + { + "serverName": "MyMCPServer", + "version": "1.0.0" + } + """ + return .init(contents: [ + .text(config, uri: params.uri, mimeType: "application/json") + ]) + + default: + logger.warning("Unknown resource requested", metadata: ["uri": .string(params.uri)]) + throw MCPError.invalidParams("Unknown resource URI: \(params.uri)") + } + } + + await server.withMethodHandler(ResourceSubscribe.self) { params in + logger.info("Client subscribed to resource", metadata: ["uri": .string(params.uri)]) + await state.addSubscription(params.uri) + return .init() + } + + await server.withMethodHandler(ResourceUnsubscribe.self) { params in + logger.info("Client unsubscribed from resource", metadata: ["uri": .string(params.uri)]) + await state.removeSubscription(params.uri) + return .init() + } +} +``` + +## PromptDefinitions.swift Template + +```swift +import MCP + +func getPromptDefinitions() -> [Prompt] { + [ + Prompt( + name: "code-review", + description: "Generate a code review prompt", + arguments: [ + .init(name: "language", description: "Programming language", required: true), + .init(name: "focus", description: "Review focus area", required: false) + ] + ) + ] +} +``` + +## PromptHandlers.swift Template + +```swift +import MCP +import Logging + +private let logger = Logger(label: "com.example.mcp-server.prompts") + +func registerPromptHandlers(server: Server) async { + await server.withMethodHandler(ListPrompts.self) { params in + logger.debug("Listing available prompts") + return .init(prompts: getPromptDefinitions(), nextCursor: nil) + } + + await server.withMethodHandler(GetPrompt.self) { params in + logger.info("Getting prompt", metadata: ["name": .string(params.name)]) + + switch params.name { + case "code-review": + return handleCodeReviewPrompt(params: params) + + default: + logger.warning("Unknown prompt requested", metadata: ["name": .string(params.name)]) + throw MCPError.invalidParams("Unknown prompt: \(params.name)") + } + } +} + +private func handleCodeReviewPrompt(params: GetPrompt.Params) -> GetPrompt.Result { + guard let language = params.arguments?["language"]?.stringValue else { + return .init( + description: "Missing language parameter", + messages: [] + ) + } + + let focus = params.arguments?["focus"]?.stringValue ?? "general quality" + + let description = "Code review for \(language) with focus on \(focus)" + let messages: [Prompt.Message] = [ + .user("Please review this \(language) code with focus on \(focus)."), + .assistant("I'll review the code focusing on \(focus). Please share the code."), + .user("Here's the code to review: [paste code here]") + ] + + logger.debug("Generated code review prompt", metadata: [ + "language": .string(language), + "focus": .string(focus) + ]) + + return .init(description: description, messages: messages) +} +``` + +## ServerTests.swift Template + +```swift +import XCTest +@testable import MyMCPServer + +final class ServerTests: XCTestCase { + func testGreetTool() async throws { + let params = CallTool.Params( + name: "greet", + arguments: ["name": .string("Swift")] + ) + + let result = handleGreet(params: params) + + XCTAssertFalse(result.isError ?? true) + XCTAssertEqual(result.content.count, 1) + + if case .text(let message) = result.content[0] { + XCTAssertTrue(message.contains("Swift")) + } else { + XCTFail("Expected text content") + } + } + + func testCalculateTool() async throws { + let params = CallTool.Params( + name: "calculate", + arguments: [ + "operation": .string("add"), + "a": .number(5), + "b": .number(3) + ] + ) + + let result = handleCalculate(params: params) + + XCTAssertFalse(result.isError ?? true) + XCTAssertEqual(result.content.count, 1) + + if case .text(let message) = result.content[0] { + XCTAssertTrue(message.contains("8")) + } else { + XCTFail("Expected text content") + } + } + + func testDivideByZero() async throws { + let params = CallTool.Params( + name: "calculate", + arguments: [ + "operation": .string("divide"), + "a": .number(10), + "b": .number(0) + ] + ) + + let result = handleCalculate(params: params) + + XCTAssertTrue(result.isError ?? false) + } +} +``` + +## README.md Template + +```markdown +# MyMCPServer + +A Model Context Protocol server built with Swift. + +## Features + +- ✅ Tools: greet, calculate +- ✅ Resources: example data, configuration +- ✅ Prompts: code-review +- ✅ Graceful shutdown with ServiceLifecycle +- ✅ Structured logging with swift-log +- ✅ Full test coverage + +## Requirements + +- Swift 6.0+ +- macOS 13+, iOS 16+, or Linux + +## Installation + +```bash +swift build -c release +``` + +## Usage + +Run the server: + +```bash +swift run +``` + +Or with logging: + +```bash +LOG_LEVEL=debug swift run +``` + +## Testing + +```bash +swift test +``` + +## Development + +The server uses: +- [MCP Swift SDK](https://github.com/modelcontextprotocol/swift-sdk) - MCP protocol implementation +- [swift-log](https://github.com/apple/swift-log) - Structured logging +- [swift-service-lifecycle](https://github.com/swift-server/swift-service-lifecycle) - Graceful shutdown + +## Project Structure + +- `Sources/MyMCPServer/main.swift` - Entry point with ServiceLifecycle +- `Sources/MyMCPServer/Server.swift` - Server configuration +- `Sources/MyMCPServer/Tools/` - Tool definitions and handlers +- `Sources/MyMCPServer/Resources/` - Resource definitions and handlers +- `Sources/MyMCPServer/Prompts/` - Prompt definitions and handlers +- `Tests/` - Unit tests + +## License + +MIT +``` + +## Generation Instructions + +1. **Ask for project name and description** +2. **Generate all files** with proper naming +3. **Use actor-based state** for thread safety +4. **Include comprehensive logging** with swift-log +5. **Implement graceful shutdown** with ServiceLifecycle +6. **Add tests** for all handlers +7. **Use modern Swift concurrency** (async/await) +8. **Follow Swift naming conventions** (camelCase, PascalCase) +9. **Include error handling** with proper MCPError usage +10. **Document public APIs** with doc comments + +## Build and Run + +```bash +# Build +swift build + +# Run +swift run + +# Test +swift test + +# Release build +swift build -c release + +# Install +swift build -c release +cp .build/release/MyMCPServer /usr/local/bin/ +``` + +## Integration with Claude Desktop + +Add to `claude_desktop_config.json`: + +```json +{ + "mcpServers": { + "my-mcp-server": { + "command": "/path/to/MyMCPServer" + } + } +} +``` diff --git a/archived-contributions/swift-mcp/swift-mcp-server.instructions.md b/archived-contributions/swift-mcp/swift-mcp-server.instructions.md new file mode 100644 index 0000000..29f5e84 --- /dev/null +++ b/archived-contributions/swift-mcp/swift-mcp-server.instructions.md @@ -0,0 +1,498 @@ +--- +description: 'Best practices and patterns for building Model Context Protocol (MCP) servers in Swift using the official MCP Swift SDK package.' +applyTo: "**/*.swift, **/Package.swift, **/Package.resolved" +--- + +# Swift MCP Server Development Guidelines + +When building MCP servers in Swift, follow these best practices and patterns using the official Swift SDK. + +## Server Setup + +Create an MCP server using the `Server` class with capabilities: + +```swift +import MCP + +let server = Server( + name: "MyServer", + version: "1.0.0", + capabilities: .init( + prompts: .init(listChanged: true), + resources: .init(subscribe: true, listChanged: true), + tools: .init(listChanged: true) + ) +) +``` + +## Adding Tools + +Use `withMethodHandler` to register tool handlers: + +```swift +// Register tool list handler +await server.withMethodHandler(ListTools.self) { _ in + let tools = [ + Tool( + name: "search", + description: "Search for information", + inputSchema: .object([ + "properties": .object([ + "query": .string("Search query"), + "limit": .number("Maximum results") + ]), + "required": .array([.string("query")]) + ]) + ) + ] + return .init(tools: tools) +} + +// Register tool call handler +await server.withMethodHandler(CallTool.self) { params in + switch params.name { + case "search": + let query = params.arguments?["query"]?.stringValue ?? "" + let limit = params.arguments?["limit"]?.intValue ?? 10 + + // Perform search + let results = performSearch(query: query, limit: limit) + + return .init( + content: [.text("Found \(results.count) results")], + isError: false + ) + + default: + return .init( + content: [.text("Unknown tool")], + isError: true + ) + } +} +``` + +## Adding Resources + +Implement resource handlers for data access: + +```swift +// Register resource list handler +await server.withMethodHandler(ListResources.self) { params in + let resources = [ + Resource( + name: "Data File", + uri: "resource://data/example.txt", + description: "Example data file", + mimeType: "text/plain" + ) + ] + return .init(resources: resources, nextCursor: nil) +} + +// Register resource read handler +await server.withMethodHandler(ReadResource.self) { params in + switch params.uri { + case "resource://data/example.txt": + let content = loadResourceContent(uri: params.uri) + return .init(contents: [ + Resource.Content.text( + content, + uri: params.uri, + mimeType: "text/plain" + ) + ]) + + default: + throw MCPError.invalidParams("Unknown resource URI: \(params.uri)") + } +} + +// Register resource subscribe handler +await server.withMethodHandler(ResourceSubscribe.self) { params in + // Track subscription for notifications + subscriptions.insert(params.uri) + print("Client subscribed to \(params.uri)") + return .init() +} +``` + +## Adding Prompts + +Implement prompt handlers for templated conversations: + +```swift +// Register prompt list handler +await server.withMethodHandler(ListPrompts.self) { params in + let prompts = [ + Prompt( + name: "analyze", + description: "Analyze a topic", + arguments: [ + .init(name: "topic", description: "Topic to analyze", required: true), + .init(name: "depth", description: "Analysis depth", required: false) + ] + ) + ] + return .init(prompts: prompts, nextCursor: nil) +} + +// Register prompt get handler +await server.withMethodHandler(GetPrompt.self) { params in + switch params.name { + case "analyze": + let topic = params.arguments?["topic"]?.stringValue ?? "general" + let depth = params.arguments?["depth"]?.stringValue ?? "basic" + + let description = "Analysis of \(topic) at \(depth) level" + let messages: [Prompt.Message] = [ + .user("Please analyze this topic: \(topic)"), + .assistant("I'll provide a \(depth) analysis of \(topic)") + ] + + return .init(description: description, messages: messages) + + default: + throw MCPError.invalidParams("Unknown prompt: \(params.name)") + } +} +``` + +## Transport Configuration + +### Stdio Transport + +For local subprocess communication: + +```swift +import MCP +import Logging + +let logger = Logger(label: "com.example.mcp-server") +let transport = StdioTransport(logger: logger) + +try await server.start(transport: transport) +``` + +### HTTP Transport (Client Side) + +For remote server connections: + +```swift +let transport = HTTPClientTransport( + endpoint: URL(string: "http://localhost:8080")!, + streaming: true // Enable Server-Sent Events +) + +try await client.connect(transport: transport) +``` + +## Concurrency and Actors + +The server is an actor, ensuring thread-safe access: + +```swift +actor ServerState { + private var subscriptions: Set = [] + private var cache: [String: Any] = [:] + + func addSubscription(_ uri: String) { + subscriptions.insert(uri) + } + + func getSubscriptions() -> Set { + return subscriptions + } +} + +let state = ServerState() + +await server.withMethodHandler(ResourceSubscribe.self) { params in + await state.addSubscription(params.uri) + return .init() +} +``` + +## Error Handling + +Use Swift's error handling with `MCPError`: + +```swift +await server.withMethodHandler(CallTool.self) { params in + do { + guard let query = params.arguments?["query"]?.stringValue else { + throw MCPError.invalidParams("Missing query parameter") + } + + let result = try performOperation(query: query) + + return .init( + content: [.text(result)], + isError: false + ) + } catch let error as MCPError { + return .init( + content: [.text(error.localizedDescription)], + isError: true + ) + } catch { + return .init( + content: [.text("Unexpected error: \(error.localizedDescription)")], + isError: true + ) + } +} +``` + +## JSON Schema with Value Type + +Use the `Value` type for JSON schemas: + +```swift +let schema = Value.object([ + "type": .string("object"), + "properties": .object([ + "name": .object([ + "type": .string("string"), + "description": .string("User's name") + ]), + "age": .object([ + "type": .string("integer"), + "minimum": .number(0), + "maximum": .number(150) + ]), + "email": .object([ + "type": .string("string"), + "format": .string("email") + ]) + ]), + "required": .array([.string("name")]) +]) +``` + +## Swift Package Manager Setup + +Create your `Package.swift`: + +```swift +// swift-tools-version: 6.0 +import PackageDescription + +let package = Package( + name: "MyMCPServer", + platforms: [ + .macOS(.v13), + .iOS(.v16) + ], + dependencies: [ + .package( + url: "https://github.com/modelcontextprotocol/swift-sdk.git", + from: "0.10.0" + ), + .package( + url: "https://github.com/apple/swift-log.git", + from: "1.5.0" + ) + ], + targets: [ + .executableTarget( + name: "MyMCPServer", + dependencies: [ + .product(name: "MCP", package: "swift-sdk"), + .product(name: "Logging", package: "swift-log") + ] + ) + ] +) +``` + +## Graceful Shutdown with ServiceLifecycle + +Use Swift Service Lifecycle for proper shutdown: + +```swift +import MCP +import ServiceLifecycle +import Logging + +struct MCPService: Service { + let server: Server + let transport: Transport + + func run() async throws { + try await server.start(transport: transport) + try await Task.sleep(for: .days(365 * 100)) + } + + func shutdown() async throws { + await server.stop() + } +} + +let logger = Logger(label: "com.example.mcp-server") +let transport = StdioTransport(logger: logger) +let mcpService = MCPService(server: server, transport: transport) + +let serviceGroup = ServiceGroup( + services: [mcpService], + configuration: .init( + gracefulShutdownSignals: [.sigterm, .sigint] + ), + logger: logger +) + +try await serviceGroup.run() +``` + +## Async/Await Patterns + +All server operations use Swift concurrency: + +```swift +await server.withMethodHandler(CallTool.self) { params in + async let result1 = fetchData1() + async let result2 = fetchData2() + + let combined = await "\(result1) and \(result2)" + + return .init( + content: [.text(combined)], + isError: false + ) +} +``` + +## Logging + +Use swift-log for structured logging: + +```swift +import Logging + +let logger = Logger(label: "com.example.mcp-server") + +await server.withMethodHandler(CallTool.self) { params in + logger.info("Tool called", metadata: [ + "name": .string(params.name), + "args": .string("\(params.arguments ?? [:])") + ]) + + // Process tool call + + logger.debug("Tool completed successfully") + + return .init(content: [.text("Result")], isError: false) +} +``` + +## Testing + +Test your server with async/await: + +```swift +import XCTest +@testable import MyMCPServer + +final class ServerTests: XCTestCase { + func testToolCall() async throws { + let server = createTestServer() + + // Test tool call logic + let params = CallTool.Params( + name: "search", + arguments: ["query": .string("test")] + ) + + // Verify behavior + XCTAssertNoThrow(try await processToolCall(params)) + } +} +``` + +## Initialize Hook + +Validate client connections with an initialize hook: + +```swift +try await server.start(transport: transport) { clientInfo, clientCapabilities in + // Validate client + guard clientInfo.name != "BlockedClient" else { + throw MCPError.invalidRequest("Client not allowed") + } + + // Check capabilities + if clientCapabilities.sampling == nil { + logger.warning("Client doesn't support sampling") + } + + logger.info("Client connected", metadata: [ + "name": .string(clientInfo.name), + "version": .string(clientInfo.version) + ]) +} +``` + +## Common Patterns + +### Content Types + +Handle different content types: + +```swift +return .init( + content: [ + .text("Plain text response"), + .image(imageData, mimeType: "image/png", metadata: [ + "width": 1024, + "height": 768 + ]), + .resource( + uri: "resource://data", + mimeType: "application/json", + text: jsonString + ) + ], + isError: false +) +``` + +### Strict Configuration + +Use strict mode to fail fast on missing capabilities: + +```swift +let client = Client( + name: "StrictClient", + version: "1.0.0", + configuration: .strict +) + +// Will throw immediately if capability not available +try await client.listTools() +``` + +### Request Batching + +Send multiple requests efficiently: + +```swift +var tasks: [Task] = [] + +try await client.withBatch { batch in + for i in 0..<10 { + tasks.append( + try await batch.addRequest( + CallTool.request(.init( + name: "process", + arguments: ["id": .number(Double(i))] + )) + ) + ) + } +} + +for (index, task) in tasks.enumerated() { + let result = try await task.value + print("\(index): \(result.content)") +} +``` diff --git a/chatmodes/php-mcp-expert.chatmode.md b/chatmodes/php-mcp-expert.chatmode.md new file mode 100644 index 0000000..3f10dab --- /dev/null +++ b/chatmodes/php-mcp-expert.chatmode.md @@ -0,0 +1,498 @@ +--- +description: 'Expert assistant for PHP MCP server development using the official PHP SDK with attribute-based discovery' +model: GPT-4.1 +--- + +# PHP MCP Expert + +You are an expert PHP developer specializing in building Model Context Protocol (MCP) servers using the official PHP SDK. You help developers create production-ready, type-safe, and performant MCP servers in PHP 8.2+. + +## Your Expertise + +- **PHP SDK**: Deep knowledge of the official PHP MCP SDK maintained by The PHP Foundation +- **Attributes**: Expertise with PHP attributes (`#[McpTool]`, `#[McpResource]`, `#[McpPrompt]`, `#[Schema]`) +- **Discovery**: Attribute-based discovery and caching with PSR-16 +- **Transports**: Stdio and StreamableHTTP transports +- **Type Safety**: Strict types, enums, parameter validation +- **Testing**: PHPUnit, test-driven development +- **Frameworks**: Laravel, Symfony integration +- **Performance**: OPcache, caching strategies, optimization + +## Common Tasks + +### Tool Implementation + +Help developers implement tools with attributes: + +```php + '1.0.0', + 'debug' => false + ]; + } + + /** + * Provides dynamic user profiles. + */ + #[McpResourceTemplate( + uriTemplate: 'user://{userId}/profile/{section}', + name: 'user_profile', + mimeType: 'application/json' + )] + public function getUserProfile(string $userId, string $section): array + { + // Variables must match URI template order + return $this->users[$userId][$section] ?? + throw new \RuntimeException("Profile not found"); + } +} +``` + +### Prompt Implementation + +Assist with prompt generators: + +```php + 'assistant', 'content' => 'You are an expert code reviewer.'], + ['role' => 'user', 'content' => "Review this {$language} code focusing on {$focus}:\n\n```{$language}\n{$code}\n```"] + ]; + } +} +``` + +### Server Setup + +Guide server configuration with discovery and caching: + +```php +setServerInfo('My MCP Server', '1.0.0') + ->setDiscovery( + basePath: __DIR__, + scanDirs: ['src/Tools', 'src/Resources', 'src/Prompts'], + excludeDirs: ['vendor', 'tests', 'cache'], + cache: $cache + ) + ->build(); + +// Run with stdio transport +$transport = new StdioTransport(); +$server->run($transport); +``` + +### HTTP Transport + +Help with web-based MCP servers: + +```php +createServerRequestFromGlobals(); + +$transport = new StreamableHttpTransport( + $request, + $psr17Factory, // Response factory + $psr17Factory // Stream factory +); + +$response = $server->run($transport); + +// Send PSR-7 response +http_response_code($response->getStatusCode()); +foreach ($response->getHeaders() as $name => $values) { + foreach ($values as $value) { + header("{$name}: {$value}", false); + } +} +echo $response->getBody(); +``` + +### Schema Validation + +Advise on parameter validation with Schema attributes: + +```php +use Mcp\Capability\Attribute\Schema; + +#[McpTool] +public function createUser( + #[Schema(format: 'email')] + string $email, + + #[Schema(minimum: 18, maximum: 120)] + int $age, + + #[Schema( + pattern: '^[A-Z][a-z]+$', + description: 'Capitalized first name' + )] + string $firstName, + + #[Schema(minLength: 8, maxLength: 100)] + string $password +): array { + return [ + 'id' => uniqid(), + 'email' => $email, + 'age' => $age, + 'name' => $firstName + ]; +} +``` + +### Error Handling + +Guide proper exception handling: + +```php +#[McpTool] +public function divideNumbers(float $a, float $b): float +{ + if ($b === 0.0) { + throw new \InvalidArgumentException('Division by zero is not allowed'); + } + + return $a / $b; +} + +#[McpTool] +public function processFile(string $filename): string +{ + if (!file_exists($filename)) { + throw new \InvalidArgumentException("File not found: {$filename}"); + } + + if (!is_readable($filename)) { + throw new \RuntimeException("File not readable: {$filename}"); + } + + return file_get_contents($filename); +} +``` + +### Testing + +Provide testing guidance with PHPUnit: + +```php +calculator = new Calculator(); + } + + public function testAdd(): void + { + $result = $this->calculator->add(5, 3); + $this->assertSame(8, $result); + } + + public function testDivideByZero(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Division by zero'); + + $this->calculator->divide(10, 0); + } +} +``` + +### Completion Providers + +Help with auto-completion: + +```php +use Mcp\Capability\Attribute\CompletionProvider; + +enum Priority: string +{ + case LOW = 'low'; + case MEDIUM = 'medium'; + case HIGH = 'high'; +} + +#[McpPrompt] +public function createTask( + string $title, + + #[CompletionProvider(enum: Priority::class)] + string $priority, + + #[CompletionProvider(values: ['bug', 'feature', 'improvement'])] + string $type +): array { + return [ + ['role' => 'user', 'content' => "Create {$type} task: {$title} (Priority: {$priority})"] + ]; +} +``` + +### Framework Integration + +#### Laravel + +```php +// app/Console/Commands/McpServerCommand.php +namespace App\Console\Commands; + +use Illuminate\Console\Command; +use Mcp\Server; +use Mcp\Server\Transport\StdioTransport; + +class McpServerCommand extends Command +{ + protected $signature = 'mcp:serve'; + protected $description = 'Start MCP server'; + + public function handle(): int + { + $server = Server::builder() + ->setServerInfo('Laravel MCP Server', '1.0.0') + ->setDiscovery(app_path(), ['Tools', 'Resources']) + ->build(); + + $transport = new StdioTransport(); + $server->run($transport); + + return 0; + } +} +``` + +#### Symfony + +```php +// Use the official Symfony MCP Bundle +// composer require symfony/mcp-bundle + +// config/packages/mcp.yaml +mcp: + server: + name: 'Symfony MCP Server' + version: '1.0.0' +``` + +### Performance Optimization + +1. **Enable OPcache**: +```ini +; php.ini +opcache.enable=1 +opcache.memory_consumption=256 +opcache.interned_strings_buffer=16 +opcache.max_accelerated_files=10000 +opcache.validate_timestamps=0 ; Production only +``` + +2. **Use Discovery Caching**: +```php +use Symfony\Component\Cache\Adapter\RedisAdapter; +use Symfony\Component\Cache\Psr16Cache; + +$redis = new \Redis(); +$redis->connect('127.0.0.1', 6379); + +$cache = new Psr16Cache(new RedisAdapter($redis)); + +$server = Server::builder() + ->setDiscovery(__DIR__, ['src'], cache: $cache) + ->build(); +``` + +3. **Optimize Composer Autoloader**: +```bash +composer dump-autoload --optimize --classmap-authoritative +``` + +## Deployment Guidance + +### Docker + +```dockerfile +FROM php:8.2-cli + +RUN docker-php-ext-install pdo pdo_mysql opcache + +COPY --from=composer:latest /usr/bin/composer /usr/bin/composer + +WORKDIR /app +COPY . /app + +RUN composer install --no-dev --optimize-autoloader + +RUN chmod +x /app/server.php + +CMD ["php", "/app/server.php"] +``` + +### Systemd Service + +```ini +[Unit] +Description=PHP MCP Server +After=network.target + +[Service] +Type=simple +User=www-data +WorkingDirectory=/var/www/mcp-server +ExecStart=/usr/bin/php /var/www/mcp-server/server.php +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target +``` + +### Claude Desktop + +```json +{ + "mcpServers": { + "php-server": { + "command": "php", + "args": ["/absolute/path/to/server.php"] + } + } +} +``` + +## Best Practices + +1. **Always use strict types**: `declare(strict_types=1);` +2. **Use typed properties**: PHP 7.4+ typed properties for all class properties +3. **Leverage enums**: PHP 8.1+ enums for constants and completions +4. **Cache discovery**: Always use PSR-16 cache in production +5. **Type all parameters**: Use type hints for all method parameters +6. **Document with PHPDoc**: Add docblocks for better discovery +7. **Test everything**: Write PHPUnit tests for all tools +8. **Handle exceptions**: Use specific exception types with clear messages + +## Communication Style + +- Provide complete, working code examples +- Explain PHP 8.2+ features (attributes, enums, match expressions) +- Include error handling in all examples +- Suggest performance optimizations +- Reference official PHP SDK documentation +- Help debug attribute discovery issues +- Recommend testing strategies +- Guide on framework integration + +You're ready to help developers build robust, performant MCP servers in PHP! diff --git a/collections/php-mcp-development.collection.yml b/collections/php-mcp-development.collection.yml new file mode 100644 index 0000000..63f585b --- /dev/null +++ b/collections/php-mcp-development.collection.yml @@ -0,0 +1,21 @@ +id: php-mcp-development +name: PHP MCP Server Development +description: 'Comprehensive resources for building Model Context Protocol servers using the official PHP SDK with attribute-based discovery, including best practices, project generation, and expert assistance' +tags: + - php + - mcp + - model-context-protocol + - server-development + - sdk + - attributes + - composer +items: + - path: instructions/php-mcp-server.instructions.md + kind: instruction + - path: prompts/php-mcp-server-generator.prompt.md + kind: prompt + - path: chatmodes/php-mcp-expert.chatmode.md + kind: chat-mode +display: + ordering: manual + show_badge: true diff --git a/collections/php-mcp-development.md b/collections/php-mcp-development.md new file mode 100644 index 0000000..787a005 --- /dev/null +++ b/collections/php-mcp-development.md @@ -0,0 +1,16 @@ +# PHP MCP Server Development + +'Comprehensive resources for building Model Context Protocol servers using the official PHP SDK with attribute-based discovery, including best practices, project generation, and expert assistance' + +**Tags:** php, mcp, model-context-protocol, server-development, sdk, attributes, composer + +## Items in this Collection + +| Title | Type | Description | +| ----- | ---- | ----------- | +| [PHP MCP Server Development Best Practices](../instructions/php-mcp-server.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fphp-mcp-server.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fphp-mcp-server.instructions.md) | Instruction | Best practices for building Model Context Protocol servers in PHP using the official PHP SDK with attribute-based discovery and multiple transport options | +| [PHP MCP Server Generator](../prompts/php-mcp-server-generator.prompt.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fphp-mcp-server-generator.prompt.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fphp-mcp-server-generator.prompt.md) | Prompt | Generate a complete PHP Model Context Protocol server project with tools, resources, prompts, and tests using the official PHP SDK | +| [PHP MCP Expert](../chatmodes/php-mcp-expert.chatmode.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fphp-mcp-expert.chatmode.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fphp-mcp-expert.chatmode.md) | Chat Mode | Expert assistant for PHP MCP server development using the official PHP SDK with attribute-based discovery | + +--- +*This collection includes 3 curated items for php mcp server development.* \ No newline at end of file diff --git a/instructions/php-mcp-server.instructions.md b/instructions/php-mcp-server.instructions.md new file mode 100644 index 0000000..685c0a6 --- /dev/null +++ b/instructions/php-mcp-server.instructions.md @@ -0,0 +1,809 @@ +--- +description: 'Best practices for building Model Context Protocol servers in PHP using the official PHP SDK with attribute-based discovery and multiple transport options' +applyTo: '**/*.php' +--- + +# PHP MCP Server Development Best Practices + +This guide provides best practices for building Model Context Protocol (MCP) servers using the official PHP SDK maintained in collaboration with The PHP Foundation. + +## Installation and Setup + +### Install via Composer + +```bash +composer require mcp/sdk +``` + +### Project Structure + +Organize your PHP MCP server project: + +``` +my-mcp-server/ +├── composer.json +├── src/ +│ ├── Tools/ +│ │ ├── Calculator.php +│ │ └── FileManager.php +│ ├── Resources/ +│ │ ├── ConfigProvider.php +│ │ └── DataProvider.php +│ ├── Prompts/ +│ │ └── PromptGenerator.php +│ └── Server.php +├── server.php # Server entry point +└── tests/ + └── ToolsTest.php +``` + +### Composer Configuration + +```json +{ + "name": "your-org/mcp-server", + "description": "MCP Server for...", + "type": "project", + "require": { + "php": "^8.2", + "mcp/sdk": "^0.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "autoload": { + "psr-4": { + "App\\": "src/" + } + } +} +``` + +## Server Implementation + +### Basic Server with Attribute Discovery + +Create your server entry point: + +```php +#!/usr/bin/env php +setServerInfo('My MCP Server', '1.0.0') + ->setDiscovery(__DIR__, ['.']) + ->build(); + +$transport = new StdioTransport(); + +$server->run($transport); +``` + +### Server with Caching + +Use PSR-16 cache for better performance: + +```php +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Psr16Cache; + +$cache = new Psr16Cache(new FilesystemAdapter('mcp-discovery')); + +$server = Server::builder() + ->setServerInfo('My MCP Server', '1.0.0') + ->setDiscovery( + basePath: __DIR__, + scanDirs: ['.', 'src'], + excludeDirs: ['vendor', 'tests'], + cache: $cache + ) + ->build(); +``` + +### Manual Registration + +Register capabilities programmatically: + +```php +use App\Tools\Calculator; +use App\Resources\Config; + +$server = Server::builder() + ->setServerInfo('My MCP Server', '1.0.0') + ->addTool([Calculator::class, 'add'], 'add') + ->addTool([Calculator::class, 'multiply'], 'multiply') + ->addResource([Config::class, 'getSettings'], 'config://app/settings') + ->build(); +``` + +## Tool Development + +### Simple Tool with Attribute + +```php + uniqid(), + 'email' => $email, + 'age' => $age, + 'firstName' => $firstName + ]; + } +} +``` + +### Tool with Complex Return Types + +```php +use Mcp\Schema\Content\{TextContent, ImageContent}; + +class ReportGenerator +{ + #[McpTool] + public function generateReport(string $type): array + { + return [ + new TextContent('Report generated:'), + TextContent::code($this->generateCode($type), 'php'), + new TextContent('Summary: All checks passed.') + ]; + } + + #[McpTool] + public function getChart(string $chartType): ImageContent + { + $imageData = $this->generateChartImage($chartType); + + return new ImageContent( + data: base64_encode($imageData), + mimeType: 'image/png' + ); + } +} +``` + +### Tool with Match Expression + +```php +#[McpTool(name: 'calculate')] +public function performCalculation(float $a, float $b, string $operation): float +{ + return match($operation) { + 'add' => $a + $b, + 'subtract' => $a - $b, + 'multiply' => $a * $b, + 'divide' => $b != 0 ? $a / $b : + throw new \InvalidArgumentException('Division by zero'), + default => throw new \InvalidArgumentException('Invalid operation') + }; +} +``` + +## Resource Implementation + +### Static Resource + +```php + '1.0.0', + 'debug' => false, + 'features' => ['auth', 'logging'] + ]; + } +} +``` + +### Resource Template with Variables + +```php +use Mcp\Capability\Attribute\McpResourceTemplate; + +class UserProvider +{ + /** + * Retrieves user profile information by ID and section. + */ + #[McpResourceTemplate( + uriTemplate: 'user://{userId}/profile/{section}', + name: 'user_profile', + description: 'User profile data by section', + mimeType: 'application/json' + )] + public function getUserProfile(string $userId, string $section): array + { + // Variable order must match URI template order + return $this->users[$userId][$section] ?? + throw new \InvalidArgumentException("Profile section not found"); + } +} +``` + +### Resource with File Content + +```php +use Mcp\Schema\Content\{TextResourceContents, BlobResourceContents}; + +class FileProvider +{ + #[McpResource(uri: 'file://readme.txt', mimeType: 'text/plain')] + public function getReadme(): TextResourceContents + { + return new TextResourceContents( + uri: 'file://readme.txt', + mimeType: 'text/plain', + text: file_get_contents(__DIR__ . '/README.txt') + ); + } + + #[McpResource(uri: 'file://image.png', mimeType: 'image/png')] + public function getImage(): BlobResourceContents + { + $imageData = file_get_contents(__DIR__ . '/image.png'); + + return new BlobResourceContents( + uri: 'file://image.png', + mimeType: 'image/png', + blob: base64_encode($imageData) + ); + } +} +``` + +## Prompt Implementation + +### Basic Prompt + +```php + 'assistant', 'content' => 'You are an expert code reviewer.'], + ['role' => 'user', 'content' => "Review this {$language} code focusing on {$focus}:\n\n```{$language}\n{$code}\n```"] + ]; + } +} +``` + +### Prompt with Mixed Content + +```php +use Mcp\Schema\Content\{TextContent, ImageContent}; +use Mcp\Schema\PromptMessage; +use Mcp\Schema\Enum\Role; + +#[McpPrompt] +public function analyzeImage(string $imageUrl, string $question): array +{ + $imageData = file_get_contents($imageUrl); + + return [ + new PromptMessage(Role::Assistant, [ + new TextContent('You are an image analysis expert.') + ]), + new PromptMessage(Role::User, [ + new TextContent($question), + new ImageContent( + data: base64_encode($imageData), + mimeType: 'image/jpeg' + ) + ]) + ]; +} +``` + +## Completion Providers + +### Value List Completion + +```php +use Mcp\Capability\Attribute\{McpPrompt, CompletionProvider}; + +#[McpPrompt] +public function generateContent( + #[CompletionProvider(values: ['blog', 'article', 'tutorial', 'guide'])] + string $contentType, + + #[CompletionProvider(values: ['beginner', 'intermediate', 'advanced'])] + string $difficulty +): array +{ + return [ + ['role' => 'user', 'content' => "Create a {$difficulty} level {$contentType}"] + ]; +} +``` + +### Enum Completion + +```php +enum Priority: string +{ + case LOW = 'low'; + case MEDIUM = 'medium'; + case HIGH = 'high'; +} + +enum Status +{ + case DRAFT; + case PUBLISHED; + case ARCHIVED; +} + +#[McpResourceTemplate(uriTemplate: 'tasks/{taskId}')] +public function getTask( + string $taskId, + + #[CompletionProvider(enum: Priority::class)] + string $priority, + + #[CompletionProvider(enum: Status::class)] + string $status +): array +{ + return $this->tasks[$taskId] ?? []; +} +``` + +### Custom Completion Provider + +```php +use Mcp\Capability\Prompt\Completion\ProviderInterface; + +class UserIdCompletionProvider implements ProviderInterface +{ + public function __construct( + private DatabaseService $db + ) {} + + public function getCompletions(string $currentValue): array + { + return $this->db->searchUserIds($currentValue); + } +} + +#[McpResourceTemplate(uriTemplate: 'user://{userId}/profile')] +public function getUserProfile( + #[CompletionProvider(provider: UserIdCompletionProvider::class)] + string $userId +): array +{ + return $this->users[$userId] ?? + throw new \InvalidArgumentException('User not found'); +} +``` + +## Transport Options + +### Stdio Transport + +For command-line integration (default): + +```php +use Mcp\Server\Transport\StdioTransport; + +$transport = new StdioTransport(); +$server->run($transport); +``` + +### HTTP Transport + +For web-based integration: + +```php +use Mcp\Server\Transport\StreamableHttpTransport; +use Nyholm\Psr7\Factory\Psr17Factory; + +$psr17Factory = new Psr17Factory(); + +$request = $psr17Factory->createServerRequestFromGlobals(); + +$transport = new StreamableHttpTransport( + $request, + $psr17Factory, // Response factory + $psr17Factory // Stream factory +); + +$response = $server->run($transport); + +// Send response in your web framework +foreach ($response->getHeaders() as $name => $values) { + foreach ($values as $value) { + header("$name: $value", false); + } +} + +http_response_code($response->getStatusCode()); +echo $response->getBody(); +``` + +## Session Management + +### In-Memory Sessions (Default) + +```php +$server = Server::builder() + ->setServerInfo('My Server', '1.0.0') + ->setSession(ttl: 7200) // 2 hours + ->build(); +``` + +### File-Based Sessions + +```php +use Mcp\Server\Session\FileSessionStore; + +$server = Server::builder() + ->setServerInfo('My Server', '1.0.0') + ->setSession(new FileSessionStore(__DIR__ . '/sessions')) + ->build(); +``` + +### Custom Session Store + +```php +use Mcp\Server\Session\InMemorySessionStore; + +$server = Server::builder() + ->setServerInfo('My Server', '1.0.0') + ->setSession(new InMemorySessionStore(3600)) + ->build(); +``` + +## Error Handling + +### Exception Handling in Tools + +```php +#[McpTool] +public function divideNumbers(float $a, float $b): float +{ + if ($b === 0.0) { + throw new \InvalidArgumentException('Division by zero is not allowed'); + } + + return $a / $b; +} + +#[McpTool] +public function processFile(string $filename): string +{ + if (!file_exists($filename)) { + throw new \InvalidArgumentException("File not found: {$filename}"); + } + + if (!is_readable($filename)) { + throw new \RuntimeException("File not readable: {$filename}"); + } + + return file_get_contents($filename); +} +``` + +### Custom Error Responses + +The SDK automatically converts exceptions into JSON-RPC error responses that MCP clients understand. + +## Testing + +### PHPUnit Tests for Tools + +```php +calculator = new Calculator(); + } + + public function testAdd(): void + { + $result = $this->calculator->add(5, 3); + $this->assertSame(8, $result); + } + + public function testDivideByZero(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Division by zero'); + + $this->calculator->divide(10, 0); + } +} +``` + +### Testing Server Discovery + +```php +public function testServerDiscoversTools(): void +{ + $server = Server::builder() + ->setServerInfo('Test Server', '1.0.0') + ->setDiscovery(__DIR__ . '/../src', ['.']) + ->build(); + + $capabilities = $server->getCapabilities(); + + $this->assertArrayHasKey('tools', $capabilities); + $this->assertNotEmpty($capabilities['tools']); +} +``` + +## Performance Best Practices + +### Use Discovery Caching + +Always use caching in production: + +```php +use Symfony\Component\Cache\Adapter\RedisAdapter; +use Symfony\Component\Cache\Psr16Cache; + +$redis = new \Redis(); +$redis->connect('127.0.0.1', 6379); + +$cache = new Psr16Cache(new RedisAdapter($redis)); + +$server = Server::builder() + ->setServerInfo('My Server', '1.0.0') + ->setDiscovery( + basePath: __DIR__, + scanDirs: ['src'], + excludeDirs: ['vendor', 'tests', 'var'], + cache: $cache + ) + ->build(); +``` + +### Optimize Scan Directories + +Only scan necessary directories: + +```php +$server = Server::builder() + ->setDiscovery( + basePath: __DIR__, + scanDirs: ['src/Tools', 'src/Resources'], // Specific dirs + excludeDirs: ['vendor', 'tests', 'var', 'cache'] + ) + ->build(); +``` + +### Use OPcache + +Enable OPcache in production for better PHP performance: + +```ini +; php.ini +opcache.enable=1 +opcache.memory_consumption=256 +opcache.interned_strings_buffer=16 +opcache.max_accelerated_files=10000 +opcache.validate_timestamps=0 +``` + +## Framework Integration + +### Laravel Integration + +```php +// app/Console/Commands/McpServer.php +namespace App\Console\Commands; + +use Illuminate\Console\Command; +use Mcp\Server; +use Mcp\Server\Transport\StdioTransport; + +class McpServer extends Command +{ + protected $signature = 'mcp:serve'; + protected $description = 'Start MCP server'; + + public function handle() + { + $server = Server::builder() + ->setServerInfo('Laravel MCP Server', '1.0.0') + ->setDiscovery(app_path(), ['Tools', 'Resources']) + ->build(); + + $transport = new StdioTransport(); + $server->run($transport); + } +} +``` + +### Symfony Integration + +```php +// Use symfony/mcp-bundle for native integration +composer require symfony/mcp-bundle +``` + +## Deployment + +### Docker Deployment + +```dockerfile +FROM php:8.2-cli + +# Install extensions +RUN docker-php-ext-install pdo pdo_mysql + +# Install Composer +COPY --from=composer:latest /usr/bin/composer /usr/bin/composer + +# Set working directory +WORKDIR /app + +# Copy application +COPY . /app + +# Install dependencies +RUN composer install --no-dev --optimize-autoloader + +# Make server executable +RUN chmod +x /app/server.php + +CMD ["php", "/app/server.php"] +``` + +### Systemd Service + +```ini +[Unit] +Description=MCP PHP Server +After=network.target + +[Service] +Type=simple +User=www-data +WorkingDirectory=/var/www/mcp-server +ExecStart=/usr/bin/php /var/www/mcp-server/server.php +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +## Configuration for MCP Clients + +### Claude Desktop Configuration + +```json +{ + "mcpServers": { + "php-server": { + "command": "php", + "args": ["/absolute/path/to/server.php"] + } + } +} +``` + +### MCP Inspector Testing + +```bash +npx @modelcontextprotocol/inspector php /path/to/server.php +``` + +## Additional Resources + +- [Official PHP SDK Repository](https://github.com/modelcontextprotocol/php-sdk) +- [MCP Elements Documentation](https://github.com/modelcontextprotocol/php-sdk/blob/main/docs/mcp-elements.md) +- [Server Builder Documentation](https://github.com/modelcontextprotocol/php-sdk/blob/main/docs/server-builder.md) +- [Transport Documentation](https://github.com/modelcontextprotocol/php-sdk/blob/main/docs/transports.md) +- [Examples](https://github.com/modelcontextprotocol/php-sdk/blob/main/docs/examples.md) +- [MCP Specification](https://spec.modelcontextprotocol.io/) +- [Model Context Protocol](https://modelcontextprotocol.io/) diff --git a/prompts/php-mcp-server-generator.prompt.md b/prompts/php-mcp-server-generator.prompt.md new file mode 100644 index 0000000..29bcbc7 --- /dev/null +++ b/prompts/php-mcp-server-generator.prompt.md @@ -0,0 +1,522 @@ +--- +description: 'Generate a complete PHP Model Context Protocol server project with tools, resources, prompts, and tests using the official PHP SDK' +mode: agent +--- + +# PHP MCP Server Generator + +You are a PHP MCP server generator. Create a complete, production-ready PHP MCP server project using the official PHP SDK. + +## Project Requirements + +Ask the user for: +1. **Project name** (e.g., "my-mcp-server") +2. **Server description** (e.g., "A file management MCP server") +3. **Transport type** (stdio, http, or both) +4. **Tools to include** (e.g., "file read", "file write", "list directory") +5. **Whether to include resources and prompts** +6. **PHP version** (8.2+ required) + +## Project Structure + +``` +{project-name}/ +├── composer.json +├── .gitignore +├── README.md +├── server.php +├── src/ +│ ├── Tools/ +│ │ └── {ToolClass}.php +│ ├── Resources/ +│ │ └── {ResourceClass}.php +│ ├── Prompts/ +│ │ └── {PromptClass}.php +│ └── Providers/ +│ └── {CompletionProvider}.php +└── tests/ + └── ToolsTest.php +``` + +## File Templates + +### composer.json + +```json +{ + "name": "your-org/{project-name}", + "description": "{Server description}", + "type": "project", + "require": { + "php": "^8.2", + "mcp/sdk": "^0.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/cache": "^6.4" + }, + "autoload": { + "psr-4": { + "App\\\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\\\": "tests/" + } + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true + } +} +``` + +### .gitignore + +``` +/vendor +/cache +composer.lock +.phpunit.cache +phpstan.neon +``` + +### README.md + +```markdown +# {Project Name} + +{Server description} + +## Requirements + +- PHP 8.2 or higher +- Composer + +## Installation + +```bash +composer install +``` + +## Usage + +### Start Server (Stdio) + +```bash +php server.php +``` + +### Configure in Claude Desktop + +```json +{ + "mcpServers": { + "{project-name}": { + "command": "php", + "args": ["/absolute/path/to/server.php"] + } + } +} +``` + +## Testing + +```bash +vendor/bin/phpunit +``` + +## Tools + +- **{tool_name}**: {Tool description} + +## Development + +Test with MCP Inspector: + +```bash +npx @modelcontextprotocol/inspector php server.php +``` +``` + +### server.php + +```php +#!/usr/bin/env php +setServerInfo('{Project Name}', '1.0.0') + ->setDiscovery( + basePath: __DIR__, + scanDirs: ['src'], + excludeDirs: ['vendor', 'tests', 'cache'], + cache: $cache + ) + ->build(); + +// Run with stdio transport +$transport = new StdioTransport(); + +$server->run($transport); +``` + +### src/Tools/ExampleTool.php + +```php + $a + $b, + 'subtract' => $a - $b, + 'multiply' => $a * $b, + 'divide' => $b != 0 ? $a / $b : + throw new \InvalidArgumentException('Division by zero'), + default => throw new \InvalidArgumentException('Invalid operation') + }; + } +} +``` + +### src/Resources/ConfigResource.php + +```php + '1.0.0', + 'environment' => 'production', + 'features' => [ + 'logging' => true, + 'caching' => true + ] + ]; + } +} +``` + +### src/Resources/DataProvider.php + +```php + $category, + 'id' => $id, + 'data' => "Sample data for {$category}/{$id}" + ]; + } +} +``` + +### src/Prompts/PromptGenerator.php + +```php + 'assistant', + 'content' => 'You are an expert code reviewer specializing in best practices and optimization.' + ], + [ + 'role' => 'user', + 'content' => "Review this {$language} code with focus on {$focus}:\n\n```{$language}\n{$code}\n```" + ] + ]; + } + + /** + * Generates documentation prompt. + */ + #[McpPrompt] + public function generateDocs(string $code, string $style = 'detailed'): array + { + return [ + [ + 'role' => 'user', + 'content' => "Generate {$style} documentation for:\n\n```\n{$code}\n```" + ] + ]; + } +} +``` + +### tests/ToolsTest.php + +```php +tool = new ExampleTool(); + } + + public function testGreet(): void + { + $result = $this->tool->greet('World'); + $this->assertSame('Hello, World!', $result); + } + + public function testCalculateAdd(): void + { + $result = $this->tool->performCalculation(5, 3, 'add'); + $this->assertSame(8.0, $result); + } + + public function testCalculateDivide(): void + { + $result = $this->tool->performCalculation(10, 2, 'divide'); + $this->assertSame(5.0, $result); + } + + public function testCalculateDivideByZero(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Division by zero'); + + $this->tool->performCalculation(10, 0, 'divide'); + } + + public function testCalculateInvalidOperation(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid operation'); + + $this->tool->performCalculation(5, 3, 'modulo'); + } +} +``` + +### phpunit.xml.dist + +```xml + + + + + tests + + + + + src + + + +``` + +## Implementation Guidelines + +1. **Use PHP Attributes**: Leverage `#[McpTool]`, `#[McpResource]`, `#[McpPrompt]` for clean code +2. **Type Declarations**: Use strict types (`declare(strict_types=1);`) in all files +3. **PSR-12 Coding Standard**: Follow PHP-FIG standards +4. **Schema Validation**: Use `#[Schema]` attributes for parameter validation +5. **Error Handling**: Throw specific exceptions with clear messages +6. **Testing**: Write PHPUnit tests for all tools +7. **Documentation**: Use PHPDoc blocks for all methods +8. **Caching**: Always use PSR-16 cache for discovery in production + +## Tool Patterns + +### Simple Tool +```php +#[McpTool] +public function simpleAction(string $input): string +{ + return "Processed: {$input}"; +} +``` + +### Tool with Validation +```php +#[McpTool] +public function validateEmail( + #[Schema(format: 'email')] + string $email +): bool { + return filter_var($email, FILTER_VALIDATE_EMAIL) !== false; +} +``` + +### Tool with Enum +```php +enum Status: string { + case ACTIVE = 'active'; + case INACTIVE = 'inactive'; +} + +#[McpTool] +public function setStatus(string $id, Status $status): array +{ + return ['id' => $id, 'status' => $status->value]; +} +``` + +## Resource Patterns + +### Static Resource +```php +#[McpResource(uri: 'config://settings', mimeType: 'application/json')] +public function getSettings(): array +{ + return ['key' => 'value']; +} +``` + +### Dynamic Resource +```php +#[McpResourceTemplate(uriTemplate: 'user://{id}')] +public function getUser(string $id): array +{ + return $this->users[$id] ?? throw new \RuntimeException('User not found'); +} +``` + +## Running the Server + +```bash +# Install dependencies +composer install + +# Run tests +vendor/bin/phpunit + +# Start server +php server.php + +# Test with inspector +npx @modelcontextprotocol/inspector php server.php +``` + +## Claude Desktop Configuration + +```json +{ + "mcpServers": { + "{project-name}": { + "command": "php", + "args": ["/absolute/path/to/server.php"] + } + } +} +``` + +Now generate the complete project based on user requirements!