Skip to content

Add Authorization #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 22, 2025
Merged
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
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ repos:
rev: v5.0.0
hooks:
- id: trailing-whitespace
exclude: \.md$
- id: check-yaml
- id: check-added-large-files

Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.3.1]

🚀 FastApiMCP now supports MCP Authorization!

You can now add MCP-compliant OAuth configuration in a FastAPI-native way, using your existing FastAPI `Depends()` that we all know and love.

### Added
- 🎉 Support for Authentication / Authorization compliant to [MCP 2025-03-26 Specification](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization), using OAuth 2.1. (#10)
- 🎉 Support passing http headers to tool calls (#82)

## [0.3.0]

🚀 FastApiMCP now works with ASGI-transport by default.
Expand Down
40 changes: 30 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<p align="center"><a href="https://github.com/tadata-org/fastapi_mcp"><img src="https://github.com/user-attachments/assets/7e44e98b-a0ba-4aff-a68a-4ffee3a6189c" alt="fastapi-to-mcp" height=100/></a></p>
<h1 align="center">FastAPI-MCP</h1>
<p align="center">A zero-configuration tool for automatically exposing FastAPI endpoints as Model Context Protocol (MCP) tools.</p>
<p align="center">Expose your FastAPI endpoints as Model Context Protocol (MCP) tools, with Auth!</p>
<div align="center">

[![PyPI version](https://img.shields.io/pypi/v/fastapi-mcp?color=%2334D058&label=pypi%20package)](https://pypi.org/project/fastapi-mcp/)
Expand All @@ -16,11 +16,20 @@

## Features

- **Zero configuration** - Just point it at your FastAPI app and it works, with automatic discovery of endpoints and conversion to MCP tools
- **Schema & docs preservation** - Keep the same request/response models and preserve documentation of all your endpoints
- **Flexible deployment** - Mount your MCP server to the same FastAPI application, or deploy separately
- **Custom endpoint exposure** - Control which endpoints become MCP tools using operation IDs and tags
- **ASGI transport** - Uses FastAPI's ASGI interface directly by default for efficient communication
- **Authentication** built in, using your existing FastAPI dependencies!

- **FastAPI-native:** Not just another OpenAPI -> MCP converter

- **Zero/Minimal configuration** required - just point it at your FastAPI app and it works

- **Preserving schemas** of your request models and response models

- **Preserve documentation** of all your endpoints, just as it is in Swagger

- **Flexible deployment** - Mount your MCP server to the same app, or deploy separately

- **ASGI transport** - Uses FastAPI's ASGI interface directly for efficient communication


## Installation

Expand Down Expand Up @@ -52,9 +61,7 @@ mcp = FastApiMCP(app)
mcp.mount()
```

That's it! Your auto-generated MCP server is now available at `https://app.base.url/mcp`.

> **Note on `base_url`**: While `base_url` is optional, it is highly recommended to provide it explicitly. The `base_url` tells the MCP server where to send API requests when tools are called. Without it, the library will attempt to determine the URL automatically, which may not work correctly in deployed environments where the internal and external URLs differ.
That's it! Your auto-generated MCP server is now available at `https://app.base.url/mcp`.

## Documentation, Examples and Advanced Usage

Expand All @@ -63,10 +70,23 @@ FastAPI-MCP provides comprehensive documentation in the `docs` folder:
- [FAQ](docs/00_FAQ.md) - Frequently asked questions about usage, development and support
- [Tool Naming](docs/01_tool_naming.md) - Best practices for naming your MCP tools using operation IDs
- [Connecting to MCP Server](docs/02_connecting_to_the_mcp_server.md) - How to connect various MCP clients like Cursor and Claude Desktop
- [Advanced Usage](docs/03_advanced_usage.md) - Advanced features like custom schemas, endpoint filtering, and separate deployment
- [Authentication and Authorization](docs/03_authentication_and_authorization.md) - How to authenticate and authorize your MCP tools
- [Advanced Usage](docs/04_advanced_usage.md) - Advanced features like custom schemas, endpoint filtering, and separate deployment

Check out the [examples directory](examples) for code samples demonstrating these features in action.

## FastAPI-first Approach

FastAPI-MCP is designed as a native extension of FastAPI, not just a converter that generates MCP tools from your API. This approach offers several key advantages:

- **Native dependencies**: Secure your MCP endpoints using familiar FastAPI `Depends()` for authentication and authorization

- **ASGI transport**: Communicates directly with your FastAPI app using its ASGI interface, eliminating the need for HTTP calls from the MCP to your API

- **Unified infrastructure**: Your FastAPI app doesn't need to run separately from the MCP server (though [separate deployment](docs/04_advanced_usage.md#deploying-separately-from-original-fastapi-app) is also supported)

This design philosophy ensures minimum friction when adding MCP capabilities to your existing FastAPI services.

## Development and Contributing

Thank you for considering contributing to FastAPI-MCP! We encourage the community to post Issues and create Pull Requests.
Expand Down
39 changes: 16 additions & 23 deletions docs/02_connecting_to_the_mcp_server.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,35 @@

## Connecting to the MCP Server using SSE

Once your FastAPI app with MCP integration is running, you can connect to it with any MCP client supporting SSE, such as Cursor:
Once your FastAPI app with MCP integration is running, you can connect to it with any MCP client supporting SSE.

1. Run your application.
2. In Cursor -> Settings -> MCP, use the URL of your MCP server endpoint (e.g., `http://localhost:8000/mcp`) as sse.
3. Cursor will discover all available tools and resources automatically.
All the most popular MCP clients (Claude Desktop, Cursor & Windsurf) use the following config format:

## Connecting to the MCP Server using [mcp-proxy stdio](https://github.com/sparfenyuk/mcp-proxy?tab=readme-ov-file#1-stdio-to-sse)

If your MCP client does not support SSE, for example Claude Desktop:

1. Run your application.
2. Install [mcp-proxy](https://github.com/sparfenyuk/mcp-proxy?tab=readme-ov-file#installing-via-pypi), for example: `uv tool install mcp-proxy`.
3. Add in Claude Desktop MCP config file (`claude_desktop_config.json`):

On Windows:
```json
{
"mcpServers": {
"my-api-mcp-proxy": {
"command": "mcp-proxy",
"args": ["http://127.0.0.1:8000/mcp"]
"fastapi-mcp": {
"url": "http://localhost:8000/mcp"
}
}
}
```
On MacOS:

## Connecting to the MCP Server using [mcp-remote](https://www.npmjs.com/package/mcp-remote)

If you want to support authentication, or your MCP client does not support SSE, we recommend using `mcp-remote` as a bridge.

```json
{
"mcpServers": {
"my-api-mcp-proxy": {
"command": "/Full/Path/To/Your/Executable/mcp-proxy",
"args": ["http://127.0.0.1:8000/mcp"]
"fastapi-mcp": {
"command": "npx",
"args": [
"mcp-remote",
"http://localhost:8000/mcp",
"8080" // Optional port number. Necessary if you want your OAuth to work and you don't have dynamic client registration.
]
}
}
}
```
Find the path to mcp-proxy by running in Terminal: `which mcp-proxy`.

4. Claude Desktop will discover all available tools and resources automatically.
223 changes: 223 additions & 0 deletions docs/03_authentication_and_authorization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
# Authentication and Authorization

FastAPI-MCP supports authentication and authorization using your existing FastAPI dependencies.

It also supports the full OAuth 2 flow, compliant with [MCP Spec 2025-03-26](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization).

It's worth noting that most MCP clients currently do not support the latest MCP spec, so for our examples we might use a bridge client such as `npx mcp-remote`. We recommend you use it as well, and we'll show our examples using it.

## Basic Token Passthrough

If you just want to be able to pass a valid authorization header, without supporting a full authentication flow, you don't need to do anything special.

You just need to make sure your MCP client is sending it:

```json
{
"mcpServers": {
"remote-example": {
"command": "npx",
"args": [
"mcp-remote",
"http://localhost:8000/mcp",
"--header",
"Authorization:${AUTH_HEADER}"
]
},
"env": {
"AUTH_HEADER": "Bearer <your-token>"
}
}
}
```

This is enough to pass the authorization header to your FastAPI endpoints.

Optionally, if you want your MCP server to reject requests without an authorization header, you can add a dependency:

```python
from fastapi import Depends
from fastapi_mcp import FastApiMCP, AuthConfig

mcp = FastApiMCP(
app,
name="Protected MCP",
auth_config=AuthConfig(
dependencies=[Depends(verify_auth)],
),
)
mcp.mount()
```

## OAuth Flow

FastAPI-MCP supports the full OAuth 2 flow, compliant with [MCP Spec 2025-03-26](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization).

It would look something like this:

```python
from fastapi import Depends
from fastapi_mcp import FastApiMCP, AuthConfig

mcp = FastApiMCP(
app,
name="MCP With OAuth",
auth_config=AuthConfig(
issuer=f"https://auth.example.com/",
authorize_url=f"https://auth.example.com/authorize",
oauth_metadata_url=f"https://auth.example.com/.well-known/oauth-authorization-server",
audience="my-audience",
client_id="my-client-id",
client_secret="my-client-secret",
dependencies=[Depends(verify_auth)],
setup_proxies=True,
),
)

mcp.mount()
```

And you can call it like:

```json
{
"mcpServers": {
"fastapi-mcp": {
"command": "npx",
"args": [
"mcp-remote",
"http://localhost:8000/mcp",
"8080" // Optional port number. Necessary if you want your OAuth to work and you don't have dynamic client registration.
]
}
}
}
```

You can use it with any OAuth provider that supports the OAuth 2 spec. See explanation on [AuthConfig](#authconfig-explained) for more details.

## Custom OAuth Metadata

If you already have a properly configured OAuth server that works with MCP clients, or if you want full control over the metadata, you can provide your own OAuth metadata directly:

```python
from fastapi import Depends
from fastapi_mcp import FastApiMCP, AuthConfig

mcp = FastApiMCP(
app,
name="MCP With Custom OAuth",
auth_config=AuthConfig(
# Provide your own complete OAuth metadata
custom_oauth_metadata={
"issuer": "https://auth.example.com",
"authorization_endpoint": "https://auth.example.com/authorize",
"token_endpoint": "https://auth.example.com/token",
"registration_endpoint": "https://auth.example.com/register",
"scopes_supported": ["openid", "profile", "email"],
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code"],
"token_endpoint_auth_methods_supported": ["none"],
"code_challenge_methods_supported": ["S256"]
},

# Your auth checking dependency
dependencies=[Depends(verify_auth)],
),
)

mcp.mount()
```

This approach gives you complete control over the OAuth metadata and is useful when:
- You have a fully MCP-compliant OAuth server already configured
- You need to customize the OAuth flow beyond what the proxy approach offers
- You're using a custom or specialized OAuth implementation

For this to work, you have to make sure mcp-remote is running [on a fixed port](#add-a-fixed-port-to-mcp-remote), for example `8080`, and then configure the callback URL to `http://127.0.0.1:8080/oauth/callback` in your OAuth provider.

## Working Example with Auth0

For a complete working example of OAuth integration with Auth0, check out the [auth_example_auth0.py](/examples/08_auth_example_auth0.py) in the examples folder. This example demonstrates the simple case of using Auth0 as an OAuth provider, with a working example of the OAuth flow.

For it to work, you need an .env file in the root of the project with the following variables:

```
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_AUDIENCE=https://your-tenant.auth0.com/api/v2/
AUTH0_CLIENT_ID=your-client-id
AUTH0_CLIENT_SECRET=your-client-secret
```

You also need to make sure to configure callback URLs properly in your Auth0 dashboard.

## AuthConfig Explained

### `setup_proxies=True`

Most OAuth providers need some adaptation to work with MCP clients. This is where `setup_proxies=True` comes in - it creates proxy endpoints that make your OAuth provider compatible with MCP clients:

```python
mcp = FastApiMCP(
app,
auth_config=AuthConfig(
# Your OAuth provider information
issuer="https://auth.example.com",
authorize_url="https://auth.example.com/authorize",
oauth_metadata_url="https://auth.example.com/.well-known/oauth-authorization-server",

# Credentials registered with your OAuth provider
client_id="your-client-id",
client_secret="your-client-secret",

# Recommended, since some clients don't specify them
audience="your-api-audience",
default_scope="openid profile email",

# Your auth checking dependency
dependencies=[Depends(verify_auth)],

# Create compatibility proxies - usually needed!
setup_proxies=True,
),
)
```

You also need to make sure to configure callback URLs properly in your OAuth provider. With mcp-remote for example, you have to [use a fixed port](#add-a-fixed-port-to-mcp-remote).

### Why Use Proxies?

Proxies solve several problems:

1. **Missing registration endpoints**:
The MCP spec expects OAuth providers to support [dynamic client registration (RFC 7591)](https://datatracker.ietf.org/doc/html/rfc7591), but many don't.
Furthermore, dynamic client registration is probably overkill for most use cases.
The `setup_fake_dynamic_registration` option (True by default) creates a compatible endpoint that just returns a static client ID and secret.

2. **Scope handling**:
Some MCP clients don't properly request scopes, so our proxy adds the necessary scopes for you.

3. **Audience requirements**:
Some OAuth providers require an audience parameter that MCP clients don't always provide. The proxy adds this automatically.

### Add a fixed port to mcp-remote

```json
{
"mcpServers": {
"example": {
"command": "npx",
"args": [
"mcp-remote",
"http://localhost:8000/mcp",
"8080"
]
}
}
}
```

Normally, mcp-remote will start on a random port, making it impossible to configure the OAuth provider's callback URL properly.


You have to make sure mcp-remote is running on a fixed port, for example `8080`, and then configure the callback URL to `http://127.0.0.1:8080/oauth/callback` in your OAuth provider.
Loading