Skip to content

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

Merged
saurabhjain1592 merged 5 commits into
mainfrom
feat/2906-langgraph-server-tool-split
Jul 17, 2026
Merged

fix(langgraph): stop concatenating server/tool into connector_type#216
saurabhjain1592 merged 5 commits 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.

@saurabhjain1592

Copy link
Copy Markdown
Member

Delta note — semver classification (net-new, decision-forcing). This de-concatenation changes the wire value of connector_type from server.tool to bare server. For any default-config user, existing customer policies that match the concatenated value (e.g. "filesystem.read_file") silently stop matching after upgrade — silent policy non-enforcement in a governance SDK. Nothing is removed from the API surface, but that is a breaking behavioral change for consumers.

Honest call: this warrants a MAJOR SDK bump (SDK semver is decoupled from the platform, so the platform stays on its own track). The connectorTypeFn escape hatch is the migration path, not a reason to call it minor. If the team decides the derived connector_type default is non-contractual and keeps this minor, that ruling must be recorded on epic #2905 and this changelog needs a prominent Breaking/Migration section (currently it's under ### Fixed, which materially understates it) plus a min-platform pin: check-input tool shipped in released v9.10.0; check-output tool is still unconsumed on every version while #2955 is open.

(All prior-round findings on this PR are still open at the current head — no post-review commits landed.)

saurabhjain1592 added a commit that referenced this pull request Jul 17, 2026
…ULING 1)

Apply epic #2905 RULING 1 to ComputerUseGovernor so the `tool_name` audit
column means the tool name in every adapter:

- connector_type = the constant "computer_use" connector/domain marker
- tool = the tool NAME ("computer"/"bash"/"text_editor"), never the action
- the action stays in the serialized `statement` (unchanged) — it is NOT
  placed in `tool`, nor in `operation` (agent-api constrains operation to
  the enum {query, execute})

check_result() now sends the same identity on the response plane so request-
and response-plane rows correlate. Removes _derive_connector_type.

BREAKING: the connector_type wire value changes shape; policies scoped to the
old concatenated value (e.g. "computer_use.left_click", a bare tool name, or a
computer_use.* prefix) stop matching. Re-scope to connector_type=="computer_use"
plus the tool field. No connector_type_fn/tool_fn escape hatch on this adapter.
Requires platform v9.10.0+ to consume `tool` on check-input; check-output does
not consume it on any released version yet (#2955).

Reconciles the shared client.py/types.py/wire_shape_baseline.json hunks with
PR #216 to byte-identical, corrected text (#2916/c8df2006b merged, first
released in v9.10.0); regenerates .lint_baselines/falsey_clobber.json (CI).

Refs #2910, epic #2905.

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
…2905)

Mark the LangGraph server/tool de-concatenation as a BREAKING change (the
connector_type wire value changes from "{server}.{tool}" to the bare server
name) and document the migration:

- Changelog: move to "Changed (BREAKING)", require a major SDK bump, add the
  policy re-scope note (connector_type_fn is the compat lever), the custom-fn
  statement-shape change, the missing-server fail-closed edge, and the
  platform-version pins (tool consumed on check-input at v9.10.0+; check-output
  tracked by #2955).
- Add the missing-server edge test: empty server_name -> connector_type=""
  -> platform 400 -> ConnectorError (fail-closed); document + pin the wire.
- Reconcile the shared client.py/types.py/wire_shape_baseline.json hunks with
  PR #217 to byte-identical, corrected text (#2916/c8df2006b merged, first
  released in v9.10.0); thread `tool` through the check_tool_input/
  check_tool_output aliases too. Declare the #216-before-#217 merge order.
- Regenerate .lint_baselines/falsey_clobber.json (line-shift only; CI green).

Refs #2906, epic #2905.

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
…erver-tool-split

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>

# Conflicts:
#	.lint_baselines/falsey_clobber.json
#	CHANGELOG.md
Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
@saurabhjain1592

Copy link
Copy Markdown
Member

Follow-up commits (WS-DECONCAT): reconciled the shared client.py/types.py/wire_shape_baseline.json hunks with #217 to byte-identical, corrected text (#2916/c8df2006b merged, first released v9.10.0; threaded tool through the check_tool_input/check_tool_output aliases too — #217's copy was missing that); regenerated the falsey_clobber baseline (the CI blocker); rewrote the changelog as Changed (BREAKING) with the major-bump requirement, policy re-scope note, custom-fn statement-shape change, missing-server fail-closed edge, and min-platform pins; added a wire-pinned missing-server edge test. Merge #216 before #217. Merged origin/main in (caller_name legs) non-destructively. CI green, MERGEABLE/CLEAN. Master-R3'd (2 rounds). Ready for the operator's bundled major cut. Rulings recorded on epic #2905.

@saurabhjain1592

Copy link
Copy Markdown
Member

Master independent R3: CLEAR-FOR-SET (code). Two-field split coherent with the other SDKs (connector_type=server, tool=tool name, omitted when empty), statement derives from the resolved connector type, missing-server edge tested, breaking changelog proper. CI green, non-destructive origin/main merge (caller_name + tool coexist). Version-file major bump lands at the operator release-cut (consistent with how the caller_name legs merged at 8.5.1). Held for the operator SDK-major cut.

Merge #216 before #217 (byte-identical shared hunks).

Bump pyproject.toml + axonflow/_version.py to 9.0.0 and finalize the
tool-identity content under ## [9.0.0] - 2026-07-18 (banner dropped). The
LangGraph and Computer Use de-concatenation is described in a single combined
entry (byte-identical across #216/#217 — merge #216 first). Changelog scrubbed
of internal issue/PR numbers and internal review terminology; minimum platform
stated as versions (check-input tool = platform v9.10.0+, response-plane tool =
platform v9.11.0+).

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
saurabhjain1592 added a commit that referenced this pull request Jul 17, 2026
Bump pyproject.toml + axonflow/_version.py to 9.0.0 and finalize the
tool-identity content under ## [9.0.0] - 2026-07-18 (banner dropped). The
LangGraph and Computer Use de-concatenation is described in a single combined
entry (byte-identical across #216/#217 — merge #216 first). Changelog scrubbed
of internal issue/PR numbers and internal review terminology; minimum platform
stated as versions (check-input tool = platform v9.10.0+, response-plane tool =
platform v9.11.0+).

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
@saurabhjain1592

Copy link
Copy Markdown
Member

Release-ready follow-up: bumped the version to 9.0.0 and finalized the tool-identity content under a real ## [9.0.0] - 2026-07-18 changelog header (dropped the "must be a major bump" banner — the header is the bump). Scrubbed the [9.0.0] changelog of internal issue/PR numbers and internal review terminology; minimum platform is now stated as versions (check-input tool = AxonFlow platform v9.10.0+, response-plane tool = v9.11.0+). Version-alignment gate green. CI green, MERGEABLE/CLEAN. Master-R3'd (2 rounds, incl. an independent finalization pass).

Python: #216 and #217 share one byte-identical, combined [9.0.0] de-concat entry (LangGraph + Computer Use) — merge #216 first.

@saurabhjain1592

Copy link
Copy Markdown
Member

Master re-R3 of the v9.0.0 finalize delta: CLEAR-FOR-SET. Version file 8.5.1 → 9.0.0; correct Keep-a-Changelog shape (empty [Unreleased] above ## [9.0.0], BREAKING content under [9.0.0], release-workflow preflight will match). Public [9.0.0] block scrubbed clean — no enterprise issue/PR numbers, no RULING language, min-platform stated as versions (v9.10.0+/v9.11.0+). Finalize commit is additive; de-concat logic (two-field split + rulings) untouched. CI green.

@saurabhjain1592
saurabhjain1592 merged commit 5a0d92f into main Jul 17, 2026
18 checks passed
@saurabhjain1592
saurabhjain1592 deleted the feat/2906-langgraph-server-tool-split branch July 17, 2026 23:19
saurabhjain1592 added a commit that referenced this pull request Jul 17, 2026
* fix(computer-use): stop dropping tool name when action is present

_derive_connector_type(tool_name, action) discarded tool_name whenever
action was present, returning only "computer_use.{action}" — so every
actioned Computer Use tool call collapsed to the same connector_type
shape regardless of which tool (computer vs. any other actioned tool)
triggered it.

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.

_derive_connector_type is removed; ComputerUseGovernor now passes
connector_type=tool_name and tool=action directly to mcp_check_input,
so neither value is discarded. tool_name is chosen as the
connector_type-equivalent (rather than a fixed "computer_use" literal)
because it matches the rest of the SDK's convention of bare category
names for connector_type (e.g. "postgres", "snowflake") with no
namespace prefix, and because action already varies meaningfully within
a single tool (e.g. "left_click" vs "screenshot" for "computer") while
tool_name is the more stable partition key. check_result() has no
action available at its call site, so tool is left unset there,
matching its pre-existing behavior of not being able to distinguish
sub-actions on output checks.

Signed-off-by: Gregory Zak <gzak@getaxonflow.com>

* fix(computer-use)!: report (server, tool) identity, tool=tool name (RULING 1)

Apply epic #2905 RULING 1 to ComputerUseGovernor so the `tool_name` audit
column means the tool name in every adapter:

- connector_type = the constant "computer_use" connector/domain marker
- tool = the tool NAME ("computer"/"bash"/"text_editor"), never the action
- the action stays in the serialized `statement` (unchanged) — it is NOT
  placed in `tool`, nor in `operation` (agent-api constrains operation to
  the enum {query, execute})

check_result() now sends the same identity on the response plane so request-
and response-plane rows correlate. Removes _derive_connector_type.

BREAKING: the connector_type wire value changes shape; policies scoped to the
old concatenated value (e.g. "computer_use.left_click", a bare tool name, or a
computer_use.* prefix) stop matching. Re-scope to connector_type=="computer_use"
plus the tool field. No connector_type_fn/tool_fn escape hatch on this adapter.
Requires platform v9.10.0+ to consume `tool` on check-input; check-output does
not consume it on any released version yet (#2955).

Reconciles the shared client.py/types.py/wire_shape_baseline.json hunks with
PR #216 to byte-identical, corrected text (#2916/c8df2006b merged, first
released in v9.10.0); regenerates .lint_baselines/falsey_clobber.json (CI).

Refs #2910, epic #2905.

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>

* docs(changelog): add blank line before ### Added (MD022)

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>

* release: finalize v9.0.0 — version bump + clean [9.0.0] changelog

Bump pyproject.toml + axonflow/_version.py to 9.0.0 and finalize the
tool-identity content under ## [9.0.0] - 2026-07-18 (banner dropped). The
LangGraph and Computer Use de-concatenation is described in a single combined
entry (byte-identical across #216/#217 — merge #216 first). Changelog scrubbed
of internal issue/PR numbers and internal review terminology; minimum platform
stated as versions (check-input tool = platform v9.10.0+, response-plane tool =
platform v9.11.0+).

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>

---------

Signed-off-by: Gregory Zak <gzak@getaxonflow.com>
Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
Co-authored-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
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