Skip to main content
Glama

agenthold

Stop your AI agents from silently overwriting each other.

When two agents update the same value, the second write quietly destroys the first. No error, no exception, just wrong data and a system that keeps running. agenthold is an MCP server that gives agents shared, versioned state with conflict detection built in. Think of it as git for your agents' working memory.

CI PyPI version PyPI Downloads Coverage 80%+ Python 3.11+ Ruff Glama score License: MIT


The problem

When two agents update the same value at the same time, the second write silently overwrites the first. No exception is raised. The value is wrong. The system keeps running.

Without agenthold: the silent overcommit problem

Two agents read a $10,000 budget and allocate from it independently. Total committed: $15,000. The budget object never complains. This is a read-modify-write conflict: each agent's write assumes nothing changed since its read.


Related MCP server: Agent Orchestrator MCP Server

How it works

agenthold solves this with optimistic concurrency control (OCC), the same mechanism Postgres uses in UPDATE ... WHERE version = N and DynamoDB uses in conditional writes.

Every value stored in agenthold has a version number. When an agent writes, it passes the version it read. If the stored version has changed since the read, the write is rejected with a ConflictError that includes the current value. The agent re-reads, recalculates, and retries.

With agenthold: conflict-safe allocation

The losing agent detects the conflict, re-reads the real remaining budget ($2,000), and adjusts its allocation. The total committed is always exactly $10,000. Every write is tracked.

OCC is the right fit for agent workflows because:

  • Agents do work between reads and writes (network calls, LLM inference). You cannot hold a database lock across that work.

  • Conflicts are rare. Retrying once is cheaper than acquiring a lock on every read.

  • The retry logic is simple, explicit, and fully in the agent's control.


Works with any agent framework

agenthold connects via MCP (Model Context Protocol), the open standard for tool integration. Any framework that speaks MCP can use agenthold with zero glue code.

Framework

How to connect

Claude Desktop / Claude Code

Built-in: add to mcpServers config

Cursor / Continue / Windsurf

Built-in: add to MCP config

LangChain / LangGraph

langchain-mcp-adapters

CrewAI

Native mcps field on Agent

OpenAI Agents SDK

Built-in mcp_servers param

Google ADK

Built-in MCP Toolbox

AutoGen

autogen_ext.tools.mcp

PydanticAI

Native MCP integration

agenthold is not a framework. It is shared infrastructure that sits underneath your orchestration layer, the same way a database sits underneath your application. Your agents keep their existing tools and logic; agenthold adds the coordination primitive they are missing.

Not using MCP yet? agenthold also works as a Python library you can call directly from any framework. Import StateStore, call .get() and .set() with version checks, and you have conflict-safe shared state.


Architecture

graph LR
    A1["Agent 1
    LangChain, CrewAI, etc."] -->|MCP| S["agenthold
    MCP Server"]
    A2["Agent 2
    Claude, OpenAI, etc."] -->|MCP| S
    A3["Agent 3
    AutoGen, ADK, etc."] -->|MCP| S
    S --> DB[("SQLite
    WAL mode")]
    DB -->|version 3| S
    S -->|"conflict! retry"| A2

Every write carries a version number. If the stored version has changed since an agent's read, the write is rejected and the agent retries with current data. This is the same mechanism used by Postgres conditional updates and DynamoDB conditional writes.


Quick start

1. Install

pip install agenthold
# or
uv pip install agenthold

2. Add to your MCP client config

{
  "mcpServers": {
    "agenthold": {
      "command": "agenthold",
      "args": ["--db", "/path/to/state.db"]
    }
  }
}

3. Done

Agents automatically coordinate. No CLAUDE.md, no system prompt changes, no namespace design.

When an agent connects, it sees five self-documenting tools: agenthold_register, agenthold_claim, agenthold_release, agenthold_status, and agenthold_wait. The tool descriptions tell the agent when and how to use each one. Server instructions reinforce the protocol when the MCP client includes them.


Resources

agenthold identifies resources by canonical URIs. Tools accept either form:

  • Bare path (e.g. "src/main.py") — resolves against the workspace named default, or the only configured workspace if exactly one exists.

  • Explicit URI"file://<workspace>/<path>" for files, "custom://<name>" for opaque resources.

Equivalent inputs (./src/main.py, src\main.py, src//main.py, an absolute path inside the workspace) all canonicalize to the same internal URI, so two agents using different shorthands never fragment the keyspace. Path traversal (..) and dot segments (.) are rejected at the boundary.

On case-insensitive filesystems (Windows and macOS by default), file resources are matched case-insensitively, so src/Main.py and src/main.py resolve to the same resource — two agents can't both claim the same physical file. This follows the running platform; custom:// names are always case-sensitive.

Configure workspaces with --workspace name=path (repeatable). With no flag, agenthold creates a single default workspace at the current working directory. Multi-workspace setups let one agenthold process coordinate across separate codebases.


Tools

agenthold exposes five coordination tools by default.

agenthold_register

Register yourself and receive a unique agent ID. Must be called once before using agenthold_claim or agenthold_release.

{ "name": "editor-agent", "model": "claude-sonnet-4-6" }
{
  "status": "registered",
  "agent_id": "agent-a1b2c3d4",
  "name": "editor-agent",
  "registered_at": "2026-03-18T10:00:00+00:00"
}

agenthold_claim

Claim exclusive access to a resource before modifying it. Requires a registered agent_id.

{ "resource": "intro.md", "agent_id": "agent-a1b2c3d4" }

Claimed (you hold exclusive access):

{ "status": "claimed", "resource": "file://default/intro.md", "version": 1 }

If the resource has a non-trivial prior history (deleted, moved, abandoned, or expired), the response also carries previous_outcome, previous_holder, previous_outcome_at, and a hint describing what the previous holder did. For previous_outcome: "moved", moved_to is the new resource URI.

Busy (another agent is working on this resource):

{
  "status": "busy",
  "resource": "file://default/intro.md",
  "held_by": "agent-e5f6g7h8",
  "claimed_at": "2026-03-18T10:00:00+00:00",
  "hint": "Another agent holds this resource. Work on a different resource, or call agenthold_wait to be notified when it becomes available."
}

Already claimed (you already hold this claim, idempotent):

{ "status": "already_claimed", "resource": "file://default/intro.md", "version": 1 }

agenthold_release

Release your claim with an explicit outcome describing what you did. The outcome is preserved in the free-state record and shown to the next claimant so they don't act on stale assumptions. Requires a registered agent_id.

{
  "resource": "intro.md",
  "agent_id": "agent-a1b2c3d4",
  "outcome": "modified"
}
{ "status": "released", "resource": "file://default/intro.md", "version": 2, "outcome": "modified" }

Outcomes: released (default — no lifecycle claim), modified (changed in place), created (didn't exist before), deleted (no longer exists at this resource), moved (relocated — also pass moved_to with the new resource).

For renames (mv old new): claim BOTH paths, do the rename on disk, then release the source with outcome: "moved", moved_to: "new" and the destination with outcome: "created".


agenthold_status

Check whether a resource is available or currently claimed. Does not require registration.

{ "resource": "intro.md" }

Available:

{ "status": "available", "resource": "file://default/intro.md" }

If a previous holder declared a non-trivial outcome (deleted, moved, abandoned, expired), the response also includes previous_outcome, previous_holder, previous_outcome_at, and a hint. For moved, moved_to is the new resource URI.

Claimed:

{
  "status": "claimed",
  "resource": "file://default/intro.md",
  "held_by": "agent-e5f6g7h8",
  "agent_name": "editor-agent",
  "agent_model": "claude-sonnet-4-6",
  "claimed_at": "2026-03-18T10:00:00+00:00",
  "version": 3
}

agenthold_wait

Wait for a claimed resource to become available. Blocks the agent turn until the holder releases, or the timeout expires.

{ "resource": "intro.md", "timeout_seconds": 30 }

Available (resource was released):

{ "status": "available", "resource": "file://default/intro.md", "elapsed_seconds": 2.4 }

When the wait fires on a release with a non-trivial outcome, the response also carries previous_outcome, previous_holder, previous_outcome_at, optional moved_to, and a hint. An agent that was waiting to edit a moved or deleted resource learns why the path became available.

Timeout:

{
  "status": "timeout",
  "resource": "file://default/intro.md",
  "held_by": "writer-2",
  "elapsed_seconds": 30.2,
  "hint": "The resource was not released within the timeout. Try working on a different resource, or call agenthold_wait again with a longer timeout."
}

Advanced tools

For custom coordination protocols, agenthold exposes eight low-level primitives via --tools advanced:

agenthold_get · agenthold_set · agenthold_list · agenthold_history · agenthold_delete · agenthold_watch · agenthold_clear_namespace · agenthold_export

These give agents direct read/write/watch access to the versioned state store with full OCC conflict detection. No server instructions are sent in this mode.

See the full advanced tools reference →


Conflict detection

The read-modify-write pattern with expected_version is the core of agenthold. Here is the canonical retry loop:

from agenthold.store import StateStore
from agenthold.exceptions import ConflictError

store = StateStore("./state.db")

record = store.get("campaign", "budget")   # read once before doing work
do_expensive_work()                         # LLM call, API request, etc.

while True:
    new_value = compute_new_value(record.value)
    try:
        store.set(
            "campaign", "budget", new_value,
            updated_by="my-agent",
            expected_version=record.version,
        )
        break   # write succeeded
    except ConflictError:
        record = store.get("campaign", "budget")   # re-read and retry

Why this works: The version number is the contract. If the stored version has advanced since your read, another agent wrote first. You take the current value, recalculate, and try again. The number of retries is bounded by the number of concurrent writers. In practice, agents almost never conflict more than once.

Why not locks? Locks require a lease mechanism (what happens if the agent crashes holding a lock?), add latency on every read, and interact badly with the long I/O waits inherent in agent workflows. OCC pays a cost only when there actually is a conflict.


Use as a Python library

from agenthold.store import StateStore
from agenthold.exceptions import ConflictError

store = StateStore("./state.db")

# Write a value (first write, no conflict check needed)
store.set("order-1234", "status", "received", updated_by="intake-agent")

# Read it back; always get the version number too
record = store.get("order-1234", "status")
print(record.value)    # "received"
print(record.version)  # 1

# Write with conflict detection; pass the version you read
try:
    store.set(
        "order-1234", "status", "processing",
        updated_by="fulfillment-agent",
        expected_version=record.version,  # rejected if another agent wrote first
    )
except ConflictError as e:
    # Another agent wrote between your read and write.
    # e.detail has the current version, value, and who wrote it.
    record = store.get("order-1234", "status")
    # ... recalculate and retry

What it looks like in practice

In a multi-agent session, the coordination is automatic. An agent's tool calls look like this:

Agent A: agenthold_register(name="writer", model="claude-sonnet-4-6")
         → agent_id: "agent-a1b2c3d4"

Agent A: agenthold_claim(resource="chapter-3.md", agent_id="agent-a1b2c3d4")
         → status: "claimed", resource: "file://default/chapter-3.md"

Agent B: agenthold_claim(resource="chapter-3.md", agent_id="agent-e5f6g7h8")
         → status: "busy", hint: "Work on a different resource..."

Agent A: agenthold_release(resource="chapter-3.md", agent_id="agent-a1b2c3d4", outcome="modified")
         → status: "released", outcome: "modified"

No system prompt engineering. The tool descriptions guide the agents.

Worked examples

Two worked examples are included, each with a "before" and "after" script.

Order processing: two agents update the same order record concurrently:

uv run python examples/order_processing/without_agenthold.py   # silent overwrite
uv run python examples/order_processing/with_agenthold.py      # conflict detection + retry

Budget allocation: two agents draw from a shared marketing budget:

uv run python examples/budget_allocation/without_agenthold.py  # $10k budget → $15k committed
uv run python examples/budget_allocation/with_agenthold.py     # exact allocation, full audit trail

Configuration

agenthold --db ./state.db                              # standard mode (default)
agenthold --db ./state.db --tools advanced             # advanced mode
agenthold --db ./state.db --claim-ttl 1800             # standard + 30 min TTL
agenthold --workspace myproj=/abs/path                 # named workspace
agenthold --workspace a=/x --workspace b=/y            # multiple workspaces
agenthold --transport http --port 8417                 # serve over HTTP (see below)

Flag

Default

Description

--db

./agenthold.db

Path to the SQLite database file. Use :memory: for an in-process store (testing only; data is lost when the process exits).

--tools

standard

Tool set: standard (register/claim/release/status/wait) or advanced (get/set/delete/watch/list/history/clear/export).

--claim-ttl

None (no expiry)

Seconds before an inactive agent's claims expire. Only applies in standard mode. When set, claims held by agents whose last activity exceeds this value are treated as expired and can be taken by other agents. Recommended with --transport http.

--workspace

one workspace named default at the current working directory

Configure a workspace as name=path (e.g. myproj=/abs/path) or as an absolute path (name derived from the basename). Repeatable. Multiple workspaces let one agenthold process coordinate across separate codebases against the same database. Bare paths in tool inputs resolve against the workspace named default, or against the only configured workspace if exactly one exists.

--transport

stdio

stdio (one local subprocess per agent) or http (one long-lived server many agents connect to over Streamable HTTP).

--host

127.0.0.1

HTTP bind address. Only used with --transport http.

--port

8417

HTTP port. Only used with --transport http.

--path

/mcp

HTTP endpoint path the MCP transport is mounted at. Only used with --transport http.

--json-response

off

Return JSON responses instead of SSE streams over HTTP. Only used with --transport http.

--allowed-host

none

Enable DNS-rebinding protection and allow this Host header value. Repeatable. When omitted, protection stays disabled (localhost-friendly). Only used with --transport http.

--auth-token

none

Require this bearer token on every HTTP request (Authorization: Bearer <token>). Repeatable. Also settable via the AGENTHOLD_AUTH_TOKEN env var (comma-separated), which is preferred. Only used with --transport http.

The database file is created automatically on first run. Back it up like any other SQLite file.

HTTP transport

By default agenthold speaks stdio: your MCP client spawns one agenthold subprocess per agent, and those subprocesses coordinate through the shared SQLite file on the same machine. That is perfect for local, single-machine multi-agent runs.

To coordinate agents that live in different processes, containers, or hosts, run one long-lived agenthold server over Streamable HTTP and point every agent at it:

agenthold --db ./state.db --transport http --host 127.0.0.1 --port 8417 --claim-ttl 1800

Then connect MCP clients by URL instead of by command:

{
  "mcpServers": {
    "agenthold": {
      "url": "http://127.0.0.1:8417/mcp"
    }
  }
}

All five standard tools (and the advanced set, with --tools advanced) work identically over HTTP — only the transport changes. Because a single server now serves many agents over its lifetime, set --claim-ttl so a claim held by an agent that disconnects is automatically reclaimable.

Authentication

To require a bearer token on every HTTP request, set one or more tokens. Prefer the environment variable so the token doesn't appear in process listings:

AGENTHOLD_AUTH_TOKEN=s3cr3t agenthold --db ./state.db --transport http --host 0.0.0.0
# or, less privately:
agenthold --transport http --auth-token s3cr3t --auth-token another-token

Clients then send the token in the Authorization header:

{
  "mcpServers": {
    "agenthold": {
      "url": "http://your-host:8417/mcp",
      "headers": { "Authorization": "Bearer s3cr3t" }
    }
  }
}

Requests without a valid token are rejected with 401 before reaching the store. Tokens are compared in constant time. Authentication applies to --transport http only (stdio is a trusted local transport). This release is all-or-nothing authentication; per-namespace scoping is on the roadmap.

The AGENTHOLD_AUTH_TOKEN value is split on commas, so a token itself must not contain a comma (use repeated --auth-token for such tokens). If authentication is requested but every token is blank, the server refuses to start rather than serving without auth.

Security. Bearer tokens are only as private as the transport carrying them — put TLS in front of agenthold (a reverse proxy) for any real deployment. The default bind address is 127.0.0.1 (localhost only); binding beyond localhost without --auth-token prints a startup warning. Pass --allowed-host <host> (repeatable) to enable DNS-rebinding (Host-header) protection — note this is not authentication and does not replace --auth-token.

Long-lived servers. agenthold does not yet reap idle agent records, so a server that runs for a very long time with high agent churn will accumulate agent metadata. Set --claim-ttl (so disconnected agents' claims recover) and restart periodically if needed. Automatic reaping is on the roadmap.


Inspecting the store

An agenthold database is just a SQLite file, but you rarely want to open it by hand. The agenthold command doubles as a read-only inspector so you can see what agents are doing at a glance — no MCP client required. These commands are strictly read-only (the database is opened with query_only and no schema writes), so they never modify the store — even if you point --db at the wrong file.

agenthold agents --db ./state.db          # who is registered
agenthold claims --db ./state.db          # what is currently claimed, and by whom
agenthold claims --db ./state.db --all    # also show freed/moved/deleted claims
agenthold namespaces --db ./state.db      # namespaces + record counts
agenthold keys orders --db ./state.db     # keys in a namespace
agenthold history orders order-1 --db ./state.db   # a key's version history

Example:

$ agenthold claims --db ./state.db
RESOURCE                    STATE    BY            SINCE
--------------------------  -------  ------------  ------
custom://chapter-1          claimed  editor-agent  2s ago
file://default/src/main.py  claimed  review-bot    2s ago

Every command accepts --db PATH (default ./agenthold.db) and --json for machine-readable output. A bare agenthold (no subcommand) still starts the MCP server exactly as before — the inspector is purely additive.


Development

git clone https://github.com/edobusy/agenthold.git
cd agenthold
uv sync --all-extras --dev

Run the tests:

uv run pytest tests/ -v

Check coverage:

uv run pytest tests/ --cov=agenthold --cov-report=term-missing

Lint and type-check:

uv run ruff check src/ tests/
uv run ruff format src/ tests/
uv run mypy src/

CI runs on Python 3.11 and 3.12 on every push to main. See CONTRIBUTING.md for detailed guidelines.


Why SQLite? SQLite is the right tool for this scope. It is zero-dependency, ships in the Python stdlib, and runs everywhere. WAL mode is enabled so that read-only operations (exports, watches) do not block writers across processes. Write transactions use BEGIN IMMEDIATE to acquire the write lock upfront, ensuring OCC conflict detection works correctly even when multiple agenthold processes share the same database file. busy_timeout is set to 5 seconds so a second writer waits rather than failing immediately. Postgres adds an ops dependency with no benefit at this scale. The storage backend is behind a clean interface (StateStore) that can be swapped for Postgres when the need arises. Choosing a simple tool deliberately is not a limitation.

Why OCC instead of pessimistic locking? Locks require the holder to release them, which means the system must handle crashes, timeouts, and stale holders. That complexity is not worth it when conflicts are rare. OCC pays a cost only when a conflict actually occurs: one extra read and one retry. For multi-agent workflows where agents do significant work between reads and writes (LLM inference, API calls, tool execution), OCC is the correct choice.

What the versioning guarantees: Each key has a version that starts at 1 and increments by exactly 1 on every write. The state_history table is append-only and records every write before the live record is updated, so a crash between the two writes leaves history consistent. Deletions also write a tombstone entry to state_history (with event_type: "delete") before removing the live record, so the full lifecycle of a key is visible in history. The ordering guarantee is per-key, not global; two different keys can have their versions updated in any order.

What would change for production scale: Mainly one thing: replace SQLite with Postgres for better concurrent write throughput, replication, and managed hosting. The StateStore interface is already designed to make this a contained change. The network transport and authentication already exist — agenthold serves Streamable HTTP via the MCP SDK's StreamableHTTPSessionManager (so remote agents connect over the network instead of a local process), and --auth-token gates access with a bearer token (see Authentication). Put TLS in front via a reverse proxy for production, and note that per-namespace/tenant scoping of tokens is not yet implemented.


License

MIT. See LICENSE.


mcp-name: io.github.edobusy/agenthold

Available Tools

5 tools
agenthold_claimA

Claim exclusive access to a resource before modifying it. IMPORTANT: You MUST call this before editing any file or shared resource when other agents may be working in the same environment. Do not proceed with modifications until the claim is granted. Claim each resource right before you modify it — do not claim multiple resources in advance. Finish editing and release one resource before claiming the next. You must call agenthold_register first to get an agent_id. Pass the filename as the resource identifier (e.g. "intro.md", "src/main.py"). Possible responses: "claimed": You now hold exclusive access. Proceed with your edits, then call agenthold_release when done. "already_claimed": You already hold this claim. Safe to proceed. "busy": Another agent is working on this resource. Do NOT modify it. Work on a different resource, or call agenthold_wait to be notified when it becomes available. The response includes who holds the claim and when they claimed it.

ParametersJSON Schema
NameRequiredDescriptionDefault
resourceYesIdentifier for the resource, e.g. a filename like 'intro.md' or 'src/main.py'
agent_idYesYour agent ID, received from agenthold_register. You must register before calling this tool.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden and comprehensively discloses behavioral traits: it explains the concurrency control mechanism (exclusive access), required preconditions (registration), usage patterns (claim right before modification, release after), and detailed response handling (claimed, already_claimed, busy with actions for each).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded, starting with the core purpose and importance, followed by usage rules and response details. Every sentence earns its place by providing critical information without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (concurrency control with no annotations and no output schema), the description is complete: it covers purpose, usage, parameters, behavioral outcomes, and integration with siblings, providing all necessary context for an agent to use it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description adds value by providing context for the 'resource' parameter (e.g., 'filename as the resource identifier' with examples like 'intro.md'), reinforcing the schema's description, but doesn't significantly enhance the 'agent_id' parameter beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verbs ('claim exclusive access to a resource before modifying it') and distinguishes it from siblings by specifying it's for claiming resources before editing, unlike agenthold_register (for registration), agenthold_release (for releasing), agenthold_status (for checking status), and agenthold_wait (for waiting).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides explicit when-to-use guidance ('call this before editing any file or shared resource when other agents may be working in the same environment'), when-not-to-use ('do not claim multiple resources in advance'), and alternatives ('work on a different resource, or call agenthold_wait'), with clear prerequisites ('must call agenthold_register first').

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

agenthold_registerA

Register yourself and receive a unique agent_id. IMPORTANT: You MUST call this once before using any other agenthold tool that requires an agent_id. Pass your name (e.g. 'editor-agent') and optionally the model you are running on (e.g. 'claude-sonnet-4-6'). The returned agent_id is your identity for this session — use it in all subsequent agenthold_claim and agenthold_release calls. Do not call this more than once per session.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesA short descriptive name for your agent, e.g. 'editor-agent' or 'review-bot'.
modelNoThe model you are running on, e.g. 'claude-sonnet-4-6'. Optional.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden and does well by explaining the session-based nature ('for this session'), the one-time requirement, and that the returned agent_id serves as identity for subsequent operations. It doesn't cover error conditions or rate limits, but provides solid behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core purpose, followed by critical usage instructions. Every sentence earns its place: registration purpose, prerequisite warning, parameter guidance, session identity explanation, and usage restriction. No wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a registration tool with no annotations and no output schema, the description does well by explaining the session-based workflow, one-time requirement, and relationship to sibling tools. It could mention what happens on registration failure or the format of the returned agent_id, but covers the essential context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already fully documents both parameters. The description provides examples ('e.g. 'editor-agent'', 'e.g. 'claude-sonnet-4-6'') that match the schema descriptions, adding minimal value beyond what's already structured. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Register yourself'), the resource ('receive a unique agent_id'), and distinguishes this from sibling tools by explaining it's a prerequisite for other agenthold tools. It goes beyond just restating the name to explain the registration function.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use ('call this once before using any other agenthold tool that requires an agent_id'), when not to use ('Do not call this more than once per session'), and mentions specific alternatives ('agenthold_claim and agenthold_release calls'). Provides clear sequencing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

agenthold_releaseA

Release your exclusive claim on a resource after finishing your edits. IMPORTANT: You MUST call this when done modifying a resource. Holding claims longer than necessary blocks other agents. If you claimed a resource but decided not to modify it, release it anyway. The release immediately notifies any agents waiting via agenthold_wait. Possible responses: "released": Claim released successfully. Other agents can now claim the resource. "already_free": The resource was already free. No action needed. "not_found": The resource was never claimed. No action needed. "error": You tried to release a resource claimed by a different agent.

ParametersJSON Schema
NameRequiredDescriptionDefault
resourceYesIdentifier for the resource, e.g. a filename like 'intro.md' or 'src/main.py'
agent_idYesYour agent ID, received from agenthold_register. You must register before calling this tool.

TDQS

A4.3/5.0
Behavior4/5

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 effectively describes key behaviors: the tool releases a claim, notifies waiting agents via 'agenthold_wait', and includes possible responses with their meanings (e.g., 'released', 'already_free'). However, it lacks details on error handling beyond the 'error' response, such as retry logic or timeouts, which could be useful for an agent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded, starting with the core action and importance. Each sentence adds value: the first states the purpose, the second emphasizes necessity, the third explains consequences, the fourth covers edge cases, and the fifth details responses. However, the response explanations could be slightly more concise, as they list multiple outcomes without grouping them efficiently.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of a coordination tool with no annotations and no output schema, the description is largely complete. It covers purpose, usage, behaviors, and responses, which compensates for the lack of structured output. However, it could improve by explicitly mentioning the tool's role in the sibling ecosystem (e.g., linking to 'agenthold_claim' for context) or detailing potential side effects more thoroughly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents both parameters ('resource' and 'agent_id') with clear descriptions. The description adds no additional parameter-specific information beyond what the schema provides, such as examples or constraints. Thus, it meets the baseline of 3 by not compensating but not detracting from the schema's coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('release your exclusive claim on a resource') and the resource type ('a resource'), distinguishing it from siblings like 'agenthold_claim' (acquire claim) and 'agenthold_wait' (wait for claim). It explicitly mentions the verb 'release' and the context of finishing edits, making the purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance on when to use this tool: 'when done modifying a resource' and 'if you claimed a resource but decided not to modify it, release it anyway.' It also specifies prerequisites ('You must register before calling this tool') and explains the consequences of misuse ('Holding claims longer than necessary blocks other agents'), offering clear when-to-use and when-not-to-use instructions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

agenthold_statusA

Check if a resource is available or currently claimed by another agent. Use this to decide which resource to work on next when you have multiple options. If the resource is available, call agenthold_claim to secure it before modifying. If claimed by another agent, work on a different resource or call agenthold_wait. Possible responses: "available": The resource is free. Call agenthold_claim to secure it before editing. "claimed": Another agent holds this resource. The response tells you who and when.

ParametersJSON Schema
NameRequiredDescriptionDefault
resourceYesIdentifier for the resource, e.g. a filename like 'intro.md' or 'src/main.py'

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It effectively discloses key behavioral traits: it's a read-only status check (implied by 'check'), describes the two possible response states with their meanings, and provides actionable next steps. It doesn't mention rate limits or error conditions, but covers the essential operational context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficiently structured with three sentences that each earn their place: purpose statement, usage context, and response interpretation. It's front-loaded with the core function and contains zero wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter tool with no annotations and no output schema, the description provides excellent context: clear purpose, usage guidelines with sibling references, behavioral expectations, and response interpretation. The only minor gap is lack of explicit error case handling, but it's otherwise complete for this complexity level.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents the single 'resource' parameter. The description doesn't add any parameter-specific information beyond what's in the schema (e.g., no additional examples or constraints). Baseline 3 is appropriate when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verbs ('check if a resource is available or currently claimed') and identifies the resource type. It distinguishes from siblings by focusing on status checking rather than claiming, registering, releasing, or waiting.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance on when to use this tool ('to decide which resource to work on next when you have multiple options') and what to do based on the outcome (call agenthold_claim if available, work on different resource or call agenthold_wait if claimed). It clearly distinguishes from sibling tools by naming alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

agenthold_waitA

Wait for a resource to become available. Blocks your turn until the current holder releases their claim, or the timeout expires. IMPORTANT: This call holds your agent turn until it returns — no other actions can be taken while waiting. Only use this when you need a specific resource and no other useful work can proceed without it. Pass a reasonable timeout (default 30 seconds). On timeout, the hint field suggests next steps. Possible responses: "available": The resource is now free. Call agenthold_claim immediately to secure it — another agent may also be waiting. "timeout": The resource was not released within the timeout. The response includes who still holds the claim.

ParametersJSON Schema
NameRequiredDescriptionDefault
resourceYesIdentifier for the resource, e.g. a filename like 'intro.md' or 'src/main.py'
timeout_secondsNoMaximum seconds to wait (default 30). On timeout, the response includes who still holds the claim.

TDQS

A4.4/5.0
Behavior4/5

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 effectively describes key behaviors: it blocks the agent's turn (no other actions possible), includes timeout handling with default values, explains response outcomes ('available' and 'timeout'), and warns about concurrency risks ('another agent may also be waiting'). It does not detail error cases or retry logic, but covers the core operational traits well.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded, starting with the core purpose. Each sentence adds critical information (blocking behavior, usage warning, timeout details, response outcomes) without redundancy. It efficiently conveys necessary details in a structured manner.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (blocking wait with concurrency) and lack of annotations or output schema, the description does a strong job covering key aspects: purpose, usage, behavior, and outcomes. It explains the two possible responses but does not detail the response structure or error handling, leaving some gaps for a tool with no output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already fully documents both parameters (resource identifier and timeout with default). The description adds minimal value beyond the schema, only reiterating the timeout default and hint field on timeout. It does not provide additional semantic context or usage examples for parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verbs ('wait for a resource to become available', 'blocks your turn') and distinguishes it from siblings by focusing on waiting rather than claiming, registering, releasing, or checking status. It explicitly mentions the resource constraint and timeout mechanism.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance on when to use this tool ('Only use this when you need a specific resource and no other useful work can proceed without it') and when not to use it (implied by the warning about blocking). It also references the sibling tool agenthold_claim as the next step after availability, offering clear alternatives in the workflow.

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. Dates show when Glama detected each change.

  1. 5 tool updatesv0.4.3
    • First observedagenthold_claim
    • First observedagenthold_register
    • First observedagenthold_release
    • First observedagenthold_status
    • First observedagenthold_wait

TDQS

A4.6/5.0
Disambiguation5/5

Each tool has a clearly distinct purpose: register sets up identity, claim acquires exclusive access, release relinquishes it, status checks availability, and wait blocks for availability. There is no overlap or ambiguity between these functions, making it easy for an agent to select the right tool for each step in the coordination workflow.

Naming Consistency5/5

All tools follow a consistent 'agenthold_' prefix with descriptive action names (register, claim, release, status, wait) in snake_case. This pattern is uniform across all five tools, providing predictability and readability without any deviations or mixed conventions.

Tool Count5/5

With 5 tools, the server is well-scoped for its purpose of agent coordination and resource locking. Each tool earns its place by covering essential operations: setup (register), acquisition (claim), release (release), checking (status), and waiting (wait). This count is neither too sparse nor bloated, fitting the domain perfectly.

Completeness5/5

The tool set provides complete coverage for the agent coordination domain, covering the full lifecycle from registration to resource management. There are no gaps: agents can register, claim, check status, wait, and release resources, enabling seamless workflow without dead ends or missing critical operations.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/edobusy/agenthold'

If you have feedback or need assistance with the MCP directory API, please join our Discord server