Skip to content

chore: remove unused runtime dependencies (10 -> 4) - #346

Open
K4bain wants to merge 2 commits into
tadata-org:mainfrom
K4bain:chore/trim-unused-deps
Open

chore: remove unused runtime dependencies (10 -> 4)#346
K4bain wants to merge 2 commits into
tadata-org:mainfrom
K4bain:chore/trim-unused-deps

Conversation

@K4bain

@K4bain K4bain commented Sep 3, 2026

Copy link
Copy Markdown

Problem

The library declares 10 runtime dependencies, but several are never imported by the package itself. This adds install weight and friction for downstream adopters (raised in #275rich in particular impacts log output configuration of host apps).

Audit results

Dependency Usage in fastapi_mcp/ Action
fastapi core keep
mcp core keep
pydantic core keep
httpx server.py, auth/proxy.py keep
typer never imported remove
rich never imported remove
requests never imported (httpx used everywhere) remove
tomli never imported remove
pydantic-settings never imported (mcp already depends on it) remove
uvicorn never imported by the package; used by examples and the real-transport tests; mcp itself pulls it for its own transports moved to a deploy extra + dev group

Runtime dependencies go from 10 to 4.

Notes

  • uvicorn remains reachable: pip install fastapi-mcp[deploy], and the dev dependency group keeps it for tests/test_http_real_transport.py / tests/test_sse_real_transport.py.
  • uv.lock regenerated; typer/rich/requests/urllib3/pygments/shellingham and friends drop out of the resolved graph entirely.
  • Wheel metadata verified: only fastapi, httpx, mcp, pydantic (+ uvicorn; extra == 'deploy').

Verification

  • uv run pytest tests/: 78 passed (real-transport os.fork errors are the pre-existing Windows limitation tracked in test: skip real-transport tests on Windows (they require os.fork) #332, identical on unpatched main).
  • uv run ruff check fastapi_mcp + ruff format --check: clean.
  • uv run mypy fastapi_mcp: no issues in 12 source files.
  • uv build + METADATA inspection of the built wheel.

Fixes #275

…f injecting an arbitrary type

## Problem

For a request body field typed as a union of two real types (e.g. `dict[str, list[str]] | list[str]`), the projected MCP tool schema gets a single arbitrary `"type"` injected alongside `anyOf`. The MCP server's jsonschema validator then enforces that `"type"` strictly and rejects every call using the variant that lost the pick, with a misleading error like:

```
Input validation error: {'Skills': [...]} is not of type 'array'
```

The injected type is also non-deterministic (`next(iter(set))`), so which variant breaks can differ per run. Fixes tadata-org#307.

## Root cause

In `convert_openapi_to_mcp_tools`, both the query-param and body-param loops do:

```python
if "type" not in properties[param_name]:
    properties[param_name]["type"] = get_single_param_type_from_schema(param_schema)
```

For `T | U` (both real types) Pydantic emits `{"anyOf": [...]}` with no top-level `"type"`, so the branch fires and one arbitrary variant type wins.

## Fix

Only fall back to `get_single_param_type_from_schema` when the union has at most one non-null variant. `T | None` (nullable) keeps the existing collapse behavior — its existing tests still pass unchanged — while `T | U` keeps `anyOf` verbatim, which the JSON Schema validator at the MCP layer handles correctly.

## Verification

- New test `test_union_type_schema_not_collapsed` asserts `anyOf` is preserved with no injected `"type"` and both `object`/`array` variants present.
- Repro from the issue validated with `jsonschema`: both the dict variant and the list variant pass validation (previously one of them always failed).
- Full suite: 70 passed (the 20 `test_sse_real_transport` errors are the pre-existing Windows `os.fork` limitation tracked in tadata-org#332).
The package itself imports none of these:

- typer, rich: never referenced anywhere in fastapi_mcp/
- requests: zero imports (httpx is used everywhere)
- tomli: zero imports
- pydantic-settings: zero imports (mcp already depends on it anyway)
- uvicorn: not needed at runtime (mcp itself pulls it for its own
  transports); kept as a 'deploy' extra and in the dev group for the
  real-transport tests and the examples

Runtime deps go from 10 to 4: fastapi, mcp, pydantic, httpx.
Verified: 78/78 tests, ruff clean, mypy clean, wheel metadata confirmed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reduce dependency installs that are not required by the project.

1 participant