Add MCP adapters to autogen-ext (#5251)

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
This commit is contained in:
Richárd Gyikó 2025-02-09 06:20:00 +01:00 committed by GitHub
parent 7fc7f383f0
commit 5308b76d5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 803 additions and 1 deletions

View File

@ -54,6 +54,7 @@ python/autogen_ext.models.replay
python/autogen_ext.models.azure
python/autogen_ext.models.semantic_kernel
python/autogen_ext.tools.langchain
python/autogen_ext.tools.mcp
python/autogen_ext.tools.graphrag
python/autogen_ext.tools.code_execution
python/autogen_ext.tools.semantic_kernel

View File

@ -0,0 +1,8 @@
autogen\_ext.tools.mcp
======================
.. automodule:: autogen_ext.tools.mcp
:members:
:undoc-members:
:show-inheritance:

View File

@ -112,6 +112,11 @@ semantic-kernel-all = [
rich = ["rich>=13.9.4"]
mcp = [
"mcp>=1.1.3",
"json-schema-to-pydantic>=0.2.2"
]
[tool.hatch.build.targets.wheel]
packages = ["src/autogen_ext"]

View File

@ -1,6 +1,6 @@
import json
from typing import Any, Literal, Mapping, Optional, Sequence
import warnings
from typing import Any, Literal, Mapping, Optional, Sequence
from autogen_core import FunctionCall
from autogen_core._cancellation_token import CancellationToken

View File

@ -0,0 +1,13 @@
from ._config import McpServerParams, SseServerParams, StdioServerParams
from ._factory import mcp_server_tools
from ._sse import SseMcpToolAdapter
from ._stdio import StdioMcpToolAdapter
__all__ = [
"StdioMcpToolAdapter",
"StdioServerParams",
"SseMcpToolAdapter",
"SseServerParams",
"McpServerParams",
"mcp_server_tools",
]

View File

@ -0,0 +1,101 @@
from abc import ABC
from typing import Any, Generic, Type, TypeVar
from autogen_core import CancellationToken
from autogen_core.tools import BaseTool
from json_schema_to_pydantic import create_model
from mcp import Tool
from pydantic import BaseModel
from ._config import McpServerParams
from ._session import create_mcp_server_session
TServerParams = TypeVar("TServerParams", bound=McpServerParams)
class McpToolAdapter(BaseTool[BaseModel, Any], ABC, Generic[TServerParams]):
"""
Base adapter class for MCP tools to make them compatible with AutoGen.
Args:
server_params (TServerParams): Parameters for the MCP server connection.
tool (Tool): The MCP tool to wrap.
"""
component_type = "tool"
def __init__(self, server_params: TServerParams, tool: Tool) -> None:
self._tool = tool
self._server_params = server_params
# Extract name and description
name = tool.name
description = tool.description or ""
# Create the input model from the tool's schema
input_model = create_model(tool.inputSchema)
# Use Any as return type since MCP tool returns can vary
return_type: Type[Any] = object
super().__init__(input_model, return_type, name, description)
async def run(self, args: BaseModel, cancellation_token: CancellationToken) -> Any:
"""
Run the MCP tool with the provided arguments.
Args:
args (BaseModel): The arguments to pass to the tool.
cancellation_token (CancellationToken): Token to signal cancellation.
Returns:
Any: The result of the tool execution.
Raises:
Exception: If the operation is cancelled or the tool execution fails.
"""
kwargs = args.model_dump()
try:
async with create_mcp_server_session(self._server_params) as session:
await session.initialize()
if cancellation_token.is_cancelled():
raise Exception("Operation cancelled")
result = await session.call_tool(self._tool.name, kwargs) # type: ignore
if result.isError:
raise Exception(f"MCP tool execution failed: {result.content}")
return result.content
except Exception as e:
raise Exception(str(e)) from e
@classmethod
async def from_server_params(cls, server_params: TServerParams, tool_name: str) -> "McpToolAdapter[TServerParams]":
"""
Create an instance of McpToolAdapter from server parameters and tool name.
Args:
server_params (TServerParams): Parameters for the MCP server connection.
tool_name (str): The name of the tool to wrap.
Returns:
McpToolAdapter[TServerParams]: An instance of McpToolAdapter.
Raises:
ValueError: If the tool with the specified name is not found.
"""
async with create_mcp_server_session(server_params) as session:
await session.initialize()
tools_response = await session.list_tools()
matching_tool = next((t for t in tools_response.tools if t.name == tool_name), None)
if matching_tool is None:
raise ValueError(
f"Tool '{tool_name}' not found, available tools: {', '.join([t.name for t in tools_response.tools])}"
)
return cls(server_params=server_params, tool=matching_tool)

View File

@ -0,0 +1,22 @@
from typing import Any, TypeAlias
from mcp import StdioServerParameters
from pydantic import BaseModel
class StdioServerParams(StdioServerParameters):
"""Parameters for connecting to an MCP server over STDIO."""
pass
class SseServerParams(BaseModel):
"""Parameters for connecting to an MCP server over SSE."""
url: str
headers: dict[str, Any] | None = None
timeout: float = 5
sse_read_timeout: float = 60 * 5
McpServerParams: TypeAlias = StdioServerParams | SseServerParams

View File

@ -0,0 +1,134 @@
from ._config import McpServerParams, SseServerParams, StdioServerParams
from ._session import create_mcp_server_session
from ._sse import SseMcpToolAdapter
from ._stdio import StdioMcpToolAdapter
async def mcp_server_tools(
server_params: McpServerParams,
) -> list[StdioMcpToolAdapter | SseMcpToolAdapter]:
"""Creates a list of MCP tool adapters that can be used with AutoGen agents.
This factory function connects to an MCP server and returns adapters for all available tools.
The adapters can be directly assigned to an AutoGen agent's tools list.
Args:
server_params (McpServerParams): Connection parameters for the MCP server.
Can be either StdioServerParams for command-line tools or
SseServerParams for HTTP/SSE services.
Returns:
list[StdioMcpToolAdapter | SseMcpToolAdapter]: A list of tool adapters ready to use
with AutoGen agents.
Examples:
**Local file system MCP service over standard I/O example:**
Install the filesystem server package from npm (requires Node.js 16+ and npm).
.. code-block:: bash
npm install -g @modelcontextprotocol/server-filesystem
Create an agent that can use all tools from the local filesystem MCP server.
.. code-block:: python
import asyncio
from pathlib import Path
from autogen_ext.models.openai import OpenAIChatCompletionClient
from autogen_ext.tools.mcp import StdioServerParams, mcp_server_tools
from autogen_agentchat.agents import AssistantAgent
from autogen_core import CancellationToken
async def main() -> None:
# Setup server params for local filesystem access
desktop = str(Path.home() / "Desktop")
server_params = StdioServerParams(
command="npx.cmd", args=["-y", "@modelcontextprotocol/server-filesystem", desktop]
)
# Get all available tools from the server
tools = await mcp_server_tools(server_params)
# Create an agent that can use all the tools
agent = AssistantAgent(
name="file_manager",
model_client=OpenAIChatCompletionClient(model="gpt-4"),
tools=tools, # type: ignore
)
# The agent can now use any of the filesystem tools
await agent.run(task="Create a file called test.txt with some content", cancellation_token=CancellationToken())
if __name__ == "__main__":
asyncio.run(main())
**Local fetch MCP service over standard I/O example:**
Install the `mcp-server-fetch` package.
.. code-block:: bash
pip install mcp-server-fetch
Create an agent that can use the `fetch` tool from the local MCP server.
.. code-block:: python
import asyncio
from autogen_agentchat.agents import AssistantAgent
from autogen_ext.models.openai import OpenAIChatCompletionClient
from autogen_ext.tools.mcp import StdioServerParams, mcp_server_tools
async def main() -> None:
# Get the fetch tool from mcp-server-fetch.
fetch_mcp_server = StdioServerParams(command="uvx", args=["mcp-server-fetch"])
tools = await mcp_server_tools(fetch_mcp_server)
# Create an agent that can use the fetch tool.
model_client = OpenAIChatCompletionClient(model="gpt-4o")
agent = AssistantAgent(name="fetcher", model_client=model_client, tools=tools, reflect_on_tool_use=True) # type: ignore
# Let the agent fetch the content of a URL and summarize it.
result = await agent.run(task="Summarize the content of https://en.wikipedia.org/wiki/Seattle")
print(result.messages[-1].content)
asyncio.run(main())
**Remote MCP service over SSE example:**
.. code-block:: python
from autogen_ext.tools.mcp import SseServerParams, mcp_server_tools
async def main() -> None:
# Setup server params for remote service
server_params = SseServerParams(url="https://api.example.com/mcp", headers={"Authorization": "Bearer token"})
# Get all available tools
tools = await mcp_server_tools(server_params)
# Create an agent with all tools
agent = AssistantAgent(name="tool_user", model_client=OpenAIChatCompletionClient(model="gpt-4"), tools=tools) # type: ignore
For more examples and detailed usage, see the samples directory in the package repository.
"""
async with create_mcp_server_session(server_params) as session:
await session.initialize()
tools = await session.list_tools()
if isinstance(server_params, StdioServerParams):
return [StdioMcpToolAdapter(server_params=server_params, tool=tool) for tool in tools.tools]
elif isinstance(server_params, SseServerParams):
return [SseMcpToolAdapter(server_params=server_params, tool=tool) for tool in tools.tools]
raise ValueError(f"Unsupported server params type: {type(server_params)}")

View File

@ -0,0 +1,23 @@
from contextlib import asynccontextmanager
from typing import AsyncGenerator
from mcp import ClientSession
from mcp.client.sse import sse_client
from mcp.client.stdio import stdio_client
from ._config import McpServerParams, SseServerParams, StdioServerParams
@asynccontextmanager
async def create_mcp_server_session(
server_params: McpServerParams,
) -> AsyncGenerator[ClientSession, None]:
"""Create an MCP client session for the given server parameters."""
if isinstance(server_params, StdioServerParams):
async with stdio_client(server_params) as (read, write):
async with ClientSession(read_stream=read, write_stream=write) as session:
yield session
elif isinstance(server_params, SseServerParams):
async with sse_client(**server_params.model_dump()) as (read, write):
async with ClientSession(read_stream=read, write_stream=write) as session:
yield session

View File

@ -0,0 +1,104 @@
from autogen_core import Component
from mcp import Tool
from pydantic import BaseModel
from typing_extensions import Self
from ._base import McpToolAdapter
from ._config import SseServerParams
class SseMcpToolAdapterConfig(BaseModel):
"""Configuration for the MCP tool adapter."""
server_params: SseServerParams
tool: Tool
class SseMcpToolAdapter(
McpToolAdapter[SseServerParams],
Component[SseMcpToolAdapterConfig],
):
"""
Allows you to wrap an MCP tool running over Server-Sent Events (SSE) and make it available to AutoGen.
This adapter enables using MCP-compatible tools that communicate over HTTP with SSE
with AutoGen agents. Common use cases include integrating with remote MCP services,
cloud-based tools, and web APIs that implement the Model Context Protocol (MCP).
Args:
server_params (SseServerParameters): Parameters for the MCP server connection,
including URL, headers, and timeouts
tool (Tool): The MCP tool to wrap
Examples:
Use a remote translation service that implements MCP over SSE to create tools
that allow AutoGen agents to perform translations:
.. code-block:: python
import asyncio
from autogen_ext.models.openai import OpenAIChatCompletionClient
from autogen_ext.tools.mcp import SseMcpToolAdapter, SseServerParams
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.ui import Console
from autogen_core import CancellationToken
async def main() -> None:
# Create server params for the remote MCP service
server_params = SseServerParams(
url="https://api.example.com/mcp",
headers={"Authorization": "Bearer your-api-key", "Content-Type": "application/json"},
timeout=30, # Connection timeout in seconds
)
# Get the translation tool from the server
adapter = await SseMcpToolAdapter.from_server_params(server_params, "translate")
# Create an agent that can use the translation tool
model_client = OpenAIChatCompletionClient(model="gpt-4")
agent = AssistantAgent(
name="translator",
model_client=model_client,
tools=[adapter],
system_message="You are a helpful translation assistant.",
)
# Let the agent translate some text
await Console(
agent.run_stream(task="Translate 'Hello, how are you?' to Spanish", cancellation_token=CancellationToken())
)
if __name__ == "__main__":
asyncio.run(main())
"""
component_config_schema = SseMcpToolAdapterConfig
component_provider_override = "autogen_ext.tools.mcp.SseMcpToolAdapter"
def __init__(self, server_params: SseServerParams, tool: Tool) -> None:
super().__init__(server_params=server_params, tool=tool)
def _to_config(self) -> SseMcpToolAdapterConfig:
"""
Convert the adapter to its configuration representation.
Returns:
SseMcpToolAdapterConfig: The configuration of the adapter.
"""
return SseMcpToolAdapterConfig(server_params=self._server_params, tool=self._tool)
@classmethod
def _from_config(cls, config: SseMcpToolAdapterConfig) -> Self:
"""
Create an instance of SseMcpToolAdapter from its configuration.
Args:
config (SseMcpToolAdapterConfig): The configuration of the adapter.
Returns:
SseMcpToolAdapter: An instance of SseMcpToolAdapter.
"""
return cls(server_params=config.server_params, tool=config.tool)

View File

@ -0,0 +1,61 @@
from autogen_core import Component
from mcp import Tool
from pydantic import BaseModel
from typing_extensions import Self
from ._base import McpToolAdapter
from ._config import StdioServerParams
class StdioMcpToolAdapterConfig(BaseModel):
"""Configuration for the MCP tool adapter."""
server_params: StdioServerParams
tool: Tool
class StdioMcpToolAdapter(
McpToolAdapter[StdioServerParams],
Component[StdioMcpToolAdapterConfig],
):
"""Allows you to wrap an MCP tool running over STDIO and make it available to AutoGen.
This adapter enables using MCP-compatible tools that communicate over standard input/output
with AutoGen agents. Common use cases include wrapping command-line tools and local services
that implement the Model Context Protocol (MCP).
Args:
server_params (StdioServerParams): Parameters for the MCP server connection,
including command to run and its arguments
tool (Tool): The MCP tool to wrap
See :func:`~autogen_ext.tools.mcp.mcp_server_tools` for examples.
"""
component_config_schema = StdioMcpToolAdapterConfig
component_provider_override = "autogen_ext.tools.mcp.StdioMcpToolAdapter"
def __init__(self, server_params: StdioServerParams, tool: Tool) -> None:
super().__init__(server_params=server_params, tool=tool)
def _to_config(self) -> StdioMcpToolAdapterConfig:
"""
Convert the adapter to its configuration representation.
Returns:
StdioMcpToolAdapterConfig: The configuration of the adapter.
"""
return StdioMcpToolAdapterConfig(server_params=self._server_params, tool=self._tool)
@classmethod
def _from_config(cls, config: StdioMcpToolAdapterConfig) -> Self:
"""
Create an instance of StdioMcpToolAdapter from its configuration.
Args:
config (StdioMcpToolAdapterConfig): The configuration of the adapter.
Returns:
StdioMcpToolAdapter: An instance of StdioMcpToolAdapter.
"""
return cls(server_params=config.server_params, tool=config.tool)

View File

@ -0,0 +1,271 @@
from unittest.mock import AsyncMock, MagicMock
import pytest
from autogen_core import CancellationToken
from autogen_ext.tools.mcp import (
SseMcpToolAdapter,
SseServerParams,
StdioMcpToolAdapter,
StdioServerParams,
)
from json_schema_to_pydantic import create_model
from mcp import ClientSession, Tool
@pytest.fixture
def sample_tool() -> Tool:
return Tool(
name="test_tool",
description="A test tool",
inputSchema={
"type": "object",
"properties": {"test_param": {"type": "string"}},
"required": ["test_param"],
},
)
@pytest.fixture
def sample_server_params() -> StdioServerParams:
return StdioServerParams(command="echo", args=["test"])
@pytest.fixture
def sample_sse_tool() -> Tool:
return Tool(
name="test_sse_tool",
description="A test SSE tool",
inputSchema={
"type": "object",
"properties": {"test_param": {"type": "string"}},
"required": ["test_param"],
},
)
@pytest.fixture
def mock_sse_session() -> AsyncMock:
session = AsyncMock(spec=ClientSession)
session.initialize = AsyncMock()
session.call_tool = AsyncMock()
session.list_tools = AsyncMock()
return session
@pytest.fixture
def mock_session() -> AsyncMock:
session = AsyncMock(spec=ClientSession)
session.initialize = AsyncMock()
session.call_tool = AsyncMock()
session.list_tools = AsyncMock()
return session
@pytest.fixture
def mock_tool_response() -> MagicMock:
response = MagicMock()
response.isError = False
response.content = {"result": "test_output"}
return response
@pytest.fixture
def cancellation_token() -> CancellationToken:
return CancellationToken()
def test_adapter_config_serialization(sample_tool: Tool, sample_server_params: StdioServerParams) -> None:
"""Test that adapter can be saved to and loaded from config."""
original_adapter = StdioMcpToolAdapter(server_params=sample_server_params, tool=sample_tool)
config = original_adapter.dump_component()
loaded_adapter = StdioMcpToolAdapter.load_component(config)
# Test that the loaded adapter has the same properties
assert loaded_adapter.name == "test_tool"
assert loaded_adapter.description == "A test tool"
# Verify schema structure
schema = loaded_adapter.schema
assert "parameters" in schema, "Schema must have parameters"
params_schema = schema["parameters"]
assert isinstance(params_schema, dict), "Parameters must be a dict"
assert "type" in params_schema, "Parameters must have type"
assert "required" in params_schema, "Parameters must have required fields"
assert "properties" in params_schema, "Parameters must have properties"
# Compare schema content
assert params_schema["type"] == sample_tool.inputSchema["type"]
assert params_schema["required"] == sample_tool.inputSchema["required"]
assert (
params_schema["properties"]["test_param"]["type"] == sample_tool.inputSchema["properties"]["test_param"]["type"]
)
@pytest.mark.asyncio
async def test_mcp_tool_execution(
sample_tool: Tool,
sample_server_params: StdioServerParams,
mock_session: AsyncMock,
mock_tool_response: MagicMock,
cancellation_token: CancellationToken,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test that adapter properly executes tools through ClientSession."""
mock_context = AsyncMock()
mock_context.__aenter__.return_value = mock_session
monkeypatch.setattr(
"autogen_ext.tools.mcp._base.create_mcp_server_session",
lambda *args, **kwargs: mock_context, # type: ignore
)
mock_session.call_tool.return_value = mock_tool_response
adapter = StdioMcpToolAdapter(server_params=sample_server_params, tool=sample_tool)
result = await adapter.run(
args=create_model(sample_tool.inputSchema)(**{"test_param": "test"}),
cancellation_token=cancellation_token,
)
assert result == mock_tool_response.content
mock_session.initialize.assert_called_once()
mock_session.call_tool.assert_called_once()
@pytest.mark.asyncio
async def test_adapter_from_server_params(
sample_tool: Tool,
sample_server_params: StdioServerParams,
mock_session: AsyncMock,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test that adapter can be created from server parameters."""
mock_context = AsyncMock()
mock_context.__aenter__.return_value = mock_session
monkeypatch.setattr(
"autogen_ext.tools.mcp._base.create_mcp_server_session",
lambda *args, **kwargs: mock_context, # type: ignore
)
mock_session.list_tools.return_value.tools = [sample_tool]
adapter = await StdioMcpToolAdapter.from_server_params(sample_server_params, "test_tool")
assert isinstance(adapter, StdioMcpToolAdapter)
assert adapter.name == "test_tool"
assert adapter.description == "A test tool"
# Verify schema structure
schema = adapter.schema
assert "parameters" in schema, "Schema must have parameters"
params_schema = schema["parameters"]
assert isinstance(params_schema, dict), "Parameters must be a dict"
assert "type" in params_schema, "Parameters must have type"
assert "required" in params_schema, "Parameters must have required fields"
assert "properties" in params_schema, "Parameters must have properties"
# Compare schema content
assert params_schema["type"] == sample_tool.inputSchema["type"]
assert params_schema["required"] == sample_tool.inputSchema["required"]
assert (
params_schema["properties"]["test_param"]["type"] == sample_tool.inputSchema["properties"]["test_param"]["type"]
)
@pytest.mark.asyncio
async def test_sse_adapter_config_serialization(sample_sse_tool: Tool) -> None:
"""Test that SSE adapter can be saved to and loaded from config."""
params = SseServerParams(url="http://test-url")
original_adapter = SseMcpToolAdapter(server_params=params, tool=sample_sse_tool)
config = original_adapter.dump_component()
loaded_adapter = SseMcpToolAdapter.load_component(config)
# Test that the loaded adapter has the same properties
assert loaded_adapter.name == "test_sse_tool"
assert loaded_adapter.description == "A test SSE tool"
# Verify schema structure
schema = loaded_adapter.schema
assert "parameters" in schema, "Schema must have parameters"
params_schema = schema["parameters"]
assert isinstance(params_schema, dict), "Parameters must be a dict"
assert "type" in params_schema, "Parameters must have type"
assert "required" in params_schema, "Parameters must have required fields"
assert "properties" in params_schema, "Parameters must have properties"
# Compare schema content
assert params_schema["type"] == sample_sse_tool.inputSchema["type"]
assert params_schema["required"] == sample_sse_tool.inputSchema["required"]
assert (
params_schema["properties"]["test_param"]["type"]
== sample_sse_tool.inputSchema["properties"]["test_param"]["type"]
)
@pytest.mark.asyncio
async def test_sse_tool_execution(
sample_sse_tool: Tool,
mock_sse_session: AsyncMock,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test that SSE adapter properly executes tools through ClientSession."""
params = SseServerParams(url="http://test-url")
mock_context = AsyncMock()
mock_context.__aenter__.return_value = mock_sse_session
mock_sse_session.call_tool.return_value = MagicMock(isError=False, content={"result": "test_output"})
monkeypatch.setattr(
"autogen_ext.tools.mcp._base.create_mcp_server_session",
lambda *args, **kwargs: mock_context, # type: ignore
)
adapter = SseMcpToolAdapter(server_params=params, tool=sample_sse_tool)
result = await adapter.run(
args=create_model(sample_sse_tool.inputSchema)(**{"test_param": "test"}),
cancellation_token=CancellationToken(),
)
assert result == mock_sse_session.call_tool.return_value.content
mock_sse_session.initialize.assert_called_once()
mock_sse_session.call_tool.assert_called_once()
@pytest.mark.asyncio
async def test_sse_adapter_from_server_params(
sample_sse_tool: Tool,
mock_sse_session: AsyncMock,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test that SSE adapter can be created from server parameters."""
params = SseServerParams(url="http://test-url")
mock_context = AsyncMock()
mock_context.__aenter__.return_value = mock_sse_session
monkeypatch.setattr(
"autogen_ext.tools.mcp._base.create_mcp_server_session",
lambda *args, **kwargs: mock_context, # type: ignore
)
mock_sse_session.list_tools.return_value.tools = [sample_sse_tool]
adapter = await SseMcpToolAdapter.from_server_params(params, "test_sse_tool")
assert isinstance(adapter, SseMcpToolAdapter)
assert adapter.name == "test_sse_tool"
assert adapter.description == "A test SSE tool"
# Verify schema structure
schema = adapter.schema
assert "parameters" in schema, "Schema must have parameters"
params_schema = schema["parameters"]
assert isinstance(params_schema, dict), "Parameters must be a dict"
assert "type" in params_schema, "Parameters must have type"
assert "required" in params_schema, "Parameters must have required fields"
assert "properties" in params_schema, "Parameters must have properties"
# Compare schema content
assert params_schema["type"] == sample_sse_tool.inputSchema["type"]
assert params_schema["required"] == sample_sse_tool.inputSchema["required"]
assert (
params_schema["properties"]["test_param"]["type"]
== sample_sse_tool.inputSchema["properties"]["test_param"]["type"]
)

View File

@ -2,6 +2,7 @@ version = 1
requires-python = ">=3.10, <3.13"
resolution-markers = [
"python_full_version >= '3.12.4' and sys_platform == 'darwin'",
"python_version < '0'",
"python_full_version >= '3.12' and python_full_version < '3.12.4' and sys_platform == 'darwin'",
"python_full_version >= '3.12.4' and platform_machine == 'aarch64' and sys_platform == 'linux'",
"python_full_version >= '3.12' and python_full_version < '3.12.4' and platform_machine == 'aarch64' and sys_platform == 'linux'",
@ -611,6 +612,10 @@ magentic-one = [
{ name = "pillow" },
{ name = "playwright" },
]
mcp = [
{ name = "json-schema-to-pydantic" },
{ name = "mcp" },
]
openai = [
{ name = "aiofiles" },
{ name = "openai" },
@ -694,10 +699,12 @@ requires-dist = [
{ name = "graphrag", marker = "extra == 'graphrag'", specifier = ">=1.0.1" },
{ name = "grpcio", marker = "extra == 'grpc'", specifier = "~=1.70.0" },
{ name = "ipykernel", marker = "extra == 'jupyter-executor'", specifier = ">=6.29.5" },
{ name = "json-schema-to-pydantic", marker = "extra == 'mcp'", specifier = ">=0.2.2" },
{ name = "langchain-core", marker = "extra == 'langchain'", specifier = "~=0.3.3" },
{ name = "markitdown", marker = "extra == 'file-surfer'", specifier = ">=0.0.1a2" },
{ name = "markitdown", marker = "extra == 'magentic-one'", specifier = ">=0.0.1a2" },
{ name = "markitdown", marker = "extra == 'web-surfer'", specifier = ">=0.0.1a2" },
{ name = "mcp", marker = "extra == 'mcp'", specifier = ">=1.1.3" },
{ name = "nbclient", marker = "extra == 'jupyter-executor'", specifier = ">=0.10.2" },
{ name = "openai", marker = "extra == 'openai'", specifier = ">=1.52.2" },
{ name = "openai-whisper", marker = "extra == 'video-surfer'" },
@ -2813,6 +2820,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fd/2d/79a46330c4b97ee90dd403fb0d267da7b25b24d7db604c5294e5c57d5f7c/json_repair-0.30.3-py3-none-any.whl", hash = "sha256:63bb588162b0958ae93d85356ecbe54c06b8c33f8a4834f93fa2719ea669804e", size = 18951 },
]
[[package]]
name = "json-schema-to-pydantic"
version = "0.2.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pydantic" },
]
sdist = { url = "https://files.pythonhosted.org/packages/13/87/af1870beea329744a759349b972b309f8c95ae21e986e387e19733b85cc9/json_schema_to_pydantic-0.2.2.tar.gz", hash = "sha256:685db8d93aa29ccd257b2803fcd9a956c527e5fb108a523cbfe8cac1239b3785", size = 34158 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/25/8d/3c738e4b4b041269c4a506544b90e9ba924bbd800c8d496ed3e5a6da0265/json_schema_to_pydantic-0.2.2-py3-none-any.whl", hash = "sha256:01b82d234f2b482a273e117e29d063b6b86021a250035873d6eec4b85b70e64d", size = 11396 },
]
[[package]]
name = "jsonpatch"
version = "1.33"
@ -3854,6 +3873,23 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 },
]
[[package]]
name = "mcp"
version = "1.1.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "httpx" },
{ name = "httpx-sse" },
{ name = "pydantic" },
{ name = "sse-starlette" },
{ name = "starlette" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f7/60/66ebfd280b197f9a9d074c9e46cb1ac3186a32d12e6bd0425c24fe7cf7e8/mcp-1.1.3.tar.gz", hash = "sha256:af11018b8e9153cdd25f3722ec639fe7a462c00213a330fd6f593968341a9883", size = 57903 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b8/08/cfcfa13e41f8d27503c51a8cbf1939d720073ace92469d08655bb5de1b24/mcp-1.1.3-py3-none-any.whl", hash = "sha256:71462d6cd7c06c14689dfcf110ff22286ba1b608cfc3515c0a5cbe33d131731a", size = 36997 },
]
[[package]]
name = "mdit-py-plugins"
version = "0.4.2"
@ -4308,6 +4344,7 @@ name = "nvidia-cublas-cu12"
version = "12.4.5.8"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7f/7f/7fbae15a3982dc9595e49ce0f19332423b260045d0a6afe93cdbe2f1f624/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0f8aa1706812e00b9f19dfe0cdb3999b092ccb8ca168c0db5b8ea712456fd9b3", size = 363333771 },
{ url = "https://files.pythonhosted.org/packages/ae/71/1c91302526c45ab494c23f61c7a84aa568b8c1f9d196efa5993957faf906/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b", size = 363438805 },
]
@ -4316,6 +4353,7 @@ name = "nvidia-cuda-cupti-cu12"
version = "12.4.127"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/93/b5/9fb3d00386d3361b03874246190dfec7b206fd74e6e287b26a8fcb359d95/nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:79279b35cf6f91da114182a5ce1864997fd52294a87a16179ce275773799458a", size = 12354556 },
{ url = "https://files.pythonhosted.org/packages/67/42/f4f60238e8194a3106d06a058d494b18e006c10bb2b915655bd9f6ea4cb1/nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb", size = 13813957 },
]
@ -4324,6 +4362,7 @@ name = "nvidia-cuda-nvrtc-cu12"
version = "12.4.127"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/77/aa/083b01c427e963ad0b314040565ea396f914349914c298556484f799e61b/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0eedf14185e04b76aa05b1fea04133e59f465b6f960c0cbf4e37c3cb6b0ea198", size = 24133372 },
{ url = "https://files.pythonhosted.org/packages/2c/14/91ae57cd4db3f9ef7aa99f4019cfa8d54cb4caa7e00975df6467e9725a9f/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338", size = 24640306 },
]
@ -4332,6 +4371,7 @@ name = "nvidia-cuda-runtime-cu12"
version = "12.4.127"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a1/aa/b656d755f474e2084971e9a297def515938d56b466ab39624012070cb773/nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:961fe0e2e716a2a1d967aab7caee97512f71767f852f67432d572e36cb3a11f3", size = 894177 },
{ url = "https://files.pythonhosted.org/packages/ea/27/1795d86fe88ef397885f2e580ac37628ed058a92ed2c39dc8eac3adf0619/nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5", size = 883737 },
]
@ -4354,6 +4394,7 @@ dependencies = [
{ name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/7a/8a/0e728f749baca3fbeffad762738276e5df60851958be7783af121a7221e7/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5dad8008fc7f92f5ddfa2101430917ce2ffacd86824914c82e28990ad7f00399", size = 211422548 },
{ url = "https://files.pythonhosted.org/packages/27/94/3266821f65b92b3138631e9c8e7fe1fb513804ac934485a8d05776e1dd43/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9", size = 211459117 },
]
@ -4362,6 +4403,7 @@ name = "nvidia-curand-cu12"
version = "10.3.5.147"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/80/9c/a79180e4d70995fdf030c6946991d0171555c6edf95c265c6b2bf7011112/nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1f173f09e3e3c76ab084aba0de819c49e56614feae5c12f69883f4ae9bb5fad9", size = 56314811 },
{ url = "https://files.pythonhosted.org/packages/8a/6d/44ad094874c6f1b9c654f8ed939590bdc408349f137f9b98a3a23ccec411/nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b", size = 56305206 },
]
@ -4375,6 +4417,7 @@ dependencies = [
{ name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/46/6b/a5c33cf16af09166845345275c34ad2190944bcc6026797a39f8e0a282e0/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d338f155f174f90724bbde3758b7ac375a70ce8e706d70b018dd3375545fc84e", size = 127634111 },
{ url = "https://files.pythonhosted.org/packages/3a/e1/5b9089a4b2a4790dfdea8b3a006052cfecff58139d5a4e34cb1a51df8d6f/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:19e33fa442bcfd085b3086c4ebf7e8debc07cfe01e11513cc6d332fd918ac260", size = 127936057 },
]
@ -4386,6 +4429,7 @@ dependencies = [
{ name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/96/a9/c0d2f83a53d40a4a41be14cea6a0bf9e668ffcf8b004bd65633f433050c0/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9d32f62896231ebe0480efd8a7f702e143c98cfaa0e8a76df3386c1ba2b54df3", size = 207381987 },
{ url = "https://files.pythonhosted.org/packages/db/f7/97a9ea26ed4bbbfc2d470994b8b4f338ef663be97b8f677519ac195e113d/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ea4f11a2904e2a8dc4b1833cc1b5181cde564edd0d5cd33e3c168eff2d1863f1", size = 207454763 },
]
@ -4402,6 +4446,7 @@ name = "nvidia-nvjitlink-cu12"
version = "12.4.127"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/02/45/239d52c05074898a80a900f49b1615d81c07fceadd5ad6c4f86a987c0bc4/nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4abe7fef64914ccfa909bc2ba39739670ecc9e820c83ccc7a6ed414122599b83", size = 20552510 },
{ url = "https://files.pythonhosted.org/packages/ff/ff/847841bacfbefc97a00036e0fce5a0f086b640756dc38caea5e1bb002655/nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57", size = 21066810 },
]
@ -4410,6 +4455,7 @@ name = "nvidia-nvtx-cu12"
version = "12.4.127"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/06/39/471f581edbb7804b39e8063d92fc8305bdc7a80ae5c07dbe6ea5c50d14a5/nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7959ad635db13edf4fc65c06a6e9f9e55fc2f92596db928d169c0bb031e88ef3", size = 100417 },
{ url = "https://files.pythonhosted.org/packages/87/20/199b8713428322a2f22b722c62b8cc278cc53dffa9705d744484b5035ee9/nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a", size = 99144 },
]
@ -6785,6 +6831,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276 },
]
[[package]]
name = "sse-starlette"
version = "2.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "starlette" },
]
sdist = { url = "https://files.pythonhosted.org/packages/71/a4/80d2a11af59fe75b48230846989e93979c892d3a20016b42bb44edb9e398/sse_starlette-2.2.1.tar.gz", hash = "sha256:54470d5f19274aeed6b2d473430b08b4b379ea851d953b11d7f1c4a2c118b419", size = 17376 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120 },
]
[[package]]
name = "stack-data"
version = "0.6.3"