feat: initial remote MCP source management UI#2608
Conversation
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 86a161d The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
🚀 Preview Environment (PR #2608)Preview URL: https://pr-2608.dev.getgram.ai
Gram Preview Bot |
fa9540c to
0d7e4e2
Compare
0d7e4e2 to
3a3e584
Compare
simplesagar
left a comment
There was a problem hiding this comment.
Nice work! Some feedback on the UX primarily on the state post adding a server by url, screenshot below
- I think we need a display name that's easier for customers to reference rather than just the long id eg: https://pr-2608.dev.getgram.ai/speakeasy-team/projects/default/sources/remotemcp/019dfd85-a73e-76f0-8cc9-41c579064bcf
- If we can we should materialise the remote tools into a list on this page as a new tab. See tabs for other source types as inspiration
- on the settings tab it let me update to a url that was invalid whereas in the initial setup step it does not
- in the initial setup step there's a nice warning below the entry box indicating validity of the URL. We should highlight/color that text to make it stand out as a warning.
Other ideas
- you could probably lift and shift the source overview tab (table and activity overview) we have for other source types
- We show a filtered view of deployments for other sources. We could probably do something similar here.
|
Thanks, @simplesagar!
I agree, I do think we need some form of (display) name. I'm not sure about using it as slug as that'll have URL path character requirements then. Either which way (whether just name or name + slug) can get that added into the backend real quick. Using the URL everywhere leaves much to be desired.
Is there a particular reason? A few of us are thinking we should only do this when working with policies and we should only do this during management of those. Getting the list requires authentication and we don't want to store this sort of data if avoidable (one of the problems with the existing implementation).
Will get that sorted, good catch.
Will also get this sorted, thank you.
Will reach out to you about this.
We need to handle this differently for Registry MCP / Remote MCP. They're intentionally not using the toolset deployment model. Let me have a think about this, we might be able to augment the deployments page with audit logging for these sources or something so it shows similar information. |
https://linear.app/speakeasy/issue/AGE-1719/initial-remote-mcp-source-management-ui Both filters are optional and mutually exclusive — the schema's mcp_servers_backend_exclusivity_check guarantees exactly one backend per row, so a query that pinned both would always be empty. Index migration tracked separately in AGE-2121.
https://linear.app/speakeasy/issue/AGE-1719/initial-remote-mcp-source-management-ui Adds a flag-gated (gram-remote-mcp) Add Source entry, create form, and detail page with Overview / MCP Servers / Settings tabs for the new Remote MCP source type. The create and delete flows orchestrate sequential remoteMcp + mcpServers RPC calls with rollback so the linked records stay in sync. Also renames the existing Third party server entry to Registry server to disambiguate.
Strip the http(s):// prefix when rendering a remote MCP server URL in the page header, breadcrumbs, source card, and source table row. The Settings input and Overview URL field keep the canonical value.
Orphaned by the source index empty-state refactor that switched the condition to allSources.length === 0.
6d8edf0 to
6a39405
Compare
This comment has been minimized.
This comment has been minimized.
…utton https://linear.app/speakeasy/issue/AGE-1719/initial-remote-mcp-source-management-ui Adds a new remoteMcp.verifyURL management API method that probes a candidate remote MCP server URL with an MCP initialize request and reports a verification outcome. Wires it into the Add Source create form and the source Settings tab so users get a reachability signal before they save. Treats reachable-but-401/403 responses as verified — auth verification is intentionally out of scope.
https://linear.app/speakeasy/issue/AGE-1719/initial-remote-mcp-source-management-ui Adds an optional human-readable name and a URL-derived slug to remote MCP server records. createServer and updateServer accept an optional name; slug is computed server-side on every write from the URL host+path and the last four characters of the server ID. getServer takes mutually-exclusive id and slug parameters; either resolves the row. Dashboard adds a name input on both the create form and the Settings tab, prefers the name (falling back to the protocol-stripped URL) in breadcrumbs, hero, source cards, and source table rows, and routes /sources/remotemcp by slug (falling back to id). Other changes: - verifyURL returns "MCP response not found" for 404 responses. - The Settings tab URL update navigates to the new slug to avoid bouncing the user back to the Sources index after save.
… MCP source overview https://linear.app/speakeasy/issue/AGE-1719/initial-remote-mcp-source-management-ui Mirrors the OpenAPI source overview's two-column layout: a Source Information table on the left (Name, URL, Transport Type, Source ID, Created, Updated, Linked MCP servers) and a Source Activity panel on the right. Activity renders the structural panel with the same "no data yet" empty state as OpenAPI — telemetry wiring is deferred since the proxy's remote_mcp_server.id metrics aren't reachable from the dashboard's ClickHouse-backed observability RPC yet. Lifts OverviewRow into a shared SourceInfoTable / SourceInfoRow component used by both overview tabs.
https://linear.app/speakeasy/issue/AGE-1719/initial-remote-mcp-source-management-ui CreateServer now requires an explicit id (the impl pre-generates one so it can compute the slug from the id's last four chars), but the test fixtures still passed uuid.Nil by omission, causing primary-key collisions whenever a test seeded more than one remote_mcp_server in the same DB. Adds a remotemcptest.SeedServer helper that generates a UUIDv7 when none is supplied, and migrates the five call sites across mcpservers, mcpendpoints, and xmcp to use it.
26c8b29 to
86a161d
Compare
|
Thanks for addressing the feedback Brian!
Primary reason is that it when a user adds in a source its an unusual experience not to be able to see what tools you are bringing into the system. There is also a matter of inconsistency between http, function, external and remote sources. I'm not opposed to asking the user to auth just in time during the process of adding a remote mcp. I think that ensures they are the right person to add in the source too. |
|
We chatted about the tools listing vs security configuration awkwardness out of band. We'll create a way to handle that (either via wizard and/or some handling on these pages) such that you can go through the security configuration and persist it, rather than it potentially being temporary just to get the tools list. We just want to ensure there's one way to do that rather than introducing multiple UI journeys to manage. |
Summary
Implements AGE-1719 — the day-0 dashboard UI for the new Remote MCP source type, gated behind the
gram-remote-mcpPostHog flag. Lets users register an external MCP server by URL and manage it with full CRUD without touching the catalog or deployment surface.What's in this PR
Backend:
mcpServers.listwith optionalremote_mcp_server_idandtoolset_idfilters so the detail page's MCP Servers tab can scope to a single backend. The two filters are mutually exclusive (themcp_serversschema'smcp_servers_backend_exclusivity_checkalready guarantees exactly one backend per row, so a query that pinned both would always be empty).remoteMcp.verifyURLRPC that probes a candidate URL with an MCPinitializerequest and reports a verification outcome.nameand a URL-derivedslugtoremote_mcp_servers;slugis computed server-side on every write from the URL host+path and the last four characters of the id, andgetServeraccepts mutually-exclusiveidorslugparameters.Dashboard — gated behind
gram-remote-mcp:Network, violet swatch). Existing Third party server entry renamed to Registry server to disambiguate from the new flow./sources/add-remote-mcpwith a URL field, an optional Display Name field, a Verify MCP Connectivity button (callsremoteMcp.verifyURL), and absolute-http(s) + non-empty-host validation that mirrors the server'surl.Parsechecks./sources/remotemcp/:slug(with id fallback) modeled onExternalMCPDetails.tsx. Three hash-routed tabs: Overview (Source Information table + Source Activity panel mirroring the OpenAPI source layout — Activity renders a structural empty state pending telemetry wiring tracked in AGE-2213 and AGE-2214), MCP Servers (cards formcp_serversrows whose backend points at this remote MCP server), Settings (Display Name + URL edit with Verify MCP Connectivity button, plus Danger Zone delete).remotemcpvariant onNamedAssetwith a Remote MCP badge; the display label prefers the server name and falls back to the URL with the protocol stripped. Card click routes into the new detail page.RemoveSourceDialogContentandViewAssetDialogContentnarrow theirassetprop to excluderemotemcpsince those flows only handle deployment-bound sources.Orchestration
The backend doesn't expose a single transactional RPC for the compound create/delete operations the issue describes, so the create and delete flows live in
pages/sources/remote-mcp/hooks.ts:useCreateRemoteMcpSource— callsremoteMcp.createServer, thenmcpServers.createwith the new id andvisibility: "disabled". On partial failure (mcp_server create fails) it attempts to roll back theremoteMcprow; if rollback also fails, surfaces the orphaned id so the user can clean up manually.useDeleteRemoteMcpSource— soft-deletes each linkedmcp_serversrow first (which cascades tomcp_endpointsserver-side), then deletes theremoteMcprow. The confirmation dialog enumerates linked servers and their endpoints up front so the user understands the full fan-out before confirming.Both hooks invalidate
remoteMcp.listServers,mcpServers.list, andmcpEndpoints.listwithrefetchType: "all"on success so the index, detail, and dialog caches stay coherent across navigation.Follow-ups
telemetry_logsschema change (AGE-2213) and the dashboard wiring on top (AGE-2214).