Skip to content

fix(langgraph): stop concatenating server/tool into connector_type#216

Open
gzak wants to merge 1 commit into
mainfrom
feat/2906-langgraph-server-tool-split
Open

fix(langgraph): stop concatenating server/tool into connector_type#216
gzak wants to merge 1 commit into
mainfrom
feat/2906-langgraph-server-tool-split

Conversation

@gzak

@gzak gzak commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

The platform's MCP check-input/check-output schema now has a genuine two-field (server, tool) identity contract (epic #2905, platform sub-issue #2904 — R3-approved, CI-green, not yet merged to axonflow-enterprise but the schema is finalized). Previously this SDK only had connector_type, which forced callers to smuggle the tool name in by concatenating it ("server.tool") or dropping it entirely.

Fixes #2906 (epic #2905).

  • axonflow.client.Client.mcp_check_input / mcp_check_output (async and sync) gain a tool: str | None = None keyword-only parameter, sent as its own tool field on the wire body only when non-empty — same optional-field convention already used for client_id/tenant_id/etc.
  • MCPCheckInputRequest / MCPCheckOutputRequest pydantic models gain a matching optional tool field.
  • axonflow/adapters/langgraph.py:
    • mcp_tool_interceptor's default connector-type resolution no longer returns f"{server_name}.{name}". It now sends connector_type=request.server_name and tool=request.name as two separate values. This holds even when a caller supplies a custom connector_type_fn — the tool name is never folded back into connector_type and is never dropped.
    • tool_output_wrapper(): ToolNode's call_request dict (from LangGraph's awrap_tool_call) carries no server/origin metadata to improve on the existing connector_type default (bare tool name) — confirmed by inspecting the call site; there is no tool-registry reference available to derive a better value. Rather than continue using the bare name as connector_type with no way to recover the tool identity, the wrapper now also sends the bare name as tool on every call, so tool identity survives explicitly instead of being implicit/ambiguous in connector_type.
    • Updated the MCPInterceptorOptions docstring and both methods' docstring examples to show the two-field form instead of the old connector_type_fn=lambda call: f"local.{call['name']}" pattern.
  • tests/fixtures/wire_shape_baseline.json: acknowledged tool as an SDK-superset field on MCPCheckInputRequest/MCPCheckOutputRequest (mirrors the existing content_type entry) until #2904 merges to axonflow-enterprise and the OpenAPI spec catches up. Per CONTRIBUTING.md's baseline burndown policy — justified rather than burned down, since it's blocked on the platform-side PR landing.

Test plan

  • Added/updated unit tests in tests/test_langgraph_adapter.py asserting connector_type and tool are sent as two separate, correct values (not concatenated, not dropped) for both mcp_tool_interceptor (default and custom connector_type_fn) and tool_output_wrapper (default and custom connector_type_fn).
  • pytest tests/test_langgraph_adapter.py tests/test_langgraph_wrapper.py — 102 passed
  • Full suite: pytest — 1050 passed, 29 skipped
  • ruff check / ruff format --check on changed files — clean
  • mypy axonflow — no new errors (one pre-existing unrelated error in heartbeat.py)

@gzak
gzak force-pushed the feat/2906-langgraph-server-tool-split branch from 0af0b86 to 5e641fd Compare July 16, 2026 22:42
MCPCheckInputRequest/MCPCheckOutputRequest now carry a genuine two-field
(server, tool) identity contract (epic #2905, platform sub-issue #2904).
Client.mcp_check_input/mcp_check_output gain a `tool` parameter sent as
its own wire field instead of being folded into `connector_type`.

mcp_tool_interceptor's default connector-type resolution now sends
connector_type=request.server_name and tool=request.name as two
separate values instead of the dotted "server.tool" string, and this
holds even when a caller supplies a custom connector_type_fn. In
tool_output_wrapper, ToolNode's call_request carries no server/origin
info to improve connector_type's default beyond the bare tool name, but
the tool name itself is now always forwarded via the new `tool` field
rather than only living inside connector_type.

Signed-off-by: Gregory Zak <gzak@getaxonflow.com>
@gzak
gzak force-pushed the feat/2906-langgraph-server-tool-split branch from 5e641fd to ad99e32 Compare July 16, 2026 22:49
@saurabhjain1592

Copy link
Copy Markdown
Member

R3 review (master + independent hostile pass; platform contract verified on enterprise origin/main). Fix direction is right and the tests are strong (both-fields, custom-fn tool preservation, alias forwarding sync+async, real wire capture incl. legacy tool-omitted). Must-fix before this can join the set:

BLOCKER — CI is red

lint fails (run 29540637836): the falsey-clobber rule flags axonflow/adapters/langgraph.py:592:43 (pre_check.block_reason or "..."). Pre-existing pattern, but this PR's line shifts moved it out of the line-number-keyed baseline .lint_baselines/falsey_clobber.json. Fix the pattern or regenerate the baseline in this PR.

MEDIUM — coordinate with #217 (guaranteed conflict)

Both PRs edit the same hunks of client.py, types.py, and tests/fixtures/wire_shape_baseline.json with DIVERGENT comment text — this PR says #2904 is "merged... and live" (correct: enterprise c8df2006b), #217 says "not yet merged" (stale). Define merge order; the reconciled text must be this PR's version.

MEDIUM — changelog gaps (cluster-wide asks)

  • Min-platform note: on every RELEASED platform today (≤v9.9.0, which lacks c8df2006b), the new tool key is silently dropped and identity degrades to bare server — strictly worse than the concat hack. SDK release must be gated on a platform release containing #2916 and the changelog must pin the minimum platform version.
  • Policy re-scope note (Go fix(telemetry): drop profile field [skip-runtime-e2e] #189 has the model text): customer policies matching the old concatenated connector_type (e.g. "filesystem.read_file") stop matching after upgrade; the connector_type_fn escape hatch is the compat path — document it.

LOW

  • Custom-connector_type_fn statement shape changed: old "{custom}(args)" → new "{custom}.{tool}(args)" (langgraph.py:573). Policies matching statement text shift silently — changelog line needed.
  • Missing-server edge untested cluster-wide: empty server_name now sends connector_type="" which the platform 400s (old concat ".tool" passed). Worth one test.

Also note the response-plane gap filed from this review: the platform's check-output schema has no tool field even on main, so the tool this PR sends on check-output is unconsumed everywhere — see the sub-issue on enterprise (linked from epic #2905) for sequencing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants