diff --git a/plugins/plugin-dev/skills/mcp-integration/SKILL.md b/plugins/plugin-dev/skills/mcp-integration/SKILL.md index 509adc5..315a307 100644 --- a/plugins/plugin-dev/skills/mcp-integration/SKILL.md +++ b/plugins/plugin-dev/skills/mcp-integration/SKILL.md @@ -520,6 +520,7 @@ For detailed information, consult: - **`references/server-types.md`** - Deep dive on each server type - **`references/authentication.md`** - Authentication patterns and OAuth - **`references/tool-usage.md`** - Using MCP tools in commands and agents +- **`references/discovery.md`** - Finding and evaluating MCP servers ### Example Configurations @@ -530,6 +531,12 @@ Working examples in `examples/`: - **`http-server.json`** - REST API with token auth - **`ws-server.json`** - WebSocket server for real-time communication +### Scripts + +Utility scripts in `scripts/`: + +- **`search-mcp-servers.sh`** - Search MCP Registry with GitHub star rankings (see `references/discovery.md` for usage) + ### External Resources - **Official MCP Docs**: diff --git a/plugins/plugin-dev/skills/mcp-integration/references/discovery.md b/plugins/plugin-dev/skills/mcp-integration/references/discovery.md new file mode 100644 index 0000000..84d5563 --- /dev/null +++ b/plugins/plugin-dev/skills/mcp-integration/references/discovery.md @@ -0,0 +1,314 @@ +# MCP Server Discovery: Finding and Evaluating Servers + +Complete reference for discovering MCP servers to integrate into Claude Code plugins. + +## Overview + +When building plugins that need external capabilities, finding the right MCP server saves development time and leverages community-tested implementations. This guide covers discovery sources, search strategies, and evaluation criteria. + +### When to Use MCP Servers + +**Integrate existing MCP servers when:** + +- Standard functionality exists (file system, database, API clients) +- Multiple tools needed from single service (10+ related operations) +- OAuth or complex authentication required +- Community maintenance preferred over custom implementation + +**Build custom solutions when:** + +- Functionality is unique to your use case +- Only 1-2 simple tools needed +- Tight integration with plugin logic required +- Existing servers don't meet security requirements + +## Official MCP Registry + +The official registry at `registry.modelcontextprotocol.io` provides a REST API for discovering servers. + +### API Endpoint + +```text +https://registry.modelcontextprotocol.io/v0/servers +``` + +### Search Syntax + +**Basic search:** + +```text +GET /v0/servers?search=filesystem +``` + +**With limit:** + +```text +GET /v0/servers?search=database&limit=10 +``` + +**Pagination:** + +```text +GET /v0/servers?cursor= +``` + +**Quick test:** + +```bash +curl "https://registry.modelcontextprotocol.io/v0/servers?search=filesystem&limit=5" +``` + +No authentication required. Returns JSON with matching servers. + +### Response Structure + +```json +{ + "servers": [ + { + "name": "@modelcontextprotocol/server-filesystem", + "description": "MCP server for file system operations", + "repository": "https://github.com/modelcontextprotocol/servers", + "homepage": "https://modelcontextprotocol.io" + } + ], + "cursor": "next_page_cursor" +} +``` + +### Key Fields + +| Field | Description | +|-------|-------------| +| `name` | Package/server name (often npm scope) | +| `description` | Brief functionality summary | +| `repository` | Source code location | +| `homepage` | Documentation URL | + +### Limitations + +The official registry provides no popularity metrics. Supplement with GitHub stars or npm downloads for quality signals. + +## Alternative Discovery Sources + +### Smithery.ai + +Largest MCP server marketplace with usage metrics. + +**URL:** + +**Advantages:** + +- Call volume statistics (popularity indicator) +- Curated categories +- Installation instructions +- User ratings + +**Search tips:** + +- Browse categories for common use cases +- Sort by popularity for battle-tested servers +- Check "Recently Added" for new capabilities + +### npm Registry + +Official MCP servers and community packages available via npm. + +**Official scope search:** + +```bash +npm search @modelcontextprotocol +``` + +**Community search:** + +```bash +npm search mcp-server +``` + +**Key indicators:** + +- Weekly downloads (popularity) +- Last publish date (maintenance) +- Dependency count (complexity) + +### GitHub + +Direct source code access and community engagement metrics. + +**Topic search:** + +```text +https://github.com/topics/mcp-server +``` + +**Curated lists:** + +- `awesome-mcp-servers` repositories +- `modelcontextprotocol` organization + +**Search queries:** + +```text +mcp server in:name,description language:TypeScript +model context protocol in:readme +``` + +### MCP.SO + +Third-party aggregator with rankings and categories. + +**URL:** + +**Features:** + +- Combined metrics from multiple sources +- Category browsing +- Quick comparison view + +## Category Mappings + +Find servers for common plugin needs: + +| Plugin Need | Search Terms | Notable Servers | +|-------------|--------------|-----------------| +| Database access | postgres, sqlite, database, sql | @modelcontextprotocol/server-postgres, mcp-server-sqlite | +| File operations | filesystem, files, directory | @modelcontextprotocol/server-filesystem | +| GitHub integration | github, git, repository | Official GitHub MCP server | +| Web search | search, web, exa, tavily | Exa MCP, Tavily MCP | +| Browser automation | browser, playwright, puppeteer | Browserbase, Playwright MCP | +| Memory/knowledge | memory, knowledge, notes | @modelcontextprotocol/server-memory | +| Time/scheduling | time, calendar, scheduling | Google Calendar MCP | +| HTTP/APIs | fetch, http, rest, api | mcp-server-fetch | +| Slack integration | slack, messaging | Slack MCP server | +| Email | email, gmail, smtp | Gmail MCP servers | + +## Evaluation Criteria + +### Popularity Signals + +Strong indicators of production-ready servers: + +| Metric | Good | Excellent | +|--------|------|-----------| +| GitHub stars | 50+ | 500+ | +| npm weekly downloads | 100+ | 1,000+ | +| Smithery call volume | 1K+ | 10K+ | +| Forks | 10+ | 50+ | + +### Maintenance Indicators + +Signs of active development: + +- **Recent commits:** Last commit within 3 months +- **Issue response time:** Maintainers respond within 1-2 weeks +- **Release cadence:** Regular releases (monthly or quarterly) +- **Open issues ratio:** Less than 50% of total issues open + +### Quality Markers + +Technical quality signals: + +- **TypeScript:** Type safety and better IDE support +- **Tests:** Test suite with reasonable coverage +- **Documentation:** README with setup instructions and examples +- **Semantic versioning:** Proper version management +- **License:** Permissive license (MIT, Apache 2.0) +- **CI/CD:** Automated testing and publishing + +### Red Flags + +Avoid servers with these issues: + +| Red Flag | Risk | +|----------|------| +| No license | Legal uncertainty | +| Abandoned (1+ year stale) | Security vulnerabilities, no bug fixes | +| Hardcoded secrets in examples | Poor security practices | +| No error handling | Unreliable in production | +| Excessive permissions | Security risk | +| No README | Integration difficulty | +| Only personal use | Not designed for distribution | + +## Discovery Workflow + +Recommended process for finding MCP servers: + +1. **Define requirements** + - List needed capabilities + - Identify authentication requirements + - Consider security constraints + +2. **Search official registry first** + - Query `registry.modelcontextprotocol.io` + - Check official `@modelcontextprotocol` packages + +3. **Expand to alternative sources** + - Browse Smithery categories + - Search npm for community packages + - Check GitHub awesome lists + +4. **Evaluate candidates** + - Apply popularity filters + - Check maintenance status + - Review code quality + +5. **Test integration** + - Clone and run locally + - Verify tools work as expected + - Check error handling + +6. **Document choice** + - Record why server was selected + - Note any limitations + - Plan for future updates + +## Discovery Script + +Use the discovery script for automated server search with popularity ranking: + +```bash +# Basic search +./scripts/search-mcp-servers.sh database postgres + +# Limit results and get JSON output +./scripts/search-mcp-servers.sh --limit 5 --format json filesystem + +# Simple output for scripting (stars|name|url) +./scripts/search-mcp-servers.sh --format simple github +``` + +Run `./scripts/search-mcp-servers.sh --help` for all options. + +## Quick Reference + +### Search Priority + +1. Official MCP Registry (`registry.modelcontextprotocol.io`) +2. npm `@modelcontextprotocol` scope +3. Smithery.ai curated servers +4. npm community packages (`mcp-server-*`) +5. GitHub topic search + +### Minimum Quality Bar + +Before integrating an MCP server, verify: + +- [ ] Active maintenance (commits within 6 months) +- [ ] Proper license (MIT, Apache 2.0, etc.) +- [ ] Documentation exists +- [ ] No obvious security issues +- [ ] Works with current MCP protocol version + +**Verifying protocol compatibility:** Check the server's `package.json` for `@modelcontextprotocol/sdk` dependency version, or look for protocol version notes in the README. + +### Quick Evaluation + +```bash +# Check npm package health +npm view time.modified +npm view repository.url + +# Check GitHub activity +gh repo view --json stargazerCount,pushedAt +``` diff --git a/plugins/plugin-dev/skills/mcp-integration/scripts/search-mcp-servers.sh b/plugins/plugin-dev/skills/mcp-integration/scripts/search-mcp-servers.sh new file mode 100755 index 0000000..f3bfe6f --- /dev/null +++ b/plugins/plugin-dev/skills/mcp-integration/scripts/search-mcp-servers.sh @@ -0,0 +1,381 @@ +#!/bin/bash +# MCP Server Discovery Script +# Search the official MCP Registry and enrich results with GitHub stars + +set -euo pipefail + +# Configuration +REGISTRY_URL="https://registry.modelcontextprotocol.io/v0/servers" +DEFAULT_LIMIT=10 +DEFAULT_FORMAT="table" + +# Colors (disabled if not a terminal) +if [ -t 1 ]; then + BOLD='\033[1m' + DIM='\033[2m' + RESET='\033[0m' + YELLOW='\033[33m' + RED='\033[31m' + GREEN='\033[32m' +else + BOLD='' + DIM='' + RESET='' + YELLOW='' + RED='' + GREEN='' +fi + +# Show help message +show_help() { + cat << 'EOF' +MCP Server Discovery + +Search the official MCP Registry for servers and sort by GitHub popularity. + +USAGE: + search-mcp-servers.sh [OPTIONS] + +OPTIONS: + -n, --limit N Maximum results to return (default: 10) + -f, --format FMT Output format: table, json, simple (default: table) + -t, --transport T Filter by transport type: stdio, sse, http, ws + -h, --help Show this help message + +EXAMPLES: + # Search for database servers + ./search-mcp-servers.sh database postgres + + # Get top 5 results as JSON + ./search-mcp-servers.sh -n 5 --format json filesystem + + # Search for stdio-only servers + ./search-mcp-servers.sh --transport stdio github + + # Simple output for scripting + ./search-mcp-servers.sh --format simple "web search" + +OUTPUT FORMATS: + table - Formatted table with stars, name, description, URL (default) + json - JSON array for programmatic use + simple - One server per line: stars|name|url + +NOTES: + - Results are sorted by GitHub stars (descending) + - Servers without GitHub repos appear at the bottom + - Requires: curl, jq, gh (GitHub CLI) +EOF +} + +# Check for required dependencies +check_dependencies() { + local missing=() + + if ! command -v curl &> /dev/null; then + missing+=("curl") + fi + + if ! command -v jq &> /dev/null; then + missing+=("jq") + fi + + if ! command -v gh &> /dev/null; then + missing+=("gh (GitHub CLI)") + fi + + if [ ${#missing[@]} -gt 0 ]; then + echo -e "${RED}❌ Missing required dependencies:${RESET}" >&2 + for dep in "${missing[@]}"; do + echo " - $dep" >&2 + case "$dep" in + "curl") + echo " Install: brew install curl OR apt-get install curl" >&2 ;; + "jq") + echo " Install: brew install jq OR apt-get install jq" >&2 ;; + "gh (GitHub CLI)") + echo " Install: https://cli.github.com/" >&2 ;; + esac + done + exit 1 + fi +} + +# Get GitHub stars for a repository URL +# Returns 0 if not a GitHub repo or on error +get_github_stars() { + local repo_url="$1" + local stars=0 + + # Extract owner/repo from GitHub URL + if [[ "$repo_url" =~ github\.com[/:]([^/]+)/([^/]+) ]]; then + local owner="${BASH_REMATCH[1]}" + local repo="${BASH_REMATCH[2]}" + # Remove .git suffix if present + repo="${repo%.git}" + + # Query GitHub API + stars=$(gh api "repos/$owner/$repo" --jq '.stargazers_count' 2>/dev/null || echo "0") + + # Handle non-numeric responses + if ! [[ "$stars" =~ ^[0-9]+$ ]]; then + stars=0 + fi + fi + + echo "$stars" +} + +# Query the MCP Registry +query_registry() { + local keywords="$1" + local limit="$2" + + local encoded_keywords + encoded_keywords=$(echo -n "$keywords" | jq -sRr @uri) + + local url="${REGISTRY_URL}?search=${encoded_keywords}&limit=${limit}" + + local response + if ! response=$(curl -s --fail --max-time 30 "$url" 2>/dev/null); then + echo -e "${RED}❌ Failed to query MCP Registry${RESET}" >&2 + echo -e "${DIM} URL: $url${RESET}" >&2 + echo -e "${DIM} Check your network connection${RESET}" >&2 + exit 1 + fi + + # Validate response is valid JSON with servers array + if ! echo "$response" | jq -e '.servers' &>/dev/null; then + echo -e "${RED}❌ Invalid response from MCP Registry${RESET}" >&2 + exit 1 + fi + + echo "$response" +} + +# Format output as table +format_table() { + local data="$1" + local count + count=$(echo "$data" | jq 'length') + + if [ "$count" -eq 0 ]; then + echo -e "${YELLOW}No servers found matching your search.${RESET}" + echo "" + echo "Suggestions:" + echo " - Try broader search terms" + echo " - Check spelling" + echo " - Browse categories at https://smithery.ai/" + return + fi + + echo -e "${BOLD}┌─────────────────────────────────────────────────────────────────┐${RESET}" + echo -e "${BOLD}│ MCP Server Discovery Results (sorted by GitHub stars) │${RESET}" + echo -e "${BOLD}├─────────────────────────────────────────────────────────────────┤${RESET}" + + echo "$data" | jq -r '.[] | "\(.stars)|\(.name)|\(.description)|\(.repository)"' | while IFS='|' read -r stars name description repo; do + # Truncate description if too long + if [ ${#description} -gt 55 ]; then + description="${description:0:52}..." + fi + + # Format stars with padding + if [ "$stars" -eq 0 ]; then + stars_display=" - " + else + stars_display=$(printf "⭐ %'d" "$stars") + # Pad to 6 chars + while [ ${#stars_display} -lt 8 ]; do + stars_display="$stars_display " + done + fi + + echo -e "│ ${GREEN}${stars_display}${RESET} ${BOLD}${name}${RESET}" + echo -e "│ ${DIM}${description}${RESET}" + echo -e "│ ${DIM}${repo}${RESET}" + echo -e "├─────────────────────────────────────────────────────────────────┤" + done | head -n -1 # Remove last separator + + echo -e "${BOLD}└─────────────────────────────────────────────────────────────────┘${RESET}" + echo "" + echo -e "Found ${BOLD}$count${RESET} server(s) matching your search" +} + +# Format output as JSON +format_json() { + local data="$1" + echo "$data" | jq '.' +} + +# Format output as simple (one per line) +format_simple() { + local data="$1" + echo "$data" | jq -r '.[] | "\(.stars)|\(.name)|\(.repository)"' +} + +# Main function +main() { + local limit=$DEFAULT_LIMIT + local format=$DEFAULT_FORMAT + local transport="" + local keywords="" + + # Parse arguments + while [ $# -gt 0 ]; do + case "$1" in + -h|--help) + show_help + exit 0 + ;; + -n|--limit) + if [ -z "${2:-}" ] || [[ "$2" == -* ]]; then + echo -e "${RED}❌ --limit requires a number${RESET}" >&2 + exit 1 + fi + limit="$2" + if ! [[ "$limit" =~ ^[0-9]+$ ]] || [ "$limit" -eq 0 ]; then + echo -e "${RED}❌ --limit must be a positive number${RESET}" >&2 + exit 1 + fi + shift 2 + ;; + -f|--format) + if [ -z "${2:-}" ] || [[ "$2" == -* ]]; then + echo -e "${RED}❌ --format requires a value (table, json, simple)${RESET}" >&2 + exit 1 + fi + format="$2" + if [[ ! "$format" =~ ^(table|json|simple)$ ]]; then + echo -e "${RED}❌ Invalid format: $format (use table, json, or simple)${RESET}" >&2 + exit 1 + fi + shift 2 + ;; + -t|--transport) + if [ -z "${2:-}" ] || [[ "$2" == -* ]]; then + echo -e "${RED}❌ --transport requires a value (stdio, sse, http, ws)${RESET}" >&2 + exit 1 + fi + transport="$2" + if [[ ! "$transport" =~ ^(stdio|sse|http|ws)$ ]]; then + echo -e "${RED}❌ Invalid transport: $transport (use stdio, sse, http, or ws)${RESET}" >&2 + exit 1 + fi + shift 2 + ;; + -*) + echo -e "${RED}❌ Unknown option: $1${RESET}" >&2 + echo "Use --help for usage information" >&2 + exit 1 + ;; + *) + # Collect positional arguments as keywords + if [ -n "$keywords" ]; then + keywords="$keywords $1" + else + keywords="$1" + fi + shift + ;; + esac + done + + # Validate keywords + if [ -z "$keywords" ]; then + echo -e "${RED}❌ No search keywords provided${RESET}" >&2 + echo "" >&2 + echo "Usage: search-mcp-servers.sh [OPTIONS] " >&2 + echo "Use --help for more information" >&2 + exit 1 + fi + + # Check dependencies + check_dependencies + + # Query registry + if [ "$format" = "table" ]; then + echo -e "${DIM}Searching MCP Registry for: $keywords${RESET}" + echo "" + fi + + local response + response=$(query_registry "$keywords" "$limit") + + # Extract servers and enrich with stars + local servers + servers=$(echo "$response" | jq -c '.servers // []') + + local server_count + server_count=$(echo "$servers" | jq 'length') + + if [ "$server_count" -eq 0 ]; then + format_table "[]" + exit 0 + fi + + # Build enriched data with stars + if [ "$format" = "table" ]; then + echo -e "${DIM}Fetching GitHub stars for $server_count server(s)...${RESET}" + fi + + local enriched="[]" + local rate_limit_warned=false + + while IFS= read -r server; do + local name description repo stars + + name=$(echo "$server" | jq -r '.name // "unknown"') + description=$(echo "$server" | jq -r '.description // "No description"') + repo=$(echo "$server" | jq -r '.repository // ""') + + # Get stars (with rate limit handling) + if [ -n "$repo" ]; then + stars=$(get_github_stars "$repo") + + # Check for potential rate limiting (all zeros after first few) + if [ "$stars" -eq 0 ] && [ "$rate_limit_warned" = false ]; then + # Simple heuristic: if we're getting zeros, might be rate limited + : + fi + else + stars=0 + fi + + # Add to enriched array + enriched=$(echo "$enriched" | jq --arg name "$name" \ + --arg desc "$description" \ + --arg repo "$repo" \ + --argjson stars "$stars" \ + '. + [{name: $name, description: $desc, repository: $repo, stars: $stars}]') + + done < <(echo "$servers" | jq -c '.[]') + + # Filter by transport if specified + # NOTE: Transport filtering is reserved for future use. The MCP Registry API + # does not currently include transport type in server metadata. When this data + # becomes available, client-side filtering will be implemented here. + if [ -n "$transport" ]; then + if [ "$format" = "table" ]; then + echo -e "${YELLOW}Note: Transport filtering not yet available (registry doesn't provide transport data)${RESET}" + fi + fi + + # Sort by stars descending + enriched=$(echo "$enriched" | jq 'sort_by(-.stars)') + + # Output in requested format + case "$format" in + table) + echo "" + format_table "$enriched" + ;; + json) + format_json "$enriched" + ;; + simple) + format_simple "$enriched" + ;; + esac +} + +# Run main function +main "$@"