feat(mcp): support server log notifications (#31752)
This commit is contained in:
@@ -7,7 +7,12 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
|
|||||||
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"
|
||||||
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
|
||||||
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js"
|
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js"
|
||||||
import { type Tool as MCPToolDef, ToolListChangedNotificationSchema } from "@modelcontextprotocol/sdk/types.js"
|
import {
|
||||||
|
type LoggingMessageNotification,
|
||||||
|
LoggingMessageNotificationSchema,
|
||||||
|
type Tool as MCPToolDef,
|
||||||
|
ToolListChangedNotificationSchema,
|
||||||
|
} from "@modelcontextprotocol/sdk/types.js"
|
||||||
import { Config } from "@/config/config"
|
import { Config } from "@/config/config"
|
||||||
import { ConfigMCPV1 } from "@opencode-ai/core/v1/config/mcp"
|
import { ConfigMCPV1 } from "@opencode-ai/core/v1/config/mcp"
|
||||||
import { NamedError } from "@opencode-ai/core/util/error"
|
import { NamedError } from "@opencode-ai/core/util/error"
|
||||||
@@ -391,6 +396,10 @@ export const layer = Layer.effect(
|
|||||||
)
|
)
|
||||||
|
|
||||||
function watch(s: State, name: string, client: MCPClient, bridge: EffectBridge.Shape, timeout?: number) {
|
function watch(s: State, name: string, client: MCPClient, bridge: EffectBridge.Shape, timeout?: number) {
|
||||||
|
client.setNotificationHandler(LoggingMessageNotificationSchema, (notification) =>
|
||||||
|
bridge.promise(serverLog(name, notification.params)),
|
||||||
|
)
|
||||||
|
|
||||||
if (!client.getServerCapabilities()?.tools) return
|
if (!client.getServerCapabilities()?.tools) return
|
||||||
client.setNotificationHandler(ToolListChangedNotificationSchema, async () => {
|
client.setNotificationHandler(ToolListChangedNotificationSchema, async () => {
|
||||||
if (s.clients[name] !== client || s.status[name]?.status !== "connected") return
|
if (s.clients[name] !== client || s.status[name]?.status !== "connected") return
|
||||||
@@ -404,6 +413,24 @@ export const layer = Layer.effect(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function serverLog(name: string, params: LoggingMessageNotification["params"]) {
|
||||||
|
const fields = { server: name, logger: params.logger, level: params.level, data: params.data }
|
||||||
|
switch (params.level) {
|
||||||
|
case "debug":
|
||||||
|
return Effect.logDebug("MCP server log", fields)
|
||||||
|
case "info":
|
||||||
|
case "notice":
|
||||||
|
return Effect.logInfo("MCP server log", fields)
|
||||||
|
case "warning":
|
||||||
|
return Effect.logWarning("MCP server log", fields)
|
||||||
|
case "error":
|
||||||
|
case "critical":
|
||||||
|
case "alert":
|
||||||
|
case "emergency":
|
||||||
|
return Effect.logError("MCP server log", fields)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const state = yield* InstanceState.make<State>(
|
const state = yield* InstanceState.make<State>(
|
||||||
Effect.fn("MCP.state")(function* () {
|
Effect.fn("MCP.state")(function* () {
|
||||||
const cfg = yield* cfgSvc.get()
|
const cfg = yield* cfgSvc.get()
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { expect, mock, beforeEach } from "bun:test"
|
import { expect, mock, beforeEach } from "bun:test"
|
||||||
|
import { ToolListChangedNotificationSchema } from "@modelcontextprotocol/sdk/types.js"
|
||||||
import { Cause, Effect, Exit } from "effect"
|
import { Cause, Effect, Exit } from "effect"
|
||||||
import type { MCP as MCPNS } from "../../src/mcp/index"
|
import type { MCP as MCPNS } from "../../src/mcp/index"
|
||||||
import { testEffect } from "../lib/effect"
|
import { testEffect } from "../lib/effect"
|
||||||
@@ -394,7 +395,7 @@ it.instance(
|
|||||||
{ name: "next_tool", description: "next", inputSchema: { type: "object", properties: {} } },
|
{ name: "next_tool", description: "next", inputSchema: { type: "object", properties: {} } },
|
||||||
]
|
]
|
||||||
|
|
||||||
const handler = Array.from(serverState.notificationHandlers.values())[0]
|
const handler = serverState.notificationHandlers.get(ToolListChangedNotificationSchema)
|
||||||
expect(handler).toBeDefined()
|
expect(handler).toBeDefined()
|
||||||
yield* Effect.promise(() => handler?.())
|
yield* Effect.promise(() => handler?.())
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user