@@ -27,6 +27,7 @@ const mockPosixTools = {
2727// mock.module below runs, making the "restore" a no-op.
2828const realToolsPosix = { ...( await import ( "@intx/tools-posix" ) ) } ;
2929const realPosixToolPlugins = { ...( await import ( "../../../src/agent/posix-tool-plugins.js" ) ) } ;
30+ const realMcpClient = { ...( await import ( "../../../src/mcp/client.js" ) ) } ;
3031const realMcpPlugin = { ...( await import ( "../../../src/mcp/plugin.js" ) ) } ;
3132const realPathEscapePlugin = { ...( await import ( "../../../src/plugins/path-escape-plugin.js" ) ) } ;
3233const realAuthzPlugin = { ...( await import ( "../../../src/plugins/authz-plugin.js" ) ) } ;
@@ -47,6 +48,17 @@ mock.module("../../../src/agent/posix-tool-plugins.js", () => ({
4748 buildCorePosixToolPlugins : ( ) => [ ] ,
4849} ) ) ;
4950
51+ const mockConnectMCPServer = mock ( async ( config : { name : string } ) => ( {
52+ ok : false as const ,
53+ serverName : config . name ,
54+ error : "not connected" ,
55+ } ) ) ;
56+
57+ mock . module ( "../../../src/mcp/client.js" , ( ) => ( {
58+ ...realMcpClient ,
59+ connectMCPServer : mockConnectMCPServer ,
60+ } ) ) ;
61+
5062mock . module ( "../../../src/mcp/plugin.js" , ( ) => ( {
5163 mcpClientToAgentTools : ( ) => [ ] ,
5264} ) ) ;
@@ -113,6 +125,7 @@ mock.module("../../../src/agent/director.js", () => ({
113125afterAll ( ( ) => {
114126 mock . module ( "@intx/tools-posix" , ( ) => realToolsPosix ) ;
115127 mock . module ( "../../../src/agent/posix-tool-plugins.js" , ( ) => realPosixToolPlugins ) ;
128+ mock . module ( "../../../src/mcp/client.js" , ( ) => realMcpClient ) ;
116129 mock . module ( "../../../src/mcp/plugin.js" , ( ) => realMcpPlugin ) ;
117130 mock . module ( "../../../src/plugins/path-escape-plugin.js" , ( ) => realPathEscapePlugin ) ;
118131 mock . module ( "../../../src/plugins/authz-plugin.js" , ( ) => realAuthzPlugin ) ;
@@ -304,6 +317,26 @@ test("default session registers task and search_agents", async () => {
304317 expect ( names ) . toContain ( "search_agents" ) ;
305318} ) ;
306319
320+ test ( "headless MCP connection does not wait for interactive OAuth" , async ( ) => {
321+ mockConnectMCPServer . mockClear ( ) ;
322+ const toolset = await createAgentToolset ( {
323+ cwd : "/fake" ,
324+ permissionGate : fakePermissionGate ,
325+ onOperatorGate : async ( ) => ( { kind : "cancel" } ) ,
326+ mcpServers : [ { name : "granola" , url : "https://example.test/mcp" } ] ,
327+ mcpServersSource : "global" ,
328+ } ) ;
329+
330+ await toolset . connectMCP ( {
331+ interactiveAuth : false ,
332+ onStatus : ( ) => { } ,
333+ onToolsChanged : ( ) => { } ,
334+ } ) ;
335+
336+ expect ( mockConnectMCPServer ) . toHaveBeenCalledTimes ( 1 ) ;
337+ expect ( mockConnectMCPServer . mock . calls [ 0 ] ?. [ 1 ] ?. onAuthURL ) . toBeUndefined ( ) ;
338+ } ) ;
339+
307340test ( "dispose calls posixTools.dispose" , async ( ) => {
308341 mockDispose . mockClear ( ) ;
309342
0 commit comments