Skip to main content
Glama
Mipiti
by Mipiti

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
MIPITI_API_KEYYesYour Mipiti API key
MIPITI_API_URLNoAPI base URLhttps://api.mipiti.io
SERVER_VERSIONYesIdentifier for the running server's MCP surface. For local runs, any sentinel string is fine (e.g., 'local').

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": true
}
logging
{}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}

Tools

Functions exposed to the LLM to take actions

NameDescription
generate_threat_modelA

Generate a complete threat model from a feature description.

Analyzes the feature using the Security Properties (Confidentiality, Integrity, Availability, Usage) methodology with capability-defined attackers. Produces trust boundaries, asset inventory, attacker inventory, control objective matrix, and assumptions.

Runs a multi-step AI pipeline. Progress is reported automatically.

Similar-model short-circuit: if the backend finds an existing model in the workspace whose feature description substantially overlaps with the new one, it does NOT generate a duplicate. This tool returns {"similar_models": [{"id", "title", "reason"}, ...], "suggestion": "..."} with the candidate IDs instead. The agent should then either:

  • Call refine_threat_model on one of the candidates to extend the existing model (usually the right answer — avoids duplicate modeling of the same system and preserves control/assertion history).

  • Retry this tool with force=True to bypass the check and create a genuinely new model anyway (e.g., when the similarity is superficial and the operator confirmed the new model is distinct).

The request names its purpose, so the platform always generates: it never reads the description as a question or a change to another model.

refine_threat_modelA

Refine an existing threat model based on an instruction.

Updates the model's assets, attackers, trust boundaries, and control objectives based on the instruction. Creates a new version. Progress is reported automatically.

Refine CANNOT silently replace an entity's identity under a stable ID or silently drop an entity. Behavior:

  • Preserved entities where the LLM proposed an identity- bearing rewrite (name / description / security_properties on assets; capability / archetype / position on attackers) run through a semantic-preservation guard. Rewrites classified as replace or ambiguous (or unavailable if the gate LLM is down) have their identity fields REVERTED to the pre-refine values. Each rejection shows up as an entry in the semantic_rejections array in this tool's return value — surface these to the operator.

  • Entities the LLM drops from the refined output are re- appended to the model unchanged. The only sanctioned removal path is remove_entity (entity_type="asset") / remove_entity (entity_type="attacker") (soft-delete).

  • CO IDs are stable across refinements; pairs (asset, attacker) that disappear come back as tombstones with removed=True (not renumbered). Controls that only mapped to tombstoned COs become orphaned at read time.

query_threat_modelA

Ask a natural-language question about an existing threat model.

Read-only; no side effects (no new version, no mutation). Uses AI to answer questions grounded in the model's assets, attackers, control objectives, assumptions, and current security posture, returning {model_id, answer} where answer is prose.

Use this for interpretation or summary questions ("what are the biggest gaps?", "which attackers target the token store?"). Do NOT use it to change the model — use refine_threat_model for that — and prefer get_threat_model / assess_model when you need structured data (entity lists, coverage counts) rather than a written answer.

list_threat_modelsA

List saved threat models in the current workspace.

Read-only; no side effects. Returns {items: [{id, title, version, created_at, ...}], count}. Use this to discover model IDs to pass to other tools, or for a portfolio overview.

update_threat_modelA

Change a threat model's metadata: its name, its parent, where its description came from. Mutating; pass only what changes.

  • name (1-120 chars) renames it; no new version. Titles are unique within a workspace, case-insensitive (409 on a clash).

  • parent_id wires it under a parent on the recursive composition tree, so it inherits the parent's topology and objectives; clear_parent=True makes it a tree root. Cycles (409) and chains past the platform's maximum depth (400) are refused. No new version.

  • provenance_kind (code, ticket, document, manual, mixed) records where the description came from, with the other provenance_* values. code with provenance_commit_sha means the code is authoritative and the model follows it (reconcile_model measures it against the code); any other kind means the description is intent and the code is measured against it. Bumps the model version.

Changes apply in that order. Returns {model_id, name?, parent?, provenance?}, one entry per change applied. A failure raises and names the changes already applied.

declare_foundationA

Mark a model as a shared foundation that advertises providable controls.

Mutating: records this model as a foundation and stores its advertised controls; other models can then delegate to them (see attach_foundation). A foundation is a shared service (auth, logging, a shared datastore) whose controls other models can rely on.

Each entry in provides advertises one of THIS model's controls as providable: {"control_id": "CTRL-07", "capability_label": "Validates session tokens", "description": "..."}. A capability always advertises a control (a proven mechanism), never an objective.

list_relianceA

List a model's cross-model dependency edges, in both directions.

Read-only; no side effects. Returns {model_id, as_consumer: [...], as_provider: [...]}. Consumer edges are this model's declared delegations / reliances on other models' controls; provider edges are other models relying on this one (its blast radius if its controls change).

Use this to inspect existing dependencies before creating or deleting edges (manage_reliance / attach_foundation), or to understand what breaks if this model's controls change.

manage_relianceA

Create, confirm or delete one cross-model reliance edge. Mutating.

action="create" declares that model_id relies on a provider control (the target is ALWAYS a control, so credit ends at a proven mechanism). mode is delegated (this model does not implement the objective; pass source_objective_id) or relied_upon (this model's own control depends on the provider's; pass source_control_id). The provider must be in the same workspace. The edge enters draft, runs LLM semantic validation, and carries no credit until confirmed. Returns the edge.

action="confirm" promotes the draft edge_id to active, the credit-soundness gate: refused unless validation returned valid; a partial result or a mode mismatch is never silently credited. Returns the edge.

action="delete" permanently removes edge_id, withdrawing any credit the consumer derived from it (its coverage can move); neither model's controls change. Returns {deleted: true, edge_id}.

list_reliance shows a model's edges and their ids.

attach_foundationA

Delegate this model's objectives to a foundation's controls, in bulk.

Without selections it is read-only: it returns candidate (objective ↔ provider control) pairs with a match score, and nothing is created or credited. Show them to the operator.

With selections — a list of {"source_objective_id": ..., "provider_control_id": ...}, typically the confirmed subset of those candidates — it is mutating: each becomes a delegated draft edge that runs LLM validation and carries no credit until confirmed with manage_reliance(action="confirm"). Returns {created, failed}.

delete_threat_modelA

Delete a threat model and all associated data. Destructive and permanent — cannot be undone.

Mutating: removes the model along with every version, its controls, assertions, findings, attestations, and tag/reliance memberships. Reliance edges from other models that pointed at this one are invalidated, which can move those consumers' posture.

Confirm intent before calling. To keep a copy first, use export_report (scope="model", format="archive") (a self-contained, re-importable JSON archive). Returns {deleted: True, model_id}.

get_threat_modelA

Get a specific threat model by ID.

Returns the full threat model including trust boundaries, assets, attackers, control objectives, and assumptions.

Important for agents reading model state:

  • Assets and attackers may carry deleted: true (soft-deleted). Exclude these when showing "what's in the model now"; include them only when discussing history or offering restore. Restore an entity via restore_entity (entity_type="asset") / restore_entity (entity_type="attacker").

  • Control objectives may carry removed: true (tombstone — the (asset, attacker) pair was removed in a later version). Exclude these from coverage math and LLM prompts; they exist to keep CO IDs stable so controls referencing them can be detected as "orphaned" rather than silently rebinding.

import_threat_model_archiveA

Import a JSON audit archive (from export_report (scope="model", format="archive")) into a target workspace.

Mutating: creates a NEW threat model in the target workspace. Requires write access to that workspace. A fresh model_id is assigned on every import, so the same envelope can be imported any number of times without collisions; title collisions in the target workspace auto-suffix (imported YYYY-MM-DD). Non-destructive — never overwrites or touches an existing model.

Use to move or clone a model between workspaces or across instances; the envelope round-trips through export_report (scope="model", format="archive") first.

The archive carries the model's current state, and the import creates it as version 1 of a new model: its controls, live assertions, decisions in force and open findings. Earlier versions, activity and chat are not carried.

The restored model arrives UNVERIFIED. The tier verdicts on its assertions, the attested flag on a verification result, and the facts a verification run reported are the origin's record of what it claimed — kept with the model as that record, and not credited here: a verdict belongs to the run that produced it and the judge that decided it, and this workspace has neither. Verification is earned here by running it against code this workspace can reach, so plan for a restored model to read unverified until it has. The same holds for the judgements of its mitigation groups: the import queues none, and its objectives read awaiting judgement until someone asks for them.

get_control_generation_statusA

Read a model's control build: the one proposed, and the last one started. Read-only.

A build runs only when someone starts it; a write that owes controls PROPOSES one. proposal (or null) carries mode, objective_count, estimated_credits and the model_version / set_revision that start_control_build must name. Poll until terminal; hint names the next action.

status: queued | generating | deferred | pausing | paused | blocked | complete | failed | skipped | discarded | none. deferred waits for the daily budget reset. pausing is stopping; paused keeps its staged work until resume_control_generation (or discard_control_build). blocked carries code (dependency_unavailable or analysis_incomplete), message and retry_after_seconds: relay the message and retry with resume_control_generation, never regenerate_controls, which redoes and re-bills the work.

While running: ready_cos / target_cos count progress, never coverage; stage names the stage; elapsed_seconds is the time since the last progress (large means it may be stuck). selfheal_activity is a SAMPLE of what a strengthening round works on: read refining_total / authoring_total / set_aside_total for the counts; set_aside objectives wait for a person and are NOT a failure.

Once complete: covered_cos of judged_cos objectives would be mitigated by their controls; awaiting_judgement_cos have no answer yet. diagnosis counts covered, uncovered and undecided (what strengthen_controls works on), judging (queued: wait), not_judged (none queued: judge_objectives) and awaiting_assumption (in the review queue). strengthening says whether that pass has run. analysis_pending means the figures may still move; duration_seconds is the runtime.

pause_control_generationA

Pause a model's background control generation. Mutating.

Use when the user asks to stop a build — for example one started by mistake — or before deleting a model whose controls are still being built. A running build stops at its next step (status pausing, then paused); a queued or waiting one is paused at once. Everything already done is kept in the build's staging copy; nothing is published, nothing new is started or billed, and nothing resumes it except resume_control_generation. A paused build still holds the model: to drop it instead, call discard_control_build once it shows paused. Pausing is idempotent.

Returns one of:

  • {paused: true, model_id, status, status_detail} — status is pausing (still stopping) or paused.

  • {paused: false, http_status: 409, code: "not_running", status} — there is no generation to pause; read status.

resume_control_generationA

Resume control generation that was paused, or retry one that stopped before finishing. Mutating.

Use when get_control_generation_status returns status: "paused" (someone paused it) or status: "blocked" (blocked.code dependency_unavailable or analysis_incomplete). A paused run resumes at once. For a blocked one the platform checks the services it depends on first, so a retry while one is still down costs nothing and changes nothing.

Returns one of:

  • {resumed: true, status: "queued", status_detail} — the run resumes where it stopped (only the unfinished work, billed to the original generation). Poll get_control_generation_status until complete.

  • {resumed: false, http_status: 409, code: "pause_in_progress"} — the run is still stopping after a pause; resume once it shows paused.

  • {resumed: false, http_status: 503, code: "dependency_unavailable", message, retry_after_seconds, ...} — still unavailable; relay the message and try again after retry_after_seconds.

  • {resumed: false, http_status: 409, code: "retry_too_soon", retry_after_seconds, ...} — a retry was just tried; wait.

  • {resumed: false, http_status: 409, code: "not_blocked", status} — nothing is paused; read status.

strengthen_controlsA

Strengthen a model's controls: work on the objectives whose mitigation groups the background judge found do not cover them (the uncovered and undecided of get_control_generation_status's diagnosis). Mutating only with confirm_estimate=True; consumes credits then. not_judged objectives have nothing to strengthen from: judge them first with judge_objectives.

  1. Call with confirm_estimate=False (the default): nothing starts or is charged; the answer carries diagnosis, scope, estimate (credits, per_objective, basis) and the model_version / set_revision the model stands at. Show the user the estimate.

  2. Once they agree, call with confirm_estimate=True and those two values (their review of the model as it stood). A background run starts (started: true); poll get_control_generation_status. It holds the model like any build: pausable, resumable, discardable.

A gap only the environment can close (hosting, a third party) is never answered with a control: an accepted assumption stating it is bound into the group; otherwise an assumption proposal waits in the review queue (get_review_queue / decide_proposal) and the objective counts as awaiting_assumption. A rejected one is not proposed again.

Refusals come back as data, {started: false, http_status, code}: 409 review_stale (the model or its controls changed since the estimate: confirm again with the values returned), 409 generation_active (a build holds the model), 402 (the balance cannot cover the estimate).

judge_objectivesA

Have judged every objective whose mitigation group has no judgement for its current controls and none queued (the diagnosis's not_judged, or objectives reading awaiting_judgement). Mutating only with confirm_estimate=True; may consume credits then. Adding or implementing controls does not move such an objective; a judgement does. judging objectives are already queued: wait for them.

  1. Call with confirm_estimate=False (the default): nothing is queued or charged; the answer carries diagnosis, scope, ungrouped and estimate (credits, objectives, computed_at, rate_version). Show the user the estimate.

  2. Once they agree, call with confirm_estimate=True: each objective in scope is queued (confirmed: true, queued), metered at actuals as it runs, and status_detail is the fresh status.

ungrouped objectives have no mitigation group and are never judged: group their controls with set_mitigation_groups first.

A judgement is not a repair: it can come back insufficient or undecided, which counts the objective as uncovered or undecided, work for strengthen_controls. judge_objective does the same for one.

Refusals come back as data, {confirmed: false, queued: 0, http_status, code, message}: 409 control_generation_in_progress (poll get_control_generation_status), 402 insufficient_credits / quota_exceeded (with estimated_credits), 503 (judging unavailable). An unknown id in co_ids is a 400 error.

regenerate_controlsA

Propose a regeneration of the model's controls. Starts nothing.

A regeneration re-authors controls from the current COs; its publish creates the next model version. Controls whose descriptions survive unchanged KEEP their implementation status, evidence, notes, assertions, and Jira / compliance mappings. Controls whose descriptions change or disappear are soft-deleted (still queryable via get_controls(include_deleted=True)). When co_ids is given, only those COs' controls are regenerated — all other controls are left as-is.

This tool records the regeneration as the model's PROPOSED build and returns at once with status: "proposed" and proposal (mode, objective_ids, objective_count, estimated_credits, and the model_version and set_revision a start must name). A proposal merges with any already proposed for the model, the broader one winning. Show the user what it would build and cost; once they agree, call start_control_build with those values and confirm_estimate=True. A build someone started holds the model, so this is refused (409 generation_active) until it finishes, or is resumed and finishes, or is discarded.

To rebuild everything, omit co_ids. To fix only stale/orphaned CO mappings without re-authoring control text, prefer remap_control (mechanical, no LLM).

start_control_buildA

Start the model's proposed control build. Mutating only with confirm_estimate=True; consumes credits then.

A model's controls are built only by a build someone starts. Generating or refining the model, editing an entity and regenerate_controls each PROPOSE one; get_control_generation_status shows it as proposal.

  1. Call with confirm_estimate=False (the default). Nothing starts and nothing is charged; the answer is {started: false, proposal, message}, the proposal carrying a fresh estimated_credits and the model_version and set_revision the model stands at. Show the user what it would build and cost.

  2. Call again with confirm_estimate=True and those two values once they have reviewed the model. The build starts ({started: true, job_id, model_version, status: "queued", status_detail}) and holds the model until it publishes, fails or is discarded: other writers of the model's controls are refused meanwhile, and reads show the last published controls. Poll get_control_generation_status until terminal; the publish is one step, after which get_controls shows the result.

Refusals come back as data:

  • {started: false, http_status: 404, code: "no_proposal"} — nothing is proposed for the model.

  • {started: false, http_status: 409, code: "review_stale", proposal, model_version, set_revision, estimated_credits} — the model or its controls changed since the values were read (or none were named). Review again and start with the values returned.

  • {started: false, http_status: 409, code: "generation_active", status} — a build already holds the model.

  • {started: false, http_status: 402, ...} — the balance this workspace bills to cannot cover the estimate.

discard_control_buildA

Discard a model's held control build. Mutating.

A build that is queued, deferred, paused or blocked holds the model without running. Discarding it drops everything it staged — the model keeps its published controls exactly as they were — releases the model, and proposes the build again so it can be started later. The credits it already consumed are not returned. A running build must be paused first (pause_control_generation, then wait for paused).

Returns one of:

  • {discarded: true, model_id, status: "discarded", proposal, status_detail}.

  • {discarded: false, http_status: 409, code: "pause_first", status} — the build is running; pause it first.

  • {discarded: false, http_status: 409, code: "not_held", status} — no build holds the model.

list_control_revisionsA

List every change to a model version's set of controls. Read-only.

Each write to a version's published controls — a build's publish, an import, an edit, a deletion, an undo — is a set revision with its author. Returns {model_id, model_version, latest_version, discarded, revisions, undo_target}; each revision carries revision, job_id (the build that wrote it, if any), started_by, started_at, controls (the ids it touched), undo_of (the revision it undid, for an undo) and undone_by / undone_at. undo_target is the revision undo_model_change(target="controls") would undo (null when none, and for any version but the latest). discarded is true for a version a revert replaced.

undo_model_changeA

Undo the latest change to a model's controls, or revert its latest version. Mutating.

target="controls" restores exactly what the latest set revision of the latest version replaced (list_control_revisions' undo_target). Changes are undone latest first, one per call; the undo is itself recorded, and the next undo goes to the change before it. There is no redo. Verdicts the undo returns to are served again rather than re-judged. Answers {applied: true, model_id, model_version, undone, revision, controls}: undone is the revision undone, revision the one the undo recorded, controls the ids it restored.

target="version" creates a new version copying the latest earlier version not already discarded — the model, its controls and their objective metadata — and marks the replaced version discarded. Version numbers are never reused, and the discarded version stays readable in the history. Findings on controls the revert removes are resolved. Answers {applied: true, model_id, model_version, copied_from, discarded}: model_version is the new version.

A refusal comes back as {applied: false, http_status: 409, code, message}: generation_active (a build holds the model), and for controls nothing_to_undo or set_diverged (a control the change touched has changed since; undo the later change first), for a version no_earlier_version.

update_control_statusA

Update the implementation status of a security control. Mutating.

Sets the control's status to "implemented" or "not_implemented". Marking a control "implemented" REQUIRES at least one assertion on the control — check its assertion_count (via get_controls) first and submit assertions with submit_assertions if it is zero, or the call is rejected.

refine_controlA

Refine a control's description with AI-gated CO sufficiency check.

Two modes:

  • Provide description: proposes a new description directly.

  • Provide codebase_findings: the platform proposes a description based on existing code that may already satisfy the control.

  • Both can be provided: the platform evaluates the proposed description with the codebase findings as context.

The AI evaluates whether the mitigation group still collectively satisfies all mapped control objectives. If rejected, returns {accepted: false, reason, per_co} with per-CO reasoning.

A refinement is rejected when the proposed description would reduce the protection the control currently states for an objective it is mapped to; per_co names each objective and explains why. This is a decision, not a transient error — re-wording the same narrowing will not pass it, and it applies however well-motivated the narrowing is. A control is a requirement that must be met to cover its objectives, so evidence that the system does not currently meet it means the control is UNMET, never that the control should ask for less.

After an accepted refinement the control's assertions are kept and judged again against the new description in the background: an assertion that still fits keeps counting as evidence, and one that no longer fits is flagged as not aligned with the control. Read get_sufficiency once that re-judgement lands, and replace the assertions it names. The refinement itself supersedes nothing; the response's superseded_assertions is always 0.

remap_controlA

Mechanical, non-AI-gated remap of a control's CO mappings.

Distinct from refine_control (AI-gated description edit) and set_mitigation_groups (AI-gated CO-centric group authoring). Use remap_control when the operator already knows the correct co_ids and just needs to persist the mapping change — e.g., restoring mappings after an asset/attacker edit left the control with stale or orphaned CO references. No LLM evaluation runs.

Rejects target co_ids that do not exist on the model or are tombstoned (the pair was removed in a later version) — map to live COs only.

apply_control_changesetA

Apply a batch of control operations atomically as ONE transaction.

Use this to reorganize a model's controls in a single step — for example to deduplicate controls (remap several onto the right objectives and delete the redundant ones at once), instead of many separate calls. All operations commit together or not at all.

Mapping-only: remap/delete/set_groups change objective mappings and retire controls but never re-author a control's description, so a kept or reused control keeps its status, evidence, and assertions. The orphan guard is evaluated on the FINAL state of the batch, so a delete paired with a covering remap or add in the same changeset is allowed; a changeset that would leave any previously-covered control objective uncovered is rejected as a whole and nothing is written.

model_coherence_reportA

How coherent a model's structure is: its component bindings, the repos its controls' assertions name, and whether every CO is structurally reachable. Read-only.

Pass co_id to keep only the findings about that CO (404 if it does not exist). Each finding carries type, severity, a message and the ids it concerns, so its fix can be called directly:

  • control_component_unknown, assertion_repo_orphan, control_unscoped_with_scoped_assertions: assign_to_components(target_type="control"). assertion_repo_mismatch: rebind the assertion or rescope the control.

  • asset_component_unknown: edit_asset with corrected component_ids.

  • component_unbound: for your own code, edit_component with the real repo_url. An external-zone component (a third-party service, a customer's IdP) stays unbound: the finding is a permanent marker of an external dependency, not a TODO, and client code touching it is no reason to bind it.

  • co_attacker_unpositioned: edit_attacker with trust_boundary_ids. co_asset_unbounded: assign_to_components(target_type="asset"). co_no_shared_boundary: reposition the attacker or rescope the asset; if the boundaries truly do not meet, that is the answer. co_missing_entity: restore_entity, or remove the CO.

An indeterminate reachability verdict means the structure it needs is missing: supply it. It never means the objective is inapplicable, which is a separate claim recorded with create_co_disposition. get_reachability_verdicts returns the raw verdicts.

get_compositionA

A model's composed view: its own entities with everything inherited from its ancestors on the recursive tree. Read-only.

view selects what is returned:

  • overview (default, ~1-2KB, read it first): {model_id, model_version, flag_enabled, tree: {parent_id, ancestor_chain, depth, child_ids}, counts: {entities, control_objectives, reconciliation_candidates}, warnings}.

  • entities: the effective entity set keyed by kind (trust boundaries, components, assets, attackers, attack paths), each entry {kind, qualified_id, owner_model_id, owner_title, origin, entity}. Paginated (page, page_size); kind (e.g. "attackers") keeps one kind.

  • objectives: effective COs {co_qid, asset_qid, attacker_qid, security_properties, origin}, where origin is own, cross (inherited, with a local asset or attacker) or inherited.

  • coverage: per effective CO {co_qid, is_covered, own_credit, inherited_credit, contributing_controls} — the composed figures, not the per-model get_verification_report. Paginated; origin filters by contributing-control origin.

  • attack_paths: {effective_paths, lattice_positions, authored_paths, suggestions: {missing_path, dangling_path}} against the composed topology.

Where composition is not available, every view returns its shape empty with flag_enabled: false rather than an error. Composed reachability is get_reachability_verdicts(composed=True).

decide_reconciliation_candidateA

Decide a reconciliation candidate from list_reconciliation_candidates. Mutating.

kind is assets, attackers or components; own_qid / inherited_qid are the pair's qualified ids ("child:A1", "parent:A1").

  • decision="apply" records that the descendant's own entity IS the inherited one: the own entity stays in the model and is left out of its composed view, so the inherited entity is canonical and credit keys on it. The record is dropped when the pair stops matching (an edit, a re-parent). A heuristic-tier candidate is refused unless confirm_heuristic=True acknowledges its structural divergence. The server re-validates the pair (400 when the model has moved since: refresh the list). Bumps the model version; returns {model, controls_carried, controls_orphaned, orphaned_control_ids}.

  • decision="reject" records "these are NOT duplicates" at org scope, so the pair leaves the active queue for everyone. Idempotent on the pair; no new version. Returns the record; keep its id.

  • decision="unreject" removes the rejection rejection_id (from list_reconciliation_candidates(disposition="rejected")), returning the pair to the queue. Returns {ok: true}.

503 where composition is not available on the backend.

lift_composition_entityA

Promote a shared-anchor entity from two sibling descendants to their lowest common ancestor. Mutates state across THREE models.

The operator has confirmed (via the composition lift-candidate view) that the entity local_id_a on descendant_a_id and the entity local_id_b on descendant_b_id are the same logical thing and should be modeled once on the LCA. The route's model_id is the operator's current context model — typically the LCA, but the server accepts any ancestor of both descendants.

Conflict resolution. The server re-detects field-level and attached-state conflicts against current live state before applying. If new conflicts have surfaced since the operator's last candidate fetch, the call returns 400 with the missing conflict keys; refresh the lift-candidate view and resubmit with resolutions covering every key. Each entry in field_resolutions / attached_state_resolutions is "keep_a" | "keep_b" | "keep_both" (union for list/set fields; falls back to B for scalars).

Over-application gate. The lift extends visibility to every descendant of the LCA, not just the two source descendants. The server runs an over-application gate that refuses lifts touching descendants outside an acknowledged set; pass acknowledged_third_party_subtrees to acknowledge specific subtrees, or skip_overapplication_gate=True to override entirely after explicit operator confirmation.

Each affected model (LCA + both descendants) bumps version and emits a model_refined activity event; a structured lift_applied event with the full lift_event payload lands on the LCA. The audit pack surfaces this under lift_history. Reverse it with undo_composition_event(event_type="lift"), which previews unless dry_run=False; the inverse operation is split_composition_entity.

split_composition_entityA

Push an ancestor-owned entity down to one or more descendants and soft-delete the ancestor's copy. Mutates state across the ancestor + every target descendant.

Inverse of lift_composition_entity. Use when an entity that currently lives on an ancestor is in fact descendant-specific and should be modeled separately per descendant — the operator chooses which descendants take a copy. A new local id is minted on each target; attached state on the ancestor's entity (assertions, jira mappings, risk acceptances, etc.) is duplicated to every target.

The route's model_id IS the ancestor (the entity being split lives on it). Each affected model (ancestor + every target descendant) bumps version and emits a model_refined activity event; a structured split_applied event with the full split_event payload lands on the ancestor. The audit pack surfaces this under split_history.

get_mitigation_groupsA

Get the current mitigation group structure for a control objective.

Returns the grouped view of controls for this CO with details (id, description, status) for each control:

  • groups: numbered groups (within=AND, across=OR)

  • defense_in_depth: tracked but not required for mitigation

  • unmapped: model controls not mapped to this CO (available for assignment)

Use cases:

  • Before set_mitigation_groups to see the current structure

  • When reviewing a CO's assessment to understand why it is at_risk or mitigated

  • When deciding which unmapped controls to assign to a CO

set_mitigation_groupsA

Declaratively set the mitigation-group structure for a control objective. Mutating; runs as a polled background job (an LLM sufficiency check evaluates whether the new structure satisfies the CO) and returns once complete.

Replaces ALL mitigation-group assignments for this CO. Call get_mitigation_groups first to see the current structure and the unmapped controls available for assignment.

Mitigation groups define alternative paths to satisfy a CO:

  • Within a group: AND — all controls must be implemented.

  • Across groups: OR — any one complete group mitigates the CO.

  • Defense-in-depth: tracked but not required for mitigation.

edit_evidenceA

Attach or detach an auxiliary evidence item (doc, link, artifact reference) on a control. Mutating.

Evidence is contextual metadata only: it does NOT count toward a control's implementation status, and removing it changes neither the status nor any assertion. Only assertions prove controls.

import_controlsA

Import existing security controls into a threat model.

Accepts structured JSON or free-text. Controls are auto-mapped to COs and deduplicated against existing ones. The parse/map/dedup runs as a background job (polled for progress), then — because this mutates the model — you are asked to confirm before the controls are saved.

The saved controls are added to the model's current controls as one change (undoable with undo_model_change); no model version is created, and it is refused while a control build holds the model. Nothing runs for them unprompted: the result's awaiting_judgement lists them, and the mitigation groups they join credit nothing and read awaiting judgement until judge_imported_controls is called (estimate first, then confirm_estimate=True).

judge_imported_controlsA

Have the imported controls awaiting their judgement judged. Mutating only with confirm_estimate=True; may consume credits then.

Controls saved by import_controls are not judged unprompted: the mitigation groups they join credit nothing until this runs. It judges every objective those controls join, priced and charged as judge_objectives prices and charges them.

  1. Call with confirm_estimate=False (the default). Nothing is queued and nothing is charged; the answer carries awaiting_judgement (the control ids), co_ids (the objectives they join), scope, ungrouped and estimate. Show the user the estimate.

  2. Call again with confirm_estimate=True once they agree. The judgements are queued (confirmed: true, queued) and the controls stop awaiting (awaiting_judgement comes back empty).

ungrouped lists objectives with no mitigation group: nothing can be judged there until their controls are grouped with set_mitigation_groups. When nothing awaits, the answer says so and does nothing.

Refusals come back as data, {confirmed: false, queued: 0, http_status, ...}: 409 while a control build is running, 402 when the balance this workspace bills to cannot cover the estimate, 503 when judging is unavailable on this deployment.

delete_controlA

Soft-delete a security control, optionally with a justification. Destructive (mutating): the control is retired, not permanently erased.

Blocks with HTTP 409 when the control is the ONLY control covering any control objective — removing it would leave that CO uncovered. Add a replacement control (or refine the threat model) before deleting.

check_control_gapsA

Analyze control coverage and surface control objectives that lack sufficient controls. Read-only (does not mutate the model); runs as a polled background job and uses LLM reasoning.

Complements the deterministic assess_model (which scores each CO's mitigated / at_risk / unassessed status from control implementation state) by reasoning about which COs are under-covered and where new controls are needed. Use this to decide what controls to add; use assess_model to score the current state.

assess_modelA

Run the deterministic assurance assessment over a threat model. Read-only — no LLM calls, no mutation.

Evaluates each control objective from its controls' implementation status and returns summary counts (mitigated / at_risk / unassessed) plus progressive metrics (defined / implemented / verified). For LLM-based reasoning about which COs are under-covered and what controls to add, use check_control_gaps instead.

Use summary_only=True to get just the counts without per-CO assessments.

get_review_queueA

Returns the workspace's review queue: what needs a decision or a re-check, ranked. Read-only; no side effects.

Each row carries an item_type, one of escalation (a judgment an agent was refused and parked for a person), proposal (an open change of scope or design, or an assumption proposal: a precondition a strengthening run found only the environment can meet), unaccepted_assumption (an assumption something depends on that is not accepted — never attested, lapsed, or its text changed since it was attested — with the controls and objectives that wait on it), open_assumption, or stale_control (an implemented/verified control whose assertions have not been checked in 90+ days). Rows are ranked in that order. Escalations and proposals are decided with decide_proposal; an unaccepted assumption is accepted with submit_attestation; for each stale control, verify its assertions against the codebase. Accepting an assumption is a person's judgment unless the workspace delegates it. Start here for periodic maintenance.

add_assetA

Add a new asset to a threat model. Creates a new version.

Authoring contract: name the data or resource being protected and the security property at stake (Confidentiality / Integrity / Availability / Usage), not a mechanism or control ("per-organization key-wrapping material", not "KMS encryption"). An asset phrased as a mechanism is flagged with a quality_warning and yields under-specified control objectives. An asset that does not apply is recorded with a non-applicability assumption or create_co_disposition; there is no status to set.

The platform reasons the factor decomposition and composes impact with the prompt generation uses, so factors are calibrated alike; override one afterwards with edit_asset and a change_reason. component_ids links the asset to the deployable units that hold it, which feeds reachability (several for a multi-instance asset, e.g. a session token on client and cache).

A proposal matching a soft-deleted asset is gated: it either restores that asset (auto_restored: true, restored_asset_id, discarded_fields; its CO tombstones revive) or is refused as similar ({accepted: false, classification: "similar", candidate_restore_id}, nothing saved). A normal create returns {model, controls_carried, ...}. 503: an evaluator is unavailable, retry with backoff; 502: it answered malformed, retry.

edit_assetA

Edit an existing asset. Only provided fields changed.

When changing identity fields, hold to the asset authoring contract: name the data/resource protected and its security property, not a mechanism — otherwise the result is flagged with a quality_warning (see add_asset). There is no status field to set.

The composed impact is server-derived from the factor fields; there is no way to set it directly. To change the rating, set factor values (the platform composes the new rating) and supply change_reason documenting the operator override of the LLM-generated factors. The reason is captured in the rating-revision audit trail.

LLM-gated on identity-bearing fields (name, description, security_properties). Factor and notes edits skip the gate.

Outcomes when identity fields change:

  • Accepted edit (LLM classifies as preserve) — normal envelope response.

  • Rejected edit (LLM classifies as replace / ambiguous) — {"accepted": False, ...}; nothing saved. Soft-delete + add-new instead.

Editing a soft-deleted asset is rejected — restore_entity (entity_type="asset") first. 503 on evaluator outage, 502 on malformed response, 400 when factor fields are sent without change_reason.

add_attackerA

Add a new attacker to a threat model. Creates a new version.

Authoring contract: capability names the operations the attacker can perform from its position and what they achieve — not just the access or vantage point. Phrase it as "From [position], the attacker can [concrete operations] …" (e.g. "From the network path between the API server and the database, the attacker can read and alter requests and responses to exfiltrate data in transit or inject forged responses"). A capability that states only access is flagged with a quality_warning and the control objectives derived from it may be under-specified.

The caller supplies identity-bearing fields (capability, position, archetype, trust_boundary_ids); the backend LLM-reasons the factor decomposition. Override any factor post-create via edit_attacker with a change_reason. Mirror of add_asset semantics.

Three outcomes (normal create / auto-restore / similar-rejection) mirror add_asset. 503 on factor-reasoning or restore-candidate evaluator outage, 502 on malformed restore-candidate response.

surface_extent says how much of the reached interface this attacker's operations range over. An attacker ranging over the whole interface makes the objectives it appears in for-all obligations, which only a sound witness (typed_boundary / sink_default_deny) can credit. Declaring it here is an operator statement about the attacker's reach, recorded attested with its change_reason, so a create takes the two together. Only whole is declarable on a create: narrowing to one named entry is a statement about the objectives the attacker anchors, and a create has none yet — add the attacker, then narrow it with edit_attacker and a change_reason, where the narrowing is checked against the assets those objectives defend. There is no attacker status to set.

edit_attackerA

Edit an existing attacker. Only provided fields changed.

When changing identity fields, hold to the attacker authoring contract: capability names the operations performable from the position ("From [position], the attacker can [operations] …"), not just access — otherwise the result is flagged with a quality_warning (see add_attacker).

The composed likelihood is server-derived from the factor fields; to change the rating, set factor values and supply change_reason for the audit trail.

LLM-gated on identity-bearing fields (capability, archetype, position). Factor and trust_boundary edits skip the gate.

503 on evaluator outage, 502 on malformed response, 400 when factor fields, surface_extent or attest_surface_extent are sent without change_reason.

Attesting surface_extent is a person's audited structural declaration, ledgered like a factor override and forking a model version: "whole" makes every objective the attacker appears in a for-all obligation. "point" is REFUSED where an asset on one of those objectives is implemented by several components and is not split-knowledge — reaching any one of them reaches the asset, so a narrowing to one named entry would not be true of it — and it never makes a clause whose own text is universal existential.

reevaluate_threat_model_factorsA

Re-run the LLM factor judgment on every asset and attacker in a threat model. Useful for re-baselining factors after a bug fix or feature-description change, without regenerating the whole model (which would destroy controls, assertions, components).

Each entity's factors and rationale are replaced with a fresh LLM-judged decomposition; the composed impact / likelihood is re-derived deterministically from the new factors. Each re-rating is recorded as a rating revision in the audit trail with change_reason (default: "LLM factor re-evaluation") so the starting-point regeneration is distinguishable from operator- supplied factor overrides via edit_asset / edit_attacker.

The platform's LLM factor judgment is a starting point. For deployment-specific factor adjustments (e.g., elevated regulatory_scope because your tenant is HIPAA-covered, or Commodity prevalence because your endpoint is public-internet exposed), use edit_asset / edit_attacker afterward with a change_reason documenting the operator override.

Per-entity soft-fail: an LLM failure on one entity is recorded in the response's failed_entities list (with id, kind, and reason); the remaining entities are still re-evaluated and their rating revisions persisted as they complete. The endpoint returns 503 only when every live entity failed — in which case nothing was persisted; retry when the evaluator is reachable.

Soft-deleted assets and attackers are skipped.

get_verdict_divergenceA

Where the LLM's verdicts disagree with the model's authored state.

Two coverage divergence kinds, distinguished by the LLM's p_covers (probability the control covers the CO), shown as "model confidence":

  • missing_mapping: HIGH p_covers, but the CO is NOT mapped — the LLM is confident the control covers it, so it should be mapped. Accepting ADDS the mapping.

  • spurious_mapping: LOW p_covers, but the CO IS mapped — the LLM is confident the control does NOT cover it, so the mapping is likely wrong and inflates apparent coverage. Accepting REMOVES the mapping. Only confident rows surface; the uncertain middle band is dropped. So a ~100%-confidence row is a strong "add" and a ~0%-confidence row is a strong "remove" — both are actionable, in opposite directions.

Rows are sorted by confidence, so the strongest calls come first. Each section is paginated: its pagination.filtered_total reports the full count, so when it exceeds the rows returned, raise limit (up to 500) or page with offset to review every divergence — not only the first page.

Also returns group_sufficiency divergences (observation-only). Apply coverage rows with resolve_verdict_divergences(action="accept"); set aside rows the structural model got right with resolve_verdict_divergences(action="dismiss").

resolve_verdict_divergencesA

Accept a batch of coverage divergences as mapping changes, or dismiss a batch of divergences. Mutating.

action="accept": each missing_mapping ADDS its CO to the control, each spurious_mapping REMOVES it, one version per affected control. Items are validated one by one: the answer separates applied from skipped (stale, would orphan, already so). To accept only confident rows, filter get_verdict_divergence's coverage rows by p_covers (near 1.0 for missing, near 0.0 for spurious) first. reason (min 10 chars) is recorded on each control's history.

action="dismiss": the structural model was right and the LLM was not; the model does not change. A dismissal is keyed to the row's current verdict input, so the row reappears once its control or objective changes. Works for coverage and group_sufficiency rows.

list_compliance_frameworksA

List the compliance frameworks available to map controls against.

Read-only; no side effects. Returns both built-in frameworks (e.g. OWASP ASVS) and any custom frameworks in the workspace. Use this to discover framework identifiers before select_compliance_frameworks (activate one for a model) or import_compliance_framework (add a custom one). Takes no arguments beyond the version guard.

import_compliance_frameworkA

Import a custom compliance framework. Requires PRO tier.

Use this when your customer's program (regulatory, contractual, or internal) is not covered by Mipiti's built-in frameworks. After import, the framework is selectable on threat models exactly like a built-in.

Fields: name (required), version, description, requirements (required, non-empty), level_definitions.

Each requirement takes id and description (required), level (integer, default 1), the optional grouping chapter_id / chapter_name / section_id / section_name / title, scope (component, the default, or system: covered if ANY model satisfies it) and level_specific_text (per-level text).

level_definitions and level_specific_text are keyed by the level as a string integer ("1", "2"): the key is the ordinal the level <= target_level filter compares, so a non-integer key is refused (400). Labels ("Baseline", "SL3") go in each value's name. A level value is {"name", "description", "source"}, where source is authoritative (paraphrased from the published standard) or mipiti_convention (tiers you defined).

Example::

{
  "name": "ACME Tiered",
  "level_definitions": {
    "1": {"name": "Baseline", "description": "Minimum.",
          "source": "mipiti_convention"}
  },
  "requirements": [
    {"id": "ACME-PWD", "description": "Passwords meet policy",
     "level": 1, "level_specific_text": {"1": "Min 8 characters."}}
  ]
}
map_control_to_requirementA

Manually map one security control to one compliance-framework requirement. Mutating: records a control-to-requirement mapping, which re-derives that requirement's coverage in the compliance report.

Use for a single, deliberate mapping you are asserting by hand. To let the LLM propose mappings across many requirements at once, use auto_map_controls; to close gaps end-to-end (map + exclude + fill), use auto_remediate_compliance.

auto_map_controlsA

LLM-map a model's existing controls to a framework's requirements. Requires PRO tier. Mutating: writes control-to-requirement mappings. Runs as a background job (typically 20-45s); this tool waits for completion and returns the result.

Sits between the manual map_control_to_requirement (one mapping at a time) and the full auto_remediate_compliance loop (which also excludes non-applicable requirements and proposes new entities for remaining gaps). auto_map_controls only creates mappings from controls that already exist — it never adds or excludes entities.

update_organizationA

Set per-organization level grades for IEC 62443-4-1 and NIST CSF.

Admin-only: the backend requires the caller to be an admin in the organization (or a superadmin). Non-admins will get a 403; do not invoke this tool unless you've verified admin role for the target org.

target_ml is the IEC 62443-4-1 Maturity Level the organization targets for its secure-development program (1-5). csf_tier is the NIST CSF Tier the organization targets for its cybersecurity risk-management posture (1-4).

Because None on the wire is indistinguishable from "field omitted", pass clear_target_ml=True or clear_csf_tier=True to explicitly reset a value to NULL. Omitting both the value and its clear_* flag leaves the existing server-side value untouched.

add_componentA

Add a component to a threat model.

Components bridge security architecture to code organization. They map trust boundaries to repos so controls can be scoped to the codebase that implements them. They also drive the deterministic reachability composer's asset-boundary derivation: an asset's trust-boundary footprint is the union of its components' trust_boundary_ids.

Generation reads no components, so add or edit them after generate_threat_model, not before.

A component with empty repo_url is either speculative (your own code, not linked to a repo yet) or external (e.g. a third-party service, the customer's IdP, or other external infrastructure you call but don't own). The component's trust boundary tells them apart: bind an internal-zone component to its repo via edit_component; leave an external-zone component unbound — its component_unbound finding is a permanent external-dependency marker, not a gap to close. Binding by "some client code touches it" is wrong: client code for external dependencies lives in your repo too.

edit_componentA

Edit a component's properties.

Per-component level grades are orthogonal axes — set whichever apply to the program the component is in scope for. Leave a field unset (None) to keep the current server-side value; backend treats absent fields as "unchanged".

get_group_dependenciesA

The reliance edges among a tag's member models. Read-only; no side effects.

Returns {tag_id, tag_name, models, edges, total}: one row per edge a member declared on another model's control (manage_reliance / attach_foundation), with both models' titles, the mode (delegated or relied_upon), the source objective or control, the provider control, its status (draft, active, broken, rejected), validation_verdict and credit_state (whether it credits its objective now). Empty when no member relies on another.

Use it to review the cross-model dependencies of a product or audit scope before an auditor export, or to find broken edges. A model's own edges, in both directions, are list_reliance.

get_assertion_typesA

List the assertion types submit_assertions accepts, with their params.

Read-only. Returns the catalogue as structured data: every type, what it proves, its soundness class, which params it requires, which it accepts (an array-valued param carries its item_schema), and a worked example. soundness_classes defines the five classes by the fact a pass establishes, weakest to strongest — presence, under_approximating_scan, existential_witness, sound_over_approximation, by_construction — and sound_classes names the two that can credit a for-all clause. covers gives the accepted form of a binding declaration.

Call this before writing assertions. submit_assertions names the types and their required params in its own description, but descriptions are prose a client may present only in part, and a half-list reads exactly like a whole one. This returns data, so what you get back is the complete contract.

submit_assertionsD

Typed claims about a control, assumption or functional test; CI checks later. get_assertion_types returns it all as data.

By class, strongest first, as name(required) [opt: optional]: [by_construction]

  • typed_boundary(scope, sinks, boundary_type, constructors, property) [opt: allowlist, wrappers] [sound_over_approximation]

  • sink_default_deny(scope, sinks, safe_forms, property) [opt: allowlist, wrappers] [existential_witness]

  • test_attested(test) [opt: env, mechanism] [under_approximating_scan]

  • pattern_matches(file, pattern) [opt: scope_start, scope_end, multiline, dotall, target]

  • pattern_absent(file, pattern) [opt: scope_start, scope_end, multiline, dotall, target]

  • no_plaintext_secret(file, patterns) [presence]

  • function_exists(file, name)

  • class_exists(file, name)

  • decorator_present(file, function, decorator)

  • function_calls(file, caller, callee)

  • import_present(file, module)

  • file_exists(file)

  • file_hash(file, algorithm, expected_hash, scope_file) [opt: scope_start, scope_end]

  • config_key_exists(file, key)

  • config_value_matches(file, key, pattern)

  • env_var_referenced(file, variable)

  • dependency_exists(manifest, package)

  • dependency_version(manifest, package, constraint)

  • parameter_validated(file, function, parameter)

  • error_handled(file, function)

  • middleware_registered(file, middleware)

  • http_header_set(file, header)

  • test_exists(pattern)

  • module_exists(file, name)

  • module_instantiated(file, parent, child)

  • port_exists(file, module, port) [opt: direction]

  • parameter_defined(file, parameter) [opt: module, pattern]

  • signal_exists(file, name) [opt: module, kind]

  • sva_assertion_present(file, name)

  • register_reset(file, signal) [opt: reset]

Each: type, params, description, repo ("/" or "no_repo"), covers beside them, never in params: the CO-NN or cls_ ids proved. A for-all clause takes only typed_boundary (sinks accept one boundary type) or, when they do not, sink_default_deny, bound with covers.

list_assertionsA

List active assertions for a control or assumption.

Provide exactly one of control_id or assumption_id.

Returns a flat list of assertions. Each assertion carries an origin field: "own" for assertions submitted directly against this model's control or assumption, "inherited" for assertions contributed through model composition (composed models whose assertions apply here). Inherited assertions are included in the listing.

Each assertion also carries three INDEPENDENT verdict fields. Read them together — a passing tier check is not the same as sufficient evidence:

  • tier1_status — mechanical check: the named file, symbol, or pattern is actually there. "pass" | "fail" | "pending".

  • tier2_status — semantic check: the cited code meaningfully implements the claim. "pass" | "fail" | "pending".

  • coherence_status — advisory consistency signal across the control's evidence set. "pending" here does NOT block the control from verifying, does NOT mean a verdict is missing, and is NOT a reason to trigger a recompute.

An assertion can pass BOTH tiers while its control stays unverified, because verification is decided per CONTROL, not per assertion: a control verifies only when its assertions collectively cover every clause of the control description. Read get_sufficiency for that verdict; never infer it from the tier fields here.

Each assertion also carries covers (the objective or clause ids it was declared to prove; empty when undeclared) and, where the platform surfaces it, tier1_attested and evidence_provenance (whether the run that verified it was signed and by what class of identity).

delete_assertionA

Permanently delete a single assertion from a control or assumption. Mutating and destructive: the assertion record is removed, not soft-deleted, and its contribution to sufficiency/verification is dropped. It does NOT itself re-run verification; the deletion queues a background re-evaluation of the control's sufficiency, which a later get_sufficiency read reports once it lands.

Use to retract a claim that was submitted in error or that get_verification_report flagged as misaligned (off-topic for the control's current description). To add assertions use submit_assertions; to inspect them first use list_assertions. Only "own" assertions can be removed here — inherited assertions come from composed models and must be managed on their source model.

get_verification_reportA

Get verification report with summary stats and sufficiency gaps.

Returns tier1/tier2 pass/fail/pending counts, per-control verification status, and sufficiency details.

Each per-control sufficiency block carries:

  • status: "sufficient" | "insufficient" | "pending" | "stale". "stale" means the stored verdict no longer reflects the current control description, active assertion set or the rules it was computed under. Reading does not queue a re-evaluation: the write that changed a control queues its own. Call this tool again later for the refreshed verdict.

  • details: human-readable LLM reasoning.

  • misaligned_assertion_ids: assertions whose stated subject is off-topic for the control's current description (common after a control has been refined or regenerated). Treat as a directive: rebind to the right control, supersede via delete_assertion, or rewrite. Do NOT treat them as evidence. A non-empty list forces the verdict to "insufficient".

  • stale: boolean shortcut for status == "stale", kept distinct so an INSUFFICIENT verdict that's also stale (the prior insufficient decision was computed under outdated inputs) can be flagged without overloading status.

A drift item means the accepted evidence changed (a test's definition, a witness's scope or allowlist) and its verdict was withdrawn until reviewed again.

By default returns summary only (no per-assertion details). Set summary_only=False to include full assertion details and drift items.

get_sufficiencyA

Whether the submitted assertions of one control, or of one functional test, together prove it. Read-only; name exactly one id.

For a control this explains verification_status: "partially_verified". status is sufficient | insufficient | pending, with freshness (fresh | stale | pending) beside it; insufficient carries details naming EACH uncovered clause and the evidence that would close it. A soundness_tier is the weakest clause's tier: a control is proven no more strongly than its thinnest clause. Reading does not queue a re-evaluation: the write that changed a control queues its own. For the whole model use get_verification_report.

Act by submitting the named assertions; a clause describing a mechanism the system does not use calls for refine_control, not evidence. get_control_work_order serves the per-clause list: where the order names a required class for a clause, required_evidence carries the clause id for covers and a suggested_submission skeleton whose <...> placeholders you replace. For a for-all clause prefer typed_boundary, else sink_default_deny. class_mismatch means the bound evidence is the wrong CLASS and more of it will not help. An attestation covers an existential clause, never a for-all one; that clause's only other exits are a risk acceptance or a not-applicable disposition.

For a functional test it is whether the test's evidence proves the objectives it is associated with, with the reasoning; computed after evidence is submitted, so it can read pending or absent until then.

submit_findingsA

Record negative findings (gaps discovered while scanning a codebase against a model's controls). Mutating: persists new finding records against the model.

Use after a gap-discovery scan (see get_scan_prompt) to log where expected control evidence was NOT found. Findings are the negative counterpart to assertions (positive proof via submit_assertions): a finding says "I looked here for this and it was missing." Once submitted, drive a finding through its lifecycle with update_finding and review them with list_findings.

list_findingsA

List negative findings recorded on a threat model. Read-only.

Returns finding rows with their lifecycle status; use to triage gaps or to find a finding_id for update_finding / remediate_finding. Each row carries an origin ("own" for findings recorded on this model, "inherited" for findings contributed through model composition, with inherited_from_* context); inherited findings are included in the listing.

update_findingA

Advance a finding through its lifecycle. Mutating: updates the finding's status and metadata.

Use to acknowledge, remediate, verify, or dismiss a finding previously recorded by submit_findings / list_findings. This records a MANUAL status transition — the machine-set auto_resolved state is not among the statuses it accepts; for gaps whose kind has an automatic fix, remediate_finding performs the actual cleanup instead.

remediate_findingA

Preview, and on confirmation apply, the platform's remediation of a finding.

Without apply it is read-only: it returns a structured diff of what the remediation would change, shaped by the finding's kind (for structural_duplicate_controls: which controls are kept, which dropped, and the CO mappings and framework refs the survivor takes). Show the operator that diff and get explicit confirmation.

With apply=True it commits the change, recording justification (a one-line operator rationale, required) on the audit trail. Never apply without having shown the preview: the platform records who acted but does not enforce the preview — the agent does.

404 when the finding does not exist; 422 when its kind has no automatic remediation (resolve those with the control tools); applying a finding already remediated or dismissed is refused (409).

get_findings_risksA

Workspace-scoped triage dashboard: open findings, active risk acceptances, and at-risk Control Objectives across every model the workspace can access.

Use this as the entry point when an operator asks "what's open?" or "what should I work on next?" — one round-trip returns all three categories with model context and risk dimensions (severity, status, risk_tier, owner, review_by) so the agent can triage without per-model fan-out. The endpoint is read-only and fast; it composes from existing per-model queries server-side.

Returns the envelope verbatim: {workspace_id, evaluated_at, models, findings, risk_acceptances, at_risk_cos, summary}. summary carries totals (open_findings, total_findings, active_risk_acceptances, total_risk_acceptances, at_risk_cos) for quick health-check responses.

get_remediation_leverageA

Remediation-leverage plan for a model: which controls to implement first to close the most control objectives with the least work.

Returns the model's not-yet-satisfied controls ranked by how many control objectives each one closes (ranked), plus a greedy minimal fix order — the sequence of controls that reaches the most mitigated objectives with the fewest controls (greedy_plan) — and a summary of the collapse (total objectives, currently mitigated, how many controls the plan needs). Use to prioritize implementation work: a single call tells the agent which controls give the highest leverage, so it can tackle the shortest path to coverage instead of fixing objectives one at a time. Read-only.

Composed models: each entry in ranked and greedy_plan also carries its owning model — owner_model_id and owner_model_title — and an inherited flag. inherited is true when the control is authored on an ancestor model, meaning the fix lands on that model rather than the one being assessed; summary.inherited_candidate_controls counts them. Surface the owning model so the operator knows which high-leverage fixes belong to a parent model. A flat (non-composed) model reports every control as owned by the assessed model.

list_risk_acceptancesA

List all risk acceptances on a specific threat model — risks that an operator explicitly accepted instead of mitigating.

Each entry carries the CO id, owner, justification, status (active / expired / revoked), and the review deadline. Use to inspect which gaps were intentionally accepted versus genuinely unaddressed when triaging at-risk COs.

Returns risk acceptances ONLY. An objective declared not applicable is a different claim — it is not an accepted risk, and counting it as one would read a "does not apply here" as "we are carrying this exposure". Use list_co_dispositions to see those, or both together.

create_risk_acceptanceA

Record that an operator explicitly ACCEPTS the residual risk on a control objective instead of mitigating it — the write counterpart to list_risk_acceptances.

Use when a control objective's residual risk is a deliberate, documented decision rather than an unaddressed gap: the acceptance carries an owner, a justification, and a review deadline, and reads as active until it expires or is revoked. Prefer this over leaving a known-and-accepted risk implicit — it makes the decision auditable and forces a revisit by the deadline. An accepted objective is still surfaced (as accepted, not unaddressed) when triaging at-risk objectives.

create_co_dispositionA

Record that a control objective DOES NOT APPLY to this system — a signed, expiring judgment, not a dismissal.

The sibling of create_risk_acceptance, and the distinction between them is the claim being made. A risk acceptance says the exposure is real and we are carrying it. A disposition says this objective does not apply here at all — the asset is not handled the way the objective assumes, the attacker position does not exist in this deployment, the capability is not present.

The objective is not removed. It stays in the control-objective matrix, stays in every coverage count, and is reported in its own class alongside the owner and justification recorded here. That is the point: a reviewer can see the judgment and challenge it. An objective that simply vanished would be indistinguishable from one nobody modelled.

What it does change is work: no controls are generated for the objective and no coverage gap is raised against it, because an objective that does not apply is not a gap.

review_by is required and is not a formality — the claim stops applying on that date, and the objective returns to whatever posture its controls give it, gap included. Choose a date by which someone can realistically re-check that the claim still holds.

Use create_risk_acceptance instead when the objective DOES apply and the exposure is being carried deliberately. If an objective is only unaddressed rather than inapplicable, neither tool is right — add controls.

list_co_dispositionsA

List the signed judgments recorded against this model's control objectives — risk acceptances, not-applicable dispositions, or both.

Read-only. Each entry carries the objective it names, the owner who signed it, the justification, the dates, and its status. Expired and revoked entries are included: a decision that lapsed is part of the audit trail, and hiding it would leave a reader unable to tell a judgment that was reviewed from one that was never made.

Read this before authoring a new judgment on an objective — an existing one may already cover it, or may have expired and need re-signing rather than duplicating.

complete_setup_stepA

Mark one onboarding setup step as done. Mutating: updates the workspace onboarding checklist. Call after actually performing the corresponding setup action on the user's behalf.

Check current progress with get_setup_status first to avoid re-marking completed steps. An unrecognized step_id is rejected without any state change.

get_setup_statusA

Get the workspace onboarding checklist with completed and pending steps. Read-only.

Call this before suggesting or performing setup actions so already-done steps aren't repeated; mark a step done with complete_setup_step. Takes no arguments beyond the version header.

add_trust_boundaryC

Add a trust boundary. Creates a new model version.

edit_trust_boundaryB

Edit a trust boundary. Creates a new model version.

add_assumptionA

Add an assumption. Creates a new model version.

Assumptions represent security properties outside the system owner's trust boundary. When linked to COs and attested, they mitigate those COs in the assessment.

Optionally attach a structured exclusion predicate (the exclusion_* params). The reachability composer matches active

  • attested assumptions with predicates against COs deterministically — class-3 (deterministic computation) evidence in addition to the operator-attested class-1 evidence. Pass any subset of the fields; unspecified fields default to wildcard ("*"). When exclusion_co_ids is non-empty, it takes precedence over the match fields.

Use this to resolve a CO whose composer verdict is indeterminate because no structural primitive backs an operator non-applicability claim: set exclusion_co_ids=<co_id> (and optionally the attacker/asset/property fields), and the composer will derive unreachable / reason: assumption_excludes on subsequent loads, with the assumption's structured predicate as the audit-trail cause.

edit_assumptionB

Edit an assumption. Creates a new model version.

submit_attestationA

Record that a responsible party affirmed an assumption holds.

Only for external assumptions. Non-applicability assumptions require CI verification (submit assertions + run mipiti-verify) — manual attestation is rejected for them.

An assumption with a current attestation can mitigate linked COs. When the attestation expires, those COs become at-risk until re-attested or covered by controls.

Attesting accepts the assumption, which is a judgment about the world the platform cannot check: a program may do it only under a workspace delegation rule for assumption_accepted; otherwise the call is refused with HTTP 403 and an escalation_id and the attestation is parked for a person. An attestation holds for the text it was given for: editing the assumption's description retires it, and the assumption must be accepted again. An assumption need not be linked to an objective to be accepted — one bound into a control's group (see strengthen_controls) counts only while it is accepted.

An attestation is a responsible party's claim, never a proof over every site: it can cover an existential clause of a control (its tier reads claimed) and never a for-all one, where only a sound witness counts. An attestation the platform mints from CI results is no stronger than the weakest assertion behind it. The exits for a universal objective that cannot be proven are a risk acceptance or a not-applicable disposition.

list_attestationsA

List an assumption's attestation history. Read-only; no side effects.

Returns the chronological record of attestation events recorded against the assumption (each with its actor, timestamp, and status/expiry as recorded), so you can trace why the assumption is currently attested, expired, or never attested. An assumption only mitigates its control objectives while it is active AND currently attested, so use this to diagnose coverage that depends on an attestation.

To record a new attestation use submit_attestation; for the assumption's current fields (status, description) use get_entity (entity_type="assumption").

get_control_assumption_groupsA

Get the current assumption group structure for a control.

Assumption groups define alternative sets of external claims that can satisfy a control:

  • Within a group: AND — all assumptions must be active and attested

  • Across groups: OR — any complete group is sufficient to mark the control as externally handled

set_control_assumption_groupsA

Declaratively set the assumption group structure for a control.

Replaces all assumption group assignments for this control. Each group is a set of assumption IDs that together externally handle the control; any one group being fully active+attested is sufficient.

  • Within a group: AND — all referenced assumptions must be active and attested for the group to count as complete

  • Across groups: OR — any one complete group marks the control as externally handled for mitigation purposes

To clear all assumption groups (revert to "not externally handled"), pass an empty JSON object: {}.

AI relevance gate (per group, no override): Each non-empty proposed group is evaluated independently. The behavior depends on how many groups pass:

  • All groups accepted → 200 success, structure persisted as submitted.

  • Some groups accepted (partial): the accepted groups ARE persisted (runtime OR-semantics activate immediately), the rejected groups are NOT saved, the call raises with HTTP 422 detailing both persisted_groups and rejected_groups (with per-group reasoning). Resubmit only the rejected groups with assumptions that cover the control, or sharpen those assumptions' descriptions.

  • All groups rejected: existing groups on this control are re-evaluated through the same gate. Relevant existing groups are preserved; irrelevant existing groups are dropped (assumptions themselves remain in the model — only this control's linkage is removed). The call raises with HTTP 422 detailing what was persisted, what was rejected, and what existing was dropped.

  • Empty submission ({}): clears all groups, no evaluation.

There is no force-override. To get a group accepted, choose assumptions whose descriptions actually cover the control or refine an assumption's description so coverage is explicit.

convert_assumption_to_controlsA

Convert a violated or retired assumption to controls. Mutating.

Retires the assumption's CO linkage and, when any CO it covered is left with no control, proposes a control build for those COs: the result's proposal (null when nothing is owed) is started with start_control_build after review, and authors the controls then. Nothing is authored by this call. Use when an assumption is no longer valid and the system owner needs to implement controls instead.

Side effect on control-level linkage: this assumption is also removed from every assumption_groups entry on every control that referenced it. Any group left empty by the removal is dropped; a control's status is not changed, and a control left with no group is no longer backed by an assumption. Underlying assumptions are not deleted — only the linkages.

generate_functional_objectivesA

Derive capabilities, functional objectives, and the concrete tests to implement from the feature spec.

Capabilities are the behaviours the feature must deliver; each is walked against a taxonomy of operating conditions (nominal, boundary, invalid input, dependency failure, concurrency, …) to produce testable Given-When-Then objectives — and then a concrete, implementable test is specified for each objective (so the agent implements the tests rather than deciding what to test). Requires a Pro plan. Billable — may take some time. refresh=true re-derives from scratch, replacing prior generated (not manually authored) capabilities, objectives, and tests.

get_capabilitiesA

A model's capabilities (behaviours the feature must deliver), or one of them. Read-only.

Without capability_id: every capability, each with its id, name/description and a summary of its component and asset bindings. With it: that capability with its bound components and assets.

get_functional_coverageA

A model's functional coverage report, or just its actionable gaps. Read-only.

By default the whole picture: per-objective state (verified / covered / failing / untested), the Capabilities × Conditions matrix, and the applicable / missing-objective / not-applicable cell accounting. With gaps_only=True only what needs action: applicable conditions with no objective yet, and objectives that are failing or have no passing test.

add_functional_testA

Hand-author a single functional test and map it to one or more objectives. Mutating.

Generation (generate_functional_objectives) already specifies the tests to implement, so use this only to register an extra test that generation did not produce; a manually-added test survives regeneration/refresh. For bulk-registering tests that already exist in your codebase, use import_functional_tests instead. This records the test at the status you claim — it does not run or verify anything; CI verification happens only when you attach TEST_EXISTS/TEST_ATTESTED evidence via submit_assertions with functional_test_id.

import_functional_testsA

Register tests that already exist in your codebase against a model's functional objectives, so tests you already have count toward functional conformance — not only Mipiti-specified tests. Mutating (bulk).

Scan the repo's test suite and pass the tests here. Optionally associate each with the objective ids it covers (from get_functional_objectives); the platform verifies each association is applicable before accepting it and returns any it rejected under rejected_mappings. A test with no (or a rejected) association is still imported, unmapped, so it can be associated later (see suggest_functional_test_mappings / associate_functional_test). For a single hand-authored test, use add_functional_test instead.

suggest_functional_test_mappingsA

Suggest which functional objectives each imported test likely covers.

For unmapped tests (imported without an association, or added without objective ids), this proposes objective mappings so you can review and apply them with associate_functional_test. It only suggests — nothing is associated until you confirm.

associate_functional_testA

Associate a functional test with one or more functional objectives.

Use this after suggest_functional_test_mappings, or to hand-map a test to the objectives it covers. The platform verifies each association is applicable before accepting it and returns any it declined under rejected_mappings.

get_functional_satisfaction_groupsA

Read the satisfaction-group structure for a functional objective. Read-only; no side effects.

A satisfaction group is a set of functional tests that together satisfy the objective: AND within a group (every test in the group must be verified), OR across groups (any one complete group satisfies the objective). Returns the current numbered groups plus any tests associated with the objective but not placed in a group.

Use before set_functional_satisfaction_groups to see the current structure, or to trace why an objective is / isn't satisfied. This is the functional analog of get_control_assumption_groups / get_mitigation_groups.

set_functional_satisfaction_groupsA

Declaratively set (replace) a functional objective's satisfaction groups. Mutating.

Replaces the objective's group structure wholesale. Each group is a set of functional tests that together satisfy the objective (AND within a group); the objective counts as satisfied when any one complete group has all its tests verified (OR across groups). Tests you want to keep associated with the objective but outside any group go in ungrouped. Unlike set_control_assumption_groups, there is no AI relevance gate — the structure you submit is applied as-is. Read the current state first with get_functional_satisfaction_groups.

get_cwe_catalogA

Get the platform's CWE reference catalog status.

Returns {enabled, current_version, entry_count, versions}. When CWE classification is not turned on for this instance, enabled is false and the rest is empty — this is a normal informational response, not an error.

get_model_cwe_tagsA

List CWE weakness classifications tagged onto a model's control objectives.

Each tag's name/description are resolved from the platform's CWE catalog, never model-authored. A tag whose CWE id has since been deprecated, redefined, or removed by MITRE carries a stale reason (missing / deprecated / changed) — re-run classify_model_cwe to refresh it. 404s if CWE classification is not enabled on this instance.

classify_model_cweA

Classify a model's control objectives against the platform CWE catalog.

Grounded: the model may only select from the catalog's current-version candidate ids, and every returned id is re-validated against the catalog before storage — a hallucinated or deprecated id is never persisted. Skips control objectives already tagged at the catalog's current version unless force is set. Returns a summary: {status, catalog_version, cos, classified, tags_written, skipped}. 404s if CWE classification is not enabled on this instance.

get_entityA

Get a single entity of any core type by ID. Read-only.

Dispatches on entity_type to the per-type read and returns that type's native record as-is (not wrapped in an array):

  • asset — the asset's typed fields. Soft-deleted assets carry deleted: true; the caller decides whether to surface them. entity_id e.g. A-01.

  • attacker — the attacker with its factor decomposition, its surface_extent (unset / point / whole) and surface_extent_source, which says whether a person attested it. Soft-deleted attackers carry deleted: true. entity_id e.g. T-03.

  • component — the component. Speculative components (repo_url="") are returned as-is: the empty repo IS the lifecycle state, not an error. entity_id e.g. CMP-01.

  • trust_boundary — the boundary incl. its passes set (closed-vocabulary subset of {Network, Adjacent, Local, Physical}). entity_id e.g. TB-Net.

  • assumption — the assumption with its override applied (mirrors list_assumptions' merge for one entity: typed fields, the structured exclusion predicate when present, and the override layer — status / justification / linked CO IDs). Soft-deleted assumptions carry deleted: true. entity_id e.g. AS-01.

remove_entityA

Soft-delete a single entity of any core type. Mutating: creates a new model version. Reversible with restore_entity using the same entity_type — the entity's ID is preserved (never reused) so a restore reinstates the same ID and all its links. To change an entity's fields instead of removing it, use the typed edit_* tool.

Dispatches on entity_type. Per-type consequence (all derived at read time; nothing is hard-destroyed):

  • asset — the asset's (asset × attacker) CO pairs are tombstoned, orphaning any controls mapped to them.

  • attacker — control objectives anchored to this attacker are tombstoned; controls left with no live anchor become orphaned.

  • component — controls scoped to this component have their component_id cleared (the controls themselves are kept) and the component's trust-boundary contribution to asset reachability is withdrawn.

  • trust_boundary — reachability widens: attacker vectors the boundary was filtering now pass freely and its sealed/isolation claim is dropped, so CO reachability verdicts past it can flip toward reachable/indeterminate.

  • assumption — marked deleted (kept for the audit trail); its CO links are cleared and its attestations retired; controls whose assumption_groups name it keep their groups, which credit nothing through it while it is deleted.

restore_entityA

Un-soft-delete a single entity of any core type, reversing a prior remove_entity. Mutating: creates a new model version. Only affects an entity that is currently soft-deleted.

Dispatches on entity_type. Per-type effect:

  • asset — revives the asset's tombstoned (asset × attacker) COs with their original IDs, un-orphaning any linked controls.

  • attacker — reinstates the attacker under its original ID, revives the COs tombstoned when it was removed, and un-orphans any controls that were anchored to it.

  • component — reinstates the component under its original ID, restoring its trust-boundary contribution to asset reachability.

  • trust_boundary — reinstates the boundary: the reachability it filtered re-narrows and its sealed/isolation claim is restored, so CO reachability verdicts past it can flip back toward unreachable.

  • assumption — returns the assumption to active status; controls whose assumption_groups referenced it keep their group structure intact. Its CO links are not restored (set them again with edit_assumption), and re-attestation is required before it counts anywhere it is linked or grouped.

Returns the entity-change result: {"model": <ThreatModel>, "controls_carried", "controls_orphaned", "orphaned_control_ids", ...}.

get_risk_viewA

Prioritized Risk View — one row per live Control Objective — at a chosen scope. Read-only; no side effects.

scope selects the aggregation boundary and how scope_id is interpreted:

  • "model" — a single threat model (scope_id = model id). One row per live CO with derived risk tier, asset impact, attacker likelihood, control coverage counts (coverage_ratio), and open-finding count (open_findings). Tombstoned COs are excluded; pair with get_threat_model if historical context is needed. Use to triage which COs need attention on one model — a single call ranks the work, no per-CO fan-out.

  • "tag" — every member model of a tag (scope_id = tag id). The same row shape with model_id and model_title added per row, so rows can be grouped by source model without an extra lookup, and delegation-aware (delegation_mitigated / delegating_controls): a CO mitigated via a verified cross-model delegation reads as covered, consistent with each model's own assessment. Use for a product, portfolio or audit-scope posture rollup.

get_compliance_reportA

Compliance gap-analysis report for one framework at a chosen scope. Read-only; no side effects. Requires PRO tier.

Evaluates every framework requirement against the mapped controls in scope and classifies each as covered, partial, uncovered, unmapped, or excluded, then returns coverage counts plus per-requirement rows. The framework must first be activated at the same scope via select_compliance_frameworks (with the matching scope), otherwise there is nothing to report on.

scope selects the boundary and how scope_id is read:

  • "model" — a single threat model (scope_id = model id).

  • "tag" — rolled up across every member model of a tag (scope_id = tag id).

Filtering / pagination:

  • level — level filter for level-aware frameworks; returns only requirements at or below this level (e.g. 1 for L1 only). Omit (or 0) for all levels. Honored for all scopes.

  • status — one of "covered", "partial", "uncovered", "unmapped", "excluded"; empty = all statuses. Model scope only.

  • offset / limit — per-requirement row pagination; offset skips the first N rows, limit caps rows returned (0 = no explicit limit). Model scope only.

A tag report is neither paginated nor status-filtered; passing status, offset, or limit with scope="tag" raises an error rather than silently returning unfiltered rows.

select_compliance_frameworksA

Select (activate) compliance frameworks at a chosen scope. Requires PRO tier. Mutating.

Discover valid ids with list_compliance_frameworks (or add a custom one via import_compliance_framework); view the resulting gap analysis with get_compliance_report at the same scope. Re-calling replaces the scope's framework selection.

scope selects the target and how scope_id is read:

  • "model" — a single threat model (scope_id = model id). Activating a framework also kicks off background auto-remediation: it auto-maps existing controls to requirements, excludes non-applicable requirements by taxonomy, and suggests/applies new entities for the remaining gaps. The response includes auto_remediate_jobs, which run and complete on their own; re-trigger later with auto_remediate_compliance if the model changes.

  • "tag" — a tag (scope_id = tag id). Records the frameworks against the tag AND propagates them to every member model, and to every model added to the tag later, making the tag a compliance scope (e.g. an audit boundary) spanning several models.

export_reportA

Export a threat model or a tag as a downloadable document. Read-only; no side effects on the source.

scope selects what is exported and how scope_id is read; format selects the representation:

  • scope="model" (scope_id = model id) supports format ∈ {csv, pdf, html, archive}:

    • csv — the model's current state rendered as CSV; returned inline as UTF-8 text in content.

    • pdf / html — rendered document returned base64-encoded in content_b64 (with content_type). Runs as a server-side job; progress is reported automatically while it completes, which may take time for large models.

    • archive — the self-contained, independently-verifiable JSON audit bundle of the model's current state: its latest version and controls, live assertions (with Tier 1 / Tier 2 verdicts and attested flags) and the runs behind them, open findings and those a person closed, risk acceptances and other decisions in force, assumption overrides, attestations, and instance sufficiency signatures; each control's per-clause evidence basis travels with it. Earlier versions, activity and chat are not in it. Those verdicts are the origin's record of what it claimed, which is what a third party checks against the signatures; an importing workspace credits what its own verification establishes (see import_threat_model_archive). Returned as {..., "envelope": <dict>}; feed the envelope to import_threat_model_archive to restore it into any workspace. Model scope only.

  • scope="tag" (scope_id = tag id) supports only format="html": the signed auditor report, every member model's report after the reliance edges among the members (each with its status and whether it credits its objective), in one HTML document returned inline in content. csv, pdf, and archive are rejected for tag scope.

list_groupsA

List the workspace's tags. Read-only; no side effects.

A tag is a named, overlapping grouping of threat models, for audit scopes, products, ad-hoc selections or portfolios. A model may carry many tags, and a tag never affects posture or credit. Returns {"tags": [...]}, each with id, name, description and model_ids.

Discover tag IDs here before the tag risk, compliance, dependency or export tools, or before adding/removing members. For a single model's tags use list_model_groups.

create_groupA

Create a tag, optionally with its first members. Mutating.

A tag groups models for viewing and reporting without asserting any relationship between them and without moving credit. Names are unique within the workspace (409 on a clash). Every model named in model_ids must be one the caller can access in this workspace.

add_model_to_groupA

Add a threat model to a tag. Mutating.

Links the model into the tag without moving or copying it; the model stays independently editable, and may belong to many tags. The model takes on the compliance frameworks the tag selected, marked as the tag's, so removing a framework from the tag removes it from the model again; a framework the model selected itself is left as it is.

get_groupA

Get one tag by ID, with its member model ids. Read-only; no side effects.

Returns {id, workspace_id, name, description, created_at, model_ids}. Discover tag IDs with list_groups.

delete_groupB

Delete a tag. Mutating; the member models are not affected.

The tag's framework selections, requirement exclusions and relevance data are deleted with it. Frameworks it propagated to its members stay selected on them.

remove_model_from_groupA

Remove a model from a tag. Mutating; the model itself is not deleted.

The frameworks the tag propagated to the model stay selected on it.

list_model_groupsA

List the tags a given model belongs to. Read-only; no side effects.

A model may belong to many tags. Returns {model_id, tags: [...]}. Use list_groups for every tag in the workspace.

get_reachability_verdictsA

Per-CO reachability verdicts, over this model alone or the composed tree. Read-only; derived each time, never stored.

composed=False (default): derived from this model's own structure (components, asset.component_ids, trust_boundary.passes, each attacker's trust_boundary_ids and vector, assumption exclusion predicates), deterministic, the derivation an auditor re-runs. co_id returns one verdict (404 if absent or tombstoned). Returns {model_id, model_version, verdicts: [{co_id, kind, reason, narration, boundary_id?, assumption_id?}]}; kind is reachable, unreachable or indeterminate.

composed=True: the same derivation over the model with everything it inherits from its ancestors, for a child on the composition tree. Paginated (page, page_size); kind_filter keeps one kind; co_id is ignored. Returns {model_id, flag_enabled, verdicts: [{co_qid, asset_qid, attacker_qid, kind, reason}], total, page, page_size}, empty with flag_enabled: false where composition is not available.

An indeterminate verdict names the missing structure: attacker_unpositioned (edit_attacker with trust_boundary_ids), asset_unbounded (assign_to_components(target_type="asset")), no_shared_boundary (reposition the attacker, rescope the asset, or an add_assumption exclusion), missing_entity (restore it, or remove the CO). model_coherence_report presents the same gaps as findings.

recompute_verdictsA

Estimate, force, or retry a model's verdict evaluation.

  • mode="quote" (default): read-only. The cost of a recompute, enqueueing nothing: {estimated_credits, computed_at, rate_version, informational, total_enqueueable, already_evaluated, governor}. Subjects already carrying a verdict cost nothing, so it is an upper bound. Show the operator this number before recomputing: on a large model it runs to thousands of credits.

  • mode="recompute": mutating. Queues a fresh evaluation of every control's coverage verdict and every live objective's group-sufficiency verdict, bypassing quiet-period batching; usage is metered as it runs. Returns {model_id, model_version, enqueued_coverage, enqueued_group_sufficiency, total_enqueued, estimated_credits, quote, governor}.

  • mode="retry_parked": re-runs only the verdicts a transient failure (outage, exhausted credits, timeout) parked, of every kind, including per-control sufficiency and coherence; nothing else is touched. Returns {model_id, model_version, retried_slots, governor}.

Work runs in the background; when governor.exhausted it is queued and resumes at governor.resets_at, never dropped.

A recompute evaluates COVERAGE and GROUP SUFFICIENCY only. A control at partially_verified, or coherence_status: "pending" on an assertion, is not a reason to recompute: that verdict is computed on assertion write and read with get_sufficiency. Recompute when control-to-CO mappings look wrong (get_verdict_divergence). One objective awaiting judgement is judge_objective.

judge_objectiveA

Have one control objective's mitigation group judged. Mutating — queues background work and may consume credits.

Use this for an objective whose risk_reason is awaiting_judgement: it has a built mitigation group and nothing has decided whether that group covers the objective — never evaluated, evaluated against inputs that have since changed, or the answer parked. The objective is not short of controls, so generating or implementing more will not move it; what is missing is the judgement.

This is not a repair. The judgement can come back insufficient, which moves the objective to coverage_gap / insufficient_by_design and names real work. That is the tool doing its job: it replaces "nobody has looked" with an answer, and the answer may be no.

Scoped to ONE objective, which is the difference that matters against recompute_verdicts: that tool force-enqueues every control's coverage verdict AND every live objective's group-sufficiency verdict, which on a large model runs to thousands of credits. This queues a single judgement. Any credits it consumes are metered at actuals as the work runs, like every other metered call — the account's usage is visible in its billing panel before and after.

Judging runs in the BACKGROUND; the call returns as soon as the work is queued. Re-read get_mitigation_groups (or assess_model / get_risk_view) shortly after to see the objective's new state. Calling again while a judgement is already queued is harmless and does not queue a second one.

A refusal comes back as data rather than an error, so it can be relayed:

  • {queued: false, http_status: 409, ...} — controls are still being generated for this model. Poll get_control_generation_status until terminal, then call again.

  • {queued: false, http_status: 503, ...} — judging is unavailable on this deployment.

  • {queued: false, http_status: 402, code, message} — the balance this workspace bills to cannot cover the judgement.

list_reconciliation_candidatesA

Entities a child model authored that look like ones it inherits. Read-only.

Use on a child model in a recursive tree to find duplicates before they distort coverage; decide each with decide_reconciliation_candidate.

  • disposition="active" (default): the open queue, paginated (page, page_size). {model_id, flag_enabled, total, tiers: {certain, heuristic}, page, page_size, candidates: [{kind, own_qid, inherited_qid, tier, reasons}]}. Tier certain is a deterministic match, safe to apply; heuristic is a fuzzy name/description match that needs review. Rejected pairs are left out.

  • disposition="rejected": the pairs recorded as NOT duplicates, oldest first and not paginated: {model_id, flag_enabled, rejections: [{id, model_id, kind, own_qid, inherited_qid, rejected_by, rejected_at}]}. An id is what an unreject names.

Where composition is not available both come back empty with flag_enabled: false.

undo_composition_eventA

Preview, and on confirmation apply, the undo of a lift or split.

event_type is lift or split; event_id is the forward lift_applied / split_applied activity event's id, or the lift_id / split_id in its payload. model_id is the model the event was raised on (another model's event is 404).

By default (dry_run=True) it is read-only: {plan, refusal}, exactly one non-null. plan lists the inverse operations an undo would commit (lift: tombstone the LCA entity, restore the source copies, rewrite CO references; split: restore at the ancestor, tombstone the target copies). refusal lists why it cannot: state has moved since the event (assertions submitted on the entity, objectives referencing it, an edit). Show it to the operator.

With dry_run=False it is mutating, after explicit confirmation: the divergence check runs again (409 with detail.refusal.reasons when it refuses), the inverse is persisted across every affected model, and a lift_undone / split_undone event citing original_event_id is recorded. Returns {undone_event_id, original_event_id, applied_state_ops, models}; models is {lca_model, source_descendant_models} for a lift and {ancestor_model, descendant_models} for a split.

503 where composition is not available.

get_functional_objectivesA

List a model's functional objectives, or fetch one by id. Read-only; no side effects.

A functional objective is a Capability × Condition test plan expressed as a Given-When-Then statement. functional_objective_id selects the behaviour:

  • omitted / empty string -> list every functional objective for the model (the full functional test plan).

  • a functional-objective id -> return just that one objective's detail, including its capability, condition, Given-When-Then statement, and current test state.

For pass/fail coverage state across all objectives use get_functional_coverage; for the actionable gaps pass it gaps_only=True.

get_controlsA

A model's controls, or one control. Read-only.

Without control_id: {controls, total, returned}, the published set, filtered by status, co_id and component_id and paged by offset / limit. Orphaned controls (every mapped CO tombstoned) are left out unless include_orphaned=True, soft-deleted ones unless include_deleted=True. summary_only=True returns only id, description, status, verification_status, assertion_count, co_ids, assumption_groups and attestation_dependency. An empty list on a new model means its proposed build was never started (get_control_generation_status); while a build holds the model the list is the last published set, with a building marker.

With control_id: that control, with an orphaned flag; version reads it as of a model version (0 = latest).

An objective id on a control is an ATTACHMENT, not credit: only a member of a required mitigation group earns any, and a control that is defense-in-depth everywhere can be implemented and verified without moving an objective. Read get_mitigation_groups before evidence work.

status is the operator's (not_implemented / implemented / verified); verification_status is the EVIDENCE: verified (both tiers pass and the assertions cover the whole description), partially_verified (a tier failed, clauses are unproven, or an attestation EXPIRED — get_sufficiency says which; the fix is more assertions or a narrower description, never a recompute), pending, unverified (none submitted). A for-all clause is credited only by a sound type bound with covers; get_control_work_order lists what each clause needs. status="implemented" finds what still needs evidence. Only this model's own controls are listed; inherited ones are counted by assess_model.

get_control_objectivesA

Get the control objective matrix, or one control objective. Read-only.

Two modes, selected by whether co_id is set:

  • Matrix mode (co_id omitted) — returns the model's COs, each with references to the controls that cover it. By default returns a compact summary (total count only); pass offset/limit to page through full CO records.

  • Single mode (co_id set) — returns that one CO's typed fields, the IDs of any controls that map to it, and the deterministic reachability verdict (the structural derivation that backs any reach claim on the CO). Tombstoned COs (removed: true) are returned with the flag set; the verdict is omitted because reach state is frozen at the removal version. offset/limit are ignored in this mode.

For pass/fail assurance scoring use assess_model.

assign_to_componentsA

Replace an asset's or a control's component scope. Mutating.

Components are the canonical bridge between security architecture (trust boundaries) and code organization (repos). target_type selects what is being scoped:

  • "control" — replace a control's component scope. A control scoped to one or more components is visible to coding agents working in those repos (matched via Component.repo_url + Component.path); an unscoped control is visible everywhere. Use when wiring a previously unscoped control to the component(s) that implement it, adding a second component to a cross-cutting control (e.g. "all microservices enforce JWT validation"), or correcting a wrong assignment. target_id is the control ID (e.g. "CTRL-03").

  • "asset" — replace an asset's component scope. Linking assets to components flows boundary context into reachability derivation without giving Asset its own trust_boundary_ids. Multi-component is the right shape for a multi-instance asset (e.g., a session token on client + cache + DB — each component handles a distinct instance). target_id is the asset ID (e.g. "A1").

Both variants are mechanical / non-AI-gated and validate only that every referenced component exists on the model.

get_scan_promptA

Get guidance prompts for scanning a codebase. Read-only; no side effects.

kind selects which scan brief to return:

  • "security" (default) — prompts telling the agent what evidence to look for per security control; only NOT_IMPLEMENTED controls are included (implemented ones need no scan). Use this to drive a gap-discovery pass, then record what is missing with submit_findings and what is present with submit_assertions. Pass control_id to scope the prompt to one control; empty (default) returns prompts for all not-yet-implemented controls.

  • "functional" — the agent brief for implementing functional-conformance tests. Generation specifies the functional tests, so for each test not yet verified this returns its implementation brief and the objectives it proves; it also reports objectives_without_tests (regenerate or add a test) and missing_objectives (applicable conditions with no objective yet). Drive test implementation from it, then call submit_assertions (functional_test_id) with TEST_EXISTS + TEST_ATTESTED assertions so CI verifies each test; read the resulting pass/fail state via get_functional_coverage. control_id does not apply to this kind and is ignored.

set_control_objective_calA

Set the per-CO ISO/SAE 21434 Cybersecurity Assurance Level (CAL).

CAL is a 1-4 grade on each individual control objective that expresses how much assurance the control program owes for that specific objective. It lives on the control_objectives identity side-table — writes do NOT create a new threat-model version, and the value survives soft-delete + revival of the CO.

Pass cal=None (or omit it) to clear the value.

revalidate_entity_qualityA

Re-run quality validation on a threat model's existing assets and attackers, as if they were freshly generated. A fast first-pass check judges every entity; only the ones it flags get a deeper review that confirms them, sharpens their wording, or flags them for you.

Use this to apply validation improvements to an already-generated model, or to clear stale quality warnings — without regenerating the whole model (which would destroy controls, assertions, and components). It is non-destructive: an entity that should be removed is left in place with a quality warning rather than deleted, so no control objective loses its asset or attacker anchor. It creates no new model version: the re-validation is queued and runs in the background, and the refreshed warnings appear on the next read of the model.

May consume credits for the entities that need the deeper review; a model already in good shape costs nothing. Returns at once with {"accepted": true, "queued": <entities queued>, "model": {...}}, where model is the model as it stands before the re-validation lands.

auto_remediate_complianceA

Automatically close compliance gaps for a framework. Requires PRO tier.

Three-phase loop: (1) auto-map existing controls to unmapped requirements, (2) exclude requirements for non-applicable taxonomy primitives, (3) suggest and apply new assets/attackers for remaining gaps.

Phase (3) routes every proposal whose name matches a soft-deleted asset/attacker through the same restore-candidate LLM gate add_asset uses, so reanimating a previously removed entity reinstates its stable ID and every CO tombstone + control tied to it (rather than spawning a duplicate fresh ID). The response distinguishes assets_added / attackers_added (genuinely new) from assets_restored / attackers_restored (revived soft- deletes) and lists restored_asset_ids / restored_attacker_ids. Proposals the gate classified as similar (or that fail-closed on an unavailable / malformed gate response) appear under skipped with a per-entry reason — the operator decides whether to restore manually or rephrase.

Converges automatically: stops when fully covered or when no further progress can be made.

This runs automatically when a framework is selected, but can be re-triggered manually if the model changes.

get_control_work_orderB

The work order for one control: the ticket to read BEFORE implementing it. Read-only.

Returns {model_id, model_version, control, objectives, max_tier, scan_brief, assertion_contract, acceptance_criteria, required_evidence, steps, reconcile_rules, delegation, open_proposals, provenance}: where to look, what counts as proof (assertion_contract: the types by soundness class, the evidence and universal_rule, the sound_types the platform takes, what to submit with, when the control counts as verified), what to do when the code disagrees with the model, what this agent may decide alone (delegation), and whether code or description is authoritative (provenance).

Where the order names a required class for a clause, required_evidence holds one entry per such clause: its clause, the clause_id to put in covers, its quantifier, the required_class, what is missing, and a suggested_submission skeleton. The skeleton is a fill-in, not a submission: replace its <...> placeholders; one left unfilled is refused, by this client and again by the platform, since it would record a claim nothing backs. A for-all clause takes [by_construction, sound_over_approximation]: prefer typed_boundary (the type the sinks accept, its constructors), else sink_default_deny (the sinks, the safe forms, a reviewed allowlist). When the bound evidence is the wrong CLASS, missing says so. acceptance_criteria is GENERATED from those entries, so a clause that must hold at every site reads as such and no number of tests closes it.

A type's soundness is the class to branch on; behavioral is a compatibility field for readers written before the classes.

reconcile_modelA

Reconcile a threat model with the code it describes. Call this after reading the code and before (or instead of) editing the model by hand: report what changed and what you observed, and the platform decides the consequence of each observation. Mutating only where the platform applies an observation (see below).

Two inputs, both optional:

  • changed_paths: the file paths that changed since the model's recorded commit. For a code-derived model compute them with git diff --name-only <commit_sha>..HEAD (the commit_sha from the model's provenance). The platform maps them onto components and reports which components changed, which paths no component claims, and whether a refresh is recommended.

  • observations: what you saw in the code that the model does not say. Each observation lands in one of four buckets by kind:

    • mechanism_named - the control's mechanism exists under another name (subject_id = control id). Follow up with refine_control using the codebase_findings returned in refine_suggested.

    • component_present - the code has a component the model lacks; include a proposal ({name, repo_url?, path?, trust_boundary_ids?}).

    • component_absent - a modelled component has no code (subject_id = component id).

    • forbidden_behavior - the code does something the model rules out (subject_id = control id, or empty).

The platform decides the consequence. Proposals are never applied on the agent's word, with one exception: a component change on a code-derived model (provenance kind="code") is applied immediately and queued for a person's review as applied_pending_review. Every other proposal waits for decide_proposal. Forbidden behaviors become findings. Observations the platform could not use come back in ignored with the reason.

create_proposalA

Raise a proposal to change a model's scope or design. Call this when the code or your analysis says the model should gain or lose a component, or that an attacker position or asset should be removed by design; do not edit the model directly for those changes. Mutating: persists a proposal record.

A proposal is a change of scope or design. Raising one is not deciding it: a person (or an agent under a delegation rule that names the decision) decides it with decide_proposal. Design changes are never applied automatically. Poll list_proposals for the outcome.

list_proposalsA

List proposals and escalations on a model. Call this to poll the outcome of a proposal you raised, or of a judgment you were refused. Read-only; no side effects.

Statuses: proposed and applied_pending_review are open; accepted, rejected, reverted, superseded are closed. Kinds include add_component, remove_component, design_change, assumption, and decision_request: an escalation of a judgment this agent was refused. A 403 from update_finding, create_risk_acceptance, submit_attestation or decide_proposal carries an escalation_id; that escalation appears here as a decision_request. Poll it here until a person resolves it; do not retry the refused call.

decide_proposalA

Accept or reject a proposal. Call this only when the workspace's delegation policy names this decision for this agent at the proposal's tier (the delegation block of get_control_work_order says what you may decide). Mutating: closes the proposal and applies an accepted change.

This is a judgment. The call is refused with HTTP 403 and an escalation_id unless the delegation policy permits it; the refusal parks the decision for a person as a decision_request. Do not retry a refusal: report the escalation_id, poll list_proposals for the outcome, and continue other work.

Accepting an assumption proposal accepts the assumption: it is created if new, attested until expires_at and bound into the group that waited on it, which is then judged again. That is its own decision (assumption_accepted); a rule delegating proposal acceptance does not cover it. Rejecting one records the precondition as rejected for that objective, so it is not proposed again, and the objective keeps its gap.

get_design_leverageA

Rank what eliminating each attacker position or asset BY DESIGN would remove from the matrix. Call this when deciding whether to change the design instead of implementing controls: it shows which single design change retires the most critical and high at-risk objectives. Read-only; no side effects.

Each row in ranked is an attacker or asset with the objectives its removal would take out of the matrix (objectives_removed, broken down by tier in removes), how many of those are currently at risk (removes_at_risk / removes_at_risk_by_tier), and the controls that would be retired. Rows are ranked by critical, then high, at-risk objectives removed. design_move (a concrete change of design that would eliminate the row) is filled only when include_design_moves is true. To act on a row, raise a design_change proposal with create_proposal; never apply a design change yourself.

list_decisionsA

List the decision ledger of a model: every judgment recorded on it (finding dismissed or remediated, risk accepted, not-applicable declared, proposal accepted / rejected / reverted, assumption accepted, escalation resolved), newest first, with who made it and whether it was within the workspace's delegation policy. Read-only; no side effects.

Call this BEFORE raising a proposal or asking for a judgment, so you do not propose what a person rejected or ask again for what was already decided.

The ledger is append-only. There is no tool that edits it; to undo an accepted proposal, revert or re-decide, never edit the record. Rows with outcome == "refused" are judgments a program was refused; their escalation, if any, is in list_proposals. agent is null for a person's decision.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

B3.2/5.0

Scored across 128 tools

Disambiguation3/5

Descriptions are unusually thorough and explicitly cross-reference the right sibling (e.g. judge_objective vs judge_objectives vs judge_imported_controls vs recompute_verdicts; refine_control vs remap_control vs regenerate_controls vs apply_control_changeset), which prevents most misselection. However, there are large overlapping clusters (start/pause/resume/discard control builds; get_controls/get_control_objectives/get_verification_report/get_sufficiency) and a real terminology collision where 'groups' means tags in list_groups/get_group/create_group while mitigation/assumption/satisfaction groups mean something else entirely. An agent can still pick correctly by reading carefully, but the boundaries are genuinely crowded.

Naming Consistency4/5

Nearly everything follows a predictable snake_case verb_noun pattern (add_asset, edit_attacker, remove_entity, list_findings, get_threat_model, submit_assertions). A few stative/odd names (assess_model, recompute_verdicts, judge_objective) and the decision to name tag tools 'group' while calling them tags in prose are minor deviations, but the style is internally consistent throughout.

Tool Count1/5

128 tools is an extreme count that far exceeds what an agent can reliably select from, and the surface contains visible redundancy (three judge_* tools, four control-build lifecycle tools, multiple verdict/report readers). Even granting the platform spans threat modeling, assurance, compliance, functional testing and composition, the number is a mismatch for practical tool routing.

Completeness5/5

Coverage is essentially exhaustive: full CRUD/lifecycle for models and every core entity (assets, attackers, components, trust boundaries, assumptions), control build/undo/versioning, assertions and sufficiency, findings, mitigation and satisfaction groups, risk acceptances and dispositions, compliance frameworks, functional tests, proposals/decisions, and composition lift/split. No obvious domain operation is missing, and import/export/archive round-trips close the loop.

Maintenance

ActivityActive
ResponsivenessNo issues