@@ -21,7 +21,7 @@ import { randomBytes } from "node:crypto";
2121import { createServer } from "node:http" ;
2222
2323import { expect } from "@effect/vitest" ;
24- import { Effect } from "effect" ;
24+ import { Effect , Schema } from "effect" ;
2525import { composePluginApi } from "@executor-js/api/server" ;
2626import { openApiHttpPlugin } from "@executor-js/plugin-openapi/api" ;
2727import {
@@ -38,6 +38,13 @@ import type { McpSession } from "../src/surfaces/mcp";
3838
3939const api = composePluginApi ( [ openApiHttpPlugin ( ) ] as const ) ;
4040
41+ const completion = Schema . Struct ( {
42+ structuredContent : Schema . Struct ( {
43+ status : Schema . String ,
44+ toolName : Schema . optional ( Schema . String ) ,
45+ } ) ,
46+ } ) ;
47+
4148const unique = ( prefix : string ) => `${ prefix } _${ randomBytes ( 4 ) . toString ( "hex" ) } ` ;
4249
4350type UpstreamHandle = {
@@ -136,6 +143,13 @@ const widgetsSpec = (baseUrl: string): string =>
136143 info : { title : "Widgets API" , version : "1.0.0" } ,
137144 servers : [ { url : baseUrl } ] ,
138145 paths : {
146+ "/widgets/count" : {
147+ get : {
148+ operationId : "countWidgets" ,
149+ summary : "Count widgets" ,
150+ responses : { "200" : { description : "widget count" } } ,
151+ } ,
152+ } ,
139153 "/widgets" : {
140154 get : {
141155 operationId : "listWidgets" ,
@@ -218,6 +232,47 @@ scenario(
218232 "toolPaths" ,
219233 ) ;
220234 expect ( upstream . requests ( ) , "the upstream served exactly one call" ) . toBe ( 1 ) ;
235+ const discovery = yield * executeApproved (
236+ session ,
237+ 'return await tools.search({ query: "widgets" });' ,
238+ ) ;
239+ const discoveryResult = yield * Schema . decodeUnknownEffect ( completion ) ( discovery . raw ) ;
240+ expect ( discoveryResult . structuredContent . toolName ) . toBeUndefined ( ) ;
241+ expect ( discovery . raw ) . not . toHaveProperty ( "structuredContent.toolPaths" ) ;
242+
243+ const failed = yield * executeApproved (
244+ session ,
245+ invokeByAddressCode ( address ! . replace ( / l i s t W i d g e t s $ / , "missingWidget" ) , { } ) ,
246+ ) ;
247+ const failedResult = yield * Schema . decodeUnknownEffect ( completion ) ( failed . raw ) ;
248+ expect ( failedResult . structuredContent . toolName ) . toBeUndefined ( ) ;
249+ expect ( failed . raw ) . not . toHaveProperty ( "structuredContent.toolPaths" ) ;
250+
251+ const repeated = yield * executeApproved (
252+ session ,
253+ `
254+ await tools[${ JSON . stringify ( path ) } ]({});
255+ return await tools[${ JSON . stringify ( path ) } ]({});
256+ ` ,
257+ ) ;
258+ const repeatedResult = yield * Schema . decodeUnknownEffect ( completion ) ( repeated . raw ) ;
259+ expect ( repeatedResult . structuredContent . toolName ) . toBe ( path ) ;
260+ expect ( repeated . raw ) . not . toHaveProperty ( "structuredContent.toolPaths" ) ;
261+ const anotherAddress = tools
262+ . filter ( ( tool ) => String ( tool . integration ) === slug )
263+ . map ( ( tool ) => String ( tool . address ) )
264+ . find ( ( candidate ) => candidate . endsWith ( "countWidgets" ) ) ;
265+ const anotherPath = yield * Schema . decodeUnknownEffect ( Schema . String ) ( anotherAddress ) ;
266+ const multiple = yield * executeApproved (
267+ session ,
268+ `
269+ await tools[${ JSON . stringify ( path ) } ]({});
270+ return await tools[${ JSON . stringify ( anotherPath . replace ( / ^ t o o l s \. / , "" ) ) } ]({});
271+ ` ,
272+ ) ;
273+ const multipleResult = yield * Schema . decodeUnknownEffect ( completion ) ( multiple . raw ) ;
274+ expect ( multipleResult . structuredContent . toolName ) . toBeUndefined ( ) ;
275+ expect ( multiple . raw ) . not . toHaveProperty ( "structuredContent.toolPaths" ) ;
221276
222277 // 2a. A wrong TOOL name on a live connection: tool_not_found, and the
223278 // suggestions name the connection's real tools so the agent can
@@ -267,7 +322,9 @@ scenario(
267322 "the defect mask never surfaces for a missing connection" ,
268323 ) . not . toContain ( "Internal tool error" ) ;
269324
270- expect ( upstream . requests ( ) , "no misaddressed call ever reached the upstream" ) . toBe ( 1 ) ;
325+ expect ( upstream . requests ( ) , "only the five successful calls reached the upstream" ) . toBe (
326+ 5 ,
327+ ) ;
271328 } ) ,
272329 // Selfhost shares one workspace identity — leaked resources fail other
273330 // scenarios' zero-state assertions.
0 commit comments