diff --git a/server/src/routes/openapi.ts b/server/src/routes/openapi.ts
index 5447cf48307..f5d157ab5fb 100644
--- a/server/src/routes/openapi.ts
+++ b/server/src/routes/openapi.ts
@@ -1666,7 +1666,7 @@ function resolveOperationAuthLevel(
): OpenApiAuthLevel {
const key = operationKey(method, path);
if (key === "GET /api/mcp/requests/{id}") return "public";
- if (path.startsWith("/api/mcp/requests/") || path.startsWith("/api/mcp/connections")) return "board";
+ if (path === "/api/mcp/setup" || path.startsWith("/api/mcp/requests/") || path.startsWith("/api/mcp/connections")) return "board";
if (PUBLIC_OPERATIONS.has(key)) return "public";
if (key === "POST /api/mcp/project-tools" || key === "POST /api/companies/{companyId}/slack/tasks/{issueId}/tools") return "agent_run";
if (RUNTIME_TOOLS_OPERATIONS.has(key)) return "runtime_tools";
@@ -11334,6 +11334,16 @@ registerCurrentRoute({
},
});
+registerCurrentRoute({
+ method: "get", path: "/api/mcp/setup", tags: ["tool-gateway"],
+ summary: "Read assistant connection setup for a signed-in person",
+ description: "Returns the live experimental setting and canonical MCP server URL. Requires a human browser session; agent and board API keys are not accepted. Remains available while the experiment is disabled and grants no access.",
+ responses: {
+ 200: r.ok(z.object({ enabled: z.boolean(), serverUrl: z.string().url() })),
+ 401: r.unauthorized, 403: r.forbidden, 404: r.notFound,
+ },
+});
+
registerCurrentRoute({
method: "get", path: "/api/mcp/requests/{id}", tags: ["tool-gateway"],
summary: "Describe an assistant connection request and available sign-in options",
diff --git a/ui/src/pages/InstanceExperimentalSettings.test.tsx b/ui/src/pages/InstanceExperimentalSettings.test.tsx
index 9e5c0b22c02..88dadbd6b9b 100644
--- a/ui/src/pages/InstanceExperimentalSettings.test.tsx
+++ b/ui/src/pages/InstanceExperimentalSettings.test.tsx
@@ -1,6 +1,7 @@
// @vitest-environment jsdom
import { flushSync } from "react-dom";
+import { MemoryRouter } from "react-router-dom";
import { createRoot, type Root } from "react-dom/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { INSTANCE_FEATURE_KEYS } from "@paperclipai/shared";
@@ -21,6 +22,10 @@ vi.mock("@/api/instanceSettings", () => ({
instanceSettingsApi: mockInstanceSettingsApi,
}));
+vi.mock("@/context/CompanyContext", () => ({
+ useCompany: () => ({ selectedCompany: { id: "butter", issuePrefix: "BUT" } }),
+}));
+
vi.mock("../context/BreadcrumbContext", () => ({
useBreadcrumbs: () => ({ setBreadcrumbs: vi.fn() }),
}));
@@ -165,7 +170,9 @@ describe("InstanceExperimentalSettings — Conference Room Chat card (PAP-11233)
flushSync(() => {
root!.render(
{value}