11import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" ;
22import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" ;
3- import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js" ;
4- import type { CallToolResult , JSONRPCMessage , ServerNotification , ServerRequest } from "@modelcontextprotocol/sdk/types.js" ;
3+ import type { CallToolResult , JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js" ;
54import { z } from "zod" ;
65import {
76 defaultModel ,
@@ -28,6 +27,7 @@ import { jobManager, runQueuedAgent, runQueuedAgents } from "./jobs.js";
2827import { cleanupRuntime , lifecycleStats , registerCleanupHandler } from "./lifecycle.js" ;
2928import { errorForLog , logger , loggingDiagnostics , makeLogId , summarizeRawTrafficForLog } from "./logging.js" ;
3029import { recoveryForAgentResult , recoveryForError , recoveryForWait } from "./recovery.js" ;
30+ import { createProgressReporter , type ProgressOptions , type ProgressReporter , type ToolExtra } from "./progress.js" ;
3131import {
3232 compactAgentResultForMcp ,
3333 compactAgentResultsForMcp ,
@@ -37,9 +37,6 @@ import {
3737import { sessionManager } from "./sessions.js" ;
3838import { modelPresets } from "./subagents.js" ;
3939
40- type ToolExtra = RequestHandlerExtra < ServerRequest , ServerNotification > ;
41- type ProgressReporter = ReturnType < typeof createProgressReporter > ;
42-
4340const usageGuide = [
4441 "Claude Code integration guide for codex-subagents:" ,
4542 "" ,
@@ -390,13 +387,28 @@ function toCodexSubagents(
390387}
391388
392389function jsonResult ( value : Record < string , unknown > , isError = false ) : CallToolResult {
390+ const fullText = JSON . stringify ( value , null , 2 ) ;
391+ const text =
392+ fullText . length <= 4_000
393+ ? fullText
394+ : JSON . stringify (
395+ {
396+ ok : Boolean ( value . ok ?? ! isError ) ,
397+ isError,
398+ note :
399+ "MCP text content was shortened to keep Claude responsive; use structuredContent for the compacted result." ,
400+ keys : Object . keys ( value ) ,
401+ } ,
402+ null ,
403+ 2 ,
404+ ) ;
393405 return {
394406 structuredContent : value ,
395407 isError,
396408 content : [
397409 {
398410 type : "text" ,
399- text : JSON . stringify ( value , null , 2 ) ,
411+ text,
400412 } ,
401413 ] ,
402414 } ;
@@ -492,60 +504,6 @@ async function loggedToolCall(
492504 }
493505}
494506
495- function createProgressReporter ( extra : ToolExtra | undefined ) {
496- const progressToken = extra ?. _meta ?. progressToken ;
497- let progress = 0 ;
498- let pending = Promise . resolve ( ) ;
499-
500- async function send ( message : string , options : { progress ?: number ; total ?: number } = { } ) {
501- logger . rawDebug ( "mcp.progress" , {
502- hasProgressToken : progressToken !== undefined ,
503- message,
504- options,
505- } ) ;
506- if ( progressToken === undefined || ! extra ) return ;
507-
508- pending = pending
509- . catch ( ( ) => { } )
510- . then ( async ( ) => {
511- const requested = options . progress ?? progress + 1 ;
512- progress = Math . max ( progress + 1 , requested ) ;
513- await extra . sendNotification ( {
514- method : "notifications/progress" ,
515- params : {
516- progressToken,
517- progress,
518- ...( options . total === undefined ? { } : { total : options . total } ) ,
519- message,
520- } ,
521- } ) ;
522- logger . rawDebug ( "mcp.notification.sent" , {
523- method : "notifications/progress" ,
524- params : {
525- progressToken,
526- progress,
527- ...( options . total === undefined ? { } : { total : options . total } ) ,
528- message,
529- } ,
530- } ) ;
531- } )
532- . catch ( ( error ) => {
533- logger . error ( "mcp.notification.failed" , { error : errorForLog ( error ) } ) ;
534- // Progress is best-effort; a failed notification must not fail the tool call.
535- } ) ;
536-
537- await pending ;
538- }
539-
540- async function flush ( ) {
541- if ( progressToken === undefined || ! extra ) return ;
542- await pending ;
543- await new Promise ( ( resolve ) => setTimeout ( resolve , 0 ) ) ;
544- }
545-
546- return { send, flush } ;
547- }
548-
549507function installTransportLogging ( transport : StdioServerTransport ) : void {
550508 const previousOnMessage = transport . onmessage ;
551509 transport . onmessage = ( message ) => {
@@ -590,9 +548,10 @@ async function withProgressHeartbeat<T>(
590548 progress : ProgressReporter ,
591549 message : string ,
592550 operation : ( ) => Promise < T > ,
551+ progressOptions ?: ProgressOptions ,
593552) : Promise < T > {
594553 const interval = setInterval ( ( ) => {
595- void progress . send ( message ) ;
554+ void progress . send ( message , progressOptions ) ;
596555 } , progressHeartbeatMs ( ) ) ;
597556 interval . unref ( ) ;
598557 try {
@@ -662,7 +621,7 @@ function toRunOptions(args: {
662621 skipGitRepoCheck : args . skip_git_repo_check ,
663622 ignoreRules : args . ignore_rules ,
664623 isolatedCodexHome : args . isolated_codex_home ,
665- mcpConfigPolicy : args . mcp_config_policy ,
624+ mcpConfigPolicy : args . mcp_config_policy ?? ( args . codex_mcp_servers ? "explicit" : undefined ) ,
666625 codexMcpServers : args . codex_mcp_servers ,
667626 forwardSensitiveEnv : args . forward_sensitive_env ,
668627 idleTimeoutMs : args . idle_timeout_ms ,
@@ -771,7 +730,10 @@ function toParallelRunOptions(args: ParallelToolInput) {
771730 skipGitRepoCheck : agent . skip_git_repo_check ?? args . skip_git_repo_check ,
772731 ignoreRules : agent . ignore_rules ?? args . ignore_rules ,
773732 isolatedCodexHome : agent . isolated_codex_home ?? args . isolated_codex_home ,
774- mcpConfigPolicy : agent . mcp_config_policy ?? args . mcp_config_policy ,
733+ mcpConfigPolicy :
734+ agent . mcp_config_policy ??
735+ args . mcp_config_policy ??
736+ ( agent . codex_mcp_servers ?? args . codex_mcp_servers ? "explicit" : undefined ) ,
775737 codexMcpServers : agent . codex_mcp_servers ?? args . codex_mcp_servers ,
776738 forwardSensitiveEnv : agent . forward_sensitive_env ?? args . forward_sensitive_env ,
777739 idleTimeoutMs : agent . idle_timeout_ms ?? args . idle_timeout_ms ,
@@ -1198,9 +1160,10 @@ server.registerTool(
11981160 ? `Parallel Codex run completed (${ completed } /${ args . tasks . length } )`
11991161 : `Parallel Codex run finished with errors (${ completed } /${ args . tasks . length } )`
12001162 : `${ result . ok ? "Completed" : "Finished" } ${ result . name ?? "Codex agent" } (${ completed } /${ args . tasks . length } )` ;
1201- await progress . send ( message , last ? { progress : total , total } : { total } ) ;
1163+ await progress . send ( message , last ? { progress : total , total } : { total, reserveFinal : true } ) ;
12021164 } ,
12031165 } ) ,
1166+ { total, reserveFinal : true } ,
12041167 ) ;
12051168 const ok = results . every ( ( result ) => result . ok ) ;
12061169 await progress . flush ( ) ;
@@ -1271,9 +1234,10 @@ server.registerTool(
12711234 ? `Parallel Codex run completed (${ completed } /${ args . agents . length } )`
12721235 : `Parallel Codex run finished with errors (${ completed } /${ args . agents . length } )`
12731236 : `${ result . ok ? "Completed" : "Finished" } ${ result . name ?? "Codex agent" } (${ completed } /${ args . agents . length } )` ;
1274- await progress . send ( message , last ? { progress : total , total } : { total } ) ;
1237+ await progress . send ( message , last ? { progress : total , total } : { total, reserveFinal : true } ) ;
12751238 } ,
12761239 } ) ,
1240+ { total, reserveFinal : true } ,
12771241 ) ;
12781242 const ok = results . every ( ( result ) => result . ok ) ;
12791243 await progress . flush ( ) ;
@@ -1333,10 +1297,11 @@ server.registerTool(
13331297 last
13341298 ? `Aggregating ${ completed } /${ args . agents . length } Codex results`
13351299 : `Completed ${ completed } /${ args . agents . length } Codex agents` ,
1336- last ? { progress : total , total } : { total } ,
1300+ last ? { progress : total , total } : { total, reserveFinal : true } ,
13371301 ) ;
13381302 } ,
13391303 } ) ,
1304+ { total, reserveFinal : true } ,
13401305 ) ;
13411306 const aggregation = aggregateAgentResults ( results ) ;
13421307 await progress . flush ( ) ;
@@ -1451,11 +1416,16 @@ server.registerTool(
14511416 if ( job . completedAt ) await progress . send ( `Codex job ${ job . status } ` ) ;
14521417 await progress . flush ( ) ;
14531418 const waitReason = job . completedAt ? undefined : waitCancelled ? "wait_cancelled" : "wait_timeout" ;
1419+ const completed = Boolean ( job . completedAt ) ;
14541420 return jsonResult (
14551421 {
1422+ completed,
14561423 job : compactJobSnapshotForMcp ( job ) ,
14571424 timeoutReason : waitReason ,
14581425 recovery : recoveryForWait ( "agent_job" , waitReason ) ,
1426+ note : completed
1427+ ? undefined
1428+ : "The Codex job is still managed by this MCP server. Use get_agent_run or wait_agent_run again." ,
14591429 } ,
14601430 waitCancelled || job . status === "failed" || job . status === "cancelled" ,
14611431 ) ;
@@ -1986,7 +1956,9 @@ server.registerTool(
19861956 await progress . send (
19871957 waited . completed
19881958 ? `Codex session ${ args . session_id } is ready`
1989- : `Timed out waiting for Codex session ${ args . session_id } ` ,
1959+ : waited . timeoutReason === "wait_cancelled"
1960+ ? `Cancelled wait for Codex session ${ args . session_id } `
1961+ : `Timed out waiting for Codex session ${ args . session_id } ` ,
19901962 ) ;
19911963 await progress . flush ( ) ;
19921964 if ( waited . error || ! waited . session ) {
0 commit comments