Skip to content

Fix MCP credential-handle grammar violation (mcp:<slug> -> mcp.<slug>) - #98

Merged
TheGreatAxios merged 2 commits into
mainfrom
cl-6324-mcp-handle-grammar
Aug 20, 2026
Merged

Fix MCP credential-handle grammar violation (mcp:<slug> -> mcp.<slug>)#98
TheGreatAxios merged 2 commits into
mainfrom
cl-6324-mcp-handle-grammar

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Contributor

What this does

apps/hub/src/mcp-credential-bindings.ts minted handle: "mcp:<slug>" for every tenant-connected MCP server, but @intx/types' ToolCredentialHandle grammar is /^[a-z0-9][a-z0-9._-]*$/ — no colon. Nothing parsed the handle before, so it slid through silently; PR #97's render path is the first strict consumer and fails closed on it.

Changes the handle shape to mcp.<slug> (dots are legal in the grammar) at every mint and resolve site:

  • packages/mcp-tools/src/tool.tsmcpCredentialHandle(slug), the single function both the credential-resolve call (env.credentials.resolve(...)) and the binding minter build on.
  • apps/hub/src/mcp-credential-bindings.ts — the CredentialBinding.handle minted per tenant connection.
  • Test fixtures and doc comments that stood in for either, across packages/folded-runs and apps/sidecar.

CredentialBinding.provider and the stored provider table row name (mcp:<slug>, @workbench/connections' mcp-server-store.ts/credential-expiry-sweep.ts/index.ts) are untouched — provider is a plain unconstrained string in the schema, a separate namespace from the handle grammar.

Red/green

packages/mcp-tools/src/tool.test.ts adds:

  • mcpCredentialHandle mints a handle that parses under ToolCredentialHandle.
  • A CredentialBinding built on that handle parses under @intx/types' CredentialBinding schema.
  • A mint → bind → resolve round trip: mcp_call resolves credentials with the exact handle mcpCredentialHandle mints (spied via a fake CredentialCapability).

Verified red on the pre-fix mcp:<slug> shape, green after.

Stored-data note

createMcpCredentialBindingsFor is already wired into apps/hub's folded-run launch path on main. Any dev tenant that pinned @corbits/mcp-tools and launched a folded run before this fix may have a workflow-definition tree with the old mcp:<slug> handle baked into its credentialBindings — nothing parsed that field before, so it never surfaced. A re-seed/re-deploy after this change produces a conforming handle; per repo policy dev data gets wiped/re-seeded rather than migrated in place.

Testing

Package-scoped, per shell rules (one heavy process at a time):

  • bun test packages/mcp-tools — 25 pass
  • bun test apps/hub — 130 pass
  • bun test apps/sidecar — 146 pass
  • bun test packages/folded-runs — 42 pass
  • tsc --noEmit clean in all four packages
  • prettier --check / eslint clean on every touched file

…alHandle

PR #97's render path parses a folded run's credential bindings through
@intx/types' CredentialBinding schema and fails closed: mcp-credential-
bindings.ts minted handle: "mcp:<slug>", but ToolCredentialHandle is
/^[a-z0-9][a-z0-9._-]*$/ -- no colon. Every MCP-pinned launch would hit
this at render time; any future strict CredentialBinding consumer
breaks the same way.

Changes the handle shape to mcp.<slug> (dots are legal) at every mint
and resolve site: mcpCredentialHandle() in @corbits/mcp-tools (the
resolve-key builder), apps/hub's mcp-credential-bindings.ts (the
binding minter, already wired into launchFoldedRun/wakeFoldedRun on
main), and the test fixtures/comments across folded-runs and sidecar
that stood in for either. The mcp:<slug> PROVIDER row naming
(@workbench/connections' provider table, credential-expiry-sweep.ts,
mcp-server-store.ts) is untouched -- that's a separate, unconstrained
namespace CredentialBinding.provider (plain string) resolves against,
not the handle grammar.

Stored-data note: createMcpCredentialBindingsFor is already wired into
apps/hub's folded-run launch path on main, so any dev deployment that
pinned @corbits/mcp-tools and actually launched before this fix may
have a workflow-definition tree with the old mcp:<slug> handle baked
into its credentialBindings. Nothing on main parsed that field before
now, so it never failed loudly; a re-seed/re-deploy of such tenants
after this change produces a conforming handle. Per repo policy, dev
data gets wiped/re-seeded rather than migrated in place.
@TheGreatAxios
TheGreatAxios merged commit 88285ef into main Aug 20, 2026
0 of 2 checks passed
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.

1 participant