feat(core): track mcp prompt changes

This commit is contained in:
Aiden Cline 2026-06-29 18:47:57 -05:00
commit 4ef19f2e6a
5 changed files with 210 additions and 6 deletions

View file

@ -13,6 +13,8 @@ import {
ListToolsResultSchema,
type LoggingMessageNotification,
LoggingMessageNotificationSchema,
PromptListChangedNotificationSchema,
ResourceListChangedNotificationSchema,
ToolListChangedNotificationSchema,
ToolSchema,
} from "@modelcontextprotocol/sdk/types.js"
@ -79,7 +81,7 @@ export interface CallToolResult {
export interface LogMessage {
readonly level: LoggingMessageNotification["params"]["level"]
readonly logger: LoggingMessageNotification["params"]["logger"]
readonly logger?: LoggingMessageNotification["params"]["logger"]
readonly data: LoggingMessageNotification["params"]["data"]
}
@ -106,6 +108,10 @@ export interface Connection {
readonly onLog: (callback: (message: LogMessage) => void) => void
/** Registers a callback fired when the server announces its tool list changed; no-op if unsupported. */
readonly onToolsChanged: (callback: () => void) => void
/** Registers a callback fired when the server announces its prompt list changed; no-op if unsupported. */
readonly onPromptsChanged: (callback: () => void) => void
/** Registers a callback fired when the server announces its resource list changed; no-op if unsupported. */
readonly onResourcesChanged: (callback: () => void) => void
}
/** Connects an MCP server; closing the calling scope tears down the transport and any spawned process. */
@ -267,6 +273,14 @@ export const connect = Effect.fnUntraced(function* (
if (!client.getServerCapabilities()?.tools?.listChanged) return
client.setNotificationHandler(ToolListChangedNotificationSchema, async () => callback())
},
onPromptsChanged: (callback) => {
if (!client.getServerCapabilities()?.prompts?.listChanged) return
client.setNotificationHandler(PromptListChangedNotificationSchema, async () => callback())
},
onResourcesChanged: (callback) => {
if (!client.getServerCapabilities()?.resources?.listChanged) return
client.setNotificationHandler(ResourceListChangedNotificationSchema, async () => callback())
},
} satisfies Connection
}

View file

@ -264,7 +264,16 @@ export const layer = Layer.effect(
entry.client = undefined
entry.tools = undefined
entry.status = { status: "failed", error: "Connection closed" }
fork(events.publish(McpEvent.ToolsChanged, { server: name }).pipe(Effect.ignore))
fork(
Effect.all(
[
events.publish(McpEvent.ToolsChanged, { server: name }),
events.publish(McpEvent.PromptsChanged, { server: name }),
events.publish(McpEvent.ResourcesChanged, { server: name }),
],
{ discard: true },
).pipe(Effect.ignore),
)
})
connection.onLog((message) => fork(serverLog(name, message).pipe(Effect.ignore)))
connection.onToolsChanged(() => {
@ -275,6 +284,12 @@ export const layer = Layer.effect(
),
)
})
connection.onPromptsChanged(() => {
fork(events.publish(McpEvent.PromptsChanged, { server: name }).pipe(Effect.ignore))
})
connection.onResourcesChanged(() => {
fork(events.publish(McpEvent.ResourcesChanged, { server: name }).pipe(Effect.ignore))
})
}
const serverLog = (server: ServerName, message: MCPClient.LogMessage) => {