feat: give every MCP client its own token, rate limit and log name
This commit is contained in:
1 parent
3af41c06ab
commit
bfdedefae3
8 files changed
+134
-33
No files matched your search
+12
-7
@@ -15,19 +15,24 @@ TYPO3 sends no dates or prices, and nothing flows back to TYPO3.
|
||||
`POST /mcp` (Streamable HTTP; `GET`/`DELETE` are part of the transport), served by `symfony/mcp-bundle`
|
||||
from `config/packages/mcp.yaml`. Tools only, no resources or prompts.
|
||||
|
||||
Authentication is a static bearer token, `Authorization: Bearer <APP_MCP_TOKEN>`, checked by
|
||||
`App\Security\McpAccessTokenHandler` on the `mcp` firewall. If `APP_MCP_TOKEN` is empty, every request is
|
||||
refused. It is not the OAuth2 client-credentials flow of `/api`: n8n's MCP Client Tool node sends a fixed
|
||||
Authentication is a static bearer token per client, `Authorization: Bearer <token>`, checked by
|
||||
`App\Security\McpAccessTokenHandler` on the `mcp` firewall. `APP_MCP_TOKENS` lists the clients as
|
||||
`name:token` pairs separated by commas, e.g. `APP_MCP_TOKENS="n8n:3f9c…,claude-desktop:a71e…"`. Give every
|
||||
system its own token, so one can be revoked by removing its entry without touching the others. Use tokens
|
||||
without commas, e.g. `openssl rand -hex 32`. A malformed list (no name, a duplicate name, a token shared by
|
||||
two clients) fails loudly with a 500 on `/mcp` rather than locking a client out silently. If the list is
|
||||
empty, every request is refused. It is not the OAuth2 client-credentials flow of `/api`: n8n's MCP Client Tool node sends a fixed
|
||||
header and cannot renew a token every 10 minutes.
|
||||
|
||||
Tool calls are rate-limited per token (`chatbot_mcp` limiter, 60 per minute). The limit is there to stop
|
||||
The client name becomes the authenticated user. Tool calls are rate-limited per client (`chatbot_mcp`
|
||||
limiter, 60 per minute), so testing from Claude Desktop does not eat into n8n's budget. The limit is there to stop
|
||||
runaway agent loops. Each call is logged on the `chatbot` channel (`var/log/<env>.chatbot.log`, file
|
||||
only) with the tool, arguments, result count and duration. Nothing identifies the person chatting.
|
||||
only) with the client, tool, arguments, result count and duration. Nothing identifies the person chatting.
|
||||
|
||||
### n8n
|
||||
|
||||
MCP Client Tool node: endpoint `https://<host>/mcp`, transport *HTTP Streamable*, authentication *Bearer*
|
||||
with the stage's `APP_MCP_TOKEN`. The agent prompt should tell the bot to cite each answer's `url`, and
|
||||
with n8n's own token from the stage's `APP_MCP_TOKENS`. The agent prompt should tell the bot to cite each answer's `url`, and
|
||||
that prices and availability are a snapshot (`dataAsOf`) and only the booking page is binding.
|
||||
|
||||
## Tools
|
||||
@@ -109,4 +114,4 @@ After a complete run that changed something, the codes the trip join cannot plac
|
||||
|---|---|
|
||||
| `APP_CMS_API_BASE_URL` | TYPO3 base URL per stage (also used by `CmsDataProvider`) |
|
||||
| `APP_CMS_API_KEY` | TYPO3 `X-Api-Key`: the content feed and its `contentUrl` pages, and `typo3.client` (`CmsDataProvider`) |
|
||||
| `APP_MCP_TOKEN` | Bearer token n8n sends to `/mcp` |
|
||||
| `APP_MCP_TOKENS` | Bearer tokens for `/mcp`, one per client: `name:token,name:token` |
|
||||
Reference in new issue
Block a user