Skip to content

Can we deactivated or choose the attribute in runtime tool? #376

@AlcebiadesFilho

Description

@AlcebiadesFilho

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 HumanMessage and AIMessage objects 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
    pass

Current Workarounds

We've had to implement workarounds to filter runtime at multiple layers:

  1. In middleware - Remove runtime from AIMessage.tool_calls before saving to state
  2. In MCP server - Filter runtime from parameters before Lambda invocation
  3. Tool wrapper - Wrap each tool's ainvoke to 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.14
  • langgraph>=1.0.3
  • langchain>=1.1.0
  • MCP transport: streamable_http (remote server)
  • Agent created with create_agent() + store parameter

Request

Please add a way to:

  1. Disable runtime injection entirely, OR
  2. Select which fields to inject, OR
  3. 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!

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions