Skip to main content
Glama

oto_doc

Docs (markdown pages tree inside a project; inherit the project's access — except ONE page shared on its own, see shared_with_me). A reference page is a DOC, in the PROJECT it belongs to (that project's « Documents » zone in the dashboard): CAPTURE the sourced facts of a piece of work there (kind=source/note) as you learn them, and search it before the web. How the org works (rules, conventions) belongs in its guide (oto_guide, read back by oto_context), and what concerns the person in their profile card (oto_profile) — not in a page. op=create (project_id, title; optional parent_id/body_md/kind) / bulk_create (project_id + pages=[{title, body_md?, kind?, description?, parent_index? | parent_id?}] → N pages in ONE call, build a tree via parent_index = an earlier page in the batch, or hang a page under an existing one via its parent_id; a top-level parent_id is the default parent of the batch; any other page key is refused) / list (project_id → the page INDEX, build the tree via parent_id: titles and body_md_length, NOT the bodies — pick a page here, then op=get it. fields=["*"] returns whole pages, fields=[…] picks columns) / search (project_id + query → full-text hits {id,title,kind,snippet}: LOCATE a page, then get its content) / get (the whole page, incl. rev, an ETag; pass fields=[…] to read ONLY those columns — fields=["id","rev"] gets the rev for an optimistic patch without paying for the body; section=a heading, or region="preamble", reads ONLY that region, addressed exactly as op=patch addresses it — heading and sub-sections included) / outline (doc_id → the headings: level, line, length of each section — pick the one to get or patch without reading the page) / update (title/body_md/kind, full body; snapshots the prior version; pass expected_rev from op=get for optimistic conflict detection → 409 if the page changed since) / patch (edit ONE region in place, WITHOUT re-emitting the page — this is how you edit a page too long to re-send: mode replace|append|prepend|delete, and ONE target, either section=its markdown heading + body_md = that section's BODY, WITHOUT repeating the heading (the server keeps it) — matched on the heading TEXT, level and case ignored: when several headings match, the patch is REFUSED with their list (never applied to the first), OR region="preamble" = everything ABOVE the first heading (provenance banner, "Last verified" line, front-matter) — it belongs to no section, so no section value can ever reach it; that is a SEPARATE axis, never a reserved heading name like "preamble" (a page may legitimately have such a heading, and it stays reachable via section). Passing both, or neither, is refused. mode=delete removes the target INCLUDING its heading (pass no body_md) — the only way to drop a heading without rewriting the page; to merely empty a section and keep its heading, use mode=replace with an empty body_md. Two authors on different regions don't clobber; every mode honours expected_rev and snapshots a revision. SCOPE: a section runs to the next heading of EQUAL-OR-HIGHER level, so its NESTED sub-sections are part of it — replacing OR deleting a ### also takes its #### children (the response then lists removed_subsections). To keep them, target the sub-heading itself or use mode=append. replace/delete responses say what went under removed (line bounds, line_count, subsections) — lines after a heading with no heading between belong to it; dry_run: true writes nothing and returns that same removed plus the rev to pass as expected_rev) / A SUCCESSFUL WRITE (create/update/patch/move) returns a RECEIPT, not the page: id, title, url, rev, updated_at and body_md_length — you just wrote the body, so it is not replayed back at you. Add fields=["*"] if you really want the stored page back, or fields=[…] to pick columns. / A page's description is a chapô you STORE: leave it out and the index DERIVES one from the first prose line of the body (marked description_derived), so it moves with every body edit — that is not an overwrite. Pass description explicitly to pin one that stops following the body. / EVERY page carries url — the web address to READ it, in the reader's own product. That is the answer to "where is it?": hand it over as-is, never rebuild an address from a pattern. null means that reader's product has no such view — then say where it lives (project + title) rather than invent a link. / revisions (doc_id → version history, newest first; each row's id is what op=revert takes) / revert (doc_id + revision_id from op=revisions → puts that past title+body back). A revert moves FORWARD: the current state is snapshotted first, so nothing is lost and a revert can itself be reverted; the response echoes reverted_from. It honours expected_rev too — pass it or you may silently overwrite a peer's edit. It restores a VERSION of a page that still exists; it does NOT undo a delete (a deleted page took its revisions with it) / backlinks (doc_id → the pages that CITE this one). LINK PAGES with [[Exact page title]] in body_md — that wiki-link is the ONLY thing that creates a backlink (prose mentions, text and text create none). Resolved AT WRITE TIME against the page's own project first, then every project the ORGANIZATION owns — never a team project, a personal project or another organization's — case- and edge-space-insensitive. A title carried by pages of SEVERAL org projects (none in the page's own) is AMBIGUOUS: nothing is linked and the write lists the candidates under citations_ambigues — make the title unique rather than guessing. A title that doesn't exist yet is kept as a stub and links itself once the page is created or renamed. ⚠️ That is the reach of RESOLUTION, not of the graph, and they differ BOTH ways. (a) The graph is not symmetric: a page of a team or personal project resolves into the org's projects, but no page of an org project can ever link to it. A page can therefore be cited and still read as an orphan here: do not use backlinks as a completeness or orphan check without knowing that. (b) op=backlinks shows every STORED link whatever its project, including one left behind by a page MOVED between projects — no resolution would make it today, and it disappears, silently, the next time the citing page is written. So a cross-project backlink is not proof that the same [[…]], written now, would resolve. (c) The list is filtered by YOUR access: citations living in projects you cannot read are removed. When that happens the response says hidden_by_access: true — « nobody cites this page » and « three pages cite it, you cannot see them » call for opposite moves, so the second is never reported as the first. The COUNT of hidden ones is deliberately not given: it would tell you how many pages exist in projects that are closed to you. Every write says which of its [[…]] found nothing, under citations_sans_cible / shared_with_me (→ the pages shared WITH YOU one by one — to you, your org or your team, via oto_resource op=share resource_type="doc": {id, title, updated_at, role, via, shared_by, url}; scope narrows it: me = shared with you as a person, org = shared with the organization you act in and your teams in it (in your personal org, to you as well); omitted = all of it, across all your organizations; served in any organization. Such a page is readable with op=get ALONE: its project, sibling pages, sub-pages, revisions and backlinks stay closed, and it is read-only) / archive (doc_id → the page AND its live sub-pages leave oto_search and op=list — include_archived: true lists them — but stay readable with op=get; nothing is destroyed; unarchive undoes that same gesture) / verify (doc_id → stamps verified_at/verified_by = re-read and still true; body, updated_at and versions untouched) / set_public (public: true → shareable public read-only link to THIS PAGE ALONE: the reader gets its title and body, and nothing else — not the project, not the sibling pages, not this page's own sub-pages, which each need their own link ; false → private ; returns public_url. Opening takes being a member of the org that owns the project — the parent org of an owning team, the owner themself for a personal project — or 403 publish_requires_membership; closing stays open to whoever writes) / delete (removes the page AND its whole subtree, revisions included — irreversible, there is no trash and no undelete. The response says how many pages went with it (descendants); ask FIRST with dry_run: true, which deletes nothing and returns the same count, whenever a human has to confirm) / move (reparent/reorder in-project via parent_id [null=top-level] + position; OR cross-project via to_project=target project id → moves the page AND its subtree there, write required on both. ⚠️ A move is NOT free for links: the page's own [[…]] are re-resolved in the TARGET project (some become stubs), while the links pointing AT it are left stored though now out of reach — they still show in op=backlinks and die on the citing page's next write. After reorganising a tree, rewrite the citing pages and read their citations_sans_cible). kind ∈ doc|note|source. EMBED A LIVE DATASTORE in a page body with a fenced block oto-data<newline><namespace-name-or-id><newline> → the viewer renders that datastore's table LIVE (always up to date). Prefer this over a hand-typed summary table when the data lives in a datastore (single source of truth, no drift).

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": {
      "op": "<op>"
    },
    "name": "oto_doc"
  }
}

Input Schema

NameRequiredDescriptionDefault
opYes
_orgNo
kindNo
modeNo
pagesNo
queryNo
scopeNoshared_with_me only: `me` = pages shared with YOU as a person; `org` = pages shared with the organization you are acting in (and your teams in it) — in your PERSONAL org, those shared with you as a person too. Omitted = both, across all your organizations. Served in any organization.
titleNo
doc_idNo
fieldsNoOutput projection on list/get/create/update/patch/move (refused elsewhere). Omitted, a list returns its index and a write its receipt; `["*"]` returns the whole page everywhere; a list of names picks columns.
publicNo
regionNo
_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.
body_mdNo
dry_runNo
sectionNo
positionNo
parent_idNo
project_idNo
to_projectNo
descriptionNo
revision_idNo
expected_revNo
include_archivedNo

Schema Changelog

Changes observed during successful MCP inspections.

No schema history has been recorded yet.

TDQS

A4.8/5.0
Behavior5/5

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

With no annotations at all, the description carries the full behavioral burden and does so in depth: writes return a receipt not the page, `expected_rev` produces a 409 on conflict, delete removes the whole subtree irreversibly with no trash, archive destroys nothing, revert snapshots first, `hidden_by_access` is reported rather than an empty list, and `publish_requires_membership` yields a 403. Cross-project link fragility after `move` is called out explicitly.

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?

It front-loads purpose and then walks the ops in a defensible order, but the text is a single enormous block dense with parenthetical asides and repeated restatements (e.g. the receipt/`fields` rule appears twice, the preamble caveat is re-explained). Nearly every sentence carries information, yet the density and lack of structure make scanning slow.

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 an 18-operation tool with no annotations and no output schema, the definition covers operation routing, conflict handling, return shapes (receipts, `removed`, `citations_ambigues`/`citations_sans_cible`, `reverted_from`, `descendants`, `public_url`), access filtering, and link-graph caveats. An agent has what it needs to call any op correctly without opening anything else.

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

Parameters5/5

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

Schema coverage is only 13% across 24 parameters, yet the description compensates almost completely: it defines `op` per value, `pages` with parent_index/parent_id semantics, `fields` projections, `section` vs `region="preamble"` as separate axes, `mode` values including delete-vs-empty-replace, `expected_rev`, `dry_run`, `to_project`, `position`, `scope`, `include_archived`, `revision_id`, and `description` derivation. Only `_org` is left unaddressed.

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 opening states the specific resource and its scope ("Docs (markdown pages tree inside a project; inherit the project's access — except ONE page shared on its own)") and the description enumerates all 18 operations with concrete verbs. It also distinguishes itself from nearby concepts by naming `oto_guide` and `oto_profile` and assigning rules/conventions and personal facts elsewhere.

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?

Explicit routing guidance is given: "search it before the web", "How the org works ... belongs in its guide (`oto_guide` ...) and what concerns the person ... (`oto_profile`) — not in a page", "pick a page here, then op=get it", and "Prefer this over a hand-typed summary table when the data lives in a datastore". It also states when to call alternatives such as `oto_resource op=share` for shared pages and when to use `dry_run: true` before a delete.

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