Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
Empty file added cls
Empty file.
58 changes: 43 additions & 15 deletions fastapi_mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -17,6 +19,7 @@


logger = logging.getLogger(__name__)
app = FastAPI()


class FastApiMCP:
Expand All @@ -25,7 +28,9 @@ class FastApiMCP:
"""

def __init__(
self,
self,


fastapi: Annotated[
FastAPI,
Doc("The FastAPI application to create an MCP server from"),
Expand All @@ -46,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."),
Expand Down Expand Up @@ -84,6 +90,8 @@ def __init__(
"""
),
] = ["authorization"],


):
# Validate operation and tag filtering options
if include_operations is not None and exclude_operations is not None:
Expand Down Expand Up @@ -112,11 +120,27 @@ 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,
)
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,
# )




self._forward_headers = {h.lower() for h in headers}
self._http_transport: FastApiHttpSessionManager | None = None # Store reference to HTTP transport for cleanup
Expand Down Expand Up @@ -654,3 +678,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()
Empty file added git
Empty file.
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
100 changes: 100 additions & 0 deletions tests/http-client-args.patch
Original file line number Diff line number Diff line change
@@ -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)
+```
+