diff --git a/server/src/__tests__/connection-management.integration.test.ts b/server/src/__tests__/connection-management.integration.test.ts index a34f78b432c..a9eac4a4292 100644 --- a/server/src/__tests__/connection-management.integration.test.ts +++ b/server/src/__tests__/connection-management.integration.test.ts @@ -1558,6 +1558,7 @@ describe("connection management with production default agents", () => { const tokenValue = /pcgw_[0-9a-f-]{36}\.[A-Za-z0-9_-]+/; const [company] = await server.db.select({ issuePrefix: companies.issuePrefix }).from(companies).where(eq(companies.id, f.companyId)); const tokensPath = `/${company.issuePrefix}/apps/gateways/${gateway.body.id}/tokens`; + expect(gateway.body).toMatchObject({ tokensPath, gatewayPath: `/${company.issuePrefix}/apps/gateways/${gateway.body.id}/overview` }); const created = await call("gateway-client-create", "POST /api/tool-gateway/gateways/{gatewayId}/tokens", { gatewayId: gateway.body.id }, body); expect(created, JSON.stringify(created)).toMatchObject({ status: 201, data: { name: "Fixture client", pendingReveal: true, tokensPath, diff --git a/server/src/routes/openapi.ts b/server/src/routes/openapi.ts index a44b8e6ab94..ad5cb6bd40b 100644 --- a/server/src/routes/openapi.ts +++ b/server/src/routes/openapi.ts @@ -12871,6 +12871,7 @@ registerCurrentRoute({ path: "/api/companies/{companyId}/tools/gateways", tags: ["tool-gateway"], summary: "Create a named MCP gateway", + description: "Creates a gateway that exposes a tool access profile to external MCP clients. Agents with delegated connection management may create it; agent responses also include gatewayPath (//apps/gateways//overview) and tokensPath (//apps/gateways//tokens). Link the person to them as Markdown links. A client needs a gateway client token to connect; see POST /api/tool-gateway/gateways/{gatewayId}/tokens.", body: createToolMcpGatewaySchema, responses: { 201: r.ok(), @@ -12906,7 +12907,7 @@ registerCurrentRoute({ path: "/api/tool-gateway/gateways/{gatewayId}/tokens", tags: ["tool-gateway"], summary: "Create a named MCP gateway token", - description: "Creates a client token for an external MCP client. A person caller receives the token value once in the response. An agent with delegated connection management may create the token for its responsible person, but receives only metadata (id, tokenPrefix, expiresAt, pendingReveal: true) plus tokensPath, the gateway's Tokens page. The value does not exist until that person reveals it there; send the person to tokensPath as a Markdown link, and never ask for, accept, or repeat token values in chat. To rotate, create the replacement, then revoke the old token after the person confirms the client works with the new one.", + description: "Creates a client token for an external MCP client. A person caller receives the token value once in the response. An agent with delegated connection management may create the token for its responsible person, but receives only metadata (id, tokenPrefix, expiresAt, pendingReveal: true) plus tokensPath, the gateway's Tokens page (//apps/gateways//tokens). The value does not exist until that person reveals it there; send the person to tokensPath as a Markdown link, and never ask for, accept, or repeat token values in chat. To rotate, create the replacement, then revoke the old token after the person confirms the client works with the new one.", body: toolGatewayCompanyBodySchema, responses: { 201: r.ok(), diff --git a/server/src/routes/tool-gateway.ts b/server/src/routes/tool-gateway.ts index 44a17b43d50..fc370ea841d 100644 --- a/server/src/routes/tool-gateway.ts +++ b/server/src/routes/tool-gateway.ts @@ -408,7 +408,13 @@ export function toolGatewayRoutes(db: Db, toolGateway: ToolGatewayService) { actor: { agentId: actor.agentId, userId: req.actor.type === "board" ? req.actor.userId : null }, managementContext: connectionRequestContext(req), }); - res.status(201).json(gateway); + if (req.actor.type !== "agent") { + res.status(201).json(gateway); + return; + } + // Agents link the person to the saved gateway; clients need a token from its Tokens page. + const tokensPath = await gatewayTokensPath(req.params.companyId as string, gateway.id); + res.status(201).json({ ...gateway, gatewayPath: tokensPath.replace(/\/tokens$/, "/overview"), tokensPath }); } catch (err) { sendGatewayError(res, err); }