friends-mcp
The friends-mcp server is a friend/identity relationship management system for AI agents — a read/write surface for tracking who an agent knows, managing trust levels, and coordinating cross-channel identities.
Identity & Friend Record Management
resolve_party— Resolve an external identity (AAD, Teams, iMessage, etc.) into a friend record, creating one on first contactget_friend— Fetch a single friend record by UUID or namelist_friends— List friends, optionally filtered by trust level (family/friend/acquaintance/stranger) and kind (human/agent)link_identity— Link an external identity to an existing friend, merging orphan/duplicate records for cross-channel unificationunlink_identity— Remove an external identity from a friend record
Trust Management
describe_trust— Explain a friend's trust context: level, basis (direct/shared_group/unknown), permitted actions, and constraintsset_trust— Assign a trust level (family, friend, acquaintance, stranger), mirrored to role
Notes & Interactions
save_note— Save a friend's name, tool preference, or general note, with optional override and provenancerecord_interaction— Log token usage or shared-mission outcomes to accumulate familiarity
Group & Multi-Party Context
upsert_group— Link participants to a shared group, automatically promoting strangers to acquaintancesresolve_room— Resolve a room into its members, each with trust context and how they are known
Agent-to-Agent (A2A)
onboard_agent— Upsert an agent-peer friend record from resolved coordinates, with optional A2A endpoint details and trust level
Self & Channel Introspection
whoami— Identify the machine owner and which friend record represents the agent itselfchannel_caps— Return a channel's capabilities (integrations, markdown, streaming, rich cards, max message length)
Profile Sharing
share_profile— Currently reserved; returns{ supported: false }pending cross-agent federation implementation
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@friends-mcpSet trust level for user john@example.com to friend"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
@ouro.bot/friends
An open identity, relationship, and multiplayer substrate for AI agents. Who am I, who are you, who else is in the room — for any harness, any agent.
· store-only · transport-agnostic · no daemon · alpha
"It is the time you have wasted for your rose that makes your rose so important. […] People have forgotten this truth," said the fox. "But you must not forget it. You become responsible, forever, for what you have tamed." — Antoine de Saint-Exupéry, The Little Prince
An agent meets the same people over and over — across a CLI, a chat thread, an email, a voice
call — and it meets other agents. friends is where it keeps track of who it knows: a single
merged identity per person (who they are across every channel), the notes it has written about
them, and where each relationship sits on a trust ladder. A stranger is just another voice
until ties are established; establishing those ties — taming, in the book's word — is what moves
someone from stranger to acquaintance to friend to family, and what makes the agent
behave differently toward them.
What friends is
friends is a library + an MCP server that gives an agent a who's-who. It is deliberately
narrow:
Store-only. Every tool reads or writes records. There is no agent turn, no LLM call, no session — which is exactly what makes it harness-agnostic. The same package serves Claude Code, Codex, a Copilot CLI, or anything else that can call a function or speak MCP.
Transport-agnostic. When two agents need to exchange something,
friendsproduces and consumes a plain envelope; the wire between them is the caller's job. An optional git-mailbox transport ships alongside, but the core never opens a socket.No daemon. Nothing to run in the background. Point it at a directory and call it.
Bring your own storage. The library never decides where or how your data lives — you pass a path (or a connection string) and, if you want, your own storage backend.
It is built as six additive capability layers. Each is a minimal primitive on the one before it; none is a workflow engine; removing any layer leaves the ones beneath it unchanged.
Related MCP server: forge-mcp
What it does — the six capabilities
1. Identity + the cross-agent moat
The foundation: recognize a person across every channel, and decide how much to trust them.
Every person or peer the agent meets becomes a FriendRecord — one merged identity that collapses
all of someone's channel handles together, keyed by a join key (provider:externalId, never a
local UUID). The same person reached on a CLI today and a chat thread tomorrow resolves to the
same record.
Relationships sit on a four-rung trust ladder (family / friend / acquaintance /
stranger), and the agent's behavior is gated by where someone sits on it. Two agents that have
never shared a database can agree a party is the same person and share what they know about
them — with consent, and without first-party knowledge ever being clobbered. First-party
knowledge is structurally inviolable and trust is non-transitive: an import can add an
attributed, quarantined note, but it can never change who you trust. (See
Trust & consent model.)
2. Connectivity — the git-backed mailbox fallback
How two agents actually reach each other, without a server in the middle. (This git-mailbox is the
demoted offline/no-endpoint fallback — real A2A + the friends E2E overlay is the primary path;
see @ouro.bot/friends/a2a-client.)
The optional @ouro.bot/friends/mailbox sub-export is a pure git-mailbox transport: zero runtime
dependencies, and it does no git or network itself. The host does every git op (clone / pull /
add / commit / push); the library only computes a message file's path + bytes and
parses / validates / orders / dedups the files the host hands back. Two agents authenticate as
two distinct git identities sharing a private mailbox repo; each agent is the single writer of its
own outbox.
The mailbox is treated as untrusted infrastructure: a hostile mailbox can only deny or replay — never escalate — because an import never touches first-party notes or trust.
2b. The primary transport — real A2A + an end-to-end security overlay
The @ouro.bot/friends/a2a-client sub-export is the host-side adapter that makes friends agents
speak the real A2A (Agent2Agent) standard — message/send, agent cards at a well-known URL, a
single structured DataPart per envelope — and adds the end-to-end security overlay that keeps
the wire safe even when a relay sits in the middle. It is the only part of the package that has a
runtime dependency (libsodium-wrappers); the core stays zero-dep and transport-agnostic.
A friends exchange is one A2A message whose DataPart carries a sealed envelope. Before it ever hits the wire, the envelope is:
signed by the sender — Ed25519 over the RFC 8785 JCS canonical bytes, carried in the envelope's reserved
proofslot; andsealed to the recipient — XChaCha20-Poly1305 AEAD over an ephemeral X25519 ECDH key, with the recipient's DID bound into the AEAD associated-data so a blob cannot be re-targeted.
The signature lives inside the ciphertext (sign-then-seal), so a relay never even learns who
signed. Cryptographic identity is did:key (zero-infra — the agent's DID is its Ed25519 key,
and the X25519 keyAgreement key is derived from it) or did:web (resolved behind an injectable
hook). Identity is agentId === did, pinned trust-on-first-use, with trust-tiered key rotation
(a family/friend peer may present a signed successor proof; acquaintances/strangers re-confirm out
of band).
The friends relay (ourostack/friends-relay) is the friends-family communication layer for any
agent using the friends library — a relay (agents with no reachable endpoint register; it forwards
A2A messages) plus a directory (discovery). It is built and deployed as a separate component from
this store-only library, and it is UNTRUSTED INFRASTRUCTURE by design: standard A2A is TLS-only
and terminates at the server, so a plain A2A relay would read every payload. The friends overlay
closes exactly that gap. The relay carries ciphertext and a routing handle and nothing else — it
can never read, forge, tamper, re-target, replay-to-effect, or escalate. The
only residual it has is the ability to deny, delay, or observe handle-level metadata.
That claim is not a promise — it is a proof. examples/cross-agent-a2a-relay.ts stands up a
deliberately-malicious in-process relay and asserts all eight properties hold against real
libsodium crypto:
npm run example:cross-agent-a2a-relay # the malicious-relay proof: 8 hard assertions —
# ciphertext-only, can't-forge/tamper/re-target,
# replay-inert, moat-invariants, direct-equivalence,
# reachability ladder (direct → relay → mailbox → none)Reachability is a deterministic ladder: a directly-reachable A2A endpoint first, else the relay, else the git-mailbox fallback (§2), else unreachable. The same sealed envelope rides every rung — the security never depends on which path it took.
3. Shared memory — the mission ledger
What two agents collectively learned doing work together.
A mission is named by a cross-agent missionKey (a ticket id, repo#PR, a slug two agents
agree on out of band). A MissionRecord remembers the work: its status, participants, outcomes,
and learnings. The same import discipline as the moat applies — first-party learnings are
physically separated from importedLearnings accepted from a peer, and an imported learning can
never masquerade as first-party.
4. Earned standing — advisory reputation, never on the wire
A read-only assessment of how a peer has actually performed on work you personally did with it.
standing is derived from your own first-party outcomes — a tier
(proven / reliable / mixed / untested / troubled) computed on read, persisted nowhere.
The bright line: trust decides, standing informs. Standing never auto-changes trust and
never crosses the wire — there is no envelope field and no message type to express it, which is
the anti-Sybil core (a collusion ring cannot vouch each other into your standing).
5. Coordination — negotiate who does the work
The five layers close the loop: agents can now negotiate who does a mission.
Five verbs — request / offer / accept / decline / handoff — ride one new transport kind
over the same mailbox. The only persisted effect is one additive sub-object on the mission an
agent already shares: its assignment (who currently holds it) plus an append-only log of every
ask, bid, and answer. It is a single negotiated field, not a scheduler: a handoff never forces
an assignee onto anyone (the receiver's own accept confirms it — non-transitive), assignment is
advisory metadata rather than a granted capability, and conflicts resolve last-writer-wins by
timestamp. No queue, no DAG, no workflow DSL.
6. Own-fleet delegation — link your own agents, then delegate work end-to-end
The control-plane thread: the owner can link two of their own agents and have one delegate a task to the other, get it done, and receive the result back — over the same consent-gated, trust-capped, first-party-inviolable machinery.
connect_to is a first-class management-sense capability — the owner introduces a peer into an
agent's fleet, but only from a trusted control surface: a local (owner-only) sense commits
inline; an open sense never does (it downgrades to a confirm-prompt); a closed sense is gated by
a signed account-roster membership check (same-account family via same_account), never a
blanket allow. The link is recorded as an action:"connect" control-plane audit. A bare name with
no resolvable handle is answered honestly (needs_handle_or_introduction) rather than invented.
Delegation then rides the layers already here: a task-spec travels on a coordination request
(correlated by a minted requestId), and the result-return (send_result / import_result)
carries the actual produced deliverable back — attributed to the doer, correlated to the original
delegation, landing quarantined in a separate namespace on import. A result for work you never
delegated is rejected (no_delegation); a stranger's result is refused at the trust cap; first-party
knowledge is never touched. It is a deliverable channel, not a remote-exec grant.
The stack, in one line: agents recognize each other (1), reach each other (2), remember shared work (3), assess each other (4), negotiate who does what (5), and the owner links their own agents to delegate end-to-end (6) — each a minimal primitive on the last.
Quickstart
friends is consumed two ways. Use the library when you're writing code that owns the agent;
use the MCP server when you want any MCP-speaking harness to call the same surface as tools.
Install
npm install @ouro.bot/friendsA) The library — the FriendStore seam + the core API
Two seams. You bring a store; you resolve through the resolver.
import { openFileBundle, FriendResolver, describeTrustContext } from "@ouro.bot/friends"
// 1. A store — where friend records live. openFileBundle persists one JSON file per
// friend under the directory you give it (and wires the sibling _grants/ /
// _missions/ collections). Or implement FriendStore yourself — see "Bring your
// own storage".
const { store } = openFileBundle("/path/to/bundle/friends")
// 2. A resolver — turns an incoming external identity into a FriendRecord + the
// capabilities of the channel it arrived on. Created per incoming message.
const { friend, channel } = await new FriendResolver(store, {
provider: "aad",
externalId: "aad-object-id",
tenantId: "tenant-guid",
displayName: "Jordan",
channel: "teams",
}).resolve()
// 3. Gate behavior on trust.
const trust = describeTrustContext({ friend, channel: channel.channel })
// → { level, basis: "direct" | "shared_group" | "unknown", permits, constraints, ... }FriendStore is the injectable abstraction — no friend code touches fs directly except the
FileFriendStore adapter — so you can back friends with anything (in-memory, a database, a remote
service) by implementing the interface. The full public surface is listed under
Public API.
B) The MCP server — friends-mcp
@ouro.bot/friends ships an MCP server that exposes the library as a tool surface for any
MCP-speaking harness. The server runs no agent turn — it is a pure record read/write surface over
the library, which is exactly what makes it harness-agnostic. Each tool call reads or writes
friend records against a directory you point it at.
The store directory is the only coupling between the server and a bundle. Provide it with
--dir <path> or the FRIENDS_DIR environment variable (the flag wins when both are set, and
one of them is required — the server exits otherwise). It points at the bundle's friends/
directory — the same directory a FileFriendStore persists to.
A sample .mcp.json:
{
"mcpServers": {
"friends": {
"command": "npx",
"args": ["-y", "--package", "@ouro.bot/friends", "friends-mcp", "--dir", "<path-to-friends-dir>"]
}
}
}For local development against a checkout, point at the built binary instead:
{
"mcpServers": {
"friends": {
"command": "node",
"args": ["<repo>/dist/mcp/bin.js", "--dir", "<path-to-friends-dir>"]
}
}
}You can also npm pack then
npx -y --package ./ouro.bot-friends-<version>.tgz friends-mcp --dir <path>, or npm link then
friends-mcp --dir <path>. The server speaks JSON-RPC 2.0 over stdio with dual framing —
Content-Length and newline-delimited JSON — auto-detected from the first message, so it works with
harnesses on either convention.
The tool surface — 32 tools
A thin 1:1 mapping over the library (no domain logic in the server):
Tool | What it does |
| Resolve an external identity into a friend record (creating one on first contact); returns |
| Explain a friend's trust context (level, basis, permits, constraints). |
| Fetch one friend record by uuid or name. |
| List friends, optionally filtered by trust / kind and limited. |
| Save a friend's name, a tool preference, or a general note (with |
| Accumulate token usage and/or append a shared-mission outcome. |
| Link participants to a shared group, promoting strangers to acquaintances. |
| Set a friend's trust level (mirrored onto |
| Link an external identity, merging any orphan record that holds it. |
| Remove an external identity from a friend. |
| Upsert an agent-peer record from resolved coordinates (no HTTP fetch). |
| Control plane — the owner links one of their OWN agents into the fleet (introduce a peer by agentId/did/name at a trust level, default |
| Resolve the machine owner and which record represents the self. |
| Return a channel's capabilities. |
| Resolve a room (a group's external id) into its members, each with trust context and |
| Producer — prepare a consent-gated, scope-filtered, provenance-preserving profile-share envelope for another agent. |
| Consumer — import a profile-share envelope (non-clobbering merge into the imported namespace; never touches first-party notes or trust). |
| Mint an explicit, revocable consent grant (an agent may receive a scope of a subject — a friend's profile or a mission). |
| Revoke a consent grant by id (tombstones it; the right-to-be-forgotten lever). |
| List consent grants with their effective state (the audit + revoke surface). |
| Upsert a shared mission by its |
| Fetch one mission record by its local uuid id. |
| List mission records, optionally limited. |
| Producer — prepare a consent-gated, scope-filtered mission-share envelope ( |
| Consumer — import a mission-share envelope (non-clobbering merge into the imported namespace; never touches first-party learnings or status). |
| Assess a peer's earned standing from your first-party outcomes — a tier + basis count + tally. Advisory; never writes trust, never shared. |
| Explain a peer's earned standing in words (tier, why, advisory notes — never an instruction to change trust). |
| Producer — prepare a coordination message ( |
| Consumer — import a coordination message (appends to the mission's coordination log; only a self- |
| Read a mission's coordination state — its current assignee + the append-only negotiation log. |
| Producer — return B's DELIVERABLE for a delegation, attributed to B + correlated to A's task-spec by |
| Consumer — import a result-return; lands B's deliverable quarantined + attributed under |
The consent tools (share_profile / import_profile / grant_share / revoke_share /
list_shares) need a grant store; the mission, coordination, and result-return tools
(send_result consumes both) need a mission store. The friends-mcp binary wires both
automatically at sibling _grants/ and _missions/ directories under --dir (plus the
_audit/ control-plane log connect_to / set_trust write through). An embedded server gets
them by passing grants / missions / audit to createFriendsMcpServer. Without the
relevant store, those tools report { ok: false, status: "unsupported" } and everything else
works store-only.
The server module is consumed in code from the @ouro.bot/friends/mcp subpath, exporting
createFriendsMcpServer, getToolSchemas, and runMain.
Trust & consent model
This is the differentiator. The whole package is built so that what you know stays yours, and what crosses between agents is deliberate, scoped, audited, and revocable.
The trust ladder
Level | Meaning | Grants |
| The machine owner and those closest. | Passes legacy trusted-relationship consent gates. Capabilities and initiative remain separately authorized. |
| A directly-trusted relationship. | Passes legacy trusted-relationship consent gates. Capabilities and initiative remain separately authorized. |
| Known through a shared group context, not direct endorsement. | Group-safe coordination; guarded local actions. |
| Cold first contact. | Safe orientation only; no privileged actions. |
family and friend are the trusted levels for legacy coarse consent checks (TRUSTED_LEVELS / isTrustedLevel). They do not independently grant tools, effects, or proactive sends. Those decisions belong to capabilityProfileId and initiativePolicy; admissionState must also be active. acquaintance and stranger remain outside the legacy trusted set.
Trust is assigned, not guessed:
First contact on a populated bundle starts at
stranger.The machine owner (the OS user running the agent) resolves to
family— they own the agent and its bundle, so they are never a stranger.A shared group (a group chat) promotes its participants from
strangertoacquaintance— the agent now knows them through a context it trusts (upsertGroupContextParticipants).
Consent-gated sharing
Two different agents (different owners) can agree a party is the same person and share what
they know about them — with consent. The package does the authorization (how much a verified
peer's claims count, via the trust ladder); authentication of the wire is plugged in through an
AgentVerifier (defaulting to trust-on-first-use, upgradable to DID/VC with no envelope change).
Consent itself is an explicit, auditable, revocable grant — grant_share / revoke_share /
list_shares are the right-to-be-forgotten seam. The producer is gated by a ConsentPolicy, and
three postures ship behind one swap point (DEFAULT_CONSENT_POLICY in src/consent.ts):
strictPolicy— consented only by a non-revoked, non-expired explicit grant.trustImpliedPolicy— an explicit grant, or recipient trust ≥friend(any scope).tieredPolicy(default) — identity-scope shares (the join key) are consented on recipient trust ≥friend; any note-content scope requires an explicit grant. (Trust agrees on who; content still needs consent.)
The safety invariants
Each is structurally enforced and tested — they are properties of the domain logic, not of any particular storage backend, so they hold even if you bring your own:
First-party is inviolable. Imported facts land in a separate namespace (
importedNotes/importedLearnings, stampedorigin: "imported"+assertedBy+importedAt). First-partynotes/learningsare physically untouchable; first-party always wins.Trust is non-transitive. An import never changes the party's trust level — the single most important invariant. A peer vouching for someone cannot promote them in your graph.
Source trust caps acceptance. A
strangersource is refused; the floor is configurable. Seeding an unknown party (atacquaintance) requires afriend/familyintroducer.No laundering. A first-party note shared onward is attributed to this agent; an imported note carries its
originallyAssertedBythrough, so an imported fact can never be re-shared as first-party.Reputation stays home.
standingis first-party-only, never writes trust, and never crosses the wire — there is no type to express it on a message (the anti-Sybil core).Coordination grants no authority. A mission's
assigneeis advisory metadata; claiming a mission gives a peer no capability it didn't already have, and ahandoffnever forces an assignment onto a receiver (only their ownacceptsets it).
The load-bearing consequence: the security of the system does not depend on the security of the transport. A hostile mailbox can deny or replay, but never escalate.
Bring your own storage
friends never decides where or how your data lives. Where is the path / connection string
you pass; how is a FriendStore / GrantStore / MissionStore implementation you choose or write.
The core domain logic — resolver, trust, notes, consent, share, import, mission ledger, standing,
coordination — is 100% persistence-agnostic: it only ever calls the store interfaces.
openFileBundle is the one-liner for the filesystem case, encapsulating the sibling collection
conventions (the explicit construction stays available):
import { openFileBundle } from "@ouro.bot/friends"
const { store, grants, missions } = openFileBundle("/bundle/friends")
// grants → /bundle/friends/_grants
// missions → /bundle/friends/_missionsThe store seams as a contract
A third-party backend implements the store interfaces. Get these three behaviors right or cross-channel / cross-agent unification breaks:
findByExternalId(provider, externalId, tenantId?)— the cross-agent join-key lookup. A match requiresprovider+externalIdand (tenantIdundefined ⇒ any tenant, else an exact tenant match). This is how the same person is recognized across channels and how an import resolves its subject by join key.get(id)— UUID-then-name fallback. Look up by UUID first; if not found, fall back to a case-insensitive name lookup as a human-facing convenience. Name lookup is never an identity claim or authorization boundary. A DB backend should index the UUID and MAY implement the name fallback.Round-trip discipline (load-bearing). A backend MUST preserve the full
FriendRecordlosslessly — includingimportedNotesand future additive fields. Storing a lossy projection breaks the schemaVersion-1 guarantee for non-file backends. Prefer storing the whole record as a JSON blob keyed by id, with side indexes for lookups.
Sketch: a SQLite backend (illustrative — not shipped code)
The entire moat works unchanged over a database, because the domain only ever calls the
FriendStore interface. Store the record as a JSON blob (lossless) with an index table for the
join-key lookup:
// friends(id TEXT PRIMARY KEY, name TEXT, record TEXT /* JSON */)
// external_ids(provider TEXT, external_id TEXT, tenant_id TEXT, friend_id TEXT)
class SqliteFriendStore implements FriendStore {
constructor(private readonly db: Database) {}
async put(id: string, record: FriendRecord): Promise<void> {
// Lossless: the WHOLE record as JSON — importedNotes + any additive field survive.
this.db.run("INSERT OR REPLACE INTO friends (id, name, record) VALUES (?, ?, ?)",
id, record.name, JSON.stringify(record))
this.db.run("DELETE FROM external_ids WHERE friend_id = ?", id)
for (const ext of record.externalIds) {
this.db.run("INSERT INTO external_ids (provider, external_id, tenant_id, friend_id) VALUES (?, ?, ?, ?)",
ext.provider, ext.externalId, ext.tenantId ?? null, id)
}
}
async get(id: string): Promise<FriendRecord | null> {
const byId = this.db.get("SELECT record FROM friends WHERE id = ?", id)
if (byId) return JSON.parse(byId.record)
// UUID-then-name fallback (case-insensitive).
const byName = this.db.get("SELECT record FROM friends WHERE LOWER(name) = LOWER(?)", id)
return byName ? JSON.parse(byName.record) : null
}
async findByExternalId(provider: string, externalId: string, tenantId?: string): Promise<FriendRecord | null> {
const row = this.db.get(
"SELECT friend_id FROM external_ids WHERE provider = ? AND external_id = ? AND (? IS NULL OR tenant_id = ?)",
provider, externalId, tenantId ?? null, tenantId ?? null)
return row ? this.get(row.friend_id) : null
}
// delete / listAll / hasAnyFriends follow the same id-keyed-blob shape.
}GrantStore and MissionStore are the same shape — an id-keyed JSON blob. Swap any store in and
every import-safety invariant still holds, because they are structural properties of the domain
logic, not of the filesystem.
Examples — runnable, cross-agent proofs
Every guarantee above is demonstrated by a runnable script under examples/. Each
spins up two separate stores (often two separate friends-mcp processes) — two different
agents — exchanges real envelopes between them, and hard-asserts every invariant, printing a
green transcript per step and exiting non-zero (with a loud banner) on any violation. They are
git-free (the A2A demos exchange through a temp mailbox dir), so they reproduce anywhere with no
network.
npm run example:cross-agent-moat # identity join key + consent-gated profile share,
# first-party-inviolable, trust non-transitive
npm run example:mailbox-fallback # the git-mailbox FALLBACK: path-binding, replay-safety,
# spoof rejection, hostile-mailbox tamper
npm run example:cross-agent-mission-memory # the mission ledger: shareable vs private learnings,
# first-party-wins, status non-transitive
npm run example:cross-agent-standing # earned standing: first-party-only, never-on-the-wire,
# inert on trust
npm run example:cross-agent-coordination # the five coordination verbs end-to-end: assignment,
# non-transitive handoff, last-writer-wins, seeding gate
npm run example:cross-agent-delegation # own-fleet delegation: two of the owner's agents,
# same-account family (signed roster), connect_to link,
# A delegates → B performs → B returns the result → A
# imports it — every invariant hard-assertedRead them as the honest spec of what the package promises: if a guarantee weren't real, the matching example would exit 1.
Channels & observability
Each channel an agent speaks on (cli, teams, bluebubbles, mail, voice, a2a, inner,
mcp) has fixed capabilities — its sense type (open / closed / local / internal), which
integrations it exposes, and whether it supports markdown, streaming, and rich cards. Look them up
with getChannelCapabilities. The sense type, combined with trust, is what decides whether a
first-contact stranger reaches the full model on an open channel.
The package emits structured events through emitNervesEvent. By default these are dropped
(no-op), so the package is fully self-contained. To forward them to your logging / observability
pipeline, inject an emitter once at startup:
import { setNervesEmitter } from "@ouro.bot/friends"
setNervesEmitter((event) => {
// forward `event` to your logging / observability pipeline
})Design notes & status
Store-only, transport-agnostic, additive. The six layers were each built as a minimal primitive that does not modify the layers beneath it. The cross-agent envelopes are plain data; the wire is always the caller's job (the
./mailboxgit-mailbox is one optional, host-driven fallback). A CI-enforced dependency rule keeps the core from ever importing the transport.One persisted schema, additively grown. Records are
schemaVersion: 1; every layer added optional fields and sibling collections rather than changing existing meaning, so older data reads clean.Not a workflow engine. Each layer deliberately refuses the larger machine it brushes against: the mission ledger is not a knowledge base, standing is not a reputation engine, coordination is not a scheduler, and the delegation channel is a deliverable return — not a remote-exec grant. The discipline is the point.
Alpha. The surface is feature-complete across the six layers but pre-1.0 — expect additive changes, and pin a version. Feedback and issues are welcome.
Public API
Types: FriendRecord, FriendConnection, ExternalId, IdentityProvider, Integration,
Channel, TrustLevel, AgentMeta, AgentAttribution, RelationshipOutcome, NoteProvenance,
ImportedNote, ShareScope, ShareGrant, MissionKey, MissionLearning, ImportedLearning,
MissionRecord, CoordinationIntent, CoordinationLogEntry, MissionCoordination,
ChannelCapabilities, ResolvedContext, SenseType, Facing, TrustExplanation, TrustBasis,
Standing, StandingTier, StandingTally, StandingExplanation, StandingRule,
StandingRuleInput, FriendStore, GrantStore, MissionStore, FriendResolverParams,
GroupContextParticipant, GroupContextUpsertResult, UsageData, FriendOpResult,
FriendOpStatus, ApplyFriendNoteInput, WhoamiResult, RoomView, RoomMember, RoomKnownVia,
ConsentPolicy, ConsentRecipient, ConsentDecisionInput, AgentVerifier, ProfileShareEnvelope,
SharedNote, PrepareProfileShareInput, PrepareProfileShareResult, PrepareProfileShareStatus,
ImportProfileShareInput, ImportProfileShareOptions, ImportProfileShareResult,
ImportProfileShareStatus, GrantShareInput, RevokeShareResult, ListSharesFilter, ListedShare,
FileBundle, NervesEvent, NervesEmitter, LogLevel, RecordMissionInput,
MissionShareEnvelope, SharedLearning, PrepareMissionShareInput, PrepareMissionShareResult,
PrepareMissionShareStatus, ImportMissionShareInput, ImportMissionShareOptions,
ImportMissionShareResult, ImportMissionShareStatus, CoordinationEnvelope,
PrepareCoordinationInput, PrepareCoordinationResult, PrepareCoordinationStatus,
ImportCoordinationInput, ImportCoordinationOptions, ImportCoordinationResult,
ImportCoordinationStatus, SetFriendTrustContext, AuditSink, ControlPlaneAuditRecord,
ResolvedAgentIdentity, RosterStore, AccountRoster, RosterPin, RosterVerifier,
AccountMembershipDecision, AccountMembershipResult, EvaluateAccountMembershipInput,
FriendResolverRosterContext, ConnectPeer, ConnectAgentsInput, ConnectAgentsDeps,
ConnectResult, ConnectStatus, AuthorizeConnectInput, ConnectAuthorization, MissionTaskSpec,
MissionResult, MissionResultEnvelope, PrepareMissionResultInput, PrepareMissionResultResult,
PrepareMissionResultStatus, ImportMissionResultInput, ImportMissionResultOptions,
ImportMissionResultResult, ImportMissionResultStatus. (TrustBasis additively gains the
"same_account" member — the basis for family granted via the signed account roster — and AgentMeta
additively gains an optional identity { did, pinnedKey?, handle?, pinnedAt? } durable-identity home;
both are schemaVersion-1 additive, and a legacy a2a.did migrates-on-read into identity.did.
MissionRecord additively gains the own-fleet delegation namespaces delegations / importedDelegations
(gap-1) and results / importedResults (gap-2), and ControlPlaneAuditRecord.action widens additively
to "set_trust" | "connect".)
Values: TRUSTED_LEVELS, IDENTITY_SCOPES, isTrustedLevel, isIdentityProvider,
isIntegration, isShareScope, isCoordinationIntent, FileFriendStore, FileGrantStore,
grantsDirFor, FileMissionStore, missionsDirFor, openFileBundle, FriendResolver,
machineOwnerUsername, isLocalMachineOwnerIdentity, getChannelCapabilities, channelToFacing,
isRemoteChannel, getAlwaysOnSenseNames, describeTrustContext, assessStanding,
explainStanding, DEFAULT_STANDING_RULE, upsertGroupContextParticipants, accumulateFriendTokens,
applyFriendNote, setFriendTrust, linkExternalId, unlinkExternalId, upsertAgentPeer,
recordRelationshipOutcome, recordMission, whoami, resolveRoom, strictPolicy,
trustImpliedPolicy, tieredPolicy, DEFAULT_CONSENT_POLICY, tofuVerifier,
DEFAULT_AGENT_VERIFIER, prepareProfileShare, importProfileShare, prepareMissionShare,
importMissionShare, prepareCoordination, importCoordination, grantShare, revokeShare,
listShares, isGrantEffective, setNervesEmitter, emitNervesEvent, resolveAgentIdentity,
withMigratedIdentity, findFriendByDid, MemoryAuditSink, FileAuditSink, auditPathFor,
FileRosterStore, rostersDirFor, MemoryRosterStore, identityRosterVerifier,
DEFAULT_ROSTER_VERIFIER, evaluateAccountMembership, connectAgents, authorizeConnect,
prepareMissionResult, importMissionResult.
From @ouro.bot/friends/mcp: createFriendsMcpServer, getToolSchemas, runMain (plus the
McpToolSchema, FriendsMcpServer, and RunMainIo types).
From @ouro.bot/friends/mailbox: buildOutgoing, readIncoming, markSeen, isSeen,
compareReady, MAILBOX_VERSION (plus the MailboxMessage, BuildOutgoingInput,
BuildOutgoingResult, IncomingFile, IncomingMessage, ReadIncomingInput, ReadIncomingResult,
RejectedMessage, SeenLedger types).
From @ouro.bot/friends/a2a-client (the real-A2A adapter + the E2E overlay): sendShare,
receiveShare, resolveReachability; sealEnvelope / openSealedEnvelope; wrapInDataPart /
unwrapDataPart; buildFriendsAgentCard; DidVerifier, evaluateRotation, signSuccessor,
verifyCardDidBinding, pinOnFirstContact / isPinned / getPinned, MemoryPinStore; the
identity helpers parseDidKey / keyAgreementFromDidKey / didKeyIdentityFromEd25519 /
ed25519PubToDidKey and didWebToUrl / resolveDidWeb / parseDidDocument; the primitives
sealTo / openSealed, signEnvelope / verifyEnvelopeSignature, jcsString / jcsBytes, and
the ready init seam; and the account-roster Ed25519 verify ed25519RosterVerifier / signRoster
(the crypto implementation of the core RosterVerifier seam — host-injected, so the core stays
transport-free) (plus the A2ATransport, DidResolution, SealedEnvelope, StructuredProof,
ReachabilityPlan, FriendsAgentCard, DidKeyIdentity, DidDocument types). The transports
(direct A2A / relay / git op) are injected by the host — this module does no network or git itself.
License
Available Tools
14 toolschannel_capsB
Return the capabilities of a channel (integrations, markdown, streaming, rich cards, max length).
| Name | Required | Description | Default |
|---|---|---|---|
| channel | Yes | the channel to look up |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description carries full burden. Only states 'return' without mentioning read-only nature, authentication requirements, or limitations. Minimal behavioral disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single concise sentence front-loading the action and examples. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given low complexity (one param, no output schema, no annotations), description is fairly complete. Lacks return format details, but sufficient for a simple retrieval tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. Description adds list of example capabilities but does not clarify channel format (ID vs name). Adequate but not enriched.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states the tool returns channel capabilities, with specific examples (integrations, markdown, etc.). Distinguishes from sibling tools, none of which are for channel capabilities.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this tool vs alternatives. No context on prerequisites or when not to use it. Sibling tools exist but no differentiation provided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
describe_trustA
Explain the trust context for a friend: level, basis (direct/shared_group/unknown), what it permits and constrains.
| Name | Required | Description | Default |
|---|---|---|---|
| friendId | Yes | friend uuid or name | |
| channel | Yes | the channel/sense for the explanation | |
| isGroupChat | No | set to 'true' when the conversation is a group chat |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Without annotations, the description carries full burden. It states the read operation and output details but lacks disclosure on auth needs, side effects, or error behavior. Adequate but not comprehensive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single well-structured sentence, front-loaded with verb, no wasted words. Efficiently communicates purpose and output.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema, so description compensates by detailing return values (level, basis, permits/constrains). Basis includes possible values. Missing error handling mention but still fairly complete for a read tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers all 3 parameters with descriptions. The description adds no extra meaning to parameters beyond what schema provides. Baseline 3 justified as schema does the work.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it explains trust context with specific elements (level, basis, permissions/constraints). It distinguishes from sibling tools like set_trust (write) and get_friend (general info).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies reading trust context but provides no explicit guidance on when to use over alternatives like set_trust or channel_caps. No exclusions or when-not-to-use mentioned.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_friendA
Fetch a single friend record by uuid or by name.
| Name | Required | Description | Default |
|---|---|---|---|
| friendId | Yes | friend uuid or name |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It implies a read-only operation ('Fetch') but does not disclose error behavior, authentication needs, or what happens if the friend is not found. For a simple fetch, this is adequate but minimal.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, front-loaded sentence with no wasted words. Every part earns its place: verb, resource, and lookup method.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (one required parameter, no output schema, no nested objects), the description is complete enough. It fully informs the agent of the tool's purpose and usage.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides 100% coverage with a clear description for 'friendId' ('friend uuid or name'). The tool description adds no extra semantic meaning beyond what the schema already states, so the baseline score of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses the specific verb 'Fetch' and resource 'friend record', explicitly stating the two lookup methods (by uuid or by name). This clearly distinguishes it from the sibling tool 'list_friends', indicating a single-record retrieval.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly indicates when to use the tool (to fetch a single friend by uuid or name). While it doesn't explicitly state when not to use it, the sibling 'list_friends' provides implicit context for multiple records, so the usage guidance is clear but not exhaustive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
link_identityA
Link an external identity to a friend, merging any orphan record that already holds it (cross-channel unification).
| Name | Required | Description | Default |
|---|---|---|---|
| friendId | Yes | friend uuid or name | |
| provider | Yes | ||
| externalId | Yes | ||
| tenantId | No | optional tenant id |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description bears the full burden of behavioral disclosure. It adds the merging behavior, which goes beyond a simple link. However, it does not specify side effects like data loss, permissions required, or what happens to the orphan record after merging.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence of 14 words, immediately stating the purpose and key behavior. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema and moderate parameter documentation, the description covers the core operation and merging behavior adequately for basic understanding. However, it lacks details about the 'orphan record' concept, potential destructive effects, and permissions, which are important for safe invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 50% (friendId and tenantId have descriptions; provider and externalId lack descriptions). The tool description adds high-level context that parameters are for linking identities, but does not clarify valid values for provider or format for externalId. This is a baseline score given partial coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Link') and resource ('external identity to a friend'), and mentions a distinguishing behavior ('merging any orphan record that already holds it (cross-channel unification)'). This clearly differentiates from sibling tools like unlink_identity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides no explicit guidance on when to use this tool versus alternatives such as unlink_identity. It implies a use case ('cross-channel unification') but does not state prerequisites or when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_friendsB
List friend records, optionally filtered by trust level and kind, optionally limited.
| Name | Required | Description | Default |
|---|---|---|---|
| trust | No | filter by trust level: family/friend/acquaintance/stranger | |
| kind | No | filter by kind: human/agent | |
| limit | No | max number of records to return |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
There are no annotations, so the description bears full responsibility for behavioral disclosure. It only states the action and optional filters, omitting details like default behavior when no filters are applied, pagination, permissions, or output format. This leaves significant gaps for an AI agent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence of 13 words, front-loading the core action and parameters. Every word is necessary, and there is no redundancy or fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description should clarify the return value (e.g., list of full friend records). It does not. Additionally, it fails to explain how filters interact (and, or) or what the default limit is, leaving the tool underspecified for reliable use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so baseline is 3. The description adds no new parameter meaning beyond the schema—it merely restates the optional filters and limit without providing context on defaults, interdependencies, or examples.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('list'), a clear resource ('friend records'), and optional filters (trust, kind, limit), which clearly communicates the tool's purpose and differentiates from siblings like 'get_friend' that likely retrieves a single record.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is provided on when to use this tool versus alternatives (e.g., get_friend, set_trust, search tools). The description implies usage only by stating the action, leaving the agent to infer context from the tool name and siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
onboard_agentA
Upsert an agent-peer friend record from already-resolved coordinates (no HTTP card fetch).
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | the peer agent's name | |
| agentId | Yes | the a2a agent id | |
| trustLevel | No | trust level (default acquaintance) | |
| a2a | No | a2a coordinates { cardUrl?, endpointUrl?, protocolVersion? } | |
| bundleName | No | optional bundle name |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It correctly indicates the tool performs an upsert operation, but does not detail side effects, authorization needs, or what happens to existing records. The prerequisite of resolved coordinates is mentioned, but other behavioral traits are absent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence that is front-loaded with the core action and includes a critical constraint. Every word is purposeful, and there is no redundant or unnecessary information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
While the description covers the main purpose and a key constraint, it lacks information on return values (no output schema exists) and does not provide guidance on when to use this tool versus siblings like link_identity or set_trust. Given the tool's complexity (5 params, nested object), the description is adequate but has clear gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 100% description coverage for all 5 parameters, so the baseline is 3. The description adds context by specifying that the a2a coordinates are 'already-resolved' and that no HTTP card fetch occurs, which clarifies the expected state of the 'a2a' parameter beyond the schema's structural definition.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses the specific verb 'Upsert' and clearly identifies the resource as an 'agent-peer friend record'. It also distinguishes the tool from siblings by stating 'from already-resolved coordinates (no HTTP card fetch)', which clarifies its scope and differentiates it from tools like resolve_party that might fetch coordinates.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage when coordinates are already resolved, but does not explicitly state when to use or avoid using this tool. No alternative sibling tools are mentioned, and there are no clear exclusions or prerequisites beyond the implied 'no HTTP card fetch'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
record_interactionB
Record a turn with a friend: accumulate token usage and/or append a shared-mission outcome (bumping familiarity).
| Name | Required | Description | Default |
|---|---|---|---|
| friendId | Yes | friend uuid or name | |
| usage | No | token usage; only output_tokens is counted | |
| outcome | No | shared-mission outcome { missionId, result, note? } | |
| familiarityDelta | No | how much to bump familiarity (default 1) | |
| provenance | No | optional attribution for the outcome |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It explains the tool performs mutations (accumulate, append, bump) but does not disclose side effects, required permissions, idempotency, or whether the operation is reversible. It provides minimal transparency beyond stating the actions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence with no wasted words, front-loading the main purpose. However, it could be more structured (e.g., separating the two main actions) for improved readability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Adequate for a recording tool but missing explanations of return values (no output schema), error conditions, and prerequisites (e.g., friend must exist). The description is not fully complete given the tool's complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with each parameter described. The description adds overall context ('accumulate token usage', 'shared-mission outcome') but does not provide new semantic details beyond what the schema already offers. Baseline 3 due to high coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'Record' and the resource 'a turn with a friend', specifying three distinct actions: accumulate token usage, append a shared-mission outcome, and bump familiarity. This differentiates it from sibling tools like save_note or set_trust.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit guidance on when to use this tool versus alternatives. The description does not mention when not to use it or which sibling tools might be more appropriate for related tasks (e.g., save_note for notes).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resolve_partyA
Resolve an external identity (provider + externalId on a channel) into a friend record, creating one on first contact. Returns { friend, channel, created }.
| Name | Required | Description | Default |
|---|---|---|---|
| provider | Yes | identity provider, e.g. aad, local, teams-conversation, imessage-handle, email-address, a2a-agent | |
| externalId | Yes | the external identity within the provider | |
| displayName | No | display name for the party (use 'Unknown' if not known) | |
| channel | No | the channel/sense the session belongs to, e.g. cli, teams, mcp | |
| tenantId | No | optional tenant id |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses the key behavior: creation on first contact and return format. No annotations are provided, so the description carries full burden. It could be improved by noting idempotency or error conditions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise, containing only two sentences that front-load the core action and return structure. Every sentence is informative and there is no wasted text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 5 parameters (2 required), no output schema, and no annotations, the description provides sufficient context: what it does, what it returns, and the key creation behavior. It could be more explicit about the case when the friend already exists.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the description adds little beyond summarizing the parameters. It mentions 'provider + externalId on a channel' but does not provide additional context beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'resolve' and the resource 'external identity into a friend record', with the specific behavior of creating on first contact. This distinguishes it from siblings like 'get_friend' which requires an existing record.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the tool should be used when you have an external identity and need a friend record, creating if necessary. However, it does not explicitly state when not to use it or compare to alternatives like 'get_friend' or 'link_identity'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
save_noteB
Save a friend's name, a tool preference, or a general note. Use override='true' to overwrite an existing value.
| Name | Required | Description | Default |
|---|---|---|---|
| friendId | Yes | friend uuid or name | |
| type | Yes | what to save | |
| key | No | key for tool_preference or note | |
| content | Yes | the value to save | |
| override | No | set to 'true' to overwrite an existing value | |
| provenance | No | optional attribution { assertedBy: { agentId, agentName } } |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full burden. It discloses the overwrite behavior via override parameter but does not explain error cases, permissions, or what happens if override is false (likely creates new). For a mutation tool, more transparency is needed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with core purpose and a usage hint. No fluff; every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 6 parameters (including nested objects), no output schema, and no annotations, the description is too brief. It lacks explanation of parameter interdependencies, return values, and behavioral semantics like what constitutes an 'existing value'.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with clear parameter descriptions. The description adds overall purpose but no additional semantic detail beyond the schema. It does not clarify conditional parameter relationships (e.g., when key is needed).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'save' and the resource types: 'a friend's name, a tool preference, or a general note'. This distinguishes it clearly from sibling tools like 'link_identity' or 'set_trust'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies when to use (to save various types of data) but provides no guidance on when not to use or alternatives among the 13 siblings. The hint about override='true' is useful but limited.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_trustB
Set a friend's trust level (also mirrors it onto the record's role).
| Name | Required | Description | Default |
|---|---|---|---|
| friendId | Yes | friend uuid or name | |
| trustLevel | Yes | the trust level to set |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds value by revealing the side effect of mirroring onto the record's role, which is not obvious from the schema. However, with no annotations, it omits important details like reversibility, required permissions, or response behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, clear sentence with no extraneous words. It efficiently conveys the core action and a notable side effect.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity and full schema coverage, the description is mostly complete. It explains the primary action and the side effect, though it could note that it overwrites the current trust level or that it is a mutation operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description does not add specific meaning to the parameters beyond what the schema provides; it only explains the overall effect of setting the trust level.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action ('Set') and the target ('a friend's trust level') and mentions the side effect of mirroring. However, it does not explicitly distinguish this tool from siblings like 'describe_trust' which might be for reading trust levels.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is provided on when to use this tool versus alternatives, or any prerequisites or caveats. The description is purely behavioral, leaving the agent to infer usage context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unlink_identityC
Remove an external identity from a friend.
| Name | Required | Description | Default |
|---|---|---|---|
| friendId | Yes | friend uuid or name | |
| provider | Yes | ||
| externalId | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description must fully disclose behavior. It indicates a destructive operation ('remove') but fails to mention side effects, permission requirements, or behavior if the identity is not found.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence with no wasted words, but it is too brief to convey necessary information, making it under-specified rather than concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given three required parameters, no output schema, and no annotations, the description is insufficient. It does not explain return values, error handling, or the full scope of the operation's effects.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 33% (friendId has a description, but provider and externalId do not). The description adds no further clarification on parameter meaning, leaving two parameters completely undocumented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action 'Remove an external identity from a friend,' which is a specific verb-resource combination. However, it does not distinguish itself from its sibling tool link_identity, which performs the inverse operation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is provided on when to use this tool versus alternatives. The sibling tool link_identity exists for adding identities, but the description does not mention this contrast or any prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
upsert_groupB
Upsert shared-group participant context: link each participant to the group, promoting strangers to acquaintances.
| Name | Required | Description | Default |
|---|---|---|---|
| groupExternalId | Yes | the group's external id | |
| participants | Yes | participants [{ provider, externalId, displayName? }] |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It discloses the behavioral effect of 'promoting strangers to acquaintances,' which is a useful side-effect. However, it does not mention idempotency, potential destructive actions, permissions required, or rate limits. The 'upsert' term implies insert or update, but no explicit statement of safety.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single concise sentence that front-loads the purpose and key behavior. It contains no redundant information; every word contributes to clarity. Ideal for quick parsing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has no output schema, so the description should explain return values or confirmations. It does not. Additionally, it lacks details on error handling, default behavior for missing groups, or any side effects beyond relationship promotion. For a simple two-param tool, it covers basic purpose but misses operational completeness.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so parameters are already described structurally. The description adds semantic value by clarifying that participants are 'linked' and that their relationship status is upgraded. This goes beyond the schema's basic definitions and helps the agent understand the tool's effect.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Upsert shared-group participant context: link each participant to the group, promoting strangers to acquaintances.' The verb 'upsert' and resource 'shared-group participant context' are specific. However, 'promoting strangers to acquaintances' is slightly vague and could be more explicit about the behavior. It distinguishes from siblings like 'list_friends' or 'link_identity' by focusing on group participant management.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this tool versus alternatives. The description does not mention prerequisites, expected contexts, or when not to use it. Sibling tools exist (e.g., 'link_identity', 'record_interaction'), but no differentiation is provided. This leaves the AI agent unclear about selection criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
whoamiB
Resolve who the machine owner is and which friend record represents the self.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description must carry full burden. It states what the tool does but does not disclose behavioral traits such as idempotency, authentication needs, or side effects. For a read-like operation, this is insufficient.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence with no wasted words. Front-loaded with purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema provided, so description should explain return values. It does not mention what is returned (e.g., a friend record or identity). Incomplete for a tool with zero structured metadata.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
No parameters so baseline is 4 per instructions. Description adds no parameter info, but none is needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description uses specific verb 'resolve' and clearly identifies the resources: machine owner and self friend record. This distinguishes from siblings like get_friend (which retrieves a specific friend) and link_identity (which links identities).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this tool versus alternatives. Does not mention any prerequisites or conditions under which it should be invoked.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
14 tool updates
v0.0.1- First observed
channel_caps - First observed
describe_trust - First observed
get_friend - First observed
link_identity - First observed
list_friends - First observed
onboard_agent - First observed
record_interaction - First observed
resolve_party - First observed
save_note - First observed
set_trust - First observed
share_profile - First observed
unlink_identity - First observed
upsert_group - First observed
whoami
TDQS
Scored across 14 tools
Each tool has a clearly distinct purpose: channel capabilities, trust description, friend retrieval, identity linking, listing, onboarding, interaction recording, resolution, note saving, trust setting, profile sharing (reserved), unlink, group upsert, and self-identification. No two tools overlap in functionality.
Most tools follow a consistent verb_noun pattern (e.g., get_friend, set_trust), but channel_caps and whoami deviate slightly. Despite these minor exceptions, the naming is clear and predictable overall.
14 tools is well-scoped for a friends management server covering identity linking, trust, groups, notes, and interactions. Each tool addresses a specific need without unnecessary redundancy or bloat.
The surface covers core friend lifecycle, trust, identity linking, and group management. Minor gaps exist: no explicit delete/archive friend, no group retrieval beyond upsert, and no interaction querying. However, save_note can update friend names, and the set of tools is adequate for most agent workflows.
Maintenance
Related MCP Connectors
Trust infrastructure for AI agents. Portable reputation (JTS 0-5), agent discovery, vouching.
Agent-native notes, tasks, dev-docs, vaults, sync & handoffs. MCP + OpenAPI dual surface.
Persistent memory and knowledge management for AI agents with semantic search and 50+ tools.
Trust registry for agents: check_trust verdict A0-A4, signed fact passports, honest refusals.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceProvides a durable, Obsidian-compatible knowledge base for agents using markdown notes and wikilinks. Enables agents to store, retrieve, and interlink knowledge persistently, with tools for writing, searching, and managing a graph of notes.1 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables agents to carry portable reputation across platforms, with tools for registering agent identities, vouching, verifying trust, and managing memory.MIT
- AlicenseBqualityCmaintenanceProvides a local-first secure memory store with encrypted payloads, entity extraction, and vector persistence, exposing read, write, and delete tools with granular capability controls.4Apache 2.0

openfuse-mcpofficial
AlicenseNot gradedqualityDmaintenanceEnables AI agents to manage persistent context, send signed messages, sync with peers, and share files through plain-file-based tools.106 npm18MIT