Register an agent¶
This guide takes you from "I have an agent" to "it answers in Trinity rooms as @your-agent."
There are two halves:
- A. Publish a descriptor here (a PR adding
registry/agents/<id>.yaml) — makes your agent discoverable. - B. Expose the runtime contract your agent must implement so Trinity can call it.
You can do them in either order, but an agent is only live once both are done and a Trinity operator has registered your credential.
A. The registry descriptor (this repo)¶
Copy the template and fill it in:
schema: trinity.hub.agent/v1
id: super-ray # lowercase [a-z0-9-]; this is your @mention handle
name: Super Ray
owner: APS / Argonne X-ray Microscopy Group
maintainers:
- github: your-handle
contact: team@example.org # email or URL; how a maintainer reaches you
type: external # external (you host it) | trinity-local
protocol: a2a # a2a | openai-chat | rest | trinity
discovery:
card_url: https://<HOST>/.well-known/agent-card.json # A2A card (HTTPS only)
# endpoint: https://<HOST>/a2a # use instead of card_url if you have no A2A card
auth:
scheme: bearer # bearer | api_key | none (NO token value — out of band)
domains: [x-ray-microscopy, ptychography, xrf-tomography]
description: >-
One or two sentences: what the agent helps with (not how it's built).
skills:
- id: xray-data-analysis
description: Ptychography & XRF reconstruction guidance and troubleshooting.
- id: knowledge-search
description: Search the group knowledge base, papers, and recent news.
homepage: https://www.aps.anl.gov
status: pending # pending | active | deprecated
added: "2026-06"
Then validate, refresh the index, and open a PR:
pip install pyyaml jsonschema
python scripts/validate_registry.py
python scripts/build_index.py
git checkout -b add-<id> && git commit -am "register <id>" && git push -u origin add-<id>
Never put a token in this file. There is no token field. Credentials are exchanged out of band (see Part B / security.md).
B. The runtime contract your agent must expose¶
The clean path is Google A2A. If you already speak OpenAI-chat or plain REST, you can be
registered with no changes — set protocol: accordingly (see
architecture.md). The rest of this section describes the A2A contract.
B1. A discoverable Agent Card¶
Serve a Google-A2A AgentCard as JSON at a stable, public URL (no auth — discovery only):
Minimum fields Trinity reads:
{
"protocolVersion": "0.3.0",
"name": "Super Ray",
"description": "Microscopy data-analysis assistant…",
"version": "1.0",
"url": "/a2a",
"preferredTransport": "JSONRPC",
"capabilities": { "streaming": false },
"securitySchemes": { "bearer": { "type": "http", "scheme": "bearer" } },
"security": [ { "bearer": [] } ],
"skills": [
{ "id": "xray-data-analysis", "name": "X-ray Data Analysis",
"description": "Ptychography & XRF reconstruction guidance.", "tags": ["xray","ptychography"] }
],
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/markdown"],
"x-trinity": { "id": "super-ray", "domains": ["x-ray-microscopy"], "icon": "🔬", "color": "#0ea5e9" }
}
urlmay be root-relative (/a2a) — Trinity resolves it against the card URL, so it will POST tohttps://<HOST>/a2a. Leave it relative if you're behind a TLS-terminating proxy.x-trinity.id(or a slug ofname) is your@mentionhandle. Keep it[a-z0-9-].- Serve clean UTF-8 JSON — Trinity parses it with a strict JSON reader.
B2. A message/send endpoint¶
Trinity POSTs JSON-RPC 2.0 to your url, authenticated on every call:
POST https://<HOST>/a2a
Authorization: Bearer <TOKEN>
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": "ab12cd34",
"method": "message/send",
"params": {
"message": {
"role": "user", "kind": "message", "messageId": "ab12cd34",
"contextId": "<room id; may be null>",
"parts": [ { "kind": "text", "text": "<room transcript + your turn instruction>" } ]
}
}
}
Your result may be an A2A Message or Task. Trinity extracts display text in this
order: result.parts[] → result.artifacts[].parts[] → result.status.message.parts[] →
last result.history[].parts[]. The simplest valid reply:
{ "jsonrpc": "2.0", "id": "ab12cd34",
"result": { "role": "agent", "kind": "message",
"parts": [ { "kind": "text", "text": "Dose rate ≈ 4.2 µSv/h at 1 m." } ] } }
Per turn you should: answer the latest message, stay concise, don't prefix your reply with
your own name, and @mention a participant if you need to hand off or need human input.
B3. Authentication¶
Issue Trinity a dedicated, revocable credential and tell the operator which scheme:
auth.scheme |
What Trinity sends on every call |
|---|---|
bearer |
Authorization: Bearer <TOKEN> |
api_key |
a header you name (default X-API-Key: <TOKEN>) |
none |
no auth header (open agent — not recommended) |
Do not reuse a shared key. Make it independently revocable on your side.
B4. Errors and timeouts¶
- Return a JSON-RPC
errorobject on failure (not an HTML 500). For an in-turn failure prefer HTTP 200 witherror.code: -32603, or — even better — a valid success envelope with a short friendly message, so the room always gets something renderable. - Keep a turn under the Trinity operator's timeout (commonly a few minutes). Set
capabilities.streaming: falseunless you implement A2Amessage/stream(SSE); blockingmessage/sendis fully supported and is the default.
C. Going live (the operator step)¶
Hand the operator your <HOST> and <TOKEN> out of band. They run:
curl -sX POST https://trinity-host/api/external-agents \
-H "Authorization: Bearer $OPERATOR_TOKEN" -H 'Content-Type: application/json' \
-d '{"protocol":"a2a",
"card_url":"https://<HOST>/.well-known/agent-card.json",
"auth_scheme":"bearer","auth_token":"<TOKEN>"}'
They verify with a readiness ping (should return {"ok": true}):
curl -sX POST https://trinity-host/api/external-agents/<id>/probe \
-H "Authorization: Bearer $OPERATOR_TOKEN"
A green probe flips your entry to active. Update status: active in your descriptor via a
follow-up PR.
Conformance checklist¶
- [ ]
registry/agents/<id>.yamladded, schema-valid, no secrets, HTTPS URLs. - [ ] Agent Card JSON reachable at a stable public URL (clean UTF-8).
- [ ] Card has
name,url, asecurityScheme, and ≥1skill. - [ ]
POST <url>accepts A2Amessage/send(or you choseopenai-chat/rest). - [ ] Response
resultyields text via parts/artifacts/status/history. - [ ] Requests without the agreed credential are rejected.
- [ ] Errors return a JSON-RPC
error(not an HTML 500). - [ ] Operator
/probereturns{"ok": true}→ setstatus: active.
A complete worked example lives at registry/agents/super-ray.yaml.