2026-07-03 05:28:34 +00:00
import * as Tool from "./tool"
2026-07-03 08:21:42 +00:00
import { CallToolResultSchema , type CallToolResult } from "@modelcontextprotocol/sdk/types.js"
2026-07-03 05:28:34 +00:00
import { Cause , Effect , Schema } from "effect"
import {
CodeMode ,
Tool as SandboxTool ,
toolError ,
type ExecuteResult ,
type JsonSchema ,
type ToolDefinition ,
} from "@opencode-ai/codemode"
import { MCP } from "@/mcp"
import { McpCatalog } from "@/mcp/catalog"
import { Agent } from "@/agent/agent"
import { Session } from "@/session/session"
import { Permission } from "@/permission"
import { Plugin } from "@/plugin"
export const CODE_MODE_TOOL = "execute"
const DESCRIPTION = [
"Execute a JavaScript/TypeScript program that orchestrates the connected MCP tools inside a confined runtime." ,
"The full usage guide and the catalog of available tools follow below." ,
] . join ( "\n" )
export const Parameters = Schema . Struct ( {
code : Schema.String.annotate ( {
description : [
"JavaScript source to execute." ,
"Inside CodeMode, `tools` contains only the MCP/CodeMode tools listed in this execute tool's description; top-level opencode tools like bash, read, or lsp are not available unless listed there." ,
"Call available tools using the exact signatures shown in this execute tool's description, compose the results, and `return` the final value." ,
] . join ( " " ) ,
} ) ,
} )
2026-07-03 06:57:12 +00:00
type CallEntry = { tool : string ; status : "running" | "completed" | "error" ; input? : Record < string , unknown > }
2026-07-03 05:28:34 +00:00
type Metadata = {
toolCalls : CallEntry [ ]
error? : boolean
}
2026-07-03 06:57:12 +00:00
type Attachment = NonNullable < Tool.ExecuteResult [ " attachments " ] > [ number ]
2026-07-03 05:28:34 +00:00
2026-07-03 06:57:12 +00:00
type CatalogEntry = {
2026-07-03 05:28:34 +00:00
path : string
key : string
server : string
local : string
2026-07-03 08:21:42 +00:00
tool : MCP.McpTool
2026-07-03 05:28:34 +00:00
}
const toJsonSchema = ( schema : unknown ) : JsonSchema = > schema as JsonSchema
2026-07-03 08:21:42 +00:00
function groupByServer ( mcpTools : Record < string , MCP.McpTool > , servers : readonly string [ ] ) : Map < string , CatalogEntry [ ] > {
2026-07-03 05:28:34 +00:00
const byLongest = [ . . . servers ] . sort ( ( a , b ) = > b . length - a . length )
const groups = new Map < string , CatalogEntry [ ] > ( )
for ( const key of Object . keys ( mcpTools ) . sort ( ( a , b ) = > a . localeCompare ( b ) ) ) {
2026-07-03 06:57:12 +00:00
const server =
byLongest . find ( ( name ) = > key . startsWith ( name + "_" ) ) ? ? ( key . includes ( "_" ) ? key . slice ( 0 , key . indexOf ( "_" ) ) : key )
2026-07-03 05:28:34 +00:00
const local = server && key . startsWith ( server + "_" ) ? key . slice ( server . length + 1 ) : key
const entry : CatalogEntry = {
path : ` ${ server } . ${ local } ` ,
key ,
server ,
local ,
2026-07-03 08:21:42 +00:00
tool : mcpTools [ key ] ! ,
2026-07-03 05:28:34 +00:00
}
groups . set ( server , [ . . . ( groups . get ( server ) ? ? [ ] ) , entry ] )
}
return groups
}
2026-07-03 08:21:42 +00:00
export function describeCatalog ( mcpTools : Record < string , MCP.McpTool > , servers : readonly string [ ] ) : string {
2026-07-03 05:28:34 +00:00
return CodeMode . make ( {
2026-07-03 06:39:25 +00:00
tools : toolTree (
2026-07-03 08:21:42 +00:00
[ . . . groupByServer ( mcpTools , servers ) . values ( ) ] . flat ( ) ,
2026-07-03 06:39:25 +00:00
( ) = > ( ) = > Effect . fail ( toolError ( "Tool preview is not executable." ) ) ,
) ,
2026-07-03 05:28:34 +00:00
} ) . instructions ( )
}
const lastSegment = ( uri : string ) = > {
const trimmed = uri . split ( /[?#]/ , 1 ) [ 0 ] ! . replace ( /\/+$/ , "" )
const segment = trimmed . slice ( trimmed . lastIndexOf ( "/" ) + 1 )
return segment . length > 0 ? segment : undefined
}
const dataUrl = ( mime : string , base64 : string ) = > ` data: ${ mime } ;base64, ${ base64 } `
2026-07-03 08:21:42 +00:00
function projectMcpResult ( result : CallToolResult , collect : ( attachment : Attachment ) = > void ) : unknown {
2026-07-03 05:28:34 +00:00
const text : string [ ] = [ ]
let files = 0
let images = 0
const push = ( attachment : Attachment ) = > {
files += 1
if ( attachment . mime . startsWith ( "image/" ) ) images += 1
collect ( attachment )
}
2026-07-03 08:21:42 +00:00
for ( const block of result . content ) {
2026-07-03 05:28:34 +00:00
switch ( block . type ) {
case "text" :
2026-07-03 08:21:42 +00:00
text . push ( block . text )
2026-07-03 05:28:34 +00:00
break
case "image" :
case "audio" :
2026-07-03 08:21:42 +00:00
push ( { type : "file" , mime : block.mimeType , url : dataUrl ( block . mimeType , block . data ) } )
2026-07-03 05:28:34 +00:00
break
case "resource" : {
2026-07-03 08:21:42 +00:00
if ( "text" in block . resource ) {
text . push ( block . resource . text )
break
2026-07-03 05:28:34 +00:00
}
2026-07-03 08:21:42 +00:00
const mime = block . resource . mimeType ? ? "application/octet-stream"
push ( { type : "file" , mime , url : dataUrl ( mime , block . resource . blob ) , filename : lastSegment ( block . resource . uri ) } )
2026-07-03 05:28:34 +00:00
break
}
case "resource_link" :
2026-07-03 08:21:42 +00:00
push ( {
type : "file" ,
mime : block.mimeType ? ? "application/octet-stream" ,
url : block.uri ,
filename : block.name ,
} )
2026-07-03 05:28:34 +00:00
break
}
}
2026-07-03 08:21:42 +00:00
if ( result . structuredContent !== undefined && result . structuredContent !== null ) return result . structuredContent
2026-07-03 05:28:34 +00:00
if ( text . length > 0 ) return text . join ( "\n" )
2026-07-03 07:09:58 +00:00
if ( files > 0 ) {
const noun = files === images ? "image" : "file"
return ` [ ${ files } ${ noun } ${ files === 1 ? "" : "s" } attached to the result] `
}
2026-07-03 08:21:42 +00:00
return null
2026-07-03 05:28:34 +00:00
}
type Run = ( input : unknown ) = > Effect . Effect < unknown , unknown >
function toolTree ( catalog : readonly CatalogEntry [ ] , run : ( entry : CatalogEntry ) = > Run ) {
const tree : Record < string , Record < string , ToolDefinition > > = { }
for ( const entry of catalog ) {
const namespace = ( tree [ entry . server ] ? ? = { } )
namespace [ entry . local ] = SandboxTool . make ( {
2026-07-03 08:21:42 +00:00
description : entry.tool.def.description ? ? "" ,
input : toJsonSchema ( entry . tool . def . inputSchema ) ,
output : entry.tool.def.outputSchema ? toJsonSchema ( entry . tool . def . outputSchema ) : undefined ,
2026-07-03 05:28:34 +00:00
run : run ( entry ) ,
} )
}
return tree
}
2026-07-03 08:21:42 +00:00
const invokeChildTool = Effect . fn ( "CodeMode.invokeChildTool" ) ( function * ( input : {
2026-07-03 05:51:28 +00:00
plugin : Plugin.Interface
entry : CatalogEntry
2026-07-03 08:21:42 +00:00
args : Record < string , unknown >
2026-07-03 05:51:28 +00:00
callID : string
ctx : Tool.Context
} ) {
yield * input . plugin . trigger (
"tool.execute.before" ,
{ tool : input.entry.key , sessionID : input.ctx.sessionID , callID : input.callID } ,
{ args : input.args } ,
)
2026-07-03 08:21:42 +00:00
const result : CallToolResult = yield * Effect . gen ( function * ( ) {
2026-07-03 05:51:28 +00:00
yield * input . ctx . ask ( { permission : input.entry.key , metadata : { } , patterns : [ "*" ] , always : [ "*" ] } )
2026-07-03 08:21:42 +00:00
// Deliberately mirrors McpCatalog.convertTool's transport call so the MCP service stays free of tool-loop concerns.
return yield * Effect . promise ( async ( ) = > {
const raw = await input . entry . tool . client . callTool (
{ name : input.entry.tool.def.name , arguments : input.args } ,
CallToolResultSchema ,
{
resetTimeoutOnProgress : true ,
signal : input.ctx.abort ,
timeout : input.entry.tool.timeout ,
// The MCP SDK only sends a progress token when this hook is present, enabling timeout resets.
onprogress : ( ) = > { } ,
} ,
)
if ( raw . isError )
throw new Error (
raw . content
. flatMap ( ( item ) = > ( item . type === "text" ? [ item . text ] : [ ] ) )
. filter ( ( text ) = > text . trim ( ) )
. join ( "\n\n" ) || "MCP tool returned an error" ,
)
return raw
} )
2026-07-03 05:51:28 +00:00
} ) . pipe (
Effect . withSpan ( "Tool.execute" , {
attributes : {
"tool.name" : input . entry . key ,
"tool.call_id" : input . callID ,
"session.id" : input . ctx . sessionID ,
"message.id" : input . ctx . messageID ,
} ,
} ) ,
)
yield * input . plugin . trigger (
"tool.execute.after" ,
{ tool : input.entry.key , sessionID : input.ctx.sessionID , callID : input.callID , args : input.args } ,
result ,
)
return result
} )
2026-07-03 05:28:34 +00:00
export const CodeModeTool = Tool . define (
CODE_MODE_TOOL ,
Effect . gen ( function * ( ) {
const mcp = yield * MCP . Service
const agents = yield * Agent . Service
const sessions = yield * Session . Service
const plugin = yield * Plugin . Service
const init : Tool.DefWithoutID < typeof Parameters , Metadata > = {
description : DESCRIPTION ,
parameters : Parameters ,
execute : Effect.fn ( "CodeMode.execute" ) ( function * ( params , ctx ) {
if ( ctx . abort . aborted ) {
return {
title : CODE_MODE_TOOL ,
metadata : { toolCalls : [ ] , error : true } ,
output : "Execution cancelled." ,
} satisfies Tool . ExecuteResult < Metadata >
}
const agent = yield * agents . get ( ctx . agent )
const session = yield * sessions . get ( ctx . sessionID ) . pipe ( Effect . orDie )
const ruleset = Permission . merge ( agent . permission , session . permission ? ? [ ] )
const mcpTools = Permission . visibleTools ( yield * mcp . tools ( ) , ruleset )
const servers = Object . keys ( yield * mcp . clients ( ) ) . map ( McpCatalog . sanitize )
2026-07-03 08:21:42 +00:00
const catalog = [ . . . groupByServer ( mcpTools , servers ) . values ( ) ] . flat ( )
2026-07-03 05:28:34 +00:00
const calls : CallEntry [ ] = [ ]
const attachments : Attachment [ ] = [ ]
const collect = ( attachment : Attachment ) = > void attachments . push ( attachment )
2026-07-03 06:57:12 +00:00
const publish = ( ) = >
ctx . metadata ( { title : CODE_MODE_TOOL , metadata : { toolCalls : calls.map ( ( c ) = > ( { . . . c } ) ) } } )
2026-07-03 05:28:34 +00:00
let childCalls = 0
const callTool = ( entry : CatalogEntry ) = > ( input : unknown ) = >
2026-07-03 06:57:12 +00:00
Effect . gen ( function * ( ) {
childCalls += 1
2026-07-03 08:21:42 +00:00
const result = yield * invokeChildTool ( {
2026-07-03 06:57:12 +00:00
plugin ,
entry ,
2026-07-03 08:21:42 +00:00
args : ( input ? ? { } ) as Record < string , unknown > ,
2026-07-03 06:57:12 +00:00
callID : ` ${ ctx . callID ? ? entry . key } / ${ childCalls } ` ,
ctx ,
} )
2026-07-03 08:21:42 +00:00
return projectMcpResult ( result , collect )
2026-07-03 06:57:12 +00:00
} ) . pipe (
Effect . catchCause ( ( cause ) = > {
if ( Cause . hasInterruptsOnly ( cause ) ) return Effect . interrupt
const error = Cause . squash ( cause )
return Effect . fail ( toolError ( error instanceof Error ? error.message : String ( error ) , error ) )
2026-07-03 05:28:34 +00:00
} ) ,
)
const runtime = CodeMode . make ( {
tools : toolTree ( catalog , callTool ) ,
onToolCallStart : ( { index , name , input } ) = >
Effect . suspend ( ( ) = > {
2026-07-03 07:09:58 +00:00
const shown = ( ( ) = > {
if ( input === null || input === undefined ) return
if ( typeof input === "object" && ! Array . isArray ( input ) ) {
const value = input as Record < string , unknown >
return Object . keys ( value ) . length > 0 ? value : undefined
}
return { input }
} ) ( )
2026-07-03 05:28:34 +00:00
calls [ index ] = { tool : name , status : "running" , . . . ( shown ? { input : shown } : { } ) }
return publish ( )
} ) ,
onToolCallEnd : ( { index , outcome } ) = >
Effect . suspend ( ( ) = > {
const current = calls [ index ]
if ( current ) calls [ index ] = { . . . current , status : outcome === "success" ? "completed" : "error" }
return publish ( )
} ) ,
} )
2026-07-03 07:09:58 +00:00
const abort = Effect . callback < void > ( ( resume ) = > {
if ( ctx . abort . aborted ) return resume ( Effect . void )
const handler = ( ) = > resume ( Effect . void )
ctx . abort . addEventListener ( "abort" , handler , { once : true } )
return Effect . sync ( ( ) = > ctx . abort . removeEventListener ( "abort" , handler ) )
} )
const cancelled = ( ) : ExecuteResult = > ( {
ok : false ,
error : { kind : "ExecutionFailure" , message : "Execution cancelled." } ,
toolCalls : calls.map ( ( call ) = > ( { name : call.tool } ) ) ,
2026-07-03 05:28:34 +00:00
} )
2026-07-03 07:09:58 +00:00
const result = yield * Effect . raceFirst (
runtime . execute ( params . code ) ,
abort . pipe ( Effect . map ( cancelled ) ) ,
)
2026-07-03 05:28:34 +00:00
const logs = result . logs ? ? [ ]
const attached = attachments . length > 0 ? { attachments } : { }
2026-07-03 06:57:12 +00:00
const hints = result . ok
? [ ]
: ( result . error . suggestions ? ? [ ] ) . filter ( ( hint ) = > ! result . error . message . includes ( hint ) )
const metadata : Metadata = result . ok ? { toolCalls : calls } : { toolCalls : calls , error : true }
let output : string
2026-07-03 05:28:34 +00:00
if ( result . ok ) {
2026-07-03 06:57:12 +00:00
if ( typeof result . value === "string" ) output = result . value
else if ( result . value === undefined ) output = "undefined"
else {
try {
output = JSON . stringify ( result . value , null , 2 ) ? ? String ( result . value )
} catch {
output = String ( result . value )
}
}
} else {
output = [ result . error . message , . . . hints ] . join ( "\n" )
2026-07-03 05:28:34 +00:00
}
2026-07-03 06:57:12 +00:00
if ( logs . length > 0 )
output = output . length > 0 ? ` ${ output } \ n \ nLogs: \ n ${ logs . join ( "\n" ) } ` : ` Logs: \ n ${ logs . join ( "\n" ) } `
2026-07-03 05:28:34 +00:00
return {
title : CODE_MODE_TOOL ,
2026-07-03 06:57:12 +00:00
metadata ,
output ,
2026-07-03 05:28:34 +00:00
. . . attached ,
} satisfies Tool . ExecuteResult < Metadata >
} ) ,
}
return init
} ) ,
)