<!-- Generated from the code by `make docs`. Change the code it describes, then run that; an edit here is overwritten. -->

# MCP tools

What an agent connected to CoDNA is offered: 39 tools, each as `tools/list` describes it to the
model. How an agent connects, and what it is told when it does, are in [MCP server](https://codna.me/docs/mcp.md).
Every tool acts as the person the agent signed in as, and sees only what they can.

## whoami

**Who am I acting for.** Reads only.

Identify the person this session acts for. Everything you can see and do is bounded by their permissions, so this tells you whose knowledge base you are in.

No inputs.

Answers with `email`, `name`, `groups`, `kind`.

## search

**Search the knowledge base.** Reads only.

Full-text search across every document this user can see, with typo tolerance. Start here before writing anything: extending an existing wiki page is almost always better than adding a second one on the same subject. Results never include documents private to other people. When more is true, call again with page set to next_page for the rest.

Inputs:

- `query` (string, required): What to look for. Natural language works; typos are tolerated. Use \* to list everything.
- `space` (string): Restrict to one space: raw, wiki, or output. Omit to search all.
- `tags` (string): Comma-separated tags. A document must carry all of them.
- `path` (string): Only documents in this folder or beneath it, for example engineering or wiki:engineering
- `limit` (integer): Results per page, default 10, at most 50.
- `page` (integer): Which page, from 1: pass next_page from the previous call

Answers with `hits`, `found`, `more`, `next_page`.

## get_document

**Read a document.** Reads only.

Read a document by address or id. Returns a short header -- address, title, version, owner, whether you can edit, tags, its images and files -- then a blank line, then the markdown exactly as stored, so text you quote from it matches the page. For a long page, ask for the outline first, then read only the section you need. Keep the version: pass it as expected_version when replacing the whole page with update_document.

Inputs:

- `document` (string, required): Address like wiki:engineering/onboarding, or a document id
- `section` (string): Read only this section: a heading, with or without its #s. It runs to the next heading of the same or a higher level.
- `outline` (true or false): Read only the headings, with the line each starts on and how big its section is, instead of the page

## get_file

**Look at an image in a page.** Reads only.

Look at an image a page shows: a screenshot, a diagram, a scanned form. A page keeps its images and PDFs in a folder beside it and shows them with ordinary markdown, such as !\[The deploy pipeline\](onboarding.files/diagram.png); get_document lists a page's files in its header. Pass the page and the link as the page writes it, or just the file's name, and an image comes back as an image you can see. An image larger than 1568 pixels on a side is scaled to fit first, which is all a model sees of it anyway. A PDF is described, not sent: a person opens it from the page. You see a file only if you can read its page, and a page that shows another page's image shows it only to people who can read that one.

Inputs:

- `document` (string, required): The page the file is in, or the page whose link to it you are following: an address like wiki:engineering/onboarding, or a document id
- `file` (string, required): The file's name, such as diagram.png, or its link exactly as the page writes it, such as onboarding.files/diagram.png or ../ops/deploy.files/flow.png

## list_documents

**Browse documents.** Reads only.

List documents by space, folder, or tag, newest first. Use this to see how knowledge is organised before adding to it; use search when you know what you are looking for. A page holds at most 200: when more is true, call again with offset set to next_offset to get the rest.

Inputs:

- `space` (string): raw, wiki, or output
- `path` (string): Folder, for example engineering or wiki:engineering. Includes its subfolders
- `tags` (string): Comma-separated tags; a document must carry all of them
- `limit` (integer): Results per page, default 50, at most 200
- `offset` (integer): Where this page starts: pass next_offset from the previous page

Answers with `documents`, `more`, `next_offset`.

## create_document

**Create a document.** Adds things, and removes nothing.

Create a new document. Search first: adding a second page on a subject that already has one makes the knowledge base worse, and extending the existing page is usually right. The new document is visible only to this person until it is shared.

The returned address is what other documents must link to. It comes from the title unless you pass an explicit slug, and a title rarely produces the address you assumed: 'CoDNA: Overview' becomes codna-overview, so a link written as ./overview.md will never resolve. When writing a set of documents that reference each other, either set slug on each, or create them first and write the links afterwards using the addresses you got back.

Inputs:

- `space` (string, required): wiki for knowledge, output for task artifacts. raw is written only by ingestion and cannot be created here.
- `title` (string, required)
- `content` (string, required): Markdown body. Link to other documents with relative paths such as ./laptop-setup.md or ../raw/2026/09/handbook.md. A diagram goes in as a fenced code block marked mermaid; CoDNA draws it.
- `path` (string): Folder, for example engineering/onboarding
- `slug` (string): The last part of the address, controlling where links must point. Derived from the title when omitted -- a page titled 'CoDNA: Overview' lands at codna-overview, not overview. Set it explicitly when other documents link here.
- `tags` (string): Comma-separated tags

Answers with `id`, `address`, `title`, `space`, `tags`, `version`, `content`, `owner`, `can_edit`, `created_at`, `updated_at`.

## update_document

**Edit a document.** Changes things, and may overwrite or remove them.

Replace a document's whole content, or its title or tags. To change part of a page -- a few words, a line, a section -- use edit_document instead, which sends only the change. When replacing content, read it first and pass expected_version, so an edit that raced with somebody else's is rejected instead of silently discarding their work. Give a change_summary: the history is what makes a knowledge base auditable. Documents in raw cannot be edited.

Inputs:

- `document` (string, required): Address or id
- `content` (string): Replacement markdown body: the whole page. Omit to leave unchanged. To change part of a page, use edit_document. A diagram goes in as a fenced code block marked mermaid; CoDNA draws it.
- `title` (string)
- `tags` (string): Comma-separated; replaces the existing tags
- `change_summary` (string): Why this edit was made. Shown in the document's history.
- `expected_version` (integer): The version you read. The edit is rejected if it changed since.
- `append` (string): Deprecated: use edit_document. Text to add at the end of the page, or of section.
- `section` (string): Deprecated: use edit_document. The heading append or section_content acts on.
- `section_content` (string): Deprecated: use edit_document. New content for everything under section.
- `find` (string): Deprecated: use edit_document. Exact text, occurring once, to replace with replace_with.
- `replace_with` (string): Deprecated: use edit_document. What replaces find; empty removes it.

Answers with `id`, `address`, `title`, `space`, `tags`, `version`, `content`, `owner`, `can_edit`, `created_at`, `updated_at`.

## edit_document

**Change part of a document.** Changes things, and may overwrite or remove them.

Change a document without sending it back: a list of edits, each naming the exact text it changes or goes next to, applied in order and saved as one revision -- or, if any cannot apply, none of them. Put every change to a page in one call.

```text
  {op: replace, old, new}            exact text, once (all: true for every one); new "" removes it
  {op: insert_after, anchor, text}   new lines after the line the anchor ends on
  {op: insert_before, anchor, text}  new lines before the line the anchor starts on
  {op: append, text, section?}       at the end of the page, or of a section
  {op: replace_section, section, text}   everything under a heading, keeping it
  {op: delete_section, section}      a heading and everything under it
```

Copy old and anchor from get_document's text exactly; give section to look only within one. If text is not found, the answer names the closest line; if it occurs more than once, the lines it is on. The answer is the diff of what changed, so there is no need to read the page again to check. dry_run shows the diff and saves nothing. A title or tags can change in the same revision. Raw cannot be edited.

Inputs:

- `document` (string, required): Address or id
- `edits` (list of objects): Applied in order, each to the page as the ones before left it, and saved as one revision -- or, if any cannot apply, none of them
  - `op` (string, required): replace, insert_after, insert_before, append, replace_section, or delete_section
  - `old` (string): replace: the exact text to change, copied from the page. It must occur once, unless all is set.
  - `new` (string): replace: what old becomes; empty removes it. A diagram goes in as a fenced code block marked mermaid; CoDNA draws it.
  - `all` (true or false): replace: change every occurrence of old
  - `anchor` (string): insert_after and insert_before: exact text occurring once. The text goes on new lines after the line the anchor ends on, or before the line it starts on.
  - `text` (string): insert_after, insert_before, append, replace_section: the markdown to put in, as written; end it with a blank line to leave one. A diagram goes in as a fenced code block marked mermaid; CoDNA draws it.
  - `section` (string): A heading, with or without its #s. append adds to the end of this section (else of the page); replace_section and delete_section act on it; replace and insert\_\* look only within it.
- `title` (string): A new title, in the same revision
- `tags` (string): Comma-separated; replaces the tags, in the same revision
- `change_summary` (string): Why: shown in the document's history
- `expected_version` (integer): Refuse if the page is no longer this version. Usually leave it out: edits name the text they change, so they apply to the page as it is or fail loudly.
- `dry_run` (true or false): Show the diff these edits would make, and save nothing

## revert_document

**Undo changes to a document.** Changes things, and may overwrite or remove them.

Bring back an earlier version's title, tags and content, as a new revision: the history keeps both what was undone and the undoing. Use it to undo an edit that went wrong; document_history lists the versions. The document stays where it is.

Inputs:

- `document` (string, required): Address or id
- `version` (integer, required): The earlier version to bring back, from document_history
- `change_summary` (string): Why it is being undone

## move_document

**Move or rename a document.** Changes things, and may overwrite or remove them.

Change a document's address. Its own relative links are rewritten in the same revision so they reach the same documents from the new address (except in raw, which is never rewritten). Links pointing at it keep working, but their written text becomes out of date, and the people who wrote those links are asked whether to update them. Move things sparingly for that reason.

Inputs:

- `document` (string, required): Address or id
- `to` (string, required): New address, for example wiki:ops/deploy

Answers with `id`, `address`, `title`, `space`, `tags`, `version`, `content`, `owner`, `can_edit`, `created_at`, `updated_at`.

## delete_document

**Delete a document.** Changes things, and may overwrite or remove them.

Delete a document. It is hidden everywhere and removed from search, but its full history is kept and anyone who could read it before can restore it. People whose documents link to it are asked what to do. Ask before deleting anything you did not just create.

Inputs:

- `document` (string, required): Address or id

Answers with `ok`, `message`.

## document_history

**Document history.** Reads only.

List how a document has changed: what kind of change, when, and why. Useful for answering whether knowledge is current and who last touched it.

Inputs:

- `document` (string, required): Address or id
- `limit` (integer): Maximum revisions, default 20

Answers with `revisions`.

## backlinks

**What links here.** Reads only.

List the documents that link to this one, limited to those you can see. Tells you what depends on a page before you change or move it, and for a raw document, what has been distilled out of it.

Inputs:

- `document` (string, required): Address like wiki:engineering/onboarding, or a document id

Answers with `backlinks`.

## preview_share

**Preview what sharing would include.** Reads only.

Show what else a recipient is likely to need alongside what is being shared, found by following its links. Give one document, several (documents), or folders -- one preview covers the whole set, with counts for all of it and the pages it would add and the most linked-to suggestions listed; a folder's preview is what share_folder would share now. Grants nothing. Always run this before share_document or share_folder, show the person the suggestions and why each was suggested, and let them choose -- approving a list nobody explained is how people over-share.

Inputs:

- `document` (string): Address or id of one document
- `documents` (string): Comma-separated addresses or ids, to preview sharing them together
- `folder` (string): Comma-separated folders as space:path, for example wiki:se/proposals: everything of yours in them and beneath them, previewed as one set
- `recipient` (string, required): An email address, or group:slug for a team
- `limit` (integer): For several documents or folders: how many of the pages to be shared, and of the suggestions, to list. Default 25, at most 200; the counts always cover all of them.

Answers with `document`, `recipient`, `already_has_access`, `through`, `documents`, `folder`, `skipped`, `truncated`, `suggestions`, `documents_total`, `documents_already_open`, `documents_more`, `suggestions_total`.

## share_document

**Share documents.** Adds things, and removes nothing.

Grant a person or team access to one or more documents. Run preview_share first and only pass what the person actually agreed to. Never share on your own initiative: access is the one thing here that cannot be quietly undone, because whoever gained it may already have read the document.

Inputs:

- `documents` (string, required): Comma-separated addresses or ids: the document plus any suggestions the person accepted
- `recipient` (string, required): An email address, or group:slug for a team
- `permission` (string): read (default) or write
- `declined` (string): Suggestions the person rejected. Recorded so they are not suggested again.

Answers with `granted`, `skipped`.

## list_access

**Who can see a document.** Reads only.

List everyone a document is shared with -- people and groups, read or write, how each share was made and by whom -- besides its owner, who always can. Its owner and its editors may ask: anyone who can edit a document can share it. To go the other way, from a person to everything they were given, use shared_with.

Inputs:

- `document` (string, required): Address or id

Answers with `document`, `owner`, `grants`.

## shared_with

**What a person was given.** Reads only.

List what this person has given someone else -- a person or a group -- and through which shares: given to them directly, or to a group they are in, Everyone included. Their own documents, and other people's they shared as an editor. Answers "what does Theodor see of mine now?" in one call. Shares somebody else made on other people's documents are theirs to report.

Inputs:

- `person` (string, required): An email address, or group:slug
- `folder` (string): Only documents in this folder or beneath it, as space:path (wiki:se); a folder without a space is in wiki

Answers with `person`, `documents`, `folders`, `truncated`.

## share_folder

**Share a folder, now and from now on.** Adds things, and removes nothing.

Share a folder of this person's with people or teams, standing: every document of theirs in it and beneath it is shared now, and every one they put there later -- by writing it there or moving it there -- is shared as it arrives. Only their own documents: a page someone else keeps in the same folder is theirs to share. Several folders and several recipients can go in one call. Run preview_share with the folder first and show the person what it covers and what it links to; never share on your own initiative. Sharing again changes the permission; unshare_folder ends it.

Inputs:

- `folder` (string, required): Comma-separated folders as space:path, for example wiki:se/proposals. Each covers every folder beneath it; wiki: alone is the whole space.
- `recipient` (string, required): Comma-separated email addresses, or group:slug for a team
- `permission` (string): read (default) or write

Answers with `folder_shares`, `granted`, `skipped`.

## unshare_folder

**Stop sharing a folder.** Changes things, and may overwrite or remove them.

End a standing folder share: nothing put in the folder from now on is shared by it. What it already shared stays shared unless revoke_existing is set. Ask the person which they mean.

Inputs:

- `folder` (string, required): The folder as space:path, as it was shared
- `recipient` (string, required): An email address, or group:slug
- `revoke_existing` (true or false): Also take back what the folder share already shared. A document shared with them directly stays shared.

Answers with `ok`, `message`.

## list_folder_shares

**Which folders are shared, and with whom.** Reads only.

List this person's standing folder shares: which folders, with whom, read or write. Each goes on sharing what they put in that folder.

No inputs.

Answers with `folder_shares`.

## recent_access

**What was shared with me recently.** Reads only.

List documents this user was recently given access to. Worth checking at the start of a conversation: newly shared material is often exactly what they are about to ask about, and they may not know it arrived.

Inputs:

- `days` (integer): How far back to look, default 14

Answers with `granted`.

## pending_decisions

**Links in my documents that need a decision.** Reads only.

List what is waiting on this person's decision about links. Two kinds: links in their documents that point at something which moved or was deleted (resolve with update_link, keep_link to leave it as written -- a stale link still resolves to the right document -- or drop_link), and links someone proposed adding to a document they can edit (resolve with decide_link_suggestion). CoDNA never rewrites a document on its own, so these wait for a person. Worth checking when asked what needs attention.

No inputs.

Answers with `decisions`, `link_suggestions`.

## update_link

**Point a stale link at its target's new address.** Changes things, and may overwrite or remove them.

Rewrite a link whose target moved, keeping the link text. This edits the document containing the link and produces a normal revision attributed to this user -- which is why CoDNA asks rather than doing it automatically on every rename.

Inputs:

- `link_id` (string, required): From pending_decisions

Answers with `ok`, `message`.

## drop_link

**Remove a link, keeping its words.** Changes things, and may overwrite or remove them.

Remove the link markup around a reference, leaving the text that was linked so the sentence still reads. Use when the target is gone for good and the mention still makes sense without a link.

Inputs:

- `link_id` (string, required): From pending_decisions

Answers with `ok`, `message`.

## keep_link

**Leave a moved link as written.** Adds things, and removes nothing. Repeating it changes nothing more.

Answer a link whose target moved by leaving it as it is. It still reaches the right document; only the address written in the text is out of date. Nothing in the document changes, and the link stops being listed by pending_decisions until its target moves again or is deleted. Not for links to deleted documents: those can only be dropped, or the document restored.

Inputs:

- `link_id` (string, required): From pending_decisions

Answers with `ok`, `message`.

## ingest_text

**File source material into raw.** Changes things, and may overwrite or remove them.

Store source material in the raw space, where it becomes citable evidence. Use this for anything given to you rather than written by you: a pasted email or document, a fetched page, a meeting transcript. Raw is immutable, so file it as it is -- do not summarise or tidy it first. Write the summary into wiki afterwards and link back to the address this returns. Always record where it came from in source.

Inputs:

- `title` (string, required): What this is. Becomes the document title and its address.
- `content` (string, required): The material itself.
- `source` (string): Where it came from: a URL, an email subject, a person. Recorded with the document.
- `format` (string): markdown (default), html, or text.

Answers with `address`, `title`, `id`, `warning`.

## list_ingestions

**What this user has uploaded.** Reads only.

List what this user has fed into raw, most recent first, including uploads that failed to convert. A failed ingestion produces no document, so this is the only way to answer "where did the file I uploaded go?". Recordings (audio and video) are listed from the moment they start arriving, with where their transcript stands: a recording's raw page is filed at once and gains the transcript as its next version.

Inputs:

- `limit` (integer): Maximum rows, default 20.

Answers with `ingestions`.

## create_automation

**Register a script that keeps running.** Changes things, and may overwrite or remove them.

Register JavaScript that runs on an event or a schedule, as the person you act for, for something continuing ("whenever a page is tagged runbook, share it with ops"). For something once, just do it. To share a folder, use share_folder. It can never do more than they could by hand. Register it disabled, show them the script and a run_automation dry_run, and enable it once they agree.

Script: one default-exported synchronous function:

```text
  export default function ({ event, doc, actor, recipient, codna, actions }) { }

  event      { type, at, id }; type is 'schedule.tick' on a schedule
  doc        the subject, frozen, absent on a schedule: { id, address, space, path,
             slug, title, tags, status, version, content, createdBy, createdAt,
             updatedAt }; address 'wiki:se/x', path the folder 'se'
  actor      who caused it; recipient who gained or lost access (access.* only):
             { id, email, name }
```

Reads, as the owner, seeing only what they can:

```text
  codna.find({ space, pathPrefix, tags, status, updatedBefore, updatedAfter, limit,
    includeContent })  -> docs as above
  codna.get(ref) -> doc or null      codna.backlinks(ref) -> [{ id, address, title, space }]
```

Actions are PROPOSED, then checked against the owner's permissions:

```text
  actions.share(principal, 'read'|'write')   email or 'group:ops'
  actions.unshare(principal)   actions.addTag(tag)   actions.removeTag(tag)
  actions.notify(principal, message)   'me' is the owner
  actions.suggestLink(targetRef, rationale)   actions.log(message)
```

Each takes a last { document: ref } to act on another document.

No fetch, require, process, Date, Math.random, setTimeout, eval or async: use
event.at for the time. Per run: 100ms, 16MB, 50 actions, 20 reads.

Inputs:

- `name` (string, required): Short name, shown to the owner in their list of automations.
- `description` (string): What this does and why, in a sentence, for whoever reads it in six months.
- `script` (string, required): The JavaScript. One default-exported synchronous function.
- `triggers` (string): Comma-separated event types for an event automation, for example document.created,document.updated
- `schedule` (string): Five-field cron for a scheduled automation, for example '0 9 \* \* 1'. Give this or triggers, not both.
- `timezone` (string): IANA timezone for the schedule, for example Asia/Tbilisi. Defaults to UTC.
- `scope_space` (string): Only fire for documents in this space.
- `scope_path` (string): Only fire for documents in this folder or any folder beneath it: 'se' covers se/proposals, never se-old. Every scope given must match.
- `scope_tags` (string): Comma-separated; only fire for documents carrying all of them. Much cheaper than checking inside the script.
- `enabled` (true or false): Start it running. Default false: register it, show the person what it does, then enable it.

Answers with `id`, `name`, `description`, `kind`, `enabled`, `triggers`, `schedule`, `timezone`, `scope`, `next_run_at`, `owner`, `disabled_reason`, `disabled_at`.

## list_automations

**What is running for this person.** Reads only.

List the automations this person owns, whether each is enabled, and what fires it. Check here before writing a new one: an automation that duplicates an existing one is two scripts fighting over the same documents.

Inputs:

- `limit` (integer): Maximum results, default 50.

Answers with `automations`, `available_triggers`.

## automation_runs

**Why an automation is not doing what was expected.** Reads only.

Show an automation's recent runs: what it proposed, what was applied, and what was refused and why. Start here whenever someone says a script "is not working" -- the usual answer is that its actions are being refused because the owner cannot share or edit the documents it is reaching for, which the run record states outright.

Inputs:

- `automation` (string, required): The automation's id.
- `limit` (integer): Maximum runs, default 10.

Answers with `runs`.

## get_automation

**Read an automation.** Reads only.

Read one of this person's automations in full: its script, what fires it, and its scope. Read it before changing it with update_automation.

Inputs:

- `automation` (string, required): The automation's id, from list_automations.

Answers with `id`, `name`, `description`, `kind`, `enabled`, `triggers`, `schedule`, `timezone`, `scope`, `next_run_at`, `owner`, `disabled_reason`, `disabled_at`, `script`.

## update_automation

**Change an automation.** Changes things, and may overwrite or remove them. Repeating it changes nothing more.

Change an automation in place: its script, triggers or schedule, scope, name, or whether it is on. Only what you give changes; it keeps its id and its run history. A change to an enabled automation takes effect at once, so show the person the change first -- or pass enabled: false, try it with run_automation dry_run, and enable it once they agree.

Inputs:

- `automation` (string, required): The automation's id.
- `name` (string)
- `description` (string)
- `script` (string): The whole new script; the contract is in create_automation's description.
- `triggers` (string): Comma-separated event types. Makes it an event automation.
- `schedule` (string): Five-field cron. Makes it a scheduled automation.
- `timezone` (string)
- `scope_space` (string): Empty clears it.
- `scope_path` (string): A folder and every folder beneath it. Empty clears it.
- `scope_tags` (string): Comma-separated. Empty clears them.
- `enabled` (true or false): Left as it is unless given.

Answers with `id`, `name`, `description`, `kind`, `enabled`, `triggers`, `schedule`, `timezone`, `scope`, `next_run_at`, `owner`, `disabled_reason`, `disabled_at`, `script`.

## run_automation

**Run an automation now.** Changes things, and may overwrite or remove them.

Run an automation by hand, disabled or not: on one document, or on every document already in its scope (automations otherwise only act on what happens after they are enabled), or -- for a scheduled one -- as a tick. With dry_run it runs the script and checks every action it proposes, and applies nothing: use that to show the person what a new or changed automation will do before it is enabled. A real run is recorded like any other and acts as its owner.

Inputs:

- `automation` (string, required): The automation's id.
- `document` (string): Run it on this one document, as if its trigger had fired for it.
- `all_in_scope` (true or false): Run it on every existing document its scope matches -- what it would have done to them had it been running when they were written. A page at a time; see more.
- `dry_run` (true or false): Report what it would do, change nothing and record nothing.
- `event` (string): Which of its triggers the script is told fired; its first by default.
- `limit` (integer): Documents per page with all_in_scope, at most 200.
- `offset` (integer): Where the page starts: next_offset from the previous call.

Answers with `dry_run`, `event`, `runs`, `more`, `next_offset`.

## set_automation_enabled

**Turn an automation on or off.** Changes things, and may overwrite or remove them. Repeating it changes nothing more.

Enable or disable an automation without losing it or its history. Enable only when the person has seen what the script does and agreed to it.

Inputs:

- `automation` (string, required): The automation's id.
- `enabled` (true or false, required)

Answers with `id`, `name`, `description`, `kind`, `enabled`, `triggers`, `schedule`, `timezone`, `scope`, `next_run_at`, `owner`, `disabled_reason`, `disabled_at`.

## delete_automation

**Remove an automation.** Changes things, and may overwrite or remove them.

Delete an automation and its run history. Prefer disabling it: a disabled automation can be read, explained, and turned back on, and its history still says what it did.

Inputs:

- `automation` (string, required): The automation's id.

Answers with `deleted`.

## list_groups

**Teams that can be shared with.** Reads only.

List the groups in this organisation and which of them this person belongs to. Use it to resolve a name someone says out loud -- "share this with the ops team" -- into the 'group:\<slug>' that share_document takes, and to check how many people that actually is before proposing it. 'everyone' is the whole organisation. Creating groups and changing who is in them is done by the organisation's managers, not here.

No inputs.

Answers with `groups`, `my_groups`.

## related_documents

**Documents worth linking from this one.** Reads only.

Suggest documents that probably belong linked from this one, by shared tags and by co-citation -- documents other pages already cite alongside it. Read the candidates before proposing any: shared tags alone are weak evidence, and a link is only worth proposing if a reader of this document would actually want to follow it. Documents already linked, and pairs already proposed or rejected, are left out.

Inputs:

- `document` (string, required): Address or id of the document to find links for
- `limit` (integer): Maximum candidates, default 10

Answers with `candidates`.

## propose_link

**Suggest a link, for a person to decide.** Changes things, and may overwrite or remove them.

Propose adding a link from one document to another. Nothing changes yet: the proposal waits for whoever can edit the source to accept or reject it, and accepting adds an ordinary markdown link to the document. Use this rather than editing a link in yourself when the document is not one you were asked to change. Give a rationale a person can judge in one read. A rejected link is never proposed again, so do not retry one that comes back as rejected.

Inputs:

- `from` (string, required): The document the link would be added to
- `to` (string, required): The document it would point at
- `rationale` (string, required): Why a reader of 'from' would want to follow it. Shown to the person deciding.

Answers with `suggestion_id`.

## decide_link_suggestion

**Accept or reject a proposed link.** Changes things, and may overwrite or remove them.

Record the person's decision on a proposed link. Only call this when they have said yes or no -- proposing is your job, deciding is theirs. Accepting adds the link under a Related heading in the source document, as an ordinary edit with its own revision. Rejecting is permanent: the same link will not be proposed again.

Inputs:

- `suggestion_id` (string, required): From pending_decisions
- `accept` (true or false, required): true adds the link to the document; false rejects it for good

Answers with `status`.
