@@ -24,92 +24,62 @@ export async function callMcpTool(
2424 args : unknown = { } ,
2525 { timeoutMs = DEFAULT_TIMEOUT_MS } : { timeoutMs ?: number } = { }
2626) : Promise < string > {
27- const controller = new AbortController ( ) ;
28- const timeout = setTimeout ( ( ) => controller . abort ( ) , timeoutMs ) ;
29-
30- const client = new Client ( { name : "chat-ui-mcp" , version : "0.1.0" } ) ;
3127 const url = toUrl ( server . url ) ;
32-
3328 const normalizedArgs =
3429 typeof args === "object" && args !== null && ! Array . isArray ( args )
3530 ? ( args as Record < string , unknown > )
3631 : undefined ;
3732
38- async function connectStreamable ( ) {
39- const transport = new StreamableHTTPClientTransport ( url , {
40- requestInit : { headers : server . headers } ,
41- } ) ;
33+ async function connect ( kind : "streamable" | "sse" , signal : AbortSignal , client : Client ) {
34+ const requestInit : RequestInit = { headers : server . headers , signal } ;
35+ const transport =
36+ kind === "streamable"
37+ ? new StreamableHTTPClientTransport ( url , { requestInit } )
38+ : new SSEClientTransport ( url , { requestInit } ) ;
4239 await client . connect ( transport ) ;
4340 }
4441
45- async function connectSse ( ) {
46- const transport = new SSEClientTransport ( url , {
47- requestInit : { headers : server . headers } ,
48- } ) ;
49- await client . connect ( transport ) ;
50- }
42+ let lastError : unknown ;
5143
52- try {
53- let usingSse = false ;
54- try {
55- await connectStreamable ( ) ;
56- } catch {
57- await connectSse ( ) ;
58- usingSse = true ;
59- }
44+ for ( const kind of [ "streamable" , "sse" ] as const ) {
45+ const controller = new AbortController ( ) ;
46+ const client = new Client ( { name : "chat-ui-mcp" , version : "0.1.0" } ) ;
47+ const timeout = setTimeout ( ( ) => controller . abort ( ) , timeoutMs ) ;
6048
61- const runTool = async ( ) => client . callTool ( { name : tool , arguments : normalizedArgs } ) ;
62- type CallToolResult = Awaited < ReturnType < typeof runTool > > ;
49+ try {
50+ await connect ( kind , controller . signal , client ) ;
51+ const response = await client . callTool ( { name : tool , arguments : normalizedArgs } ) ;
52+ const parts = Array . isArray ( response ?. content ) ? ( response . content as Array < unknown > ) : [ ] ;
53+ const textParts = parts
54+ . filter ( ( part ) : part is { type : "text" ; text : string } => {
55+ if ( typeof part !== "object" || part === null ) return false ;
56+ const candidate = part as { type ?: unknown ; text ?: unknown } ;
57+ return candidate . type === "text" && typeof candidate . text === "string" ;
58+ } )
59+ . map ( ( part ) => part . text ) ;
6360
64- const raceWithTimeout = < T > ( promise : Promise < T > ) =>
65- Promise . race ( [
66- promise ,
67- new Promise < never > ( ( _ , reject ) => {
68- controller . signal . addEventListener (
69- "abort" ,
70- ( ) => reject ( new Error ( "MCP tool call timed out" ) ) ,
71- {
72- once : true ,
73- }
74- ) ;
75- } ) ,
76- ] ) ;
61+ if ( textParts . length > 0 ) {
62+ return textParts . join ( "\n" ) ;
63+ }
7764
78- let response : CallToolResult ;
79- try {
80- response = await raceWithTimeout ( runTool ( ) ) ;
65+ return JSON . stringify ( response ) ;
8166 } catch ( error ) {
82- if ( usingSse ) throw error ;
67+ lastError = error ;
68+ if ( kind === "sse" ) {
69+ throw error ;
70+ }
71+ } finally {
72+ clearTimeout ( timeout ) ;
73+ controller . abort ( ) ;
8374 try {
8475 await client . close ?.( ) ;
8576 } catch {
8677 // ignore close errors
8778 }
88- await connectSse ( ) ;
89- usingSse = true ;
90- response = await raceWithTimeout ( runTool ( ) ) ;
91- }
92-
93- const parts = Array . isArray ( response ?. content ) ? ( response . content as Array < unknown > ) : [ ] ;
94- const textParts = parts
95- . filter ( ( part ) : part is { type : "text" ; text : string } => {
96- if ( typeof part !== "object" || part === null ) return false ;
97- const candidate = part as { type ?: unknown ; text ?: unknown } ;
98- return candidate . type === "text" && typeof candidate . text === "string" ;
99- } )
100- . map ( ( part ) => part . text ) ;
101-
102- if ( textParts . length > 0 ) {
103- return textParts . join ( "\n" ) ;
104- }
105-
106- return JSON . stringify ( response ) ;
107- } finally {
108- clearTimeout ( timeout ) ;
109- try {
110- await client . close ?.( ) ;
111- } catch {
112- // ignore close errors
11379 }
11480 }
81+
82+ throw lastError instanceof Error
83+ ? lastError
84+ : new Error ( String ( lastError ?? "MCP tool call failed" ) ) ;
11585}
0 commit comments