Skip to content

Conversation

jeff52415
Copy link

@jeff52415 jeff52415 commented Sep 1, 2025

Implement MCP structuredContent support to resolve data loss issue

This branch addresses Issue #283 by implementing proper support for MCP's
structuredContent field in CallToolResult responses. Previously, the
langchain-mcp-adapters library ignored the structuredContent
field.

Key Changes:
• Refactored _convert_call_tool_result() to handle structuredContent
• Updated return format to use LangChain's content_and_artifact pattern

The implementation ensures:

  • structuredContent is preserved in artifact["structuredContent"]
  • Backward compatibility with existing text/non-text content handling
  • Compliance with MCP specification (2025-06-18)

Example:

from langchain_core.messages import ToolCall
result = await tool.ainvoke(
    ToolCall(
        name="tool_name",
        args={....},
        id="call_123",  # any unique string ID
        type="tool_call",
    )
    
)
print(result.content)
if result.artifact:
    print(result.artifact.get("structuredContent", None))
    print(result.artifact.get("nonText", None))

Fixes: #283

@IoannisMaras
Copy link

Can confirm this works . And I need it as well

@eyurtsev eyurtsev self-assigned this Sep 8, 2025
@eyurtsev
Copy link
Collaborator

eyurtsev commented Sep 9, 2025

@eyurtsev
Copy link
Collaborator

eyurtsev commented Sep 9, 2025

How is your application consuming this information? Do you have an agent or is it a fixed workflow where you access the results of the tool call? If it's an agent, do you loop through the message history and check the artifacts field?

@jeff52415 jeff52415 force-pushed the feat/integrate-mcp-structured-content branch from 81f6de3 to 00be302 Compare September 10, 2025 00:26
@jeff52415
Copy link
Author

@eyurtsev Thanks for asking.
content_and_artifact pattern only propagates through the full flow when emitted via the ToolCall interface.
This is how I use to integrate structured outputs into the agent pipeline.

async def tool_node(state: AgentState) -> dict[str, list[ToolMessage]]:
    """Execute tool calls by constructing ToolCall objects for each call."""
    last = state["messages"][-1]
    outputs: list[ToolMessage] = []
    for call in getattr(last, "tool_calls", []) or []:
        # Build ToolCall to satisfy adapters requiring ToolCall input.
        name = call["name"]
        args = call["args"]
        if call.get("id") is None:
            call_id = str(uuid.uuid4())
        else:
            call_id = call["id"]
        tool_call = ToolCall(name=name, args=args, id=call_id, type="tool_call")

        tool = tools_by_name[name]
        if hasattr(tool, "ainvoke"):
            result = await tool.ainvoke(tool_call)  # type: ignore[attr-defined]
        else:
            # Fall back to sync execution if async not available
            result = tool.invoke(tool_call)
    output = "some post-processing......."
    return {"messages": outputs}

workflow = StateGraph(AgentState)
workflow.add_node("tools", tool_node)

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.

langchain-mcp-adapters ignores structuredContent field from MCP tool responses
3 participants