[PR #5955] feat: expose collections as MCP HTTP endpoints #5433

Open
opened 2026-03-17 02:52:45 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/hoppscotch/hoppscotch/pull/5955
Author: @jamesgeorge007
Created: 3/6/2026
Status: 🔄 Open

Base: nextHead: feat/share-collection-as-mcp


📝 Commits (8)

  • 6dc86b7 feat(backend,common): add Share as MCP — expose Hoppscotch collections as MCP HTTP endpoints
  • 93bdfb8 fix(backend): cast McpShare return object to resolve WorkspaceType enum mismatch
  • dca3b4c fix(backend): correct slugify test expectation in mcp-tool-generator spec
  • 8e5ba0a fix(backend): harden mcp-share — SSRF guard, body routing, bounded cache, migration
  • 7528a21 fix(backend): fix SSRF regex anchoring and strip internal schema fields from mcp tools/list
  • 152b8b4 fix(common): rewrite MCP share GQL calls to use proper urql Client API
  • f2918b3 fix(backend): add fetch timeout, use findUnique, guard cache eviction
  • d3b4f2c fix(backend): harden MCP controller — SSRF redirect bypass, response limits, notifications

📊 Changes

22 files changed (+1707 additions, -0 deletions)

View changed files

packages/hoppscotch-backend/prisma/migrations/20260306000000_add_mcp_share/migration.sql (+24 -0)
📝 packages/hoppscotch-backend/prisma/schema.prisma (+16 -0)
📝 packages/hoppscotch-backend/src/app.module.ts (+2 -0)
📝 packages/hoppscotch-backend/src/errors.ts (+36 -0)
packages/hoppscotch-backend/src/mcp-share/mcp-share.controller.ts (+349 -0)
packages/hoppscotch-backend/src/mcp-share/mcp-share.model.ts (+39 -0)
packages/hoppscotch-backend/src/mcp-share/mcp-share.module.ts (+14 -0)
packages/hoppscotch-backend/src/mcp-share/mcp-share.resolver.ts (+55 -0)
packages/hoppscotch-backend/src/mcp-share/mcp-share.service.ts (+194 -0)
packages/hoppscotch-backend/src/mcp-share/mcp-tool-generator.spec.ts (+250 -0)
packages/hoppscotch-backend/src/mcp-share/mcp-tool-generator.ts (+211 -0)
📝 packages/hoppscotch-backend/src/types/InfraConfig.ts (+2 -0)
📝 packages/hoppscotch-common/locales/en.json (+14 -0)
📝 packages/hoppscotch-common/src/components/collections/Collection.vue (+16 -0)
packages/hoppscotch-common/src/components/collections/McpShare.vue (+277 -0)
📝 packages/hoppscotch-common/src/components/collections/MyCollections.vue (+14 -0)
📝 packages/hoppscotch-common/src/components/collections/TeamCollections.vue (+14 -0)
📝 packages/hoppscotch-common/src/components/collections/index.vue (+47 -0)
packages/hoppscotch-common/src/composables/mcpShareVisibility.ts (+16 -0)
packages/hoppscotch-common/src/helpers/backend/mutations/McpShare.ts (+114 -0)

...and 2 more files

📄 Description

Adds "Share as MCP" — users can expose a root Hoppscotch collection as an MCP Streamable HTTP endpoint that AI clients (Claude Desktop, Cursor, Windsurf) connect to directly.

What's changed

Backend (hoppscotch-backend)

  • New McpShare Prisma model with migration — stores share tokens, workspace references, soft-delete support
  • New mcp-share/ NestJS module: service, GQL resolver, REST controller, tool generator
  • POST /v1/mcp/:shareToken endpoint handles JSON-RPC (initialize, tools/list, tools/call) — no JWT needed, the share token is the credential
  • Tool generator walks the collection tree and converts REST/GQL requests into MCP tool definitions with path params, query params, and JSON body inference
  • SSRF guard (isPrivateUrl) blocks loopback, RFC-1918, link-local, IPv4-mapped IPv6, ULA ranges, and localhost — with redirect: 'manual' to prevent redirect-based bypass
  • 30s fetch timeout and 1 MB response body cap on upstream calls
  • In-process TTL cache (60s, 1000 entries max) for tool definitions
  • Internal fields (bodyKeys, queryKeys, _meta) stripped from tools/list responses
  • Share creation restricted to OWNER/EDITOR roles — VIEWERs cannot expose collection credentials
  • 6 new error constants, MCP_SHARE_WILDCARD_DOMAIN infra config enum value
  • 11 unit tests for the tool generator covering REST, GQL, nested folders, name collisions, slugification, body/query key separation

Frontend (hoppscotch-common)

  • McpShare.vue modal — load existing share, generate new, copy URL, client config snippets (Claude/Cursor/Windsurf tabs), revoke
  • GQL mutation helpers (McpShare.ts) using TE.tryCatch + client.value!.mutation/query().toPromise() pattern
  • mcpShareVisibility.ts composable tied to ENABLE_MCP_SHARE setting
  • Context menu item on root collections guarded by isMcpShareVisible && isRootCollection
  • Event bubbling through Collection.vueMyCollections.vue / TeamCollections.vueindex.vue
  • 12 new i18n keys in mcp namespace
  • ENABLE_MCP_SHARE: false default in settings store + validation schema

Notes to reviewers

The UI entry point is hidden by default (ENABLE_MCP_SHARE: false in the client-side settings store). The backend endpoints and GQL resolver are always active once deployed — the setting only controls menu item visibility.

The MCP endpoint is unauthenticated by design — the 20-char base64url share token (≈120 bits entropy) acts as the credential, rate-limited via ThrottlerBehindProxyGuard.

The tool generator only infers flat (one-level) JSON body schemas — nested objects become { type: 'string' }. This is intentional for v1 and matches how MCP tool parameters typically work.

DNS rebinding is a known residual risk (hostname regex doesn't resolve DNS). Mitigated by redirect: 'manual' — can be hardened with dns.lookup() pre-check before wider rollout.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/hoppscotch/hoppscotch/pull/5955 **Author:** [@jamesgeorge007](https://github.com/jamesgeorge007) **Created:** 3/6/2026 **Status:** 🔄 Open **Base:** `next` ← **Head:** `feat/share-collection-as-mcp` --- ### 📝 Commits (8) - [`6dc86b7`](https://github.com/hoppscotch/hoppscotch/commit/6dc86b77cc23d395334da68b75852e321677bcaf) feat(backend,common): add Share as MCP — expose Hoppscotch collections as MCP HTTP endpoints - [`93bdfb8`](https://github.com/hoppscotch/hoppscotch/commit/93bdfb86512210cc5e212b7bc809eff6746df119) fix(backend): cast McpShare return object to resolve WorkspaceType enum mismatch - [`dca3b4c`](https://github.com/hoppscotch/hoppscotch/commit/dca3b4c8ea5a3514177de2af2a8886703249b441) fix(backend): correct slugify test expectation in mcp-tool-generator spec - [`8e5ba0a`](https://github.com/hoppscotch/hoppscotch/commit/8e5ba0a29b88f696c1d8c2b0d7c043d095af31d8) fix(backend): harden mcp-share — SSRF guard, body routing, bounded cache, migration - [`7528a21`](https://github.com/hoppscotch/hoppscotch/commit/7528a214fa517510a7eaea9a2a7fee5fbd076679) fix(backend): fix SSRF regex anchoring and strip internal schema fields from mcp tools/list - [`152b8b4`](https://github.com/hoppscotch/hoppscotch/commit/152b8b45be5b978e6c6ef53d9f9e9d32a8ef58bd) fix(common): rewrite MCP share GQL calls to use proper urql Client API - [`f2918b3`](https://github.com/hoppscotch/hoppscotch/commit/f2918b3a3b9d06f97798be90a2720c52fe041f5b) fix(backend): add fetch timeout, use findUnique, guard cache eviction - [`d3b4f2c`](https://github.com/hoppscotch/hoppscotch/commit/d3b4f2c97c154e5202d927b3730950416fbcc1dc) fix(backend): harden MCP controller — SSRF redirect bypass, response limits, notifications ### 📊 Changes **22 files changed** (+1707 additions, -0 deletions) <details> <summary>View changed files</summary> ➕ `packages/hoppscotch-backend/prisma/migrations/20260306000000_add_mcp_share/migration.sql` (+24 -0) 📝 `packages/hoppscotch-backend/prisma/schema.prisma` (+16 -0) 📝 `packages/hoppscotch-backend/src/app.module.ts` (+2 -0) 📝 `packages/hoppscotch-backend/src/errors.ts` (+36 -0) ➕ `packages/hoppscotch-backend/src/mcp-share/mcp-share.controller.ts` (+349 -0) ➕ `packages/hoppscotch-backend/src/mcp-share/mcp-share.model.ts` (+39 -0) ➕ `packages/hoppscotch-backend/src/mcp-share/mcp-share.module.ts` (+14 -0) ➕ `packages/hoppscotch-backend/src/mcp-share/mcp-share.resolver.ts` (+55 -0) ➕ `packages/hoppscotch-backend/src/mcp-share/mcp-share.service.ts` (+194 -0) ➕ `packages/hoppscotch-backend/src/mcp-share/mcp-tool-generator.spec.ts` (+250 -0) ➕ `packages/hoppscotch-backend/src/mcp-share/mcp-tool-generator.ts` (+211 -0) 📝 `packages/hoppscotch-backend/src/types/InfraConfig.ts` (+2 -0) 📝 `packages/hoppscotch-common/locales/en.json` (+14 -0) 📝 `packages/hoppscotch-common/src/components/collections/Collection.vue` (+16 -0) ➕ `packages/hoppscotch-common/src/components/collections/McpShare.vue` (+277 -0) 📝 `packages/hoppscotch-common/src/components/collections/MyCollections.vue` (+14 -0) 📝 `packages/hoppscotch-common/src/components/collections/TeamCollections.vue` (+14 -0) 📝 `packages/hoppscotch-common/src/components/collections/index.vue` (+47 -0) ➕ `packages/hoppscotch-common/src/composables/mcpShareVisibility.ts` (+16 -0) ➕ `packages/hoppscotch-common/src/helpers/backend/mutations/McpShare.ts` (+114 -0) _...and 2 more files_ </details> ### 📄 Description Adds "Share as MCP" — users can expose a root Hoppscotch collection as an MCP Streamable HTTP endpoint that AI clients (Claude Desktop, Cursor, Windsurf) connect to directly. ### What's changed **Backend (`hoppscotch-backend`)** - New `McpShare` Prisma model with migration — stores share tokens, workspace references, soft-delete support - New `mcp-share/` NestJS module: service, GQL resolver, REST controller, tool generator - `POST /v1/mcp/:shareToken` endpoint handles JSON-RPC (`initialize`, `tools/list`, `tools/call`) — no JWT needed, the share token is the credential - Tool generator walks the collection tree and converts REST/GQL requests into MCP tool definitions with path params, query params, and JSON body inference - SSRF guard (`isPrivateUrl`) blocks loopback, RFC-1918, link-local, IPv4-mapped IPv6, ULA ranges, and localhost — with `redirect: 'manual'` to prevent redirect-based bypass - 30s fetch timeout and 1 MB response body cap on upstream calls - In-process TTL cache (60s, 1000 entries max) for tool definitions - Internal fields (`bodyKeys`, `queryKeys`, `_meta`) stripped from `tools/list` responses - Share creation restricted to OWNER/EDITOR roles — VIEWERs cannot expose collection credentials - 6 new error constants, `MCP_SHARE_WILDCARD_DOMAIN` infra config enum value - 11 unit tests for the tool generator covering REST, GQL, nested folders, name collisions, slugification, body/query key separation **Frontend (`hoppscotch-common`)** - `McpShare.vue` modal — load existing share, generate new, copy URL, client config snippets (Claude/Cursor/Windsurf tabs), revoke - GQL mutation helpers (`McpShare.ts`) using `TE.tryCatch` + `client.value!.mutation/query().toPromise()` pattern - `mcpShareVisibility.ts` composable tied to `ENABLE_MCP_SHARE` setting - Context menu item on root collections guarded by `isMcpShareVisible && isRootCollection` - Event bubbling through `Collection.vue` → `MyCollections.vue` / `TeamCollections.vue` → `index.vue` - 12 new i18n keys in `mcp` namespace - `ENABLE_MCP_SHARE: false` default in settings store + validation schema ### Notes to reviewers The UI entry point is hidden by default (`ENABLE_MCP_SHARE: false` in the client-side settings store). The backend endpoints and GQL resolver are always active once deployed — the setting only controls menu item visibility. The MCP endpoint is unauthenticated by design — the 20-char base64url share token (≈120 bits entropy) acts as the credential, rate-limited via `ThrottlerBehindProxyGuard`. The tool generator only infers flat (one-level) JSON body schemas — nested objects become `{ type: 'string' }`. This is intentional for v1 and matches how MCP tool parameters typically work. DNS rebinding is a known residual risk (hostname regex doesn't resolve DNS). Mitigated by `redirect: 'manual'` — can be hardened with `dns.lookup()` pre-check before wider rollout. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/hoppscotch#5433
No description provided.