Skip to content

Commit 8432774

Browse files
committed
support streamable http
1 parent 9341d03 commit 8432774

File tree

10 files changed

+394
-681
lines changed

10 files changed

+394
-681
lines changed

package-lock.json

Lines changed: 65 additions & 670 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@
407407
"@google-cloud/vertexai": "^1.9.3",
408408
"@google/genai": "^0.9.0",
409409
"@mistralai/mistralai": "^1.3.6",
410-
"@modelcontextprotocol/sdk": "^1.7.0",
410+
"@modelcontextprotocol/sdk": "^1.10.2",
411411
"@types/clone-deep": "^4.0.4",
412412
"@types/pdf-parse": "^1.1.4",
413413
"@types/tmp": "^0.2.6",

src/services/mcp/McpHub.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as vscode from "vscode"
22
import { ClineProvider } from "../../core/webview/ClineProvider"
33
import type { ConfigChangeEvent } from "./config"
44
import { ConfigManager } from "./config"
5-
import { ConnectionFactory, ConnectionManager, SseHandler, StdioHandler } from "./connection"
5+
import { ConnectionFactory, ConnectionManager, SseHandler, StdioHandler, StreamableHttpHandler } from "./connection"
66
import { ConfigSource, McpConnection, McpResourceResponse, McpServer, McpToolCallResponse, ServerConfig } from "./types"
77

88
export class McpHub {
@@ -24,6 +24,7 @@ export class McpHub {
2424
)
2525
connectionFactory.registerHandler(new StdioHandler())
2626
connectionFactory.registerHandler(new SseHandler())
27+
connectionFactory.registerHandler(new StreamableHttpHandler())
2728

2829
this.connectionManager = new ConnectionManager(this.configManager, connectionFactory)
2930

src/services/mcp/__tests__/McpHub.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,10 @@ describe("McpHub", () => {
112112
mockConfigManager.updateServerConfig = jest.fn().mockResolvedValue(undefined)
113113

114114
// Mock ConnectionFactory
115-
mockConnectionFactory = new ConnectionFactory(mockConfigManager) as jest.Mocked<ConnectionFactory>
115+
mockConnectionFactory = new ConnectionFactory(
116+
mockConfigManager,
117+
mockProvider as ClineProvider,
118+
) as jest.Mocked<ConnectionFactory>
116119

117120
// Mock ConnectionManager
118121
mockConnectionManager = new ConnectionManager(

src/services/mcp/config/validation.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ const createServerConfigSchema = () => {
2020
args: z.array(z.string()).optional(),
2121
cwd: z.string().default(() => vscode.workspace.workspaceFolders?.at(0)?.uri.fsPath ?? process.cwd()),
2222
env: z.record(z.string()).optional(),
23-
// Ensure no SSE fields are present
23+
// Ensure no HTTP fields are present
2424
url: z.undefined().optional(),
2525
headers: z.undefined().optional(),
26+
sessionId: z.undefined().optional(),
2627
})
2728
.transform((data) => ({
2829
...data,
@@ -35,6 +36,7 @@ const createServerConfigSchema = () => {
3536
type: z.enum(["sse"]).optional(),
3637
url: z.string().url("URL must be a valid URL format"),
3738
headers: z.record(z.string()).optional(),
39+
sessionId: z.undefined().optional(),
3840
// Ensure no stdio fields are present
3941
command: z.undefined().optional(),
4042
args: z.undefined().optional(),
@@ -46,6 +48,26 @@ const createServerConfigSchema = () => {
4648
type: "sse" as const,
4749
}))
4850
.refine((data) => data.type === undefined || data.type === "sse", { message: typeErrorMessage }),
51+
52+
// Streamable HTTP config (has url field and optional sessionId)
53+
BaseConfigSchema.extend({
54+
type: z.enum(["streamable-http"]).optional(),
55+
url: z.string().url("URL must be a valid URL format"),
56+
headers: z.record(z.string()).optional(),
57+
sessionId: z.string().optional(),
58+
// Ensure no stdio fields are present
59+
command: z.undefined().optional(),
60+
args: z.undefined().optional(),
61+
cwd: z.undefined().optional(),
62+
env: z.undefined().optional(),
63+
})
64+
.transform((data) => ({
65+
...data,
66+
type: "streamable-http" as const,
67+
}))
68+
.refine((data) => data.type === undefined || data.type === "streamable-http", {
69+
message: typeErrorMessage,
70+
}),
4971
])
5072
}
5173

src/services/mcp/connection/ConnectionFactory.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ServerConfig, McpConnection, McpServer, ConfigSource } from "../types"
22
import { ConnectionHandler } from "./ConnectionHandler"
33
import { FileWatcher } from "./FileWatcher"
44
import { ConfigManager } from "../config"
5+
import { ClineProvider } from "../../../core/webview/ClineProvider"
56

67
/**
78
* Connection factory class
@@ -11,11 +12,11 @@ export class ConnectionFactory {
1112
private handlers: ConnectionHandler[] = []
1213
private connections: McpConnection[] = []
1314
private fileWatcher: FileWatcher
14-
private provider: any
15+
private provider: ClineProvider
1516
private configHandler: ConfigManager
1617
private onStatusChange?: (server: McpServer) => void
1718

18-
constructor(configHandler: ConfigManager, provider?: any, onStatusChange?: (server: McpServer) => void) {
19+
constructor(configHandler: ConfigManager, provider: ClineProvider, onStatusChange?: (server: McpServer) => void) {
1920
this.configHandler = configHandler
2021
this.fileWatcher = new FileWatcher()
2122
this.provider = provider
@@ -58,7 +59,8 @@ export class ConnectionFactory {
5859
if (patchedConfig.command) {
5960
patchedConfig.type = "stdio"
6061
} else if (patchedConfig.url) {
61-
patchedConfig.type = "sse"
62+
// If url is present, prefer streamable-http if headers are present, otherwise use sse
63+
patchedConfig.type = patchedConfig.headers ? "streamable-http" : "sse"
6264
}
6365
}
6466

src/services/mcp/connection/ConnectionManager.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,13 @@ export class ConnectionManager {
124124
const currentConfig = JSON.parse(existingServer.config)
125125

126126
const stripNonConnectionFields = (configObj: any) => {
127-
// Exclude alwaysAllow and timeout, timeout changes do not trigger reconnection
128-
const { alwaysAllow: _alwaysAllow, timeout: _timeout, ...rest } = configObj
127+
// Exclude changes do not trigger reconnection
128+
const {
129+
alwaysAllow: _alwaysAllow,
130+
timeout: _timeout,
131+
sessionId: _sessionId,
132+
...rest
133+
} = configObj
129134
return rest
130135
}
131136

0 commit comments

Comments
 (0)