Skip to content

feat(builder+studio): add any MCP you can find (paste / GitHub / surface)#8

Merged
maxlibin merged 10 commits into
mainfrom
feat/mcp-add-any
Jun 10, 2026
Merged

feat(builder+studio): add any MCP you can find (paste / GitHub / surface)#8
maxlibin merged 10 commits into
mainfrom
feat/mcp-add-any

Conversation

@maxlibin

Copy link
Copy Markdown
Collaborator

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 universal mcpServers config blob every README/Claude-Desktop/.mcp.json/Cursor config ships (plus single objects, {servers:[]} lists, and already-canonical servers), normalizes to canonical McpServer[], and lifts every secret env/header to a ${credentialRef:…} placeholder + a CredentialRequirement. Secret detection reuses the core gate via a new isLikelySecretValue export. A pasted config with a raw sk-… 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 MCP CatalogSource (mirrors the skills source), registered in defaultMcpSources. extractMcpFromReadme() pulls a real mcpServers block 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/paste previews the normalized server(s) and the lifted credential fields ("needs BRAVE_API_KEY") before adding via /api/mcp/paste/add. importMcpServers now routes through the normalizer too.

Verified

  • New tests: mcp-normalize (7, incl. gate-passes proof), github-mcp (3), studio paste-mcp (3). Full suite green (spec/core/builder/adapters/studio/cli), typecheck + lint clean.
  • Browser dogfood: pasted a real Claude-Desktop blob with a fake sk-… key → preview showed ✓ brave-search (stdio) + needs brave-search_brave_api_key → Add wired a brave-search MCP node to the agent; footer reads 0 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

@maxlibin maxlibin marked this pull request as draft June 10, 2026 10:07
@maxlibin maxlibin marked this pull request as ready for review June 10, 2026 10:38
@maxlibin maxlibin requested a review from Copilot June 10, 2026 11:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 common mcpServers blobs / 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;
}
}
@maxlibin maxlibin merged commit 7d36ea2 into main Jun 10, 2026
3 checks passed
@maxlibin maxlibin deleted the feat/mcp-add-any branch June 10, 2026 15:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants