Source code for agentstr.mcp.langgraph
from typing import Any
from langchain_core.tools import BaseTool, StructuredTool, ToolException
from mcp.types import (
CallToolResult,
EmbeddedResource,
ImageContent,
TextContent,
)
from agentstr.nostr_mcp_client import NostrMCPClient
NonTextContent = ImageContent | EmbeddedResource
def _convert_call_tool_result(
call_tool_result: CallToolResult,
) -> tuple[str | list[str], list[NonTextContent] | None]:
"""Convert a CallToolResult into a format suitable for LangGraph tools.
Args:
call_tool_result: The result from calling an MCP tool.
Returns:
A tuple containing:
- The tool's output as a string or list of strings
- A list of non-text content (images, embedded resources) or None
Raises:
ToolException: If the tool call resulted in an error.
"""
text_contents: list[TextContent] = []
non_text_contents = []
for content in call_tool_result.content:
if isinstance(content, TextContent):
text_contents.append(content)
else:
non_text_contents.append(content)
tool_content: str | list[str] = [content.text for content in text_contents]
if not text_contents:
tool_content = ""
elif len(text_contents) == 1:
tool_content = tool_content[0]
if call_tool_result.isError:
raise ToolException(tool_content)
return tool_content, non_text_contents or None
[docs]
async def to_langgraph_tools(nostr_mcp_client: NostrMCPClient) -> list[BaseTool]:
"""Convert tools from the MCP client to LangGraph tools.
Args:
nostr_mcp_client: An instance of NostrMCPClient to fetch tools from.
Returns:
A list of LangGraph BaseTool objects that wrap the MCP tools.
"""
# Load tools from this server
tools = await nostr_mcp_client.list_tools()
server_tools = []
def call_tool(
tool_name: str,
):
async def inner(**arguments: dict[str, Any]):
call_tool_result = await nostr_mcp_client.call_tool(tool_name, arguments)
call_tool_result = CallToolResult(**call_tool_result)
result = _convert_call_tool_result(call_tool_result)
return result, None
return inner
for tool in tools["tools"]:
server_tools.append(
StructuredTool(
name=tool["name"],
description=tool.get("description") or "",
metadata={"satoshis": tool.get("satoshis", 0)},
args_schema=tool["inputSchema"],
coroutine=call_tool(tool["name"]),
response_format="content_and_artifact",
),
)
return server_tools