feat(connections): add connection auth tools and inline auth card UI#2769
feat(connections): add connection auth tools and inline auth card UI#2769
Conversation
🧪 BenchmarkShould we run the Virtual MCP strategy benchmark for this PR? React with 👍 to run the benchmark.
Benchmark will run on the next push after you react. |
Release OptionsSuggested: Minor ( React with an emoji to override the release type:
Current version:
|
There was a problem hiding this comment.
3 issues found across 10 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/tools/connection/install.ts">
<violation number="1" location="apps/mesh/src/tools/connection/install.ts:69">
P2: Duplicate detection should compare the canonical connection slug (app_name/URL/title) instead of raw connection_url. URL-only matching can block installs for different apps that share the same MCP endpoint.
(Based on your team's feedback about using getConnectionSlug as the canonical identifier for sibling connections.) [FEEDBACK_USED]</violation>
</file>
<file name="apps/mesh/src/web/components/chat/message/parts/tool-call-part/connection-auth.tsx">
<violation number="1" location="apps/mesh/src/web/components/chat/message/parts/tool-call-part/connection-auth.tsx:121">
P1: Same `fetch`-doesn't-throw issue here — the OAuth token save response is never checked. A server-side rejection will be silently treated as success. Check `response.ok` (or at minimum read the response) before falling through to the success state.</violation>
<violation number="2" location="apps/mesh/src/web/components/chat/message/parts/tool-call-part/connection-auth.tsx:160">
P1: `fetch` does not throw on HTTP error responses (4xx/5xx). If the server rejects the token, this silently reports success to the user. Check `response.ok` before proceeding.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
apps/mesh/src/web/components/chat/message/parts/tool-call-part/connection-auth.tsx
Outdated
Show resolved
Hide resolved
apps/mesh/src/web/components/chat/message/parts/tool-call-part/connection-auth.tsx
Outdated
Show resolved
Hide resolved
2273451 to
a4eba86
Compare
There was a problem hiding this comment.
10 issues found across 9 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/web/components/store/store-registry-empty-state.tsx">
<violation number="1" location="apps/mesh/src/web/components/store/store-registry-empty-state.tsx:57">
P1: Do not include `configuration_scopes` in the connection install payload; this can overwrite/drop backend-populated registry scopes.
(Based on your team's feedback about excluding `configuration_scopes` from connection create/install payloads.) [FEEDBACK_USED]</violation>
</file>
<file name="apps/mesh/src/web/components/connection-auth-card.tsx">
<violation number="1" location="apps/mesh/src/web/components/connection-auth-card.tsx:122">
P1: `fetch` does not throw on HTTP error responses. If saving the OAuth token fails server-side, the card will still show success. Check `response.ok` and throw on failure.</violation>
<violation number="2" location="apps/mesh/src/web/components/connection-auth-card.tsx:162">
P1: `fetch` does not throw on HTTP error responses (4xx/5xx). If the token save fails server-side, the card will incorrectly show "Authenticated". Check `response.ok` and throw on failure.</violation>
</file>
<file name="apps/mesh/src/tools/connection/install.ts">
<violation number="1" location="apps/mesh/src/tools/connection/install.ts:96">
P2: Duplicate matching should use the canonical connection slug, not `connection_url`/`app_name` equality, to avoid misidentifying unrelated connections as duplicates.
(Based on your team's feedback about using `getConnectionSlug()` as the canonical identifier for sibling connection matching.) [FEEDBACK_USED]</violation>
<violation number="2" location="apps/mesh/src/tools/connection/install.ts:157">
P1: Do not let install input override `configuration_scopes`; use backend-fetched scopes so auth requirements and stored scopes stay consistent.
(Based on your team's feedback about not setting `configuration_scopes` from create/install payloads because it can drop registry scopes.) [FEEDBACK_USED]</violation>
</file>
<file name="packages/mesh-sdk/src/hooks/use-connection-install.ts">
<violation number="1" location="packages/mesh-sdk/src/hooks/use-connection-install.ts:69">
P2: Do not pass `configuration_scopes` through install input; it can override backend-discovered scopes during connection creation.
(Based on your team's feedback about not sending configuration_scopes in creation/install payloads.) [FEEDBACK_USED]</violation>
<violation number="2" location="packages/mesh-sdk/src/hooks/use-connection-install.ts:76">
P2: The success invalidation targets `KEYS.connections(...)`, but connection lists are cached under collection query keys, so installs may not refresh `useConnections()` data.</violation>
</file>
<file name="apps/mesh/src/web/routes/orgs/connections.tsx">
<violation number="1" location="apps/mesh/src/web/routes/orgs/connections.tsx:1026">
P2: Handle `result.is_existing` in the create flow before navigating; otherwise duplicate installs can navigate using `newId` for a connection that was not created.</violation>
<violation number="2" location="apps/mesh/src/web/routes/orgs/connections.tsx:1048">
P1: Do not send `configuration_scopes` in the install payload; it can overwrite backend-populated scopes and produce incorrect auth scope state.
(Based on your team's feedback about excluding `configuration_scopes` from create/install payloads.) [FEEDBACK_USED]</violation>
</file>
<file name="apps/mesh/src/web/routes/orgs/store/mcp-server-detail.tsx">
<violation number="1" location="apps/mesh/src/web/routes/orgs/store/mcp-server-detail.tsx:644">
P1: Do not send `configuration_scopes` in the install payload; it can override MCP-fetched scopes and store incorrect auth scope state.
(Based on your team's feedback about avoiding `configuration_scopes` in creation/install payloads.) [FEEDBACK_USED]</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| configuration_scopes: firstRegistry.configuration_scopes ?? undefined, | ||
| metadata: | ||
| (firstRegistry.metadata as Record<string, unknown>) ?? undefined, |
There was a problem hiding this comment.
P1: Do not include configuration_scopes in the connection install payload; this can overwrite/drop backend-populated registry scopes.
(Based on your team's feedback about excluding configuration_scopes from connection create/install payloads.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/store/store-registry-empty-state.tsx, line 57:
<comment>Do not include `configuration_scopes` in the connection install payload; this can overwrite/drop backend-populated registry scopes.
(Based on your team's feedback about excluding `configuration_scopes` from connection create/install payloads.) </comment>
<file context>
@@ -34,8 +34,31 @@ export function StoreRegistryEmptyState({
+ configuration_state:
+ (firstRegistry.configuration_state as Record<string, unknown>) ??
+ undefined,
+ configuration_scopes: firstRegistry.configuration_scopes ?? undefined,
+ metadata:
+ (firstRegistry.metadata as Record<string, unknown>) ?? undefined,
</file context>
| configuration_scopes: firstRegistry.configuration_scopes ?? undefined, | |
| metadata: | |
| (firstRegistry.metadata as Record<string, unknown>) ?? undefined, | |
| metadata: | |
| (firstRegistry.metadata as Record<string, unknown>) ?? undefined, |
| } | ||
|
|
||
| if (result.tokenInfo) { | ||
| await fetch(`/api/connections/${data.connection_id}/oauth-token`, { |
There was a problem hiding this comment.
P1: fetch does not throw on HTTP error responses. If saving the OAuth token fails server-side, the card will still show success. Check response.ok and throw on failure.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/connection-auth-card.tsx, line 122:
<comment>`fetch` does not throw on HTTP error responses. If saving the OAuth token fails server-side, the card will still show success. Check `response.ok` and throw on failure.</comment>
<file context>
@@ -0,0 +1,249 @@
+ }
+
+ if (result.tokenInfo) {
+ await fetch(`/api/connections/${data.connection_id}/oauth-token`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
</file context>
| if (!authState.tokenValue.trim()) return; | ||
| setAuthState((prev) => ({ ...prev, loading: true, error: null })); | ||
| try { | ||
| await fetch(`/api/connections/${data.connection_id}/token`, { |
There was a problem hiding this comment.
P1: fetch does not throw on HTTP error responses (4xx/5xx). If the token save fails server-side, the card will incorrectly show "Authenticated". Check response.ok and throw on failure.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/connection-auth-card.tsx, line 162:
<comment>`fetch` does not throw on HTTP error responses (4xx/5xx). If the token save fails server-side, the card will incorrectly show "Authenticated". Check `response.ok` and throw on failure.</comment>
<file context>
@@ -0,0 +1,249 @@
+ if (!authState.tokenValue.trim()) return;
+ setAuthState((prev) => ({ ...prev, loading: true, error: null }));
+ try {
+ await fetch(`/api/connections/${data.connection_id}/token`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
</file context>
| configuration_state: | ||
| (connectionData.configuration_state as Record<string, unknown>) ?? | ||
| undefined, | ||
| configuration_scopes: |
There was a problem hiding this comment.
P1: Do not send configuration_scopes in the install payload; it can overwrite backend-populated scopes and produce incorrect auth scope state.
(Based on your team's feedback about excluding configuration_scopes from create/install payloads.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/routes/orgs/connections.tsx, line 1048:
<comment>Do not send `configuration_scopes` in the install payload; it can overwrite backend-populated scopes and produce incorrect auth scope state.
(Based on your team's feedback about excluding `configuration_scopes` from create/install payloads.) </comment>
<file context>
@@ -1018,70 +1017,48 @@ function OrgMcpsContent() {
+ configuration_state:
+ (connectionData.configuration_state as Record<string, unknown>) ??
+ undefined,
+ configuration_scopes:
+ connectionData.configuration_scopes ?? undefined,
+ metadata:
</file context>
| configuration_state: | ||
| (connectionData.configuration_state as Record<string, unknown>) ?? | ||
| undefined, | ||
| configuration_scopes: |
There was a problem hiding this comment.
P1: Do not send configuration_scopes in the install payload; it can override MCP-fetched scopes and store incorrect auth scope state.
(Based on your team's feedback about avoiding configuration_scopes in creation/install payloads.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/routes/orgs/store/mcp-server-detail.tsx, line 644:
<comment>Do not send `configuration_scopes` in the install payload; it can override MCP-fetched scopes and store incorrect auth scope state.
(Based on your team's feedback about avoiding `configuration_scopes` in creation/install payloads.) </comment>
<file context>
@@ -617,65 +614,56 @@ function StoreMCPServerDetailContent() {
+ configuration_state:
+ (connectionData.configuration_state as Record<string, unknown>) ??
+ undefined,
+ configuration_scopes:
+ connectionData.configuration_scopes ?? undefined,
+ metadata:
</file context>
| const existing = await ctx.storage.connections.list(organization.id); | ||
| const duplicate = existing.find( | ||
| (c: { connection_url?: string | null; app_name?: string | null }) => | ||
| c.connection_url === input.connection_url || |
There was a problem hiding this comment.
P2: Duplicate matching should use the canonical connection slug, not connection_url/app_name equality, to avoid misidentifying unrelated connections as duplicates.
(Based on your team's feedback about using getConnectionSlug() as the canonical identifier for sibling connection matching.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/tools/connection/install.ts, line 96:
<comment>Duplicate matching should use the canonical connection slug, not `connection_url`/`app_name` equality, to avoid misidentifying unrelated connections as duplicates.
(Based on your team's feedback about using `getConnectionSlug()` as the canonical identifier for sibling connection matching.) </comment>
<file context>
@@ -62,11 +89,12 @@ export const CONNECTION_INSTALL = defineTool({
- (c: { connection_url?: string | null }) =>
- c.connection_url === input.connection_url,
+ (c: { connection_url?: string | null; app_name?: string | null }) =>
+ c.connection_url === input.connection_url ||
+ (input.app_name && c.app_name === input.app_name),
);
</file context>
| return extractPayload<ConnectionInstallOutput>(result); | ||
| }, | ||
| onSuccess: () => { | ||
| queryClient.invalidateQueries({ |
There was a problem hiding this comment.
P2: The success invalidation targets KEYS.connections(...), but connection lists are cached under collection query keys, so installs may not refresh useConnections() data.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/mesh-sdk/src/hooks/use-connection-install.ts, line 76:
<comment>The success invalidation targets `KEYS.connections(...)`, but connection lists are cached under collection query keys, so installs may not refresh `useConnections()` data.</comment>
<file context>
@@ -0,0 +1,81 @@
+ return extractPayload<ConnectionInstallOutput>(result);
+ },
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: KEYS.connections(locator),
+ });
</file context>
1a26ad4 to
a47d069
Compare
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/shared/utils/slugify.ts">
<violation number="1" location="apps/mesh/src/shared/utils/slugify.ts:22">
P2: Avoid returning an empty slug when `app_name` normalizes to nothing; fall back to other identifiers instead.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| id?: string; | ||
| }): string { | ||
| if (connection.app_name) { | ||
| return slugify(connection.app_name); |
There was a problem hiding this comment.
P2: Avoid returning an empty slug when app_name normalizes to nothing; fall back to other identifiers instead.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/shared/utils/slugify.ts, line 22:
<comment>Avoid returning an empty slug when `app_name` normalizes to nothing; fall back to other identifiers instead.</comment>
<file context>
@@ -0,0 +1,40 @@
+ id?: string;
+}): string {
+ if (connection.app_name) {
+ return slugify(connection.app_name);
+ }
+ if (connection.connection_url) {
</file context>
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/mcp-clients/virtual-mcp/passthrough-client.ts">
<violation number="1" location="apps/mesh/src/mcp-clients/virtual-mcp/passthrough-client.ts:357">
P2: Downstream tools are mapped only to namespaced keys, so plain unambiguous tool names now fail lookup in `callTool`.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
e800ed1 to
c1dc1b3
Compare
There was a problem hiding this comment.
1 issue found across 5 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/api/routes/decopilot/built-in-tools/built-in-mcp-server.ts">
<violation number="1" location="apps/mesh/src/api/routes/decopilot/built-in-tools/built-in-mcp-server.ts:57">
P2: Tool output IDs use `Date.now()`, which can collide under concurrency and overwrite stored outputs.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| const tokens = estimateJsonTokens(serialized); | ||
|
|
||
| if (tokens > MAX_RESULT_TOKENS) { | ||
| const toolCallId = `prompt_${Date.now()}`; |
There was a problem hiding this comment.
P2: Tool output IDs use Date.now(), which can collide under concurrency and overwrite stored outputs.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/api/routes/decopilot/built-in-tools/built-in-mcp-server.ts, line 57:
<comment>Tool output IDs use `Date.now()`, which can collide under concurrency and overwrite stored outputs.</comment>
<file context>
@@ -0,0 +1,246 @@
+ const tokens = estimateJsonTokens(serialized);
+
+ if (tokens > MAX_RESULT_TOKENS) {
+ const toolCallId = `prompt_${Date.now()}`;
+ toolOutputMap.set(toolCallId, serialized);
+ const preview = createOutputPreview(serialized);
</file context>
Add CONNECTION_INSTALL, CONNECTION_AUTHENTICATE, and CONNECTION_AUTH_STATUS tools with an inline ConnectionAuthPart UI component for OAuth/token auth flows in chat. Includes token management API routes and stream-core auto-detection of connections needing auth. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…and AI Extract AuthCard into reusable component, create useConnectionInstall hook, and route all HTTP/SSE/Websocket installs through CONNECTION_INSTALL tool. STDIO connections continue using COLLECTION_CONNECTIONS_CREATE directly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e setup Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… compat The MCP SDK's ToolSchema uses z.custom() types that fail JSON Schema serialization inside the MCP server's tools/list handler. Replace with an equivalent inline schema covering all known MCP tool fields. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…:tool_name When multiple connections expose the same tool name, all colliding tools are now namespaced instead of silently dropping duplicates. Non-colliding tools keep their plain names. Downstream calls use the original name. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…slug Instead of only namespacing on collision, all downstream tools are now consistently prefixed as connection-slug::tool_name. Simpler and avoids surprise renames when a new connection introduces a collision. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously, when no plugins were activated for an org, enabledPlugins was set to null which the filter treated as "show everything". Now passes an empty array so plugin tools are correctly excluded. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…try schema Add a builtin MCP server (read_prompt, read_resource, read_tool_output) to Claude Code sessions so they can access prompts and resources. Use the McpServerConfig type from claude-agent-sdk for proper typing. Also fix registry app GET schema validation by adding missing is_unlisted field and allowing passthrough on server JSON. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rendering Use regex pattern to match CONNECTION_AUTHENTICATE tool names regardless of casing or namespace prefix (e.g. Mcp_Agent_Deco_Management_Mcp_Connection_Authenticate). Also handle dynamic-tool part type which was bypassing the auth card check. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…NSTALL flow Replace COLLECTION_CONNECTIONS_CREATE + CONNECTION_TEST references with the new CONNECTION_INSTALL → CONNECTION_AUTHENTICATE → CONNECTION_TEST workflow. Update registry tool names to COLLECTION_REGISTRY_APP_SEARCH/GET. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The constant was removed from @decocms/mesh-sdk. Remove the unused import from store-registry-empty-state.tsx and inline "org-admin" in mcp-server-detail.tsx. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3e0bafd to
9e97d48
Compare
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
What is this contribution about?
Adds connection authentication tools and inline auth card UI for AI-driven connection setup. Key changes:
CONNECTION_INSTALL,CONNECTION_AUTHENTICATE,CONNECTION_AUTH_STATUSfor AI-driven connection setup and authConnectionAuthPartandAuthCardcomponents render OAuth/API-key auth cards directly in chatstream-core.tsauto-emitsdata-connection-authfor connections needing auth after installconnection-slug::tool_namefor deterministic routingread_prompt,read_resource,read_tool_outputtoolsis_unlistedfield and.passthrough()on server schema to fix structured content validation errorsMcp_Agent_Deco_Management_Mcp_Connection_Authenticate) for auth card rendering in bothdynamic-toolandtool-*part typesScreenshots/Demonstration
N/A
How to Test
bun run devMcp_Agent_Deco_Management_Mcp_Connection_AuthenticateformatCOLLECTION_REGISTRY_APP_GETwith{"id": "deco/hyperdx"}→ should return item without schema validation errorbun run check && bun run fmt && bun testto verify no regressionsMigration Notes
N/A
Review Checklist