feat(mcp): add resource read tools (#33483)

This commit is contained in:
Shoubhit Dash
2026-06-23 13:49:42 +05:30
committed by GitHub
parent ea17d9bed8
commit 3f3f120825
8 changed files with 376 additions and 22 deletions
+5 -1
View File
@@ -86,6 +86,7 @@ export function fetch<T extends { name: string }>(
client: Client,
list: (client: Client) => Promise<T[]>,
label: string,
key?: (item: T) => string,
) {
return Effect.tryPromise({
try: () => list(client),
@@ -100,7 +101,10 @@ export function fetch<T extends { name: string }>(
Effect.map((items) => {
const sanitizedClient = sanitize(clientName)
return Object.fromEntries(
items.map((item) => [sanitizedClient + ":" + sanitize(item.name), { ...item, client: clientName }]),
items.map((item) => [
key ? clientName + ":" + key(item) : sanitizedClient + ":" + sanitize(item.name),
{ ...item, client: clientName },
]),
)
}),
Effect.orElseSucceed(() => undefined),
+15 -4
View File
@@ -161,7 +161,7 @@ export interface Interface {
readonly clients: () => Effect.Effect<Record<string, MCPClient>>
readonly tools: () => Effect.Effect<Record<string, Tool>>
readonly prompts: () => Effect.Effect<Record<string, PromptInfo & { client: string }>>
readonly resources: () => Effect.Effect<Record<string, ResourceInfo & { client: string }>>
readonly resources: (clientName?: string) => Effect.Effect<Record<string, ResourceInfo & { client: string }>>
readonly add: (name: string, mcp: ConfigMCPV1.Info) => Effect.Effect<{ status: Record<string, Status> | Status }>
readonly connect: (name: string) => Effect.Effect<void, NotFoundError>
readonly disconnect: (name: string) => Effect.Effect<void, NotFoundError>
@@ -654,17 +654,22 @@ export const layer = Layer.effect(
s: State,
listFn: (c: Client, timeout?: number) => Promise<T[]>,
label: string,
key?: (item: T) => string,
targetClientName?: string,
) {
return Effect.gen(function* () {
const cfg = yield* cfgSvc.get()
return yield* Effect.forEach(
Object.entries(s.clients).filter(([name]) => s.status[name]?.status === "connected"),
Object.entries(s.clients).filter(
([name]) => s.status[name]?.status === "connected" && (!targetClientName || name === targetClientName),
),
([clientName, client]) =>
McpCatalog.fetch(
clientName,
client,
(c) => listFn(c, requestTimeout(s, clientName, cfg.mcp?.[clientName], cfg.experimental?.mcp_timeout)),
label,
key,
).pipe(Effect.map((items) => Object.entries(items ?? {}))),
{ concurrency: "unbounded" },
).pipe(Effect.map((results) => Object.fromEntries<T & { client: string }>(results.flat())))
@@ -675,8 +680,14 @@ export const layer = Layer.effect(
return yield* collectFromConnected(yield* InstanceState.get(state), McpCatalog.prompts, "prompts")
})
const resources = Effect.fn("MCP.resources")(function* () {
return yield* collectFromConnected(yield* InstanceState.get(state), McpCatalog.resources, "resources")
const resources = Effect.fn("MCP.resources")(function* (clientName?: string) {
return yield* collectFromConnected(
yield* InstanceState.get(state),
McpCatalog.resources,
"resources",
(resource) => resource.uri,
clientName,
)
})
const withClient = Effect.fnUntraced(function* <A>(