1+ import { serializePrincipal } from '@sim/auth/principal'
12import { createLogger } from '@sim/logger'
23import { getErrorMessage , toError } from '@sim/utils/errors'
34import { generateId } from '@sim/utils/id'
@@ -18,18 +19,19 @@ import {
1819} from '@/lib/execution/private-tool-metadata'
1920import { createExecutorPrincipalFromExecutionContext } from '@/lib/internal/principals/executor'
2021import { MCP_SERVER_DELEGATION_AUDIENCE } from '@/lib/mcp/application/authorization'
21- import { buildIntegrationToolSchemas } from '@/lib/mothership/chat/payload'
22+ import { resolveMcpToolBinding } from '@/lib/mcp/tool-binding'
23+ import { createMcpToolId } from '@/lib/mcp/utils'
2224import { processContextsServer } from '@/lib/mothership/chat/process-contents'
2325import {
2426 type CopilotEnvironmentContext ,
2527 createCopilotEnvironmentContext ,
2628} from '@/lib/mothership/environment-context'
29+ import { IntegrationCatalogMcpExecution } from '@/lib/mothership/generated/integration-catalog'
2730import {
2831 MothershipStreamV1EventType ,
2932 MothershipStreamV1TextChannel ,
3033} from '@/lib/mothership/generated/mothership-stream-v1'
3134import { PROTOCOL_VERSION } from '@/lib/mothership/generated/protocol'
32- import { buildSelectedMcpToolSchemas , buildTaggedMcpToolSchemas } from '@/lib/mothership/mcp-tools'
3335import { runHeadlessCopilotLifecycle } from '@/lib/mothership/request/lifecycle/headless'
3436import { requestExplicitStreamAbort } from '@/lib/mothership/request/session/explicit-abort'
3537import type { StreamEvent } from '@/lib/mothership/request/types'
@@ -43,6 +45,7 @@ import {
4345 type ResolvedSecretTraceRegistry ,
4446} from '@/executor/utils/resolved-secret-trace-registry'
4547import type { ChatContext } from '@/stores/panel'
48+ import { hasToolId } from '@/tools/tool-ids'
4649
4750export const maxDuration = 3600
4851
@@ -94,12 +97,10 @@ function encodeNdjson(value: unknown): Uint8Array {
9497
9598export function buildExecuteResponsePayload (
9699 result : Awaited < ReturnType < typeof runHeadlessCopilotLifecycle > > ,
97- effectiveChatId : string ,
98- integrationTools : Array < { name : string } >
100+ effectiveChatId : string
99101) {
100- const clientToolNames = new Set ( integrationTools . map ( ( t ) => t . name ) )
101102 const clientToolCalls = ( result . toolCalls || [ ] ) . filter (
102- ( tc : { name : string } ) => clientToolNames . has ( tc . name ) || tc . name . startsWith ( 'mcp-' )
103+ ( tc : { name : string } ) => hasToolId ( tc . name ) || tc . name . startsWith ( 'mcp-' )
103104 )
104105
105106 return {
@@ -246,34 +247,25 @@ export const POST = withRouteHandler(async (req: NextRequest) => {
246247 )
247248 const nonMcpAgentMentions = agentMentions ?. filter ( ( context ) => context . kind !== 'mcp' )
248249 const userPermission = workspaceAccess . permission
249- const mothershipToolsPromise = Promise . allSettled ( [
250- buildSelectedMcpToolSchemas ( userId , workspaceId , mcpTools ?? [ ] , mcpContext ) ,
251- buildTaggedMcpToolSchemas ( userId , workspaceId , taggedMcpServerIds , mcpContext ) ,
252- ] ) . then ( ( results ) => {
253- const groups = results . map ( ( result ) => {
254- if ( result . status === 'rejected' ) throw result . reason
255- return result . value
250+ const selectedMcpToolIds = ( mcpTools ?? [ ] )
251+ . filter ( ( tool ) => tool . type === 'mcp' && ( tool . usageControl || 'auto' ) !== 'none' )
252+ . map ( ( tool ) => {
253+ const { serverId, toolName } = resolveMcpToolBinding ( tool )
254+ return createMcpToolId ( serverId , toolName )
256255 } )
257- const byName = new Map ( groups . flat ( ) . map ( ( tool ) => [ tool . name , tool ] ) )
258- return [ ...byName . values ( ) ]
256+ const agentContexts = await processContextsServer (
257+ nonMcpAgentMentions ,
258+ userId ,
259+ lastUserMessage ,
260+ workspaceId ,
261+ effectiveChatId ,
262+ activeResolvedSecretTraceRegistry
263+ ) . catch ( ( error ) => {
264+ reqLogger . warn ( 'Failed to resolve agent contexts for execution' , {
265+ error : toError ( error ) . message ,
266+ } )
267+ return [ ]
259268 } )
260- const [ integrationTools , mothershipTools , agentContexts ] = await Promise . all ( [
261- buildIntegrationToolSchemas ( userId , undefined , workspaceId ) ,
262- mothershipToolsPromise ,
263- processContextsServer (
264- nonMcpAgentMentions ,
265- userId ,
266- lastUserMessage ,
267- workspaceId ,
268- effectiveChatId ,
269- activeResolvedSecretTraceRegistry
270- ) . catch ( ( error ) => {
271- reqLogger . warn ( 'Failed to resolve agent contexts for execution' , {
272- error : toError ( error ) . message ,
273- } )
274- return [ ]
275- } ) ,
276- ] )
277269 /**
278270 * The wire payload IS the shared ExecuteRequest contract. Caller-side context —
279271 * resolved mentions and the MCP-enablement notice — folds into the message array
@@ -285,12 +277,13 @@ export const POST = withRouteHandler(async (req: NextRequest) => {
285277 const c = ctx as { type ?: string ; content ?: string }
286278 return `[Attached ${ c . type ?? 'context' } ]\n${ c . content ?? '' } `
287279 } ) ,
288- ...( mothershipTools . length > 0
280+ ...( taggedMcpServerIds . length || selectedMcpToolIds . length
289281 ? [
290282 [
291- 'The following MCP operations are enabled. Find their input schemas with search_integration_tools using the exact toolId shown , then invoke them through call_integration_tool.' ,
283+ 'MCP operations are enabled. Discover their current input schemas with search_integration_tools, then invoke them through call_integration_tool.' ,
292284 'Do not narrate discovery, tool-name selection, or retries. Call the tool first, then respond once with the result. Never claim the server works before a successful tool result. Do not automatically retry a timed-out or abandoned MCP call.' ,
293- ...mothershipTools . map ( ( tool ) => `- ${ tool . name } : ${ tool . description || tool . name } ` ) ,
285+ ...taggedMcpServerIds . map ( ( id ) => `Enabled MCP server: ${ id } ` ) ,
286+ ...selectedMcpToolIds . map ( ( id ) => `Enabled MCP operation: ${ id } ` ) ,
294287 ] . join ( '\n' ) ,
295288 ]
296289 : [ ] ) ,
@@ -310,8 +303,18 @@ export const POST = withRouteHandler(async (req: NextRequest) => {
310303 workspaceId,
311304 chatId : effectiveChatId ,
312305 messageId,
313- ...( integrationTools . length > 0 ? { integrationTools } : { } ) ,
314- ...( mothershipTools . length > 0 ? { mothershipTools } : { } ) ,
306+ integrationCatalog : {
307+ mcpServerIds : taggedMcpServerIds ,
308+ mcpToolIds : selectedMcpToolIds ,
309+ mcpExecution : IntegrationCatalogMcpExecution . parse ( {
310+ workflowId : delegation . workflowId ,
311+ executionId : delegation . executionId ,
312+ mcpBlockId : delegation . mcpBlockId ,
313+ subjectUserId : delegation . subjectUserId ,
314+ ...( delegation . principal ? { principal : serializePrincipal ( delegation . principal ) } : { } ) ,
315+ ...( delegation . currentWorkflow ? { currentWorkflow : delegation . currentWorkflow } : { } ) ,
316+ } ) ,
317+ } ,
315318 }
316319
317320 let allowExplicitAbort = true
@@ -491,7 +494,7 @@ export const POST = withRouteHandler(async (req: NextRequest) => {
491494 send ( {
492495 type : 'final' ,
493496 data : withPrivateProvenance (
494- buildExecuteResponsePayload ( result , effectiveChatId , integrationTools ) ,
497+ buildExecuteResponsePayload ( result , effectiveChatId ) ,
495498 resolvedSecretTraceRegistry ,
496499 includePrivateProvenance
497500 ) ,
@@ -619,7 +622,7 @@ export const POST = withRouteHandler(async (req: NextRequest) => {
619622
620623 return NextResponse . json (
621624 withPrivateProvenance (
622- buildExecuteResponsePayload ( result , effectiveChatId , integrationTools ) ,
625+ buildExecuteResponsePayload ( result , effectiveChatId ) ,
623626 resolvedSecretTraceRegistry ,
624627 includePrivateProvenance
625628 ) ,
0 commit comments