This is the second part of the MCP server guide. The first part — issuing an agent token, scopes, delegation, and connecting each client — is here: https://help.auditbadger.com/hc/auditbadger-help-center/articles/1775898378-mcp-server-connect-ai-assistants-to-humadroid
The tools
The server mounts 134 tools. You never need to memorise them — the assistant discovers them, and the server's own instructions tell it how they fit together — but here is the shape.
Reads (65)
Every read returns current state plus an observed version for each resource. Two different reads legitimately return two different observed versions for the same record — each one fingerprints its own projection, so list_risks, get_risk, and an operation companion each answer "has what this read shows changed?", not "has the row changed?". Only the versions returned by the operation companion (the operation_companion tool, or the operation's paired read) are valid in a proposal — every read envelope says so in capability.operation_companion_required_for_mutation. Hand those back when proposing, and if the record moved in between, the proposal is refused as stale instead of overwriting someone's work.
-
Orientation:
whoami,list_available_operations,describe_operations,operation_companion,get_project_posture -
Projects and controls:
list_projects/get_project,list_sections/get_section,list_controls/get_control(controls report their notes and whether notes and status are directly editable, so a register audit is one list call) -
Evidence:
list_evidence/get_evidence,list_collection_sources/get_collection_source -
Documents:
list_documents/get_document,list_document_assignments -
Risks:
list_risks/get_risk,get_risk_assessment_context,list_treatment_actions/get_treatment_action -
Checks:
list_checks/get_check,list_check_acceptances/get_check_acceptance -
Vendors:
list_vendors/get_vendor,list_assurance_reviews/get_assurance_review -
Business continuity:
list_business_continuity_processes/get_business_continuity_process, and the same pair forplansandexercises -
Assets:
list_physical_assets/get_physical_asset, the same fordigitalandinfrastructure, pluslist_asset_relationshipsandlist_asset_reference_data -
ISMS workbook:
get_isms_workbook,list_workbook_requirements/get_workbook_requirement,list_security_objectives/get_security_objective,list_management_reviews/get_management_review, andlist_management_review_input_candidates -
People:
list_people/get_person,list_employment_types/get_employment_type -
Reference vocabularies:
list_data_classification_levels(documents access),list_risk_scoring_methods/get_risk_scoring_method(risks access) -
Pipeline:
list_users,list_change_sets/get_change_set,list_staged_uploads/get_staged_upload
Control reads report whether in-app AI implementation notes still await a person's approval, without returning the private notes. ISMS requirement reads report the same pending/approved state for AI-prefilled narratives; the requirement's ordinary reviewed state identifies the approving person and time. After approval, a manual edit makes the current text human-authored and reports not_ai_generated; only an explicit AI generation or regeneration creates a new pending approval.
Proposal tools (57)
One focused tool per registered operation, named after it: risk_assess, control_create, document_publish, evidence_create, and so on. Each creates a one-item change set for review.
Every one takes the same envelope: an idempotency_key (reuse it to replay a submission safely rather than to make a second one), a title naming the proposal rather than the record, an optional rationale, the operation's own fields under params, the observed_versions the assistant read, and source_refs — where the information came from, [] for internal-only work. Project-scoped operations also require compliance_project_id.
42 of the 57 are bulk-safe: reversible bookkeeping a reviewer can approve together. The other 15 need explicit judgment and must be decided one at a time, because they are the moments where a person is asserting something to an auditor, or handing somebody authority over somebody else:
-
risk_assess,risk_update_treatment,risk_create_treatment_action -
control_update_implementation -
document_publish -
evidence_submit -
vendor_record_assurance_review -
check_acceptance_create -
asset_unlink_infrastructure -
isms_requirement_mark_reviewed -
isms_security_objective_create,isms_security_objective_update,isms_security_objective_record_review -
isms_management_review_complete -
people_person_set_manager
Two of those go further. document_publish and isms_management_review_complete are the only tools that reach an action the server otherwise refuses outright, so neither is ever eligible for the per-token delegation described in part 1: a signed-in person confirms the item, whatever the token is allowed to approve.
Lifecycle tools (3)
-
submit_change_set— propose several related changes as one set, with dependencies between items where one needs another to land first -
cancel_change_set— withdraw a proposal the assistant no longer stands behind -
approve_change_set— only usable when you have enabled the per-token delegation described in part 1
Upload tools (2)
request_upload_url and complete_staged_upload. See Attaching files.
Feedback (1)
send_feedback — the one tool that acts immediately. It emails product feedback — a bug, a missing capability, a place the assistant got stuck — to the AuditBadger team. It needs no scope, goes through no review, and changes nothing in your account.
Deprecated aliases (6)
The six write tools from the old endpoint keep their names here so a migrating prompt does not simply break: create_vendor, update_vendor, update_control, update_control_implementation, update_document_content, and record_vendor_assurance_review.
They keep the name but not the behaviour — each one proposes for review like everything else, and each one's description names its replacement. update_control_implementation is the awkward case: it used to take implementation status and notes together, and those are now two operations, one of them a judgment call. Send one contract's fields or the other's; a request mixing them is refused rather than silently split.
Move off the aliases. They exist to make the migration legible, not to be a permanent second name for the same thing.
A worked example
Suppose you ask an assistant: "Our access-control policy has been in draft since the review. Check whether the control it covers is still marked unimplemented, and fix the notes."
1. It reads.
list_documents finds the policy. get_control returns the control and its implementation notes. Then, because it intends to propose, the assistant calls operation_companion for control.update_implementation_notes on that control — the companion's observed_versions are the ones a proposal must carry (a foundation read's own observed_version fingerprints that read's projection and is refused as stale if submitted).
2. It proposes.
control_update_implementation_notes with:
-
idempotency_key: a fresh UUID -
title: "Refresh access-control notes after policy review" -
rationale: what it concluded and why -
params: the new notes -
observed_versions: the versions the operation companion returned -
source_refs: a reference to the policy document it read -
compliance_project_id: the project
The receipt comes back with a change_set_id, state proposed, and a review_path. Nothing has changed yet.
Note what it did not do: implementation status is control_update_implementation, an explicit-judgment operation. An assistant that decides a control is implemented is asserting a compliance fact, so that comes to you as its own decision even inside a bigger set.
3. You review.
The change set appears in your review queue, and you get a notification. The item shows the before and after, the assistant's rationale, and the source it cites. You approve it, reject it with a note, or leave it — proposals expire on their own within 14 days by default.
4. It polls.
get_change_set returns the state and, per item, an outcome code and a next_action telling the assistant what to do: await_review (nothing to do yet), await_apply (decided, queued), none (done, or permanently refused), repropose (read again and try once more — this is what a stale version gets), or retry.
A set ends in applied, partially_applied, rejected, failed, cancelled, or expired. Per item, the application state is applied or one of failed_precondition, failed_validation, failed_infrastructure, skipped_dependency_rejected, skipped_dependency_failed.
5. The audit log records it.
Account Settings, Security, Audit Logs. The entry carries the agent channel, the token, the change set, and you as the approver.
A second example: onboarding a new employment type
"We are taking on contractors. Create a contractor employment type, require the handbook of it, and move Ada onto it."
list_employment_types shows the type does not exist and list_documents finds the published handbook. Then three proposals, in order: people_employment_type_create with the name, people_employment_type_link_documents naming the handbook, and people_person_set_employment_type naming Ada and the new type. There is no compliance_project_id — these are account-scoped.
Approving the third one does what the settings screen does: Ada is asked to acknowledge every document her new employment type requires, and to sign every contract it carries, inside the same operation. The receipt tells you how many assignments that created. Withdrawing a requirement later, with people_employment_type_unlink_documents, does not take those obligations back — the tool says so in its output, because cancelling something somebody already owes is a separate decision a person makes on the web.
The assistant cannot invite Ada, change her role or email, set her joining or leaving date, deactivate her, or acknowledge a document on her behalf. Those have no tool at all.
Attaching files
An assistant can attach a screenshot or a PDF to evidence, or a SOC 2 report to a vendor assurance review. The flow is four steps:
-
request_upload_url— the assistant declares the filename, content type, byte size, and SHA-256 checksum. It gets back astaged_upload_id, asource_ref, and a presigned form POST target: aurl, a set offields, and anyheaders. The capability is valid for 5 minutes. -
POST the bytes to that URL as a multipart form, including the returned fields. The presign is pinned to the exact byte size and content type declared, so the file that arrives is the file that was described.
-
complete_staged_upload— AuditBadger verifies the checksum, sniffs the real content type from the bytes rather than trusting the name, neutralizes the content, and seals it. -
Bind it by passing
staged_upload_idson the proposal. Which operation an upload may be attached to is decided then, against that operation's own scopes.
Allowed types: PNG, JPEG, GIF, WebP, and PDF. Nothing else. This is stricter than ordinary evidence upload on purpose — every extra format is another parser. SVG is excluded despite being an image, because it is a document format that executes script.
Size: 10 MB per file, up to 10 uploads and 50 MB per change set.
Content neutralization happens on every file:
-
A PDF is refused if its bytes carry a name that brings active content — script, open or page actions, launch actions, embedded files, rich media, XFA forms — or encryption, which could hide any of those. You get
upload_active_content. Send a flattened PDF. -
An image is decoded and written back in the same format with every metadata block dropped: EXIF, ICC, XMP, comments, and anything appended after the image data. A rotated photo stays upright because orientation is applied before it is stripped; an animated image keeps its first frame. If re-encoding pushes it past 10 MB you get
upload_neutralized_too_large.
Uploads are not scanned for malware today. Neutralization is what stands in for it, and it cannot see inside a compressed PDF object stream.
Scopes: an upload capability needs at least one <module>:propose scope for a module with an operation that accepts attachments — evidence and vendor assurance. A vendors-only token can upload the report it attaches; a controls-only token cannot upload at all, and gets token_scope_missing.
Limits and errors
Rate limits
-
100 requests per minute per token. The bucket is keyed by a digest of the credential, never the credential itself.
-
150 bearer-carrying requests per minute per IP address, across all tokens. This is what stops someone rotating bearer values to earn a fresh budget per guess. A team behind one shared egress address can reach it without any single token exceeding its own limit.
-
20 requests per minute per IP for requests with no bearer header at all.
-
An IP that presents 30 rejected bearers within 10 minutes is banned for 30 minutes. A client still polling with a token you revoked can walk into this; fix the configuration and wait it out.
Exceeding a throttle returns 429 with a Retry-After header.
Authentication
Every rejection is the same 401 with agent_token_required. It does not tell you whether the subdomain exists, whether the token exists, or whether it was merely the wrong kind of credential — that is deliberate, so the endpoint cannot be used to enumerate accounts. A valid REST API token lands here too, and gets the same 401: it authenticates elsewhere but is not valid for this surface.
Check, in order: the subdomain in the URL, the token's expiry, whether it was revoked, whether your account still has API access enabled, and whether you are still an active member.
Maintenance pauses
If we pause the ingress for incident response, every request answers 503 headless_compliance_ingress_paused with retryable: true, a Retry-After: 60 header, and retry_after_seconds: 60 in the body. That tells a client to come back in a minute rather than re-authenticate or give up. Work already approved keeps applying.
Error codes
Errors are stable codes with a retryable flag and a next_action, so an assistant can act on them rather than parse prose. A refusal about the request itself also says where to look: item_position names the item (1-based), field is a JSON pointer into it (/params/owner_id, /observed_versions, /source_refs/2), and detail is one sentence about the rule it broke — so a 25-item set is fixed in one round rather than by bisection. Those three describe only what the assistant sent or what a read already returned to it; a domain refusal whose remedy is the assistant's — a subcontrol identifier that does not extend its parent, say — carries them too, and the rest stay a bare code. The ones you are most likely to see:
-
version_stale/version_required— the record moved, or the assistant did not send what it observed.next_actionisrepropose: read again and try once more. -
token_scope_missing— the token lacks the module scope for that operation. Rotate with wider scopes, or narrow the task. -
unauthorized— the sponsor cannot perform that action on that record. The agent inherits your permissions; it does not exceed them. -
idempotency_key_reused— the same key with different content. A replay is fine; a second, different proposal needs a new key. -
explicit_judgment_confirmation_required— delegated approval was asked to approve a judgment call. Open the review queue. -
proposal_limit_reached— 20 open sets per token, 100 per account. Decide some. -
control_cannot_hold_evidence— evidence was proposed on a criterion rather than a subcontrol beneath it. -
source_refs_invalid— provenance was missing or malformed;fieldnames the reference anddetailthe rule (httpsorurnwithout a query string, an explicitly zonedobserved_at). -
invalid_request— the request broke a structural rule;detailsays which: an unknown field, a wrong type,compliance_project_idpresent on an account-scoped set or missing on a project-scoped one, an item mixing scopes, a subcontrol identifier that is not the parent identifier plus a numeric suffix (CC1.1.4underCC1.1). -
headless_compliance_ingress_unavailable— the pause above, seen from inside a tool result.
Workload limits
A single change set holds at most 25 items, with dependency chains at most 5 deep and 10 wide. Each item may carry 10 source references and 256 KB of serialized content; a set may carry 50 references and 1 MB. Titles are capped at 200 characters, rationales and decision notes at 5,000. Lists page 50 at a time, 100 at most. Proposals expire 14 days after submission by default, and never later than 30 days.
The assistant can read all of these from whoami rather than discovering them by failing. whoami also reports whether self-approval is enabled on the token, and both whoami and describe_operations give each operation its scope_kind (whether a set needs compliance_project_id) and approval_policy (explicit_judgment items are decided one by one by a person, so a set containing one is never approved through approve_change_set).
Notifications and webhooks
Seven lifecycle events are published, and both channels use the same list, so a subscription and a notification can never disagree about what exists:
-
agent.change_set_proposed -
agent.change_set_decisions_finalized -
agent.change_set_applied -
agent.change_set_partially_applied -
agent.change_set_failed -
agent.change_set_cancelled -
agent.change_set_expired
Notifications reach reviewers by email by default, linking straight to the review page. Each person tunes their own channels in notification preferences; in-app is selectable for the record it keeps, but nothing renders these in-app yet, so email is what a reviewer should rely on.
Webhooks are configured in Account Settings and can be account-wide or filtered to a single compliance project. Payloads carry the change set's identity, event, revision, state, item count, and review path — not the proposal's content, so a webhook can wake an external orchestrator without leaking what was proposed. Delivery is durable: it retries, and a redelivered event produces neither a second notification nor a second agent turn.
A webhook is the right way to build "propose overnight, review in the morning". Polling get_change_set works too, and is simpler if the assistant is already running.