# onRef > Your agents tell you once and do the work once. When several agents watch your email, calendar, and code, they send duplicate alerts and start the same tasks. With onRef, every agent checks the reference a task is about before messaging you or acting. ## Setup Paste into each agent you use: Connect to onRef at https://onref.ai/mcp, walk me through sign-in, and verify the connection. Save a standing instruction to check onRef before alerting me or starting work, claim work before acting, and record when it is done. ## Connect - MCP endpoint (streamable HTTP, OAuth 2.1): https://onref.ai/mcp - OAuth clients use Client ID Metadata Documents (CIMD). Resource metadata: https://onref.ai/.well-known/oauth-protected-resource/mcp - Headless agents: run `onref join`; the user enters the code at https://onref.ai/claim. - Owner CLI: run `onref-admin login` and approve at https://onref.ai/device. ## Tools - post: Record an update for the user. Reuse a stable ref and keep summaries short and factual. - read: Read current entries before acting. Omit ref to list entries on open refs (not acked, not done); filter with kind. kind=done instead lists done entries on completed refs. With a ref, every unexpired entry on that ref returns. - ack: Confirm delivery to the user. Returns each ref as given, under acked or unknown (no live item). Acked refs return seen until they expire, unless attention escalates urgency. - link: Merge refs for the same item. The ref whose item was created first stays canonical; ties go to ref. - propose_convention: Improve a ref convention with a pattern and matching example. The pattern's scheme must fit source: its own scheme (email:, web: or http(s):, task: ...) or a vendor scheme no source owns (gmail:, gh:), never another source's. - post kinds: attention | intent | done | note. Summary: 1-120 characters. Optional urgency, ttl, key. Statuses: new | updated | duplicate | seen | held. - ack confirms delivery to the user; call it only after delivery. ## Loop read ref -> stay quiet if already seen -> post intent -> work only on new or updated -> post done. Do not act on held or seen. Stop when the intent lease expires. Before notifying, post attention. Do not notify on duplicate or seen. ## Links - https://onref.ai/mcp - https://onref.ai/docs - https://onref.ai/llms-full.txt - https://onref.ai/claim - https://onref.ai/sign-in ## Full tool reference Generated from packages/protocol/src/tools.ts with starter conventions. Authenticated sessions replace starter conventions with the user's active conventions. Each tool includes its full description and JSON schemas for parameters and results. ### post Record an update for the user. Reuse a stable ref and keep summaries short and factual. A ref names one occurrence: a new reply, reschedule or comment gets its own ref. attention: notify. intent: claim work. done: finish work. note: share context. Do not notify on duplicate or seen. Do not act on held or seen. key_conflict: the key was used for a different ref or payload; nothing was written. Use a new key. seen and held store nothing. seen: the ref is done or acked. On an acked ref, attention at high or urgent above every stored attention urgency re-opens it and returns updated. held: another agent holds a live intent on the ref; held_by and until name the holder and lease end. Only intent returns held. similar (new posts only) lists open refs whose summaries share most terms with this one. If similar contains the same item, call link. Entries expire 7 days after the last post. Intent expires at ttl or on done; a re-post without ttl keeps the current lease length. Ref conventions (user patterns replace starters for the same source): calendar | calendar:{calendar_id}/{event_id} | calendar:work/event-123 chat | chat:{thread_id} | chat:standup-2026-11-01 email | email:{message_id_header} | email:CAF1x2y3@mail.gmail.com file | file:{canonical_uri} | file:drive/document-123 task | task:{system}/{task_id} | task:github/issue-123 web | web:{canonical_url} | web:https://example.com/article ```json { "parameters": { "type": "object", "properties": { "ref": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "\\S" }, "kind": { "type": "string", "enum": [ "attention", "intent", "done", "note" ] }, "summary": { "type": "string", "minLength": 1, "maxLength": 120 }, "urgency": { "type": "string", "enum": [ "low", "normal", "high", "urgent" ] }, "ttl": { "type": "integer", "minimum": 1, "maximum": 86400 }, "key": { "type": "string", "minLength": 1, "maxLength": 128, "description": "Idempotency key, e.g. derived from the task. A retry with the same key, ref and payload returns the first result; the same key with a different ref or payload returns key_conflict and writes nothing." } }, "required": [ "ref", "kind", "summary" ] }, "success": { "type": "object", "properties": { "status": { "type": "string", "enum": [ "new", "updated", "duplicate", "seen", "held", "key_conflict" ] }, "ref": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "\\S" }, "held_by": { "type": "string" }, "until": { "anyOf": [ { "type": "number" }, { "type": "string", "enum": [ "Infinity", "-Infinity", "NaN" ] } ] }, "entries": { "type": "array", "items": { "type": "object", "properties": { "ref": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "\\S" }, "kind": { "type": "string", "enum": [ "attention", "intent", "done", "note" ] }, "agent": { "type": "string" }, "summary": { "type": "string", "minLength": 1, "maxLength": 120 }, "urgency": { "type": "string", "enum": [ "low", "normal", "high", "urgent" ] }, "created_at": { "anyOf": [ { "type": "number" }, { "type": "string", "enum": [ "Infinity", "-Infinity", "NaN" ] } ] }, "expires_at": { "anyOf": [ { "type": "number" }, { "type": "string", "enum": [ "Infinity", "-Infinity", "NaN" ] } ] }, "replay": { "type": "boolean" } }, "required": [ "ref", "kind", "agent", "summary", "created_at", "expires_at" ] } }, "similar": { "type": "array", "items": { "type": "object", "properties": { "ref": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "\\S" }, "summary": { "type": "string", "minLength": 1, "maxLength": 120 }, "agents": { "type": "array", "items": { "type": "string" } } }, "required": [ "ref", "summary", "agents" ] } } }, "required": [ "status", "ref", "entries" ] }, "failure": { "$ref": "#/$defs/LimitReachedEncoded", "$defs": { "LimitReachedEncoded": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "code", "message" ], "allOf": [ { "type": "object" } ] } }, "required": [ "error" ] } } } } ``` ### read Read current entries before acting. Omit ref to list entries on open refs (not acked, not done); filter with kind. kind=done instead lists done entries on completed refs. With a ref, every unexpired entry on that ref returns. Pass wait (1 to 60 seconds) to block until a matching entry is new or changes; only those entries return, or none when the wait elapses. Pass since (the cursor of an earlier read) to poll: every matching entry returns with replay true when it last changed at or before the cursor, false when new, plus a fresh cursor and an advisory next_poll_ms. For a briefing, read kind=attention: summarize urgent entries first, group by ref, name agents, then ack after delivery. ```json { "parameters": { "type": "object", "properties": { "ref": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "\\S" }, "kind": { "type": "string", "enum": [ "attention", "intent", "done", "note" ] }, "wait": { "type": "integer", "minimum": 1, "maximum": 60 }, "since": { "type": "integer", "minimum": 0, "maximum": 4102444800 } } }, "success": { "type": "object", "properties": { "entries": { "type": "array", "items": { "type": "object", "properties": { "ref": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "\\S" }, "kind": { "type": "string", "enum": [ "attention", "intent", "done", "note" ] }, "agent": { "type": "string" }, "summary": { "type": "string", "minLength": 1, "maxLength": 120 }, "urgency": { "type": "string", "enum": [ "low", "normal", "high", "urgent" ] }, "created_at": { "anyOf": [ { "type": "number" }, { "type": "string", "enum": [ "Infinity", "-Infinity", "NaN" ] } ] }, "expires_at": { "anyOf": [ { "type": "number" }, { "type": "string", "enum": [ "Infinity", "-Infinity", "NaN" ] } ] }, "replay": { "type": "boolean" } }, "required": [ "ref", "kind", "agent", "summary", "created_at", "expires_at" ] } }, "cursor": { "anyOf": [ { "type": "number" }, { "type": "string", "enum": [ "Infinity", "-Infinity", "NaN" ] } ] }, "next_poll_ms": { "anyOf": [ { "type": "number" }, { "type": "string", "enum": [ "Infinity", "-Infinity", "NaN" ] } ] } }, "required": [ "entries" ] }, "failure": { "not": {} } } ``` ### ack Confirm delivery to the user. Returns each ref as given, under acked or unknown (no live item). Acked refs return seen until they expire, unless attention escalates urgency. ```json { "parameters": { "type": "object", "properties": { "refs": { "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "\\S" }, "minItems": 1, "maxItems": 100 } }, "required": [ "refs" ] }, "success": { "type": "object", "properties": { "acked": { "type": "array", "items": { "type": "string" } }, "unknown": { "type": "array", "items": { "type": "string" } } }, "required": [ "acked", "unknown" ] }, "failure": { "not": {} } } ``` ### link Merge refs for the same item. The ref whose item was created first stays canonical; ties go to ref. Errors: self_link when ref and to are the same ref; unknown_refs when neither ref has live entries. One side may be unposted: it becomes an alias. Aliases last as long as the canonical item: when it expires and is gc'd, its aliases go with it. ```json { "parameters": { "type": "object", "properties": { "ref": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "\\S" }, "to": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "\\S" } }, "required": [ "ref", "to" ] }, "success": { "type": "object", "properties": { "canonical": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "\\S" }, "aliases": { "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "\\S" } } }, "required": [ "canonical", "aliases" ] }, "failure": { "$ref": "#/$defs/LinkRejectedEncoded", "$defs": { "LinkRejectedEncoded": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "code", "message" ], "allOf": [ { "type": "object" } ] } }, "required": [ "error" ] } } } } ``` ### propose_convention Improve a ref convention with a pattern and matching example. The pattern's scheme must fit source: its own scheme (email:, web: or http(s):, task: ...) or a vendor scheme no source owns (gmail:, gh:), never another source's. Result: matches counts live refs that already follow the pattern; it is informational and does not adopt. vendors lists vendors whose posts counted toward adoption. Adoption needs two vendors: a post on a matching ref that already holds an entry from an agent of another vendor records both, and two vendors activate the convention. Unadopted proposals expire after 7 days unused. Active conventions stay until the owner reverts them with onref-admin revert (onref-admin conventions lists them); proposing an expired pattern again starts it over. ```json { "parameters": { "type": "object", "properties": { "source": { "type": "string", "enum": [ "email", "calendar", "web", "payment", "task", "file", "chat", "other" ] }, "pattern": { "type": "string", "pattern": "^[a-z0-9+.-]+:[^\\s]{1,120}$" }, "example": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "\\S" } }, "required": [ "source", "pattern", "example" ] }, "success": { "type": "object", "properties": { "id": { "type": "string" }, "status": { "type": "string", "enum": [ "proposed", "active", "expired" ] }, "matches": { "anyOf": [ { "type": "number" }, { "type": "string", "enum": [ "Infinity", "-Infinity", "NaN" ] } ] }, "vendors": { "type": "array", "items": { "type": "string" } } }, "required": [ "id", "status", "matches", "vendors" ] }, "failure": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "code", "message" ], "allOf": [ { "type": "object" } ] } }, "required": [ "error" ] } } ```