Skip to content

MCP tools

relaybay serves MCP over Streamable HTTP at http://127.0.0.1:8765/mcp. Every request needs Authorization: Bearer <token> (relaybay token prints it). Results are short markdown meant to be read by the model; failures (unknown task, version conflict, invalid stage) come back as tool errors with a message that says what to do.

Server instructions

The server's MCP instructions (sent once when a session connects) are a short working contract: read project_snapshot on start (its first line names the rules); pick or create a task and pass session (your local_ id with session_title if you know it, plus hook_session if the session start gave you one; if not, don't rebind — the coordinator binds you); update next_step, waiting_on and progress on every stage change and report; start a report with the configured first line, states and sizes (taken from the effective global rules, so they match rules_get); set waiting_on to who decides when the work needs a decision; use task_patch for fields your tool schema lacks; treat a Warning (rules) line as something to fix; when a hook hands over owner comments, handle them like a message from the user and call comment_ack (or answer with comment_add); before touching a server, cluster or database, read environments_get — the level there says what is allowed unasked, live is read-only unless the owner set otherwise — never paste secrets into relaybay and propose changes with environment_propose; before moving a task to review or test, write what the owner should check with review_steps_set and attach screenshots with attach_file (the owner's decisions arrive as owner comments); start with a plan — plan_set from PLAN.md or your own 2-5 steps (S tasks: at least a todo) — and mark steps in_progress / done with plan_step_update (done needs one line as planned or deviation: why). The details live in the rules below.

Conventions:

  • Task reference — the key (acme-shop-12) or the numeric id.
  • Stages — plan, dev, review, test, live, done. Any transition is allowed; the event log keeps the history. review is where the owner accepts the work step by step (see Review below).
  • session — optional on writes: who is calling. Pass the Claude desktop app session id (local_<uuid>) if the session knows it, together with session_title (its display name); otherwise a human-readable name. relaybay creates the session on first use and records it as the actor of the event. It is for display only; the token is the only authorization.
  • hook_session — optional on every write tool: your own Claude Code session id as relaybay's hooks report it, never another session's. An id another live session linked first is not moved (the response ends with Warning: hook_session … belongs to another session (…); not relinked.), and task_bind_session never links one (a coordinator binds other sessions; pass the id on your own writes). With hooks set up (relaybay setup), the session start adds "Your relaybay hook session: . Pass it as hook_session in relaybay writes." to the agent's context. Passed together with session, it links that hook id to the calling session exactly, so the board's live status (working / waiting / idle / ended) follows the right card. A row the hooks had created for the id by guess hands over its status and is removed. Without an exact link relaybay guesses from the working directory (see Session status from hooks).
  • Session aliases — if you don't know your local_ id, don't rebind a task: the coordinator binds you; pass your session name only as session on writes. When task_bind_session gets a plain name for a task bound to a desktop session, the desktop binding (and its "Open in desktop" link) is kept and the name is recorded as that session's alias (event session.alias_added). From then on the name resolves to the desktop session: writes are recorded as it, task_list and task_unbind_session accept the name. Binding by another local_ id still rebinds; a plain name on a task without a desktop session binds as before. Aliases are global: one name maps to one session across all projects, and later binds by that name move it to the desktop session bound to that task. Because an alias would hijack a plain session of the same name, none is created when such a session exists; the call then changes nothing and answers "already bound to (local_…); pass your local id".</li> <li><strong>Claude desktop app session links</strong> — for a desktop session id, relaybay builds a link (<code>claude://claude.ai/epitaxy/<id></code> by default, see <code>desktop_link_template</code> in the README) that the board shows as "Open in desktop" and that <code>task_get</code> prints. Plain names have no link.</li> <li><strong>Clearing a field</strong> — in <code>task_update</code>, an omitted field stays unchanged; <code>"-"</code> or an empty string clears it (models cannot always send an empty string, so <code>"-"</code> is the reliable form). In <code>task_create</code>, <code>"-"</code> leaves a field empty; in <code>report_add</code>, <code>progress="-"</code> clears the summary.</li> <li><strong>Description and progress</strong> — two markdown fields per task. <code>description</code> says what to do and why (1–5 sentences). <code>progress</code> is a running summary of everything done so far, not the last step: the session rewrites it as it goes, ideally with every report (<code>report_add(progress=…)</code>). The board shows both in full in the task dialog (not on the card).</li> <li><strong>Next step and waiting on</strong> — keep them current: <code>task_set_stage</code> and <code>report_add</code> take optional <code>next_step</code> (what happens next) and <code>waiting_on</code> (who acts next), saved in the same transaction as the stage change or the report; omitted, they stay; <code>"-"</code> or <code>""</code> clears them. A next step written before the task's last stage change is <em>stale</em>: the card and the task dialog show it muted with a "stale" marker, <code>task_get</code> and <code>project_snapshot</code> append <code>(stale)</code>. Writing it again, even with the same text, confirms it as current (a <code>task.updated</code> with a version bump). On the board, saving the edit form confirms it only when the next-step text itself was changed.</li> <li><strong>External tracker</strong> — <code>tracker_ref</code> (issue key, e.g. <code>ENG-12</code>, up to 100 characters), <code>tracker_name</code> (any tracker, free text), <code>tracker_url</code> (optional, <code>http://</code> or <code>https://</code> only; anything else is refused) and <code>tracker_logged</code> (bool: the work log is entered there). The card shows "<tracker_name> <tracker_ref>" as a badge whenever a key is set, a link with a URL, with ✓ when logged. With the server setting <code>require_tracker_log = true</code>, a done task that is not logged shows "not logged" (also in <code>task_get</code> / snapshots) and is never archived.</li> <li><strong>Tracker history</strong> — every issue key a task has had is kept: setting a different <code>tracker_ref</code> appends an entry (earlier ones stay, clearing the key keeps them too), and marking <code>tracker_logged</code> stamps "logged <date>" / "unlogged <date>" on the current entry (logging again restarts its mark). <code>task_get</code> lists the entries newest first; snapshots and the card show only the current issue.</li> <li><strong>Automatic unlog</strong> — a logged task that gets new work loses the mark in the same transaction, with an event <code>task.tracker_unlogged</code> (payload <code>reason</code>): <code>report_add</code>, a changed <code>progress</code>, or a move back out of <code>live</code> or <code>done</code> to an earlier stage. Metadata edits (title, branch, waiting on, next step, other tracker fields) keep it; passing <code>tracker_logged</code> in the same write wins. A new <code>tracker_ref</code> starts as not logged unless <code>tracker_logged</code> is passed in the same write (a plain <code>task.updated</code>; the previous issue's entry keeps its "logged" date). Mark it again once the log covers the new work.</li> <li><strong>Done window</strong> — the board's <code>done</code> column shows tasks closed in the last <code>done_window_days</code> (default 7); older ones are archived on the board only (with <code>require_tracker_log</code>, not-logged done tasks stay). <code>project_snapshot</code> and <code>task_list</code> are unaffected: <code>include_done</code> returns all done tasks.</li> </ul> <h2 id="reading">Reading<a class="headerlink" href="#reading" title="Permanent link">¶</a></h2> <table> <thead> <tr> <th>Tool</th> <th>Arguments</th> <th>Returns</th> </tr> </thead> <tbody> <tr> <td><code>project_list</code></td> <td>—</td> <td>Table of projects with task counts per stage.</td> </tr> <tr> <td><code>project_snapshot</code></td> <td><code>project</code>, <code>include_done?</code></td> <td>Open tasks grouped by stage (waiting on, next step, branch, session name and desktop id, version, the start of the description and progress, last report) and recent decisions.</td> </tr> <tr> <td><code>task_list</code></td> <td><code>project?</code>, <code>stage?</code>, <code>waiting_on?</code>, <code>session?</code> (id or name), <code>include_done?</code></td> <td>One line per task. Done tasks are left out unless <code>stage</code> or <code>include_done</code> is given.</td> </tr> <tr> <td><code>task_get</code></td> <td><code>task</code>, <code>reports?</code> (default 5)</td> <td>Ends with "Editable fields: …". All fields (a stale next step ends with <code>(stale)</code>), description and progress in full, current <code>version</code>, the bound session (name and desktop id, link), the tracker issue and work-log mark, session binding history, tracker history, latest reports with their bodies, decisions.</td> </tr> <tr> <td><code>events_since</code></td> <td><code>since_id?</code> (default 0), <code>project?</code>, <code>limit?</code> (default 50, max 500)</td> <td>Events oldest first and the <code>since_id</code> for the next call.</td> </tr> <tr> <td><code>comments_list</code></td> <td><code>task</code>, <code>unread_only?</code></td> <td>The task's comments oldest first: owner comments with <code>sent / delivered … to <session> / read</code> marks, agents' replies (<code>unread by the owner</code> until the owner marks them read).</td> </tr> <tr> <td><code>environments_get</code></td> <td><code>project</code></td> <td>The project's environments (see <em>Environments and access</em> below): the preset and guard mode, per environment kind, base URL, agents' level (<code>(default)</code> when it comes from the default preset, <code>(policy: <layer>)</code> from another policy layer) and per-capability overrides, sign-in flag and login-state path, access entries (references only; test secrets by name only unless the policy exposes them), notes, last change.</td> </tr> <tr> <td><code>policy_get</code></td> <td><code>project?</code>, <code>environment?</code></td> <td>The effective policy (see <em>Policy</em> below) of the global scope, a project or one of its environments: the layer chain, then one line per value with the layer that set it, e.g. <code>- levels.live.default = "read" (preset:default)</code>, and any ignored broken layer.</td> </tr> <tr> <td><code>plan_get</code></td> <td><code>task</code></td> <td>The plan (see <em>Plan</em> below): status (<code>accepted by … <time></code> or draft), <code>2/4 done, 1 deviation(s)</code>, each step as <code>1. [done] Title — done <time> · as planned · outcome · report:12</code> with its "what and why", then your Claude Code todo as a separate list. Without steps: how to write one, and the todo if any.</td> </tr> <tr> <td><code>review_get</code></td> <td><code>task</code></td> <td>The checklist (<code>1. [accepted] Sign in — accepted by owner …</code>) with each step's what / how / why, its screenshots (<code>screenshot #7 (image/png, 41 KB): caption</code>), the owner's comments on the step, then screenshots of no step.</td> </tr> <tr> <td><code>rules_get</code></td> <td><code>project?</code></td> <td>The working rules as markdown: hash, scope, mode, report format, stage requirements, description template, guidance. Without <code>project</code>, the global rules; with it, the project's merged rules.</td> </tr> </tbody> </table> <h2 id="writing">Writing<a class="headerlink" href="#writing" title="Permanent link">¶</a></h2> <table> <thead> <tr> <th>Tool</th> <th>Arguments</th> <th>Effect</th> </tr> </thead> <tbody> <tr> <td><code>project_upsert</code></td> <td><code>slug</code>, <code>name?</code>, <code>repo_path?</code>, <code>description?</code>, <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code></td> <td>Creates the project, or updates the given fields. Slugs: lowercase letters, digits, dashes.</td> </tr> <tr> <td><code>task_create</code></td> <td><code>project</code>, <code>title</code>, <code>stage?</code> (default <code>plan</code>), <code>waiting_on?</code>, <code>next_step?</code>, <code>branch?</code>, <code>doc_path?</code>, <code>description?</code>, <code>progress?</code>, <code>tracker_ref?</code>, <code>tracker_name?</code>, <code>tracker_url?</code>, <code>tracker_logged?</code> (default false), <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code></td> <td>Creates a task with the next key of the project (<code>acme-shop-1</code>, <code>acme-shop-2</code>, …). With <code>session</code>, the task is bound to it.</td> </tr> <tr> <td><code>task_update</code></td> <td><code>task</code>, <code>title?</code>, <code>waiting_on?</code>, <code>next_step?</code>, <code>branch?</code>, <code>doc_path?</code>, <code>stage_note?</code>, <code>description?</code>, <code>progress?</code>, <code>tracker_ref?</code>, <code>tracker_name?</code>, <code>tracker_url?</code>, <code>tracker_logged?</code>, <code>size?</code> (<code>S</code>, <code>M</code>, <code>L</code>), <code>expected_version?</code>, <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code></td> <td>Patches fields; <code>"-"</code> or <code>""</code> clears a text field. With <code>expected_version</code>, fails instead of overwriting when the task changed meanwhile.</td> </tr> <tr> <td><code>task_patch</code></td> <td><code>task</code>, <code>fields</code> (JSON object), <code>expected_version?</code>, <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code></td> <td>Changes any editable task fields with the same validation as <code>task_update</code> / <code>task_set_stage</code> (a <code>stage</code> value moves the task, auto-unlog applies). Unknown names fail with the list of allowed ones; <code>"-"</code>, <code>""</code> or <code>null</code> clears an optional text field. The allowed fields: the end of <code>task_get</code> or <code>relaybay://schema/task</code>.</td> </tr> <tr> <td><code>task_set_stage</code></td> <td><code>task</code>, <code>stage</code>, <code>note?</code>, <code>next_step?</code>, <code>waiting_on?</code>, <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code></td> <td>Moves the task; <code>note</code> is shown next to the stage. Moving to <code>done</code> sets the closed time. <code>next_step</code> and <code>waiting_on</code> are saved in the same transaction (<code>task.stage_changed</code> + <code>task.updated</code>, one version bump); <code>"-"</code> clears them.</td> </tr> <tr> <td><code>task_bind_session</code></td> <td><code>task</code>, <code>session</code>, <code>session_title?</code>, <code>hook_session?</code> (ignored: never linked here, a note says so)</td> <td>Shows the session in the task dialog as the one working on the task (a desktop session also gets an "open in desktop" icon on the card). The previous binding is closed and kept in the task's history (event <code>task.session_bound</code>). A plain name on a task bound to a desktop session keeps that binding and becomes its alias (see <em>Session aliases</em>).</td> </tr> <tr> <td><code>task_unbind_session</code></td> <td><code>task</code>, <code>session?</code></td> <td>Removes the bound session from the task; the binding is closed and kept in the history (event <code>task.session_unbound</code>). With <code>session</code> (desktop id or name), fails without changes if another session is bound. Nothing bound: says so, changes nothing.</td> </tr> <tr> <td><code>report_add</code></td> <td><code>task</code>, <code>body</code>, <code>progress?</code>, <code>next_step?</code>, <code>waiting_on?</code>, <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code></td> <td>Appends a markdown report. Its first line is the report summary; a first line <code>state · size · gist</code> also sets the state (<code>inspected</code>, <code>changed</code>, <code>verified</code>, <code>committed</code>, <code>pushed</code>, <code>blocked</code>) and size (<code>S</code>, <code>M</code>, <code>L</code>); that size also becomes the task's <code>size</code> when it has none. <code>progress</code> replaces the task's progress summary in the same transaction (<code>task.updated</code> + <code>report.added</code>); omitted, the summary stays; <code>"-"</code> clears it. <code>next_step</code> and <code>waiting_on</code> work the same way and go into the same <code>task.updated</code>.</td> </tr> <tr> <td><code>decision_add</code></td> <td><code>project</code>, <code>text</code>, <code>task?</code>, <code>decided_by?</code>, <code>source?</code>, <code>supersedes?</code>, <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code></td> <td>Records a decision of the project (scope <code>task</code> with <code>task</code>); it appears in the project snapshot and <code>decision_list</code>. <code>source</code>: <code>report:12</code>, <code>comment:7</code>, <code>event:301</code> or <code>message:<id></code>, optionally followed by display text, or display text alone; a named report, comment or event must exist in the project. <code>supersedes</code>: the id of a current decision of the same project this one replaces (see <em>Memory and documents</em>).</td> </tr> <tr> <td><code>comment_ack</code></td> <td><code>comment_id?</code> or <code>task?</code>, <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code></td> <td>Marks owner comments read: one by id, or every unread one of the task (event <code>comment.read</code> each). The board shows them as read and the Stop hook stops reminding. Neither argument, or an id that is not an owner comment: a tool error.</td> </tr> <tr> <td><code>comment_add</code></td> <td><code>task</code>, <code>body</code> (markdown, up to 4000 characters), <code>in_reply_to?</code>, <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code></td> <td>An agent's reply on the task's thread (author: the calling session; <code>comment.added</code>). It marks the owner comment it answers read, or without <code>in_reply_to</code> every unread owner comment of the task. The owner sees unread replies in the Inbox.</td> </tr> <tr> <td><code>environment_propose</code></td> <td><code>project</code>, <code>change</code> (object), <code>reason</code>, <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code></td> <td>Records a proposal for the owner and changes nothing (<code>environment.proposed</code>); it shows in the owner's Inbox and on the Stands & access page, where the owner accepts or rejects it. <code>change</code>: <code>environment</code> (name) plus any of <code>kind</code>, <code>base_url</code>, <code>notes</code>, <code>sign_in_allowed</code>, <code>login_state_path</code>, <code>level</code> (<code>none</code> / <code>read</code> / <code>ask</code> / <code>full</code> / <code>default</code>), <code>overrides</code> (<code>{"ssh": "ask"}</code>; capabilities <code>sign_in</code>, <code>ssh</code>, <code>kubectl</code>, <code>db</code>, <code>deploy</code>), <code>access</code> (<code>[{"kind": "ssh", "fields": {"alias": "acme-live"}}]</code> to add). A new environment needs <code>kind</code>. Secret values are refused (a tool error naming the field); test secrets are never accepted from agents.</td> </tr> <tr> <td><code>policy_propose</code></td> <td><code>scope</code> (<code>global</code>, <code><project></code> or <code><project>/<environment></code>), <code>patch</code> (object), <code>reason</code>, <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code></td> <td>Records a policy proposal and changes nothing (<code>policy.proposed</code>); it shows in the owner's Inbox and in Settings → Policy. The patch is merged into the layer's JSON (objects key by key, other values replace, <code>null</code> removes a key) and the result must validate against the <a href="../reference/policy/">policy schema</a>; a tool error names the path otherwise. Accepting applies it (<code>policy.updated</code>, <code>policy.proposal_accepted</code>), with a confirmation when it widens agents' access on live.</td> </tr> </tbody> </table> <p>| <code>review_steps_set</code> | <code>task</code>, <code>steps</code> (<code>[{"title", "details?"}]</code>, up to 50), <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code> | Replaces the checklist (see <em>Review</em>). Step N keeps its status and screenshots while its title and details stay the same; a changed step is open again; steps past the new end are removed (their screenshots stay on the task). | | <code>review_step_update</code> | <code>task</code>, <code>step</code> (number), <code>title?</code>, <code>details?</code>, <code>status?</code> (only <code>open</code>), <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code> | Changes one step's text (which reopens it) or reopens it after a fix. Accepting and rejecting is the owner's, on the board. | | <code>attach_file</code> | <code>task</code>, <code>path?</code> or <code>content_base64?</code>, <code>filename?</code>, <code>step?</code> (number), <code>caption?</code> (markdown), <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code> | Copies a PNG, JPEG, WebP or GIF (checked by content; SVG and others refused) to the task, optionally to a step. <code>path</code> is absolute and inside the project's <code>repo_path</code>, the temp folder or <code>attachment_roots</code>. Limits <code>attachments_max_mb</code> per file and <code>attachments_task_max_mb</code> per task. The same image on the same task is stored once (the answer says so). How to produce the file: <a href="../how-to/screenshots-for-review/">Screenshots for review</a>. | | <code>attachment_caption</code> | <code>attachment_id</code>, <code>caption</code> (<code>"-"</code> clears), <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code> | Changes a screenshot's caption. | | <code>plan_set</code> | <code>task</code>, <code>steps</code> (<code>[{"title", "details?"}]</code>, up to 30), <code>reason?</code>, <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code> | Replaces the plan (see <em>Plan</em>). Step N keeps its status and checkpoint while its title stays the same; a renamed step is pending again; steps past the new end are removed; <code>[]</code> removes the plan. A new plan is a draft; changing the titles or their order makes an accepted plan a draft again (the answer says so). Every write is kept in the plan's history with <code>reason</code>. | | <code>plan_step_update</code> | <code>task</code>, <code>step</code> (number), <code>status</code> (<code>pending</code>, <code>in_progress</code>, <code>done</code>, <code>blocked</code>), <code>outcome?</code> (one line), <code>link?</code> (done only: a report of the task such as <code>12</code>, or a PR URL), <code>conformance?</code> (required with done: <code>as planned</code> or <code>deviation: <why></code>), <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code> | Moves one step. Done is a checkpoint with its time; leaving done clears it. Never turns an accepted plan back into a draft. | | <code>plan_accept</code> | <code>task</code>, <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code> | The coordinator accepts the plan (the owner uses the board). Already accepted: says so, changes nothing. A task without plan steps, or a call from the session bound to the task (an agent does not accept its own plan): a tool error. |</p> <p>Writes that change a task increment its <code>version</code>. Every change appends an event and updates open boards immediately.</p> <p>A stage move (<code>task_set_stage</code>, <code>task_patch</code> / <code>task_update</code> with a stage, the board form) is checked against the project's rules. In <code>warn</code> mode the write succeeds and the response gets one line per unmet requirement, e.g. <code>Warning (rules): moving to test usually needs a report since the last stage change.</code>; the <code>task.stage_changed</code> event carries the same lines as <code>warnings</code>. In <code>strict</code> mode the write fails with <code>Rules: moving to test requires … Nothing changed.</code></p> <h2 id="review">Review<a class="headerlink" href="#review" title="Permanent link">¶</a></h2> <p>The owner accepts a task step by step in the task dialog's <strong>Review</strong> tab. What to check by eye (the Owner check) belongs in the checklist, not only in the report:</p> <div class="highlight"><pre><span></span><code>review_steps_set(task="acme-shop-12", steps=[ {"title": "Sign in with 2FA", "details": "**What:** the code screen. **How:** sign in with the demo account. **Why:** the new step must not skip the code."}, {"title": "Cart badge", "details": "Add two products; the badge shows 2."}]) attach_file(task="acme-shop-12", path="/tmp/acme-shop-2fa.png", step=1, caption="Code screen") task_set_stage(task="acme-shop-12", stage="review", waiting_on="owner") </code></pre></div> <p>Moving a task to <code>review</code> or <code>test</code> with an empty checklist adds a <code>Hint:</code> line to the answer. Each decision on the board reaches the bound session as an owner comment (like any other, see <em>Owner comments through hooks</em>), linked to its step: <code>Step 1 'Sign in with 2FA' accepted — continue with step 2</code>, <code>Step 2 'Cart badge' rejected: the badge shows 1</code>, or a plain comment <code>On step 2 'Cart badge': …</code>. Acknowledge it with <code>comment_ack</code>; after a fix, reopen the step with <code>review_step_update(status="open")</code> or change its text. The card shows "Review 1/2"; tasks in review or test with a checklist are listed in the owner's Inbox (Review queue).</p> <h2 id="plan">Plan<a class="headerlink" href="#plan" title="Permanent link">¶</a></h2> <p>Every task should be worked by a plan the owner can see. Write it before you start coding, from PLAN.md or as your own 2-5 steps, and keep it current:</p> <div class="highlight"><pre><span></span><code>plan_set(task="acme-shop-12", steps=[ {"title": "Address table and model", "details": "Store addresses per account."}, {"title": "Address book page", "details": "Shoppers add, edit and remove addresses."}, {"title": "Picker at checkout", "details": "Pick a saved address instead of typing it."}], reason="from PLAN.md") plan_step_update(task="acme-shop-12", step=1, status="in_progress") plan_step_update(task="acme-shop-12", step=1, status="done", outcome="Migration and model", link="12", conformance="as planned") plan_step_update(task="acme-shop-12", step=2, status="done", conformance="deviation: editing waits for the shared form component") </code></pre></div> <p>The plan is a draft until the owner accepts it in the task's <strong>Plan</strong> tab or the coordinator calls <code>plan_accept</code>. Renaming or reordering steps of an accepted plan makes it a draft again; marking steps does not. The card shows <code>Plan 1/3 · now: Address book page</code>, deviations highlighted.</p> <p><strong>Work by plan (rule).</strong> By default a move to <code>dev</code> needs an accepted plan unless the task's <code>size</code> is <code>S</code> (<code>{kind = "plan_accepted", unless_size = ["S"]}</code>); a task without a size needs one. In <code>warn</code> mode the move gets a <code>Warning (rules)</code> line, in <code>strict</code> mode it is refused. Moving a task with no plan and no todo to <code>dev</code> also adds <code>Hint: … write down what you are going to do (todo or 2-3 steps with plan_set).</code></p> <p><strong>Your todo is the second layer.</strong> Claude Code's own todo list (<code>TaskCreate</code> / <code>TaskUpdate</code>, or <code>TodoWrite</code> in older builds) is read from the <code>PostToolUse</code> hook of your session and shown under the current plan step ("Now doing: …"), never merged into the plan. An S task without plan steps shows the todo as "Agent's plan, not accepted". The todo tools are off by default (Claude Code 2.1.309); turn them on in Claude Code's settings (relaybay setup does not write this):</p> <div class="highlight"><pre><span></span><code><span class="p">{</span><span class="w"> </span><span class="nt">"env"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"CLAUDE_CODE_ENABLE_TODO_TOOLS"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span> </code></pre></div> <p><strong>Reminders.</strong> When you linked your hook session (<code>hook_session</code>), the hooks remind you of the current step ("Plan of acme-shop-12 (accepted): step 2/3 'Address book page' — … Now doing: …") on session start, after a todo change (only when the text changed) and every <code>plan_reminder_every</code> tool calls (default 30).</p> <h2 id="session-status-from-hooks">Session status from hooks<a class="headerlink" href="#session-status-from-hooks" title="Permanent link">¶</a></h2> <p>Not an MCP tool: Claude Code's hooks call <code>POST /hooks/claude-code</code> (same Bearer token as <code>/mcp</code>) with the hook's input JSON. <code>relaybay setup</code> writes the hook entries (see <a href="../connect-claude-code/#3-live-session-status-hooks">connect-claude-code.md</a>).</p> <table> <thead> <tr> <th>Hook event</th> <th>Session status</th> <th>Activity shown</th> </tr> </thead> <tbody> <tr> <td><code>SessionStart</code></td> <td>working</td> <td><code>started (<source>)</code>; the answer adds the agent's <code>hook_session</code> id to its context, plus "You have N unread owner comment(s)…" when there are some</td> </tr> <tr> <td><code>UserPromptSubmit</code></td> <td>working</td> <td><code>prompt</code> (the prompt text is not stored)</td> </tr> <tr> <td><code>PreToolUse</code>, <code>PostToolUse</code></td> <td>working</td> <td>the tool name (the tool input is not stored)</td> </tr> <tr> <td><code>Notification</code></td> <td>waiting</td> <td>the notification message</td> </tr> <tr> <td><code>Stop</code></td> <td>idle</td> <td><code>turn finished</code></td> </tr> <tr> <td><code>SessionEnd</code></td> <td>ended</td> <td>the session is unbound from its tasks (history kept, <code>task.session_unbound</code> with <code>reason: "session_end"</code>), except after <code>/clear</code> or <code>/resume</code> and for desktop sessions</td> </tr> </tbody> </table> <p>Other events are ignored (<code>204</code>); an event with nothing to say also gets an empty <code>204</code>. Activity of a working session is written at most once per 5 s per session (the latest one wins); status changes are written at once. Each change is a <code>session.status_changed</code> event per task the session is bound to.</p> <p>A hook session is matched to a relaybay session in this order: on <code>SessionStart</code>, the desktop session named by <code>CLAUDE_CODE_HOST_SESSION_ID</code> (set by the Claude desktop app; after <code>/clear</code> or a restart the new Claude Code id moves to it by itself; a <code>claude -p</code> started from the window's tools runs unattended and never takes the window's link, nor an id another live session's agent linked); the exact link from a write with <code>hook_session</code>; otherwise, on first sight, the one desktop session bound to a task in the project (not ended) whose <code>repo_path</code> contains the hook's <code>cwd</code>. When more than one could match (or the folder belongs to several projects, or the bound desktop sessions are linked to other live hook sessions), nothing is guessed: the hook session stays unlinked and is listed on the board under Integrations until the agent passes its <code>hook_session</code>. With no desktop session in the project (or no project), it becomes a plain session named after the folder.</p> <h3 id="owner-comments-through-hooks">Owner comments through hooks<a class="headerlink" href="#owner-comments-through-hooks" title="Permanent link">¶</a></h3> <p>Comments the owner writes on a task (task dialog or task page) reach the agent of the session bound to the task, but only when the agent linked its hook session itself (<code>hook_session</code> on a write); a link guessed from the folder, an unlinked or ended session never gets comment text.</p> <ul> <li><code>UserPromptSubmit</code>, <code>PreToolUse</code>, <code>PostToolUse</code>: the answer is <code>{"hookSpecificOutput": {"hookEventName": …, "additionalContext": …}}</code> with the undelivered owner comments of the session's tasks, one line each — <code>Owner comment on acme-shop-12 (#7): …</code> — and how to answer (<code>comment_add</code> / <code>comment_ack</code>). They count as delivered (<code>comment.delivered</code>) and are not sent again. At most about 8,000 characters per answer (below Claude Code's 10,000 limit); the rest follow with the next events.</li> <li><code>Stop</code>: while delivered owner comments are not acknowledged (or new ones arrived), the answer is <code>{"decision": "block", "reason": …}</code> with the comments, so the agent handles them before it ends the turn. At most twice per turn per session and twice per comment in all; never when <code>stop_hook_active</code> is set before relaybay blocked in this turn (another hook is already continuing the turn); the per-turn counter resets on the next <code>UserPromptSubmit</code> and whenever a Stop is allowed. Claude Code's own cap (8 continuations) still applies.</li> <li>A rebind to another session hands the delivered-but-unread comments to the new session.</li> <li>An idle session sees a comment with its next prompt. The coordinator sees <code>comment.added</code> in <code>events_since</code> and can forward it; with the opt-in wake-up hook (<code>relaybay setup --wake</code>, below) the comment wakes the idle session itself.</li> </ul> <p>The command hook prints the server's answer only when it is a <code>200</code> with a JSON object, and still exits 0 within 0.3 s per network step; http hooks get the same JSON body.</p> <p><strong>Wake-up hook (opt-in).</strong> <code>relaybay setup --apply --wake</code> adds a separate command hook to <code>Stop</code> with <code>"asyncRewake": true</code> and a 1800 s <code>timeout</code>: <code><python> -I -m relaybay.hook_client --url …/hooks/claude-code/wait --token-file <path> --wait</code>. Claude Code runs it in the background after every turn; it long-polls <code>POST /hooks/claude-code/wait</code> for up to 25 minutes. When an owner comment arrives for the idle, exactly linked session, the server marks it delivered and answers <code>{"wake": text}</code>; the hook prints the text to stderr and exits 2, which wakes the session with the text as a system reminder. In every other case (nothing arrived, relaybay down, the session started working, a newer wait for the same session) it exits 0 silently. Checked live with Claude Code 2.1.309; <code>--no-wake</code> removes only this hook, a plain <code>--apply</code> keeps it.</p> <p><strong>Fallback risk:</strong> a Claude Code build that ignored <code>asyncRewake</code> would run this hook in the foreground and hold the end of every turn for up to 30 minutes, so <code>--wake</code> on a Claude Code older than 2.1.309 (or of unknown version) is refused unless <code>--force-wake</code> is given too. If turns end only after a long wait, run <code>relaybay setup --apply --no-wake</code>.</p> <h2 id="environments-and-access">Environments and access<a class="headerlink" href="#environments-and-access" title="Permanent link">¶</a></h2> <p>Each project can list its environments (<code>local</code>, <code>test</code>, <code>live</code>) on the board's <strong>Stands & access</strong> page: base URL, how to reach each one (ssh alias, kube context, database host, GitOps repository, log and monitoring links) and what agents may do there. relaybay stores <strong>references only</strong>: a key file path (<code>~/.ssh/acme_live</code>), an environment variable name (<code>$DB_PASSWORD</code>) or a vault path (<code>vault://kv/acme/db</code>), never a password, key or token; writes with something that looks like one are refused.</p> <ul> <li><strong>Levels</strong> — <code>none</code> (do not touch), <code>read</code> (look only), <code>ask</code> (ask the owner before each action), <code>full</code> (act as needed), per environment and optionally per capability (<code>sign_in</code>, <code>ssh</code>, <code>kubectl</code>, <code>db</code>, <code>deploy</code>). Where an environment sets none, the policy's level for its kind applies (default preset: local and test <code>full</code>, live <code>read</code>). Only the owner changes them on the board; agents call <code>environment_propose</code> or <code>policy_propose</code>.</li> <li><strong>Guard</strong> — the <code>PreToolUse</code> hook checks <code>Bash</code> commands and the input of MCP tools for the environments' names (whole tokens; a single word such as <code>app</code> only as an ssh, <code>--context</code>, <code>-h</code> or URL host) and answers by level and the policy's guard mode (<code>off</code> / <code>log</code> / <code>warn</code> / <code>block</code>, default <code>warn</code>; <code>log</code> only journals): <code>deny</code> (with the reason shown to you) for <code>none</code>, or for <code>read</code> when the command looks like a change (kubectl apply / delete / rollout restart …, helm upgrade, ssh with <code>systemctl restart</code> / <code>rm</code> …, SQL <code>insert</code> / <code>update</code> / <code>delete</code> / <code>drop</code> …, <code>git push</code>, and the policy's own patterns); <code>ask</code> (the user confirms) for <code>ask</code>; a warning in <code>additionalContext</code> instead of <code>deny</code> in <code>warn</code> mode. It is a safety net against mistakes, not a sandbox: it knows only the names listed on the board. Details and the full rule table: <a href="../SPEC/#spec-guard">SPEC §7</a>.</li> <li><strong>Journal</strong> — every guarded tool call that touches a live environment is recorded as a <code>live.action</code> event (tool, a short redacted command, decision) on your bound task; the owner sees them under Activity → Actions on live and in the task dialog.</li> </ul> <h2 id="policy">Policy<a class="headerlink" href="#policy" title="Permanent link">¶</a></h2> <p>What agents may do, what the guard counts as a change and what counts as a secret come from a policy: a built-in preset (<code>default</code>, <code>strict</code>, <code>trusted</code>), the global <code>policy.json</code>, and project and environment layers, a later one winning. <code>policy_get</code> shows the effective values and which layer set each; <code>policy_propose</code> asks the owner for a change. Test secrets (harmless test values on local and test stands, only when the policy allows them) are never sent to you unless the policy sets <code>expose_test_secrets_to_agents</code>. Keys and presets: <a href="../reference/policy/">policy reference</a>.</p> <h2 id="memory-and-documents">Memory and documents<a class="headerlink" href="#memory-and-documents" title="Permanent link">¶</a></h2> <p>Project documents (spec, status, brief, runbooks, ADRs), decisions, memory notes and blocks live in the database; the board shows them on <strong>Docs & rules</strong> (<code>/p/<project>/docs</code>). Every write that holds a secret value (a key, a token, <code>password: …</code>) is refused with the detector's name, never the value. Who may read what (coordinator everything, a manager its project, a session its task) is not enforced yet: every client with the token sees everything.</p> <table> <thead> <tr> <th>Tool</th> <th>Arguments</th> <th>Returns or does</th> </tr> </thead> <tbody> <tr> <td><code>doc_list</code></td> <td><code>project</code></td> <td>The project's documents: name, title, kind, version, last change, resource URI.</td> </tr> <tr> <td><code>doc_get</code></td> <td><code>project</code>, <code>doc</code>, <code>section?</code>, <code>version?</code></td> <td>The markdown (or one section: its heading and everything up to the next heading of the same or a higher level), after a header line with the current version to pass as <code>base_version</code>, and the section anchors. Anchors come from <code>#</code> headings: <code>## Gift cards</code> → <code>gift-cards</code>, a repeat → <code>gift-cards-2</code>.</td> </tr> <tr> <td><code>doc_update</code></td> <td><code>project</code>, <code>doc</code>, <code>body</code>, <code>reason</code>, <code>base_version</code>, <code>section?</code>, <code>title?</code>, <code>kind?</code>, <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code></td> <td>Writes a new version (<code>document.updated</code>). <code>base_version</code> 0 creates the document (title: <code>title</code>, else its first <code>#</code> heading, else the name; kind default <code>doc</code>). With <code>section</code>, <code>body</code> replaces that section: starting with a heading it replaces the heading too, otherwise only the text under it. A <code>base_version</code> that is not the current version saves nothing and fails with the current version number and the current text (or section) to merge. The same text again writes no version.</td> </tr> <tr> <td><code>doc_history</code></td> <td><code>project</code>, <code>doc</code>, <code>from_version?</code>, <code>to_version?</code></td> <td>The versions newest first (author, time, section, reason, size, base version); with both versions also their unified diff.</td> </tr> <tr> <td><code>decision_list</code></td> <td><code>project</code>, <code>task?</code>, <code>query?</code>, <code>include_superseded?</code>, <code>limit?</code> (default 20)</td> <td>Decisions newest first (with <code>query</code>, best matches first), with source and <code>superseded by #n</code>. Current ones only unless <code>include_superseded</code>.</td> </tr> <tr> <td><code>memory_add</code></td> <td><code>scope</code> (<code>global</code> / <code>role</code> / <code>project</code>), <code>kind</code> (<code>user</code> / <code>feedback</code> / <code>project</code> / <code>reference</code>), <code>title</code>, <code>body</code>, <code>project?</code>, <code>role?</code>, <code>tags?</code>, <code>source?</code>, <code>review_at?</code>, <code>op?</code>, <code>target?</code>, <code>base_version?</code>, <code>force_new?</code>, <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code></td> <td>Without <code>op</code>: looks for current notes of the same scope sharing a word with the title; if there are any, writes nothing and lists them (id, title, first line, title similarity). Call again with <code>op</code> = <code>add</code>, <code>update</code> (rewrites <code>target</code>; needs <code>base_version</code> = the candidate's <code>v<n></code>, a newer version is a conflict), <code>supersede</code> (a new note replaces <code>target</code>, which stays in history) or <code>noop</code>. With no candidates it adds directly. A <strong>strong match</strong> is a candidate whose stemmed title words overlap the new title's by at least 0.6 (shared / all, stop words left out); <code>op=add</code> next to one needs <code>force_new=true</code>. <code>memory.added</code> / <code>memory.updated</code>.</td> </tr> <tr> <td><code>memory_update</code></td> <td><code>id</code>, <code>base_version</code>, <code>title?</code>, <code>body?</code>, <code>tags?</code>, <code>review_at?</code> (<code>"-"</code> clears tags and the date), <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code></td> <td>Changes a note (<code>memory.updated</code>); a stale <code>base_version</code> fails with the current note to merge. A superseded note cannot be changed.</td> </tr> <tr> <td><code>memory_get</code></td> <td><code>id</code></td> <td>The note in full (scope, kind, version, author, time axes, source, tags, review date, read count); reading it counts (<code>last_read_at</code>, <code>read_count</code>).</td> </tr> <tr> <td><code>memory_search</code></td> <td><code>query</code>, <code>scope?</code>, <code>project?</code>, <code>kinds?</code>, <code>limit?</code> (default 10)</td> <td>Hits over notes, decisions, documents (current versions), reports and comments: kind, id, project and task, title, and the line that holds most query words (<code>line 14: …</code>). <code>kinds</code> picks among <code>note</code>, <code>decision</code>, <code>doc</code>, <code>report</code>, <code>comment</code>; <code>scope</code> narrows to notes of that scope. With <code>project</code>: its items plus global and role notes. Superseded notes and decisions are left out.</td> </tr> <tr> <td><code>block_get</code></td> <td><code>label?</code>, <code>project?</code></td> <td>A block (label, version, characters used and limit, owner-only mark, text), or the list of blocks of the project or the global scope.</td> </tr> <tr> <td><code>block_set</code></td> <td><code>label</code>, <code>body</code>, <code>base_version</code>, <code>project?</code>, <code>description?</code>, <code>char_limit?</code>, <code>session?</code>, <code>session_title?</code>, <code>hook_session?</code></td> <td>Writes a block (<code>block.updated</code>); <code>base_version</code> 0 creates it (limit 2000 characters unless <code>char_limit</code>). Over the limit it fails with the count; a stale <code>base_version</code> fails with the current text. Owner-only blocks are read-only here: the owner edits them on the board.</td> </tr> </tbody> </table> <p>Search uses SQLite FTS5 when the SQLite build has it: words match by prefix and by their Snowball stem (English, or Russian for Cyrillic words), diacritics are ignored, a title hit ranks highest. User text is turned into quoted words, so <code>"</code>, <code>(</code>, <code>AND</code>, <code>*</code> never cause a syntax error. Without FTS5 the same search runs as <code>LIKE</code> substring matches of each word or its stem (case-insensitive for ASCII letters only).</p> <h2 id="resources">Resources<a class="headerlink" href="#resources" title="Permanent link">¶</a></h2> <p><code>relaybay://project/{slug}</code> — the same markdown as <code>project_snapshot</code>, so a session can attach a project snapshot as context.</p> <p><code>relaybay://project/{slug}/environments</code> — the project's environments map (same as <code>environments_get</code>); references only, never secrets.</p> <p><code>relaybay://rules</code> — the global working rules (same as <code>rules_get()</code>).</p> <p><code>relaybay://rules/{project}</code> — a project's working rules, merged (same as <code>rules_get(project=…)</code>).</p> <p><code>relaybay://doc/{project}/{doc}</code> — a project document's current markdown (same as <code>doc_get</code>).</p> <p><code>relaybay://schema/task</code> — the editable task fields as a markdown table (field, type, description, how to clear it), generated from the same list the server validates writes against.</p> <h2 id="working-rules">Working rules<a class="headerlink" href="#working-rules" title="Permanent link">¶</a></h2> <p>One structured document with a fixed set of rule kinds:</p> <table> <thead> <tr> <th>Key</th> <th>Meaning</th> <th>Default</th> </tr> </thead> <tbody> <tr> <td><code>report_format</code></td> <td><code>first_line</code> (the pattern shown to agents), <code>states</code>, <code>sizes</code> (subsets of the built-in states and sizes)</td> <td><code>state · size · gist</code>, all states, <code>S</code>/<code>M</code>/<code>L</code></td> </tr> <tr> <td><code>stage_requirements</code></td> <td>per target stage, a list of requirements: <code>{kind = "report_since_stage_change"}</code> (a report added since the task's last stage change), <code>{kind = "field_present", field = "<task field>"}</code> or <code>{kind = "plan_accepted", unless_size = ["S"]}</code> (an accepted plan unless the task's size is listed; see <em>Plan</em>)</td> <td><code>dev</code>: an accepted plan unless size <code>S</code>; <code>test</code>: a report since the last stage change; <code>done</code>: <code>progress</code></td> </tr> <tr> <td><code>description_template</code></td> <td>markdown template for new task descriptions (shown in the rules)</td> <td>none</td> </tr> <tr> <td><code>guidance</code></td> <td>free-text markdown shown to agents</td> <td>one short paragraph</td> </tr> </tbody> </table> <p>Layers, merged in this order:</p> <ol> <li>built-in defaults;</li> <li>the global <code>[rules]</code> table in <code>config.toml</code> (see the README);</li> <li>a project override: a row in the table <code>project_rules</code> (<code>project_id</code>, <code>body</code> = the same document as JSON, <code>updated_at</code>). There is no write tool yet; insert the row with any SQLite client, e.g. <code>INSERT INTO project_rules (project_id, body, updated_at) SELECT id, '{"stage_requirements": {"test": []}}', datetime('now') FROM project WHERE slug = 'acme-shop';</code></li> </ol> <p>Each layer may be partial. <code>report_format</code> merges per key, <code>stage_requirements</code> per stage (a stage's list replaces the earlier list; <code>[]</code> drops the stage's requirements), <code>description_template</code> replaces; the config <code>guidance</code> replaces the built-in text, a project's <code>guidance</code> is appended after the global one. Every layer is validated (unknown keys, stages, rule kinds or fields are errors naming the key). An error in <code>config.toml</code> stops the server at startup. A broken project override (invalid JSON or rules) never stops the server or the project: it is logged once as a warning, the project uses the global rules, and <code>rules_get</code> / <code>relaybay://rules/{project}</code> and the first line of <code>project_snapshot</code> add "project override ignored: <error>".</p> <p>The rules hash is the first 8 hex digits of the SHA-256 of the merged document plus <code>rules_mode</code> (canonical JSON), so it changes exactly when the rules an agent reads change, including a switch between <code>warn</code> and <code>strict</code>. <code>project_snapshot</code> starts with <code>Rules <hash>: accepted plan before dev, report before test, progress before done (read relaybay://rules/<project>)</code>.</p> <p><code>rules_mode</code> (config, default <code>warn</code>) decides what an unmet stage requirement does: <code>warn</code> lets the write through with a warning, <code>strict</code> refuses it.</p> <h2 id="schema-changes-and-long-running-sessions">Schema changes and long-running sessions<a class="headerlink" href="#schema-changes-and-long-running-sessions" title="Permanent link">¶</a></h2> <p>An MCP client keeps the parameter schema of a tool it has already loaded. A Claude Code session that was running before relaybay was upgraded can reconnect after a server restart and see new tools, while an existing tool (such as <code>task_update</code>) keeps its old parameter list, so a new field looks unavailable. Two ways out:</p> <ul> <li>Use <code>task_patch(task, fields={...})</code> for fields your client does not know yet: <code>fields</code> is a plain JSON object, validated on the server with the same rules as <code>task_update</code> and <code>task_set_stage</code>. The allowed names are listed at the end of <code>task_get</code> ("Editable fields: …") and in the resource <code>relaybay://schema/task</code> (name, type, description, how to clear).</li> <li>Refresh the schemas: reconnect the server in the client (<code>/mcp</code> in the Claude Code CLI) or start a new session.</li> </ul> <h2 id="example">Example<a class="headerlink" href="#example" title="Permanent link">¶</a></h2> <div class="highlight"><pre><span></span><code>project_upsert(slug="acme-shop", name="Acme Shop") task_create(project="acme-shop", title="Checkout page", branch="feat/checkout", description="Let customers pay by card without leaving the shop.", session="local_8f14e45f-ceea-467f-a0b6-1f1a2b3c4d5e", session_title="Checkout") → Created **acme-shop-1** “Checkout page” · stage plan · version 1. task_set_stage(task="acme-shop-1", stage="dev", next_step="Card form", waiting_on="-", session="local_8f14e45f-ceea-467f-a0b6-1f1a2b3c4d5e") report_add(task="acme-shop-1", body="verified · M · checkout works\n\nTests pass.", progress="- Cart with totals\n- Card form with validation\n- Tests", next_step="Review the PR", waiting_on="reviewer", session="local_8f14e45f-ceea-467f-a0b6-1f1a2b3c4d5e") → Report #1 added to **acme-shop-1** (verified · M): checkout works task_unbind_session(task="acme-shop-1", session="local_8f14e45f-ceea-467f-a0b6-1f1a2b3c4d5e") → Unbound Checkout (local_8f14e45f-ceea-467f-a0b6-1f1a2b3c4d5e) from **acme-shop-1** “Checkout page” · stage dev · version 4. </code></pre></div> </article> </div> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type="button" class="md-top md-icon" data-md-component="top" hidden> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Back to top </button> </main> <footer class="md-footer"> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class="md-copyright"> <div class="md-copyright__highlight"> MIT License </div> Made with <a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener"> Material for MkDocs </a> </div> </div> </div> </footer> </div> <div class="md-dialog" data-md-component="dialog"> <div class="md-dialog__inner md-typeset"></div> </div> <script id="__config" type="application/json">{"annotate": null, "base": "..", "features": ["navigation.sections", "navigation.top", "navigation.indexes", "content.code.copy", "search.highlight"], "search": "../assets/javascripts/workers/search.a7633da7.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script> <script src="../assets/javascripts/bundle.c21346df.min.js"></script> </body> </html>