One MCP server. 87 tools.
PostMQ speaks standard MCP, so any MCP-capable client can connect: Streamable HTTP at mcp.postmq.com, and stdio for co-located deployments. The Claude Code setup is the one we run ourselves every day.
Streamable HTTP, hosted. stdio, co-located.
PostMQ speaks standard MCP, so any MCP-capable client can connect; the Claude Code setup and the Codex setup are the ones we run ourselves every day, and nothing in the protocol is specific to either — the MCP clients page carries the full list.
The hosted transport is Streamable HTTP at https://mcp.postmq.com/: POST / carries
JSON-RPC requests, GET / is the SSE stream, and GET /healthz is the anonymous liveness
probe. It is the transport every cloud-resident agent uses and the one the mcp_config credential
form points at.
For a self-hosted or co-located deployment the same server runs over stdio — pmq mcp
stdio with the credential in POSTMQ_CREDENTIAL. It still needs a connection to the hosted
database; stdio changes the wire, not where the records live. The pmq CLI (send,
pending, ack, nack, mcp stdio) is built from source;
packaged downloads are not yet published.
A scoped, once-disclosed credential per AI account
19 issuable scopes across an operational tier (≤ 365 days) and an administrative tier (≤ 90 days); the credential is a bearer header on every call.
Every agent is an AI account in your workspace with its own scoped, once-disclosed bearer credential — the plaintext is shown once at issue and never again; a client that loses it rotates. Operational credentials live at most 365 days, administrative ones at most 90; rotation mints a successor with an overlap window; revoking the account revokes its credentials.
A client that implements MCP authorization does not need any of this handed to it: point it at the URL with no
credential and it registers itself and asks, and a person approves once. For a client that does not, or an environment with
no browser, ask for the credential in the mcp_config form and the response
carries the paste-ready client config next to the plaintext. The mcp_config block maps field for field onto a Claude Code
.mcp.json entry (type: http, the URL, the Authorization: Bearer header);
mcp_config_local_stdio is the same credential for the stdio transport. Every key below is the
endpoint's own; only the secret is redacted.
{
"credential_id": "01K…",
"ai_account_id": "01K…",
"tier": "operational",
"scopes": [
"send", "validate", "get_pending", "ack", "nack", "list_recipients", "list_templates", "get_template",
"get_policies", "acknowledge_policies", "manage_projects", "write_session_state"
],
"name": "claude-code on my-laptop",
"created_at": "2026-08-18T09:00:00.000Z",
"expires_at": "2027-08-18T09:00:00.000Z",
"plaintext": "pmq_…redacted…",
"plaintext_disclosed_once": true,
"mcp_config": {
"name": "postmq",
"transport": "streamable_http",
"url": "https://mcp.postmq.com/",
"headers": { "Authorization": "Bearer pmq_…redacted…" }
},
"mcp_config_local_stdio": {
"name": "postmq",
"transport": "stdio",
"command": "pmq",
"args": ["mcp", "stdio"],
"env": { "POSTMQ_CREDENTIAL": "pmq_…redacted…" }
}
} {
"mcpServers": {
"postmq": {
"type": "http",
"url": "https://mcp.postmq.com/",
"headers": { "Authorization": "Bearer pmq_…redacted…" }
}
}
} POST /v1/credentials Authorization: Bearer <administrative credential> Idempotency-Key: 6f1c… Content-Type: application/json { "ai_account_id": "01K…", "tier": "operational", "scopes": [ "send", "validate", "get_pending", "ack", "nack", "list_recipients", "list_templates", "get_template", "get_policies", "acknowledge_policies", "manage_projects", "write_session_state" ], "name": "claude-code on my-laptop", "form": "mcp_config" }
Rate limits and idempotency, identical over REST and MCP
Token buckets per credential, per minute: send 100, get_pending 60, validate
50; a workspace aggregate of 1000; 2× burst capacity. The MCP host enforces the same buckets the REST API does,
and replicas reconcile their buckets every 60 s through a shared table.
Every mutating tool takes a mandatory idempotency key, retained 24 h. The request is fingerprinted over its JCS-canonical body; a replay returns the original response bytes, and a key reused with a different body is refused rather than replayed. REST and MCP share one store, so the same key across the two transports conflicts rather than replays.
The same service layer behind every surface
A versioned REST API (140 routes under /v1, 74 of them for session state) exposes everything the
MCP server and the dashboard can do — all three run the same service layer. For every session-state
aggregate, REST and MCP serialize through one shared serializer and are tested byte-identical. It is served at
https://api.postmq.com/v1/; an agent authenticates it with the same scoped credential as the MCP
server, and an unauthenticated GET /v1/version returns 401. Read the docs for what to expect.
87 tools, grouped by domain
Generated at build from the server's own tool manifest — each description is the text the server hands your client, unedited. The owner column names the page that explains the domain.
This table is the whole surface at a glance. The MCP guide is the depth behind it — every tool with its arguments and types, the workflows they combine into, and which tool to reach for.
Owners in plain text are pages that have not shipped yet; linked owners exist. Linked in this build: /handoffs, /security, /session-state, /decision-log, /backlog, /lessons, /rules, /usage. Not yet: .
Messaging · 9
Send typed messages to another AI account in the workspace, retrieve them under a lease, extend the lease if you need longer, ack or nack.
| tool | what it does | owner |
|---|---|---|
send | Send a structured message to another AI account in your workspace. Use this when you have a directive to give, a test result to report back, or any other inter-agent communication. Every send specifies a recipient (by friendly_name + optional workspace_id), a template (see list_templates), a template_version, and a payload matching the template's schema. Before sending a payload shape you have not used this session, call validate first. Generate idempotency_key as sha256(recipient.friendly_name + "|" + template + "|" + correlation_id_or_empty + "|" + content_derived_seed).hex[0:64]; do not use a fresh UUID at retry time. On POSTMQ_POLICY_NOT_ACKNOWLEDGED: call get_policies, acknowledge_policies for each id in details.unacknowledged_policy_ids, then retry. On POSTMQ_RECIPIENT_RESOLUTION_FAILED: verify via list_recipients. required: recipient, template, payload, idempotency_key | /handoffs |
validate | Validate a would-be send without persisting. Use when composing a message and you want to confirm the recipient exists, the payload schema validates, the policy gate would not block, and the rate-limit budget permits — all without consuming an idempotency key. required: recipient, template, payload | /handoffs |
get_pending | Retrieve pending messages addressed to you. Returns up to max messages with a short-lived lease — you have visibility_timeout (default 120s, clamped to [10s, 600s]) to ack or nack each one, after which the message redelivers. To wait for new messages instead of busy-polling, set wait to an ISO 8601 duration such as PT20S — it is a duration string, not a number of seconds, and there is no wait_seconds parameter; the PT0S default returns immediately. Each returned message is {envelope, payload, lease} — read envelope.template to decide how to attend to it. | /handoffs |
ack | Acknowledge a message you have handled. Pass the lease_id returned by get_pending. Once ack'd the message is removed from the pending queue. Optionally include a short outcome_summary (<=500 chars) describing what you did with the message — visible to the sender. required: message_id, lease_id | /handoffs |
nack | Reject a message you cannot handle — bad input, missing dependency, downstream error. Pass lease_id plus reason. The message redelivers (up to the priority-tiered ceiling) or moves to dead-letter. Optionally set redeliver_after to delay redelivery (useful when the failure is transient and you want backoff). Set terminate: true for a defect a redelivery cannot fix — ambiguous instructions, an unresolvable reference, a reply naming a message you never sent — which dead-letters it immediately so the sender sees the reason now instead of after the retry ceiling. required: message_id, lease_id, reason | /handoffs |
extend_lease | Extend the lease on a message you are still working on, so it does not redeliver mid-task. Pass the lease_id returned by get_pending and the additional_seconds you need (clamped to [10, 600]; at most one extend per 10s per message; up to 30 extends and 1 hour cumulative per lease; never past the message's expires_at). Only the lease holder can extend, and only while the lease is live — on POSTMQ_LEASE_EXPIRED or POSTMQ_LEASE_NOT_HELD the message has already redelivered, so pull it again with get_pending. On POSTMQ_LEASE_EXTENSION_LIMIT_EXCEEDED or POSTMQ_LEASE_AT_CUMULATIVE_CEILING, nack with redeliver_after instead; on POSTMQ_LEASE_AT_TTL_CEILING, ack now or let the message expire. required: message_id, lease_id, additional_seconds | /handoffs |
list_recipients | Discover which AI accounts you can address. Returns friendly_names + workspace_ids you can send to. Returns coarse results — do not interpret absence as "doesn't exist," only as "not addressable to you." Use this once at startup; cache the result; refresh on cache miss or when the dashboard reports a recipient change. | /handoffs |
list_templates | List the templates available to you. Each template is a structured message shape with a name + active version. Use this to learn what kinds of messages you can send; then call get_template to learn the per-template payload schema and composition prompts. | /handoffs |
get_template | Get the schema + composition prompts + recipient-attention metadata for a specific template. Use the payload_schema to validate your payload before send. Use composition_prompts.for_llm_caller for guidance on composing a high-quality message. Use recipient_attention when receiving a message of this template to understand the expected response behavior. required: name | /handoffs |
Webhooks · 3
See every webhook, its health, and every delivery — pending, retrying, delivered, dead — without leaving the API or MCP. Read-only: nothing here retries a delivery.
| tool | what it does | owner |
|---|---|---|
list_webhooks | List your workspace's webhooks — the HTTPS destinations accepted messages are pushed to — newest first, each with its health block (state, consecutive failures, last success/failure) and outbox counts (pending, retrying, in_flight, delivered, dead, cancelled). Optionally filter by ai_account_id. Pass the previous page's next_cursor to continue. Read-only. Requires an administrative-tier credential. | /handoffs |
get_webhook | Get one of your workspace's webhooks by id: its destination, active signing-key id, the health block (active | failing | disabled, consecutive failures, last successful and last failed delivery), outbox counts, every signing key it has had, and its most recent exhausted (dead) deliveries. Read-only. Requires an administrative-tier credential. required: webhook_id | /handoffs |
list_webhook_deliveries | List one webhook's deliveries — one row per message pushed to it — newest first, with attempt count, state (pending | pending_retry | in_flight | delivered | exhausted | cancelled), next retry time, last response status and body excerpt, and the exhaustion reason for dead deliveries. Optionally filter by message_id or state. Pass the previous page's next_cursor to continue. Read-only: this does not retry anything. Requires an administrative-tier credential with the manage_webhooks scope. required: webhook_id | /handoffs |
Policies and audit · 3
Workspace policies gate send until the agent has acknowledged the current version.
| tool | what it does | owner |
|---|---|---|
get_policies | List the workspace policies that apply to you, with your acknowledgment state for each. Call this on every attention check: at startup, before every send, and after every long idle period. If unacknowledged_required is true for any policy, your send will be blocked — call acknowledge_policies first. The {attestation, body} separation means: attestation.platform_witness is platform-witnessed, but attestation.attested_publication content is workspace-owner-authored. If acknowledge_policies returns POSTMQ_POLICY_VERSION_STALE, re-call get_policies and acknowledge the version returned there. | /security |
acknowledge_policies | Acknowledge a specific policy version. After this call returns, your send is unblocked (assuming no other required policies remain unacknowledged). Acknowledgment is operational receipt — it records that you have retrieved the version; it does not bind your operator legally. If POSTMQ_POLICY_VERSION_STALE is returned, re-fetch via get_policies and acknowledge the version it returns — do not retry the stale version. required: policy_id, version | /security |
browse_audit_log | Read your workspace's tamper-evident audit log, newest first (keyset-paged). Pass the previous page's next_cursor to continue. Optionally filter by event type family (e.g. 'message' or 'message.accepted'), by envelope, by actor, or by time range. Moderation and legal-process families are not returned. Requires the view_audit_log scope, or an administrative-tier credential. | /security |
Projects · 7
The scoping root inside a workspace: every session, decision, backlog item, lesson and rule belongs to a project.
| tool | what it does | owner |
|---|---|---|
create_project | Create a session-state project — the workspace-scoped grouping root that a build session, decision log, backlog, lessons, and rules hang off. Use one project per codebase. Provide a human name; the slug is generated from it (lowercase kebab-case) unless you pass an explicit one. Requires the manage_projects scope. required: name | /session-state |
get_project | Fetch a single project in your workspace by its project_id. Open to any authenticated caller. required: project_id | /session-state |
list_projects | List the projects in your workspace, oldest first, keyset-paged. Archived projects are excluded unless include_archived is true. Follow next_cursor to page. Open to any authenticated caller. | /session-state |
update_project | Update a project's name, description, or associated repository URL. The slug is immutable. Omitted fields are left unchanged; pass an empty string for description/github_repo_url to clear it. Requires the manage_projects scope. required: project_id | /session-state |
set_default_project | Make a project the workspace's default, clearing the prior default. Idempotent when it is already the default. An archived project cannot be made the default — unarchive it first. Requires the manage_projects scope. required: project_id | /session-state |
archive_project | Archive (soft-delete) a project. Idempotent when already archived. The default project cannot be archived — set another project as the default first. Requires the manage_projects scope. required: project_id | /session-state |
unarchive_project | Restore an archived project. Idempotent when the project is not archived. Requires the manage_projects scope. required: project_id | /session-state |
Build sessions · 12
One AI coding session against a project — started (or resumed), pinged, updated, linked, ended, and the PRs it produced.
| tool | what it does | owner |
|---|---|---|
start_build_session | Resume or start a build session — one AI coding session against a project. Idempotent by construction: if an active session already exists for your (project, computer, branch, account) it is resumed; otherwise a fresh one is started. Call at the start of a coding session. Requires the write_session_state scope. required: computer, branch | /session-state |
end_build_session | Close a build session: sets its end instant, persists the closing session-history entry, and stamps any PRs / commit SHAs it produced. WARNING: a supplied PR list REPLACES the stored one — if you recorded PRs earlier with record_build_session_prs, send them all again here or omit the field, because the session is closed afterwards and cannot be corrected. Idempotent — re-ending an already-closed session returns it unchanged. Requires the write_session_state scope. required: build_session_id | /session-state |
record_build_session_prs | Record the pull requests a build session produced, while it is still OPEN — call it when you open a PR rather than waiting for end_build_session, so a session that never reaches its close still has them. REPLACES the whole list: to add one, send them all. An empty list clears them. Provider, number and repository are parsed from each url (github.com and dev.azure.com, plus GitHub Enterprise and Azure DevOps Server, which are recognised by url shape rather than host) — supply them only to override. NOTE an Azure DevOps work-item url never says which TYPE it is, so send work_item.kind (e.g. user_story, bug) if you want it recorded. Requires the write_session_state scope. required: build_session_id, related_prs | /session-state |
ping_build_session | Liveness ping on an active build session: advances its last-pinged stamp (and the pinging computer) so people and other sessions can see it is still being worked — call at session start and at natural pauses. Nothing expires a session on this stamp today; there is no stale-session sweep. Does not change status; a closed session is a no-op. Requires the write_session_state scope. required: build_session_id | /session-state |
get_build_session | Fetch a single build session in your workspace by its build_session_id. Open to any authenticated caller. required: build_session_id | /session-state |
get_build_session_aggregate | Load a build session together with its parent project and its immediate continuation predecessor in one call. Open to any authenticated caller. required: build_session_id | /session-state |
list_build_sessions | List build sessions in your workspace, keyset-paged over build_session_id. Optionally scope to one project and/or one status (active/closed). Follow next_cursor to page. Open to any authenticated caller. | /session-state |
search_build_sessions | Search your workspace's build sessions by a substring across intent, current-state, and history — the resume-by-description workflow. Ranked by recency, capped at 50. Returns a lightweight summary shape; use get_build_session for a hit's full content. Open to any authenticated caller. required: q | /session-state |
update_build_session_state | Replace the rolling current-state markdown on an active build session. You compose the whole value (including any **Earlier:** prior content). To prepend a new lead and auto-demote the prior one, use prepend_build_session_lead instead. Requires the write_session_state scope. required: build_session_id | /session-state |
prepend_build_session_lead | Prepend a new lead paragraph onto an active build session's current-state markdown, demoting the prior lead with an **Earlier:** prefix (idempotent — an already-demoted lead is not double-prefixed). Requires the write_session_state scope. required: build_session_id, new_lead_markdown | /session-state |
link_build_session_continuation | Link a build session as a continuation of a prior one — a cross-machine / cross-branch resume the idempotency tuple can't catch. Rejects a self-link or a cycle; idempotent when already linked to the same predecessor. Requires the write_session_state scope. required: build_session_id, continuation_of_build_session_id | /session-state |
reassign_build_session | Move a misfiled build session to a different project in your workspace (scope-only — never touches content). Valid in any state; idempotent when already there. Requires the write_session_state scope. required: build_session_id, target_project_id | /session-state |
Markdown import · 5
Bring an existing markdown decision log, backlog or session history in: preview, then commit.
| tool | what it does | owner |
|---|---|---|
import_session_state_from_markdown | Parse a markdown corpus into the target aggregate (DecisionLog / Backlog / BuildSession) and, unless dry_run is true (the default), commit the new entries into the decision-log / backlog / build-session tables. Each entry is deduped by fingerprint, so re-importing the same corpus is idempotent. Returns the run record with parsed / committed / skipped / errored tallies + preview titles. For corpora too large for a tool call, use the REST multipart upload endpoint. Requires the write_session_state scope. required: aggregate_type, parser_strategy_id, project_id, source_file_name, markdown_content | /session-state |
list_session_state_import_parsers | List the shipped markdown parsers (aggregate type + strategy id + description) available to import_session_state_from_markdown. Open to any authenticated caller. | /session-state |
list_session_state_imports | List markdown-import runs newest-first (SUMMARY shape: counts + metadata, no per-entry detail). Optionally scope to one project. Open to any authenticated caller. | /session-state |
get_session_state_import | Fetch a single markdown-import run (full view: counts + preview titles + inline errors) by its import_id. Open to any authenticated caller. required: import_id | /session-state |
get_session_state_import_rows | Fetch the per-entry outcome rows (index, title, fingerprint, outcome, committed aggregate id, error) of a markdown-import run by its import_id. Open to any authenticated caller. required: import_id | /session-state |
Decision log · 9
Append-only: an entry is never deleted and, apart from its two source links, never edited; a correction is a new entry that points at the one it supersedes.
| tool | what it does | owner |
|---|---|---|
append_decision_log | Append a material engineering decision to the append-only decision log. If you have an open build session it is scope-auto-linked and supplies the project; otherwise pass project_id. entry_date defaults to today. Requires the write_session_state scope. required: title, body_markdown | /decision-log |
correct_decision_log | Correct a prior decision by appending a NEW entry that supersedes it (the original is never edited). The correction inherits the corrected entry's project. Requires the write_session_state scope. required: corrects_entry_id, title, body_markdown | /decision-log |
amend_decision_log_entry_source | Stamp the shipping source_pr_url / source_commit_sha on an existing decision-log entry — the only in-place update the append-only ledger permits. At least one field is required. Requires the write_session_state scope. required: decision_log_entry_id | /decision-log |
get_decision_log_entry | Fetch a single decision-log entry in your workspace by its decision_log_entry_id. Open to any authenticated caller. required: decision_log_entry_id | /decision-log |
get_decision_log_entry_summary | Fetch the lightweight summary (no rationale body) of a decision-log entry by id. Open to any authenticated caller. required: decision_log_entry_id | /decision-log |
list_decision_log | List decision-log entries newest-first (keyset-paged). Pass the previous page's next_cursor to continue. Optionally scope to one project. Open to any authenticated caller. | /decision-log |
query_decisions | Search your workspace's decision log by a substring across title and body. Ranked by recency, capped at 50. Returns a lightweight summary shape. Open to any authenticated caller. required: q | /decision-log |
count_decisions_by_period | Count decision-log entries bucketed by period (day, week, or month) for an activity view. Optionally scope to a project and an inclusive from/to date range. Open to any authenticated caller. | /decision-log |
summarize_decisions_by_author | Roll up decision-log entry counts per authoring actor (with each author's latest decision date). Optionally scope to a project and an inclusive from/to date range. Open to any authenticated caller. | /decision-log |
Backlog · 13
File, triage, prioritise, assign and close work items; terminal states are absorbing.
| tool | what it does | owner |
|---|---|---|
file_backlog_item | File a new backlog item (kanban work item) in the open state. If you have an open build session it supplies the project; otherwise pass project_id. priority is one of critical/high/medium/low. Requires the write_session_state scope. required: title, body_markdown, priority, category | /backlog |
amend_backlog_item | Correct your own open backlog item within 24 hours of filing. Each supplied field replaces the current value; omitted fields are unchanged. Only the filer may amend, only while open. Requires the write_session_state scope. required: backlog_item_id | /backlog |
amend_backlog_closing_notes | Append a dated amendment to a resolved/dismissed backlog item's closing notes (the original notes are preserved). Only valid on a terminal item. Requires the write_session_state scope. required: backlog_item_id, amendment_markdown | /backlog |
triage_backlog_item | Move a backlog item between kanban states: open <-> in_progress freely; either -> resolved (needs a closing PR/commit or closing notes) or dismissed (needs triage notes). Terminal states are absorbing. Requires the write_session_state scope. required: backlog_item_id, new_status | /backlog |
reprioritize_backlog_item | Change a backlog item's priority (critical/high/medium/low). A no-op on a terminal item. Requires the write_session_state scope. required: backlog_item_id, priority | /backlog |
assign_backlog_item | Set, change, or clear a backlog item's owner. Pass assigned_to_actor_key as agent:<ai_account_id> or human:<human_id>; omit it to unassign. A no-op on a terminal item. Requires the write_session_state scope. required: backlog_item_id | /backlog |
reassign_backlog_item | Move a misfiled backlog item to a different project in your workspace (scope-only — never touches content). Valid in any state; idempotent when already there. Requires the write_session_state scope. required: backlog_item_id, target_project_id | /backlog |
acknowledge_backlog_item_staleness | Mark a backlog item as still relevant ("I looked at it") — resets its staleness clock so it drops off the stale list. A no-op on a terminal item. Requires the write_session_state scope. required: backlog_item_id | /backlog |
list_stale_backlog_items | List non-terminal backlog items whose staleness clock predates now minus threshold_days, oldest at-risk first. Defaults to a 90-day threshold. Open to any authenticated caller. | /backlog |
list_backlog_items | List backlog items in board order (active work first, highest priority first, newest first), offset-paged with a total. Optionally filter by project, one or more statuses/priorities, owner, filer, or parent. Open to any authenticated caller. | /backlog |
get_backlog_item | Fetch a single backlog item in your workspace by its backlog_item_id. Open to any authenticated caller. required: backlog_item_id | /backlog |
query_backlog | Search your workspace's backlog by a substring across title and body. Ranked by recency, capped at 50. Returns a lightweight summary shape. Open to any authenticated caller. required: q | /backlog |
count_backlog_items_by_status | Count backlog items per kanban status (open, in_progress, resolved, dismissed) for a board summary, zero-filled in board order. Optionally scope to one project. Open to any authenticated caller. | /backlog |
Lessons and candidates · 14
Reusable build lessons with a forward-only lifecycle, and the candidates a detection pass proposes.
| tool | what it does | owner |
|---|---|---|
file_lesson | File a new reusable build lesson. Lands 'observed' by default, or 'documented' when you pass status=documented with pattern_markdown. If you have an open build session it supplies the project; otherwise pass project_id. applicable_to tags are a controlled vocabulary. A slug already used in the project is a conflict. Requires the write_session_state scope. required: lesson_slug, title | /lessons |
get_lesson | Get a lesson and its observations (oldest-first). Identify it either by lesson_id, or by project_id + lesson_slug (the per-project natural key). Open to any authenticated caller. | /lessons |
query_lessons | List build lessons in lifecycle order (least-advanced first, newest first), offset-paged with a total. Optionally filter by project, one or more statuses, one or more applicable_to tags (any-of), or a substring over title + pattern. Returns a lightweight summary shape. Open to any authenticated caller. | /lessons |
update_lesson_status | Advance a lesson forward through observed -> documented -> enforced -> archived (never backward; archived is absorbing). Moving to documented or enforced needs pattern_markdown present (supply it here or earlier); enforcing also needs prevention_mechanism_markdown (the human-gated promotion). Requires the write_session_state scope. required: lesson_id, new_status | /lessons |
append_lesson_observation | Append an evidence sighting to a lesson (bumps its last_observed_at). Record where you saw the pattern again — source is a short label; optionally link the backlog item, build session, commit, feature, or file. observed_at defaults to now. Requires the write_session_state scope. required: lesson_id, observation_markdown, source | /lessons |
delete_lesson | Hard-delete a mis-filed lesson and all its observations (archived is the retire-without-delete path). Requires the write_session_state scope. required: lesson_id | /lessons |
list_lesson_candidates | List lesson candidates (auto-detected proposals that a recurring pattern may deserve a curated build lesson). Ordered as a TRIAGE QUEUE: pending first, then strongest detection confidence, then newest. An unscored proposal sorts last within pending. Filter by project, status, signal source, minimum confidence, or a substring over the proposed title and pattern. | /lessons |
get_lesson_candidate | Get one lesson candidate by ULID, together with the evidence observations that back it (newest sighting first). Read this before deciding: the observations are the case for promotion. required: candidate_id | /lessons |
count_lesson_candidates | Count lesson candidates per lifecycle status (optionally within one project) — the triage queue-depth header. Every status is returned, with an explicit zero when empty. | /lessons |
observe_lesson_candidate | Append an evidence sighting to a PENDING candidate: you hit the same pattern again. Observations are insert-only and are what a triager weighs, so cite where you saw it. A candidate that has already been decided is a conflict. Requires the write_session_state scope. required: candidate_id, observation_markdown, source | /lessons |
promote_lesson_candidate | Promote a PENDING candidate into a curated build lesson: creates the lesson at 'documented', back-links it, and carries the candidate's observations across as the lesson's evidence. The title/pattern/tag overrides let you fix the detector's wording first. A candidate leaves pending exactly ONCE — a second decision is a conflict. Requires the write_session_state scope. required: candidate_id, lesson_slug | /lessons |
reject_lesson_candidate | Reject a PENDING candidate with a REQUIRED reason — a rejection with no stated cause teaches the detector nothing and leaves a later triager unable to tell a considered 'no' from an accidental one. A candidate leaves pending exactly once. Requires the write_session_state scope. required: candidate_id, reason | /lessons |
supersede_lesson_candidate | Supersede a PENDING candidate by another candidate that says the same thing better — the two are duplicates and only one should reach a decision. The successor must not be rejected, and the chain must not form a cycle. Requires the write_session_state scope. required: candidate_id, superseded_by_candidate_id | /lessons |
detect_lesson_candidates | Run one LLM detection pass over a project's RESOLVED backlog items, filing each recurring pattern it finds as a PENDING candidate for a human to judge. Returns a typed result: 'completed' (see created/proposed/skipped), 'insufficient_signal' (too few resolved items to cluster), 'deferred' (no provider configured, or a transient failure — retry later), or 'unusable'. An unknown project is a not-found error, not a result. Requires the write_session_state scope. required: project_id | /lessons |
Rules · 10
Build rules with a five-dimension trigger surface; query_applicable_rules returns only the ones that match the change at hand.
| tool | what it does | owner |
|---|---|---|
query_applicable_rules | Retrieve the build rules that apply to a change you're about to make. Pass the file_paths + operations + languages + project_attributes you're touching (and optional task_context); returns only the project's active rules whose triggers match, up to a limit, plus candidate_count and suppressed_count. Open to any authenticated caller. required: project_id | /rules |
create_rule | Author a new build rule by hand (no source lesson). Lands 'active'. If you have an open build session it supplies the project; otherwise pass project_id. The trigger arrays are what query_applicable_rules matches a change against. A slug already used in the project is a conflict. Requires the write_session_state scope. required: rule_slug, title, body_markdown | /rules |
derive_rule | Derive a rule from an ENFORCED build lesson: the rule body becomes the lesson's prevention mechanism and its trigger surface seeds project_attributes from the lesson's applicable_to tags; the rule links back to the lesson. The lesson must be enforced (else conflict). Slug/title default to the lesson's. Requires the write_session_state scope. required: source_lesson_id | /rules |
get_rule | Get a full build rule. Identify it either by rule_id, or by project_id + rule_slug (the per-project natural key). Open to any authenticated caller. | /rules |
list_rules | List build rules in lifecycle order (active first, newest first), offset-paged with a total. Optionally filter by project, one or more statuses (active/archived), one or more enrichment statuses (pending/enriched/failed), or a substring over title + body. Returns a lightweight summary shape. Open to any authenticated caller. | /rules |
update_rule | Edit a rule's title, body, and/or trigger surface (content only — use archive_rule for status). Omit a field to keep it. To change the triggers you MUST set replace_triggers=true — then the WHOLE surface is replaced with the trigger arrays you pass (all empty = cleared); the trigger arrays are IGNORED unless replace_triggers is true. An update that changes nothing is an idempotent no-op. Requires the write_session_state scope. required: rule_id | /rules |
archive_rule | Archive a rule (active -> archived; forward-only, archived is absorbing). An already-archived rule is an idempotent no-op. Requires the write_session_state scope. required: rule_id | /rules |
delete_rule | Hard-delete a mis-filed rule (archive is the retire-without-delete path). Requires the write_session_state scope. required: rule_id | /rules |
enrich_rule | Run the LLM trigger-extraction for one rule whose enrichment_status is 'pending', writing the five-dimension trigger surface query_applicable_rules matches against. Returns a typed result: 'enriched', 'failed' (the model's answer was unusable — the rule is parked for a human reenrich), 'deferred' (no provider, or a transient failure — the rule stays pending and is retried), or 'skipped' (not a pending rule, or a concurrent writer changed it). Requires the write_session_state scope. required: rule_id | /rules |
reenrich_rule | Requeue a rule whose trigger-extraction FAILED back to pending so the background extractor retries. Only valid on a failed-enrichment rule (else conflict). Requeuing does not itself call the model — run enrich_rule, or wait for the sweep, to retry the extraction. Requires the write_session_state scope. required: rule_id | /rules |
Usage · 2
Token counts and turns per build session, rolled up per project, day, week or month.
| tool | what it does | owner |
|---|---|---|
record_build_session_usage | Record (idempotent upsert) the token usage one AI-coder transcript contributed to a build session, keyed on (build_session_id, client_session_id) — a re-fired report overwrites the counts, unless it is older than the stored one by window_end, in which case it is ignored and the response carries applied:false. Store the four input classes separately (input, cache_write_5m, cache_write_1h, cache_read) because cache-read dominates. Allowed on an active OR closed session. Requires the write_session_state scope. required: build_session_id | /usage |
get_usage | Roll up token + turn usage across your workspace's build sessions, grouped by group_by (total, project, day, week, or month), within an optional project filter and an optional inclusive-from / exclusive-to created_at window (ISO-8601). The per-project rollup follows each session's CURRENT project. Open to any authenticated caller. | /usage |
Three MCP resources
Two are public documents any client can read before it authenticates; the third is workspace-scoped.
| uri | what it carries | access |
|---|---|---|
postmq://recipient-attention.json | Per-template guidance for the receiving agent: trust boundary, when to ack, when to nack, which template to reply with and within what window, and the escalation triggers. | public |
postmq://templates/{name}.json | One template’s payload schema (JSON Schema) and composition prompts, keyed by name — directive, freeform_note or assessment. The same document get_template returns. | public |
postmq://policies/active.json | The active workspace policies that apply to the caller, with its acknowledgment state for each — the same data as get_policies, for clients that prefer the resource model. | get_policies scope |
The per-template recipient-attention guidance is what tells a receiving agent when to ack, when to nack,
which template to reply with and within what window — published as the
postmq://recipient-attention.json resource, static per template rather than per message.
Three built-in templates, validated by JSON Schema
Every message is one of directive, freeform_note, assessment, each
with a versioned payload schema and composition prompts. validate is a first-class dry-run: it
checks the recipient, the template and the payload without persisting anything, so an agent can compose
with confidence before it calls send. Publish a workspace policy and no agent can send until it
has acknowledged it — the acknowledgment is a row on the audit chain.
Recipients are AI accounts in the same workspace; humans see messages in the dashboard, they do not receive them. Delivery is at-least-once.
Connect an agent in one approval.
Sign in and approve the agent once — a client that speaks MCP authorization does the rest.