-
Notifications
You must be signed in to change notification settings - Fork 336
Description
New version 0.0.14 broke my prod.
Summary
Since version 0.1.14 (commit 092da2a), langchain-mcp-adapters automatically injects the full ToolRuntime object as a parameter to all tool invocations when using create_agent() with a store. While this feature provides access to useful information like tool_call_id and context, it also injects the entire runtime object including full conversation state, message history, callbacks, and internal LangGraph objects.
This causes significant issues in production environments with remote MCP servers:
Problem Description
1. Massive Payload Size
The injected runtime contains:
- Complete message history (all
HumanMessageandAIMessageobjects with full content) - Full state dictionary with all channels
- Callbacks objects (
AsyncCallbackManager) - Checkpointer references
- Store references
- Internal LangGraph objects (
PregelScratchpad,PregelTaskWrites, etc.)
Impact: HTTP payloads from client → MCP server grow from ~1-5KB to 150-300KB per tool call.
2. State Bloat and Token Consumption
The runtime is included in the tool_calls args of AIMessage, which gets:
- Saved to the checkpoint/state
- Sent back to the LLM on subsequent calls
- Duplicated with each tool invocation
Impact:
- State size grows exponentially (each tool call adds another copy of runtime with full history)
- Token consumption increases by 5,000-20,000 tokens per tool call
- If agent calls 5 tools in a conversation = +750KB-1.5MB just from duplicated runtime in state
3. Network Latency
For remote MCP servers (HTTP/streamable-http transport):
- Large payloads increase network transfer time
- Lambda cold starts are more frequent due to memory pressure
- Overall latency increases significantly
Example
Before 0.1.14:
# Tool receives only its parameters
{
'parameters': {'q': 'search query', 'num': 10}
}After 0.1.14:
# Tool receives parameters + massive runtime object
{
'parameters': {'q': 'search query', 'num': 10},
'runtime': ToolRuntime(
state={
'messages': [
HumanMessage(content='...', id='...'),
AIMessage(content=[...], tool_calls=[...], usage_metadata={...})
]
},
context=None,
config={
'tags': ['dev'],
'metadata': {...},
'callbacks': <AsyncCallbackManager object at 0x...>,
'recursion_limit': 200,
'configurable': {
'__pregel_runtime': Runtime(...),
'__pregel_task_id': '...',
'__pregel_send': <deque object>,
'__pregel_read': functools.partial(...),
'__pregel_checkpointer': <AgentCoreMemorySaver object>,
'__pregel_scratchpad': PregelScratchpad(...),
'__pregel_call': functools.partial(...)
}
},
stream_writer=<function>,
tool_call_id='...',
store=<AgentCoreMemoryStore object>
)
}This massive object is then serialized to JSON and sent over HTTP, and saved in the conversation state.
Proposed Solutions
Option 1: Add inject_runtime Parameter (Preferred)
agent = create_agent(
model=llm,
tools=tools,
store=memory_store,
inject_runtime=False, # <-- New parameter to disable runtime injection
)Option 2: Add runtime_fields Parameter for Selective Injection
agent = create_agent(
model=llm,
tools=tools,
store=memory_store,
runtime_fields=['tool_call_id', 'context'], # <-- Only inject specific fields
)This would inject a lightweight object:
{
'parameters': {...},
'runtime': {
'tool_call_id': 'call_abc123',
'context': {'user_id': '123'}
}
}Option 3: Make Runtime Optional in Tool Signature
Allow tools to opt-in to runtime access via type hints:
@mcp.tool()
async def my_tool(param: str, runtime: Optional[ToolRuntime] = None):
# Only tools that explicitly request runtime receive it
passCurrent Workarounds
We've had to implement workarounds to filter runtime at multiple layers:
- In middleware - Remove runtime from
AIMessage.tool_callsbefore saving to state - In MCP server - Filter runtime from parameters before Lambda invocation
- Tool wrapper - Wrap each tool's
ainvoketo strip runtime from kwargs
These workarounds are fragile and shouldn't be necessary.
Use Case
Our architecture:
- LangGraph agent in AWS Lambda (client)
- MCP server in ECS Fargate (remote HTTP endpoint)
- Tools are AWS Lambda functions invoked via MCP server
- Conversations can have 10-50+ tool calls
- State is persisted via
langgraph-checkpoint-aws
Environment
langchain-mcp-adapters==0.1.14langgraph>=1.0.3langchain>=1.1.0- MCP transport:
streamable_http(remote server) - Agent created with
create_agent()+storeparameter
Request
Please add a way to:
- Disable runtime injection entirely, OR
- Select which fields to inject, OR
- Make runtime opt-in per tool
This would allow us to benefit from the store feature for long-term memory without the payload/state bloat from runtime injection.
Thank you for considering this feature request!