From 7631f36de1ef7d6f9bdb8e0dc70be994a5a032ec Mon Sep 17 00:00:00 2001 From: Guilherme Rodrigues Date: Sat, 14 Mar 2026 11:53:09 -0300 Subject: [PATCH] feat(connections): embed sanitized title in connection IDs for debuggability Connection IDs now include a slug derived from the title, e.g. conn_gmail_abc123 instead of conn_abc123, making it much easier to identify connections in logs, URLs, and database queries. Co-Authored-By: Claude Opus 4.6 --- apps/mesh/src/shared/utils/generate-id.ts | 12 ++++++++++++ apps/mesh/src/storage/connection.ts | 4 ++-- .../components/details/connection/collection-tab.tsx | 6 +++--- apps/mesh/src/web/routes/orgs/connections.tsx | 4 ++-- apps/mesh/src/web/utils/extract-connection-data.ts | 4 ++-- 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/apps/mesh/src/shared/utils/generate-id.ts b/apps/mesh/src/shared/utils/generate-id.ts index ebea14fccb..c7f2c57dcf 100644 --- a/apps/mesh/src/shared/utils/generate-id.ts +++ b/apps/mesh/src/shared/utils/generate-id.ts @@ -21,3 +21,15 @@ type IdPrefixes = export function generatePrefixedId(prefix: IdPrefixes) { return `${prefix}_${nanoid()}`; } + +export function generateConnectionId(title: string): string { + const slug = title + .toLowerCase() + .replace(/[^a-z0-9]+/g, "_") + .replace(/^_+|_+$/g, "") + .slice(0, 20) + .replace(/_+$/g, ""); + + const suffix = nanoid(); + return slug ? `conn_${slug}_${suffix}` : `conn_${suffix}`; +} diff --git a/apps/mesh/src/storage/connection.ts b/apps/mesh/src/storage/connection.ts index 64274082e4..939e1388ae 100644 --- a/apps/mesh/src/storage/connection.ts +++ b/apps/mesh/src/storage/connection.ts @@ -15,7 +15,7 @@ import type { ToolDefinition, } from "../tools/connection/schema"; import { isStdioParameters } from "../tools/connection/schema"; -import { generatePrefixedId } from "@/shared/utils/generate-id"; +import { generateConnectionId } from "@/shared/utils/generate-id"; import { getWellKnownDecopilotConnection, isDecopilot, @@ -65,7 +65,7 @@ export class ConnectionStorage implements ConnectionStoragePort { ) {} async create(data: Partial): Promise { - const id = data.id ?? generatePrefixedId("conn"); + const id = data.id ?? generateConnectionId(data.title ?? ""); const now = new Date().toISOString(); const existing = await this.findById(id); diff --git a/apps/mesh/src/web/components/details/connection/collection-tab.tsx b/apps/mesh/src/web/components/details/connection/collection-tab.tsx index 2642c7ca85..5b41b68e86 100644 --- a/apps/mesh/src/web/components/details/connection/collection-tab.tsx +++ b/apps/mesh/src/web/components/details/connection/collection-tab.tsx @@ -1,4 +1,4 @@ -import { generatePrefixedId } from "@/shared/utils/generate-id"; +import { generateConnectionId } from "@/shared/utils/generate-id"; import { CollectionDisplayButton } from "@/web/components/collections/collection-display-button.tsx"; import { CollectionSearch } from "@/web/components/collections/collection-search.tsx"; import { @@ -114,7 +114,7 @@ export function CollectionTab({ const now = new Date().toISOString(); await actions.create.mutateAsync({ ...item, - id: generatePrefixedId("conn"), + id: generateConnectionId(`${item.title} (Copy)`), title: `${item.title} (Copy)`, created_at: now, updated_at: now, @@ -153,7 +153,7 @@ export function CollectionTab({ const now = new Date().toISOString(); const newItem: BaseCollectionEntity = { - id: generatePrefixedId("conn"), + id: generateConnectionId("New Item"), title: "New Item", description: "A brief description of the item", created_at: now, diff --git a/apps/mesh/src/web/routes/orgs/connections.tsx b/apps/mesh/src/web/routes/orgs/connections.tsx index 136703e37e..2ca41f5369 100644 --- a/apps/mesh/src/web/routes/orgs/connections.tsx +++ b/apps/mesh/src/web/routes/orgs/connections.tsx @@ -1,4 +1,4 @@ -import { generatePrefixedId } from "@/shared/utils/generate-id"; +import { generateConnectionId } from "@/shared/utils/generate-id"; import { CollectionDisplayButton } from "@/web/components/collections/collection-display-button.tsx"; import { CollectionSearch } from "@/web/components/collections/collection-search.tsx"; import { type TableColumn } from "@/web/components/collections/collection-table.tsx"; @@ -1724,7 +1724,7 @@ function OrgMcpsContent() { return; } - const newId = generatePrefixedId("conn"); + const newId = generateConnectionId(data.title); // Create new connection await actions.create.mutateAsync({ id: newId, diff --git a/apps/mesh/src/web/utils/extract-connection-data.ts b/apps/mesh/src/web/utils/extract-connection-data.ts index 646b439a25..9bae87b7b7 100644 --- a/apps/mesh/src/web/utils/extract-connection-data.ts +++ b/apps/mesh/src/web/utils/extract-connection-data.ts @@ -11,7 +11,7 @@ import type { import { MCP_MESH_DECOCMS_KEY } from "@/web/utils/constants"; import { getGitHubAvatarUrl } from "@/web/utils/github"; import { getConnectionTypeLabel } from "@/web/utils/registry-utils"; -import { generatePrefixedId } from "@/shared/utils/generate-id"; +import { generateConnectionId } from "@/shared/utils/generate-id"; /** * Get a display name for a remote endpoint @@ -177,7 +177,7 @@ export function extractConnectionData( const title = baseName + titleSuffix; return { - id: generatePrefixedId("conn"), + id: generateConnectionId(title), title, description, icon,