Skip to content

Commit 65e2011

Browse files
committed
chore: pull transport construction into new file
1 parent 173b9db commit 65e2011

File tree

2 files changed

+117
-107
lines changed

2 files changed

+117
-107
lines changed

extensions/cli/src/services/MCPService.ts

Lines changed: 12 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,6 @@
1-
import { Agent as HttpsAgent } from "https";
2-
31
import { decodeFQSN, getTemplateVariables } from "@continuedev/config-yaml";
42
import { type AssistantConfig } from "@continuedev/sdk";
53
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6-
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
7-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
8-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
9-
import {
10-
HttpMcpServer,
11-
SseMcpServer,
12-
StdioMcpServer,
13-
} from "node_modules/@continuedev/config-yaml/dist/schemas/mcp/index.js";
144

155
import { isAuthenticated, loadAuthConfig } from "src/auth/workos.js";
166

@@ -19,6 +9,11 @@ import { getErrorString } from "../util/error.js";
199
import { logger } from "../util/logger.js";
2010

2111
import { BaseService, ServiceWithDependencies } from "./BaseService.js";
12+
import {
13+
constructHttpTransport,
14+
constructSseTransport,
15+
constructStdioTransport,
16+
} from "./mcpTransports.js";
2217
import { isAuthError } from "./mcpUtils.js";
2318
import { serviceContainer } from "./ServiceContainer.js";
2419
import {
@@ -474,7 +469,7 @@ Org-level secrets can only be used for MCP by Background Agents (https://docs.co
474469

475470
if ("command" in serverConfig) {
476471
// STDIO: no need to check type, just if command is present
477-
const transport = this.constructStdioTransport(serverConfig, connection);
472+
const transport = constructStdioTransport(serverConfig, connection);
478473
await client.connect(transport, {});
479474
} else {
480475
// SSE/HTTP: if type isn't explicit: try http and fall back to sse
@@ -483,21 +478,22 @@ Org-level secrets can only be used for MCP by Background Agents (https://docs.co
483478
if (serverConfig.apiKey && !this.apiKeyCache.get(serverConfig.name)) {
484479
this.apiKeyCache.set(serverConfig.name, serverConfig.apiKey);
485480
}
481+
const apiKey = this.apiKeyCache.get(serverConfig.name);
486482
if (serverConfig.type === "sse") {
487-
const transport = this.constructSseTransport(serverConfig);
483+
const transport = constructSseTransport(serverConfig, apiKey);
488484
await client.connect(transport, {});
489485
} else if (serverConfig.type === "streamable-http") {
490-
const transport = this.constructHttpTransport(serverConfig);
486+
const transport = constructHttpTransport(serverConfig, apiKey);
491487
await client.connect(transport, {});
492488
} else {
493489
try {
494-
const transport = this.constructHttpTransport(serverConfig);
490+
const transport = constructHttpTransport(serverConfig, apiKey);
495491
await client.connect(transport, {});
496492
} catch (e) {
497493
if (isAuthError(e)) {
498494
throw e;
499495
}
500-
const transport = this.constructSseTransport(serverConfig);
496+
const transport = constructSseTransport(serverConfig, apiKey);
501497
await client.connect(transport, {});
502498
}
503499
}
@@ -527,7 +523,7 @@ Org-level secrets can only be used for MCP by Background Agents (https://docs.co
527523
);
528524
}
529525

530-
const transport = this.constructStdioTransport(
526+
const transport = constructStdioTransport(
531527
{
532528
name: serverConfig.name,
533529
command: "npx",
@@ -544,95 +540,4 @@ Org-level secrets can only be used for MCP by Background Agents (https://docs.co
544540

545541
return client;
546542
}
547-
548-
private constructSseTransport(
549-
serverConfig: SseMcpServer,
550-
): SSEClientTransport {
551-
const apiKey = this.apiKeyCache.get(serverConfig.name);
552-
const sseAgent =
553-
serverConfig.requestOptions?.verifySsl === false
554-
? new HttpsAgent({ rejectUnauthorized: false })
555-
: undefined;
556-
557-
const headers = {
558-
...serverConfig.requestOptions?.headers,
559-
...(apiKey && {
560-
Authorization: `Bearer ${apiKey}`,
561-
}),
562-
};
563-
564-
return new SSEClientTransport(new URL(serverConfig.url), {
565-
eventSourceInit: {
566-
fetch: (input, init) =>
567-
fetch(input, {
568-
...init,
569-
headers: {
570-
...init?.headers,
571-
...headers,
572-
},
573-
...(sseAgent && { agent: sseAgent }),
574-
}),
575-
},
576-
requestInit: {
577-
headers,
578-
...(sseAgent && { agent: sseAgent }),
579-
},
580-
});
581-
}
582-
private constructHttpTransport(
583-
serverConfig: HttpMcpServer,
584-
): StreamableHTTPClientTransport {
585-
const apiKey = this.apiKeyCache.get(serverConfig.name);
586-
const streamableAgent =
587-
serverConfig.requestOptions?.verifySsl === false
588-
? new HttpsAgent({ rejectUnauthorized: false })
589-
: undefined;
590-
591-
const headers = {
592-
...serverConfig.requestOptions?.headers,
593-
...(apiKey && {
594-
Authorization: `Bearer ${apiKey}`,
595-
}),
596-
};
597-
598-
return new StreamableHTTPClientTransport(new URL(serverConfig.url), {
599-
requestInit: {
600-
headers,
601-
...(streamableAgent && { agent: streamableAgent }),
602-
},
603-
});
604-
}
605-
private constructStdioTransport(
606-
serverConfig: StdioMcpServer,
607-
connection: ServerConnection,
608-
): StdioClientTransport {
609-
const env: Record<string, string> = serverConfig.env || {};
610-
if (process.env) {
611-
for (const [key, value] of Object.entries(process.env)) {
612-
if (!(key in env) && !!value) {
613-
env[key] = value;
614-
}
615-
}
616-
}
617-
618-
const transport = new StdioClientTransport({
619-
command: serverConfig.command,
620-
args: serverConfig.args || [],
621-
env,
622-
cwd: serverConfig.cwd,
623-
stderr: "pipe",
624-
});
625-
626-
const stderrStream = transport.stderr;
627-
if (stderrStream) {
628-
stderrStream.on("data", (data: Buffer) => {
629-
const stderrOutput = data.toString().trim();
630-
if (stderrOutput) {
631-
connection.warnings.push(stderrOutput);
632-
}
633-
});
634-
}
635-
636-
return transport;
637-
}
638543
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { Agent as HttpsAgent } from "https";
2+
3+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
4+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
5+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
6+
import {
7+
HttpMcpServer,
8+
SseMcpServer,
9+
StdioMcpServer,
10+
} from "node_modules/@continuedev/config-yaml/dist/schemas/mcp/index.js";
11+
12+
import { MCPConnectionInfo } from "./types.js";
13+
14+
export function constructSseTransport(
15+
serverConfig: SseMcpServer,
16+
apiKey: string | undefined,
17+
): SSEClientTransport {
18+
const sseAgent =
19+
serverConfig.requestOptions?.verifySsl === false
20+
? new HttpsAgent({ rejectUnauthorized: false })
21+
: undefined;
22+
23+
const headers = {
24+
...serverConfig.requestOptions?.headers,
25+
...(apiKey && {
26+
Authorization: `Bearer ${apiKey}`,
27+
}),
28+
};
29+
30+
return new SSEClientTransport(new URL(serverConfig.url), {
31+
eventSourceInit: {
32+
fetch: (input, init) =>
33+
fetch(input, {
34+
...init,
35+
headers: {
36+
...init?.headers,
37+
...headers,
38+
},
39+
...(sseAgent && { agent: sseAgent }),
40+
}),
41+
},
42+
requestInit: {
43+
headers,
44+
...(sseAgent && { agent: sseAgent }),
45+
},
46+
});
47+
}
48+
49+
export function constructHttpTransport(
50+
serverConfig: HttpMcpServer,
51+
apiKey: string | undefined,
52+
): StreamableHTTPClientTransport {
53+
const streamableAgent =
54+
serverConfig.requestOptions?.verifySsl === false
55+
? new HttpsAgent({ rejectUnauthorized: false })
56+
: undefined;
57+
58+
const headers = {
59+
...serverConfig.requestOptions?.headers,
60+
...(apiKey && {
61+
Authorization: `Bearer ${apiKey}`,
62+
}),
63+
};
64+
65+
return new StreamableHTTPClientTransport(new URL(serverConfig.url), {
66+
requestInit: {
67+
headers,
68+
...(streamableAgent && { agent: streamableAgent }),
69+
},
70+
});
71+
}
72+
73+
export function constructStdioTransport(
74+
serverConfig: StdioMcpServer,
75+
connection: MCPConnectionInfo,
76+
): StdioClientTransport {
77+
const env: Record<string, string> = serverConfig.env || {};
78+
if (process.env) {
79+
for (const [key, value] of Object.entries(process.env)) {
80+
if (!(key in env) && !!value) {
81+
env[key] = value;
82+
}
83+
}
84+
}
85+
86+
const transport = new StdioClientTransport({
87+
command: serverConfig.command,
88+
args: serverConfig.args || [],
89+
env,
90+
cwd: serverConfig.cwd,
91+
stderr: "pipe",
92+
});
93+
94+
const stderrStream = transport.stderr;
95+
if (stderrStream) {
96+
stderrStream.on("data", (data: Buffer) => {
97+
const stderrOutput = data.toString().trim();
98+
if (stderrOutput) {
99+
connection.warnings.push(stderrOutput);
100+
}
101+
});
102+
}
103+
104+
return transport;
105+
}

0 commit comments

Comments
 (0)