@hasna/agent-registry
v0.1.0
Published
One canonical, persistent implementation of the agent-lifecycle MCP tools (register_agent, heartbeat, set_focus, list_agents, send_feedback) exposed as a single registerAgentTools(server). SQLite-backed via @hasna/cloud, optional @hasna/events lifecycle e
Maintainers
Readme
@hasna/agent-registry
One canonical, persistent implementation of the agent-lifecycle tools every Hasna MCP server ships:
register_agent · heartbeat · set_focus · list_agents · send_feedbackIt replaces the 43 divergent hand-rolled copies — some backed by an in-memory
Map (presence lost on restart, not listable cross-process), four different id
schemes, two incompatible identity contracts — with a single SQLite-backed core
that syncs local ⇄ cloud through @hasna/cloud
and delegates feedback to it (never reimplements it).
Install
bun add @hasna/agent-registry
# peers you already bundle:
bun add @modelcontextprotocol/sdk zod
# optional lifecycle events:
bun add @hasna/eventsOne-line service bootstrap
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerAgentTools } from "@hasna/agent-registry";
const server = new McpServer({ name: "my-service", version: "0.1.0" });
registerAgentTools(server); // register_agent, heartbeat, set_focus, list_agents, send_feedbackBy default this persists to ~/.hasna/agent-registry/agent-registry.db (via
@hasna/cloud's SqliteAdapter). Pass your own handle to share a database:
import { SqliteAdapter } from "@hasna/cloud";
const db = new SqliteAdapter("/path/to/service.db");
registerAgentTools(server, {
db,
service: "my-service", // used for feedback delegation
events: myEventsClient, // optional @hasna/events client
includeExtendedTools: true, // get_focus / unfocus / rename_agent / remove_agent
toolFilter: (name) => name !== "send_feedback",
});Options (AgentToolsOptions)
| Option | Default | Meaning |
| --- | --- | --- |
| db | shared local SQLite | Injected DbAdapter (from @hasna/cloud) |
| service | "agent-registry" | Service name for feedback delegation |
| events | – | Optional @hasna/events client (best-effort lifecycle emit) |
| agentFocus | – | Host-shared Map updated on set_focus/unfocus |
| includeFeedback | true | Register send_feedback (delegated to @hasna/cloud) |
| includeExtendedTools | false | Add the conversations-style superset |
| toolFilter | – | (name) => boolean to skip specific tools |
Programmatic core
The store is a pure, DbAdapter-injectable module (@hasna/agent-registry/store):
import { SqliteAdapter } from "@hasna/cloud";
import {
ensureAgentsTable, registerAgent, heartbeat, setFocus,
listAgents, getAgent, getAgentByName, resolveAgent,
releaseAgent, archiveAgent, autoReleaseStaleAgents, isAgentConflict,
} from "@hasna/agent-registry/store";
const db = new SqliteAdapter(":memory:");
ensureAgentsTable(db);
const result = registerAgent({ name: "brutus", session_id: "sess-1" }, db);
if (isAgentConflict(result)) {
// structured descriptor — never thrown
console.log(result.message, result.existing_id);
}Canonical decisions
- Persistence — always SQLite via an injected
DbAdapter; theagentstable participates in@hasna/cloud'ssyncPush/syncPulllike every other table (no bespoke sync, no in-memory variants). - Id —
ag_+crypto.randomUUID().slice(0, 8)(greppable, sync-safe). - Resolution — accepts either
agent_idornameeverywhere, plusHASNA_AGENT_SESSION_IDenv auto-detect. - Conflict policy — 30-min active window (
HASNA_AGENT_TIMEOUT_MSto override),forcetakeover, same-session heartbeat, stale takeover; returns a structuredAgentConflictError { conflict: true, ... }— never throws. set_focus— persistsactive_project_idand updates an optional in-process focusMap.send_feedback— delegated to@hasna/cloud'ssendFeedback, not reimplemented.
CLI
agent-registry list [--online] [--archived] [--json]
agent-registry register <name> [--session <id>] [--role <role>] [--project <id>] [--force]
agent-registry heartbeat <id|name>
agent-registry focus <id|name> [<project_id>]
agent-registry remove <id|name>Environment
Canonical prefix is HASNA_AGENT_REGISTRY_; the shorter HASNA_AGENT_ is
accepted as an alias.
| Var | Default | Meaning |
| --- | --- | --- |
| HASNA_AGENT_REGISTRY_TIMEOUT_MS | 1800000 | Active window before an agent is stale |
| HASNA_AGENT_REGISTRY_SESSION_ID | – | Auto-detected session id for resolution |
| HASNA_AGENT_REGISTRY_AUTO_RELEASE | – | true enables autoReleaseStaleAgents() |
License
Apache-2.0
