From f814dea334155f2723d7e6f2079f87cd56298761 Mon Sep 17 00:00:00 2001 From: Abir00009 Date: Sun, 17 Aug 2025 18:06:29 +0530 Subject: [PATCH 1/2] Allow passing custom http client args in client.py --- cls | 0 fastapi_mcp/server.py | 14 ++++++++++++-- git | 0 tests/http-client-args.patch | 0 4 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 cls create mode 100644 git create mode 100644 tests/http-client-args.patch diff --git a/cls b/cls new file mode 100644 index 0000000..e69de29 diff --git a/fastapi_mcp/server.py b/fastapi_mcp/server.py index bb75106..273270c 100644 --- a/fastapi_mcp/server.py +++ b/fastapi_mcp/server.py @@ -3,9 +3,11 @@ from typing import Dict, Optional, Any, List, Union, Literal, Sequence from typing_extensions import Annotated, Doc + from fastapi import FastAPI, Request, APIRouter, params from fastapi.openapi.utils import get_openapi from mcp.server.lowlevel.server import Server +from fastapi_mcp import FastApiMCP import mcp.types as types from fastapi_mcp.openapi.convert import convert_openapi_to_mcp_tools @@ -17,6 +19,7 @@ logger = logging.getLogger(__name__) +app = FastAPI() class FastApiMCP: @@ -112,11 +115,14 @@ def __init__( if self._auth_config: self._auth_config = self._auth_config.model_validate(self._auth_config) - self._http_client = http_client or httpx.AsyncClient( + self._http_client = http_client or httpx.AsyncClient( transport=httpx.ASGITransport(app=self.fastapi, raise_app_exceptions=False), base_url=self._base_url, timeout=10.0, - ) + ) # type: ignore + + + self._forward_headers = {h.lower() for h in headers} self._http_transport: FastApiHttpSessionManager | None = None # Store reference to HTTP transport for cleanup @@ -654,3 +660,7 @@ def _filter_tools(self, tools: List[types.Tool], openapi_schema: Dict[str, Any]) } return filtered_tools + +if __name__ == "__main__": + mcp = FastApiMCP(app) + mcp.mount() \ No newline at end of file diff --git a/git b/git new file mode 100644 index 0000000..e69de29 diff --git a/tests/http-client-args.patch b/tests/http-client-args.patch new file mode 100644 index 0000000..e69de29 From 0ec480a8637691279147c311cf805fbfc53f066a Mon Sep 17 00:00:00 2001 From: Abir00009 Date: Tue, 19 Aug 2025 17:08:02 +0530 Subject: [PATCH 2/2] updated http client server --- README.md | 18 +++++++ fastapi_mcp/server.py | 48 +++++++++++------ tests/conftest.py | 2 +- tests/http-client-args.patch | 100 +++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 789ea7c..628dee2 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,24 @@ - **ASGI transport** - Uses FastAPI's ASGI interface directly for efficient communication +### Customizing the HTTP Client + +By default, FastAPI-MCP creates an `httpx.AsyncClient` with defaults: + +- `transport`: ASGITransport bound to the app +- `base_url`: internal base URL +- `timeout`: 10 seconds + +If you only need to override one parameter (e.g. `timeout`), pass `http_client_args`: + +```python +client = MyClass( + http_client_args={ + "timeout": 30.0, + "headers": {"Authorization": "Bearer TOKEN"} + } +) + ## Hosted Solution If you prefer a managed hosted solution check out [tadata.com](https://tadata.com). diff --git a/fastapi_mcp/server.py b/fastapi_mcp/server.py index 273270c..b097dfa 100644 --- a/fastapi_mcp/server.py +++ b/fastapi_mcp/server.py @@ -28,7 +28,9 @@ class FastApiMCP: """ def __init__( - self, + self, + + fastapi: Annotated[ FastAPI, Doc("The FastAPI application to create an MCP server from"), @@ -49,15 +51,16 @@ def __init__( bool, Doc("Whether to include full json schema for responses in tool descriptions"), ] = False, - http_client: Annotated[ - Optional[httpx.AsyncClient], - Doc( - """ - Optional custom HTTP client to use for API calls to the FastAPI app. - Has to be an instance of `httpx.AsyncClient`. - """ - ), - ] = None, + http_client: Optional[httpx.AsyncClient] = None, + http_client_args: Optional[Dict[str, Any]] = None, + # [ + + # Doc( """ + # Optional custom HTTP client to use for API calls to the FastAPI app. + # Has to be an instance of `httpx.AsyncClient`. + # """ + # ), + # ] = None, include_operations: Annotated[ Optional[List[str]], Doc("List of operation IDs to include as MCP tools. Cannot be used with exclude_operations."), @@ -87,6 +90,8 @@ def __init__( """ ), ] = ["authorization"], + + ): # Validate operation and tag filtering options if include_operations is not None and exclude_operations is not None: @@ -115,11 +120,24 @@ def __init__( if self._auth_config: self._auth_config = self._auth_config.model_validate(self._auth_config) - self._http_client = http_client or httpx.AsyncClient( - transport=httpx.ASGITransport(app=self.fastapi, raise_app_exceptions=False), - base_url=self._base_url, - timeout=10.0, - ) # type: ignore + http_client_args = http_client_args or {} + + defaults = { + "transport": httpx.ASGITransport(app=self.fastapi, raise_app_exceptions=False), + "base_url": self._base_url, + "timeout": 10.0, + } + + merged_args = {**defaults, **http_client_args} + + self._http_client = http_client or httpx.AsyncClient(**merged_args) + + + # self._http_client = http_client or httpx.AsyncClient( + # transport=httpx.ASGITransport(app=self.fastapi, raise_app_exceptions=False), + # base_url=self._base_url, + # timeout=10.0, + # ) diff --git a/tests/conftest.py b/tests/conftest.py index f448eb7..1ecf029 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -36,4 +36,4 @@ def pytest_sessionfinish(session, exitstatus): cov.combine(data_paths=[cov_dir], strict=True) cov.save() except Exception as e: - print(f"Error combining coverage data: {e}", file=sys.stderr) + print(f"Error combining coverage data: {e}", file=sys.stderr) \ No newline at end of file diff --git a/tests/http-client-args.patch b/tests/http-client-args.patch index e69de29..b51cf1c 100644 --- a/tests/http-client-args.patch +++ b/tests/http-client-args.patch @@ -0,0 +1,100 @@ +diff --git a/fastapi_mcp/client.py b/fastapi_mcp/client.py +index abc123..def456 100644 +--- a/fastapi_mcp/client.py ++++ b/fastapi_mcp/client.py +@@ -1,6 +1,7 @@ + import httpx + from typing import Optional ++from typing import Dict, Any + + class MyClass: +- def __init__(self, http_client: Optional[httpx.AsyncClient] = None, ...): +- if self._auth_config: +- self._auth_config = self._auth_config.model_validate(self._auth_config) +- +- http_client_args = http_client_args or {} +- +- self._http_client = http_client or httpx.AsyncClient( +- transport=httpx.ASGITransport(app=self.fastapi, raise_app_exceptions=False), +- base_url=self._base_url, +- timeout=10.0, +- ) ++ def __init__( ++ self, ++ http_client: Optional[httpx.AsyncClient] = None, ++ http_client_args: Optional[Dict[str, Any]] = None, ++ ... ++ ): ++ if self._auth_config: ++ self._auth_config = self._auth_config.model_validate(self._auth_config) ++ ++ http_client_args = http_client_args or {} ++ ++ defaults = { ++ "transport": httpx.ASGITransport( ++ app=self.fastapi, ++ raise_app_exceptions=False, ++ ), ++ "base_url": self._base_url, ++ "timeout": 10.0, ++ } ++ ++ merged_args = {**defaults, **http_client_args} ++ ++ self._http_client = http_client or httpx.AsyncClient(**merged_args) +diff --git a/tests/test_http_client_args.py b/tests/test_http_client_args.py +new file mode 100644 +index 0000000..1111111 +--- /dev/null ++++ b/tests/test_http_client_args.py +@@ -0,0 +1,15 @@ ++import httpx ++from fastapi_mcp import MyClass # adjust if MyClass is imported differently ++ ++ ++def test_http_client_args_override_timeout(): ++ client = MyClass(http_client_args={"timeout": 25.0}) ++ assert client._http_client.timeout == 25.0 ++ ++ ++def test_http_client_args_add_headers(): ++ client = MyClass(http_client_args={"headers": {"X-Test": "123"}}) ++ assert client._http_client.headers["x-test"] == "123" +diff --git a/README.md b/README.md +index 1234567..89abcde 100644 +--- a/README.md ++++ b/README.md +@@ + ## Usage + ++### Customizing the HTTP Client ++ ++By default, FastAPI-MCP creates an `httpx.AsyncClient` with: ++ ++- `transport`: `httpx.ASGITransport` bound to the app ++- `base_url`: internal base URL ++- `timeout`: 10 seconds ++ ++If you only need to override one parameter (e.g. `timeout`) or add headers, pass `http_client_args`: ++ ++```python ++from fastapi_mcp import MyClass ++ ++client = MyClass( ++ http_client_args={ ++ "timeout": 30.0, ++ "headers": {"Authorization": "Bearer TOKEN"}, ++ } ++) ++``` ++ ++For full control, you can pass a custom `http_client`: ++ ++```python ++import httpx ++from fastapi_mcp import MyClass ++ ++custom_client = httpx.AsyncClient(timeout=60.0) ++client = MyClass(http_client=custom_client) ++``` ++