@@ -91,7 +91,7 @@ describe("doctor runtime tool schema checks", () => {
9191 checkId : "core/doctor/runtime-tool-schemas" ,
9292 severity : "error" ,
9393 message :
94- "Tool dofbot__dofbot_move_angles from plugin bundle-mcp has an unsupported input schema for runtime projection." ,
94+ "Agent main tool dofbot__dofbot_move_angles from plugin bundle-mcp has an unsupported input schema for runtime projection." ,
9595 path : "mcp.servers" ,
9696 target : "dofbot__dofbot_move_angles" ,
9797 requirement : 'dofbot__dofbot_move_angles.parameters.type must be "object"' ,
@@ -101,6 +101,128 @@ describe("doctor runtime tool schema checks", () => {
101101 expect ( mocks . disposeBundleRuntime ) . toHaveBeenCalledTimes ( 1 ) ;
102102 } ) ;
103103
104+ it ( "reports unsupported schemas exposed only to a non-default configured agent" , async ( ) => {
105+ mocks . createOpenClawCodingTools . mockImplementation ( ( options ) =>
106+ options ?. agentId === "worker"
107+ ? [ tool ( "dofbot_move_angles" , { type : "array" , items : { type : "number" } } ) ]
108+ : [ tool ( "healthy" , { type : "object" , properties : { } } ) ] ,
109+ ) ;
110+
111+ await expect (
112+ collectRuntimeToolSchemaFindings ( {
113+ agents : {
114+ list : [
115+ { id : "main" , default : true , workspace : "/tmp/shared-workspace" } ,
116+ { id : "worker" , workspace : "/tmp/shared-workspace" } ,
117+ ] ,
118+ } ,
119+ } ) ,
120+ ) . resolves . toContainEqual ( {
121+ checkId : "core/doctor/runtime-tool-schemas" ,
122+ severity : "error" ,
123+ message :
124+ "Agent worker tool dofbot_move_angles has an unsupported input schema for runtime projection." ,
125+ path : "tools.dofbot_move_angles" ,
126+ target : "dofbot_move_angles" ,
127+ requirement : 'dofbot_move_angles.parameters.type must be "object"' ,
128+ fixHint :
129+ "Disable or update the offending plugin/tool so its parameters are a JSON object schema, then rerun doctor." ,
130+ } ) ;
131+ expect ( mocks . createOpenClawCodingTools ) . toHaveBeenCalledWith (
132+ expect . objectContaining ( { agentId : "main" } ) ,
133+ ) ;
134+ expect ( mocks . createOpenClawCodingTools ) . toHaveBeenCalledWith (
135+ expect . objectContaining ( { agentId : "worker" } ) ,
136+ ) ;
137+ expect ( mocks . createBundleMcpToolRuntime ) . toHaveBeenCalledTimes ( 1 ) ;
138+ expect ( mocks . disposeBundleRuntime ) . toHaveBeenCalledTimes ( 1 ) ;
139+ } ) ;
140+
141+ it ( "skips ACP-only agents because they do not use embedded tool projection" , async ( ) => {
142+ mocks . createOpenClawCodingTools . mockImplementation ( ( options ) =>
143+ options ?. agentId === "acp-worker"
144+ ? [ tool ( "dofbot_move_angles" , { type : "array" , items : { type : "number" } } ) ]
145+ : [ tool ( "healthy" , { type : "object" , properties : { } } ) ] ,
146+ ) ;
147+ mocks . createBundleMcpToolRuntime . mockImplementation (
148+ async ( options : { workspaceDir : string } ) => ( {
149+ tools : options . workspaceDir . includes ( "acp" )
150+ ? [ bundleMcpTool ( "dofbot__bad" , { type : "array" , items : { type : "number" } } ) ]
151+ : [ ] ,
152+ dispose : mocks . disposeBundleRuntime ,
153+ } ) ,
154+ ) ;
155+
156+ await expect (
157+ collectRuntimeToolSchemaFindings ( {
158+ agents : {
159+ list : [
160+ { id : "main" , default : true , workspace : "/tmp/main-workspace" } ,
161+ {
162+ id : "acp-worker" ,
163+ workspace : "/tmp/acp-workspace" ,
164+ runtime : { type : "acp" } ,
165+ } ,
166+ ] ,
167+ } ,
168+ } ) ,
169+ ) . resolves . toEqual ( [ ] ) ;
170+ expect ( mocks . createOpenClawCodingTools ) . toHaveBeenCalledTimes ( 1 ) ;
171+ expect ( mocks . createOpenClawCodingTools ) . toHaveBeenCalledWith (
172+ expect . objectContaining ( { agentId : "main" } ) ,
173+ ) ;
174+ expect ( mocks . createBundleMcpToolRuntime ) . toHaveBeenCalledTimes ( 1 ) ;
175+ expect ( mocks . createBundleMcpToolRuntime ) . toHaveBeenCalledWith (
176+ expect . objectContaining ( { workspaceDir : expect . stringContaining ( "main-workspace" ) } ) ,
177+ ) ;
178+ } ) ;
179+
180+ it ( "loads bundled MCP runtime once per distinct agent workspace" , async ( ) => {
181+ mocks . createOpenClawCodingTools . mockReturnValue ( [ ] ) ;
182+ mocks . createBundleMcpToolRuntime . mockImplementation (
183+ async ( options : { workspaceDir : string } ) => ( {
184+ tools : options . workspaceDir . includes ( "worker" )
185+ ? [
186+ bundleMcpTool ( "dofbot__dofbot_move_angles" , {
187+ type : "array" ,
188+ items : { type : "number" } ,
189+ } ) ,
190+ ]
191+ : [ bundleMcpTool ( "healthy" , { type : "object" , properties : { } } ) ] ,
192+ dispose : mocks . disposeBundleRuntime ,
193+ } ) ,
194+ ) ;
195+
196+ await expect (
197+ collectRuntimeToolSchemaFindings ( {
198+ agents : {
199+ list : [
200+ { id : "main" , default : true , workspace : "/tmp/main-workspace" } ,
201+ { id : "worker" , workspace : "/tmp/worker-workspace" } ,
202+ ] ,
203+ } ,
204+ } ) ,
205+ ) . resolves . toContainEqual ( {
206+ checkId : "core/doctor/runtime-tool-schemas" ,
207+ severity : "error" ,
208+ message :
209+ "Agent worker tool dofbot__dofbot_move_angles from plugin bundle-mcp has an unsupported input schema for runtime projection." ,
210+ path : "mcp.servers" ,
211+ target : "dofbot__dofbot_move_angles" ,
212+ requirement : 'dofbot__dofbot_move_angles.parameters.type must be "object"' ,
213+ fixHint :
214+ "Disable or update the offending MCP server/tool so its parameters are a JSON object schema, then rerun doctor." ,
215+ } ) ;
216+ expect ( mocks . createBundleMcpToolRuntime ) . toHaveBeenCalledTimes ( 2 ) ;
217+ expect ( mocks . createBundleMcpToolRuntime ) . toHaveBeenCalledWith (
218+ expect . objectContaining ( { workspaceDir : expect . stringContaining ( "main-workspace" ) } ) ,
219+ ) ;
220+ expect ( mocks . createBundleMcpToolRuntime ) . toHaveBeenCalledWith (
221+ expect . objectContaining ( { workspaceDir : expect . stringContaining ( "worker-workspace" ) } ) ,
222+ ) ;
223+ expect ( mocks . disposeBundleRuntime ) . toHaveBeenCalledTimes ( 2 ) ;
224+ } ) ;
225+
104226 it ( "does not report bundle MCP schemas filtered out by the final runtime tool policy" , async ( ) => {
105227 mocks . createBundleMcpToolRuntime . mockResolvedValueOnce ( {
106228 tools : [
0 commit comments