You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
MCP connection setup UI should validate, connect, discover, and test a user-provided endpoint #266
The current MCP-client setup is just agent settings fields like mcp_url and auth_mode. That is not enough for the user expectation: “give it a URL and it configures itself to operate an app.”
Even if backend OAuth works, users need a guided connection lifecycle that validates the endpoint, completes auth, discovers capabilities, chooses allowed tools, and verifies the connection.
Expected behavior
Instance settings should have a first-class MCP connection setup flow:
Enter MCP endpoint URL.
Validate URL and transport support.
Discover auth requirements and start browser sign-in if needed.
Run protocol/version negotiation.
Fetch tool/resource/prompt catalogs.
Let user enable allowed capabilities and write/destructive consent.
Run a safe test call.
Save connection state and show health/status.
Acceptance criteria
Settings page shows an MCP Connections section for agents with outbound MCP capability.
User can add, rename, reconnect, disconnect, and refresh an MCP endpoint connection.
Connection status distinguishes unconfigured, auth required, connected, expired, unsupported protocol, unreachable, and permission denied.
Note from #260/#265 (landed in 4f77f33): the server side this UI needs now exists, so the
remaining work here is mostly the panel itself.
Validate / connect: normalizeMcpEndpoint (lib/mcp-consent.ts) is the canonical
endpoint form — https-only, query and userinfo stripped, trailing slash and host case
collapsed. Use it so the URL the UI shows is the URL enforcement and logging actually key on.
The "connection test UI exposes actionable diagnostics" acceptance criterion on #265 is
deferred here rather than duplicated — #265 stays open only for that and for admin-side
endpoint/tool filtering.
Settings → MCP connections is now a lifecycle instead of two text inputs. Enter a URL and POST /v1/instances/:id/mcp/test validates and normalizes it, negotiates the protocol era, reads the server's own tools/list, and returns the catalog — you tick the tools this agent may call. Discovery is read-scoped and needs no consent, which is what makes per-tool consent (#262) usable at all: approving individual tools is impossible if you cannot first enumerate them.
The report answers the whole question. A test that reports success while consent would still refuse every call is a lie the owner only discovers later as "the agent does nothing". So the response carries all three gates a real mcp_call_tool passes — the declared tool allowlist, the connector-level write kill switch (#90), and the per-(endpoint, tool) grant — and every discovered tool says whether it is callable and which gate blocks it. "Connected, 14 tools found, this agent may call none of them" is now a sentence the panel can say.
No new storage. A connection IS its grants on a normalized endpoint — the URL is already the identity that consent, the trace and the connector all key on, so a second record with its own id and nickname would be a second name for one thing, drifting the first time either was edited. Multiple endpoints per instance fall out for free. Test results are deliberately not cached: a connection's health is a fact about now, and a stored "connected" is how a green badge outlives the server.
SSRF. An authenticated "make the Worker fetch this URL" button on user-supplied config is textbook SSRF, so the test path goes probeMcpEndpoint → the ordinary connector call → safeFetch with no fast path of its own: same https-only redirect-revalidated guard, a strict rate limit, and the same redacted agent_events row, so a test is as auditable as a real call rather than a side channel around the log. The endpoint is normalized with the query string dropped — an MCP endpoint's query routinely IS the token.
Routes are specced in store/openapi.yaml; docs:drift clean at 266/266.
Activity
serge-ivo commentedon Aug 6, 2026
Note from #260/#265 (landed in 4f77f33): the server side this UI needs now exists, so the
remaining work here is mostly the panel itself.
normalizeMcpEndpoint(lib/mcp-consent.ts) is the canonicalendpoint form — https-only, query and userinfo stripped, trailing slash and host case
collapsed. Use it so the URL the UI shows is the URL enforcement and logging actually key on.
mcp_list_toolsis read-scoped and needs no consent, so a setup panel can listthe server's tools before anything is granted — which is what makes per-tool consent (Outbound MCP consent should be per server and per remote tool, not one global write grant #262)
usable.
protocol era it negotiated,
unsupported_versionwith both version lists,header_mismatch,auth(withdiscoverAuthServer's report of which authorization server fronts it, per[connectors] dcr-oauth2 — discovery + dynamic client registration + PKCE for user-named endpoints #180/[connectors] Model unattended credential survivability — warn at wiring time, not via dead letters #181),
blocked,unparseable. Every attempt also writes a redactedagent_eventsrow(
source: "mcp"), so the panel can show the last few attempts without inventing storage.The "connection test UI exposes actionable diagnostics" acceptance criterion on #265 is
deferred here rather than duplicated — #265 stays open only for that and for admin-side
endpoint/tool filtering.
feat(mcp): connect an MCP server by testing it, not by guessing (#266, …
serge-ivo commentedon Aug 6, 2026
Shipped in
8de7c56.Settings → MCP connections is now a lifecycle instead of two text inputs. Enter a URL and
POST /v1/instances/:id/mcp/testvalidates and normalizes it, negotiates the protocol era, reads the server's owntools/list, and returns the catalog — you tick the tools this agent may call. Discovery is read-scoped and needs no consent, which is what makes per-tool consent (#262) usable at all: approving individual tools is impossible if you cannot first enumerate them.The report answers the whole question. A test that reports success while consent would still refuse every call is a lie the owner only discovers later as "the agent does nothing". So the response carries all three gates a real
mcp_call_toolpasses — the declared tool allowlist, the connector-level write kill switch (#90), and the per-(endpoint, tool) grant — and every discovered tool says whether it is callable and which gate blocks it. "Connected, 14 tools found, this agent may call none of them" is now a sentence the panel can say.No new storage. A connection IS its grants on a normalized endpoint — the URL is already the identity that consent, the trace and the connector all key on, so a second record with its own id and nickname would be a second name for one thing, drifting the first time either was edited. Multiple endpoints per instance fall out for free. Test results are deliberately not cached: a connection's health is a fact about now, and a stored "connected" is how a green badge outlives the server.
SSRF. An authenticated "make the Worker fetch this URL" button on user-supplied config is textbook SSRF, so the test path goes
probeMcpEndpoint→ the ordinary connector call →safeFetchwith no fast path of its own: same https-only redirect-revalidated guard, a strict rate limit, and the same redactedagent_eventsrow, so a test is as auditable as a real call rather than a side channel around the log. The endpoint is normalized with the query string dropped — an MCP endpoint's query routinely IS the token.Routes are specced in
store/openapi.yaml;docs:driftclean at 266/266.fix(mcp): an MCP token belongs to one server, not to the account (#286)
feat(mcp): a granted remote tool is a real tool, not a string the mod…
fix(ui): the accessibility + layout-shift pass, replayed onto current…
feat(mcp): say what else a server publishes, and whether the agent ca…