Skip to main content
Glama

data_claim_next

Atomically claim the NEXT unprocessed row of a datastore (work queue).

Claim with empties="sentinel" when you will re-send a list: an assumed empty (written @empty, its reason in comment) then comes back "@empty" and, sent back as is, stays one; sent back as "" it becomes an ordinary empty, refused on a required field. Never copy @empty into a deliverable.

⚠️ Your filter MUST name a column your processing WRITES. That is the one condition which makes the queue EMPTY, and nothing enforces it. The order: rows never served first, then the least recently served — a released row goes behind every fresh row, but it comes back once they are served, for as long as it still matches your filter.

⚠️ How to see a queue running empty: the row carries _claims. Above 1 it means « you have already been served this row and it was not written » — that is the signature. Stop and re-read your filter; claiming again will not help.

⚠️ A claim lives in a run. Call run_start first and pass its run_id as _run_id= on this call: without an active run the claim is REFUSED and nothing is reserved — a row held outside a run could not be written, so you would research it for nothing.

Write your result and release it by the _id of the returned row.

⚠️ Address the table by its NUMBER, not its name: the reply carries ns_id (e.g. 174), the form to pass as datastore in every following call — data_write(datastore=174, id=…), data_release(datastore=174, …).

The primitive for draining a table with N parallel (sub-)agents without collisions: picks, among rows whose claim lease is free or expired, the one served least recently (never-served rows first, then creation order), stamps _claimed_by/_claimed_until and returns it — two concurrent workers never get the same row. Returns {row: null} when nothing is left to claim.

Filter on column A, write into column B, and the queue never empties: once the fresh rows are served, your own finished rows come back, round after round. Measured on a 3 766-row table, when the order was still « oldest first »: three workers filtered on a column the processing never touched. 834 fresh rows were never reached, and one worker claimed the SAME row seven times. Every call succeeded and no error was raised — a livelock, not a failure, and you cannot see it from inside. Rows claimed again and again without a write end up set aside (the ceiling below) — LOST to the pass, without being at fault.

A name still resolves as datastore until 2026-11-08 — then it is refused — but the number is what to carry: it survives a rename, it is unique where a name is only unique per owner, and it is what the platform records. datastore in the reply is the table's REAL name whatever form you passed in, so reading "600" back from a claim on 600 no longer happens.

worker is a label YOU choose and REUSE verbatim on data_release — the guard so one agent cannot release another's claim. filter (exact {col: val}, e.g. {"status": "nouveau"}) selects what counts as claimable; it narrows the table's declared lifecycle.claimable, never widens it. The claim does NOT change the row: write your progress via data_write (id=…), then release it — writing a "final" status does not free it (#317). Release the row with data_release if you have it; otherwise finishing your run (run_finish) releases it. Never write your intent into the row (no _action/_liberation columns). The lease (lease_s, default 900s) only covers a worker that died. While you hold a row, nobody else can write it.

Multi-pass table (lifecycle.advance declared): claim by the state of YOUR pass (filter={"statut": "societe"}), write your findings, release — the platform moves the row to the next state. Do not write that state yourself.

The row carries _claims = how many times it has been claimed since the last successful write. A row claimed over and over WITHOUT a write is a queue running empty: past the ceiling — lifecycle.max_claims declared on the table, else a platform default of 3; max_claims here can only RAISE it for this pass — the server sets it aside: it stamps _abandon with the reason, ending with what the LAST attempt hit (the job's error, a refused write, the tools in error, or no write attempted) and _abandon_run with that attempt's run, moves it to lifecycle.abandon_state when the table declares a ceiling (with the default one its status is left untouched), and STOPS serving it — whatever your filter says. It stays readable and repairable: an explicit data_write puts it back in the queue and resets the counter.

⚠️ layers="nested" gives the row back in the SHAPE YOU WRITE — a cell that carries layers comes as {"valeur": …, "comment": …} instead of the flat pair champ + champ.comment. ⚠️ It stays a READ: do NOT send this row back. Write only the fields you established — and never <field>.origine, which is read here and set by the platform, never by an agent. The flat form shows a key with a DOT, and a dot does not look like a field name: agents turn effectif.comment into effectif_comment, which creates a ghost column — or, on a table that refuses unknown columns, loses the whole row. This is the only read that feeds a WRITE loop, so it is the one where the shape matters.

Every column DECLARED in the schema is on the row, null when no value is in place — null means "nothing here yet": find it if your task needs it, never make it up. Sending such a null back changes nothing (a null only erases a value that is in place); still, write only the fields you established.

filters is the LIST form — [{field, op, value}], ANDed with filter. Use it when one column needs TWO bounds: filter accepts a single operator per column, so a range (score >= 10 AND score <= 20) can only be expressed here. Same grammar as data_rows.

datastore also accepts slot:<name> (table bound by the active project).

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "datastore": "<datastore>",
      "worker": "<worker>"
    },
    "name": "data_claim_next"
  }
}

Input Schema

NameRequiredDescriptionDefault
_orgNoOrg (id) de CET APPEL. `oto_list_orgs()`.
_groupNoÉquipe (id) de CET APPEL, sous son org parente.
filterNo
layersNoshape of a cell that carries layers. `flat` (default): each filled layer beside the value (`email.comment`). `nested`: `{"valeur": …, "comment": …}`, the shape you write. Any other value is refused.flat
workerYes
_run_idNoOBLIGATOIRE à chaque appel dès `run_start` ou `data_claim_next` : sans lui, l'écriture sur une ligne réservée est refusée. Hérité seulement si le serveur tient un run actif — n'y compte pas.
emptiesNohow an assumed empty (written `@empty`) comes back. `plain` (default): `""`, like any empty cell. `sentinel`: `"@empty"`, the word that writes it. Any other value is refused.plain
filtersNo
lease_sNo
_projectNoProjet (id) de CET APPEL — jeton PRIMAIRE. `oto_project(op='list')`.
datastoreYesthe table's NUMBER (`ns_id`, e.g. 174) — the form to use. Its name still resolves until 2026-11-08, then is refused. `slot:<name>` also works. It must already exist.
max_claimsNo

Schema Changelog

Changes observed during successful MCP inspections.

No schema history has been recorded yet.

TDQS

A4.7/5.0
Behavior5/5

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

With no annotations supplied, the description carries the full burden and does so thoroughly: lease semantics (900s default, covers only dead workers), the max_claims ceiling and _abandon stamping behavior, that a claim does not change the row, that the write lock blocks other agents, and the failure mode of silent livelock seen via _claims > 1. This is behavior an agent could not infer from the schema.

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

Conciseness3/5

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

Front-loaded with a one-sentence purpose, but the body sprawls: the 'write only the fields you established' instruction is stated twice in slightly different words, the 3,766-row livelock anecdote runs long, and the ⚠️ markers proliferate until nearly every paragraph is flagged. The content is valuable but could be half the length without losing guidance.

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?

For a 12-parameter concurrency primitive with no output schema and no annotations, the description anticipates the real failure modes an agent would hit: claiming outside a run, filtering on an unwritten column, passing a name instead of ns_id, and mis-shaping layered cells back into a write. Little an agent needs to call this correctly is missing.

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?

Adds real meaning beyond the 58%-covered schema: filter grammar with a concrete example, the filters list form and why a range forces it, empties='sentinel' round-trip semantics, layers='nested' vs 'flat' cell shape, worker reuse requirement for release, and datastore-as-number vs name vs slot:<name>. The org/group/project scoping params are left entirely to their schema descriptions, so it is not fully complete.

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?

Opens with a specific verb and resource: 'Atomically claim the NEXT unprocessed row of a datastore (work queue).' It immediately establishes the concurrency primitive framing, which cleanly separates it from siblings like data_rows (read), data_write (mutate), and data_release (free a claim).

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 prerequisites and alternatives: call run_start first or the claim is REFUSED; use data_release or run_finish to free a row; use filters only when one column needs two bounds; use filter by your pass's state on multi-pass tables. It even names the anti-pattern (filtering on a column you never write) that makes the queue never empty.

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

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources