Repository navigation
docs: add auth0-ai-fastmcp guides - #176
kishore7snehil wants to merge 3 commits into
Conversation
| ```python | ||
| @mcp.resource("orders://recent") | ||
| async def recent_orders() -> str: | ||
| require_scopes(get_caller_principal(), "resource:recent_orders") |
There was a problem hiding this comment.
The server above advertises only the two tool:* scopes in tool_scopes. A client that follows the MCP spec asks for the advertised scopes, so it never gets resource:recent_orders or prompt:order_summary, and both handlers deny everyone. In my run the resource gave Missing one or more required scopes.
Can we add both scopes to tool_scopes in the sample above, and say in ToolScopes.md that tool_scopes should list every scope clients need, for tools, resources and prompts?
|
|
||
| There are two distinct authorization layers at play in an `auth0-ai-fastmcp` server. | ||
|
|
||
| The first layer is the MCP server's own tool authorization. The `tool:*` scopes you list in `tool_scopes` are included in the RFC 9728 protected-resource metadata under `scopes_supported`, and so is every scope in `required_scopes`. When an MCP client connects, it reads this metadata and requests those scopes during the login flow. The `with_required_scopes` decorator enforces them on a tool. If the caller's token does not carry a required scope, the tool call is rejected before it runs. |
There was a problem hiding this comment.
This says the client requests the tool_scopes and the required_scopes from the metadata. With required_scopes set, the 401 challenge carries only required_scopes in scope, and a client that follows the MCP spec uses that first. So the tool_scopes are not requested at login. The Configuration.md lines 29, 36 and 98 repeat it or point here.
Can we fix this together with my comment on config.py in #174? Either the provider returns both lists in the challenge, or these lines say what really happens.
|
|
||
| `resource_url` is the base URL of your server with no MCP path on the end. FastMCP adds the path the MCP endpoint is mounted at (`/mcp` by default) and advertises the result as the resource in the protected-resource metadata. | ||
|
|
||
| The MCP client reads that advertised resource and asks Auth0 for a token for it, so the audience that incoming tokens are validated against has to be the same value. It defaults to `resource_url` plus `mcp_path`, which means `resource_url="https://your-api.example.com"` gives the audience `https://your-api.example.com/mcp`. That value is also the API identifier to register in Auth0. |
There was a problem hiding this comment.
Registering the API is not enough. The tenant also needs the compatibility resource parameter profile, the scopes added to the API as permissions, and a client grant for the MCP client. Without the profile, Auth0 ignores resource, so login fails or the token has no API audience. This is only in the example README and in FastMCP's provider docs.
Can we add one sentence here, and in the README Getting Started, linking to 'What you need on the tenant' in the example README?
|
|
||
| `Auth0ContextMiddleware` runs on every MCP request after the token is verified, including tool calls, resource reads, prompt requests and the list calls. It builds the caller's `Principal` from the verified token and makes it available to `get_caller_principal()` and `require_caller_mcp_client()` for the length of the request. The context is cleared when the request ends, even if the handler raises. | ||
|
|
||
| A request with no verified token raises `UnauthenticatedError`. With the provider mounted this does not happen, because FastMCP turns the request away first, so seeing it means the server was created without `auth=auth0.provider`. It is logged with a traceback so the missing provider is easy to spot. The `initialize` and `ping` requests are the exception, since they carry no caller. A token without a usable `sub`, `exp` or client id never counts as verified, so it gets a `401` before the middleware runs. |
There was a problem hiding this comment.
This is not true for a client that runs in the same process. With Client(mcp) in a test there is no bearer token, server/discover fails with this error, and the client cannot connect. So seeing it does not always mean auth= is missing. references/pitfalls.md line 9 and CLAUDE.md line 81 say the same.
Can we word it as 'no verified token' (no auth=, an in memory client or stdio), and add a line on testing over HTTP with a token?
| mcp.add_middleware(auth0.middleware) | ||
| ``` | ||
|
|
||
| Settings are checked when `Auth0FastMCP(options)` runs, so a bad combination fails at server startup rather than on the first request. |
There was a problem hiding this comment.
This is not true for client_id and client_secret. With both missing, or only client_id set, Auth0FastMCP starts without error and the first exchange fails. The client sees Client credentials are required to use get_token_by_exchange_profile, a core name the user never touched, or only Error calling tool with mask_error_details=True.
Can we limit this sentence to the checks listed below, and add a line under client_id that missing credentials show up only at the first exchange?
| > With `non_strict`, a tool can get a token with more access than it asked for. A tool that asks for `read:inventory` can be handed a saved token that also carries `write:inventory`. Only choose it when that is acceptable for every tool on the server. | ||
|
|
||
| ```python | ||
| store = YourIndexedTokenStore(secret="YOUR_STORE_SECRET") # your IndexedTokenStore subclass |
There was a problem hiding this comment.
The store base class needs a secret of at least 32 characters, and this placeholder has 17. Someone who pastes it gets a ConfigurationError.
Can we use a placeholder such as secret=os.environ["TOKEN_STORE_SECRET"], or a 32 character dummy value?
|
|
||
| - `StubMcpClient` in `tests/test_verifier.py` stands in for `McpServerClient`. It returns a `VerifiedToken` with the claims a test chooses, or raises the error a test chooses. | ||
| - `tests/test_middleware.py` patches `get_access_token` in the middleware module to control whether a request has a verified token. Its `Recorder` is a `call_next` that records the caller visible while the request runs, so a test can assert on both the bound context and its reset afterwards. | ||
| - `tests/test_config.py` patches `create_mcp_client` and `Auth0MCPProvider` to stay offline. Where a test needs the real provider, it patches `OIDCConfiguration.get_oidc_configuration` instead. |
There was a problem hiding this comment.
tests/test_config.py patches config._Auth0Provider, and no test patches Auth0MCPProvider. I tried the sentence as written, and it leaves one live call to the OIDC discovery URL, so the test needs the network.
Can we change the name here? Also flow-map.md line 14 says the verifier writes a debug log, but it is WARNING, and code-style.md line 12 says no error classes while scopes.py has two private ones.
| if isinstance(node, ast.ImportFrom) and node.module and node.module.split(".")[0] in CHECKED_PACKAGES: | ||
| module = importlib.import_module(node.module) | ||
| missing = [alias.name for alias in node.names if not hasattr(module, alias.name)] | ||
| assert not missing, f"{node.module} has no {missing}" |
There was a problem hiding this comment.
The test checks only the names in from x import y lines. A wrong keyword in Auth0FastMCPOptions(...), a missing method or import x.nope still passes, and if every fence gets another tag, the test is skipped and CI stays green. Also the SIGNATURE branch looks dead, as no snippet matches it.
Can we also check the keywords of Auth0FastMCPOptions(...) calls against the dataclass fields? I tried a seven line check. It passes on this head and fails when I rename a keyword.
There was a problem hiding this comment.
The test now checks Auth0FastMCPOptions(...) keywords against the real dataclass fields, resolves import x.y forms, and fails if the docs have no Python snippets. It does not check method names such as auth0.something(...), because that needs type information and a wrong name would show up as soon as the examples run. I also removed the one-line signature skip, since it did not apply to any current snippet. Fixed in 03cd8c1.
|
|
||
| - [Configuration](./docs/Configuration.md) - all `Auth0FastMCPOptions` fields. | ||
| - [Tool Scopes](./docs/ToolScopes.md) - how scopes are advertised at login and how tools, resources and prompts are gated. | ||
| - [Examples](./EXAMPLES.md) - a full runnable server. |
There was a problem hiding this comment.
These relative links work on GitHub, but PyPI shows this README as the package page, and there they lead nowhere. The auth0-ai-mcp README uses full https://github.com/... links for its docs, and the 'Core guides' list below does too.
Also EXAMPLES.md has ... placeholders, so 'a full runnable server' is not right. The runnable one is examples/b2b_ecommerce.
|
|
||
| ### Core guides | ||
|
|
||
| These guides cover the behavior this package builds on. |
There was a problem hiding this comment.
These guides import with_required_scopes and require_scopes from auth0_ai_mcp. In a FastMCP server that import still denies, but with mask_error_details=True the client sees only Error calling tool 'x', and the server logs an ERROR traceback.
Can we add one line here: 'In FastMCP code, import the scope helpers from auth0_ai_fastmcp'? ToolScopes.md says it, but most readers copy the snippet first.
ed71bf7 to
14a7f6b
Compare
790e09d to
e83c6ef
Compare
14a7f6b to
bdafc92
Compare
e83c6ef to
e17aa27
Compare
bdafc92 to
78d92a7
Compare
e17aa27 to
359cc8a
Compare
📋 Changes
Adds the guides for
auth0-ai-fastmcp, links them from the README, and adds a test that checks the code samples in them. This PR is stacked on #175.✨ Features
docs/Configuration.mdcovers everyAuth0FastMCPOptionsfield, how requests are verified, and the errors callers see.docs/ToolScopes.mdcovers how scopes are advertised at login and how tools, resources and prompts are gated.EXAMPLES.mdshows a full server, gated tools, resources and prompts.README.mdgets a Documentation section that links the guides and theauth0-ai-mcpguides it builds on.CLAUDE.md,AGENTS.mdandreferences/give contributors and coding agents the package layout, commands, code style, and which docs to update for which change.📖 Documentation
Links from the example
README.mdtodocs/Configuration.mdnow resolve.🧪 Testing
tests/test_docs.pyparses every Python snippet in the README,EXAMPLES.mdanddocs/, and checks that the names imported fromauth0_ai_fastmcp,auth0_ai_mcpandfastmcpexist. Tests andruff checkpass on Python 3.13.Contributor Checklist