fix(api-catalog): send an own MCP server's API key to the secure setup page

An agent asked to connect a person's own MCP server with an API key saw
the connections_search instruction but kept searching the API for a way
to create the connection itself. The two connection-creation entries it
finds now say the person enters the key on Connect your own MCP server,
and that the agent links that page and replies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DottaandClaude Opus 5.5 committed 2026-11-09 02:25:27 -06:00
1 parent c77024412d
commit dc36aee6e1
2 files changed
+16 -3

No files matched your search

@@ -54,10 +54,10 @@ it("names only pages that the board UI mounts", () => {
const appRoutes = readFileSync(path.join(repoRoot, "ui/src/App.tsx"), "utf8");
const pages = new Set(
runnerApiCatalog()
.filter(row => /^\/api\/(mcp\/(setup|requests\/\{id\}\/consent)$|connection-intents\/|tool-connections\/\{connectionId\}\/(catalog|review-counts)$|companies\/\{companyId\}\/(tools\/apps\/attention|chat-endpoints)$)/.test(row.path))
.filter(row => /^\/api\/(mcp\/(setup|requests\/\{id\}\/consent)$|connection-intents\/|tool-connections\/\{connectionId\}\/(catalog|review-counts)$|companies\/\{companyId\}\/(tools\/apps\/attention|tools\/apps\/connect|tools\/connections|chat-endpoints)$)/.test(row.path))
.flatMap(row => [...(row.description ?? "").matchAll(/\/<prefix>\/([a-z0-9/<>-]+)/gi)].map(match => match[1]!)),
);
expect([...pages].sort()).toEqual(["apps/<connectionId>/permissions", "apps/assistant-connection", "company/settings/instance/experimental", "issues/<identifier>"]);
expect([...pages].sort()).toEqual(["apps/<connectionId>/permissions", "apps/assistant-connection", "apps/byo", "company/settings/instance/experimental", "issues/<identifier>"]);
const mounted = (page: string) => page
.replace("apps/<connectionId>/permissions", "apps/:connectionId/:tab")
.replace("issues/<identifier>", "issues/:issueId");
@@ -65,6 +65,15 @@ it("names only pages that the board UI mounts", () => {
expect(readFileSync(path.join(repoRoot, "ui/src/pages/apps/app-tabs.ts"), "utf8")).toContain("\"permissions\"");
});
it.each([
"connect custom MCP server with server URL and API key BYO connection",
"byo custom mcp server create tool app connection",
])("search %j sends an own-server API key to the secure page", (query) => {
const found = searchRunnerApi({ query, limit: 10 }).results.filter(row => ["POST /api/companies/{companyId}/tools/apps/connect", "POST /api/companies/{companyId}/tools/connections"].includes(row.operationId));
expect(found.length, query).toBeGreaterThan(0);
for (const operation of found) expect(operation.description).toContain("[Connect your own MCP server](/<prefix>/apps/byo)");
});
it("links only mounted pages from connections_search", () => {
const appRoutes = readFileSync(path.join(repoRoot, "ui/src/App.tsx"), "utf8");
const search = readFileSync(path.join(repoRoot, "server/src/services/connection-intents.ts"), "utf8");
+5 -1
View File
@@ -11667,6 +11667,9 @@ registerCurrentRoute({
"Inspect a curated app's public MCP and OAuth metadata without credentials or registration",
});
const OWN_MCP_SERVER_KEY = "When a person's own MCP server needs an API key, the person adds it themselves on [Connect your own MCP server](/<prefix>/apps/byo): they enter its URL and type the key into that page's secure field. "
+ "An agent cannot enter the key and must not ask for it in chat. Link that page and reply instead of creating the connection here.";
registerCurrentRoute({
"x-paperclip-transport": "rest",
"x-paperclip-connection-management": "connections:configure",
@@ -11674,6 +11677,7 @@ registerCurrentRoute({
path: "/api/companies/{companyId}/tools/apps/connect",
tags: ["tool-access"],
summary: "Create a draft app connection from gallery input",
description: OWN_MCP_SERVER_KEY,
body: connectToolAppSchema,
responses: {
200: r.ok(),
@@ -11845,7 +11849,7 @@ registerCurrentRoute({
path: "/api/companies/{companyId}/tools/connections",
tags: ["tool-access"],
summary: "Create a tool connection",
description: "Saves a connection as given without checking that its server answers. To connect an MCP server from its URL, use POST /api/companies/{companyId}/tools/apps/connect with link instead: it checks the server first, and a fresh setup saves nothing when the server cannot be reached.",
description: "Saves a connection as given without checking that its server answers. To connect an MCP server from its URL, use POST /api/companies/{companyId}/tools/apps/connect with link instead: it checks the server first, and a fresh setup saves nothing when the server cannot be reached." + " " + OWN_MCP_SERVER_KEY,
body: createToolConnectionSchema,
responses: {
201: r.ok(),