mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-09 07:09:11 +02:00
Add fastmcp-slim for client-only installs (#4122)
* Add fastmcp-client workspace package * Fix client package static checks * Document client-only package * Harden fastmcp-client package split * Preserve forwarded headers in full package * Switch to fastmcp-slim package * Fix fastmcp-slim release edges * Match pydantic-style slim layout * Polish fastmcp-slim packaging
This commit is contained in:
parent
8209093871
commit
bb4894d215
298 changed files with 1259 additions and 438 deletions
2
.github/workflows/martian-triage-issue.yml
vendored
2
.github/workflows/martian-triage-issue.yml
vendored
|
|
@ -125,7 +125,7 @@ jobs:
|
|||
|
||||
<evidence_standards>
|
||||
Every claim in your response must be grounded in evidence you can cite:
|
||||
- **Code references**: Always include file path and line number (e.g., `src/fastmcp/client/client.py:142`). Never say "the client code does X" without pointing to where.
|
||||
- **Code references**: Always include file path and line number (e.g., `fastmcp_slim/fastmcp/client/client.py:142`). Never say "the client code does X" without pointing to where.
|
||||
- **Bug confirmation**: If you say a bug is real, show the specific code path that produces it. If you ran a test, include the command and output.
|
||||
- **Related items**: When citing a related issue or PR, explain specifically why it's related — not just that it exists.
|
||||
- **Confidence**: If you're uncertain about a finding, say so. "I don't know" or "I couldn't confirm this" is better than a speculative diagnosis. Only report findings you would confidently defend.
|
||||
|
|
|
|||
2
.github/workflows/marvin-label-triage.yml
vendored
2
.github/workflows/marvin-label-triage.yml
vendored
|
|
@ -116,7 +116,7 @@ jobs:
|
|||
- auth: Authentication is the main concern (Bearer, JWT, OAuth, WorkOS)
|
||||
- openapi: OpenAPI integration/parsing is the primary topic
|
||||
- http: HTTP transport or networking is the main issue
|
||||
- contrib: Specifically about community contributions in src/contrib/
|
||||
- contrib: Specifically about community contributions in fastmcp_slim/fastmcp/contrib/
|
||||
- tests: Issues primarily about testing infrastructure, CI/CD workflows, or test coverage
|
||||
- security: Apply ONLY when the issue/PR addresses an exploitable vulnerability or hardens against one. Examples: SSRF, LFI, path traversal, injection, auth bypass allowing unauthorized access, scope escalation, open redirects. Do NOT apply for ordinary auth bugs (wrong scopes returned, token refresh logic, OAuth flow correctness) unless an attacker could exploit the bug to bypass access controls or escalate privileges. The key question: "Could a malicious actor exploit this?" If the answer is just "it breaks for legitimate users," that's a bug, not a security issue.
|
||||
|
||||
|
|
|
|||
30
.github/workflows/publish-fastmcp-slim.yml
vendored
Normal file
30
.github/workflows/publish-fastmcp-slim.yml
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
name: Publish fastmcp-slim to PyPI
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
pypi-publish:
|
||||
name: Upload fastmcp-slim to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
|
||||
- name: Build fastmcp-slim
|
||||
run: uv build --package fastmcp-slim
|
||||
|
||||
- name: Publish fastmcp-slim to PyPI
|
||||
run: uv publish -v dist/fastmcp_slim-*.tar.gz dist/fastmcp_slim-*.whl
|
||||
87
.github/workflows/publish-fastmcp.yml
vendored
Normal file
87
.github/workflows/publish-fastmcp.yml
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
name: Publish fastmcp to PyPI
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Publish fastmcp-slim to PyPI"]
|
||||
types: [completed]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
pypi-publish:
|
||||
name: Upload fastmcp to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'workflow_dispatch' || (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'release')
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
|
||||
- name: Build fastmcp
|
||||
run: uv build --package fastmcp
|
||||
|
||||
- name: Verify matching fastmcp-slim is published
|
||||
run: |
|
||||
SLIM_VERSION=$(python - <<'PY'
|
||||
import email.parser
|
||||
import re
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
wheel = next(Path("dist").glob("fastmcp-*.whl"))
|
||||
metadata_name = next(
|
||||
name for name in zipfile.ZipFile(wheel).namelist()
|
||||
if name.endswith(".dist-info/METADATA")
|
||||
)
|
||||
metadata = email.parser.Parser().parsestr(
|
||||
zipfile.ZipFile(wheel).read(metadata_name).decode()
|
||||
)
|
||||
for value in metadata.get_all("Requires-Dist", []):
|
||||
requirement, _, marker = value.partition(";")
|
||||
if marker.strip():
|
||||
continue
|
||||
match = re.fullmatch(
|
||||
r"fastmcp-slim(?:\[[^\]]+\])?==([^;\s]+)",
|
||||
requirement.strip(),
|
||||
)
|
||||
if match:
|
||||
print(match.group(1))
|
||||
break
|
||||
else:
|
||||
raise RuntimeError("Could not find the base fastmcp-slim dependency")
|
||||
PY
|
||||
)
|
||||
|
||||
for attempt in {1..12}; do
|
||||
if python - "$SLIM_VERSION" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
version = sys.argv[1]
|
||||
url = f"https://pypi.org/pypi/fastmcp-slim/{version}/json"
|
||||
with urllib.request.urlopen(url, timeout=30) as response:
|
||||
json.load(response)
|
||||
PY
|
||||
then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "fastmcp-slim ${SLIM_VERSION} is not available on PyPI yet; retrying (${attempt}/12)."
|
||||
sleep 10
|
||||
done
|
||||
|
||||
echo "fastmcp-slim ${SLIM_VERSION} is not available on PyPI; refusing to publish fastmcp." >&2
|
||||
exit 1
|
||||
|
||||
- name: Publish fastmcp to PyPI
|
||||
run: uv publish -v dist/fastmcp-*.tar.gz dist/fastmcp-*.whl
|
||||
26
.github/workflows/publish.yml
vendored
26
.github/workflows/publish.yml
vendored
|
|
@ -1,26 +0,0 @@
|
|||
name: Publish FastMCP to PyPI
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
pypi-publish:
|
||||
name: Upload to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write # For PyPI's trusted publishing
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: "Install uv"
|
||||
uses: astral-sh/setup-uv@v7
|
||||
|
||||
- name: Build
|
||||
run: uv build
|
||||
|
||||
- name: Publish to PyPi
|
||||
run: uv publish -v dist/*
|
||||
20
.github/workflows/run-schema-crash-test.yml
vendored
20
.github/workflows/run-schema-crash-test.yml
vendored
|
|
@ -4,21 +4,21 @@ on:
|
|||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/fastmcp/utilities/json_schema_type.py"
|
||||
- "src/fastmcp/utilities/json_schema.py"
|
||||
- "src/fastmcp/utilities/openapi/**"
|
||||
- "src/fastmcp/server/providers/openapi/**"
|
||||
- "src/fastmcp/client/mixins/tools.py"
|
||||
- "fastmcp_slim/fastmcp/utilities/json_schema_type.py"
|
||||
- "fastmcp_slim/fastmcp/utilities/json_schema.py"
|
||||
- "fastmcp_slim/fastmcp/utilities/openapi/**"
|
||||
- "fastmcp_slim/fastmcp/server/providers/openapi/**"
|
||||
- "fastmcp_slim/fastmcp/client/mixins/tools.py"
|
||||
- "tests/utilities/json_schema_type/test_real_world_schemas.py"
|
||||
- ".github/workflows/run-schema-crash-test.yml"
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
- "src/fastmcp/utilities/json_schema_type.py"
|
||||
- "src/fastmcp/utilities/json_schema.py"
|
||||
- "src/fastmcp/utilities/openapi/**"
|
||||
- "src/fastmcp/server/providers/openapi/**"
|
||||
- "src/fastmcp/client/mixins/tools.py"
|
||||
- "fastmcp_slim/fastmcp/utilities/json_schema_type.py"
|
||||
- "fastmcp_slim/fastmcp/utilities/json_schema.py"
|
||||
- "fastmcp_slim/fastmcp/utilities/openapi/**"
|
||||
- "fastmcp_slim/fastmcp/server/providers/openapi/**"
|
||||
- "fastmcp_slim/fastmcp/client/mixins/tools.py"
|
||||
- "tests/utilities/json_schema_type/test_real_world_schemas.py"
|
||||
- ".github/workflows/run-schema-crash-test.yml"
|
||||
|
||||
|
|
|
|||
4
.github/workflows/run-static.yml
vendored
4
.github/workflows/run-static.yml
vendored
|
|
@ -7,10 +7,10 @@ on:
|
|||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/**"
|
||||
- "fastmcp_slim/**"
|
||||
- "tests/**"
|
||||
- "uv.lock"
|
||||
- "pyproject.toml"
|
||||
- "uv.lock"
|
||||
- ".github/workflows/**"
|
||||
|
||||
# run on all pull requests because these checks are required and will block merges otherwise
|
||||
|
|
|
|||
103
.github/workflows/run-tests.yml
vendored
103
.github/workflows/run-tests.yml
vendored
|
|
@ -7,10 +7,10 @@ on:
|
|||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/**"
|
||||
- "fastmcp_slim/**"
|
||||
- "tests/**"
|
||||
- "uv.lock"
|
||||
- "pyproject.toml"
|
||||
- "uv.lock"
|
||||
- ".github/workflows/**"
|
||||
|
||||
# run on all pull requests because these checks are required and will block merges otherwise
|
||||
|
|
@ -117,3 +117,102 @@ jobs:
|
|||
FASTMCP_GITHUB_TOKEN: ${{ secrets.FASTMCP_GITHUB_TOKEN }}
|
||||
FASTMCP_TEST_AUTH_GITHUB_CLIENT_ID: ${{ secrets.FASTMCP_TEST_AUTH_GITHUB_CLIENT_ID }}
|
||||
FASTMCP_TEST_AUTH_GITHUB_CLIENT_SECRET: ${{ secrets.FASTMCP_TEST_AUTH_GITHUB_CLIENT_SECRET }}
|
||||
|
||||
package_install_smoke:
|
||||
name: "Package install smoke"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup uv
|
||||
uses: ./.github/actions/setup-uv
|
||||
with:
|
||||
resolution: locked
|
||||
|
||||
- name: Build package wheels
|
||||
run: uv build --all-packages --wheel --out-dir /tmp/fastmcp-dist
|
||||
|
||||
- name: Install bare slim wheel
|
||||
run: |
|
||||
uv venv /tmp/fastmcp-slim-bare-smoke
|
||||
SLIM_WHEEL=$(ls /tmp/fastmcp-dist/fastmcp_slim-*.whl)
|
||||
uv pip install --python /tmp/fastmcp-slim-bare-smoke/bin/python "$SLIM_WHEEL"
|
||||
/tmp/fastmcp-slim-bare-smoke/bin/python - <<'PY'
|
||||
from importlib.metadata import entry_points
|
||||
|
||||
import fastmcp
|
||||
import fastmcp.settings
|
||||
|
||||
assert not any(ep.name == "fastmcp" for ep in entry_points(group="console_scripts"))
|
||||
|
||||
try:
|
||||
fastmcp.FastMCP
|
||||
except ImportError as exc:
|
||||
assert "fastmcp-slim[server]" in str(exc)
|
||||
else:
|
||||
raise AssertionError("bare fastmcp-slim unexpectedly imported FastMCP")
|
||||
PY
|
||||
|
||||
- name: Install client slim wheel
|
||||
run: |
|
||||
uv venv /tmp/fastmcp-slim-client-smoke
|
||||
SLIM_WHEEL=$(ls /tmp/fastmcp-dist/fastmcp_slim-*.whl)
|
||||
uv pip install --python /tmp/fastmcp-slim-client-smoke/bin/python "${SLIM_WHEEL}[client]"
|
||||
/tmp/fastmcp-slim-client-smoke/bin/python - <<'PY'
|
||||
from importlib.metadata import entry_points
|
||||
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.transports import StdioTransport, StreamableHttpTransport
|
||||
from fastmcp.mcp_config import MCPConfig
|
||||
|
||||
assert not any(ep.name == "fastmcp" for ep in entry_points(group="console_scripts"))
|
||||
|
||||
assert Client("https://example.com/mcp")
|
||||
assert StreamableHttpTransport("https://example.com/mcp")
|
||||
assert StdioTransport(command="uvx", args=["demo"])
|
||||
assert MCPConfig.from_dict({"mcpServers": {"demo": {"url": "https://example.com/mcp"}}})
|
||||
|
||||
try:
|
||||
from fastmcp import FastMCP
|
||||
except ImportError as exc:
|
||||
assert "fastmcp-slim[server]" in str(exc)
|
||||
else:
|
||||
raise AssertionError(f"client-only slim unexpectedly imported {FastMCP!r}")
|
||||
PY
|
||||
|
||||
- name: Install server slim wheel
|
||||
run: |
|
||||
uv venv /tmp/fastmcp-slim-server-smoke
|
||||
SLIM_WHEEL=$(ls /tmp/fastmcp-dist/fastmcp_slim-*.whl)
|
||||
uv pip install --python /tmp/fastmcp-slim-server-smoke/bin/python "${SLIM_WHEEL}[server]"
|
||||
/tmp/fastmcp-slim-server-smoke/bin/python - <<'PY'
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("smoke")
|
||||
assert mcp.name == "smoke"
|
||||
PY
|
||||
|
||||
- name: Install full package from matching local wheels
|
||||
run: |
|
||||
uv venv /tmp/fastmcp-full-smoke
|
||||
FULL_WHEEL=$(ls /tmp/fastmcp-dist/fastmcp-*.whl)
|
||||
uv pip install --python /tmp/fastmcp-full-smoke/bin/python --find-links /tmp/fastmcp-dist "$FULL_WHEEL"
|
||||
/tmp/fastmcp-full-smoke/bin/python - <<'PY'
|
||||
from importlib.metadata import entry_points
|
||||
|
||||
from fastmcp import Client, FastMCP
|
||||
from fastmcp.client.client import CallToolResult
|
||||
from fastmcp.exceptions import ToolError
|
||||
|
||||
assert any(
|
||||
ep.name == "fastmcp" and ep.value == "fastmcp.cli:app"
|
||||
for ep in entry_points(group="console_scripts")
|
||||
)
|
||||
|
||||
assert Client("https://example.com/mcp")
|
||||
assert FastMCP("smoke").name == "smoke"
|
||||
assert CallToolResult is not None
|
||||
assert ToolError is not None
|
||||
PY
|
||||
|
|
|
|||
4
.github/workflows/run-upgrade-checks.yml
vendored
4
.github/workflows/run-upgrade-checks.yml
vendored
|
|
@ -7,10 +7,10 @@ on:
|
|||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/**"
|
||||
- "fastmcp_slim/**"
|
||||
- "tests/**"
|
||||
- "uv.lock"
|
||||
- "pyproject.toml"
|
||||
- "uv.lock"
|
||||
- ".github/workflows/**"
|
||||
|
||||
schedule:
|
||||
|
|
|
|||
8
.github/workflows/update-config-schema.yml
vendored
8
.github/workflows/update-config-schema.yml
vendored
|
|
@ -7,8 +7,8 @@ on:
|
|||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/fastmcp/utilities/mcp_server_config/**"
|
||||
- "!src/fastmcp/utilities/mcp_server_config/v1/schema.json"
|
||||
- "fastmcp_slim/fastmcp/utilities/mcp_server_config/**"
|
||||
- "!fastmcp_slim/fastmcp/utilities/mcp_server_config/v1/schema.json"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
|
|
@ -47,7 +47,7 @@ jobs:
|
|||
from fastmcp.utilities.mcp_server_config import generate_schema
|
||||
generate_schema('docs/public/schemas/fastmcp.json/latest.json')
|
||||
generate_schema('docs/public/schemas/fastmcp.json/v1.json')
|
||||
generate_schema('src/fastmcp/utilities/mcp_server_config/v1/schema.json')
|
||||
generate_schema('fastmcp_slim/fastmcp/utilities/mcp_server_config/v1/schema.json')
|
||||
"
|
||||
|
||||
- name: Create Pull Request
|
||||
|
|
@ -59,7 +59,7 @@ jobs:
|
|||
body: |
|
||||
This PR updates the fastmcp.json schema files to match the current source code.
|
||||
|
||||
The schema is automatically generated from `src/fastmcp/utilities/mcp_server_config/` to ensure consistency.
|
||||
The schema is automatically generated from `fastmcp_slim/fastmcp/utilities/mcp_server_config/` to ensure consistency.
|
||||
|
||||
**Note:** This PR is fully automated and will update itself with any subsequent changes to the schema, or close automatically if the schema becomes up-to-date through other means.
|
||||
|
||||
|
|
|
|||
2
.github/workflows/update-sdk-docs.yml
vendored
2
.github/workflows/update-sdk-docs.yml
vendored
|
|
@ -7,7 +7,7 @@ on:
|
|||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "src/**"
|
||||
- "fastmcp_slim/**"
|
||||
- "pyproject.toml"
|
||||
workflow_dispatch:
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ repos:
|
|||
entry: uv run --isolated ty check
|
||||
language: system
|
||||
types: [python]
|
||||
files: ^src/|^tests/
|
||||
files: ^fastmcp_slim/|^tests/
|
||||
pass_filenames: false
|
||||
require_serial: true
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ uv run prek run --all-files # Ruff + Prettier + ty
|
|||
|
||||
| Path | Purpose |
|
||||
| ----------------- | -------------------------------------- |
|
||||
| `src/fastmcp/` | Library source code |
|
||||
| `fastmcp_slim/fastmcp/` | Library source code |
|
||||
| `├─server/` | Server implementation |
|
||||
| `│ ├─auth/` | Authentication providers |
|
||||
| `│ └─middleware/` | Error handling, logging, rate limiting |
|
||||
|
|
@ -50,7 +50,7 @@ When modifying MCP functionality, changes typically need to be applied across al
|
|||
- **Resource Templates** (`src/resources/`)
|
||||
- **Prompts** (`src/prompts/`)
|
||||
|
||||
**Before writing cross-component logic (dedupe, grouping, lookups, identity checks), read `FastMCPComponent` in `src/fastmcp/utilities/components.py`.** The base class defines the shared surface — `name`, `version`, `tags`, `meta`, and critically the `key` property which is the canonical MCP identity (encodes type, identifier, and version). Prefer `item.key` over ad-hoc `name or uri or uri_template` fallbacks; overrides in `Resource` and `ResourceTemplate` already handle URI-based identity, and `.key` includes the version suffix so variants of the same component don't falsely collide.
|
||||
**Before writing cross-component logic (dedupe, grouping, lookups, identity checks), read `FastMCPComponent` in `fastmcp_slim/fastmcp/utilities/components.py`.** The base class defines the shared surface — `name`, `version`, `tags`, `meta`, and critically the `key` property which is the canonical MCP identity (encodes type, identifier, and version). Prefer `item.key` over ad-hoc `name or uri or uri_template` fallbacks; overrides in `Resource` and `ResourceTemplate` already handle URI-based identity, and `.key` includes the version suffix so variants of the same component don't falsely collide.
|
||||
|
||||
## Development Rules
|
||||
|
||||
|
|
@ -147,9 +147,9 @@ gh api -X POST repos/PrefectHQ/fastmcp/releases/generate-notes \
|
|||
- Uses Mintlify framework
|
||||
- Files must be in docs.json to be included
|
||||
- Do not manually modify `docs/python-sdk/**` — these files are auto-generated from source code by a bot and maintained via a long-lived PR. Do not include changes to these files in contributor PRs.
|
||||
- Do not manually modify `docs/public/schemas/**` or `src/fastmcp/utilities/mcp_server_config/v1/schema.json` — these are auto-generated and maintained via a long-lived PR.
|
||||
- Do not manually modify `docs/public/schemas/**` or `fastmcp_slim/fastmcp/utilities/mcp_server_config/v1/schema.json` — these are auto-generated and maintained via a long-lived PR.
|
||||
- **Core Principle:** A feature doesn't exist unless it is documented!
|
||||
- When adding or modifying settings in `src/fastmcp/settings.py`, update `docs/more/settings.mdx` to match.
|
||||
- When adding or modifying settings in `fastmcp_slim/fastmcp/settings.py`, update `docs/more/settings.mdx` to match.
|
||||
|
||||
### Documentation Guidelines
|
||||
|
||||
|
|
|
|||
89
docs/clients/client-only-package.mdx
Normal file
89
docs/clients/client-only-package.mdx
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
---
|
||||
title: Client-Only Package
|
||||
description: Use FastMCP's client without installing the full server framework.
|
||||
icon: package
|
||||
---
|
||||
|
||||
import { VersionBadge } from '/snippets/version-badge.mdx'
|
||||
|
||||
<VersionBadge version="3.3.0" />
|
||||
|
||||
FastMCP's full `fastmcp` package includes everything needed to build and run MCP servers, apps, proxies, and clients. If you are only embedding an MCP client in another framework, building your own LLM host, or testing MCP servers, you can install the smaller client-only package instead.
|
||||
|
||||
```bash
|
||||
pip install "fastmcp-slim[client]"
|
||||
```
|
||||
|
||||
The client-only package uses the `fastmcp` import namespace:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
|
||||
client = Client("https://example.com/mcp")
|
||||
```
|
||||
|
||||
Use `fastmcp-slim[client]` when your code connects to MCP servers but does not define or run FastMCP servers itself. For example, framework authors can depend on `fastmcp-slim[client]` to provide MCP connectivity without requiring users to install the full FastMCP server stack.
|
||||
|
||||
## Supported Usage
|
||||
|
||||
Client-only installs support remote and subprocess transports:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
|
||||
# Remote MCP server
|
||||
http_client = Client("https://example.com/mcp")
|
||||
|
||||
# Local MCP server over stdio
|
||||
stdio_client = Client("my_server.py")
|
||||
```
|
||||
|
||||
Single-server MCP configuration works as well:
|
||||
|
||||
```python
|
||||
from fastmcp import Client
|
||||
|
||||
config = {
|
||||
"mcpServers": {
|
||||
"weather": {
|
||||
"url": "https://weather.example.com/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client = Client(config)
|
||||
```
|
||||
|
||||
Optional sampling handlers are available through the same extras as the full package:
|
||||
|
||||
```bash
|
||||
pip install "fastmcp-slim[client,openai]"
|
||||
pip install "fastmcp-slim[client,anthropic]"
|
||||
pip install "fastmcp-slim[client,gemini]"
|
||||
```
|
||||
|
||||
## When to Use the Full Package
|
||||
|
||||
Install `fastmcp` when you need server-side FastMCP features:
|
||||
|
||||
```bash
|
||||
pip install fastmcp
|
||||
```
|
||||
|
||||
The full package remains the default for most users and continues to support the existing import style:
|
||||
|
||||
```python
|
||||
from fastmcp import Client, FastMCP
|
||||
|
||||
server = FastMCP("Example")
|
||||
client = Client(server)
|
||||
```
|
||||
|
||||
Use the full package for:
|
||||
|
||||
- defining or running FastMCP servers
|
||||
- in-memory clients connected directly to `FastMCP` server objects
|
||||
- multi-server MCP configurations
|
||||
- FastMCP apps, proxies, server auth, middleware, and other server-side features
|
||||
|
||||
The `fastmcp-slim` package is intentionally narrower: it is for client-only consumers who want FastMCP's MCP client behavior without depending on the full framework.
|
||||
|
|
@ -186,5 +186,5 @@ client = Client(
|
|||
Tool execution happens on the server side. The client's role is to pass tools to the LLM and return the LLM's response (which may include tool use requests). The server then executes the tools and may send follow-up sampling requests with tool results.
|
||||
|
||||
<Tip>
|
||||
To implement a custom sampling handler, see the [handler source code](https://github.com/PrefectHQ/fastmcp/tree/main/src/fastmcp/client/sampling/handlers) as a reference.
|
||||
To implement a custom sampling handler, see the [handler source code](https://github.com/PrefectHQ/fastmcp/tree/main/fastmcp_slim/fastmcp/client/sampling/handlers) as a reference.
|
||||
</Tip>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ Tests should complete in under 1 second unless marked as integration tests. This
|
|||
|
||||
### Test Organization
|
||||
|
||||
Our test organization mirrors the `src/` directory structure, creating a predictable mapping between code and tests. When you're working on `src/fastmcp/server/auth.py`, you'll find its tests in `tests/server/test_auth.py`. In rare cases tests are split further - for example, the OpenAPI tests are so comprehensive they're split across multiple files.
|
||||
Our test organization mirrors the source package structure, creating a predictable mapping between code and tests. When you're working on `fastmcp_slim/fastmcp/server/auth.py`, you'll find its tests in `tests/server/test_auth.py`. In rare cases tests are split further - for example, the OpenAPI tests are so comprehensive they're split across multiple files.
|
||||
|
||||
### Test Markers
|
||||
|
||||
|
|
@ -393,4 +393,4 @@ just docs
|
|||
mintlify dev
|
||||
```
|
||||
|
||||
The local server watches for changes and automatically refreshes. This preview catches formatting issues and helps you see documentation as users will experience it.
|
||||
The local server watches for changes and automatically refreshes. This preview catches formatting issues and helps you see documentation as users will experience it.
|
||||
|
|
|
|||
|
|
@ -444,7 +444,7 @@ async def dashboard(ctx: Context) -> dict:
|
|||
|
||||
**Future phases** will add a component DSL for building UIs declaratively, an in-repo renderer, and a `FastMCPApp` class.
|
||||
|
||||
Implementation: `src/fastmcp/server/apps.py` (models and constants), with integration points in `server.py` (decorator parameters), `low_level.py` (extension advertisement), and `context.py` (`client_supports_extension` method).
|
||||
Implementation: `fastmcp_slim/fastmcp/server/apps.py` (models and constants), with integration points in `server.py` (decorator parameters), `low_level.py` (extension advertisement), and `context.py` (`client_supports_extension` method).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -454,7 +454,7 @@ Implementation: `src/fastmcp/server/apps.py` (models and constants), with integr
|
|||
|
||||
v3.0 introduces a provider-based component system that replaces v2's static-only registration ([#2622](https://github.com/PrefectHQ/fastmcp/pull/2622)). Providers dynamically source tools, resources, templates, and prompts at runtime.
|
||||
|
||||
**Core abstraction** (`src/fastmcp/server/providers/base.py`):
|
||||
**Core abstraction** (`fastmcp_slim/fastmcp/server/providers/base.py`):
|
||||
```python
|
||||
class Provider:
|
||||
async def list_tools(self) -> Sequence[Tool]: ...
|
||||
|
|
@ -474,7 +474,7 @@ Providers support:
|
|||
|
||||
### LocalProvider
|
||||
|
||||
`LocalProvider` (`src/fastmcp/server/providers/local_provider.py`) manages components registered via decorators. Can be used standalone and attached to multiple servers:
|
||||
`LocalProvider` (`fastmcp_slim/fastmcp/server/providers/local_provider.py`) manages components registered via decorators. Can be used standalone and attached to multiple servers:
|
||||
|
||||
```python
|
||||
from fastmcp.server.providers import LocalProvider
|
||||
|
|
@ -492,7 +492,7 @@ server2 = FastMCP("Server2", providers=[provider])
|
|||
|
||||
### ProxyProvider
|
||||
|
||||
`ProxyProvider` (`src/fastmcp/server/providers/proxy.py`) proxies components from remote MCP servers via a client factory. Used by `create_proxy()` and `FastMCP.mount()` for remote server integration.
|
||||
`ProxyProvider` (`fastmcp_slim/fastmcp/server/providers/proxy.py`) proxies components from remote MCP servers via a client factory. Used by `create_proxy()` and `FastMCP.mount()` for remote server integration.
|
||||
|
||||
```python
|
||||
from fastmcp.server import create_proxy
|
||||
|
|
@ -503,7 +503,7 @@ server = create_proxy("http://remote-server/mcp")
|
|||
|
||||
### OpenAPIProvider
|
||||
|
||||
`OpenAPIProvider` (`src/fastmcp/server/providers/openapi/provider.py`) creates MCP components from OpenAPI specifications. Routes map HTTP operations to tools, resources, or templates based on configurable rules.
|
||||
`OpenAPIProvider` (`fastmcp_slim/fastmcp/server/providers/openapi/provider.py`) creates MCP components from OpenAPI specifications. Routes map HTTP operations to tools, resources, or templates based on configurable rules.
|
||||
|
||||
```python
|
||||
from fastmcp.server.providers.openapi import OpenAPIProvider
|
||||
|
|
@ -523,7 +523,7 @@ Features:
|
|||
|
||||
### FastMCPProvider
|
||||
|
||||
`FastMCPProvider` (`src/fastmcp/server/providers/fastmcp_provider.py`) wraps a FastMCP server to enable mounting one server onto another. Components delegate execution through the wrapped server's middleware chain.
|
||||
`FastMCPProvider` (`fastmcp_slim/fastmcp/server/providers/fastmcp_provider.py`) wraps a FastMCP server to enable mounting one server onto another. Components delegate execution through the wrapped server's middleware chain.
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
|
|
@ -548,7 +548,7 @@ main.add_provider(provider)
|
|||
|
||||
Transforms modify components (tools, resources, prompts) as they flow from providers to clients ([#2836](https://github.com/PrefectHQ/fastmcp/pull/2836)). They use a middleware pattern where each transform receives a `call_next` callable to continue the chain.
|
||||
|
||||
**Built-in transforms** (`src/fastmcp/server/transforms/`):
|
||||
**Built-in transforms** (`fastmcp_slim/fastmcp/server/transforms/`):
|
||||
|
||||
- `Namespace` - adds prefixes to names (`tool` → `api_tool`) and path segments to URIs (`data://x` → `data://api/x`)
|
||||
- `ToolTransform` - modifies tool schemas (rename, description, tags, argument transforms)
|
||||
|
|
@ -873,7 +873,7 @@ v3.0 introduces type-safe result classes that provide explicit control over comp
|
|||
|
||||
#### ToolResult
|
||||
|
||||
`ToolResult` (`src/fastmcp/tools/tool.py:79`) provides structured tool responses:
|
||||
`ToolResult` (`fastmcp_slim/fastmcp/tools/tool.py:79`) provides structured tool responses:
|
||||
|
||||
```python
|
||||
from fastmcp.tools import ToolResult
|
||||
|
|
@ -894,7 +894,7 @@ Fields:
|
|||
|
||||
#### ResourceResult
|
||||
|
||||
`ResourceResult` (`src/fastmcp/resources/resource.py:117`) provides structured resource responses:
|
||||
`ResourceResult` (`fastmcp_slim/fastmcp/resources/resource.py:117`) provides structured resource responses:
|
||||
|
||||
```python
|
||||
from fastmcp.resources import ResourceResult, ResourceContent
|
||||
|
|
@ -914,7 +914,7 @@ Accepts strings, bytes, or `list[ResourceContent]` for flexible content handling
|
|||
|
||||
#### PromptResult
|
||||
|
||||
`PromptResult` (`src/fastmcp/prompts/prompt.py:109`) provides structured prompt responses:
|
||||
`PromptResult` (`fastmcp_slim/fastmcp/prompts/prompt.py:109`) provides structured prompt responses:
|
||||
|
||||
```python
|
||||
from fastmcp.prompts import PromptResult, Message
|
||||
|
|
@ -936,7 +936,7 @@ def conversation() -> PromptResult:
|
|||
|
||||
v3.0 implements MCP SEP-1686 for background task execution via Docket integration.
|
||||
|
||||
**Configuration** (`src/fastmcp/server/tasks/config.py`):
|
||||
**Configuration** (`fastmcp_slim/fastmcp/server/tasks/config.py`):
|
||||
|
||||
```python
|
||||
from fastmcp.server.tasks import TaskConfig
|
||||
|
|
@ -1012,7 +1012,7 @@ fastmcp run server.py --reload --reload-dir ./src --reload-dir ./lib
|
|||
fastmcp run server.py --reload --transport http --port 8080
|
||||
```
|
||||
|
||||
Implementation (`src/fastmcp/cli/run.py`):
|
||||
Implementation (`fastmcp_slim/fastmcp/cli/run.py`):
|
||||
- Uses `watchfiles` for efficient file monitoring
|
||||
- Runs server as subprocess for clean restarts
|
||||
- Stateless mode for seamless reconnection after restart
|
||||
|
|
|
|||
|
|
@ -235,6 +235,7 @@
|
|||
"group": "Clients",
|
||||
"pages": [
|
||||
"clients/client",
|
||||
"clients/client-only-package",
|
||||
"clients/transports",
|
||||
{
|
||||
"collapsed": true,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ FastMCP includes a `contrib` package that holds community-contributed modules. T
|
|||
|
||||
Contrib modules provide additional features, integrations, or patterns that complement the core FastMCP library. They offer a way for the community to share useful extensions while keeping the core library focused and maintainable.
|
||||
|
||||
The available modules can be viewed in the [contrib directory](https://github.com/PrefectHQ/fastmcp/tree/main/src/fastmcp/contrib).
|
||||
The available modules can be viewed in the [contrib directory](https://github.com/PrefectHQ/fastmcp/tree/main/fastmcp_slim/fastmcp/contrib).
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ from fastmcp.contrib import my_module
|
|||
|
||||
We welcome contributions to the `contrib` package! If you have a module that extends FastMCP in a useful way, consider contributing it:
|
||||
|
||||
1. Create a new directory in `src/fastmcp/contrib/` for your module
|
||||
1. Create a new directory in `fastmcp_slim/fastmcp/contrib/` for your module
|
||||
3. Add proper tests for your module in `tests/contrib/`
|
||||
2. Include comprehensive documentation in a README.md file, including usage and examples, as well as any additional dependencies or installation instructions
|
||||
5. Submit a pull request
|
||||
|
|
|
|||
|
|
@ -254,5 +254,5 @@ Install the Anthropic handler with `pip install fastmcp[anthropic]`.
|
|||
Tool execution happens on the server side. The client's role is to pass tools to the LLM and return the LLM's response (which may include tool use requests). The server then executes the tools and may send follow-up sampling requests with tool results.
|
||||
|
||||
<Tip>
|
||||
To implement a custom sampling handler, see the [handler source code](https://github.com/PrefectHQ/fastmcp/tree/main/src/fastmcp/client/sampling/handlers) as a reference.
|
||||
To implement a custom sampling handler, see the [handler source code](https://github.com/PrefectHQ/fastmcp/tree/main/fastmcp_slim/fastmcp/client/sampling/handlers) as a reference.
|
||||
</Tip>
|
||||
|
|
@ -33,7 +33,7 @@ Tests should complete in under 1 second unless marked as integration tests. This
|
|||
|
||||
### Test Organization
|
||||
|
||||
Our test organization mirrors the `src/` directory structure, creating a predictable mapping between code and tests. When you're working on `src/fastmcp/server/auth.py`, you'll find its tests in `tests/server/test_auth.py`. In rare cases tests are split further - for example, the OpenAPI tests are so comprehensive they're split across multiple files.
|
||||
Our test organization mirrors the source package structure, creating a predictable mapping between code and tests. When you're working on `fastmcp_slim/fastmcp/server/auth.py`, you'll find its tests in `tests/server/test_auth.py`. In rare cases tests are split further - for example, the OpenAPI tests are so comprehensive they're split across multiple files.
|
||||
|
||||
### Test Markers
|
||||
|
||||
|
|
@ -393,4 +393,4 @@ just docs
|
|||
mintlify dev
|
||||
```
|
||||
|
||||
The local server watches for changes and automatically refreshes. This preview catches formatting issues and helps you see documentation as users will experience it.
|
||||
The local server watches for changes and automatically refreshes. This preview catches formatting issues and helps you see documentation as users will experience it.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ FastMCP includes a `contrib` package that holds community-contributed modules. T
|
|||
|
||||
Contrib modules provide additional features, integrations, or patterns that complement the core FastMCP library. They offer a way for the community to share useful extensions while keeping the core library focused and maintainable.
|
||||
|
||||
The available modules can be viewed in the [contrib directory](https://github.com/PrefectHQ/fastmcp/tree/main/src/fastmcp/contrib).
|
||||
The available modules can be viewed in the [contrib directory](https://github.com/PrefectHQ/fastmcp/tree/main/fastmcp_slim/fastmcp/contrib).
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ from fastmcp.contrib import my_module
|
|||
|
||||
We welcome contributions to the `contrib` package! If you have a module that extends FastMCP in a useful way, consider contributing it:
|
||||
|
||||
1. Create a new directory in `src/fastmcp/contrib/` for your module
|
||||
1. Create a new directory in `fastmcp_slim/fastmcp/contrib/` for your module
|
||||
3. Add proper tests for your module in `tests/contrib/`
|
||||
2. Include comprehensive documentation in a README.md file, including usage and examples, as well as any additional dependencies or installation instructions
|
||||
5. Submit a pull request
|
||||
|
|
|
|||
120
fastmcp_slim/README.md
Normal file
120
fastmcp_slim/README.md
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
<div align="center">
|
||||
|
||||
<!-- omit in toc -->
|
||||
|
||||
<picture>
|
||||
<source width="550" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/PrefectHQ/fastmcp/main/docs/assets/brand/f-watercolor-waves-4-dark.png">
|
||||
<source width="550" media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/PrefectHQ/fastmcp/main/docs/assets/brand/f-watercolor-waves-4.png">
|
||||
<img width="550" alt="FastMCP Logo" src="https://raw.githubusercontent.com/PrefectHQ/fastmcp/main/docs/assets/brand/f-watercolor-waves-2.png">
|
||||
</picture>
|
||||
|
||||
# FastMCP 🚀
|
||||
|
||||
<strong>Move fast and make things.</strong>
|
||||
|
||||
*Made with 💙 by [Prefect](https://www.prefect.io/)*
|
||||
|
||||
[](https://gofastmcp.com)
|
||||
[](https://discord.gg/uu8dJCgttd)
|
||||
[](https://pypi.org/project/fastmcp)
|
||||
[](https://github.com/PrefectHQ/fastmcp/actions/workflows/run-tests.yml)
|
||||
[](https://github.com/PrefectHQ/fastmcp/blob/main/LICENSE)
|
||||
|
||||
<a href="https://trendshift.io/repositories/13266" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13266" alt="prefecthq%2Ffastmcp | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
The [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) connects LLMs to tools and data. FastMCP gives you everything you need to go from prototype to production:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("Demo 🚀")
|
||||
|
||||
@mcp.tool
|
||||
def add(a: int, b: int) -> int:
|
||||
"""Add two numbers"""
|
||||
return a + b
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
```
|
||||
|
||||
## Why FastMCP
|
||||
|
||||
Building an effective MCP application is harder than it looks. FastMCP handles all of it. Declare a tool with a Python function, and the schema, validation, and documentation are generated automatically. Connect to a server with a URL, and transport negotiation, authentication, and protocol lifecycle are managed for you. You focus on your logic, and the MCP part just works: **with FastMCP, best practices are built in.**
|
||||
|
||||
**That's why FastMCP is the standard framework for working with MCP.** FastMCP 1.0 was incorporated into the official MCP Python SDK in 2024. Today, the actively maintained standalone project is downloaded a million times a day, and some version of FastMCP powers 70% of MCP servers across all languages.
|
||||
|
||||
FastMCP has three pillars:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="33%">
|
||||
<a href="https://gofastmcp.com/servers/server">
|
||||
<img src="https://raw.githubusercontent.com/PrefectHQ/fastmcp/main/docs/assets/images/servers-card.png" alt="Servers" />
|
||||
<br /><strong>Servers</strong>
|
||||
</a>
|
||||
<br />Expose tools, resources, and prompts to LLMs.
|
||||
</td>
|
||||
<td align="center" valign="top" width="33%">
|
||||
<a href="https://gofastmcp.com/apps/overview">
|
||||
<img src="https://raw.githubusercontent.com/PrefectHQ/fastmcp/main/docs/assets/images/apps-card.png" alt="Apps" />
|
||||
<br /><strong>Apps</strong>
|
||||
</a>
|
||||
<br />Give your tools interactive UIs rendered directly in the conversation.
|
||||
</td>
|
||||
<td align="center" valign="top" width="33%">
|
||||
<a href="https://gofastmcp.com/clients/client">
|
||||
<img src="https://raw.githubusercontent.com/PrefectHQ/fastmcp/main/docs/assets/images/clients-card.png" alt="Clients" />
|
||||
<br /><strong>Clients</strong>
|
||||
</a>
|
||||
<br />Connect to any MCP server — local or remote, programmatic or CLI.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**[Servers](https://gofastmcp.com/servers/server)** wrap your Python functions into MCP-compliant tools, resources, and prompts. **[Clients](https://gofastmcp.com/clients/client)** connect to any server with full protocol support. And **[Apps](https://gofastmcp.com/apps/overview)** give your tools interactive UIs rendered directly in the conversation.
|
||||
|
||||
Ready to build? Start with the [installation guide](https://gofastmcp.com/getting-started/installation) or jump straight to the [quickstart](https://gofastmcp.com/getting-started/quickstart).
|
||||
|
||||
## Run FastMCP in production with Horizon
|
||||
|
||||
FastMCP is the standard way to build MCP servers. **[Prefect Horizon](https://www.prefect.io/horizon?utm_source=github&utm_medium=readme&utm_campaign=readme_horizon&utm_content=readme_body)** is the enterprise MCP gateway for running them safely.
|
||||
|
||||
Built by the FastMCP team, Horizon packages the best practices we've learned shipping the world's most popular MCP framework.
|
||||
|
||||
Deploy FastMCP servers from GitHub with branch previews and instant rollback. Create a private registry of every MCP your company uses. Secure access with SSO and tool-level RBAC. Get audit logs, observability, and governance across your MCP stack. Remix approved tools into purpose-built endpoints for teams and agents.
|
||||
|
||||
Start with FastMCP. [Scale with Horizon →](https://www.prefect.io/horizon?utm_source=github&utm_medium=readme&utm_campaign=readme_horizon&utm_content=readme_cta)
|
||||
|
||||
## Installation
|
||||
|
||||
We recommend installing FastMCP with [uv](https://docs.astral.sh/uv/):
|
||||
|
||||
```bash
|
||||
uv pip install fastmcp
|
||||
```
|
||||
|
||||
For full installation instructions, including verification and upgrading, see the [**Installation Guide**](https://gofastmcp.com/getting-started/installation).
|
||||
|
||||
**Upgrading?** We have guides for:
|
||||
- [Upgrading from FastMCP v2](https://gofastmcp.com/getting-started/upgrading/from-fastmcp-2)
|
||||
- [Upgrading from the MCP Python SDK](https://gofastmcp.com/getting-started/upgrading/from-mcp-sdk)
|
||||
- [Upgrading from the low-level SDK](https://gofastmcp.com/getting-started/upgrading/from-low-level-sdk)
|
||||
|
||||
## 📚 Documentation
|
||||
|
||||
FastMCP's complete documentation is available at **[gofastmcp.com](https://gofastmcp.com)**, including detailed guides, API references, and advanced patterns.
|
||||
|
||||
Documentation is also available in [llms.txt format](https://llmstxt.org/), which is a simple markdown standard that LLMs can consume easily:
|
||||
|
||||
- [`llms.txt`](https://gofastmcp.com/llms.txt) is essentially a sitemap, listing all the pages in the documentation.
|
||||
- [`llms-full.txt`](https://gofastmcp.com/llms-full.txt) contains the entire documentation. Note this may exceed the context window of your LLM.
|
||||
|
||||
**Community:** Join our [Discord server](https://discord.gg/uu8dJCgttd) to connect with other FastMCP developers and share what you're building.
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions! See the [Contributing Guide](https://gofastmcp.com/development/contributing) for setup instructions, testing requirements, and PR guidelines.
|
||||
117
fastmcp_slim/fastmcp/__init__.py
Normal file
117
fastmcp_slim/fastmcp/__init__.py
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
"""FastMCP - An ergonomic MCP interface."""
|
||||
|
||||
import importlib
|
||||
import warnings
|
||||
from importlib.metadata import PackageNotFoundError, version as _version
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from fastmcp.settings import Settings
|
||||
from fastmcp.utilities.logging import configure_logging as _configure_logging
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fastmcp.client import Client as Client
|
||||
from fastmcp.apps.app import FastMCPApp as FastMCPApp
|
||||
from fastmcp.exceptions import (
|
||||
FastMCPDeprecationWarning as FastMCPDeprecationWarning,
|
||||
)
|
||||
from fastmcp.server.context import Context as Context
|
||||
from fastmcp.server.server import FastMCP as FastMCP
|
||||
|
||||
settings = Settings()
|
||||
if settings.log_enabled:
|
||||
_configure_logging(
|
||||
level=settings.log_level,
|
||||
enable_rich_tracebacks=settings.enable_rich_tracebacks,
|
||||
)
|
||||
|
||||
try:
|
||||
__version__ = _version("fastmcp-slim")
|
||||
except PackageNotFoundError:
|
||||
__version__ = _version("fastmcp")
|
||||
|
||||
if settings.deprecation_warnings:
|
||||
try:
|
||||
from fastmcp.exceptions import FastMCPDeprecationWarning
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
warnings.simplefilter("default", FastMCPDeprecationWarning)
|
||||
|
||||
|
||||
# --- Lazy imports for performance (see #3292) ---
|
||||
# Client and the client submodule are deferred so that server-only users
|
||||
# don't pay for the client import chain. Do not convert back to top-level.
|
||||
|
||||
|
||||
def __getattr__(name: str) -> object:
|
||||
if name == "Client":
|
||||
try:
|
||||
from fastmcp.client import Client
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"FastMCP client support is not installed. Install "
|
||||
"`fastmcp-slim[client]` or `fastmcp`."
|
||||
) from exc
|
||||
|
||||
return Client
|
||||
if name == "Context":
|
||||
try:
|
||||
from fastmcp.server.context import Context
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"FastMCP server support is not installed. Install "
|
||||
"`fastmcp-slim[server]` or `fastmcp`."
|
||||
) from exc
|
||||
|
||||
return Context
|
||||
if name == "FastMCP":
|
||||
try:
|
||||
from fastmcp.server.server import FastMCP
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"FastMCP server support is not installed. Install "
|
||||
"`fastmcp-slim[server]` or `fastmcp`."
|
||||
) from exc
|
||||
|
||||
return FastMCP
|
||||
if name == "FastMCPApp":
|
||||
try:
|
||||
from fastmcp.apps.app import FastMCPApp
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"FastMCP app support is not installed. Install "
|
||||
"`fastmcp-slim[server,apps]` or `fastmcp[apps]`."
|
||||
) from exc
|
||||
|
||||
return FastMCPApp
|
||||
if name == "FastMCPDeprecationWarning":
|
||||
from fastmcp.exceptions import FastMCPDeprecationWarning
|
||||
|
||||
return FastMCPDeprecationWarning
|
||||
if name == "client":
|
||||
try:
|
||||
return importlib.import_module("fastmcp.client")
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"FastMCP client support is not installed. Install "
|
||||
"`fastmcp-slim[client]` or `fastmcp`."
|
||||
) from exc
|
||||
if name == "server":
|
||||
try:
|
||||
return importlib.import_module("fastmcp.server")
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"FastMCP server support is not installed. Install "
|
||||
"`fastmcp-slim[server]` or `fastmcp`."
|
||||
) from exc
|
||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Client",
|
||||
"Context",
|
||||
"FastMCP",
|
||||
"FastMCPApp",
|
||||
"FastMCPDeprecationWarning",
|
||||
"settings",
|
||||
]
|
||||
36
fastmcp_slim/fastmcp/client/__init__.py
Normal file
36
fastmcp_slim/fastmcp/client/__init__.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
try:
|
||||
from .auth import OAuth, BearerAuth
|
||||
from .client import Client
|
||||
from .transports import (
|
||||
ClientTransport,
|
||||
FastMCPTransport,
|
||||
NodeStdioTransport,
|
||||
NpxStdioTransport,
|
||||
PythonStdioTransport,
|
||||
SSETransport,
|
||||
StdioTransport,
|
||||
StreamableHttpTransport,
|
||||
UvStdioTransport,
|
||||
UvxStdioTransport,
|
||||
)
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"FastMCP client support is not installed. Install "
|
||||
"`fastmcp-slim[client]` or `fastmcp`."
|
||||
) from exc
|
||||
|
||||
__all__ = [
|
||||
"BearerAuth",
|
||||
"Client",
|
||||
"ClientTransport",
|
||||
"FastMCPTransport",
|
||||
"NodeStdioTransport",
|
||||
"NpxStdioTransport",
|
||||
"OAuth",
|
||||
"PythonStdioTransport",
|
||||
"SSETransport",
|
||||
"StdioTransport",
|
||||
"StreamableHttpTransport",
|
||||
"UvStdioTransport",
|
||||
"UvxStdioTransport",
|
||||
]
|
||||
|
|
@ -10,7 +10,7 @@ from collections.abc import Coroutine
|
|||
from contextlib import AsyncExitStack, asynccontextmanager, suppress
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Any, Generic, Literal, TypeVar, cast, overload
|
||||
from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar, cast, overload
|
||||
|
||||
import anyio
|
||||
import httpx
|
||||
|
|
@ -20,9 +20,12 @@ from mcp import ClientSession, McpError
|
|||
from mcp.types import GetTaskResult, TaskStatusNotification
|
||||
from pydantic import AnyUrl
|
||||
|
||||
import fastmcp
|
||||
import fastmcp as fastmcp
|
||||
from fastmcp.client.auth.oauth import OAuth
|
||||
from fastmcp.client.elicitation import ElicitationHandler, create_elicitation_callback
|
||||
from fastmcp.client.elicitation import (
|
||||
ElicitationHandler,
|
||||
create_elicitation_callback,
|
||||
)
|
||||
from fastmcp.client.logging import (
|
||||
LogHandler,
|
||||
create_log_callback,
|
||||
|
|
@ -52,7 +55,6 @@ from fastmcp.client.tasks import (
|
|||
ToolTask,
|
||||
)
|
||||
from fastmcp.mcp_config import MCPConfig
|
||||
from fastmcp.server import FastMCP
|
||||
from fastmcp.utilities.exceptions import get_catch_handlers
|
||||
from fastmcp.utilities.logging import get_logger
|
||||
from fastmcp.utilities.timeout import (
|
||||
|
|
@ -60,6 +62,11 @@ from fastmcp.utilities.timeout import (
|
|||
normalize_timeout_to_timedelta,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fastmcp.server import FastMCP
|
||||
else:
|
||||
FastMCP = Any
|
||||
|
||||
from .transports import (
|
||||
ClientTransport,
|
||||
ClientTransportT,
|
||||
20
fastmcp_slim/fastmcp/client/dependencies.py
Normal file
20
fastmcp_slim/fastmcp/client/dependencies.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
"""Client-side dependency helpers."""
|
||||
|
||||
|
||||
def get_http_headers(
|
||||
include_all: bool = False,
|
||||
include: set[str] | None = None,
|
||||
) -> dict[str, str]:
|
||||
"""Return HTTP headers from an ambient server request, when available.
|
||||
|
||||
The standalone client package has no server request context. When the full
|
||||
FastMCP package is installed, delegate to its request-aware implementation.
|
||||
"""
|
||||
try:
|
||||
from fastmcp.server.dependencies import (
|
||||
get_http_headers as get_server_http_headers,
|
||||
)
|
||||
except ImportError:
|
||||
return {}
|
||||
|
||||
return get_server_http_headers(include_all=include_all, include=include)
|
||||
|
|
@ -58,7 +58,7 @@ def create_sampling_callback(
|
|||
if isinstance(result, str):
|
||||
result = CreateMessageResult(
|
||||
role="assistant",
|
||||
model="fastmcp-client",
|
||||
model="fastmcp-slim",
|
||||
content=mcp.types.TextContent(type="text", text=result),
|
||||
)
|
||||
return result
|
||||
|
|
@ -41,7 +41,7 @@ try:
|
|||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"The `anthropic` package is not installed. "
|
||||
"Install it with `pip install fastmcp[anthropic]` or add `anthropic` to your dependencies."
|
||||
"Install it with `pip install fastmcp-slim[anthropic]` or add `anthropic` to your dependencies."
|
||||
) from e
|
||||
|
||||
__all__ = ["AnthropicSamplingHandler"]
|
||||
|
|
@ -27,7 +27,7 @@ try:
|
|||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"The `google-genai` package is not installed. "
|
||||
"Install it with `pip install fastmcp[gemini]` or add `google-genai` "
|
||||
"Install it with `pip install fastmcp-slim[gemini]` or add `google-genai` "
|
||||
"to your dependencies."
|
||||
) from e
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ try:
|
|||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"The `openai` package is not installed. "
|
||||
"Please install `fastmcp[openai]` or add `openai` to your dependencies manually."
|
||||
"Please install `fastmcp-slim[openai]` or add `openai` to your dependencies manually."
|
||||
) from e
|
||||
|
||||
# OpenAI only supports wav and mp3 for input audio
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
# Re-export all public APIs for backward compatibility
|
||||
from mcp.server.fastmcp import FastMCP as FastMCP1Server
|
||||
|
||||
from fastmcp.client.transports.base import (
|
||||
|
|
@ -20,7 +19,6 @@ from fastmcp.client.transports.stdio import (
|
|||
UvStdioTransport,
|
||||
UvxStdioTransport,
|
||||
)
|
||||
from fastmcp.server.server import FastMCP
|
||||
|
||||
__all__ = [
|
||||
"ClientTransport",
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import contextlib
|
||||
import datetime
|
||||
from collections.abc import AsyncIterator
|
||||
from typing import Any
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from mcp import ClientSession
|
||||
from typing_extensions import Unpack
|
||||
|
|
@ -15,10 +15,13 @@ from fastmcp.mcp_config import (
|
|||
StdioMCPServer,
|
||||
TransformingRemoteMCPServer,
|
||||
TransformingStdioMCPServer,
|
||||
_coerce_tool_transform_configs,
|
||||
)
|
||||
from fastmcp.server.server import FastMCP, create_proxy
|
||||
from fastmcp.utilities.logging import get_logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fastmcp.server.server import FastMCP
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
|
|
@ -98,6 +101,14 @@ class MCPConfigTransport(ClientTransport):
|
|||
# each ProxyClient so its underlying transport session stays alive for
|
||||
# the duration of this context (fixes session persistence for
|
||||
# streamable-http backends — see #2790).
|
||||
try:
|
||||
from fastmcp.server.server import FastMCP
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"MCP configs with multiple servers require the full `fastmcp` "
|
||||
"package for now. Install it with `pip install fastmcp`."
|
||||
) from exc
|
||||
|
||||
timeout = session_kwargs.get("read_timeout_seconds")
|
||||
composite = FastMCP[Any](name="MCPRouter")
|
||||
|
||||
|
|
@ -138,7 +149,7 @@ class MCPConfigTransport(ClientTransport):
|
|||
config: MCPServerTypes,
|
||||
timeout: datetime.timedelta | None,
|
||||
stack: contextlib.AsyncExitStack,
|
||||
) -> tuple[ClientTransport, Any, FastMCP[Any]]:
|
||||
) -> tuple[ClientTransport, Any, "FastMCP[Any]"]:
|
||||
"""Create underlying transport, proxy client, and proxy server for a single backend.
|
||||
|
||||
The ProxyClient is connected via the AsyncExitStack *before* being
|
||||
|
|
@ -149,6 +160,7 @@ class MCPConfigTransport(ClientTransport):
|
|||
"""
|
||||
# Import here to avoid circular dependency
|
||||
from fastmcp.server.providers.proxy import StatefulProxyClient
|
||||
from fastmcp.server.server import create_proxy
|
||||
|
||||
tool_transforms = None
|
||||
include_tags = None
|
||||
|
|
@ -194,7 +206,9 @@ class MCPConfigTransport(ClientTransport):
|
|||
if tool_transforms:
|
||||
from fastmcp.server.transforms import ToolTransform
|
||||
|
||||
proxy.add_transform(ToolTransform(tool_transforms))
|
||||
proxy.add_transform(
|
||||
ToolTransform(_coerce_tool_transform_configs(tool_transforms))
|
||||
)
|
||||
# Then add enabled filters - they filter based on tags
|
||||
if include_tags:
|
||||
proxy.enable(tags=set(include_tags), only=True)
|
||||
|
|
@ -15,12 +15,12 @@ from mcp.shared._httpx_utils import McpHttpClientFactory, create_mcp_http_client
|
|||
from pydantic import AnyUrl
|
||||
from typing_extensions import Unpack
|
||||
|
||||
import fastmcp
|
||||
import fastmcp as fastmcp
|
||||
from fastmcp.client.auth.bearer import BearerAuth
|
||||
from fastmcp.client.auth.oauth import OAuth
|
||||
from fastmcp.client.dependencies import get_http_headers
|
||||
from fastmcp.client.transports.base import ClientTransport, SessionKwargs
|
||||
from fastmcp.exceptions import FastMCPDeprecationWarning
|
||||
from fastmcp.server.dependencies import get_http_headers
|
||||
from fastmcp.utilities.timeout import normalize_timeout_to_timedelta
|
||||
|
||||
|
||||
|
|
@ -9,13 +9,17 @@ from fastmcp.client.transports.config import MCPConfigTransport
|
|||
from fastmcp.client.transports.http import StreamableHttpTransport
|
||||
from fastmcp.client.transports.memory import FastMCPTransport
|
||||
from fastmcp.client.transports.sse import SSETransport
|
||||
from fastmcp.client.transports.stdio import NodeStdioTransport, PythonStdioTransport
|
||||
from fastmcp.client.transports.stdio import (
|
||||
NodeStdioTransport,
|
||||
PythonStdioTransport,
|
||||
)
|
||||
from fastmcp.mcp_config import MCPConfig, infer_transport_type_from_url
|
||||
from fastmcp.server.server import FastMCP
|
||||
from fastmcp.utilities.logging import get_logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
from fastmcp.server.server import FastMCP
|
||||
else:
|
||||
FastMCP = Any
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
|
@ -114,9 +118,9 @@ def infer_transport(
|
|||
return transport
|
||||
|
||||
# the transport is a FastMCP server (2.x or 1.0)
|
||||
elif isinstance(transport, FastMCP | FastMCP1Server):
|
||||
elif _is_fastmcp_server(transport):
|
||||
inferred_transport = FastMCPTransport(
|
||||
mcp=cast(FastMCP[Any] | FastMCP1Server, transport)
|
||||
mcp=cast("FastMCP[Any] | FastMCP1Server", transport)
|
||||
)
|
||||
|
||||
# the transport is a path to a script
|
||||
|
|
@ -152,3 +156,15 @@ def infer_transport(
|
|||
|
||||
logger.debug(f"Inferred transport: {inferred_transport}")
|
||||
return inferred_transport
|
||||
|
||||
|
||||
def _is_fastmcp_server(transport: object) -> bool:
|
||||
if isinstance(transport, FastMCP1Server):
|
||||
return True
|
||||
|
||||
try:
|
||||
from fastmcp.server.server import FastMCP as FastMCP2Server
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
return isinstance(transport, FastMCP2Server)
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
import contextlib
|
||||
import importlib
|
||||
from collections.abc import AsyncIterator
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import anyio
|
||||
from mcp import ClientSession
|
||||
|
|
@ -8,7 +10,9 @@ from mcp.shared.memory import create_client_server_memory_streams
|
|||
from typing_extensions import Unpack
|
||||
|
||||
from fastmcp.client.transports.base import ClientTransport, SessionKwargs
|
||||
from fastmcp.server.server import FastMCP
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fastmcp.server.server import FastMCP
|
||||
|
||||
|
||||
class FastMCPTransport(ClientTransport):
|
||||
|
|
@ -20,7 +24,9 @@ class FastMCPTransport(ClientTransport):
|
|||
tests or scenarios where client and server run in the same runtime.
|
||||
"""
|
||||
|
||||
def __init__(self, mcp: FastMCP | FastMCP1Server, raise_exceptions: bool = False):
|
||||
def __init__(
|
||||
self, mcp: "FastMCP[Any] | FastMCP1Server", raise_exceptions: bool = False
|
||||
):
|
||||
"""Initialize a FastMCPTransport from a FastMCP server instance."""
|
||||
|
||||
# Accept both FastMCP 2.x and FastMCP 1.0 servers. Both expose a
|
||||
|
|
@ -87,10 +93,22 @@ class FastMCPTransport(ClientTransport):
|
|||
|
||||
@contextlib.asynccontextmanager
|
||||
async def _enter_server_lifespan(
|
||||
server: FastMCP | FastMCP1Server,
|
||||
server: "FastMCP[Any] | FastMCP1Server",
|
||||
) -> AsyncIterator[None]:
|
||||
"""Enters the server's lifespan context for FastMCP servers and does nothing for FastMCP 1 servers."""
|
||||
if isinstance(server, FastMCP):
|
||||
FastMCP2: type[Any] | None
|
||||
try:
|
||||
FastMCP2 = importlib.import_module("fastmcp.server.server").FastMCP
|
||||
except ImportError:
|
||||
FastMCP2 = None
|
||||
|
||||
if FastMCP2 is None and not isinstance(server, FastMCP1Server):
|
||||
raise ImportError(
|
||||
"In-memory FastMCP transports require the full `fastmcp` package. "
|
||||
"Install it with `pip install fastmcp`."
|
||||
)
|
||||
|
||||
if FastMCP2 is not None and isinstance(server, FastMCP2):
|
||||
async with server._lifespan_manager():
|
||||
yield
|
||||
else:
|
||||
|
|
@ -17,8 +17,8 @@ from typing_extensions import Unpack
|
|||
|
||||
from fastmcp.client.auth.bearer import BearerAuth
|
||||
from fastmcp.client.auth.oauth import OAuth
|
||||
from fastmcp.client.dependencies import get_http_headers
|
||||
from fastmcp.client.transports.base import ClientTransport, SessionKwargs
|
||||
from fastmcp.server.dependencies import get_http_headers
|
||||
from fastmcp.utilities.timeout import normalize_timeout_to_timedelta
|
||||
|
||||
|
||||
|
|
@ -14,7 +14,6 @@ from typing_extensions import Unpack
|
|||
|
||||
from fastmcp.client.transports.base import ClientTransport, SessionKwargs
|
||||
from fastmcp.utilities.logging import get_logger
|
||||
from fastmcp.utilities.mcp_server_config.v1.environments.uv import UVEnvironment
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
|
@ -385,20 +384,18 @@ class UvStdioTransport(StdioTransport):
|
|||
f"Project directory not found: {project_directory}"
|
||||
)
|
||||
|
||||
# Create Environment from provided parameters (internal use)
|
||||
env_config = UVEnvironment(
|
||||
python=python_version,
|
||||
dependencies=with_packages,
|
||||
requirements=with_requirements,
|
||||
project=project_directory,
|
||||
editable=None, # Not exposed in this transport
|
||||
)
|
||||
|
||||
# Build uv arguments using the config
|
||||
uv_args: list[str] = []
|
||||
|
||||
# Check if we need any environment setup
|
||||
if env_config._must_run_with_uv():
|
||||
if any(
|
||||
[
|
||||
python_version,
|
||||
with_packages,
|
||||
with_requirements,
|
||||
project_directory,
|
||||
]
|
||||
):
|
||||
# Use the config to build args, but we need to handle the command differently
|
||||
# since transport has specific needs
|
||||
uv_args = ["run"]
|
||||
|
|
@ -2,7 +2,12 @@
|
|||
|
||||
import logging
|
||||
|
||||
from mcp import McpError # noqa: F401
|
||||
try:
|
||||
from mcp import McpError
|
||||
except ImportError:
|
||||
|
||||
class McpError(Exception): # type: ignore[no-redef]
|
||||
"""Fallback used when MCP dependencies are not installed."""
|
||||
|
||||
|
||||
class FastMCPDeprecationWarning(DeprecationWarning):
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue