Skip to content

feat: add native McpClient - #8862

Open
lloydrichards wants to merge 4 commits into
Effect-TS:mainfrom
lloydrichards:feat/mcp-client
Open

lloydrichards wants to merge 4 commits into
Effect-TS:mainfrom
lloydrichards:feat/mcp-client

Conversation

@lloydrichards

@lloydrichards lloydrichards commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Type

  • Feature

Description

Add an Effect-native McpClient so applications can use remote MCP tools, prompts, and resources through Effect schemas, services, and scopes. This first reasonably simple implementation includes stdio and Streamable HTTP, explicit 2025-11-25 and 2026-07-28 adapters, bounded discovery, direct calls, and Toolkit integration. The broader issue remains open for follow-ups.

Applications can call tools directly or expose discovered tools to a model through the existing Tool and Toolkit APIs. Prompts and resources reuse the McpSchema types already used by McpServer and would need to be consumed as part of the Prompt.Prompt API.

The main layers are the setting up the McpClient.layer which configures the client and the McpClient.layerHttp which configure the transport:

import { Effect, Layer, Schema } from "effect"
import { LanguageModel, McpClient, McpProtocol } from "effect/ai"
import { FetchHttpClient } from "effect/http"

// Provide a .layerStdio or .layerHttp depending on the transport type
const ClientLayer = McpClient.layerHttp({
  clientInfo: { name: "calculator-app", version: "1.0.0" },
  url: "http://localhost:3000/mcp",
  protocol: McpProtocol.v2026_07_28
}).pipe(
  Layer.provide(FetchHttpClient.layer)
)

For example, a server exposing the calculator tool, a prompt, and a resource:

//       ┏━ Effect.Effect<{...}, McpClient.McpClientError, never>
const workflow = Effect.gen(function*() {
  const client = yield* McpClient.McpClient
  //      ┏━ ReadonlyArray<McpSchema.Tool>
  const tools = yield* client.listTools()
  //       ┏━ ReadonlyArray<McpSchema.Prompt>
  const prompts = yield* client.listPrompts()
  //       ┏━ ReadonlyArray<McpSchema.Resource>
  const resources = yield* client.listResources()
  const tool = tools.find((tool) => tool.name === "add")
  const prompt = prompts.find((prompt) => prompt.name === "explain_sum")
  const resource = resources.find((resource) => resource.uri === "demo://calculator/guide")
  if (tool === undefined || prompt === undefined || resource === undefined) {
    return yield* Effect.die("The server must expose the example descriptors")
  }

  //     ┏━ McpSchema.CallToolResult
  const raw = yield* client.callTool({
    tool,
    arguments: { a: 20, b: 22 }
  })

  //       ┏━ { readonly total: number }
  const decoded = yield* client.callTool({
    tool,
    arguments: { a: 20, b: 22 },
    schema: Schema.Struct({ total: Schema.Number })
  }, { timeout: "2 seconds" })

  //       ┏━ McpSchema.GetPromptResult
  const messages = yield* client.getPrompt({
    prompt,
    arguments: { total: String(decoded.total) }
  })

  //       ┏━ McpSchema.ReadResourceResult
  const contents = yield* client.readResource({ uri: resource.uri })
  return { raw, decoded, messages, contents }
}).pipe(Effect.provide(ClientLayer))

Raw tool calls retain content, structured output, metadata, and isError. Supplying a schema decodes structured output and turns tool-reported errors into ToolError, retaining the original result. A possibly absent schema produces a union of the decoded value and full result.

Each list returns a complete snapshot within one deadline, capped at 100 pages and 10,000 entries. Operation helpers also support data-first and piped calls.

With a LanguageModel provided:

//       ┏━ Effect.Effect<Array<Response.ToolResultPart<string>>, AiError.AiError | McpClient.McpClientError, LanguageModel.LanguageModel>
const modelWorkflow = Effect.gen(function*() {
  const client = yield* McpClient.McpClient
  const toolkit = yield* McpClient.toolkit(client, "calculator__")

  const response = yield* LanguageModel.generateText({
    prompt: "Use calculator__add to calculate 20 + 22.",
    toolkit,
    toolChoice: "required"
  })

  return response.toolResults
}).pipe(Effect.provide(ClientLayer))

The adapter supplies discovered input schemas and bound handlers to the standard Toolkit. Its MVP output is text; rich binary model output needs application conversion.

Related

@changeset-bot

changeset-bot Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bfec3ad

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 32 packages
Name Type
effect Patch
@effect/opentelemetry Patch
@effect/vitest Patch
@effect/ai-anthropic Patch
@effect/ai-cloudflare Patch
@effect/ai-openai-compat Patch
@effect/ai-openai Patch
@effect/ai-openrouter Patch
@effect/ai-typesafe Patch
@effect/atom-react Patch
@effect/atom-solid Patch
@effect/atom-vue Patch
@effect/platform-browser Patch
@effect/platform-bun Patch
@effect/platform-deno Patch
@effect/platform-node-shared Patch
@effect/platform-node Patch
@effect/sql-clickhouse Patch
@effect/sql-d1 Patch
@effect/sql-libsql Patch
@effect/sql-mssql Patch
@effect/sql-mysql2 Patch
@effect/sql-pg Patch
@effect/sql-pglite Patch
@effect/sql-sqlite-bun Patch
@effect/sql-sqlite-do Patch
@effect/sql-sqlite-node Patch
@effect/sql-sqlite-react-native Patch
@effect/sql-sqlite-wasm Patch
@effect/docgen Patch
@effect/doctest Patch
@effect/openapi-generator Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@effect-janitor effect-janitor Bot added the 4.0 label Oct 6, 2026
@lloydrichards lloydrichards changed the title feat: add native mcp client feat: add native McpClient Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File Name Current Size Previous Size Difference
arbitrary-combinators.ts 38.84 KB 38.84 KB 0.00 KB (0.00%)
basic.ts 7.07 KB 7.07 KB 0.00 KB (0.00%)
batching.ts 10.15 KB 10.15 KB 0.00 KB (0.00%)
brand.ts 6.73 KB 6.73 KB 0.00 KB (0.00%)
cache.ts 10.92 KB 10.92 KB 0.00 KB (0.00%)
config.ts 22.32 KB 22.32 KB 0.00 KB (0.00%)
differ.ts 21.35 KB 21.35 KB 0.00 KB (0.00%)
http-client.ts 22.73 KB 22.73 KB 0.00 KB (0.00%)
http-router.ts 34.17 KB 34.17 KB 0.00 KB (0.00%)
logger.ts 11.25 KB 11.25 KB 0.00 KB (0.00%)
metric.ts 9.04 KB 9.04 KB 0.00 KB (0.00%)
optic.ts 6.96 KB 6.96 KB 0.00 KB (0.00%)
pubsub.ts 15.24 KB 15.24 KB 0.00 KB (0.00%)
queue.ts 12.16 KB 12.16 KB 0.00 KB (0.00%)
schedule.ts 11.27 KB 11.27 KB 0.00 KB (0.00%)
schema-bigdecimal.ts 13.78 KB 13.78 KB 0.00 KB (0.00%)
schema-binary.ts 40.03 KB 40.03 KB 0.00 KB (0.00%)
schema-class.ts 21.11 KB 21.11 KB 0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts 32.16 KB 32.16 KB 0.00 KB (0.00%)
schema-representation-roundtrip.ts 27.33 KB 27.33 KB 0.00 KB (0.00%)
schema-string-transformation.ts 14.54 KB 14.54 KB 0.00 KB (0.00%)
schema-string.ts 12.04 KB 12.04 KB 0.00 KB (0.00%)
schema-template-literal.ts 16.11 KB 16.11 KB 0.00 KB (0.00%)
schema-toArbitrary.ts 38.37 KB 38.37 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 25.32 KB 25.32 KB 0.00 KB (0.00%)
schema-toCodecJson.ts 20.28 KB 20.28 KB 0.00 KB (0.00%)
schema-toEquivalence.ts 20.49 KB 20.49 KB 0.00 KB (0.00%)
schema-toFormatter.ts 20.59 KB 20.59 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 25.06 KB 25.06 KB 0.00 KB (0.00%)
schema-toRepresentation.ts 20.57 KB 20.57 KB 0.00 KB (0.00%)
schema.ts 20.33 KB 20.33 KB 0.00 KB (0.00%)
stm.ts 13.29 KB 13.29 KB 0.00 KB (0.00%)
stream.ts 10.00 KB 10.00 KB 0.00 KB (0.00%)

@lloydrichards
lloydrichards force-pushed the feat/mcp-client branch 2 times, most recently from 02d1720 to 0ece300 Compare October 8, 2026 15:23

This branch is waiting to be deployed

1 waiting deployment
fork — bfec3ad0 Waiting Oct 10, 2026 by lloydrichards via approval-gate #18851
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

add Effect-native McpClient

1 participant