Tools
Every one of the 288 tools the MCP server publishes, generated from the source. What your token can actually call is narrower - discovery is permission-aware, so tools/list returns only the ones its scopes cover.
Showing 288 of 288.
Activity 2
list_activityactivity:readread-onlyRead the workspace's immutable activity log (most recent first). Optionally scope to a project. The feed is a projection of two layers underneath it: every significant change writes a transactional domain event, and every sensitive action writes an audit record. Filter with kindGroups (work | decisions | audit | other) or an explicit kinds list; pass afterCursor for a delta since your last read.
actorTypestringFilter to people (USER), agents (AGENT) or SYSTEM.afterCursorstringOpaque cursor: return only entries newer than it, oldest-first (for re-entry / briefings).cursorstringOpaque cursor for the next (older) page.kindGroupsstring[]Filter by kind group: work | decisions | audit | other.kindsstring[]Filter to these activity kinds (registry values; an unknown kind matches nothing).limitintegerprojectIdstringworkspacestringWorkspace slug or id. Defaults to your default workspace.record_activityactivity:writeAppend a note to the activity log. Use this for intent, rationale and session notes - NOT to report changes you made: every significant change is recorded automatically by the action itself. This is the only best-effort path in the log, so a returned { ok: true } does not prove the note was persisted.
summarystringrequiredmetadataobjectprojectIdstringAttach the entry to a project in the workspaceworkspacestringWorkspace slug or id. Defaults to your default workspace.Agents 3
create_agentagents:writeagents deniedRegister a named agent principal. The name becomes the attribution label on every record written by a token issued to this agent, so make it recognisable. Registering an agent does not issue it a token - a human does that from Settings, which is also the only place the token is ever shown. NOT available to an agent principal: only a human session may register agents.
namestringrequiredThe attribution label copied onto every record this agent writes, e.g. "Claude via Claude Code". Make it recognisable in a mission log.kindstring | nullClient family. Known values: claude-code, claude-desktop, codex, cursor, custom.notesstring | nulllist_agentsagents:readread-onlyList the AI agents registered on your account, each with how many live tokens it holds and when one was last used. Agents are owner-scoped rather than workspace-scoped, so there is no `workspace` argument and the response echoes workspace: null. Paginated by opaque cursor.
cursorstringOpaque cursor from a prior response nextCursor.includeArchivedboolean | stringlimitintegerupdate_agentagents:writeagents deniedRename an agent, edit its notes, or archive it with archived: true. Archiving stops every token issued to that agent from authenticating, immediately. Agents are never deleted - the record of what one did outlives it (Constitution art. 9) - and archiving is reversible with archived: false. NOT available to an agent principal.
idstringrequiredarchivedbooleantrue archives the agent and its tokens stop authenticating immediately; false restores it. Agents are never deleted - their history outlives them.defaultAutonomyLevelstringOBSERVE = read only, SUGGEST = + advisories/notes, EXECUTE = + routine writes (default), ADMIN = + deletes.kindstring | nullnamestringnotesstring | nullApprovals 3
list_approvalsapprovals:readread-onlyList approval requests. A human sees every approval on the account (their own and their agents'); an agent sees only the proposals it made. Filter by status (PENDING | APPROVED | REJECTED | EXPIRED | FAILED). Owner-scoped, so there is no `workspace` argument and the response echoes workspace: null. Paginated by opaque cursor. The command args shown are redacted for display.
cursorstringOpaque cursor from a prior response nextCursor.limitintegerstatusstringFilter to one status.request_approvalapprovals:writePropose a write for a human to run on your behalf. Give the exact tool name and args; they are captured VERBATIM, and when a human approves, the server executes EXACTLY that saved command as the owner (never a re-derived one). Use this for a change your autonomy level does not let you make solo - it bridges the autonomy gap, so you must still hold the target tool's scopes. Only WRITE and DESTRUCTIVE tools are eligible; read tools need no approval and human-authority actions (identity, governance, tenancy, membership) are never delegable. Optionally bind an entity (entityType + entityId) so the approval is refused if that entity changes before it is resolved.
toolstringrequiredThe exact MCP tool name whose call a human should run on your behalf, e.g. "delete_task".argsobjectThe arguments for that tool, exactly as you would pass them. They are captured VERBATIM and executed unchanged when a human approves - so include everything the call needs (e.g. an explicit `workspace`).entityIdstringThe id of the bound entity (required if entityType is set).entityTypestringOptional TOCTOU binding - the kind of the target entity (product | decision | timeline_item | task | epic | board_column). If set with entityId, the approval is REFUSED at resolve time if that entity changed since you proposed it.expiresInHoursintegerHow long the proposal stays open before it lapses. Default 48h, max 168h.idempotencyKeystringRetry-safe key: a repeat with the same key returns the original approval instead of creating a second.summarystringOne human sentence: why you are proposing this. Shown to the person who resolves it.resolve_approvalapprovals:writeResolve a pending approval. approve: true executes the saved command exactly as captured, as you (the owner); approve: false declines it. Both are terminal and idempotent - resolving an already-resolved approval returns its stored outcome. An approval whose bound entity changed since it was proposed, or whose expiry has passed, is refused rather than executed. NOT available to an agent principal: an agent resolving its own proposal would be self-approval.
approvebooleanrequiredtrue executes the saved command as you (the owner); false declines it. Both are terminal.idstringrequirednotestringOptional reason, recorded on the approval and in the audit trail.Assets 19
create_asset_collectionassets:writeCreate a new (empty) named collection to group assets under.
namestringrequireddescriptionstring | nullworkspacestringWorkspace slug or id. Defaults to your default workspace.delete_assetassets:writedestructiveagents deniedARCHIVE a single asset by id (this tool no longer deletes). It disappears from lists, collections, prompts and search, but its content and its whole revision history are kept (Constitution art. 15), and it KEEPS its kind+name address so nothing else can take it. Call unarchive_asset with the same id, or save_assets onto the same kind+name, to bring it back. Find archived assets with list_assets archived="only".
idstringrequireddelete_asset_collectionassets:writedestructiveagents deniedARCHIVE a collection by its slug in a workspace (this tool no longer deletes). Only the grouping is retired; the assets are kept, and so are its frozen versions. The slug stays reserved - call unarchive_asset_collection with the same slug to bring it back.
slugstringrequiredworkspacestringWorkspace slug or id. Defaults to your default workspace.get_assetassets:readread-onlyFetch a single asset by slug/name (e.g. "the code-reviewer agent"). Returns the full record including its content. Pass `kind` if the same name exists across kinds.
slugstringrequiredThe asset slug or namekindstringDisambiguates when the same slug exists across kindsworkspacestringWorkspace slug or id. Defaults to your default workspace.get_asset_collection_versionassets:readread-onlyReturn a collection exactly as it was at version N: each member's name, description and content at the PINNED revision, not its current text. A member whose pinned content no longer exists is returned with present:false rather than silently dropped, so a caller can report the gap. A member archived AFTER the snapshot still returns its frozen text - a version is frozen - but is flagged archived:true, so check that flag before applying it. Response size is bounded by the version's memberCount.
slugstringrequiredversionintegerrequiredThe version number to read.workspacestringWorkspace slug or id. Defaults to your default workspace.get_asset_revisionassets:readread-onlyRead one revision of an asset in full - its name, description, content and kind-specific data as they WERE at that revision, not as they are now. Asking for the current number returns the head with isHead:true; an out-of-range number is refused with the range.
idstringrequiredrevisionintegerrequiredThe revision number to read.get_assetsassets:readread-onlyFetch a set of assets: the union of the named collection(s), optionally restricted to one kind. Omit collections to pull across the workspace. Use to load a toolkit into a project.
collectionsstring[]Collection names/slugs to pull (unioned). Omit to pull across all your assets.kindstringOptionally restrict to one kindworkspacestringWorkspace slug or id. Defaults to your default workspace.list_asset_collection_versionsassets:readread-onlyList the frozen versions of an asset collection, newest first, with each version's member count and who cut it. Use get_asset_collection_version to read the pinned assets themselves. Paginated by opaque cursor.
slugstringrequiredcursorstringOpaque cursor from a prior response nextCursor.limitintegerworkspacestringWorkspace slug or id. Defaults to your default workspace.list_asset_collectionsassets:readread-onlyList a workspace's asset collections with their slug and (non-archived) asset count. Archived collections are excluded by default; pass archived="only" to find one to unarchive.
archivedstringexclude (default) hides archived records, only returns just them (to unarchive one), all returns both.workspacestringWorkspace slug or id. Defaults to your default workspace.list_asset_revisionsassets:readread-onlyList the immutable history of one asset, newest first. Each row is a state the asset USED to hold, with the revision number it held and how it stopped being current (edit or restore). The CURRENT content is not in this list - `headRevision` in the response names it, and get_asset_revision returns it. Paginated by opaque cursor.
idstringrequiredcursorstringOpaque cursor from a prior response nextCursor.limitintegerlist_assetsassets:readread-onlyList saved assets, optionally filtered by kind, collection slug/name, or a search term. Archived assets are excluded by default; pass archived="only" to find one to unarchive_asset.
archivedstringexclude (default) hides archived records, only returns just them (to unarchive one), all returns both.collectionstringcursorstringOpaque cursor from a prior response nextCursor.kindstringlimitintegersearchstringworkspacestringWorkspace slug or id. Defaults to your default workspace.move_assetassets:writeMove an asset into a different workspace (by slug or id). Its collection membership is cleared (collections do not span workspaces). Requires member access to both workspaces.
idstringrequiredworkspacestringrequiredDestination workspace slug or idrestore_asset_revisionassets:writePut an older revision of an asset back as the current content. Nothing is lost: the content being replaced becomes a revision of its own, so history stays append-only. Kind is never restored (it can never have changed) and the slug follows the restored name. Restoring the content the asset already has does nothing.
idstringrequiredrevisionintegerrequiredThe revision number to put back onto the current record.save_assetsassets:writeSave a batch of reusable AI building blocks (skills, agents, system prompts, commands) to makermap. Pass an ARRAY where each asset is a separate {kind, name, content, ...} object. Re-saving the same kind+name UPDATES it, keeping the previous text as a revision you can read with list_asset_revisions and put back with restore_asset_revision. Fields you omit are left as they are (pass description:null to clear one), and re-saving identical content changes nothing at all. Saving onto an ARCHIVED kind+name brings that asset back (a save is an explicit write to that address). Optionally file them all under named collections.
assetsobject[]requiredOne object per asset. Re-saving the same kind+name updates it (upsert by slug).collectionsstring[]Optional collection names to file every asset under (created if missing).workspacestringWorkspace slug or id. Defaults to your default workspace.snapshot_asset_collectionassets:writeFreeze a collection's current membership as a new version, pinning every asset to the revision it holds right now. Later edits to those assets do NOT change the version - that is what makes a pinned toolkit reproducible. Archived assets are excluded. Versions are never overwritten; each call cuts the next number. Pass idempotencyKey to make a retry safe.
slugstringrequiredidempotencyKeystringRetrying with the same key returns the original version instead of cutting a new one.namestring | nullOptional label for this version, e.g. "pre-angular-22".workspacestringWorkspace slug or id. Defaults to your default workspace.unarchive_assetassets:writeBring an archived asset back into lists, collections, prompts and search. No revision is cut - unarchiving restores visibility, not content. Safe to call on a live asset.
idstringrequiredunarchive_asset_collectionassets:writeBring an archived asset collection back. Its slug stayed reserved while it was archived, so this is how you recover the address rather than creating a duplicate. Safe to call on a live collection.
slugstringrequiredworkspacestringWorkspace slug or id. Defaults to your default workspace.update_assetassets:writeEdit an existing asset by id (get ids from list_assets). Provide only the fields to change. `collections` (names) REPLACES the whole collection membership; missing collections are created. Kind is immutable.
idstringrequiredaddCollectionsstring[]Collection names to ADD (created if missing) without touching the others. Do not combine with `collections`.collectionsstring[]Collection names; REPLACES the whole membership. Omit to leave collections unchanged.contentstringdataobjectdescriptionstring | nullnamestringremoveCollectionsstring[]Collection names to REMOVE without touching the others. Do not combine with `collections`.update_asset_collectionassets:writeRename a collection or change its description, identified by its current slug (in a workspace).
slugstringrequireddescriptionstring | nullnamestringworkspacestringWorkspace slug or id. Defaults to your default workspace.Assignments 4
create_assignmentassignments:writeAssign an agent a scoped piece of work (a product, initiative, epic or task) with expectations and an optional autonomy bound. The workspace is derived from the scope target. autonomyBounds is a RECORDED ceiling for the work, not a grant - it never widens what the agent may actually do (that stays its autonomy policy). The agent then reads get_assignment_context to bootstrap.
agentIdstringrequiredThe agent to assign the work to. Must be one of your own, non-archived agents.expectationsstringrequiredWhat "done" looks like - the definition of the work. Shown to the agent in its context package.scopeIdstringrequiredThe id of the product / initiative / epic / task the work covers. Its workspace pins the assignment.scopeTypestringrequiredWhat KIND of work: product | initiative | epic | task.autonomyBoundsstringA RECORDED autonomy ceiling for this work (OBSERVE|SUGGEST|EXECUTE|ADMIN). Advisory - it does not grant the agent anything its AutonomyPolicy does not already allow.effortstringThe effort this work is expected to run at (trivial|routine|involved|intricate), in makermap's own scale. A RECORDED expectation, not a control - nothing here changes how a model reasons.idempotencyKeystringRetry-safe key: a repeat with the same key returns the original assignment instead of creating a second.titlestringOptional short label; the scope's own name is used if unset.get_assignment_contextassignments:readproducts:readread-onlyContext Package v2 for one assignment: the assignment (agent, expectations, autonomy bound), the product context it works within (identity, why, decisions, board, rules, knowledge - reused from get_product_context with its token budget), the workspace's effective capabilities (skills/agents/prompts), and a gaps section naming what is missing for quality work. detail controls the token budget (brief | standard | full); sections restricts the product core. Requires assignments:read AND products:read. Use it to bootstrap once you are assigned.
idstringrequiredThe assignment id.detailstringbrief (counts only), standard (session bootstrap), full (inlines more of the product core).sectionsstring[]Restrict the PRODUCT core to these sections. Omit for all.sinceRevisionstringAn opaque contextRevision from a prior response. Returns whether anything changed since. Do not construct or parse it.list_assignmentsassignments:readread-onlyList assignments in a workspace, newest first. A human sees every assignment; an agent sees only its own. Filter by status (ACTIVE | COMPLETED | CANCELLED) or agentId. Paginated by opaque cursor.
agentIdstringFilter to one agent. An AGENT caller is always scoped to its own assignments whatever it passes here.cursorstringOpaque cursor from a prior response nextCursor.limitintegerstatusstringFilter to one status.workspacestringWorkspace slug or id. Defaults to your default workspace.update_assignmentassignments:writeUpdate an assignment's expectations, title, autonomy bound, or status. Moving status to COMPLETED or CANCELLED may carry a resolutionNote. The record survives cancellation (art. 9).
idstringrequiredautonomyBoundsstring | nulleffortstring | nullexpectationsstringresolutionNotestringstatusstringtitlestring | nullBilling 4
get_billing_historybilling:readread-onlyRead what has happened commercially to the organization, newest first: subscriptions started and canceled, plans and seat counts changed, payments that succeeded or failed, credits granted, and per-organization limit overrides. Each entry says WHO caused it - a person by name, or the payment provider itself, which is what a renewal or a dunning failure looks like - and carries the before/after of whatever changed. Use it to explain a charge or to see when an organization moved between plans. Requires ADMIN on the organization: knowing which plan you are on is not privileged, but a payment failure with a name attached is. Read-only, and the ledger is append-only - nothing here can be edited or removed.
cursorstringOpaque cursor from a previous page. Pass it back verbatim.limitintegerEntries per page (default 25, max 100).orgstringOrganization slug or id. Defaults to the organization of your default workspace.get_credit_balancebilling:readread-onlyRead the organization's agent-credit balance and how it was reached: how many credits were purchased, granted and refunded, and how many were spent or expired. Credits pay for the small set of operations that carry a real cost - semantic search embeds your query with the in-process model - while the structural calls an agent makes constantly (get_product_context, the board, decisions, documents, logs) are free and always will be. Adding an agent is free; agents are never a billable seat. Call this after a CREDITS_EXHAUSTED refusal to see what the balance went on.
orgstringOrganization slug or id. Defaults to the organization of your default workspace.get_entitlementsbilling:readread-onlyRead what the organization's plan actually entitles it to, and how much of each ceiling is already used. Every limit comes back with its ceiling, the current usage, the remainder, HOW the usage was counted, and where the ceiling came from (the plan, a per-organization grant, or the number of seats purchased) - so a refusal can always be explained rather than asserted. Limits counted per organization: members, workspaces. Counted per workspace: products - pass `workspace` to count them somewhere specific, otherwise those report a usage of 0 and say so. Also returns the plan's feature flags. Call this after a PLAN_LIMIT_REACHED refusal to see exactly which ceiling was hit.
orgstringOrganization slug or id. Defaults to the organization of your default workspace.workspacestringWorkspace slug or id to count the per-workspace limits (products) in. Omitted, those limits report their ceiling and a usage of 0.get_subscriptionbilling:readread-onlyRead the organization's current plan: the plan key and name, the subscription status, how many seats were purchased, the current billing period, and whether a cancellation is scheduled. Also returns the full plan catalog so you can see what other plans exist. An organization that has never subscribed is on the free plan and is reported as such rather than as an error. Provider (payment system) identifiers are included only for ADMIN and OWNER. Read-only: changing a plan moves money and is not available to an agent at any autonomy level.
orgstringOrganization slug or id. Defaults to the organization of your default workspace.Bootstrap 1
get_workspace_setupsetup_profiles:readread-onlySTART HERE on a new machine. One call returns everything needed to install a working environment from this workspace: its setup profiles (each with its defaultScope, the exact versions its manifest pins and a ready-made `apply` line), a summary of the rule sets and asset collections behind them with their latest frozen version, and ONE recommended profile with the reason it was picked. `nextSteps` carries the rest of the flow, so this call alone is enough: preview with apply_setup_profile dryRun=true, then apply. NOTE: makermap ASSEMBLES the package - your client applies it to the machine; makermap never writes files. Sections you lack the scope for (instructions:read, assets:read) are named in `missing` rather than dropped silently.
detailstringfull = each profile with its resolved manifest items and their pinned versions. summary = counts only (cheaper).workspacestringWorkspace slug or id. Defaults to your default workspace.Briefings 3
generate_briefingproducts:readAssemble a "what changed and what needs a decision" briefing for a workspace in ONE call: significant changes and health-band moves since your last briefing (a cursor delta), plus the gates that are BLOCKED right now and the open decisions already past their deadline (current-state snapshots). Every thesis cites the activity event or entity it is built on. Pass afterCursor (the nextCursor a prior briefing returned) to get only what is new; the call is idempotent by that cursor, so generating again with the same one returns the same briefing rather than recomputing. Omit afterCursor for a first briefing over the most recent activity - older history is read by paging list_activity, not by a follow-up briefing. Needs products:read; the change/health sections also need activity:read and the decision section needs decisions:read - a withheld section is named in `missing`, never dropped silently. Scope AND product-allow-list narrowing both re-apply on every read, so a narrowed token never sees a section or a product a broader token unlocked. Returns markdown plus structuredContent (theses, counts, truncated, nextCursor).
afterCursorstringThe opaque cursor a prior briefing returned (its nextCursor / toCursor). Returns only what changed since, and is IDEMPOTENT: generating again with the same cursor returns the same briefing, not a recompute. Omit for a first briefing over the most recent activity; page further back through older history with list_activity.workspacestringWorkspace slug or id. Defaults to your default workspace.get_briefingproducts:readread-onlyFetch one previously generated briefing by id, in full: every thesis with its citations, the counts, what was truncated and the cursor window it covered. The change/health theses need activity:read and the decision theses need decisions:read - a section you lack the scope for is stripped and named in `missing`, even if a more-privileged token generated it. A token narrowed to a slice of the portfolio (P1-E12) likewise never reads another product's thesis through a briefing a broader token cut. Returns markdown plus structuredContent.
idstringrequiredlist_briefingsproducts:readread-onlyList the briefings generated for a workspace, newest first, as light rows: id, when it was generated, the cursor window it covered (fromCursor -> toCursor) and its per-category counts. Read one in full with get_briefing. Paginated by opaque cursor. Section counts you lack the scope to read (activity:read for changes/health, decisions:read for overdue decisions) come back null and are named in each row's `missing`.
cursorstringOpaque cursor from a prior response nextCursor.limitintegerworkspacestringWorkspace slug or id. Defaults to your default workspace.Changes 2
list_changesactivity:readread-onlyWhat changed in this workspace since your cursor, oldest-first: the KIND of each change and the record it touched, never the values - read this, then re-read whatever you care about through its own tool. Omit `since` on the first call to establish a cursor. Reads the domain event log, so it reports changes the Mission Log does not project (campaigns, incident status, infrastructure, financial and marketing profiles, integrations). Delivery is at-least-once: an item may repeat across an overlapping window, so DEDUPE by id. This is the polling half of the browser live stream, which the stateless agent transport cannot hold.
aggregatesstring[]Only these kinds of thing (product, task, decision, document, ...). An unknown one matches nothing.limitintegerproductIdstringOnly changes belonging to this product.sincestringAn opaque cursor from a prior response. Returns only what changed after it, oldest-first. Omit for the first call. Do not construct or parse it.workspacestringWorkspace slug or id. Defaults to your default workspace.poll_changesinbox:readread-onlyPush Mode v1 (polling): return the new deliverable items that appeared in a workspace since your last cursor - pending approvals and new assignments - plus a fresh cursor to poll with next. Omit `since` on the first call to get everything current. Delivery is at-least-once: nothing is ever lost, but an item may repeat across an overlapping window, so DEDUPE by the id on each approval/assignment. For finer-grained change feeds use the existing cursors: list_activity afterCursor, get_product_context contextRevision, and read_log seq. A server-push SSE channel is deferred; polling this on an interval is the reliable path.
sincestringAn opaque cursor from a prior response. Returns only what appeared since. Omit for the first poll (returns everything current + a fresh cursor). Do not construct or parse it.workspacestringWorkspace slug or id. Defaults to your default workspace.Context 2
get_product_contextproducts:readread-onlyThe first call of a session: a token-budgeted context package for one product (identity, the five "why" fields, stack + environments, open decisions, board digest, recent activity, applicable rules, knowledge documents, gaps and follow-up pointers). detail=brief|standard|full. Pass sinceRevision (an opaque contextRevision from a prior response) to get only what changed. The recent-activity section needs activity:read and the knowledge section needs documents:read; without a scope that section is withheld and gaps says so.
productstringrequiredProduct id or exact name.detailstringbrief (~300 tokens: counts + blockers), standard (session bootstrap), full (inlines decision context + task descriptions).sectionsstring[]Restrict to these sections. Omit for all ten.sinceRevisionstringAn opaque contextRevision from a prior response. Returns only what changed since, plus a fresh contextRevision. Do not construct or parse it.get_workspace_overviewproducts:readread-onlyA portfolio attention snapshot in one call. Per product: stage (id/key/name/role/position), status, colour, owner, open-decision count plus up to 5 drill-down decision ids (a bounded sample - `openDecisionCount` is the authoritative total), oldest open deadline, urgent-task count, last-activity age and timestamp, and explainable `reasons`/`flags`. Plus the workspace's ordered lifecycle stages, and a ranked `attention` queue (open decisions deadline-first, then stalled / orphaned-owner / no-goal products) where every row carries prose to display verbatim and a `targetKind`+`targetId` to act on. Plus `launchRadar`: the products close to shipping that have a clock on them (stage role in `thresholds.radarRoles`, oldest open decision deadline inside `thresholds.radarHorizonDays`), each with prose to display verbatim and an optional `release` (its primary planned/in-progress release: name, status, gate status, CURRENT target date) and computed `readiness` (READY_INACTIVE | LAUNCH_STALLED | null). Plus `health`: the portfolio band distribution and the products currently in the CRITICAL/NEEDS_DECISION/STALLED health bands (the two most severe also raise a HEALTH_CRITICAL/HEALTH_NEEDS_DECISION attention row carrying the product's top health reason verbatim); `health.items` is display-capped (see `truncated.health` -> `list_products_by_band`). Plus `briefing`: the latest stored briefing digest (theses with citations, counts) or null if none has been generated - a pure read, never a generate. `recentActivity` needs the activity:read scope and `products[].owner.displayName` needs orgs:read; without either the value is withheld and `missing` says so, and `recentActivityCursor` feeds list_activity(afterCursor) for the delta. Plus `operations` (P6-E10): the post-launch portfolio view - products whose stage role is in `thresholds.operationsRoles` (OPERATIONS/TERMINAL), with a `byStatus` breakdown (active/paused/archived/sunset) and up to 20 `items` (product, stage, status, health band). Plus `financialPulse` (P6-E11): the portfolio burn/revenue/runway roll-up over product financial profiles (currency, totalMrr, totalMonthlyCost/Net/Burn, mixedCurrencies, and `atRisk` products burning with under a year of runway) - null and named in `missing` without the financials:read scope, or when no profile exists. Thresholds and the radar/operations role lists are echoed in `thresholds`. NB `counts.overdueDecisions` counts PRODUCTS with an overdue oldest deadline (a legacy name); `counts.overdueOpenDecisions` counts the decisions. `score` is an internal sort key - do not display it. Attention is capped at 50 rows and activity at 15; overflow is reported in `truncated` with a pointer. Backs the Command Center.
workspacestringWorkspace slug or id. Defaults to your default workspace.Conversion 1
convert_entityproducts:writeFormalize a free timeline block (a quick capture) into a real entity: a task, epic, milestone or initiative. The SAME block becomes the entity's mirror - its dates become the entity's plan and its origin is preserved (read it back with get_timeline_item, which carries formalizedToType/formalizedToId). For target="task", optionally pass columnId/epicId/priority (defaults to the backlog column). Requires products:write, and additionally tasks:write for target="task" or "epic". An already-formalized block, or a target outside task|epic|milestone|initiative, is refused with a structural error.
targetstringrequiredWhat to formalize this capture into: task | epic | milestone | initiative.timelineItemIdstringrequiredcolumnIdstring | nullFor target="task": the board column to place it in. Defaults to the BACKLOG column, else TODO, else the leftmost.epicIdstring | nullFor target="task": an epic swimlane on the same product.idempotencyKeystringRetrying with the same key returns the original conversion (no duplicate entity).prioritystring | nullFor target="task": LOW | MEDIUM | HIGH | URGENT.Credentials 1
reveal_credentialcredentials:revealagents deniedReturn the decrypted value of a NON-PRODUCTION test credential by id. This is the only way to read a credential value: get_product, get_product_context, search and exports return the metadata plus hasSecret and never the value itself. Every call writes a CREDENTIAL_REVEALED audit event (who, which record, which transport) and that audit is fail-closed - if it cannot be recorded, no value is returned. Requires the dedicated credentials:reveal scope; products:write is not sufficient. A record with no stored value answers with secret: null. Ask for a value only when you actually need it - every reveal is visible in the mission log. Never put production secrets in makermap: store a reference to your secret manager instead (Constitution art. 14).
idstringrequiredDecisions 9
create_decisiondecisions:writeOpen a decision that needs a human governance call - a waiver, a risk acceptance, a strategic exception, or an override of automation. Routine agent judgments inside an approved scope are NOT decisions. Optionally set the question, alternatives, confidence, an owner + participants, a project, a deadline, a review date, and initial links.
titlestringrequiredA short label for lists.actionstring | nullalternativesstring[]Every option weighed, INCLUDING the one you are proposing - not only the ones you rejected. Approving names one of these as chosenOption, so a list of rejected paths leaves the recommended course impossible to pick.confidenceinteger | nullconsequencestring | nullcontextstring | nulldeadlinestring | nulllinksobject[]Initial links to other entities.ownerIdstring | nullparticipantsstring[]User ids involved beyond the owner.projectIdstring | nullquestionstring | nullThe full question this decision answers.reviewAtstring | nullWhen to revisit this decision.workspacestringWorkspace slug or id. Defaults to your default workspace.decide_decisiondecisions:writeagents deniedRecord the human decision: approve, reject or defer, with an optional rationale and (on approve) the chosenOption. This is the human override over any advisory.
idstringrequiredstatusstringrequiredchosenOptionstring | nullThe option picked (required on approve when the decision lists alternatives). Free text: it usually names one of the alternatives, but it does not have to - the course taken is sometimes the proposal itself, which the list may not carry.resolutionstring | nulldelete_decisiondecisions:writedestructiveagents deniedPermanently delete a decision (and its advisories, participants and links) by id.
idstringrequiredget_decisiondecisions:readread-onlyFetch one decision by id, with its advisories, participants and links.
idstringrequiredlink_decisiondecisions:writeAttach a link from a decision to another entity. entityType is one of product | task | epic | gate | document | decision | instruction | asset; an unknown type returns a structural error listing the allowed types. Idempotent on (decision, type, id).
entityIdstringrequiredentityTypestringrequiredOne of the decision-link entity types.idstringrequiredrolestringref (the default when omitted) merely references the entity. blocks means this decision must be decided before that task or epic may enter a Done column.list_decisionsdecisions:readread-onlyList decisions in a workspace (open first, then most recent). Filter by project, status, owner, or a deadline/review cutoff (dueBefore/reviewBefore). Paginated by opaque cursor.
cursorstringOpaque cursor from a prior response nextCursor.dueBeforestringOnly decisions with a deadline at/before this time.limitintegerownerIdstringOnly decisions owned by this user.projectIdstringreviewBeforestringOnly decisions with a review date at/before this time.statusstringworkspacestringWorkspace slug or id. Defaults to your default workspace.post_advisorydecisions:writeAttach a recommendation to a decision: what you advise, plus confidence (0-100), rationale/evidence, expected impact and alternatives. Proposal only - a human still decides.
idstringrequiredrecommendationstringrequiredalternativesstring[]confidenceinteger | nullimpactstring | nullrationalestring | nullsourcestring | nullunlink_decisiondecisions:writedestructiveagents deniedRemove a link from a decision by its link id.
idstringrequiredlinkIdstringrequiredupdate_decisiondecisions:writeUpdate an open decision (title, question, context, consequence, action, alternatives, confidence, owner, deadline, reviewAt). Passing participants or links replaces that whole set.
idstringrequiredactionstring | nullalternativesstring[]Replaces the whole list. Include the option being proposed, not only the rejected ones - approving names one of these.confidenceinteger | nullconsequencestring | nullcontextstring | nulldeadlinestring | nullexpectedVersionintegerThe `version` you last read. When given, the write is refused with ENTITY_CHANGED if the record moved since - instead of overwriting a change somebody else made. Omit it for a one-shot write that has not read the record.linksobject[]Replaces the link set when given.ownerIdstring | nullparticipantsstring[]Replaces the participant set when given.questionstring | nullreviewAtstring | nulltitlestringDesign 15
archive_design_proposaldecisions:writeArchive a design review. This is the TERMINAL operation - there is no delete for a proposal, a variant, a comment or a vote in any transport (art. 9). The review, its mockups and its whole discussion stay readable; the Decision it lives on is untouched, so archiving a review does not decide it.
idstringrequiredarchive_design_variantdecisions:writeTake one option out of the running without destroying it. The variant, its revisions, its comments and its votes all stay readable - archiving only removes it from the live set, so a review that went down to two real candidates stops demanding a choice between five. There is no delete and no unarchive (art. 9). The variant an approved review pinned cannot be archived.
variantIdstringrequiredcast_design_votedecisions:writeCast an advisory vote for one design variant. Voting again for the same variant REPLACES your vote - there is one live vote per person per variant, and the change is recorded in the event history. THE VOTE DECIDES NOTHING: it is input to a human, exactly like an advisory. Only `decide_decision`, called by a person, resolves the review, and no tally here influences that.
variantIdstringrequiredcommentstring | nullWhy.valuestringAdvisory only - a tally never decides anything.create_design_proposaldecisions:writeOpen a design review on a product: a visual proposal that people discuss and a HUMAN decides. The review always lives on a Decision - pass `decisionId` to attach it to one that is already open, or omit it and a new Decision is created with the review as its subject. Then add options with `save_design_variant`. Blocking work on the outcome is a separate act: `link_decision` with role="blocks".
productIdstringrequiredThe product this review belongs to.titlestringrequiredWhat is being reviewed - "Sign-in form", "Empty board state".briefstring | nullWhat was actually asked for - the prompt behind the mockup. Reviewers read this first.deadlinestring | nullReview deadline. Rides on Decision.deadline - this epic adds no date column of its own.decisionIdstring | nullAttach the review to an EXISTING decision. Omit and a new Decision is opened for it - the review always lives on one.idempotencyKeystringparticipantsstring[]User ids to invite as reviewers. They become decision participants.questionstring | nullThe question the new Decision asks. Ignored when decisionId is given.get_design_proposaldecisions:readread-onlyRead one design review: its variants with vote breakdowns and comment counts, the Decision it lives on and that decision's status, the work it blocks, and the design token set in force. Address it by its own id or by `decisionId` - a review and its decision are one object seen from two sides. Votes are reported with an explicit `votePolicy` because they are ADVISORY: they are input to a person, never an outcome.
decisionIdstring | nullOr the id of the decision it lives on.idstring | nullThe proposal id.get_design_tokensorgs:readread-onlyRead the design tokens a mockup renders with: the resolved value of each of the 42 registry keys, THE SOURCE OF EACH ONE (product set, workspace set, or the built-in Oceanix default), and any keys no layer supplied. Resolution is key-by-key, so a product set that overrides two colours leaves the rest coming from elsewhere and this says so per key rather than per set.
productIdstring | nullResolve as this product would see it.workspacestringWorkspace slug or id. Defaults to your default workspace.get_design_variant_revisiondecisions:readread-onlyRead one revision of a design variant, as the DSL document (`format: "spec"`, the default) or as the rendered HTML the reviewers see (`format: "html"`). Asking for the current revision falls back to the live state and says so with `isHead: true`, claiming no author - under copy-on-write the head has no revision row to attribute. A revision out of range answers with the available range. The response always echoes the `tokenSet` it was rendered against, so a comment can be read back against the palette its author actually saw, and reports any `unsupportedBlocks` rather than dropping them.
revisionintegerrequiredWhich revision. Pass the head to read the current state.variantIdstringrequiredformatstringlist_design_commentsdecisions:readread-onlyThe discussion on a design review, newest first, with each comment resolved against the variant it belongs to. An anchored comment carries `anchor.present` saying whether its element still exists in the current revision; one whose element is gone is reported with `detached: true` and `anchor.lastSeenRevision` rather than being hidden. Paginated by opaque cursor.
proposalIdstringrequiredcursorstringOpaque cursor from a prior response nextCursor.limitintegerlist_design_proposalsdecisions:readread-onlyList the design reviews in a workspace, most recently touched first, with the status of the decision each lives on and counts of variants, comments and votes. Rows never carry a mockup document - read one with `get_design_proposal`. Paginated by opaque cursor.
cursorstringOpaque cursor from a prior response nextCursor.limitintegerworkspacestringWorkspace slug or id. Defaults to your default workspace.list_design_variant_revisionsdecisions:readread-onlyThe revision history of one design variant, newest first. Each row says which revision it holds, whether it was displaced by an edit or by a restore, and who wrote it - but never the document itself, which would make a page of twenty revisions enormous. Read one with `get_design_variant_revision`. Paginated by opaque cursor.
variantIdstringrequiredcursorstringOpaque cursor from a prior response nextCursor.limitintegerpost_design_commentdecisions:writePost a comment of one of three kinds: on the review as a whole (omit variantId), on one variant (omit blockId), or anchored to a single element of a variant (pass blockId, which you get from the spec or from `get_design_variant_revision`). The revision you were looking at is recorded, so the comment keeps its context after the mockup changes. An anchored comment survives later revisions whenever its block id still exists, and is reported as detached rather than deleted when it does not. A comment is INPUT: it never changes the decision.
bodystringrequiredThe comment.proposalIdstringrequiredblockIdstring | nullAnchor the comment to one element of the variant, by its block id. Survives later revisions.revisioninteger | nullThe revision you were looking at. Defaults to the variant head.variantIdstring | nullComment on one variant. Omit for a comment on the review as a whole.restore_design_variantdecisions:writeRestore an earlier revision of a design variant as the current state. The state being displaced is itself kept as a revision marked `supersededBy: "restore"` with `restoredFrom` naming the source, so the round trip stays readable. Restoring the state that is already current is an honest no-op and reports `restored: false`.
revisionintegerrequiredvariantIdstringrequiredretract_design_votedecisions:writeWithdraw your vote from a design variant. Retracting a vote you never cast is a no-op. The cast and the retraction both stay in the event history, so the record of what you thought and when survives the row.
variantIdstringrequiredsave_design_tokensorgs:writeSet design tokens for a workspace, or for one product within it. Only keys from the closed 42-key registry are accepted, and each value is checked against the shape its key takes: hex for the 35 colours, a length for the 4 radii, a stack of allowed families for the 3 fonts. Arbitrary CSS is not accepted in any form - a token is a key and a value, never a declaration. The set is versioned copy-on-write like a variant. Changing it re-renders every existing mockup, which is the point.
tokensobjectrequiredDesign tokens, from the closed 42-key Oceanix registry. Colours take hex (3/4/6/8 digits), radii take a length, fonts take a stack from the allowed families. A raw CSS declaration is never accepted.namestringproductIdstring | nullScope the set to one product. Omit for the workspace-level set.workspacestringWorkspace slug or id. Defaults to your default workspace.save_design_variantdecisions:writeCreate or update one option under review, addressed by its label. `spec` is an mm.design/1 document - typed blocks, NOT HTML: READ THE RESOURCE makermap://design/blocks FIRST for the catalog of the 14 block types and their props. An unknown block type is rejected outright, with the allowed types listed. Saving over an existing label writes a copy-on-write revision of the previous state in the same transaction, so every comment already anchored to it keeps its referent; re-saving a structurally identical document burns no revision. Omit a block's `id` and the server assigns a stable one - anchored comments hang on those ids, so keep them across saves.
labelstringrequiredThe variant address - "A", "Compact form". Unique per proposal, and what chosenOption names on approve.proposalIdstringrequiredThe design review this variant belongs to.specobjectrequiredAn mm.design/1 document: { "dsl": "mm.design/1", "blocks": [...] }. Read makermap://design/blocks for the full catalog of the 14 block types and their props before writing one.summarystring | nullOne line on what makes this variant different.DocsIndex 2
get_doc_sectiondocuments:readread-onlyRead one section of makermap's documentation in full by the `anchor` a search_docs result gave you, together with its previous/next sections and its cross-reference edges (which epics, ADRs, Constitution articles and Vision paragraphs it cites, and which sections cite it). An unknown anchor returns the nearest existing anchors in the error hint - anchors are heading paths, so they change when a heading is renamed.
anchorstringrequiredThe `anchor` from a search_docs result: "<path>#<heading > path>".search_docsdocuments:readread-onlySearch makermap's own documentation corpus (vision, constitution, ADRs, epics, guides) section by section, instead of reading whole files. Returns ranked sections with a snippet centered on the match, plus `anchor`, `path`, `headingPath` and `startLine`; read one in full with `get_doc_section`. An exact epic or ADR id (`P3-E13`, `ADR-004`) is answered by symbol lookup rather than text search. Filter with `file` (path prefix), `epic`, `adr`, `article` or `docKind`. Paginated by opaque cursor. The index is rebuilt by hand, so every response reports its own freshness: `headSha` (the commit indexed), `headShaCurrent` (the repository's HEAD now) and `stale` - true means the index is behind and should be discounted for very recent work, null means freshness could not be determined. Documentation is edited in git, never through this API.
querystringrequiredWhat to look for. Free text, or an exact epic/ADR id.adrstringOnly sections tagged with this ADR id, e.g. "ADR-004".articleintegerOnly sections citing this Constitution article number.cursorstringOpaque cursor from a prior response nextCursor.docKindstringRestrict to one kind of document.epicstringOnly sections tagged with this epic id, e.g. "P3-E13".filestringRestrict to paths starting with this prefix, e.g. "docs/product/adr/".limitintegermodestringkeyword (default): Postgres full-text over the section text, excellent within one language. semantic: meaning-based pgvector search, which is what answers an ENGLISH question about a concept written in RUSSIAN - 34 of 43 files here are Russian prose and keyword retrieval scored 0.00 on that class. Not a blend: the keyword path measures 1.00 within a language, so it is not quietly replaced.Documents 4
delete_documentdocuments:writedestructiveagents deniedPermanently delete a knowledge document by id, together with its whole revision history. This is not reversible - prefer unpinning or editing it with save_document.
idstringrequiredget_documentdocuments:readread-onlyRead one knowledge document by its slug (or id, or its exact title). Pass `version` to read a historical version read-only; version N is the content that WAS version N, and the response root always carries the current `version` plus a `revisions` index. Returns the full body.
slugstringrequiredThe document slug (or its id).productIdstring | stringDisambiguate a slug that several products share: the product owning the document, or "none" for the workspace-level one. Only needed when a bare slug matches more than one document.versionintegerReturn a specific historical version instead of the current one.workspacestringWorkspace slug or id. Defaults to your default workspace.list_documentsdocuments:readread-onlyList knowledge documents in a workspace, pinned first then most recently updated. Filter by productId, tag, pinned, or a case-insensitive `search` over title and body. Rows carry an excerpt and `bodyChars`, never the full body - fetch one with `get_document`. Paginated by opaque cursor.
cursorstringOpaque cursor from a prior response nextCursor.limitintegerpinnedboolean | stringproductIdstring | stringOnly documents attached to this product. Pass "none" for workspace-level documents only; omit for both.searchstringCase-insensitive substring of title or body.tagstringworkspacestringWorkspace slug or id. Defaults to your default workspace.save_documentdocuments:writeCreate or update a knowledge document, addressed by a stable slug. Omit `slug` on the first save to derive it from the title; pass the same `slug` on every later save to update THAT document - a title change never re-slugs, so the address survives across sessions. Re-saving with a changed title or body archives the previous version and increments `version` (nothing is destructively overwritten); a tags/pinned-only edit does not burn a version. Retrying the same call is safe: the slug is the natural key. Set `pinned: true` with a `productId` for the agent-handoff convention - pinned documents ride along in get_product_context.
bodystringrequiredThe document content, markdown.titlestringrequiredHuman title shown in lists.expectedVersionintegerThe `version` you last read. When given, the write is refused with ENTITY_CHANGED if the record moved since - instead of overwriting a change somebody else made. Omit it for a one-shot write that has not read the record.pinnedboolean | stringPinned documents ride along in get_product_context (the agent-handoff convention).productIdstring | nullAttach the document to a product. Omit/null for a workspace-level document.slugstringThe stable address. Omit on the first save to derive it from the title; pass it on every later save to update the SAME document. A title change never changes the slug.tagsstring[]workspacestringWorkspace slug or id. Defaults to your default workspace.Effort 4
get_effort_profiletasks:readread-onlymakermap's own effort scale (trivial | routine | involved | intricate) with what each level means, plus how this workspace maps those levels onto a client's tiers. The mapping is workspace DATA - edit it with upsert_effort_profile when your client's tiers differ. makermap cannot change your reasoning level; this tells you which one the work is worth.
clientKeystringNarrow to one client (e.g. "claude-code"). Omit for every profile in the workspace.workspacestringWorkspace slug or id. Defaults to your default workspace.list_effort_divergencetasks:readread-onlyTasks whose latest effort REPORT disagrees with what the task records - "we said intricate, it was worked at routine". Compares a record against a report, never against reality.
limitintegeronlyDivergedbooleanOnly tasks where the report and the record disagree. Pass false to see every report.productIdstringNarrow to one product. Omit for the whole workspace.workspacestringWorkspace slug or id. Defaults to your default workspace.record_effort_usedtasks:writeReport which effort level you actually worked a task at. This is a REPORT with your name on it, stored beside the task and never written onto it - the task keeps saying whatever was decided. Nothing here can observe how you reasoned, so this is the only way that fact enters the record.
effortstringrequiredThe level you ACTUALLY worked at, in makermap's scale. This is your report - it is stored with your name on it and never overwrites what the task says.taskIdstringrequiredThe task the work was done on.clientKeystring | nullWhich client you are, e.g. "claude-code" - so the tier can be named back.modelstring | nullWhatever you call yourself. Free text; nothing parses it.notestring | nullWhy - especially when it differs from what was recorded.upsert_effort_profiletasks:writeCreate or replace the mapping from makermap effort levels to one client's tier names. Upserted on (workspace, clientKey) - a client we have never heard of is configured here, without a deploy. Partial mappings are legal and the mapping need not be bijective.
clientKeystringrequiredThe client this profile describes, e.g. "claude-code". Upserted on (workspace, clientKey).labelstringrequiredHuman name of the client, shown in the UI.mappingobjectrequiredlevel -> that client's tier name, e.g. { "trivial": "low", "intricate": "xhigh" }. Partial is fine: a level with no tier is a valid state, and the mapping need not be bijective.notesstring | nullCaveats - why a tier was skipped, what collapses.workspacestringWorkspace slug or id. Defaults to your default workspace.Export 2
export_orgworkspace:exportinfra:readread-onlyExport the ORG-scoped infrastructure registry - hosts, orchestrators, datastores and logical databases - as a portable NDJSON bundle. This is a SECOND bundle alongside export_workspace and neither contains the other: a workspace bundle is one workspace of one org, while a host is shared by every workspace, so carrying it in each would break the second restore of the same org on a duplicate id. Returns the manifest by default; format:"json" returns the bytes inline. Needs the org OWNER role plus workspace:export and infra:read. No credentials travel: an ssh alias is a pointer into your own ~/.ssh/config, never a key.
formatstringmanifest = counts only, no records (the default over MCP). json = the whole bundle inline.orgstringOrganization id or slug. Omit to use the org owning your default workspace.export_workspaceworkspace:exportagents deniedExport a whole workspace to a portable bundle: products (with stack, deployments and credential METADATA - secret values are never exported), the board, decisions and advisories, knowledge documents with their revisions, instructions and assets, lifecycle models, gates, the activity feed and the domain/audit event log. Returns only the MANIFEST by default - the export version, the per-section counts and the REST call that streams the bytes - because a full bundle does not fit a context window. Pass format="json" or "json+markdown" to receive the bundle inline; that fails with an actionable error above the inline record cap, and the fix it names is to narrow `sections` (omitting activity and events usually suffices) or to stream it over REST. The bundle is NDJSON: a header line, one line per record, and a footer carrying counts and a sha256 - a bundle with no footer line is incomplete and must not be restored. A bundle is a fuzzy-consistent snapshot, not a database backup: rows written while it runs make the header counts differ from the footer counts. Every call writes an EXPORT_CREATED audit event before any data moves. Requires the workspace:export scope and the owner role.
formatstringmanifest = counts only, no records; json = the full bundle; json+markdown = the bundle plus a human-readable markdown rendering on every decision and document. REST defaults to json+markdown; MCP defaults to manifest.sectionsstring[]Section groups to include. Omit for everything. Allowed: lifecycle, products, board, decisions, gates, timeline, knowledge, sites, inventory, library, design, activity, events. The workspace and principals sections are always included - they are the foreign-key targets a restore needs.workspacestringWorkspace slug or id. Defaults to your default workspace.Financial 2
get_financial_profilefinancials:readread-onlyA product's financial context (Vision §29): budget, recurring cost, revenue (MRR/ARR), cash on hand and goal, PLUS computed monthly net, burn and runway - each with a plain explanation of how it was derived (art.13). null when no profile has been set. This is product financial CONTEXT, not accounting.
productIdstringrequiredupsert_financial_profilefinancials:writeCreate or update a product's financial profile. Amounts are whole currency units (no cents). Omit a field to leave it unchanged; pass null to clear it. burn/net/runway are computed on read and cannot be written. Idempotent - there is at most one profile per product.
productIdstringrequiredarrinteger | nullAnnual recurring revenue (author-supplied, not derived).cashOnHandinteger | nullCash available (drives runway).currencystringCurrency label, e.g. USD (ISO-4217-ish).financialGoalstring | nullmonthlyBudgetinteger | nullPlanned monthly spend ceiling.monthlyRecurringCostinteger | nullActual recurring monthly cost (drives burn).mrrinteger | nullMonthly recurring revenue (drives net/burn).notesstring | nulloneTimeCostToDateinteger | nulloneTimeRevenueToDateinteger | nullGates 9
add_gate_conditionproducts:writeAppend a checkable condition to an open gate. kind=MANUAL is ticked by a person, kind=AI is filled in by an agent and needs evidence, kind=AUTOMATED is computed by the server and requires a checkKey from the registry (no-open-decisions, github-ci-status). A closed gate's conditions are history and cannot be extended.
gateIdstringrequiredtitlestringrequiredcheckKeystringRequired for kind=AUTOMATED. Registry key of a server-side check: no-open-decisions, github-ci-status.kindstringpositionintegerDefaults to the end of the list.add_gate_evidenceproducts:writeAttach a structural evidence record to a condition: type (url | test-run | document | activity | manual-note | ci-run), optional source, uri, result, checksum, capturedAt, expiresAt and confidence. Expired evidence sends its condition back to PENDING and invalidates a passed gate. NEVER put a secret, token or password in uri, result or checksum - evidence is a reference, not a vault.
conditionIdstringrequiredgateIdstringrequiredtypestringrequiredEvidence type: url | test-run | document | activity | manual-note | ci-run.capturedAtstringWhen the artifact was captured (may predate this call). Defaults to now.checksumstring | nullIntegrity digest of the artifact, if it has one.confidenceinteger | nullexpiresAtstring | nullWhen this evidence goes stale. Expiry sends its condition back to PENDING and invalidates a passed gate.resultstringOutcome: PASS | FAIL | PARTIAL | INFO.sourcestringWhere it came from: manual | automated | ai | integration.uristring | nullLink to the artifact. NEVER put a secret or a token here.create_gateproducts:writeCreate a gate on a product. An authorization policy is MANDATORY - pass authorizationPolicy (HUMAN_APPROVAL, AUTO_PASS, AUTO_PASS_WITH_NOTIFICATION, AI_RECOMMENDATION_THEN_HUMAN) or fromTemplate to inherit one; MULTI_APPROVER, POLICY_EXPRESSION and EXTERNAL_SIGNAL are reserved and rejected with the phase they arrive in. conditions[] replaces the template's conditions and is created atomically with the gate. Set targetStage to guard a lifecycle transition.
productIdstringrequiredauthorizationPolicystringAuthorization policy - who or what may close this gate. Executable now: HUMAN_APPROVAL, AUTO_PASS, AUTO_PASS_WITH_NOTIFICATION, AI_RECOMMENDATION_THEN_HUMAN. MULTI_APPROVER / POLICY_EXPRESSION / EXTERNAL_SIGNAL are reserved and rejected.conditionsobject[]Replaces the template conditions entirely when given.fromTemplatestringGate template id or name, or the key of a readiness checklist from list_gate_templates (production-deploy, authentication, payments, security, automated-tests, store-submission). Inherits type, policy, conditions and target stage role. Required when authorizationPolicy is omitted.idempotencyKeystringRetrying with the same key returns the original result (no duplicates).namestringDefaults to the template name when fromTemplate is used.revalidateAfterDaysinteger | nullReevaluation policy: how many days a close stays valid. Regenerates validUntil on EVERY close, so the gate goes stale again each cycle instead of expiring once. Use this for security or compliance gates.targetStagestringLifecycle stage KEY this gate guards (type=STAGE). Defaults from the template stage role.typestringvalidUntilstring | nullValidity horizon of the closed authorization: once past, the gate is invalidated and blocks again. This is not a planning date - a planned pass date belongs to the schedule layer.evaluate_gateproducts:writeRecompute a gate: run its AUTOMATED checks, refresh the derived readiness, and return every unmet condition with what blocks it and the exact follow-up call. Idempotent - calling it again with unchanged data returns the same result and emits no second event. It WRITES: computed condition statuses, and under AUTO_PASS / AUTO_PASS_WITH_NOTIFICATION it closes the gate once every condition is met.
idstringrequiredget_gateproducts:readFetch one gate by id with its conditions, their structural evidence, the authorization policy, the derived readiness with a blocker + follow-up call per unmet condition, and the linked governance Decision if one is open. The decision rationale needs decisions:read; without it the response lists what was omitted. Not annotated read-only: reading a gate applies its expiry.
idstringrequiredlist_gate_templatesproducts:readread-onlyA workspace's own gate templates in `items`, and in `systemChecklists` the readiness corpus that ships with makermap: deploy, authentication, payments, security, tests, store submission - what "done" means for each, as conditions somebody can check. Pass a template's name or id, or a checklist's key, to create_gate as fromTemplate. `assurance` says how many conditions rest on a fact this server computed and how many rest on somebody's word; a list that hid that difference would lie by uniformity.
cursorstringOpaque cursor from a prior response nextCursor.limitintegerstageRolestringOnly templates for this lifecycle stage role.workspacestringWorkspace slug or id. Defaults to your default workspace.list_gatesproducts:readList gates in a workspace or on one product, newest first, each with its condition summary (metCount/totalCount), derived readiness, evidence and authorization policy. Filter by productId, status or type. Paginated by opaque cursor. Not annotated read-only: reading a gate applies its expiry, so a stale pass is never reported as passed.
cursorstringOpaque cursor from a prior response nextCursor.limitintegerproductIdstringOnly gates of this product. Omit for the whole workspace.statusstringtypestringworkspacestringWorkspace slug or id. Defaults to your default workspace.request_gate_passproducts:writedecisions:writeOpen the governance Decision that can close this gate, with the full condition context attached. outcome="PASS" asks a human to authorize it (policies that close through a Decision); outcome="WAIVE" asks to waive it and requires a reason - that is the only route to a WAIVED gate and the only way to override an automatic policy. An agent prepares everything; a human decides. Returns the gate with its Decision so you can route to it.
gateIdstringrequiredidempotencyKeystringRetrying with the same key returns the original result.outcomestringPASS asks a human to authorize the gate. WAIVE asks to waive it (risk acceptance / override of automation) - the only path to a WAIVED gate.reasonstring | nullRequired for WAIVE: what risk is being accepted.update_gate_conditionproducts:writeUpdate a condition (status, title, position) and record who checked it and when. An AI condition cannot be marked MET without at least one evidence record. An AUTOMATED condition is computed - use evaluate_gate instead. Waiving a condition on an auto-closing gate is rejected: a waiver is always a Decision.
conditionIdstringrequiredgateIdstringrequiredpositionintegerstatusstringtitlestringGithub 5
link_referencetasks:writeAttach a GitHub issue or pull request to a task or epic (entityType task|epic, kind issue|pull_request, its number and url; optional cached title/state). Idempotent on (entityType, entityId, kind, number) - re-linking refreshes the cached title/state. The reference then rides inside get_task (and get_board) for that entity.
entityIdstringrequiredThe task or epic identityTypestringrequiredWhat to attach to: task | epickindstringrequiredissue | pull_requestnumberintegerrequiredThe issue or PR numberurlstringrequiredstatestringCached state, e.g. open | closed | mergedtitlestringCached display titlelink_repositoryproducts:writeLink a GitHub repository (owner/repo) to a product as its external-signal source. Returns a webhookSecret and webhookConfig ONCE: makermap generates the signing secret, and it is never readable again - the only way to replace it is rotate_repository_webhook_secret. Configure a webhook in GitHub with that secret pointing at the payload URL; commit/deploy/release/CI deliveries then become REPOSITORY_SIGNAL_RECEIVED events on the product. One repository per product in v1. Reads (the link, the last signal) ride inside get_product and get_product_context.
ownerstringrequiredGitHub org or user, e.g. "makermap"productIdstringrequiredrepostringrequiredRepository name, e.g. "makermap"defaultBranchstringe.g. "main"urlstringRepository URL; defaults to https://github.com/<owner>/<repo>rotate_repository_webhook_secretproducts:writeMint a NEW webhook signing secret for a product's linked repository and invalidate the old one immediately. Returns the new secret ONCE (never re-readable). Deliveries signed with the old secret start failing at once, so re-paste the new secret into GitHub. Use this if the secret was lost or may have leaked.
productIdstringrequiredunlink_referencetasks:writedestructiveRemove a previously linked issue/PR reference by its id.
idstringrequiredunlink_repositoryproducts:writedestructiveRemove the repository link from a product. Past signals and any issue/PR references are kept (unlinking is not erasing history); future deliveries for the repo are rejected. The signing secret is destroyed - re-linking mints a new one.
productIdstringrequiredInbox 2
list_inboxinbox:readread-onlyThe single queue of what needs a human in a workspace: pending approvals (an agent proposed a write), gates awaiting a decision, and open decisions that are overdue, due soon, or just open. Each item says what it is, why now, and what happens if you ignore it, plus a one-call resolve pointer to the existing tool (resolve_approval / decide_decision). Ranked server-side; unread items are the ones that entered the queue since you last marked it read. Owner-scoped read cursor. Does not duplicate any governance write - it points at the real one.
workspacestringWorkspace slug or id. Defaults to your default workspace.mark_inbox_readinbox:writeAdvance your inbox read cursor for a workspace to now (or to `upTo`). Items that entered the queue at or before that time stop showing as unread. Per-user, per-workspace state; it changes nothing about the underlying approvals, decisions or gates.
upTostringMark read up to this time (ISO). Defaults to now.workspacestringWorkspace slug or id. Defaults to your default workspace.Incidents 4
create_incidentincidents:writeOpen an operational incident on a product (an outage, regression or degradation). severity is sev1 (most severe) | sev2 | sev3 | sev4; an UNRESOLVED sev1/sev2 pushes the product's health band to Critical, a sev3/sev4 to Watch. Cannot be created already resolved.
productIdstringrequiredtitlestringrequiredidempotencyKeystringseveritystringDefaults to sev3.statusstringDefaults to open. Creating one already resolved is refused - resolve via update.summarystring | nullget_incidentincidents:readread-onlyFetch one incident by id: its severity, status, timeline and resolution.
idstringrequiredlist_incidentsincidents:readread-onlyList a product's operational incidents. Filter by status or severity. Cursor-paginated.
productIdstringrequiredcursorstringlimitintegerseveritystringsev1 (most severe) | sev2 | sev3 | sev4.statusstringopen | mitigated | resolved.update_incidentincidents:writeUpdate an incident: attributes, severity, or a status move (open -> mitigated -> resolved). Resolving records resolvedAt and the resolution note, and clears the incident from the health input. A status or severity change recomputes the product health band.
idstringrequiredresolutionNotestring | nullRecorded when resolving.severitystringsev1 (most severe) | sev2 | sev3 | sev4.statusstringA status move; resolving records resolvedAt and clears the health input.summarystring | nulltitlestringInfra 27
add_infra_databaseinfra:writeRecord a logical database inside an instance. Leave productId null when nobody owns it - recording an unowned database is the point, not an omission. Protection (do-not-touch) is set separately with set_database_protection.
datastoreIdstringrequirednamestringrequiredenvironmentstring | nullPRODUCTION | STAGING | PREVIEW | DEVELOPMENT. Omit when the database is not tied to one.idempotencyKeystringisolationNotestring | nullnotesstring | nullownerRolestring | nullproductIdstring | nullThe product that owns it, when one does. Databases with no owner are legal and are the dangerous ones.sizeMbinteger | nulladd_infra_datastoreinfra:writeRecord a database instance on a host. `authModel` is worth filling in: recording that loopback is trusted while the network path requires a password is what explains a check that passes one way and fails the other.
enginestringrequiredOne of: postgres, mysql, mariadb, mongodb, redis, valkey, clickhouse, elasticsearch, sqlite, other.hostIdstringrequirednamestringrequiredauthModelstring | nullHow auth actually works here, in prose - e.g. "loopback = trust, network = scram-sha-256". Records why a check can pass over one path and fail over another.containerNamestring | nullidempotencyKeystringinternalHoststring | nullnetworkstring | nullnotesstring | nullpersistentbooleanportinteger | nullpubliclyExposedbooleansharingstringOne of: dedicated, shared. "shared" means cluster-wide operations reach other products.versionstring | nullvolumeNamestring | nulladd_infra_hostinfra:writeRecord a server. `sshAlias` is the name of an entry in your ~/.ssh/config - a POINTER, never a key or a password; makermap stores no infrastructure credentials at all. Recording a host that already exists is refused rather than duplicated: two rows for one machine halve every blast-radius answer.
namestringrequiredThe short name you call it by, e.g. "net".addressstring | nullPublic IP or DNS name.blastRadiusNotestring | nullProse: what breaks if this host dies.cpuCoresinteger | nulldiskGbinteger | nullhostnamestring | nullidempotencyKeystringnotesstring | nullorgstringOrganization id or slug. Omit to use the org owning your default workspace.providerstring | nullOne of: hetzner, namecheap, aws, gcp, azure, digitalocean, linode, vultr, ovh, selfhosted, other.ramMbinteger | nullsshAliasstring | nullThe ~/.ssh/config entry name. A POINTER, never a key - never send a credential here.statusstringOne of: planned, active, quiescing, decommissioned. Default active.add_infra_platforminfra:writeRecord the orchestrator running on a host. containerNaming defaults to the known template for the kind when omitted (Coolify: "<service>-<resource_uuid>").
hostIdstringrequiredkindstringrequiredOne of: coolify, dokku, kamal, k8s, nomad, compose, systemd, other.adminUrlstring | nullconnectionIdstring | nullAn IntegrationConnection, when a live connector exists.containerNamingstring | nullThe container naming template, e.g. "<service>-<resource_uuid>". Defaulted per kind when omitted.idempotencyKeystringnotesstring | nullversionstring | nullarchive_infra_databaseinfra:writedestructiveArchive a recorded database. Never deletes the row, and never touches the real database (art. 9, art. 16).
idstringrequiredarchive_infra_datastoreinfra:writedestructiveArchive a recorded database instance. Never deletes the row, and never touches the real instance (art. 9, art. 16).
idstringrequiredarchive_infra_hostinfra:writedestructiveRetire a host: sets status=decommissioned and archives it. The row is NEVER deleted - the deployments that ran on it must keep their answer to "where did this live". The blast radius at the moment of retirement is captured in the event.
idstringrequiredarchive_infra_platforminfra:writedestructiveArchive a recorded orchestrator. Never deletes the row (art. 9).
idstringrequiredget_infra_datastoreinfra:readread-onlyOne database instance with its logical databases and its TENANCY: which products live on it, which databases have no recorded owner, and which are marked do-not-touch. Read this before any cluster-level operation.
idstringrequiredget_infra_driftinfra:readread-onlyFields where the recorded infrastructure and the LAST REPORTED observation disagree, each explained with both values and a confidence. This is deliberately not "the record disagrees with reality" - makermap cannot see your servers. Deployments nobody has ever verified are absent, because they cannot drift.
environmentstringproductIdstringNarrow to one product. Omit for every deployment you can see.get_infra_hostinfra:readread-onlyOne host with everything on it - orchestrators, datastores, logical databases - plus a COMPUTED blast radius: which products, deployments and databases go down with it, including the databases nobody owns. Start an operational task here.
idstringrequiredget_operations_contextinfra:readproducts:readread-onlyOne call for an operational task: the deployments in an environment, where each runs, what it runs, its environment-variable inventory, the datastores and databases involved, the CAUTIONS that should change your behaviour (do-not-touch databases, shared instances, public exposure), open incidents and decisions, and how stale each record is. Reads everything through the services that own it and decides nothing.
productIdstringrequiredenvironmentstringDefaults to PRODUCTION.link_deployment_infrainfra:writeproducts:writePoint a deployment at the host and orchestrator it actually runs on, plus the orchestrator's own coordinates (platformRef). Infrastructure is ORG-scoped, so the host must belong to the product's own organization. Pass null to unlink.
deploymentIdstringrequiredhostIdstring | nullThe host it runs on. Pass null to unlink.platformIdstring | nullThe orchestrator it runs under. Pass null to unlink.platformRefobject | nullThe orchestrator's own coordinates, e.g. { "project": "...", "resource": "...", "uuid": "..." }.list_infra_activityinfra:readread-onlyThe org-wide history of the registry: every recorded, updated, archived, protected, linked and REPORTED-verified event across every host, orchestrator, datastore and database, newest first, each with a sentence, who did it (person or agent, captured at write time) and the field names that changed. Pass target with a host or datastore id to get that thing AND everything below it. This is the org-level feed - the workspace Mission Log (list_activity) only projects the loudest four of these and only for one workspace.
cursorstringlimitintegerorgstringOrganization id or slug. Omit to use the org owning your default workspace.targetstringA host, datastore, platform or database id. A host or datastore also returns everything BELOW it - a host whose nine databases changed must not answer "nothing happened".typesstring[]Filter to these event types (registry values; an unknown type matches nothing).list_infra_databasesinfra:readread-onlyThe logical databases across the organization, filterable by datastore, owning product, or do-not-touch. A database with no productId is legal and deliberate: orphans of dead projects are exactly what makes a shared instance dangerous.
cursorstringdatastoreIdstringdoNotTouchbooleanFilter to protected databases only.includeArchivedbooleanInclude decommissioned/archived rows. Default false.limitintegerorgstringOrganization id or slug. Omit to use the org owning your default workspace.productIdstringlist_infra_datastoresinfra:readread-onlyThe database instances an organization runs (the Postgres container, the Mongo, the Redis), each with its engine, network address, exposure and whether it is SHARED - meaning cluster-level operations on it reach other products. Pass hostId to restrict to one machine; omit it for every instance in the org.
cursorstringhostIdstringRestrict to one host. Omit to list every datastore in the org.includeArchivedbooleanInclude decommissioned/archived rows. Default false.limitintegerorgstringOrganization id or slug. Omit to use the org owning your default workspace.list_infra_hostsinfra:readread-onlyList the servers this organization runs on: name, ssh alias, status, capacity and how fresh each record is. Infrastructure is ORG-scoped, not per-product - one host serves many products. Cursor-paginated; archived (decommissioned) hosts are excluded unless includeArchived is set.
cursorstringincludeArchivedbooleanInclude decommissioned/archived rows. Default false.limitintegerorgstringOrganization id or slug. Omit to use the org owning your default workspace.list_infra_platformsinfra:readread-onlyThe orchestrators running on a host (coolify | dokku | kamal | k8s | compose | systemd), each with its admin URL and - the field that saves the most time - its CONTAINER NAMING template, without which you cannot find a service's logs.
hostIdstringrequiredcursorstringincludeArchivedbooleanInclude decommissioned/archived rows. Default false.limitintegerpreflight_operationinfra:readread-onlyBefore doing something to infrastructure, get back what the records say about it: do-not-touch flags and their reasons, shared instances and who else is on them, databases with no owner, retired hosts. Returns proceed | caution | stop. This is ADVICE, not permission - makermap runs no infrastructure operations and cannot stop one. Targets are "<type>:<id>"; any target that cannot be resolved is reported rather than silently passed.
intentstringrequiredWhat you are about to do. One of: drop_data, restart, rotate_credential, upgrade, migrate, restore, delete_resource, expose_publicly, write_compose.targetsstring[]What you are about to do it to, as "<type>:<id>" - e.g. "infra_database:<uuid>". Types: infra_host, infra_platform, infra_datastore, infra_database, deployment. Omit it to ask about the intent alone: you get the recorded rules for it, and no registry facts (the answer says so).record_verificationinfra:writeRecord that you checked a deployment against reality. Stored as YOUR report, stamped with who you are - not as an observation, because makermap cannot observe anything. `observed` is kept in the history and NEVER written onto the record: get_infra_drift compares the two, so a report can surface a disagreement without overwriting a record a person curated. state=pass with a failing result is refused.
deploymentIdstringrequiredstatestringrequiredOne of: pass, fail, partial.notestringobservedobjectWhat you actually SAW, as claims about the same fields the record holds. Kept in the event, never written onto the record - get_infra_drift compares the two.resultsobject[]One entry per check you ran.set_database_protectioninfra:writeMark a logical database untouchable, or clear the mark. Setting it REQUIRES a reason. This is a human act: an agent may not grant or revoke its own constraint, so the tool is PROTECTED at every autonomy level - the same rule that governs the autonomy policy, the spend cap and the telemetry policy.
doNotTouchbooleanrequiredidstringrequiredreasonstring | nullWhy. Required when setting the flag - an unexplained prohibition is the one people route around.set_deployment_runtimeinfra:writeproducts:writeRecord how a deployment actually works: start-up behaviour, its services, its environment-variable INVENTORY (names only - never send a value, art. 14) and the checks that confirm it. Mark a check pathMatters when running it by a shortcut path would give a confidently wrong answer.
deploymentIdstringrequiredchecksany[] | null[{ description, command, expect, pathMatters }]envInventoryany[] | null[{ name, purpose, secret, required, gotcha }] - variable NAMES ONLY. Never send a value.runtimeNotesstring | nullWhat happens on start-up, in prose.servicesany[] | null[{ name, role, image, singleton, singletonReason }]update_infra_databaseinfra:writeUpdate a recorded database: owner role, owning product, environment, size snapshot or notes. Deliberately CANNOT change the do-not-touch flag - that is set_database_protection, so the one prohibition in this domain can never be cleared by a payload that did not name it.
idstringrequiredenvironmentstring | nullPRODUCTION | STAGING | PREVIEW | DEVELOPMENT. Omit when the database is not tied to one.isolationNotestring | nullnamestringnotesstring | nullownerRolestring | nullproductIdstring | nullThe product that owns it, when one does. Databases with no owner are legal and are the dangerous ones.sizeMbinteger | nullupdate_infra_datastoreinfra:writeUpdate a recorded database instance: version, network address, exposure, sharing, auth model or storage.
idstringrequiredauthModelstring | nullHow auth actually works here, in prose - e.g. "loopback = trust, network = scram-sha-256". Records why a check can pass over one path and fail over another.containerNamestring | nullenginestringOne of: postgres, mysql, mariadb, mongodb, redis, valkey, clickhouse, elasticsearch, sqlite, other.internalHoststring | nullnamestringnetworkstring | nullnotesstring | nullpersistentbooleanportinteger | nullpubliclyExposedbooleansharingstringOne of: dedicated, shared. "shared" means cluster-wide operations reach other products.versionstring | nullvolumeNamestring | nullupdate_infra_hostinfra:writeUpdate a host's attributes, capacity snapshot or status (planned | active | quiescing | decommissioned). A status move is recorded as its own event. Use archive_infra_host to retire one.
idstringrequiredaddressstring | nullPublic IP or DNS name.blastRadiusNotestring | nullProse: what breaks if this host dies.cpuCoresinteger | nulldiskGbinteger | nullhostnamestring | nullnamestringThe short name you call it by, e.g. "net".notesstring | nullproviderstring | nullOne of: hetzner, namecheap, aws, gcp, azure, digitalocean, linode, vultr, ovh, selfhosted, other.ramMbinteger | nullsshAliasstring | nullThe ~/.ssh/config entry name. A POINTER, never a key - never send a credential here.statusstringOne of: planned, active, quiescing, decommissioned. Default active.update_infra_platforminfra:writeUpdate a recorded orchestrator: version, admin URL, container naming, or its integration connection.
idstringrequiredadminUrlstring | nullconnectionIdstring | nullAn IntegrationConnection, when a live connector exists.containerNamingstring | nullThe container naming template, e.g. "<service>-<resource_uuid>". Defaulted per kind when omitted.kindstringOne of: coolify, dokku, kamal, k8s, nomad, compose, systemd, other.notesstring | nullversionstring | nullverify_deploymentinfra:readread-onlyReturns the checks recorded for a deployment, with how to reach it. makermap RUNS NONE OF THEM - it has no outbound access to your infrastructure by design (art. 16). Run them yourself and send the outcome to record_verification, which stores it as your report.
deploymentIdstringrequiredInitiatives 4
create_initiativeproducts:writeCreate an initiative on a product: a container of direction (what large change moves the product). intendedOutcome and nextGate are the semantic center - what the bet is meant to change and the checkpoint it heads toward. `status` is one of proposed|active|paused|delivered|dropped. An optional `timeframe` (start/end ISO) is written transparently into the temporal layer. `confidence` (0-100) is only ever shown with intendedOutcome, never bare.
productIdstringrequiredtitlestringrequiredconfidenceinteger | null0-100. Shown ONLY alongside intendedOutcome (art. 13), never as a bare number.idempotencyKeystringRetrying with the same key returns the original initiative (no duplicate).intendedOutcomestring | nullWhat this bet is meant to change - the semantic center of the card (art. 13).linkedObjectivestring | nullFree-text objective in v1; an Objective entity may arrive later.nextGateIdstring | nullA Gate on the SAME product this initiative heads toward.ownerIdstring | nullA member of this product's workspace.priorityinteger | nullLower = more important. An ordering hint clients sort the list by; the list itself is paginated by an exact createdAt keyset, so sort on `priority` when you present it.statusstringproposed | active | paused | delivered | dropped (app registry, ADR-015).timeframeobject | nullOptional start/end; written transparently into the temporal layer.get_initiativeproducts:readread-onlyFetch one initiative by id: its attributes, its epics, its linked decisions and its timeframe.
idstringrequiredlist_initiativesproducts:readread-onlyList a product's initiatives (newest first), each with its timeframe from the temporal read model. Filter by status or owner. Cursor-paginated.
productIdstringrequiredcursorstringOpaque cursor from a prior response nextCursor.limitintegerownerIdstringOnly initiatives owned by this user.statusstringOnly initiatives with this status.update_initiativeproducts:writeUpdate an initiative: attributes, a status transition, or archive/restore via `archived`. Changing `timeframe` writes a new current schedule version and REQUIRES a reason; passing timeframe with both dates null clears it. Attach or detach an epic with update_epic(initiativeId); link a decision with link_decision(entityType:"initiative").
idstringrequiredarchivedbooleantrue archives (soft delete, art. 9), false restores.confidenceinteger | null0-100. Shown ONLY alongside intendedOutcome (art. 13), never as a bare number.intendedOutcomestring | nulllinkedObjectivestring | nullnextGateIdstring | nullownerIdstring | nullpriorityinteger | nullLower = more important. An ordering hint clients sort the list by; the list itself is paginated by an exact createdAt keyset, so sort on `priority` when you present it.statusstringproposed | active | paused | delivered | dropped (app registry, ADR-015).timeframeobject | nulltitlestringInstructions 18
create_instruction_setinstructions:writeCreate a new (empty) named set to group instructions under.
namestringrequireddescriptionstring | nullworkspacestringWorkspace slug or id. Defaults to your default workspace.delete_instructioninstructions:writedestructiveagents deniedARCHIVE a single instruction by id (this tool no longer deletes). It disappears from lists, sets, prompts and search, but its text and its whole revision history are kept (Constitution art. 15) - call unarchive_instruction with the same id to bring it back. Find archived rules with list_instructions archived="only".
idstringrequireddelete_instruction_setinstructions:writedestructiveagents deniedARCHIVE a set by its slug in a workspace (this tool no longer deletes). Only the grouping is retired; its instructions are kept, and so are its frozen versions. The slug stays reserved - call unarchive_instruction_set with the same slug to bring it back.
slugstringrequiredworkspacestringWorkspace slug or id. Defaults to your default workspace.get_instruction_revisioninstructions:readread-onlyRead one revision of an instruction in full. Revision N is the text that WAS revision N, not the current text; asking for the current number returns the head with isHead:true. An out-of-range number is refused with the available range.
idstringrequiredrevisionintegerrequiredThe revision number to read.get_instruction_set_versioninstructions:readread-onlyReturn a set exactly as it was at version N: each member's title and body at the PINNED revision, not its current text. A member whose pinned content no longer exists is returned with present:false rather than silently dropped, so a caller can report the gap. A member archived AFTER the snapshot still returns its frozen text - a version is frozen - but is flagged archived:true, so check that flag before applying the rule. Response size is bounded by the version's memberCount, which list_instruction_set_versions shows.
slugstringrequiredversionintegerrequiredThe version number to read.workspacestringWorkspace slug or id. Defaults to your default workspace.get_instructionsinstructions:readread-onlyFetch the union of instructions in the named set(s) as one ready-to-apply markdown rules block. Use this to apply saved rules to a new project (e.g. sets ["infrastructure","angular"]).
setsstring[]requiredSet names or slugs to pull. Multiple sets are unioned.workspacestringWorkspace slug or id. Defaults to your default workspace.list_instruction_revisionsinstructions:readread-onlyList the immutable history of one instruction, newest first. Each row is a state the rule USED to hold, with the revision number it held and how it stopped being current (edit or restore). The CURRENT text is not in this list - `headRevision` in the response names it, and get_instruction_revision returns it. Paginated by opaque cursor.
idstringrequiredcursorstringOpaque cursor from a prior response nextCursor.limitintegerlist_instruction_set_versionsinstructions:readread-onlyList the frozen versions of an instruction set, newest first, with each version's member count and who cut it. Use get_instruction_set_version to read the pinned rules themselves. Paginated by opaque cursor.
slugstringrequiredcursorstringOpaque cursor from a prior response nextCursor.limitintegerworkspacestringWorkspace slug or id. Defaults to your default workspace.list_instruction_setsinstructions:readread-onlyList a workspace's instruction sets with their slug and (non-archived) instruction count. Archived sets are excluded by default; pass archived="only" to find one to unarchive.
archivedstringexclude (default) hides archived records, only returns just them (to unarchive one), all returns both.workspacestringWorkspace slug or id. Defaults to your default workspace.list_instructionsinstructions:readread-onlyList saved instructions, optionally filtered by set slug/name or a search term. Archived rules are excluded by default; pass archived="only" to find one to unarchive_instruction.
archivedstringexclude (default) hides archived records, only returns just them (to unarchive one), all returns both.cursorstringOpaque cursor from a prior response nextCursor.limitintegersearchstringsetstringstatusstringall (default) returns drafts and published rules together, each marked; draft or published narrows to one. Only PUBLISHED rules are ever delivered to an agent - through a set, the context package or preflight_operation.workspacestringWorkspace slug or id. Defaults to your default workspace.move_instructioninstructions:writeMove an instruction into a different workspace (by slug or id). Its set membership is cleared (sets do not span workspaces). Requires member access to both workspaces.
idstringrequiredworkspacestringrequiredDestination workspace slug or idrestore_instruction_revisioninstructions:writePut an older revision of an instruction back as the current text. Nothing is lost: the text being replaced becomes a revision of its own, so the history stays complete and strictly append-only. Restoring the text the rule already has does nothing, so calling this twice with the same revision adds exactly one revision.
idstringrequiredrevisionintegerrequiredThe revision number to put back onto the current record.save_instructionsinstructions:writeSave a batch of reusable AI rules/instructions to makermap. Pass an ARRAY where each rule is a separate {title, body} object. Optionally file them all under one or more named sets. Saved rules are DRAFTS unless you pass published:true - a draft reaches no agent at all, not through a set, not in a context package and not from preflight_operation, so writing one down and putting it in force are two separate acts. The answer says which you did.
instructionsobject[]requiredOne object per rule. Do NOT concatenate multiple rules into one item.publishedbooleanfalse (default) saves DRAFTS, which reach no agent until published - not through a set, not in a context package, not from preflight_operation. Send true to put them in force now.setsstring[]Optional set names to file every instruction under (created if missing).workspacestringWorkspace slug or id. Defaults to your default workspace.snapshot_instruction_setinstructions:writeFreeze a set's current membership as a new version, pinning every rule to the revision it holds right now. Later edits to those rules do NOT change the version - that is what makes a pinned set reproducible. Archived rules are excluded. Versions are never overwritten; each call cuts the next number. Pass idempotencyKey to make a retry safe.
slugstringrequiredidempotencyKeystringRetrying with the same key returns the original version instead of cutting a new one.namestring | nullOptional label for this version, e.g. "pre-angular-22".workspacestringWorkspace slug or id. Defaults to your default workspace.unarchive_instructioninstructions:writeBring an archived instruction back into lists, sets, prompts and search. No revision is cut - unarchiving restores visibility, not content. Safe to call on a live rule.
idstringrequiredunarchive_instruction_setinstructions:writeBring an archived instruction set back. Its slug stayed reserved while it was archived, so this is how you recover the address rather than creating a duplicate. Safe to call on a live set.
slugstringrequiredworkspacestringWorkspace slug or id. Defaults to your default workspace.update_instructioninstructions:writeEdit an existing instruction by id (get ids from list_instructions). Provide only the fields to change. `sets` (names) REPLACES the whole set membership; missing sets are created.
idstringrequiredaddSetsstring[]Set names to ADD (created if missing) without touching the others. Do not combine with `sets`.bodystringremoveSetsstring[]Set names to REMOVE without touching the others. Do not combine with `sets`.setsstring[]Set names; REPLACES the whole membership. Omit to leave sets unchanged.titlestringupdate_instruction_setinstructions:writeRename a set or change its description, identified by its current slug (in a workspace).
slugstringrequireddescriptionstring | nullnamestringworkspacestringWorkspace slug or id. Defaults to your default workspace.Intake 1
get_intake_planproducts:readread-onlyThe audit specification for taking an existing codebase into the record, layer by layer: what to look for locally, which tool records the answer, and what is still owed. makermap reads no code and reaches no infrastructure - it asks the question, you run it and write the answers back. Intake is finished when every layer is either filled or explicitly declared skipped in the product's intake-report document.
productIdstringrequiredThe product being taken into the record.workspacestringWorkspace slug or id. Defaults to your default workspace.Integrations 9
create_integration_connectionintegrations:writeDeclare a connection between this workspace and an external system (provider one of github|figma|jira|gdrive|slack|generic), with a unique name and optional non-secret config (base url, project key, ...). v1 records the periphery only - no outbound and no secret (do not put keys in config).
namestringrequiredA human label, unique per (workspace, provider).providerstringrequiredThe external system: github | figma | jira | gdrive | slack | generic (app registry, ADR-015).configobject | nullProvider-specific NON-secret config (base url, project key, ...). Never a secret (art.14).idempotencyKeystringstatusstringDefaults to active.workspacestringWorkspace id or slug; defaults to your default workspace.delete_integration_connectionintegrations:writedestructiveagents deniedDelete an integration connection by id. External-periphery config, re-establishable by re-connecting; its references are unaffected.
idstringrequiredget_integration_connectionintegrations:readread-onlyFetch one integration connection by id: provider, name, status and non-secret config.
idstringrequiredlink_external_referenceintegrations:writeAttach an external object (a Figma frame, Jira issue, Drive doc, GitHub link, or generic url) to a product, epic, task or decision. provider one of github|figma|jira|gdrive|slack|generic; optional kind, externalId, cached title/status. Idempotent on (entityType, entityId, provider, url) - re-linking refreshes the cached fields. The reference then rides on the entity's card and in get_product_context.
entityIdstringrequiredThe product, epic, task or decision id.entityTypestringrequiredWhat to attach to: product | epic | task | decision.providerstringrequiredThe external system: github | figma | jira | gdrive | slack | generic.urlstringrequiredexternalIdstringProvider-side id (Jira key, Figma node, ...).kindstringDefaults to "link".metadataobject | nullstatusstringCached external status label.titlestringCached display title (no live fetch in v1).list_external_referencesintegrations:readread-onlyList external references for one entity (entityType + entityId), across a product (productId), or across a workspace. Optionally filter by provider. Cursor-paginated.
cursorstringentityIdstringFilter to one entity; requires entityType.entityTypestringFilter to one entity type (pair with entityId).limitintegerproductIdstringAll references across a product.providerstringThe external system: github | figma | jira | gdrive | slack | generic.workspacestringWorkspace id or slug for a workspace-wide list.list_integration_connectionsintegrations:readread-onlyList this workspace's declared connections to external systems (github|figma|jira|gdrive|slack|generic), with their non-secret config. Filter by provider or status. Cursor-paginated.
cursorstringlimitintegerproviderstringOnly connections for this provider.statusstringOnly connections with this status.workspacestringrotate_integration_webhook_secretintegrations:writeMint (or replace) a connection's inbound-webhook signing secret and return it ONCE, with the payload URL to point the sender at. makermap generates the secret and never reveals it again - rotate to replace it. The sender signs HMAC-SHA256 over the raw body and sends sha256=<hex> in x-makermap-signature (optional x-makermap-event / x-makermap-delivery). The github provider keeps its own rich route (link_repository); this is the generic seam for every other provider.
idstringrequiredunlink_external_referenceintegrations:writedestructiveRemove a previously linked external reference by its id.
idstringrequiredupdate_integration_connectionintegrations:writeUpdate a connection: rename, change status (active|paused|error), or replace/clear its config.
idstringrequiredconfigobject | nullnamestringstatusstringactive | paused | error (app registry, ADR-015).Intelligence 6
evaluate_scenariointelligence:readread-onlyProject a what-if over the portfolio WITHOUT changing anything: shift a product's dates (SHIFT_TIMELINE), freeze a product (FREEZE_PRODUCT - drop it from active work and pause its spend), or set hypothetical financials (ADJUST_FINANCIAL). Returns baseline vs projected portfolio metrics (burn, net, runway, at-risk products, active count, upcoming deadline count) and an explained delta for each change. Writes NOTHING - it is an unsaved projection. Read-only; financial metrics need financials:read, workload needs products:read. Products the changes name that you cannot see are returned in `unknownProductIds`.
changesobject[]requiredThe hypothetical changes to project.workspacestringWorkspace slug or id. Defaults to your default workspace.get_forecastintelligence:readread-onlyA deterministic, explainable forecast over the history that already exists (not machine learning). Four projections: date-slip risk (drift from baseline, repeated reschedules, overdue with no actual), stage stagnation (dwelling past the product's OWN typical pace), health trajectory (consecutive health-band moves the wrong way on a product that is not Critical yet), and an upcoming deadline crunch. Each risk carries a `severity` (critical|high|medium|low, the same labels a recommendation's priority uses) with the sentence explaining that label, a `horizon` saying when it bites (now|weeks|quarter|later), and a `suggestedAction` deep-linking the one call you would make next - which nothing here runs. `critical` is structurally reserved for an observed fact, never for a projection: a forecast may not shout louder than its evidence. Every projection cites the exact history it reads and an honest confidence, and says "not enough history" rather than inventing a number - in particular it never extrapolates an arrival date for a health band. Read-only; gated by intelligence:read + products:read. Omit `productId` for the whole portfolio.
productIdstringNarrow to a single product. Omit for the whole portfolio.workspacestringWorkspace slug or id. Defaults to your default workspace.get_lessonsintelligence:readread-onlyReusable lessons mined from patterns across the products in a workspace: gate types that always run long, decisions that get reversed, the stage products stall in, and products with recurring incidents. Each carries a verbatim pattern, the evidence behind it (products/counts), an explained confidence, and a deep-link to SAVE it as a knowledge document (it advises; a human decides whether to keep it). Read-only; each source gated by its own scope (products:read / decisions:read / incidents:read), withheld sources named in `missing`.
workspacestringWorkspace slug or id. Defaults to your default workspace.get_production_readinessintelligence:readread-onlyEverything standing between a product and production, counted from what the record already holds: open readiness gates and how far each one is, open decisions, unresolved incidents, deployments nobody has verified or verified long ago, and a CI signal that is not green. Every figure says how it was measured and every line deep-links to the write that would change it - this decides nothing. `assurance` reports how many settled conditions rest on a fact makermap computed and how many on somebody's word.
productIdstringrequiredThe product being assessed.workspacestringWorkspace slug or id. Defaults to your default workspace.get_recommendationsintelligence:readread-onlyExplainable portfolio recommendations: where to look, what to speed up, what to freeze. Each carries a reason, real-number evidence, an explained confidence and a deep-link to the existing action (it advises, it never decides). Aggregates health, financials, incidents, decisions and gates - each gated by its own read scope; withheld sources are named in `missing`. Omit `productId` for the whole portfolio.
productIdstringNarrow to a single product. Omit for the whole portfolio.workspacestringWorkspace slug or id. Defaults to your default workspace.run_portfolio_reviewintelligence:readread-onlyThe bounded weekly portfolio-review workflow: composes recommendations, the predictive forecast and cross-product lessons with the Command Center headline into one review, and lays out a prioritized set of PROPOSED actions - each a deep-link to the existing write (it advises, it never acts, art.11). Read-only: it makes no domain write and proposes every action for a human (or an agent via request_approval) to confirm, so an agent at any autonomy level can run it and the one call is the telemetry record. Omit `productId` for the whole portfolio (a single-product review omits the portfolio headline). Withheld sources are named in `missing`.
productIdstringNarrow the review to a single product. Omit for the whole portfolio.workspacestringWorkspace slug or id. Defaults to your default workspace.Inventory 8
capture_ideainventory:writeWrite down something you might build, in five seconds. Only a name is required; put everything else - features, look, how it would reach people - in the markdown body, structured however you like. There are deliberately no fields for those, because a form that demands them before a thought is captured loses the thought.
namestringrequiredWhat the idea is, in a few words.bodystring | nullEverything else, as markdown: what it would do, what it would look like, how it would reach people. There are deliberately no fields for those - at this stage nobody fills them in honestly, and the structure is yours to choose.idempotencyKeystringworkspacestringWorkspace slug or id. Defaults to your default workspace.convert_idea_to_productinventory:writeTurn an idea into a real product, at lifecycle stage Idea. The idea is NOT consumed: it stays, stamped with what it became, so its origin survives. The body becomes a pinned `idea-brief` document inside the product, and any domain parked on the idea moves to the product with the hand-over recorded. The answer names the "why" fields the new product does not have yet - filling them is what get_product_context later hands an agent. Needs products:write as well as inventory:write.
ideastringrequiredThe idea id.idempotencyKeystringnamestringThe product name, if it should differ from the idea name.workspacestringWorkspace slug or id. Defaults to your default workspace.create_inventory_iteminventory:writeRecord something the workspace owns - today a domain. The cost is in MINOR units (1200 is $12.00) and needs an amount, a currency and a period together or not at all. `expiresAt` is what YOU know: this server reaches no registrar and stores your report with the date you made it. Recording the same live value twice is refused, naming the holding you already have.
kindstringrequiredWhat kind of holding. This build registers: domain.valuestringrequiredThe holding itself, e.g. a domain name.costCentsinteger | nullWhat it costs, in MINOR units (cents, kopeks). 1200 is $12.00.costCurrencystring | nullISO-4217 code, e.g. USD. Required whenever costCents is given.costPeriodstring | nullHow often it costs that: annual | monthly | once.expiresAtstring | nullWhen it renews or lapses, as reported by you - this server never checks a registrar. Stored in the temporal layer, so later changes keep the original as a baseline.idempotencyKeystringnotesstring | nullMarkdown.registrarstring | nullWhere it is held. A note, not an integration.workspacestringWorkspace slug or id. Defaults to your default workspace.get_inventory_iteminventory:readread-onlyOne holding by id or by value (a domain name), with its renewal date and how old that report is, its cost, and every live holder. Use this before assigning something, so you can see what is already on it - a domain may legitimately have several holders.
itemstringrequiredThe holding: its id, or its value (a domain name).workspacestringWorkspace slug or id. Defaults to your default workspace.get_inventory_overviewinventory:readread-onlyWhat the workspace owns, what it costs per year, and which of it nothing is using. Costs are in MINOR units and reported ONE ROW PER CURRENCY - they are never added across currencies, because that total would be wrong by an amount nobody can see. One-off costs are listed apart from the yearly figure, and holdings with no recorded cost are counted rather than treated as free. `expiring` lists what renews inside the horizon, soonest first, INCLUDING dates already past - each with how long ago that date was last confirmed, since this server never contacts a registrar.
horizonDaysintegerHow far ahead "renews soon" looks. Default 30 days.workspacestringWorkspace slug or id. Defaults to your default workspace.list_ideasinventory:readread-onlyEverything captured but not built. An idea is a name and a markdown body and nothing else - it costs no product slot, so there is no reason to be sparing with them. Converted ideas are included by default and carry the product they became, which is what answers "why did we not do this" later.
cursorstringincludeArchivedboolean | stringInclude archived ideas. Default false.includeConvertedboolean | stringInclude ideas that already became products. Default true - they are the useful half of the history.limitintegerworkspacestringWorkspace slug or id. Defaults to your default workspace.list_inventory_itemsinventory:readread-onlyDomains and other holdings this workspace owns but has not necessarily built on, with what each costs, when it renews and what is using it. `idle: true` means nothing is using it right now. A renewal date is always paired with `asOf` - the date is REPORTED by a person or an agent and this server never checks a registrar, so treat an old `asOf` as an old answer. Released and archived holdings are hidden unless asked for; nothing is ever deleted.
cursorstringOpaque cursor from a prior response nextCursor.idleOnlyboolean | stringOnly holdings that nothing is currently using.includeArchivedboolean | stringInclude holdings you have hidden. Default false.includeReleasedboolean | stringInclude holdings you no longer own. Default false.kindstringOnly holdings of this kind.limitintegerworkspacestringWorkspace slug or id. Defaults to your default workspace.report_inventory_expiryinventory:writeReport when a holding renews or lapses. It is a REPORT, not a fact this server can verify - it is stored with the moment you reported it, and every read hands that back so a stale answer looks stale. Moving a date that already exists needs a `reason`, so a renewal is recorded as a renewal; the original date is kept as a baseline either way. Pass expiresAt: null to say you no longer know.
expiresAtstring | nullrequiredThe renewal date you are reporting, or null to clear it.itemstringrequiredThe holding: its id, or its value.reasonstringWhy it moved - required when there is already a date, so a renewal is recorded as a renewal rather than as a silent edit.workspacestringWorkspace slug or id. Defaults to your default workspace.Lifecycle 1
get_lifecycleproducts:readread-onlyFetch a workspace's default lifecycle model - its ordered stages with key, name, position and semantic role.
workspacestringWorkspace slug or id. Defaults to your default workspace.Logs 7
append_log_entrydocuments:writeAppend one entry to a named log. Entries are append-only: there is no way to edit or delete one, in this or any other transport. To correct an entry, append a new one with `supersedesEntryId` set to the old entry's id - the corrected entry then stops appearing in `read_log` unless `includeSuperseded` is set, and the correcting entry names what it replaced. `seq` is assigned by the server, monotonically within the log. Pass `idempotencyKey` when retrying after a timeout: the same key returns the original entry rather than creating a second one (the key is recorded after the entry commits, so a crash in that narrow window can still let a retry through).
bodystringrequiredThe entry content, markdown.slugstringrequiredThe log slug (or its id, or its exact title).idempotencyKeystringRetrying with the same key returns the original entry - no duplicate seq.supersedesEntryIdstring | nullThe id of an entry in THIS log that this one corrects. Entries are never edited or deleted: a correction is a new entry pointing at the old one, and the corrected entry stops appearing in read_log unless includeSuperseded is set.tagsstring[]titlestring | nullOptional heading for this entry - a version, a date, a one-line subject.workspacestringWorkspace slug or id. Defaults to your default workspace.create_logdocuments:writeCreate a named log: an append-only feed of entries with a stable slug, read from the tail. Use a log for anything written one entry at a time and never rewritten - a changelog, a decision journal, a running research note. Use `save_document` instead for text that is rewritten whole, because a document revision snapshots the entire body and appending to one costs a full copy every time. `format` is a discriminator; "journal" is the only value in v1.
titlestringrequiredHuman title shown in lists.descriptionstring | nullWhat this log is for. Not an entry - read_log never returns it.formatstringDiscriminator. Only "journal" exists in v1 and it switches no behaviour.productIdstring | nullAttach the log to a product. Omit/null for a workspace-level log.slugstringThe stable address. Omit to derive it from the title; it never changes when the title does.tagsstring[]workspacestringWorkspace slug or id. Defaults to your default workspace.delete_logdocuments:writedestructiveagents deniedPermanently delete a named log by id, together with every entry in it. This is not reversible, and it is the only delete a log has - individual entries can never be removed.
idstringrequiredget_logdocuments:readread-onlyRead one log's metadata by slug (or id, or exact title): its description, tags, product, format, entry count and last entry number - but NOT its entries, which are a separate paginated read via `read_log`. That separation is deliberate: a log must never be pullable whole in one call. Use this when you hold a slug and need to know what the log is for before reading it, or to read the `description`, which is where a migrated log records the convention governing it.
slugstringrequiredThe log slug (or its id, or its exact title).workspacestringWorkspace slug or id. Defaults to your default workspace.get_log_entrydocuments:readread-onlyRead a single entry of a named log by its `seq` - the number `read_log` shows on every row. A superseded entry IS returned here (addressing one directly is an explicit act) together with `supersedes` and `supersededBy`, so a correction chain can be followed in both directions.
seqintegerrequiredThe entry number within this log.slugstringrequiredThe log slug (or its id, or its exact title).workspacestringWorkspace slug or id. Defaults to your default workspace.list_logsdocuments:readread-onlyList the named logs in a workspace, most recently appended-to first. Filter by productId ("none" for workspace-level only) or tag. Rows carry `entryCount` and `lastEntrySeq` but never any entry body - read a log with `read_log`. Paginated by opaque cursor.
cursorstringOpaque cursor from a prior response nextCursor.limitintegerproductIdstring | stringOnly logs attached to this product. Pass "none" for workspace-level logs only; omit for both.tagstringworkspacestringWorkspace slug or id. Defaults to your default workspace.read_logdocuments:readread-onlyRead a named log from the tail: the newest entries first. `read_log(slug: "changelog", limit: 3)` returns the last three entries and reads only those rows, never the whole log. Paginate further back with `olderThan`, the opaque cursor from a prior response's `nextCursor` - the cursor is built on `seq`, not on time, so two entries written in the same millisecond still page correctly. Entries that a later entry corrected are hidden by default; pass `includeSuperseded: true` to see them. A log is not a document: it is appended one entry at a time and addressed by `seq`, so there is no way to read it whole and no reason to.
slugstringrequiredThe log slug (or its id, or its exact title).includeSupersededboolean | stringInclude entries that a later entry corrected. Hidden by default.limitintegerHow many entries to return, newest first.olderThanstringOpaque cursor from a prior response nextCursor. Returns entries older than it.workspacestringWorkspace slug or id. Defaults to your default workspace.Marketing 6
create_campaignmarketing:writeCreate a marketing campaign on a product: a named push over a span of time on the Product Stream. Optional `timeframe` ({ startsAt, endsAt }) is written transparently into the temporal layer; the campaign owns no date column. status defaults to planned.
namestringrequiredproductIdstringrequiredchannelstring | nullPrimary channel label, e.g. email | launch | paid.idempotencyKeystringstatusstringDefaults to planned.timeframeobject | nullOptional planned span, written to the temporal layer.get_campaignmarketing:readread-onlyFetch one campaign by id: its attributes, status and planned timeframe.
idstringrequiredget_marketing_profilemarketing:readread-onlyA product's marketing context (Vision §30): positioning, target segments, channels, brand-asset links and launch plan. null when none has been set.
productIdstringrequiredlist_campaignsmarketing:readread-onlyList a product's marketing campaigns, each with its planned timeframe from the temporal read model. Filter by status. Cursor-paginated.
productIdstringrequiredcursorstringlimitintegerstatusstringplanned | active | paused | completed | cancelled (app registry, ADR-015).update_campaignmarketing:writeUpdate a campaign: attributes, a status transition, archive/restore via `archived`, or the timeframe. Changing an existing timeframe writes a new current schedule version and REQUIRES a reason; passing timeframe { startsAt: null, endsAt: null } clears it.
idstringrequiredarchivedbooleantrue archives (soft delete, art.9), false restores.channelstring | nullnamestringstatusstringplanned | active | paused | completed | cancelled (app registry, ADR-015).timeframeobject | nullupsert_marketing_profilemarketing:writeCreate or update a product's marketing profile. Omit a field to leave it unchanged; pass a new array to replace a list (or [] to clear it), or null to clear a text field. Idempotent - at most one profile per product.
productIdstringrequiredbrandAssetsstring[]Links to brand assets (urls), not stored files.channelsstring[]launchPlanstring | nullnotesstring | nullpositioningstring | nulltargetSegmentsstring[]Replaces the list; [] clears it.Milestones 4
create_milestoneproducts:writeCreate a milestone on a product: a named point it is aiming for. `status` is one of planned|missed|cancelled (reaching goes through update_milestone). An optional `target` ({ at: ISO }) is written transparently into the temporal layer as a point; the milestone owns no date column.
productIdstringrequiredtitlestringrequireddescriptionstring | nullidempotencyKeystringRetrying with the same key returns the original milestone (no duplicate).statusstringDefaults to planned. Reaching a milestone goes through update_milestone (it writes the actual), so `reached` is refused here.targetobject | nullOptional target date; written transparently into the temporal layer as a point.get_milestoneproducts:readread-onlyFetch one milestone by id: its attributes, its status and its target.
idstringrequiredlist_milestonesproducts:readread-onlyList a product's milestones, each with its target from the temporal read model. Filter by status. Pass `dueBefore` to page milestones whose target falls before a date, target-ordered. Cursor-paginated.
productIdstringrequiredcursorstringOpaque cursor from a prior response nextCursor.dueBeforestringOnly milestones whose target is before this date, target-ordered (via the temporal read model).limitintegerstatusstringOnly milestones with this status.update_milestoneproducts:writeUpdate a milestone: attributes, a status transition, or archive/restore via `archived`. status="reached" records the actual on the timeline (via the domain-event projector). Changing `target` writes a new current schedule version and REQUIRES a reason; passing target { at: null } clears it.
idstringrequiredarchivedbooleantrue archives (soft delete, art. 9), false restores.descriptionstring | nullstatusstringA status move; `reached` records the actual on the temporal layer.targetobject | nulltitlestringOrgOverview 1
get_org_overvieworg_overview:readread-onlyThe organization-level pair of get_workspace_overview: everything you are working on across every workspace in one organization - the workspaces and their counts, every product ranked by what needs attention, the merged attention queue, and the merged "what needs a human" queue with a one-call resolve pointer on each row. Assembled from get_workspace_overview and list_inbox per workspace, so it can never disagree with the board you open from it. Read-only; it owns no data and points at existing writes. Workspaces are read most-recently-active first and capped, and the cap is reported.
orgstringOrganization slug or id. Defaults to the org owning your default workspace.Platform 1
get_my_invitesinvites:readread-onlyReturn the signup invites belonging to the authenticated account, with the quota behind them and the installation's current signup mode. An invite is a BEARER code (`MM-XXXXX-XXXXX-XXXXX-XXXXX`): whoever holds it can create one account, and it is not bound to an email address, so passing it on is the intended use. Each invite reports `state` - active, revoked, expired or exhausted - computed from its own facts rather than stored. `quota` explains the ceiling the way every other limit in this API does: `allowance`, live `used`, `remaining`, and `countedFrom` naming exactly what was counted (invites you created and have not revoked - a REVOKED invite returns its slot, a REDEEMED one does not). `signupMode` says whether a code is worth anything today: on `open` anybody can register without one, on `invite_only` a code is required, on `closed` nothing creates accounts. READ-ONLY: creating and revoking an invite are human acts and are not published to any token, because an invite brings a new account into existence.
Takes no arguments.
Portfolio 1
get_portfolio_streamproducts:readread-onlyThe whole active portfolio as condensed lanes in one call: every ACTIVE product with its lifecycle stage (id/key/name/role/position/color), its computed health band (band + confidence off the health mirror; the score is never returned), and its KEY timeline items - initiatives, milestones and releases, each carrying its plan (current), baseline (original), forecasts and actuals exactly as list_timeline_items shapes them. Gates carry no plannable date and are absent by design. This is the cross-product sibling of get_workspace_overview: ACTIVE products only (the active work surface), and it adds each product's key items, which the overview does not. The workspace's ORDERED lifecycle stages travel alongside - including the stages with no products - so you group or filter by stage.role or health band client-side without ever hardcoding stage order. Backs the /dashboard Portfolio stream view.
workspacestringWorkspace slug or id. Defaults to your default workspace.ProductHealth 2
get_product_healthproducts:readread-onlyFetch one product's current health: its severity band (Critical | Needs decision | Stalled | Watch | Healthy), the structured reasons behind it (each with its source and evidence), a confidence in the read, and the recent band-change history. Computed fresh on every call. The internal ranking number is never returned - only the band and the reasons.
productIdstringrequiredlist_products_by_bandproducts:readread-onlyList a workspace's products grouped by health band - most severe band first, and within a band by internal ranking (never shown). Filter to one band, and read `distribution` for the whole-workspace count per band. Each item carries its band, confidence and top reasons. Cursor-paginated.
bandstringRestrict to one severity band. Omit for all products, most severe band first.cursorstringOpaque cursor from a prior response nextCursor.limitintegerworkspacestringWorkspace slug or id. Defaults to your default workspace.Products 23
add_project_credentialprojects:writeStore a NON-PRODUCTION test credential (label + optional environment/url/username/secret/notes). The value is encrypted at rest and is never returned by a read - reads carry the metadata plus hasSecret, and reveal_credential (scope credentials:reveal) is the only way back to it. Not for production secrets: store a reference to your secret manager instead.
labelstringrequiredWhat this credential is for, e.g. "Admin test user".projectIdstringrequiredenvironmentstring | nullPRODUCTION is discouraged: store a reference to your secret manager rather than the value itself.hasSecretanyRead-only; derived from whether a value is stored. Pass secret: null to clear the value.notesstring | nullsecretstring | nullurlstring | nullusernamestring | nulladd_project_deploymentprojects:writeRecord where a project component is deployed (component name + environment, optional provider/url/notes).
namestringrequiredprojectIdstringrequiredenvironmentstringnotesstring | nullproviderstring | nullurlstring | nulladd_project_stack_itemprojects:writeAdd a technology to a project stack (name + category, optional version/url/notes).
namestringrequiredprojectIdstringrequiredcategorystringnotesstring | nullurlstring | nullversionstring | nullcreate_productproducts:writeCreate a new product in a workspace (defaults to your default workspace). Accepts the five "why" fields (summary, vision, problem, targetAudience, valueProposition).
namestringrequiredcolorstring | nulldescriptionstring | nullhealthanyRead-only computed cache (P4-E3); cannot be set via create/update.problemstring | nullstagestringstatusstringOperational status (ADR-013): ACTIVE | PAUSED | ARCHIVED | SUNSET. PLANNING/ON_HOLD/COMPLETED are deprecated and mapped to ACTIVE/PAUSED/ARCHIVED.summarystring | nulltagsstring[]targetAudiencestring | nulltypestring | nullvaluePropositionstring | nullvisionstring | nullworkspacestringWorkspace slug or id. Defaults to your default workspace.create_projectprojects:writeDeprecated alias of create_product. Create a new product in a workspace.
namestringrequiredcolorstring | nulldescriptionstring | nullstagestringstatusstringtagsstring[]typestring | nullworkspacestringWorkspace slug or id. Defaults to your default workspace.delete_productproducts:writedestructiveagents deniedSoft-delete a product by id: it is archived (recoverable) and hidden from every active view, not physically destroyed. Restore it with restore_product. The irreversible physical deletion is a separate, deliberate step - purge_product - which only works on an already soft-deleted product.
idstringrequireddelete_projectprojects:writedestructiveagents deniedDeprecated alias of delete_product. Soft-delete a product by id (recoverable with restore_product; hidden from active views).
idstringrequiredget_productproducts:readread-onlyFetch a single product by id.
idstringrequiredget_projectprojects:readread-onlyDeprecated alias of get_product. Fetch a single product by id.
idstringrequiredlist_productsproducts:readread-onlyList a workspace's products, most recently updated first.
cursorstringOpaque cursor from a prior response nextCursor.deletedboolean | stringtrue returns soft-deleted products (the archive) instead of active ones.limitintegerstatusstringOperational status (ADR-013): ACTIVE | PAUSED | ARCHIVED | SUNSET. PLANNING/ON_HOLD/COMPLETED are deprecated and mapped to ACTIVE/PAUSED/ARCHIVED.workspacestringWorkspace slug or id. Defaults to your default workspace.list_projectsprojects:readread-onlyDeprecated alias of list_products. List a workspace's products, most recently updated first.
limitintegerstatusstringworkspacestringWorkspace slug or id. Defaults to your default workspace.move_productproducts:writeMove a product into a different workspace (by slug or id). Requires member access to both.
idstringrequiredworkspacestringrequiredDestination workspace slug or idmove_projectprojects:writeDeprecated alias of move_product. Move a product into a different workspace (by slug or id).
idstringrequiredworkspacestringrequiredDestination workspace slug or idpurge_productproducts:writeworkspace:exportdestructiveagents deniedIrreversibly, physically delete a product and its satellite data by id. This cannot be undone. The product must ALREADY be soft-deleted (call delete_product first) - purge is the deliberate second step, never a one-shot destroy. Requires the workspace:export right (the export gate: what can be destroyed could first be exported). Always audited; not available to an agent principal.
idstringrequiredremove_project_credentialprojects:writedestructiveagents deniedDelete a test credential by id. The stored value is destroyed with it and cannot be recovered except from a backup taken with the master key.
idstringrequiredremove_project_deploymentprojects:writedestructiveagents deniedDelete a deployment by id.
idstringrequiredremove_project_stack_itemprojects:writedestructiveagents deniedDelete a stack item by id.
idstringrequiredrestore_productproducts:writeBring a soft-deleted product back into active views by id. Idempotent - restoring an already-active product is a no-op. Find soft-deleted products with list_products deleted=true.
idstringrequiredupdate_productproducts:writeUpdate fields of an existing product (name, description, status, stage, type, color, tags, and the five "why" fields). Set `stage` to advance it along the delivery lifecycle. `health` is read-only and rejected.
idstringrequiredcolorstring | nulldescriptionstring | nullexpectedVersionintegerThe `version` you last read. When given, the write is refused with ENTITY_CHANGED if the record moved since - instead of overwriting a change somebody else made. Omit it for a one-shot write that has not read the record.healthanyRead-only computed cache (P4-E3); cannot be set via create/update.namestringproblemstring | nullstagestringstatusstringOperational status (ADR-013): ACTIVE | PAUSED | ARCHIVED | SUNSET. PLANNING/ON_HOLD/COMPLETED are deprecated and mapped to ACTIVE/PAUSED/ARCHIVED.summarystring | nulltagsstring[]targetAudiencestring | nulltypestring | nullvaluePropositionstring | nullvisionstring | nullupdate_projectprojects:writeDeprecated alias of update_product. Update fields of an existing product. Set `stage` to advance it along the delivery lifecycle.
idstringrequiredcolorstring | nulldescriptionstring | nullnamestringstagestringstatusstringtagsstring[]typestring | nullupdate_project_credentialprojects:writeUpdate a NON-PRODUCTION test credential by id (label, environment, url, username, secret, notes). Omit `secret` to keep the stored value, pass null to clear it, pass a string to replace it. Values are encrypted at rest and never returned by a read; use reveal_credential to read one.
idstringrequiredenvironmentstring | nullhasSecretanyRead-only; derived from whether a value is stored. Pass secret: null to clear the value.labelstringnotesstring | nullsecretstring | nullurlstring | nullusernamestring | nullupdate_project_deploymentprojects:writeUpdate a deployment by id (name, environment, provider, url, notes).
idstringrequiredenvironmentstringnamestringnotesstring | nullproviderstring | nullurlstring | nullupdate_project_stack_itemprojects:writeUpdate a stack item by id (name, category, version, url, notes).
idstringrequiredcategorystringnamestringnotesstring | nullurlstring | nullversionstring | nullReleases 4
create_releaseproducts:writeCreate a release on a product: a shippable increment. `status` is one of draft|planned|in_progress|cancelled (publishing goes through update_release). Optional `gateId` links a launch gate that will AUTHORIZE status=released. An optional `target` ({ at: ISO }) is written into the temporal layer as a point; the release owns no date column.
namestringrequiredproductIdstringrequiredgateIdstring | nullA launch Gate on the SAME product. If set, it AUTHORIZES status=released (ADR-008): publishing is refused until it is PASSED/WAIVED.idempotencyKeystringRetrying with the same key returns the original release (no duplicate).notesstring | nullstatusstringDefaults to draft. Publishing goes through update_release (the gate authorizes it), so `released` is refused here.targetobject | nullOptional target date; written transparently into the temporal layer as a point.versionstring | nullA version label like "1.4.0"; free text.get_releaseproducts:readread-onlyFetch one release by id: its attributes, its target, its linked launch gate and a readiness preview of what would block a publish.
idstringrequiredlist_releasesproducts:readread-onlyList a product's releases, each with its target from the temporal read model. Filter by status. Pass `targetBefore` to page releases whose target falls before a date, target-ordered. Cursor-paginated.
productIdstringrequiredcursorstringOpaque cursor from a prior response nextCursor.limitintegerstatusstringOnly releases with this status.targetBeforestringOnly releases whose target is before this date, target-ordered (via the temporal read model).update_releaseproducts:writeUpdate a release: attributes, a status transition, or archive/restore via `archived`. status="released" is authorized by the linked gate - if that gate is not PASSED/WAIVED the call is refused with code GATE_NOT_PASSED and the unmet conditions, and the released status is NOT saved. Publishing records the actual on the timeline. Changing `target` writes a new current schedule version and REQUIRES a reason; target { at: null } clears it.
idstringrequiredarchivedbooleantrue archives (soft delete, art. 9), false restores.gateIdstring | nullnamestringnotesstring | nullstatusstringA status move; status="released" is authorized by the linked gate (refused with code GATE_NOT_PASSED while the gate is open).targetobject | nullversionstring | nullResources 5
allocate_resourceresources:writeTake the lowest free value (or ask for a specific one with `preferred`, which is refused rather than substituted if it is held). Atomic: two agents asking at once never receive the same value. This RECORDS an agreement - makermap cannot see your machine and has not checked that anything is listening. Refuses with POOL_EXHAUSTED naming what is free, VALUE_ALREADY_HELD naming the holder, or VALUE_NOT_IN_POOL.
poolstringrequiredPool id, or its name (unique within the org).countintegerHow many values to take. They are the lowest free ones, NOT guaranteed consecutive.holderIdstring | nullholderTypestring | nullOne of: product, deployment, datastore, external.idempotencyKeystringlabelstring | nullWhat holds it, in your words - "tidepool api".notesstring | nullorgstringOrganization id or slug. Omit to use the org owning your default workspace.preferredstringAsk for this exact value. Refused if it is held - never silently substituted.get_resource_poolresources:readread-onlyOne pool with its occupancy map: every value, whether it is held, by what, and where that came from - allocated by makermap, imported by a person, or REPORTED by an agent. makermap never scans anything, so a held value is a recorded agreement rather than an observed socket, and values an agent reported are counted separately for exactly that reason.
poolstringrequiredPool id, or its name (unique within the org).includeReleasedbooleanInclude values that were held and have since been released. Default false.orgstringOrganization id or slug. Omit to use the org owning your default workspace.list_resource_poolsresources:readread-onlyThe finite sets this organization allocates from - port ranges on a host today. Each pool reports its capacity, how many values are held and how many are free, with how those numbers were counted. A pool bound to a host counts collisions per machine; an unbound one counts them per organization.
cursorstringhostIdstringOnly pools bound to this host.includeArchivedbooleankindstringOne of: port.limitintegerorgstringOrganization id or slug. Omit to use the org owning your default workspace.record_resource_observationresources:writeReport what you SAW on the machine, with your name on it. Reporting a value as taken when the record says free records it as reported-taken - that is how a pool learns about things makermap never handed out. Reporting one as FREE does not release it: a report is evidence, not authority, and releasing on it would hand the value to the next caller while something nobody told us about is still listening. The disagreement is shown in get_resource_pool for a person to settle.
observedstringrequiredWhat you actually saw on the machine. This is a REPORT; it never overwrites the record.poolstringrequiredPool id, or its name (unique within the org).valuestringrequiredlabelstring | nullWhat is using it, if you could tell.notesstring | nullorgstringOrganization id or slug. Omit to use the org owning your default workspace.release_resourceresources:writeReturn a held value to the pool. The row is stamped released, never deleted - "who held 8081 in March" stays answerable. Releasing something that is not held is refused rather than treated as success.
poolstringrequiredPool id, or its name (unique within the org).valuestringrequiredThe value to give back.notestring | nullWhy it was released. Kept on the row.orgstringOrganization id or slug. Omit to use the org owning your default workspace.Search 1
searchproducts:readread-onlySearch a workspace across products, tasks, decisions, documents, instructions, assets, activity and infrastructure. mode:"keyword" (default) does ILIKE over text and returns { kind, id, title, snippet } rows with a per-kind limit (truncatedKinds names any kind that had more). mode:"semantic" does meaning-based pgvector search over products/decisions/documents, returns similarity-scored results plus a 1-hop graph expansion of connected items (related), reports embedding coverage, and never silently falls back to keyword. The infrastructure kind covers hosts, datastores and databases, is ORG-scoped and needs infra:read - without it that kind appears in `omitted` with the reason rather than returning nothing. Restrict with kinds and/or product (product drops hosts and datastores, which no product owns).
querystringrequiredgraphboolean | stringSemantic mode only: include the 1-hop graph expansion of items connected to the matches. Default true.kindsstring[]Restrict to these kinds. Omit for all. Semantic mode only ranks products/decisions/documents.limitintegerKeyword: max matches per kind. Semantic: max matches overall.modestringkeyword (default when omitted): ILIKE over text. semantic: meaning-based pgvector search over products/decisions/documents with a 1-hop graph expansion of connected items. Semantic reports coverage and never silently falls back to keyword.productstringScope results to one product.workspacestringWorkspace slug or id. Defaults to your default workspace.SetupProfiles 7
apply_setup_profilesetup_profiles:readinstructions:readassets:readAssemble a whole working environment in ONE call, instead of chaining get_instructions, get_asset_collection_version and get_asset: the pinned rule text, the pinned skills/agents/prompts, the recommended MCP servers and the environment notes. Scope is global (default), product (pass `product` - adds the product context package) or repository (pass `repository`). dryRun=true returns the same plan - which items at which versions, plus slug conflicts and anything that could not be resolved - without recording anything; a normal call records SETUP_PROFILE_APPLIED with every pinned version (this is the one tool that writes an activity row while holding only read scopes - use dryRun to avoid it). detail="plan" omits all text. Versions are pinned, so editing a set member later does not change what this returns. NOTE: makermap ASSEMBLES the package - your client applies it. makermap never writes files on your machine and keeps no record of what is installed where.
profilestringrequiredSetup profile id or slug.detailstringplan = ids, versions, conflicts and missing refs only (cheap). full = the applicable package with rule text and asset content.dryRunbooleanResolve the manifest and return the same plan WITHOUT recording SETUP_PROFILE_APPLIED.idempotencyKeystringRetrying a non-dry-run apply with the same key records ONE SETUP_PROFILE_APPLIED, not two.productstringProduct id or exact name. REQUIRED when scope="product".repositorystringRepository identifier, e.g. "github.com/org/repo" or an absolute path. REQUIRED when scope="repository".scopestringOne of: global, product, repository. Omit to use the profile's defaultScope.archive_setup_profilesetup_profiles:writeArchive a setup profile (the terminal operation - there is no hard delete). It leaves the list and can no longer be applied, but keeps its manifest, its history and its slug, so restoring it with archived=false can never collide with a profile created meanwhile.
idstringrequiredarchivedbooleanfalse restores the profile.create_setup_profilesetup_profiles:writeDeclare a working environment: instruction sets and asset collections at a FROZEN version, individual assets at a revision, plus MCP server hints and free-text notes. Omit a version or revision to pin whatever is current right now - the stored manifest is always pinned to concrete numbers, so a later edit to a member never changes what the profile applies. A set or collection with no frozen version is refused; call snapshot_instruction_set or snapshot_asset_collection first. Never put a token or password in the manifest.
manifestobjectrequirednamestringrequiredHuman name; the slug is derived from it.defaultScopestringUsed when apply omits `scope`. One of: global, product, repository. Defaults to global.descriptionstring | nullidempotencyKeystringRetrying with the same key returns the original profile instead of creating a second one.workspacestringWorkspace slug or id. Defaults to your default workspace.get_setup_profilesetup_profiles:readread-onlyRead a single setup profile by id or slug, including its full manifest with the pinned versions. Use apply_setup_profile instead when you want the assembled package rather than the declaration.
profilestringrequiredSetup profile id or slug.list_setup_profile_revisionssetup_profiles:readread-onlyThe edit history of one setup profile: every superseded name, description, scope and manifest, newest first. `headRevision` in the response is the number the CURRENT profile holds - it has no row here until it is superseded.
idstringrequiredcursorstringOpaque cursor from a prior response nextCursor.limitintegerlist_setup_profilessetup_profiles:readread-onlyList the setup profiles in a workspace. A setup profile is a declarative manifest of a working environment - pinned rule sets, pinned skills/agents/prompts and MCP server hints - that apply_setup_profile turns into one package. Archived profiles are hidden unless you pass includeArchived=true.
cursorstringOpaque cursor from a prior response nextCursor.includeArchivedbooleanArchived profiles are hidden by default (archive-first, art. 9).limitintegersearchstringworkspacestringWorkspace slug or id. Defaults to your default workspace.update_setup_profilesetup_profiles:writeEdit a setup profile by id. Provide only the fields to change; `manifest` REPLACES the whole manifest when given. The state being replaced is kept as a revision you can read with list_setup_profile_revisions, and a save that changes nothing writes nothing at all.
idstringrequireddefaultScopestringdescriptionstring | nullmanifestobjectREPLACES the whole manifest when given. Omit to leave it untouched.namestringSites 3
get_site_cardsite:readread-onlyEverything needed to render one product on a site: the public fields plus its `portfolio-card` document, which is the showcase text written for visitors. Anything absent is listed in `missing` and is NEVER substituted - the product's internal `summary`, `vision` and `problem` answer "why are we building this" for the team, and putting one on a public page is plausible and wrong. Refuses for a product that is not currently placed on this site. Internal judgements (health, stage, status) are deliberately not returned.
productIdstringrequiredThe placed product whose card you are rendering.sitestringrequiredThe site: the id of the product that IS the site, or its name.get_site_updatessite:readread-onlyWhat has changed on a site since your cursor, oldest-first: things placed, moved or TAKEN OFF, and edits to what is already there. A `removed` update means the page must come down - a site that only ever hears about additions keeps showing what its owner has withdrawn. Reports the KIND of each change and the placement it affects, never the values: read this, then re-read what you care about through its own tool. Omit `since` on the first call to establish a cursor. Delivery is at-least-once, so DEDUPE by id.
sitestringrequiredThe site: the id of the product that IS the site, or its name.limitintegersincestringAn opaque cursor from a prior response. Returns only what changed after it, oldest-first. Omit for the first call. Do not construct or parse it.list_site_entriessite:readread-onlyEverything an owner has marked as shown on a site, by section: portfolio, blog, changelog, docs, legal, contacts. The site is an ordinary product, named by id or name. Use this to rebuild a site from scratch; `available: false` marks a placement whose target no longer exists. Placing and removing are human acts and have no tool - makermap records what a person chose to show and never decides it.
sitestringrequiredThe site: the id of the product that IS the site, or its name.includeRemovedbooleanInclude placements that have since been taken off the site. Default false.sectionstringOnly this section. One of: portfolio, blog, changelog, docs, legal, contacts.Spend 2
get_spend_capagents:readread-onlyRead one agent's spend cap and its current-window usage: the cap in cents, whether enforcement is enabled, and how much of the window is already spent (spentCents/remainingCents) with the reset time. Owner-scoped; an agent caller may only read its own cap. Returns NOT_FOUND if the agent has no cap set.
agentIdstringrequiredThe agent whose cap and current-window spend to read.set_spend_capagents:writeSet or update an agent's spend cap: the most it may spend on WRITE-class tools inside a window (v1: per calendar day, UTC). Spend is the flat per-write estimate the gateway records (costSource 'flat-v1'); a cap of N cents allows N write calls per day at the default unit cost. Once the cap is reached the MCP transport refuses further write-class calls with a structural SPEND_CAP_EXCEEDED error naming the cap and remainder; read-only tools stay available. Owner-scoped (you must own the agent), so there is no workspace argument. Setting a cap is a human act - an agent principal cannot call this.
agentIdstringrequiredThe agent this cap governs. You must own the agent.capCentsintegerrequiredThe ceiling in cents. A write-class call is refused once the window spend would exceed it.enabledbooleanfalse disables enforcement without deleting the cap (spend still accrues, nothing is refused). Default true.windowstringAccounting window. v1 supports DAY only (calendar day, UTC).SystemLibrary 4
fork_system_skillassets:writeCopy a shipped skill (or rule) into an ordinary asset your workspace owns, marked with what it was forked from. FROM THAT MOMENT IT STOPS RECEIVING UPDATES - the copy is yours and makermap will never touch it again. If you only want to ADD something, use save_skill_overlay instead and keep the updates.
keystringrequiredtypestringrequirednamestringA name for your copy. Defaults to the original plus " (fork)".workspacestringWorkspace slug or id. Defaults to your default workspace.get_system_skillassets:readread-onlyOne shipped skill or rule, composed with your workspace's addition. `body` is the whole text to work from; `parts` says which half came from makermap and which from your workspace.
keystringrequiredThe registry key, e.g. "makermap-basics".typestringrequiredworkspacestringWorkspace slug or id. Defaults to your default workspace.list_system_libraryassets:readread-onlyThe skills and rules makermap ships. They are code, not rows: they update with the product and nobody owns them. Each carries your workspace's addition (if any) composed onto it, with `origin` marking whose text is whose. Extend one with save_skill_overlay; take a private copy with fork_system_skill.
typestringNarrow to skills or to instructions. Omit for both.workspacestringWorkspace slug or id. Defaults to your default workspace.save_skill_overlayassets:writeAdd your workspace's own text to a shipped skill or rule, or switch it off with enabled:false. Your addition is composed AFTER the shipped text and stays marked as yours - the shipped half keeps updating with the product. There is no way to edit the shipped half and no way to delete it: it is code, and a deleted one would return with the next deploy.
keystringrequiredThe system entry this extends.typestringrequiredbodystring | nullYour addition, appended after the shipped text and marked as yours. Null clears it.enabledbooleanFalse removes the system entry from this workspace. It is never deleted - it is code (art. 9).workspacestringWorkspace slug or id. Defaults to your default workspace.Tasks 16
create_board_columntasks:writeAdd a kanban column to a project board (appended to the right). Optionally set its semantic role (BACKLOG|TODO|IN_PROGRESS|REVIEW|DONE|CUSTOM) so agents and telemetry can reason about it.
namestringrequiredprojectIdstringrequiredcolorstring | nullrolestringcreate_epictasks:writeCreate an epic in a product board. An epic is a body of work, and it normally advances an INITIATIVE - pass initiativeId, or create_initiative first if the bet does not exist yet. Leave it out only for work that advances no bet.
projectIdstringrequiredtitlestringrequiredcolorstring | nulldescriptionstring | nulleffortstring | nullThe default effort for tasks in this epic. A task with no effort of its own inherits it.initiativeIdstring | nullThe initiative (bet) this epic advances - must be on the same product. Omit only when the epic advances no bet: a repair or a chore. If several epics serve one outcome, create_initiative first and pass its id here.create_tasktasks:writeCreate a task (card) in a board column, optionally under an epic swimlane.
columnIdstringrequiredprojectIdstringrequiredtitlestringrequireddescriptionstring | nulleffortstring | nullHow much thinking this is worth, in makermap's own scale: trivial | routine | involved | intricate. A RECORD, not a control - nothing here changes how your model reasons. Null clears it; empty is a valid state.epicIdstring | nullprioritystring | nullcreate_taskstasks:writeCreate a batch of tasks in one product board in a single call, with per-item results ({ ok, id?, error? }) - decompose a plan without 30 separate round-trips. Pass idempotencyKey so a retry returns the original result instead of duplicating.
projectIdstringrequiredtasksobject[]requiredOne object per task. Each is placed in its columnId (and optional epic).idempotencyKeystringRetrying with the same key returns the original result (no duplicates).delete_board_columntasks:writedestructiveagents deniedDelete a column by id. Its tasks move to another column (moveTasksToColumnId, else the nearest one).
idstringrequiredmoveTasksToColumnIdstring | nulldelete_epictasks:writedestructiveagents deniedDelete an epic by id. Its tasks stay on the board and fall to the "No epic" swimlane.
idstringrequireddelete_tasktasks:writedestructiveagents deniedDelete a task by id.
idstringrequiredget_boardtasks:readread-onlyFetch a project's board: initiatives, the epics that advance them, tasks and columns, plus `unattachedEpics` - how many epics belong to no initiative. An epic sits UNDER an initiative; unattached is a legal state, not an error.
projectIdstringrequiredget_tasktasks:readread-onlyFetch one task by id, with its column (and role), its epic, its recorded effort and - on plans that include the Intelligence Layer - a computed effort recommendation with the reasons behind it.
idstringrequiredlist_taskstasks:readread-onlyList tasks with combinable filters (productId, columnId, epicId, priority, search). Without productId it spans the whole workspace ("what is URGENT anywhere"). Cursor-paginated: pass nextCursor from a prior response as cursor. Cheaper than get_board for finding specific tasks. Each task carries its recorded effort and, where the plan includes the Intelligence Layer, a computed recommendation.
columnIdstringcursorstringOpaque cursor from a prior response nextCursor.dueBeforestringOnly tasks with a deadline before this ISO-8601 instant, ordered soonest-first.epicIdstringlimitintegerprioritystringproductIdstringsearchstringworkspacestringWorkspace slug or id. Defaults to your default workspace.move_tasktasks:writeMove a task into a column x epic cell, before beforeTaskId (or to the end). epicId null = no epic.
columnIdstringrequiredidstringrequiredbeforeTaskIdstring | nullepicIdstring | nullreorder_board_columnstasks:writeSet the left-to-right order of a board columns. orderedIds must list exactly the current columns.
orderedIdsstring[]requiredprojectIdstringrequiredreorder_epicstasks:writeSet the top-to-bottom order of swimlanes. orderedIds must list exactly the current epics.
orderedIdsstring[]requiredprojectIdstringrequiredupdate_board_columntasks:writeRename, recolor, or set the semantic role (BACKLOG|TODO|IN_PROGRESS|REVIEW|DONE|CUSTOM) of a board column by id.
idstringrequiredcolorstring | nullnamestringrolestringupdate_epictasks:writeUpdate an epic by id (title, description, color). Pass initiativeId to attach the epic to an initiative on the same product, or null to detach it.
idstringrequiredcolorstring | nulldescriptionstring | nulleffortstring | nullThe default effort for tasks in this epic. A task with no effort of its own inherits it.initiativeIdstring | nullThe initiative this epic advances (same product), or null to detach.titlestringupdate_tasktasks:writeUpdate a task by id (title, description, priority; or reassign column/epic).
idstringrequiredcolumnIdstringdescriptionstring | nulleffortstring | nullHow much thinking this is worth, in makermap's own scale: trivial | routine | involved | intricate. A RECORD, not a control - nothing here changes how your model reasons. Null clears it; empty is a valid state.epicIdstring | nullexpectedVersionintegerThe `version` you last read. When given, the write is refused with ENTITY_CHANGED if the record moved since - instead of overwriting a change somebody else made. Omit it for a one-shot write that has not read the record.prioritystring | nulltitlestringTelemetry 4
get_agent_runagents:readread-onlyOne run with its tool invocations - each call's tool name, when, how long, whether it succeeded or the structural error code, and its args with credential-shaped values redacted. An agent caller may only get its own runs.
idstringrequiredget_telemetry_policyagents:readread-onlyRead a workspace's gateway-telemetry policy: how long a recorded tool payload is kept (retentionDays), whether payloads are kept at all (capturePayload), and any extra argument key names masked on top of the built-in credential-shaped set. `isDefault` says whether a row exists or the built-in defaults apply. `effective` is what actually governs YOUR telemetry - the strictest policy across every workspace you belong to, because a tool invocation is owner-scoped and belongs to no single workspace.
workspacestringWorkspace slug or id. Defaults to your default workspace.list_agent_runsagents:readread-onlyGateway telemetry: the runs your agents made (tool invocations grouped by X-Run-Id), newest activity first. This is the SERVER's record of what ran, not the agent's self-report. An agent caller sees only its own runs. Owner-scoped, so there is no workspace argument and the response echoes workspace: null. Paginated by opaque cursor.
agentIdstringFilter to one agent. An AGENT caller is always scoped to its own runs whatever it passes here.cursorstringOpaque cursor from a prior response nextCursor.limitintegerset_telemetry_policyagents:writeSet a workspace's gateway-telemetry redaction and retention policy. Fields are patched: an omitted field keeps its current value. retentionDays bounds how long a recorded payload is kept before it is dropped to metadata only; capturePayload=false records the call but keeps no payload at all; redactKeys names extra argument keys to mask (matched case- and punctuation-insensitively). Requires ADMIN in the workspace, because it governs how long the record of what every agent did survives. Setting it is a human act - an agent principal cannot call this.
capturePayloadbooleanfalse records the call (tool, outcome, duration, cost) but keeps NO payload at all. Default true.redactKeysstring[]Extra argument key names to mask, on top of the built-in credential-shaped set. Matched case- and punctuation-insensitively, so "Customer-Email" also masks customerEmail.retentionDaysintegerHow many days a recorded tool payload is kept before it is dropped to metadata only. Default 30.workspacestringWorkspace slug or id. Defaults to your default workspace.Temporal 8
add_temporal_dependencyproducts:writeOrder two timeline items: the predecessor (the blocker) must relate to the successor (the blocked) by `type` (finish_start by default), with an optional `lagDays`. Rejects a self-link, a duplicate edge, and any edge that would create a cycle (the error names the chain). This stores the ordering; it does not recompute dates along the chain.
predecessorItemIdstringrequiredThe "blocker" item - it must relate first.successorItemIdstringrequiredThe "blocked" item - it depends on the predecessor.lagDaysinteger | nullOptional lag/lead in days.typestringcreate_timeline_itemproducts:writeCreate a free block on a product timeline: a dated (or undated) item with no entity behind it - "Trademark registration, Aug-Oct". With dates it gets a baseline and a current plan; without, it is "unscheduled" and can be scheduled later with reschedule_item. This is the quick-capture substrate: a block can be formalized into a real entity later without losing its origin.
projectIdstringrequiredtitlestringrequiredWhat this block is - it has no entity behind it.endsAtstring | nullOptional end. Omit for a point item (endsAt = startsAt).granularitystringidempotencyKeystringRetrying with the same key returns the original item (no duplicate).isPointbooleanA moment rather than a span (endsAt is ignored).kindstringplan | note | event | campaign | freeze.startsAtstring | nullOptional start. With no dates the block is "unscheduled".get_timeline_itemproducts:readread-onlyFetch one timeline item by id: its subject (if it mirrors a task/epic/milestone/initiative), its `plan` (current), `baseline` (original), forecasts, actuals, and its `dependencies` (the `blocks` / `blockedBy` ordering edges on both sides). Carries `formalizedToType`/`formalizedToId` - set when the item began as a free capture and was formalized into that entity (convert_entity), which is how a converted item's origin is read.
idstringrequiredlist_timeline_itemsproducts:readread-onlyList a product's (or a workspace's) timeline: subject items (a task with a deadline, an epic with a timeframe) and free blocks (a dated note with no entity behind it), each carrying its `plan` (current), `baseline` (original), `actuals`, and its `dependencies` - the ordering edges on both sides (`blocks` = what it blocks, `blockedBy` = what blocks it). Filter by `kinds`, by a `from`/`to` window over the current plan, and by `scheduled` (true = only planned items, false = only the unscheduled). Cursor-paginated.
cursorstringOpaque cursor from a prior response nextCursor.fromstringOnly items whose current plan overlaps at/after this instant.kindsstring[]Only items of these kinds (e.g. ["plan","task"]). Omit for all.limitintegerproductIdstringscheduledboolean | stringtrue = only items that have a current plan; false = only unscheduled items (no current plan). Omit for both. Independent of from/to (which additionally window the plan dates).tostringOnly items whose current plan starts at/before this instant.workspacestringWorkspace slug or id. Defaults to your default workspace.record_actualproducts:writeRecord an OBSERVED span of real work on a timeline item (a fact, not a plan). An item may have several - work is interrupted and resumed. `endedAt: null` means still ongoing. Actuals for a task completing (moving into a Done column) are recorded automatically from the event log; use this for anything the board does not capture.
idstringrequiredconfidenceinteger | null0-100, how sure the span is.endedAtstring | nullWhen it ended. Null = still ongoing.granularitystringsourcestringmanual (default) | integration.startedAtstring | nullWhen real work started.remove_temporal_dependencyproducts:writedestructiveagents deniedRemove a temporal dependency edge by id.
idstringrequiredreschedule_itemproducts:writeMove a timeline item's current plan. Only the dates you pass change: omit one to keep it, pass null to clear it - so you can shift a span's start without disturbing its end. The immutable baseline is never touched, so drift stays measurable; if the item was created unscheduled, this first plan also lays down that baseline. A `reason` is REQUIRED when the item mirrors a subject (a task, an epic): a subject date never moves silently. Optionally cite the `decisionId` that sanctioned the shift (validated to your workspace). Works for any item - a task mirror, an epic timeframe, or a free block.
idstringrequireddecisionIdstring | nullThe Decision that sanctioned this shift. Validated to your workspace.endsAtstring | nullNew end of the current plan. Omit to keep the existing end; pass null to clear it (making it a point).granularitystringreasonstringWhy the plan moved. REQUIRED when the item mirrors a subject (a task, epic, ...): a subject date never moves without a recorded reason.startsAtstring | nullNew start of the current plan. Omit to keep the existing start; pass null to clear it.set_task_deadlinetasks:writeSet (or move, or clear) a task's deadline. The first call records the deadline as both an immutable baseline and the current plan; later calls move only the current plan, so drift against the baseline stays measurable, and pass `reason` to say why it moved. Pass `dueAt: null` to remove the deadline entirely. The deadline is not a column on the task - it lives in the canonical temporal layer - but you set and read it as if it were: get_task returns `dueAt`, and list_tasks accepts `dueBefore`.
dueAtstring | nullrequiredThe deadline as an ISO-8601 instant, or null to clear it. The first time you set one, it becomes both the immutable baseline and the current plan; later calls move only the current plan (the baseline is kept so drift stays measurable). Passing null deletes the deadline entirely.idstringrequiredgranularitystringHow precise the date is: "day" (default) or "minute".reasonstringWhy the deadline moved. Recorded in history, not on the task.Users 2
update_settingsprofile:writeSet the signed-in account's own preferences: theme (SYSTEM | LIGHT | DARK), locale and timezone. Fields are patched - an omitted field keeps its current value, and none of the three can be cleared back to unset (they are non-null columns defaulting to SYSTEM / "en" / "UTC"). theme selects what the web app renders, with SYSTEM following the operating system. locale selects the interface LANGUAGE of the web app and is restricted to the languages makermap ships (en, ru, pt) - it does NOT translate anything this API returns: tool descriptions, refusal messages and recorded prose are English on the wire regardless. timezone is STORED but nothing reads it yet: every timestamp is returned in UTC and rendered in the viewer's own device timezone, so setting it here does not change how a single date reads today. Owner-scoped - it writes YOUR account, so there is no userId argument and no workspace argument. Returns the same account view whoami does, with the new settings. Changing an account's own preferences is a human act - an agent principal cannot call this.
localestringThe interface language. One of: en, ru, pt.themestringSYSTEM follows the operating system setting; LIGHT and DARK pin it.timezonestringIANA zone name, e.g. "Europe/Berlin".whoamiprofile:readread-onlyReturn the authenticated account plus the principal you are acting as. If `principal.agent` is set you are an AGENT principal: your records are signed with that agent name, destructive tools are not published to you, and any non-empty allow list is the exact set of workspaces/products your token can reach.
Takes no arguments.
Workspaces 10
accept_invitationorgs:writeagents deniedAccept an organization invitation using its token (from the accept link). You can only accept an invitation sent to your own email.
tokenstringrequiredInvitation token from the accept linkcreate_organizationorgs:writeagents deniedCreate a new organization (you become its owner). It starts with a "General" workspace.
namestringrequiredcreate_workspaceorgs:writeagents deniedCreate a workspace inside an organization (by slug or id). Requires the admin role.
namestringrequiredorgstringrequiredOrganization slug or idinvite_memberorgs:writeagents deniedInvite someone to an organization by email at a role (admin/member/viewer, default member). Returns an accept link; an email is also sent if delivery is configured. Requires the admin role.
emailstringrequiredorgstringrequiredOrganization slug or idrolestringlist_invitationsorgs:readread-onlyList the invitations of an organization (by slug or id) with status and role. Requires the admin role.
orgstringrequiredOrganization slug or idlist_org_membersorgs:readread-onlyList the members of an organization (by slug or id) with their roles.
orgstringrequiredOrganization slug or idlist_organizationsorgs:readread-onlyList the organizations you belong to, with your role and member/workspace counts.
Takes no arguments.
list_workspacesorgs:readread-onlyList the workspaces you can access. Optionally restrict to one organization by its slug or id. Entities (projects, instructions, assets) live in a workspace - use a workspace slug as the `workspace` argument on other tools.
orgstringOrganization slug or idrevoke_invitationorgs:writeagents deniedRevoke the pending invitation for an email in an organization (by slug or id). Requires the admin role.
emailstringrequiredorgstringrequiredOrganization slug or idset_default_workspaceorgs:writeagents deniedSet the workspace that MCP tools target when no `workspace` argument is given (by slug or id).
workspacestringrequiredWorkspace slug or id Argument types are flattened from each tool's JSON Schema. For the schema itself, ask a connected client for tools/list - the live answer, which cannot be behind this page. Scopes · Refusal codes