{"id":49535345,"url":"https://github.com/pitimon/mcp-plugin-patterns","last_synced_at":"2026-05-02T10:03:17.960Z","repository":{"id":346029089,"uuid":"1188303346","full_name":"pitimon/mcp-plugin-patterns","owner":"pitimon","description":"Production patterns for building MCP plugins for Claude Code — from c-memforge (27 tools)","archived":false,"fork":false,"pushed_at":"2026-03-21T22:48:21.000Z","size":28,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-22T11:14:18.874Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pitimon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-21T22:21:30.000Z","updated_at":"2026-03-21T22:48:24.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pitimon/mcp-plugin-patterns","commit_stats":null,"previous_names":["pitimon/mcp-plugin-patterns"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/pitimon/mcp-plugin-patterns","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pitimon%2Fmcp-plugin-patterns","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pitimon%2Fmcp-plugin-patterns/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pitimon%2Fmcp-plugin-patterns/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pitimon%2Fmcp-plugin-patterns/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pitimon","download_url":"https://codeload.github.com/pitimon/mcp-plugin-patterns/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pitimon%2Fmcp-plugin-patterns/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32530178,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T01:12:54.858Z","status":"online","status_checked_at":"2026-05-02T02:00:05.923Z","response_time":132,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-02T10:03:06.399Z","updated_at":"2026-05-02T10:03:17.950Z","avatar_url":"https://github.com/pitimon.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Building Production MCP Plugins for Claude Code\n\n**Practical patterns for building MCP plugins that LLMs actually use correctly.**\n\nThis guide shares design patterns, security practices, and lessons learned from building a production MCP plugin (27 tools, workflow hints, SSRF protection, background sync) for Claude Code. These patterns apply to any MCP plugin that talks to a remote API.\n\n**Not** a getting-started tutorial. This is a **builder's guide** — for engineers who have a working MCP server and want to make it production-grade.\n\n## Who This Is For\n\n- Engineers building Claude Code plugins with MCP tools\n- Teams shipping MCP servers that call remote APIs\n- Developers struggling with LLM tool selection across 10+ tools\n- Anyone who has shipped an MCP plugin and watched the LLM pick the wrong tool\n\n## Plugin Architecture\n\n```\n┌─────────────────────────────────────────────────────────┐\n│                    Claude Code Host                      │\n│                                                          │\n│  User Prompt ──► LLM reads tool descriptions             │\n│                  ──► selects tool by routing hints        │\n│                  ──► calls MCP tool                       │\n└──────────────────────────┬───────────────────────────────┘\n                           │ MCP Protocol (stdio)\n                           ▼\n┌─────────────────────────────────────────────────────────┐\n│                   MCP Plugin Server                      │\n│                                                          │\n│  ┌─ Validation ──┐  ┌─ Handlers ──┐  ┌─ Formatters ──┐ │\n│  │ Zod schemas   │  │ 27 tools    │  │ Markdown      │ │\n│  │ Defense-in-   │  │ by domain:  │  │ + suggested   │ │\n│  │ depth (not    │  │  search (5) │  │   _next hints │ │\n│  │ just hints)   │  │  obs    (4) │  │               │ │\n│  └───────┬───────┘  │  entity (2) │  │ \"Use X AFTER\" │ │\n│          │          │  skill  (5) │  │ \"Use Y FIRST\" │ │\n│          ▼          │  meta   (6) │  │ \"Use Z INSTEAD│ │\n│  ┌─ SSRF Guard ──┐  │  context(2) │  └───────────────┘ │\n│  │ Hostname list │  │  other  (3) │                     │\n│  │ Endpoint list │  └──────┬──────┘                     │\n│  │ HTTPS enforce │         │                            │\n│  │ Metadata block│         │                            │\n│  └───────────────┘         │                            │\n│                            ▼                            │\n│  ┌─ API Client ───────────────────────────────────────┐ │\n│  │ callRemoteAPI()  ── GET  + retry (2x backoff)      │ │\n│  │ postRemoteAPI()  ── POST (no retry, mutations)     │ │\n│  │ patchRemoteAPI() ── PATCH (pin, importance, date)  │ │\n│  │ Timeout: 30s general, 60s search                   │ │\n│  └────────────────────────┬───────────────────────────┘ │\n│                           │                             │\n│  ┌─ Sync Poller ─────────────────────────────────────┐  │\n│  │ In-process (not daemon) │ Adaptive: 1-10s          │  │\n│  │ Circuit breaker (30s)   │ Pending queue + retry    │  │\n│  └─────────────────────────────────────────────────────┘ │\n└──────────────────────────┬──────────────────────────────┘\n                           │ HTTPS (X-API-Key)\n                           ▼\n                    ┌──────────────┐\n                    │ Remote API   │\n                    │ Server       │\n                    └──────────────┘\n```\n\n## Chapters\n\n| #   | Chapter                                                | Key Insight                                                                        |\n| --- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- |\n| 1   | [Tool Design](chapters/01-tool-design.md)              | Fewer tools with modes beat many specialized tools. Group by domain.               |\n| 2   | [Descriptions as Routing](chapters/02-descriptions.md) | Descriptions are routing hints, not documentation. \"Use X FIRST\" changes behavior. |\n| 3   | [Workflow Hints](chapters/03-workflow-hints.md)        | Append \"suggested next\" actions to results. Rule-based, no LLM needed.             |\n| 4   | [Input Validation](chapters/04-validation.md)          | `inputSchema` is an LLM hint only. Zod enforces at runtime.                        |\n| 5   | [SSRF Protection](chapters/05-ssrf-protection.md)      | Hostname allowlist + endpoint allowlist + HTTPS enforcement.                       |\n| 6   | [Sync Architecture](chapters/06-sync-architecture.md)  | In-process polling with adaptive intervals and circuit breaker.                    |\n| 7   | [API Client Patterns](chapters/07-api-client.md)       | Retry transient errors only. Longer timeouts for search. Format as markdown.       |\n| 8   | [Publishing](chapters/08-publishing.md)                | 3 version files. Plugin directory structure. Marketplace schema.                   |\n\n## Anti-Patterns\n\n| Anti-Pattern                       | Why It Fails                                       | Better Alternative                                           |\n| ---------------------------------- | -------------------------------------------------- | ------------------------------------------------------------ |\n| One tool per API endpoint          | LLM can't choose between 30 tools                  | Unified tool with `mode` parameter (Ch. 1)                   |\n| Description says what, not when    | LLM picks wrong tool for the task                  | \"Use X FIRST\", \"Use Y instead\" routing (Ch. 2)               |\n| No workflow hints                  | User must manually figure out next step            | Append `suggested_next` to results (Ch. 3)                   |\n| Trust `inputSchema` for validation | LLM sends malformed data; no runtime check         | Zod schemas as defense-in-depth (Ch. 4)                      |\n| No URL validation on config        | SSRF via config file pointing to metadata endpoint | Hostname allowlist + metadata IP block (Ch. 5)               |\n| Detached sync daemon               | Orphaned processes, crash recovery complexity      | In-process poller with circuit breaker (Ch. 6)               |\n| Retry all HTTP errors              | 400/401/404 retried forever                        | Only retry network/TLS errors, not HTTP 4xx (Ch. 7)          |\n| Single version file                | Plugin metadata out of sync with package           | 3 files: package.json, plugin.json, marketplace.json (Ch. 8) |\n\n## Technology Stack (Reference Implementation)\n\n```\nRuntime:      Bun (TypeScript)\nProtocol:     MCP SDK (@modelcontextprotocol/sdk)\nValidation:   Zod 4\nTransport:    stdio (JSON-RPC over stdin/stdout)\nAPI Auth:     X-API-Key header\nSync:         In-process polling (SQLite -\u003e remote API)\n```\n\n## Decision Matrix: Tool Count\n\n| Tool Count  | Pros                          | Cons                                                | When to Use                     |\n| ----------- | ----------------------------- | --------------------------------------------------- | ------------------------------- |\n| 1-5 tools   | Easy selection, low confusion | Limited capability                                  | Simple plugins (status, config) |\n| 6-15 tools  | Good balance                  | Needs routing hints                                 | Most production plugins         |\n| 16-30 tools | Full feature coverage         | Must group by domain, needs descriptions as routing | Complex plugins (memory, data)  |\n| 30+ tools   | Maximum flexibility           | LLM confusion, slow tool listing                    | Split into multiple plugins     |\n\n## Contributing\n\nFound a pattern that worked (or broke) in your MCP plugin? PRs welcome.\n\n## License\n\nMIT\n\n---\n\n_Last verified: 2026-03-22 | Version: 1.0_\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpitimon%2Fmcp-plugin-patterns","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpitimon%2Fmcp-plugin-patterns","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpitimon%2Fmcp-plugin-patterns/lists"}