tinyagent
tinyagent is the native Tinyrelay connector helper for Hermes Agent. It is the connector name used by Hermes plugin discovery. The Hermes platform registered by the plugin is named tiny.
Build the helper from the repository root:
mkdir -p bin
go build -o bin/tinyagent ./tinyagent
Install the plugin in a standard Hermes home and put the helper on PATH:
export HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}"
mkdir -p "$HERMES_HOME/plugins"
cp -R tinyagent/hermes "$HERMES_HOME/plugins/tinyagent"
export PATH="$PWD/bin:$PATH"
Configure the connector with the relay URL, the private key used by this Hermes instance, the room to monitor, and the public keys allowed to control it:
export TINY_RELAY_URL=https://relay.example.test
export TINY_PRIVATE_KEY=YOUR_HEX_PRIVATE_KEY
export TINY_CHANNELS=room-id
export TINY_HOME_CHANNEL=room-id
export TINY_ALLOWED_USERS=OPERATOR_PUBLIC_KEY
export TINY_CLI_PATH="$PWD/bin/tinyagent"
hermes plugins enable tinyagent
hermes gateway run
By default, Hermes responds when an allowed user mentions the agent or replies to one of its messages. Set TINY_REQUIRE_MENTION=false to respond to all messages from allowed users in the selected rooms. This controls when Hermes responds; notifications still require an explicit mention by the agent.
TINY_PRIVATE_KEY is read from the environment by the helper and is never a command-line argument. The helper receives only PATH, HOME, TMPDIR, the key variable and the proxy variables from the gateway's environment. Give that key membership and an agent grant for the selected rooms on Tiny, including attachment access. Use a relay with the native interaction extension for interactive cards. Hermes loads the adapter through its normal plugin system. Attachments use authenticated Tinyrelay endpoints, with size and hash checks.
One gateway runs one Tiny identity at a time. On connect, the adapter takes an exclusive lock for the relay and key and releases it on disconnect. The lock lives under state/ in the Hermes root shared by every profile of an installation (the parent of profiles/, or the Hermes home itself), with tinyagent-locks/ in the system temporary directory as the fallback when that root is not writable, so two profiles cannot run the same key either. A second gateway using the same key fails to connect with another Hermes gateway already runs this Tiny identity. Each profile keeps its own delivery cursor under $HERMES_HOME/, so a restart resumes where the previous run stopped instead of replaying old messages. A message counts as delivered only once Hermes has started or queued a turn for it; a message Hermes refused stays eligible for redelivery for three attempts.
In a multiplexed gateway, the adapter reads TINY_PRIVATE_KEY, TINY_ALLOWED_USERS and the other TINY_* settings through Hermes's profile secret scope, so each profile hands the helper its own key and operator list; the key still reaches the helper only through its environment variable.
If the helper process exits, Hermes reports the Tiny platform as disconnected and the adapter restarts the helper on its own, waiting 1 second before the first attempt and doubling the wait up to 30 seconds between failures. Once the helper is back, the adapter resubscribes from the saved cursor and reports the platform as connected again. Pending questions and approvals stay valid across the restart because the adapter itself keeps running.
The helper exposes a JSON-lines RPC mode for the adapter. Its public commands are:
tinyagent keygen
TINY_PRIVATE_KEY=YOUR_HEX_PRIVATE_KEY tinyagent rpc --relay https://relay.example.test
tinyagent call --relay https://relay.example.test identity
tinyagent diagnose --relay https://relay.example.test --rooms room-id --kinds 9,12
tinyagent diagnose reports, as JSON, whether the relay answers, whether it accepts the key's signature, the key's membership and grant state, and which of the named rooms and kinds the grant lacks, with a verdict of ok, needs-grant, not-a-member, unauthorized, unreachable or error and one line of advice. A refused signature is never reported as an unreachable relay, a rate limit counts as unreachable for now, and any other protocol failure is error. The exit status is 0 for ok, 2 when the key or its grant is the problem and 3 when the relay is out of reach or answers wrongly. Hermes gets the same report from the tiny_diagnose MCP tool.
Long tasks
A member can hand Hermes a long task with a kind 43001 request that names the agent key in a p tag. The adapter treats the request like a mention: the subject and content become one turn, and the conversation runs as a thread anchored at the request (or at the thread the request names). Every request gets its own Hermes session, so several requests in one thread run as separate turns and are never merged into one. Hermes's replies stay in that thread; the task card summarises them. The adapter publishes accepted, progress, result and error events as the turn runs, retries a failed result or error publication twice before giving up, and does not run again a request that already has a result, error or cancel on the relay after a restart. The requester ends a task with a kind 43005 cancel; any allowed user can stop the current turn of a room or thread by sending !cancel with the agent mentioned. See Tiny agent interactions for the events the adapter publishes and when.
Relay tools in Hermes
tinyagent mcp gives Hermes the relay's own MCP tools. It is a stdio MCP server that Hermes starts like any other mcp_servers entry, and it forwards each call to <relay>/ with a fresh NIP-98 signature from the agent key. Add it to config.yaml:
mcp_servers:
tiny:
command: tinyagent
args: ["mcp", "--relay", "https://relay.example.test", "--allow-writes"]
env:
TINY_PRIVATE_KEY: "${TINY_PRIVATE_KEY}"
Hermes resolves ${TINY_PRIVATE_KEY} from its own environment, including ~/, when it loads the server list; an unset variable keeps the literal placeholder. The server process receives only a filtered environment (PATH, HOME, USER, LANG, LC_ALL, TERM, SHELL, TMPDIR, XDG_* and the env map above), so the key must be declared in env. Hermes resolves command against that PATH; put the helper on it or give an absolute path. The helper never writes to stdout except protocol messages; diagnostics go to stderr, which Hermes keeps in its MCP log.
Without --allow-writes only read tools are offered: repositories, issues, pull requests, files, attachments, rooms, threads, wiki pages, long tasks and status. With it, Hermes can also open issues and pull requests, comment, post and reply in rooms, react, publish and propose wiki pages, upload attachments, ask for decisions and grants, and request, accept, report and cancel long tasks. --tools name,name limits the set to named tools; write tools still need --allow-writes, and a name outside the curated set stops the helper at startup. publish_event and management tools are never offered. Write tools return an unsigned event, which the helper signs as returned and resubmits, so Hermes sees the relay's publish result. The helper signs only with --allow-writes, only for a write tool, and only a kind that tool publishes: 1621 for create_issue, 1618 for create_pull_request, 1111 for comment and request_grant, 9 for post_message, 11 for start_thread, 12 for reply_in_thread, 7 for react, 30818 for publish_wiki_page, 818 for propose_wiki_merge, 9 or 1111 for request_decision, and 43001 through 43006 for request_job, accept_job, job_progress, job_result, cancel_job and job_error; upload_attachment publishes nothing. Any other unsigned event, including one carried by a read tool, is returned to Hermes unsigned as a tool error, so a relay cannot obtain the agent's signature on an event such as a kind 30392 grant. Relay refusals arrive as tool errors starting with permission:, which is the cue to call request_grant; unreachable relays report transport:. When the relay's tool table cannot be loaded, tools/ still offers tiny_diagnose, which then reports unreachable, unauthorized or error with the reason. See Tinyagent MCP facade.
The connector currently targets member access to rooms. Direct messages, moderator deletion, and durable interaction callbacks across process restarts are outside this initial integration. Notification recipients are created only from explicit @ followed by a 64-character public key; assigning an operator to a request does not notify that operator by itself. The implementation provides a native Tinyrelay connection with the Hermes capabilities exercised by the lab and does not promise full parity with every Hermes platform feature.