feat: an A2A facade for the n8n agents, and Approvisionnement's first workflow
The adapter is written once and parameterised, so both n8n agents get the same A2A surface and differ only in the workflow behind the webhook. It owns the protocol and nothing else: no Keycloak client, no credentials, and the end user's token forwarded in a header so a workflow that logs its input does not log the token with it. escalate: true fails the task rather than completing it, because an autonomous agent meeting a rule that needs approval must hand it back, not decide. Serving this from the gateway was the repo's stated preference and does not reach: A2A returns a Task with a lifecycle, and a proxy cannot synthesise that from a webhook's single reply. The check_availability workflow does the token exchange itself, using the agent's client secret from the pod environment so the committed JSON holds no secret, then talks MCP in two calls — initialize to obtain the session id from a response header, then tools/call. It reads the product code with a regex rather than a model: this agent's critical path is rules, and a question naming no product escalates instead of being guessed at. MCP rather than the REST route, which would have been one HTTP call instead of two: the per-tool ACL by consumer group lives on the MCP route only. Going through REST would give an autonomous agent broader access than a conversational one, which is exactly backwards. Status: the adapter is tested (13 tests) and the identity chain is verified by hand, but the workflow fails at its token exchange and is not yet working — see the note in workflows/. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
# a2a-n8n-adapter
|
||||
|
||||
An A2A facade in front of an n8n workflow. One image, one instance per agent,
|
||||
configured entirely by environment.
|
||||
|
||||
The repo's notes proposed serving this from the gateway instead — a static Agent
|
||||
Card and a route to the webhook, no new component. That does not reach. A2A is
|
||||
not request/response: a caller gets a `Task` with an id, a context and a state
|
||||
that can be `input-required` or `failed`, and none of that can be synthesised
|
||||
from a webhook's single reply by a proxy. Templating the whole lifecycle in
|
||||
gateway configuration is an adapter, written in the wrong language.
|
||||
|
||||
So: the alternative those same notes anticipated, written once for both agents.
|
||||
|
||||
## It owns the protocol and nothing else
|
||||
|
||||
It holds no Keycloak client and no credentials. It forwards the end user's token
|
||||
to the workflow in `X-Subject-Token` — a header rather than the body, so a
|
||||
workflow that logs its input does not log the token with it — and the workflow
|
||||
exchanges it, because the component that can mint tokens should be the one that
|
||||
uses them.
|
||||
|
||||
The workflow answers `{"answer": "...", "escalate": false}`. Anything else is
|
||||
returned as raw text rather than guessed at: a workflow that has drifted from
|
||||
the contract should look wrong, not be quietly reinterpreted.
|
||||
|
||||
`escalate: true` fails the task rather than completing it. An autonomous agent
|
||||
that meets a rule needing approval must hand it back, never decide, and a
|
||||
completed task is the wrong shape for "a human has to look at this".
|
||||
|
||||
## Configuration
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `ADAPTER_AGENT_NAME` | `approvisionnement` |
|
||||
| `ADAPTER_AGENT_DESCRIPTION` | what the card says it is |
|
||||
| `ADAPTER_SKILL_ID` | `check_availability` |
|
||||
| `ADAPTER_SKILL_NAME`, `_DESCRIPTION`, `_EXAMPLES` | the card's one skill; examples are newline-separated |
|
||||
| `ADAPTER_WEBHOOK_URL` | the workflow's production webhook |
|
||||
| `ADAPTER_PUBLIC_URL` | what callers dial — the Service DNS name, not the bind address |
|
||||
| `ADAPTER_CA_BUNDLE` | the lab CA, so TLS is verified rather than disabled |
|
||||
|
||||
One skill per instance, deliberately. A second one is a card change, which is
|
||||
the moment to ask whether the agent has quietly become a data proxy.
|
||||
Reference in New Issue
Block a user