From e826ad07f65b5e8ae2a249b7981fc1caec85ad5d Mon Sep 17 00:00:00 2001
From: Kayzel
Date: Sun, 8 Nov 2026 14:43:03 +0100
Subject: [PATCH 1/2] feat(web): copy the MCP URL of any route
---
.../EnvironmentRoutesList.browser.test.tsx | 9 +++++-
.../settings/EnvironmentRoutesList.tsx | 31 ++++++++++++++++++-
.../settings/SavedEnvironmentRows.tsx | 5 ++-
docs/user/outside-agents.md | 4 ++-
.../src/connection/presentation.ts | 16 ++++++++--
5 files changed, 58 insertions(+), 7 deletions(-)
diff --git a/apps/web/src/components/settings/EnvironmentRoutesList.browser.test.tsx b/apps/web/src/components/settings/EnvironmentRoutesList.browser.test.tsx
index 78a96ba4ebab4..6f3ad0a0b4a0f 100644
--- a/apps/web/src/components/settings/EnvironmentRoutesList.browser.test.tsx
+++ b/apps/web/src/components/settings/EnvironmentRoutesList.browser.test.tsx
@@ -65,7 +65,14 @@ function routesList(routes: ReadonlyArray) {
connection: { phase: "available", error: null, traceId: null },
serverConfig: null,
};
- return undefined} />;
+ return (
+ undefined}
+ onCopyMcpUrl={() => undefined}
+ />
+ );
}
it("shows a route added after a reorder was saved, once another route is removed", async () => {
diff --git a/apps/web/src/components/settings/EnvironmentRoutesList.tsx b/apps/web/src/components/settings/EnvironmentRoutesList.tsx
index 6603f380120d6..85e9b24cee37c 100644
--- a/apps/web/src/components/settings/EnvironmentRoutesList.tsx
+++ b/apps/web/src/components/settings/EnvironmentRoutesList.tsx
@@ -21,11 +21,12 @@ import {
connectionRouteAddress,
connectionRouteId,
connectionRouteLabel,
+ connectionRouteMcpUrl,
connectionRoutes,
isLearned,
pendingRouteOrder,
} from "@t3tools/client-runtime/connection";
-import { GripVerticalIcon, PlusIcon, XIcon } from "lucide-react";
+import { CopyIcon, GripVerticalIcon, PlusIcon, XIcon } from "lucide-react";
import { useState } from "react";
import { requestConfirmDialog } from "~/confirmDialog";
@@ -39,10 +40,14 @@ import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
export function EnvironmentRoutesList({
environment,
+ relayHttpBaseUrl,
onAddRoute,
+ onCopyMcpUrl,
}: {
readonly environment: EnvironmentPresentation;
+ readonly relayHttpBaseUrl: string | undefined;
readonly onAddRoute: () => void;
+ readonly onCopyMcpUrl: (url: string) => void;
}) {
const saved = connectionRoutes(environment.entry);
const savedIds = saved.map((route) => connectionRouteId(route.target));
@@ -108,7 +113,9 @@ export function EnvironmentRoutesList({
route={route}
position={index + 1}
inUse={connectionRouteId(route.target) === activeRouteId}
+ mcpUrl={connectionRouteMcpUrl(route, relayHttpBaseUrl)}
removable={routes.length > 1 && !isLearned(route)}
+ onCopyMcpUrl={onCopyMcpUrl}
onRemove={() => void confirmRemove(route)}
/>
))}
@@ -129,13 +136,17 @@ function SortableRouteRow({
route,
position,
inUse,
+ mcpUrl,
removable,
+ onCopyMcpUrl,
onRemove,
}: {
readonly route: ConnectionRoute;
readonly position: number;
readonly inUse: boolean;
+ readonly mcpUrl: string | null;
readonly removable: boolean;
+ readonly onCopyMcpUrl: (url: string) => void;
readonly onRemove: () => void;
}) {
const id = connectionRouteId(route.target);
@@ -176,6 +187,24 @@ function SortableRouteRow({
) : null}
+ {mcpUrl !== null ? (
+
+ onCopyMcpUrl(mcpUrl)}
+ />
+ }
+ >
+
+
+ Copy MCP URL
+
+ ) : null}