Avatar for the langchain-ai user
langchain-ai
langchain
BlogDocsChangelog

Performance History

Latest Results

Merge branch 'master' into fix/reasoning-model-structured-output
visnu64:fix/reasoning-model-structured-output
6 hours ago
docs(langchain): runnable `langchain.mcp` examples (#39976) Stacked on #39939 — review that first; this branch adds only `libs/langchain_v1/examples/mcp/`. Ten self-contained scripts, one idea each. Every one starts whatever MCP server it needs, so `uv run examples/mcp/<name>.py` is the whole workflow. | Example | Shows | Model | Network | |---|---|:-:|:-:| | `transports.py` | one adapter over in-memory, stdio, and HTTP | | | | `remote_server.py` | pointing the adapter at a public MCP server | ✅ | ✅ | | `multi_server.py` | several servers behind one adapter, tools prefixed per server | ✅ | | | `graph_factory.py` | one per-user MCP fleet behind a `langgraph dev` graph factory | ✅ | | | `protocol_eras.py` | one agent holding tools from both MCP protocol eras | ✅ | | | `tool_errors.py` | a failing tool reaching the model so it can retry | ✅ | | | `elicitation.py` | a server asking a human mid-call, via `interrupt()` | ✅ | | | `destructive_interrupt.py` | gating destructive tools behind approval, from tool metadata | ✅ | | | `auth_bearer.py` | a server behind a static bearer token | | | | `auth_oauth.py` | a full OAuth 2.1 flow with dynamic client registration | | | Each was run against a real model (or a real `langgraph dev` server) before committing, and its output is what the docstring claims. A few choices worth knowing about: - **Servers come from FastMCP's own `run_server_in_process`**, not hand-rolled uvicorn plumbing, so the examples teach the adapter rather than how to start a server. - **HTTP appears only where it is the subject.** `multi_server.py` names its backends over stdio, which is less machinery and a better illustration, since a fleet addresses each backend independently. - **`remote_server.py` hits DeepWiki**, a public MCP server, where the URL is the entire configuration. It prints the tool call so the answer is visibly the remote server's work rather than the model's memory. - **`tool_errors.py` pins the model with a system prompt.** Without it the model answers the arithmetic from memory and the error path never runs. - **`destructive_interrupt.py` derives the approval gate from metadata**, reading each tool's `metadata["mcp"]["tool"]["annotations"]["destructive_hint"]` to build the `HumanInTheLoopMiddleware` `interrupt_on` map — so any tool a server flags as destructive pauses for approval, no tool names hardcoded. - **`auth_oauth.py` opens a browser tab.** The demo authorization server auto-approves, so it redirects straight back — but it is the one example that cannot run unattended. `graph_factory.py` is registered by a `langgraph.json` and run under `langgraph dev` rather than invoked directly. It shows a per-user MCP fleet: one shared `httpx` connection pool for everyone, a per-user `ClientGroup` built each run, and per-user discovery caching keyed on the caller's identity read off the injected `ServerRuntime`. `run_graph_factory_demo.py` is an end-to-end driver that stands up two guarded MCP servers plus a `langgraph dev` server with custom auth and runs the graph once per user; `auth.py`, `langgraph.json`, and `_fleet_servers.py` support it. `_servers.py` holds the small MCP servers the examples share, `_stdio_server.py` is the entry point launched as a subprocess over stdio, and neither `_`-prefixed helper is part of the API being demonstrated. `examples/*` picks up the same two ruff exemptions `scripts/*` already has, for printing and for not being a package. ## Release note No library changes — examples only. --- *Prepared with the assistance of an AI agent.* --------- Co-authored-by: Hunter Lovell <hunter@hntrl.io> Co-authored-by: Hunter Lovell <40191806+hntrl@users.noreply.github.com> Co-authored-by: Sydney Runkle <sydney@langchain.dev>
master
9 hours ago
feat(langchain): `langchain.mcp` namespace, `MCPAdapter` (#39939) Adds `langchain.mcp`: adapt an MCP server into LangChain tools ready for `create_agent`. ```python from langchain.agents import create_agent from langchain.mcp import MCPAdapter adapter = MCPAdapter("https://example.com/mcp") agent = create_agent("anthropic:claude-sonnet-5", await adapter.list_tools()) ``` `langchain-mcp-adapters` stays the place for the full surface (interceptors, callbacks, prompts, resources). This is the short path for the common case. ## Public API `MCPAdapter(target, *, elicitation=None)`, with a `client` property, `list_tools(*, cache_mode="use")`, and async context management. `as_langchain_tool(tool, client, *, elicitation=None)` converts a single tool for callers managing their own client. `MCPAdapter` accepts anything `fastmcp.Client` accepts — a URL, `Path`, in-process server, `ClientTransport`, `MCPConfig` (or its dict form), or a pre-built `Client` — plus a FastMCP `ClientGroup` for a fleet of servers behind one client. Inference is FastMCP's, so new target types work without changes here. The target union is `MCPAdapterTarget`; it is not part of the package's public surface (it is only useful for annotating a `target`), but it stays importable from `langchain.mcp.adapter` for that purpose. An `MCPConfig` naming several servers yields one prefixed toolset from one adapter: ```python MCPAdapter({"mcpServers": {"notes": {"command": "python", "args": ["notes.py"]}, "web": {"url": "https://example.com/mcp"}}}) # -> ["notes_read_note", "web_get_weather", ...] ``` ## Tool conversion Tool results are ported from `langchain-mcp-adapters`, so a call reaches a model in the same shape either way. Results become LangChain content blocks (audio raises `NotImplementedError`); structured content becomes an `MCPToolArtifact`; `args_schema` is the tool's `input_schema`. Tool *metadata* is grouped under a single `mcp` namespace so a consumer can tell an MCP tool's provenance apart and keep tool-level fields distinct from the serving server's identity: ```python tool.metadata == { "mcp": { "tool": { "annotations": {"destructive_hint": True, ...}, # snake_case, from tool.annotations "_meta": {...}, # verbatim MCP `_meta` }, "server": {"name": "files", "version": "2.1.0", ...}, # from the live client connection }, } ``` Server identity comes off the connection (a `Tool` carries no server field) and is read at conversion time while the client is connected. This metadata rides onto the LangChain tool's `metadata`, so it also lands on the tool's traced run — `mcp.server.name` becomes filterable in traces. The examples PR uses the destructive hint to gate a tool behind human approval. An `isError=True` result becomes a `ToolMessage` with `status="error"` carrying the server's own error content, so the agent can correct itself instead of the run ending. Transport failures and unconvertible content still raise. FastMCP clients are reentrant and reference-counted, so the adapter adds no second layer — tools hold the client and stay callable after the adapter's context exits. ## Discovery caching `list_tools(cache_mode=...)` selects how discovery interacts with the client-side response cache (SEP-2549): `use` (default) serves a cached tool list within the server's TTL hint, `refresh` calls the server and repopulates it, `bypass` skips the cache. The cache and its per-principal isolation are configured on the client itself (`Client(cache=...)`); this only selects how discovery reads it. The default is `use` so a configured cache is honored — note this differs from a bare `ClientGroup.list_tools()`, whose own default is `refresh`. ## Elicitation Some MCP tools need input before they can finish. `elicitation="interrupt"` surfaces the question as a LangGraph interrupt: ```python adapter = MCPAdapter(server, elicitation="interrupt") paused = await agent.ainvoke({"messages": [...]}, config) [question] = paused["__interrupt__"][0].value["requests"] # {'key': 'date', 'message': 'What date would you like to book?', 'mode': 'form', # 'requested_schema': {...}} await agent.ainvoke( Command(resume={"responses": {question["key"]: {"action": "accept", "content": {"date": "2026-08-26"}}}}), config, ) ``` Answers correlate by the server's own request keys, so nothing extra is needed on resume. Several requests in one round share one interrupt; successive rounds each get their own. The payload types live in `langchain.mcp.elicitation`, split so the type system carries the protocol's rules: `mode` discriminates form from URL requests, and only an accept can carry content, narrowed to the scalar shapes the wire accepts. It is opt-in because a declared capability is a promise on the wire — servers only build flows that depend on it once a client says yes — so left unset, a server whose tool *requires* an answer refuses the call rather than running without one. The loop is driven through `session.call_tool(..., allow_input_required=True)` rather than a FastMCP handler, which would convert the `GraphInterrupt` into an MCP error. Resuming re-issues the call from its first round, since the interrupt unwound it; a server that asks before doing work repeats nothing, which the tests assert by counting tool-body executions. Scope is elicitation only. Embedded sampling and roots requests raise, since driving the loop by hand bypasses the FastMCP callbacks that answer them, as does a continuation round carrying only `request_state` — that is the protocol's long-running-work channel, and serving it would mean polling a remote server from inside a tool call. ## Building on FastMCP Handing the client to FastMCP is what keeps this small. Most of the protocol surface is inherited rather than written: - **Negotiation, per server.** FastMCP speaks both the 2025-11-25 `initialize` handshake and the 2026-07-28 `server/discover` revision, and picks per connection — so one agent can hold tools from servers on different revisions at once, with no protocol mode to choose. There is a test that builds exactly that agent. - **Transports and auth.** Inference covers URLs, script paths, in-process servers, and config dicts, while `auth="oauth"`, bearer tokens, custom `httpx` auth, and TLS via `verify` all arrive without code here. - **Fleets.** A `ClientGroup` target lets one adapter serve a fleet of servers, prefixing each server's tools and routing every call back to the client that serves it. - **Connection lifecycle.** Clients are reentrant and reference-counted, so tools outlive the adapter's context without a second layer of bookkeeping, and requests race the background session task so a dead HTTP session raises instead of hanging a tool call. - **The input-required flow (SEP-2322).** Elicitation intercepts a round at the session layer instead of reimplementing the multi-round protocol. - **In-process servers.** The whole unit suite drives real MCP servers with no subprocess and no socket. This requires `fastmcp>=4.0.0` — the GA line whose multi-server config routes to modern-protocol servers, which the `MCPConfig` and `ClientGroup` support rely on. ## Release note New `langchain.mcp` namespace. `MCPAdapter` adapts any target `fastmcp.Client` accepts — a URL, a local script, an in-process server, an `MCPConfig` naming several servers, or a pre-built client — as well as a FastMCP `ClientGroup`, into LangChain tools ready for `create_agent`. `MCPAdapter.list_tools(cache_mode=...)` discovers tools with optional client-side response caching. `as_langchain_tool` converts a single MCP tool for callers managing their own client. Tool metadata (annotations, `_meta`, and the serving server's identity) is grouped under an `mcp` namespace on each tool. `elicitation="interrupt"` surfaces a server's mid-call questions as LangGraph interrupts. Requires the `mcp` extra: `pip install "langchain[mcp]"`. ## Notes for review - A `Path` or script-path target launches a local subprocess with no opt-in keyword. Intended, and the target comes from application code rather than a model, but it loosens the previous default and is awkward to walk back once released. - Tool metadata is traced. Whatever a server puts in `_meta` reaches the tool's LangSmith run verbatim; keeping it nested under `mcp.tool._meta` (rather than flattened) keeps that clearly demarcated. - Two FastMCP internals are load-bearing, and both now fail loudly rather than silently. `elicitation="interrupt"` installs a sentinel handler purely to trip the SDK's identity comparison against its default callback, which is the only way the SDK lets a client declare the capability; a test asserts the negotiated capability so a change upstream cannot quietly stop servers asking. `_await_monitored` reaches for FastMCP's private `_await_with_session_monitoring`, without which a session dying mid-elicitation hangs the tool call; the fallback now warns. - `tests/unit_tests/mcp/conftest.py` grants `blockbuster` two narrow allowances, both caused upstream in FastMCP: entering a server `Context` resolves an optional dependency through `importlib.metadata` on every in-process request, and `mcp.client.session` imports `jsonschema` lazily on first tool call. --- *Prepared with the assistance of an AI agent.* --------- Signed-off-by: Hunter Lovell <40191806+hntrl@users.noreply.github.com> Co-authored-by: Hunter Lovell <hunter@hntrl.io> Co-authored-by: Hunter Lovell <40191806+hntrl@users.noreply.github.com> Co-authored-by: Sydney Runkle <sydney@langchain.dev>
master
9 hours ago
docs(langchain): format mcp examples
sydney-runkle/langchain/mcp-examples
9 hours ago
chore(langchain): drop uv prerelease allow now that fastmcp 4 is GA
sydney-runkle/langchain/simplify-mcp-adapter
9 hours ago
Merge branch 'master' into sydney-runkle/langchain/simplify-mcp-adapter
sydney-runkle/langchain/simplify-mcp-adapter
9 hours ago
chore(langchain): drop uv prerelease allow now that fastmcp 4 is GA
sydney-runkle/langchain/simplify-mcp-adapter
9 hours ago

Latest Branches

CodSpeed Performance Gauge
0%
fix: handle reasoning model output tags in structured output parsing#40147
6 hours ago
c404243
visnu64:fix/reasoning-model-structured-output
CodSpeed Performance Gauge
0%
docs(langchain): runnable `langchain.mcp` examples#39976
9 hours ago
0b4480c
sydney-runkle/langchain/mcp-examples
CodSpeed Performance Gauge
0%
9 hours ago
8df2391
sydney-runkle/langchain/simplify-mcp-adapter
© 2026 CodSpeed Technology
Home Terms Privacy Docs