diff --git a/src/mcpo/__init__.py b/src/mcpo/__init__.py index 3193d452..13427962 100644 --- a/src/mcpo/__init__.py +++ b/src/mcpo/__init__.py @@ -75,6 +75,9 @@ def main( log_level: Annotated[ Optional[str], typer.Option("--log-level", help="Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)") ] = None, + client_header_forwarding: Annotated[ + Optional[str], typer.Option("--client-header-forwarding", help="Config for header forwarding") + ] = None ): server_command = None if not config_path: @@ -114,7 +117,7 @@ def main( # Set environment variables for key, value in env_dict.items(): os.environ[key] = value - except Exception as e: + except Exception: pass # Whatever the prefix is, make sure it starts and ends with a / @@ -152,6 +155,7 @@ def main( root_path=root_path, headers=headers, hot_reload=hot_reload, + client_header_forwarding=client_header_forwarding ) ) diff --git a/src/mcpo/main.py b/src/mcpo/main.py index b0196282..1692f465 100644 --- a/src/mcpo/main.py +++ b/src/mcpo/main.py @@ -755,6 +755,7 @@ async def run( # MCP Config config_path = kwargs.get("config_path") + client_header_forwarding = kwargs.get("client_header_forwarding", None) # mcpo server name = kwargs.get("name") or "MCP OpenAPI Proxy" @@ -844,6 +845,15 @@ def filter(self, record): logger.warning("Invalid JSON format for headers. Headers will be ignored.") headers = None + client_header_forwarding = kwargs.get("client_header_forwarding") + if client_header_forwarding and isinstance(client_header_forwarding, str): + try: + client_header_forwarding = json.loads(client_header_forwarding) + validate_client_header_forwarding_config("default", client_header_forwarding) + except json.JSONDecodeError: + logger.warning("Invalid JSON format for client header forwarding config. Client header forwarding config will be ignored.") + client_header_forwarding = None + if server_type == "sse": logger.info( f"Configuring for a single SSE MCP Server with URL {server_command[0]}" @@ -860,6 +870,7 @@ def filter(self, record): main_app.state.args = server_command[0] # Expects URL as the first element main_app.state.api_dependency = api_dependency main_app.state.headers = headers + main_app.state.client_header_forwarding = client_header_forwarding elif server_command: # This handles stdio logger.info( f"Configuring for a single Stdio MCP Server with command: {' '.join(server_command)}" diff --git a/src/mcpo/utils/headers.py b/src/mcpo/utils/headers.py index 6d880731..101f6957 100644 --- a/src/mcpo/utils/headers.py +++ b/src/mcpo/utils/headers.py @@ -1,6 +1,5 @@ import logging -import re -from typing import Dict, List, Optional, Any +from typing import Dict, List, Any from fastapi import Request logger = logging.getLogger(__name__) diff --git a/src/mcpo/utils/main.py b/src/mcpo/utils/main.py index d45746ce..89c5f8c0 100644 --- a/src/mcpo/utils/main.py +++ b/src/mcpo/utils/main.py @@ -283,12 +283,12 @@ def get_tool_handler( client_header_forwarding_config=None, ): async def call_tool_with_reconnect( - request: Request, arguments: Dict[str, Any] + request: Request, arguments: Dict[str, Any], meta: Dict[str, Any] ) -> CallToolResult: session_manager = getattr(request.app.state, "session_manager", None) async def _invoke(session): - return await session.call_tool(endpoint_name, arguments=arguments) + return await session.call_tool(endpoint_name, arguments=arguments, meta=meta) if session_manager: try: @@ -349,7 +349,7 @@ async def tool( logger.info(f"Calling endpoint: {endpoint_name}, with args: {args}") try: - result = await call_tool_with_reconnect(request, args) + result = await call_tool_with_reconnect(request, args, meta) if result.isError: error_message = "Unknown tool execution error" @@ -409,7 +409,7 @@ async def tool(request: Request): logger.info(f"Calling endpoint: {endpoint_name}, with no args") try: - result = await call_tool_with_reconnect(request, {}) + result = await call_tool_with_reconnect(request, {}, meta) if result.isError: error_message = "Unknown tool execution error" diff --git a/uv.lock b/uv.lock index 14e10041..8cd5bbac 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,12 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.11" +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform != 'win32'", + "python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version < '3.14' and sys_platform != 'win32'", +] [[package]] name = "annotated-types" @@ -323,40 +329,83 @@ wheels = [ [[package]] name = "mcp" -version = "1.17.0" +version = "1.27.2" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform != 'win32'", +] dependencies = [ - { name = "anyio" }, - { name = "httpx" }, - { name = "httpx-sse" }, - { name = "jsonschema" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette" }, - { name = "starlette" }, - { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, + { name = "anyio", marker = "python_full_version >= '3.14'" }, + { name = "httpx", marker = "python_full_version >= '3.14'" }, + { name = "httpx-sse", marker = "python_full_version >= '3.14'" }, + { name = "jsonschema", marker = "python_full_version >= '3.14'" }, + { name = "pydantic", marker = "python_full_version >= '3.14'" }, + { name = "pydantic-settings", marker = "python_full_version >= '3.14'" }, + { name = "pyjwt", extra = ["crypto"], marker = "python_full_version >= '3.14'" }, + { name = "python-multipart", marker = "python_full_version >= '3.14'" }, + { name = "pywin32", marker = "python_full_version >= '3.14' and sys_platform == 'win32'" }, + { name = "sse-starlette", marker = "python_full_version >= '3.14'" }, + { name = "starlette", marker = "python_full_version >= '3.14'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.14'" }, + { name = "typing-inspection", marker = "python_full_version >= '3.14'" }, + { name = "uvicorn", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/27/3c/347cf965d313f5d41764e7d46bea6ffe7d9ef13b983cc429b0340962a082/mcp-1.27.2.tar.gz", hash = "sha256:8e02db104096d1c25b28e64bde29a5c32b31bc241710213e12fd4d84985bdfef", size = 621116, upload-time = "2026-05-29T17:16:04.039Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/11/252c6f971dc4f16af1d98a1c469d8ba523aab00d1bb76b4d3bc1ff32eacc/mcp-1.27.2-py3-none-any.whl", hash = "sha256:d6ff5160c6ca65d93013626efb3fc249de683c30b2d8570755ceddd490344de5", size = 220498, upload-time = "2026-05-29T17:16:02.442Z" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/79/5724a540df19e192e8606c543cdcf162de8eb435077520cca150f7365ec0/mcp-1.17.0.tar.gz", hash = "sha256:1b57fabf3203240ccc48e39859faf3ae1ccb0b571ff798bbedae800c73c6df90", size = 477951, upload-time = "2025-10-10T12:16:44.519Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/72/3751feae343a5ad07959df713907b5c3fbaed269d697a14b0c449080cf2e/mcp-1.17.0-py3-none-any.whl", hash = "sha256:0660ef275cada7a545af154db3082f176cf1d2681d5e35ae63e014faf0a35d40", size = 167737, upload-time = "2025-10-10T12:16:42.863Z" }, + +[package.optional-dependencies] +cli = [ + { name = "python-dotenv", marker = "python_full_version >= '3.14'" }, + { name = "typer", marker = "python_full_version >= '3.14'" }, +] + +[[package]] +name = "mcp" +version = "1.28.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version < '3.14' and sys_platform != 'win32'", +] +dependencies = [ + { name = "anyio", marker = "python_full_version < '3.14'" }, + { name = "httpx", marker = "python_full_version < '3.14'" }, + { name = "httpx-sse", marker = "python_full_version < '3.14'" }, + { name = "jsonschema", marker = "python_full_version < '3.14'" }, + { name = "pydantic", marker = "python_full_version < '3.14'" }, + { name = "pydantic-settings", marker = "python_full_version < '3.14'" }, + { name = "pyjwt", extra = ["crypto"], marker = "python_full_version < '3.14'" }, + { name = "python-multipart", marker = "python_full_version < '3.14'" }, + { name = "pywin32", marker = "python_full_version < '3.14' and sys_platform == 'win32'" }, + { name = "sse-starlette", marker = "python_full_version < '3.14'" }, + { name = "starlette", marker = "python_full_version < '3.14'" }, + { name = "typing-extensions", marker = "python_full_version < '3.14'" }, + { name = "typing-inspection", marker = "python_full_version < '3.14'" }, + { name = "uvicorn", marker = "python_full_version < '3.14' and sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c1/ee/94c6c50ffc5b5cf4737052275d11b57367f32d1a8516e31dcd60591b3916/mcp-1.28.0.tar.gz", hash = "sha256:559d3f9943674cafbe5744c5d3794f3237e8b47f9bbc58e20c0fad680d8487c2", size = 636040, upload-time = "2026-06-16T21:37:17.996Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/e1/4c1dc1fbb688641a712d34650c3d58bbbdcb314ddb75bc5817bbf33515a4/mcp-1.28.0-py3-none-any.whl", hash = "sha256:9c1e7cf3a9125557e418ecd4fed8e9adddce81b0dfdae4d6601d700f5beb71a4", size = 221959, upload-time = "2026-06-16T21:37:16.579Z" }, ] [package.optional-dependencies] cli = [ - { name = "python-dotenv" }, - { name = "typer" }, + { name = "python-dotenv", marker = "python_full_version < '3.14'" }, + { name = "typer", marker = "python_full_version < '3.14'" }, ] [[package]] name = "mcpo" -version = "0.0.19" +version = "0.0.20" source = { editable = "." } dependencies = [ { name = "click" }, { name = "fastapi" }, - { name = "mcp", extra = ["cli"] }, + { name = "mcp", version = "1.27.2", source = { registry = "https://pypi.org/simple" }, extra = ["cli"], marker = "python_full_version >= '3.14'" }, + { name = "mcp", version = "1.28.0", source = { registry = "https://pypi.org/simple" }, extra = ["cli"], marker = "python_full_version < '3.14'" }, { name = "passlib", extra = ["bcrypt"] }, { name = "pydantic" }, { name = "pyjwt", extra = ["crypto"] },