Repository navigation
docs: add auth0-ai-mcp guides - #173
kishore7snehil wants to merge 5 commits into
Conversation
|
|
||
| ### Passing a prebuilt client | ||
|
|
||
| `ApiClient` comes from [`auth0-api-python`](https://github.com/auth0/auth0-api-python), the library `auth0-ai-mcp` is built on. Build one yourself when you need an option `create_mcp_client` does not expose, such as multi-domain mode or DPoP settings. |
There was a problem hiding this comment.
I tried ApiClientOptions(dpop_required=True) passed through api_client. The middleware still accepts a plain Bearer token, and also a token that has cnf.jkt sent as Bearer, and that token can be exchanged. The middleware calls verify_access_token, so the DPoP options have no effect.
Can we remove 'or DPoP settings' here, and add a warning in TokenVerification.md that DPoP is not enforced? I have raised the code side in the first PR.
There was a problem hiding this comment.
A token with a cnf claim is now rejected with a 401, even when it is sent as Bearer. TokenVerification.md says so and warns that DPoP is not supported, and the DPoP wording is gone from Configuration.md.
Fixed in dff43fb.
| ```python | ||
| from auth0_ai_mcp import with_required_scopes | ||
|
|
||
| @with_required_scopes(["read:orders", "read:payments"]) |
There was a problem hiding this comment.
This snippet shows only the scope decorator. I tried both orders with @mcp.tool(). With @mcp.tool() on top, a token without the scope gets a tool error. With it below, the registered function is the unguarded one, so the body runs and the check is silently skipped.
Can we show @mcp.tool() as the first line here, in EXAMPLES.md and in the README, with one sentence about the order?
| - `InsufficientScopeError` - caller is authenticated but is missing one or more required scopes. | ||
| - `ConfigurationError` - the `scopes` argument is empty or malformed. | ||
|
|
||
| These errors are raised inside the tool, so the MCP framework reports them as a tool error and the HTTP response is still successful. Each scope denial is logged at INFO. |
There was a problem hiding this comment.
Can we add what the caller and the logs see? With MCP SDK 2.x the client gets only Error executing tool X at HTTP 200, so the model cannot tell it is a scope problem, and there is no insufficient_scope response to step up on. Each denial also writes an ERROR traceback in the server log.
One or two sentences here will save debugging time. I have asked about raising ToolError in the first PR.
There was a problem hiding this comment.
Scope errors now extend the SDK ToolError, so the client sees Error executing tool X: Missing one or more required scopes. and the model can tell it is a scope problem. The server log gets an INFO line. There is still no insufficient_scope response to step up on, and the page now says that. I only checked this on mcp 2.3.
Fixed in dff43fb.
|
|
||
| ## Tenant-side setup: scopes must be in the client-grant | ||
|
|
||
| `with_required_scopes` checks `principal.scopes`, which comes from the incoming token. Scope checks read only the `scope` claim and never the RBAC `permissions` claim. Auth0 only includes a scope in a token if two things are true: the scope is registered on the API (resource server), and the MCP client's client-grant explicitly lists it. |
There was a problem hiding this comment.
As far as I can see there is a third condition. For a third party client the grant is only a ceiling, so the scope must also be requested at login. If the authorize request has no scope, the token has no API scopes and nothing is reported as an error.
The README quick start passes no scopes_supported, so a server built from it will see exactly this. Can we list 'requested' as the third condition here?
|
|
||
| ## Decorator form | ||
|
|
||
| `with_required_scopes(scopes)` is a decorator for async tool handlers. It reads the verified caller from the request context and checks the scopes before the tool body runs. `scopes` is one scope as a string or several as a list. |
There was a problem hiding this comment.
The matching here is exact: a token with tool:admin does not satisfy tool:read. The 2026 revision of the MCP authorization spec says servers must account for scope hierarchies, where a broader scope implies narrower ones.
It fails closed, so I do not think any code is needed. Please say here that matching is exact, and that a hierarchy needs both scopes in the token or a check inside the tool.
| ``` | ||
|
|
||
| > [!WARNING] | ||
| > The MCP SDK turns on its Host and Origin checks only when the app is bound to localhost, so keep `127.0.0.1` for local use. Bind another interface only behind a trusted proxy or after configuring the allowed hosts. |
There was a problem hiding this comment.
The rule is a bit different. The MCP SDK turns these checks on when streamable_http_app(host=...) is 127.0.0.1, localhost or ::1, and that is also the default. I tried the default with Host: your-mcp-server and got 421 'Invalid Host header'. With host="0.0.0.0" the checks are off.
Can we state this rule and show transport_security=TransportSecuritySettings(allowed_hosts=[...]) for a public URL? EXAMPLES.md uses the default call, so a pointer there also helps.
| ) | ||
| ``` | ||
|
|
||
| See [Writing an Adapter](https://github.com/auth0/auth0-ai-python/blob/main/packages/auth0-ai-mcp/docs/WritingAnAdapter.md) for other frameworks and for the native FastMCP adapter package `auth0-ai-fastmcp`. For a complete runnable server, see [`examples/b2b_ecommerce`](https://github.com/auth0/auth0-ai-python/tree/main/packages/auth0-ai-mcp/examples/b2b_ecommerce). |
There was a problem hiding this comment.
I could not find auth0-ai-fastmcp on PyPI or in this repo, and I did not see a PR for it. This line and WritingAnAdapter.md line 25 tell FastMCP users to use it, so they have nothing to install today.
Can we remove the sentence until the package is released, or say clearly that it is planned?
There was a problem hiding this comment.
We would like to keep this mention. auth0-ai-fastmcp is the FastMCP adapter for this package and it is planned to join this repo, so these two lines will not need to change when it does.
Until then it cannot be installed, so we can say clearly that it is planned if you prefer. WDYT?
| ) | ||
| ``` | ||
|
|
||
| - `get_caller_principal() -> Principal | None` - returns the verified `Principal` for the current request, or `None` when called outside a request (for example in a background task or test that has not set up a context). |
There was a problem hiding this comment.
I checked this. A task started inside a request with asyncio.create_task still saw the principal 0.2 seconds after the response, and the same for asyncio.to_thread. Only a plain thread or startup code sees None.
Can we use that as the example? A line that such a task keeps the caller's token would also help, as the exchange fails once the token expires.
| @@ -0,0 +1,40 @@ | |||
| # Commands | |||
|
|
|||
| Run these from the `auth0-ai-mcp` directory. They match `.github/workflows/test.yml` (Poetry 2.2.1, Python 3.10 to 3.12) and `CONTRIBUTING.md`. | |||
There was a problem hiding this comment.
This says Poetry 2.2.1, but test.yml installs 2.4.1. A number copied here will go stale again.
Can we drop the version, or point to the workflow file?
|
|
||
| ## Conventions | ||
|
|
||
| - `test_client.py` and `test_principal.py` use module-level `test_<subject>_<condition>` functions. `test_scopes.py` groups tests in `TestRequireScopes` and `TestWithRequiredScopes`. Follow the style of the file you are adding to. |
There was a problem hiding this comment.
test_scopes.py has no TestRequireScopes or TestWithRequiredScopes class. All the tests there are module level functions.
Line 31 also says tests use bind_request_context, but only test_request_context.py does. test_scopes.py uses set_request_context and reset_request_context.
2ce0bb3 to
564707a
Compare
205e032 to
dff43fb
Compare
564707a to
c7085b6
Compare
5538f83 to
49aa270
Compare
c7085b6 to
b9c4335
Compare
49aa270 to
ce2c3b2
Compare
ce2c3b2 to
109c402
Compare
b9c4335 to
a51a447
Compare
109c402 to
73155b4
Compare
- Document the whitespace bearer rejection and the no-store header on 401 and 503. - Document that scopes_supported must be a list of single-value strings. - Document the separate expiry messages for exchanged tokens. - Add tests/test_docs.py, which checks that the Python snippets in the docs parse and their imports resolve.
73155b4 to
de6ffca
Compare
📋 Changes
Adds the guides for
auth0-ai-mcp. The package README now links to them, and the links to these guides in the package and example READMEs from the earlier PRs resolve once this lands.📖 Documentation
docs/has one guide each for configuration, token verification, request context, scope enforcement, On-Behalf-Of, Token Vault, token caching, organization policy and writing an adapter.TokenVerification.mdcovers the whitespace bearer check andno-storeon401and503.EXAMPLES.mdpoints to the runnableb2b_ecommerceserver.README.mdgets a Documentation section and links from each feature to its guide.CLAUDE.md,AGENTS.mdandreferences/give coding agents the commands, code style, testing and git workflow for the package.🧪 Testing
Documentation, plus
tests/test_docs.py, which checks that the Python snippets in the README, EXAMPLES and guides parse and that their imports resolve. All relative links resolve.Contributor Checklist