feat(mothership): discover every workspace mcp server without tagging · simstudioai/sim@a1bb62b · GitHub
Skip to content

Commit a1bb62b

Browse files
committed
feat(mothership): discover every workspace mcp server without tagging
The integration catalog only inspected MCP servers the user had /-tagged in the chat, so a configured server's tools were invisible to the agent until tagged. Claude Code, Codex, Cursor and OpenCode treat every configured server as available and discover tools lazily; built-in integrations here already did. In agent and plan chats, the catalog's MCP candidates are now every enabled, non-Credential-Group server the requester can list in the target workspace. Search stays MCP-free, and workflow-block requests (mcpExecution or selected operations) stay scoped to their explicit selections. - Untagged servers are discovered best-effort: cache-aside through the existing McpService discovery cache (in-flight deduped, negative cache honored), and a server that does not answer within 3s is skipped and logged while its discovery keeps running to warm the cache for the next query. - Tagged servers and an explicitly targeted server (service mcp:<id> or an exact tool id, which the worker re-resolves before executing) are still listed live and completely, and a targeted server still fails with its own error. - Tagged servers rank first among equally relevant matches via a preferredServices tie-break in projectIntegrationCatalog. - A permission group that disables MCP now drops MCP from broad requests and keeps built-in operations listed; targeted and workflow-block requests still fail with the permission error. Tagging persistence is unchanged; comments that said untagged servers stay unavailable now describe tags as a priority hint.
1 parent 463350c commit a1bb62b

10 files changed

Lines changed: 340 additions & 128 deletions

File tree

‎apps/sim/app/api/mothership/integrations/catalog/route.test.ts‎

Lines changed: 1 addition & 1 deletion

‎apps/sim/lib/mothership/chat/lifecycle.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ export async function loadChatFileAttachmentsNamed(
256256
* MCP server ids tagged (`/name`) on a chat's live messages, in first-tagged
257257
* transcript order. Reads only the `mcp` entries of each message's `contexts`
258258
* instead of materializing the whole transcript, and skips soft-deleted
259-
* messages so a removed turn no longer enables its servers.
259+
* messages so a removed turn no longer tags its servers.
260260
*/
261261
export async function loadChatMcpServerIds(chatId: string): Promise<string[]> {
262262
const rows = await db

‎apps/sim/lib/mothership/chat/payload.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ interface BuildPayloadParams {
7070
provider?: string
7171
contexts?: Array<{ type: string; content: string; tag?: string; path?: string }>
7272
/**
73-
* MCP servers enabled for this chat — every server tagged on this or any
74-
* earlier turn. Servers never tagged in the chat stay unavailable.
73+
* MCP servers tagged on this or any earlier turn of the chat. Every usable
74+
* workspace server is discoverable without a tag; tagged ones rank first.
7575
*/
7676
mcpServerIds?: string[]
7777
fileAttachments?: Array<{ id: string; key: string; size: number; [key: string]: unknown }>

‎apps/sim/lib/mothership/chat/post.ts‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -508,14 +508,15 @@ function normalizeContexts(contexts: UnifiedChatRequest['contexts']) {
508508
}
509509

510510
/**
511-
* An MCP server tagged with `/name` stays enabled for the rest of the chat, not
512-
* just the turn it was tagged on. Persisted user messages already carry their
513-
* `mcp` contexts, so the transcript is the source of truth — enablement survives
514-
* reloads and reopened chats with no extra state to keep in sync. There is
515-
* deliberately no off switch: history is append-only.
511+
* An MCP server tagged with `/name` stays tagged for the rest of the chat, not
512+
* just the turn it was tagged on. Every usable workspace server is discoverable
513+
* without a tag; a tag marks the servers the user means, whose operations the
514+
* integration catalog ranks first and lists completely. Persisted user messages
515+
* already carry their `mcp` contexts, so the transcript is the source of truth —
516+
* tags survive reloads and reopened chats with no extra state to keep in sync.
517+
* There is deliberately no off switch: history is append-only.
516518
*
517-
* Only the ids travel forward, not the contexts themselves. The ids scope the
518-
* request's integration catalog, and the agent discovers these servers'
519+
* Only the ids travel forward, not the contexts themselves. The agent discovers
519520
* operations on demand, so re-expanding the prompt listing each turn adds
520521
* nothing. Keeping inherited servers out of the persisted contexts also keeps
521522
* the `/name` chips on a sent message showing only what the user actually typed

‎apps/sim/lib/mothership/chat/process-contents.test.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import {
4747
MAX_TABLE_SELECTION_PREVIEW_LENGTH,
4848
MAX_TABLE_SELECTION_ROWS,
4949
} from '@/lib/mothership/chat/selection-context'
50-
import { buildTaggedMcpToolSchemas } from '@/lib/mothership/mcp-tools'
50+
import { buildMcpToolSchemas } from '@/lib/mothership/mcp-tools'
5151
import {
5252
getBlock as registryGetBlock,
5353
getBlockRegistry as registryGetBlockRegistry,
@@ -635,7 +635,9 @@ describe('processContextsServer - MCP contexts', () => {
635635
},
636636
])
637637
expect(discoverServerTools).not.toHaveBeenCalled()
638-
const { tools: catalog } = await buildTaggedMcpToolSchemas('user-1', 'ws-1', ['mcp-server-1'])
638+
const { tools: catalog } = await buildMcpToolSchemas('user-1', 'ws-1', {
639+
complete: ['mcp-server-1'],
640+
})
639641
expect(discoverServerTools).toHaveBeenCalledTimes(1)
640642
expect(discoverServerTools).toHaveBeenCalledWith({
641643
principal: expect.objectContaining({

‎apps/sim/lib/mothership/integrations/application/catalog-registry.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { projectIntegrationCatalog } from '@/lib/mothership/integrations/applica
1010
/** Registration assertions must exercise the executable registry, not the global empty mock. */
1111
vi.unmock('@/tools/registry')
1212
vi.mock('@/lib/mothership/chat/payload', () => mothershipChatPayloadMock)
13-
vi.mock('@/lib/mothership/mcp-tools', () => ({ buildTaggedMcpToolSchemas: vi.fn() }))
13+
vi.mock('@/lib/mothership/mcp-tools', () => ({ buildMcpToolSchemas: vi.fn() }))
1414
vi.mock('@/lib/mcp/application/use-cases', () => mcpUseCasesMock)
1515

1616
const tools: ToolSchema[] = getExposedIntegrationTools().map((tool) => ({

‎apps/sim/lib/mothership/integrations/application/catalog.test.ts‎

Lines changed: 121 additions & 4 deletions

0 commit comments

Comments
 (0)