Skip to content

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:

cp registry/agents/_TEMPLATE.yaml registry/agents/<id>.yaml
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):

https://<HOST>/.well-known/agent-card.json

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" }
}
  • url may be root-relative (/a2a) — Trinity resolves it against the card URL, so it will POST to https://<HOST>/a2a. Leave it relative if you're behind a TLS-terminating proxy.
  • x-trinity.id (or a slug of name) is your @mention handle. 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 error object on failure (not an HTML 500). For an in-turn failure prefer HTTP 200 with error.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: false unless you implement A2A message/stream (SSE); blocking message/send is 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>.yaml added, schema-valid, no secrets, HTTPS URLs.
  • [ ] Agent Card JSON reachable at a stable public URL (clean UTF-8).
  • [ ] Card has name, url, a securityScheme, and ≥1 skill.
  • [ ] POST <url> accepts A2A message/send (or you chose openai-chat/rest).
  • [ ] Response result yields text via parts/artifacts/status/history.
  • [ ] Requests without the agreed credential are rejected.
  • [ ] Errors return a JSON-RPC error (not an HTML 500).
  • [ ] Operator /probe returns {"ok": true} → set status: active.

A complete worked example lives at registry/agents/super-ray.yaml.