feat(builder+studio): add any MCP you can find (paste / GitHub / surface)#8
Merged
Conversation
b131769 to
525180f
Compare
There was a problem hiding this comment.
Pull request overview
Adds end-to-end support for “add any MCP” by introducing a builder-level MCP config normalizer that lifts secrets into credential requirements, expanding MCP discovery to GitHub repo search/README extraction, and surfacing a Studio “Paste config” flow with preview + add routes—all while keeping the secret-scan gate fail-closed.
Changes:
- Exported a value-level secret heuristic (
isLikelySecretValue) from core for reuse in config normalization. - Added
normalizeMcpConfig()to accept commonmcpServersblobs / lists / single objects and lift secrets to${credentialRef:...}+CredentialRequirement. - Added GitHub MCP hub source + Studio paste-preview/add plumbing (API + session + UI), plus tests and accompanying spec/plan docs.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/core/test/secret-scan.test.ts | Adds unit coverage for isLikelySecretValue. |
| packages/core/src/secret-scan.ts | Exports isLikelySecretValue for value-only secret detection reuse. |
| packages/builder/test/mcp-normalize.test.ts | New tests validating normalization + secret lifting + gate cleanliness. |
| packages/builder/test/hubs.test.ts | Updates default MCP sources expectation to include the new GitHub source. |
| packages/builder/test/github-mcp.test.ts | New tests for GitHub repo mapping + README extraction via normalizer. |
| packages/builder/src/mcp/normalize.ts | Implements normalizeMcpConfig() and secret lifting into credential requirements. |
| packages/builder/src/index.ts | Re-exports the new normalizer from the builder package entrypoint. |
| packages/builder/src/hubs/index.ts | Exports the new GitHub MCP hub source module. |
| packages/builder/src/hubs/github-mcp.ts | Implements GitHub MCP discovery source + README config extraction helper. |
| packages/builder/src/hubs/defaults.ts | Registers GitHub MCP source in defaultMcpSources. |
| docs/superpowers/specs/2026-06-10-add-any-mcp-design.md | Design spec documenting the “add any MCP” approach and data flow. |
| docs/superpowers/plans/2026-06-10-add-any-mcp.md | Implementation plan outlining tasks/tests/verification steps. |
| apps/studio/web/src/types.ts | Adds McpNormalizePreview client-side type for paste preview responses. |
| apps/studio/web/src/Palette.tsx | Updates MCP section labeling and funnels MCP additions through the inspector flow. |
| apps/studio/web/src/Inspector.tsx | Adds “Paste an MCP config” UI with preview/add actions and credential display. |
| apps/studio/web/src/api.ts | Adds client methods for /api/mcp/paste preview and /api/mcp/paste/add. |
| apps/studio/test/paste-mcp.test.ts | New tests for preview/add behavior and invalid JSON handling in StudioSession. |
| apps/studio/src/server/session.ts | Routes MCP import through the normalizer; adds preview/add methods for pasted configs. |
| apps/studio/src/server/api.ts | Adds server routes for paste preview and paste add. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+456
to
466
| /** Bulk-import MCP servers from any shape (a canonical list OR a pasted config blob). */ | ||
| importMcpServers(servers: Array<Record<string, unknown>>): number { | ||
| let n = 0; | ||
| for (const raw of servers) { | ||
| this.brain.addMcpServer({ tools: { include: 'all' }, ...raw }); | ||
| this.ensureMcpCredential((raw.auth ?? {}) as { type?: string; credentialRef?: string }); | ||
| n++; | ||
| const r = normalizeMcpConfig(raw); | ||
| for (const s of r.servers) { | ||
| this.brain.addMcpServer(s); | ||
| n++; | ||
| } | ||
| for (const c of r.credentials) this.brain.addCredential(c); | ||
| } |
Comment on lines
+37
to
+39
| function credFor(ref: string, label: string, id: string): CredentialRequirement { | ||
| return { ref, label, type: 'apiKey', consumedBy: [`mcp:${id}`], required: true }; | ||
| } |
Comment on lines
+61
to
+84
| const headers = (raw.headers ?? {}) as Record<string, unknown>; | ||
| const secretHeaders = Object.entries(headers).filter( | ||
| ([k, v]) => typeof v === 'string' && (isSecretName(k) || isLikelySecretValue(v)), | ||
| ); | ||
| if (secretHeaders.length > 0) { | ||
| const hName = secretHeaders[0]![0]; | ||
| if (/^authorization$/i.test(hName)) { | ||
| const ref = `${id}_token`; | ||
| auth = { type: 'bearer', credentialRef: ref }; | ||
| creds.push(credFor(ref, hName, id)); | ||
| } else { | ||
| const ref = `${id}_${refSuffix(hName)}`; | ||
| auth = { type: 'header', headerName: hName, credentialRef: ref }; | ||
| creds.push(credFor(ref, hName, id)); | ||
| } | ||
| if (secretHeaders.length > 1) { | ||
| out.lossiness.push( | ||
| `${id}: only the first auth header is kept; dropped ${secretHeaders | ||
| .slice(1) | ||
| .map(([k]) => k) | ||
| .join(', ')}`, | ||
| ); | ||
| } | ||
| } |
Comment on lines
+141
to
+148
| // Already-canonical (or near) single server: has id + transport. | ||
| if (typeof obj.id === 'string' && typeof obj.transport === 'string') { | ||
| const canonical = McpServerSchema.safeParse({ tools: { include: 'all' }, ...obj }); | ||
| if (canonical.success) { | ||
| out.servers.push(canonical.data); | ||
| return out; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Today the builder is not limited to the fixed catalog (custom form, JSON import, and hub search all exist), but "add any MCP you find" had three gaps. This closes all three — no new command, no new runtime.
Part 1 —
normalizeMcpConfig()(the keystone)packages/builder/src/mcp/normalize.ts— a pure function that accepts the universalmcpServersconfig blob every README/Claude-Desktop/.mcp.json/Cursor config ships (plus single objects,{servers:[]}lists, and already-canonical servers), normalizes to canonicalMcpServer[], and lifts every secretenv/header to a${credentialRef:…}placeholder + aCredentialRequirement. Secret detection reuses the core gate via a newisLikelySecretValueexport. A pasted config with a rawsk-…now packs cleanly — the secret becomes a requirement, never travels.Part 2 — GitHub MCP discovery source
packages/builder/src/hubs/github-mcp.ts— repo search as an MCPCatalogSource(mirrors the skills source), registered indefaultMcpSources.extractMcpFromReadme()pulls a realmcpServersblock out of a repo README via the Part 1 normalizer.Part 3 — Studio "Paste config" mode
The Custom / import… MCP panel gains a paste box →
POST /api/mcp/pastepreviews the normalized server(s) and the lifted credential fields ("needsBRAVE_API_KEY") before adding via/api/mcp/paste/add.importMcpServersnow routes through the normalizer too.Verified
mcp-normalize(7, incl. gate-passes proof),github-mcp(3), studiopaste-mcp(3). Full suite green (spec/core/builder/adapters/studio/cli), typecheck + lint clean.sk-…key → preview showed✓ brave-search (stdio)+needs brave-search_brave_api_key→ Add wired abrave-searchMCP node to the agent; footer reads0 secrets · 1 credential(s) required.Spec:
docs/superpowers/specs/2026-06-10-add-any-mcp-design.md· Plan:docs/superpowers/plans/2026-06-10-add-any-mcp.md🤖 Generated with Claude Code