{"object":"mcp_manifest","server":{"name":"novumos","instructions":"NovumOS — block-based kanban API for agents. Boards hold columns, columns hold cards. Use list_boards / list_cards to discover work, create_card / update_card / move_card to act, and search_cards to find things by content. To drain a column as a durable work queue, claim_next_card leases the next card and returns a lease_token receipt; renew_lease (heartbeat) extends the lease while you work; then complete_lease advances the card to the next stage or fail_lease requeues it with backoff. Present the lease_token on every renew/complete/fail — a stale token means the card was re-claimed (409 lease_lost), so re-claim and start over. Inspect the queue read-only with list_leases (state=active|expired|all — 'expired' surfaces stuck work) and get_lease, without claiming anything. Board managers configure a column as a queue with get_column_contract / set_column_contract (lease TTL, retries, backoff, success/failure routing, concurrency, and input/output JSON Schemas) — these are manager-only. All operations are scoped to the org bound to the integration token."},"transports":[{"type":"streamable-http","path":"/v1/mcp/http","url":"https://staging.novumos.app/v1/mcp/http","status":"recommended"},{"type":"sse","path":"/v1/mcp/sse","url":"https://staging.novumos.app/v1/mcp/sse","status":"deprecated","note":"HTTP+SSE is deprecated in the MCP spec; kept for back-compat. Prefer the streamable-http transport."}],"tools":[{"name":"get_me","description":"Return the currently authenticated user. Mirrors GET /v1/me. When called with an integration token the response describes the user who minted the token.","input_schema":{"properties":{},"title":"get_meArguments","type":"object"}},{"name":"list_my_memberships","description":"List every org membership for the calling user. Mirrors GET /v1/me/memberships.","input_schema":{"properties":{},"title":"list_my_membershipsArguments","type":"object"}},{"name":"get_my_profile","description":"Return the calling user's profile (id, email, name, signup_source, has_password, created_at, last_signed_in_at). Mirrors GET /v1/me/profile.","input_schema":{"properties":{},"title":"get_my_profileArguments","type":"object"}},{"name":"update_my_profile","description":"Update the calling user's display name and/or avatar URL. Both fields optional — only what's provided is changed. Mirrors PATCH /v1/me/profile.","input_schema":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Avatar Url"}},"title":"update_my_profileArguments","type":"object"}},{"name":"change_my_password","description":"Change the calling user's password. Requires current_password verification. Refuses on OAuth-only accounts with status_code 409. Note: unlike the REST endpoint, the MCP wrapper does not revoke other sessions because MCP calls don't carry a browser cookie. Mirrors POST /v1/me/password.","input_schema":{"properties":{"current_password":{"title":"Current Password","type":"string"},"new_password":{"title":"New Password","type":"string"}},"required":["current_password","new_password"],"title":"change_my_passwordArguments","type":"object"}},{"name":"set_my_initial_password","description":"Set a password on an OAuth-only account. Returns 409 if the account already has a password. Mirrors POST /v1/me/password/set.","input_schema":{"properties":{"new_password":{"title":"New Password","type":"string"}},"required":["new_password"],"title":"set_my_initial_passwordArguments","type":"object"}},{"name":"list_my_sessions","description":"List the calling user's live browser sessions. The MCP wrapper cannot identify a 'current' session (there's no cookie), so every row returns is_current=false. Mirrors GET /v1/me/sessions.","input_schema":{"properties":{},"title":"list_my_sessionsArguments","type":"object"}},{"name":"revoke_my_session","description":"Revoke a single browser session by id. 404 if the id doesn't belong to the calling user (matches REST behavior — no leaking session existence). Mirrors DELETE /v1/me/sessions/{session_id}.","input_schema":{"properties":{"session_id":{"title":"Session Id","type":"string"}},"required":["session_id"],"title":"revoke_my_sessionArguments","type":"object"}},{"name":"revoke_all_my_sessions","description":"Revoke every live session for the calling user. Use this from an agent to force a global sign-out. Mirrors DELETE /v1/me/sessions.","input_schema":{"properties":{},"title":"revoke_all_my_sessionsArguments","type":"object"}},{"name":"get_my_preferences","description":"Return the calling user's preferences (theme, default_card_view, board_card_view, per-type email opt-out under notifications.email with defaults resolved, dismissed_hints — the one-time in-app learning callouts this user has sent away; only dismissed hints appear, so absence means 'still showing' — and surveys, the last-shown timestamps for the triggered NPS and upgrade-exit surveys, null when never shown). Mirrors GET /v1/me/preferences.","input_schema":{"properties":{},"title":"get_my_preferencesArguments","type":"object"}},{"name":"update_my_preferences","description":"Update the calling user's preferences. theme and default_card_view are optional; pass theme=None to leave it untouched, or default_card_view=None explicitly to clear it back to the client-side first-time default. An existing per-board view-mode map (board_card_view) is PRESERVED but cannot be set here — per-board stickiness is a UI concern (NOV-184); this tool never modifies it. notification_email (NOV-364) is a partial per-type email opt-out map (e.g. {'card_assigned': false}); each entry merges independently, null clears a type back to its default, and an unknown type is a validation error. In-app notifications have no opt-out in v1. dismissed_hints (NOV-400) is a partial map of one-time in-app learning callouts (e.g. {'connect_agent': true} to send the first-run 'Connect an agent' card away, or {'connect_agent': null} to bring it back); each entry merges independently and an unknown hint id is a validation error. An existing survey frequency-cap block (surveys, NOV-447) is PRESERVED but cannot be set here, for the same reason as board_card_view: a last-shown stamp records that a HUMAN was interrupted by a modal in a browser, so an agent writing one would be suppressing a survey nobody ever saw. Mirrors PATCH /v1/me/preferences.","input_schema":{"properties":{"theme":{"anyOf":[{"enum":["dark","light","system"],"type":"string"},{"type":"null"}],"default":null,"title":"Theme"},"default_card_view":{"anyOf":[{"enum":["side_panel","center_peek","focused_overlay","full_page"],"type":"string"},{"type":"null"}],"default":null,"title":"Default Card View"},"clear_default_card_view":{"default":false,"title":"Clear Default Card View","type":"boolean"},"notification_email":{"anyOf":[{"additionalProperties":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"type":"object"},{"type":"null"}],"default":null,"title":"Notification Email"},"dismissed_hints":{"anyOf":[{"additionalProperties":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"type":"object"},{"type":"null"}],"default":null,"title":"Dismissed Hints"}},"title":"update_my_preferencesArguments","type":"object"}},{"name":"list_orgs","description":"List orgs the caller has access to. Integration tokens see only their bound org; user sessions see every org they're a member of. Mirrors GET /v1/orgs.","input_schema":{"properties":{},"title":"list_orgsArguments","type":"object"}},{"name":"get_org","description":"Fetch a single org by id. Mirrors GET /v1/orgs/{id}.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"}},"required":["org_id"],"title":"get_orgArguments","type":"object"}},{"name":"create_org","description":"Create a new org for the calling user. The user becomes its first owner. Mirrors POST /v1/orgs. Integration tokens cannot call this.","input_schema":{"properties":{"name":{"title":"Name","type":"string"},"slug":{"title":"Slug","type":"string"},"primary_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Primary Domain"}},"required":["name","slug"],"title":"create_orgArguments","type":"object"}},{"name":"list_members","description":"List members of an org. Mirrors GET /v1/orgs/{id}/members.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"}},"required":["org_id"],"title":"list_membersArguments","type":"object"}},{"name":"add_member","description":"Invite or upsert a member into an org. Caller must be owner or admin in the org. Mirrors POST /v1/orgs/{id}/members.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"email":{"title":"Email","type":"string"},"role":{"default":"member","title":"Role","type":"string"}},"required":["org_id","email"],"title":"add_memberArguments","type":"object"}},{"name":"resend_member_invite","description":"Resend the invite / verification email for an existing org member. Caller must be owner or admin in the org. Picks the right template based on the target user's email_verified state. Rate-limited to once per minute per (org, target user) — 429 past that ceiling. Mirrors POST /v1/orgs/{id}/members/{user_id}:resend-invite.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"user_id":{"format":"uuid","title":"User Id","type":"string"}},"required":["org_id","user_id"],"title":"resend_member_inviteArguments","type":"object"}},{"name":"connect_agent","description":"Atomically create a User(kind='agent') + Membership + Integration in the target org and return the bearer token exactly once. Caller must be owner or admin. The token is pasted into the agent runtime (Lambda / MCP client / automation workflow / custom worker) and authenticates as the agent User on every subsequent call (spec §4.5). Mirrors POST /v1/orgs/{id}/agents.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"display_name":{"title":"Display Name","type":"string"},"role":{"default":"member","title":"Role","type":"string"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Avatar Url"}},"required":["org_id","display_name"],"title":"connect_agentArguments","type":"object"}},{"name":"list_live_agents","description":"Snapshot of agent Users currently connected over the realtime WS in the target org. Used to populate the live-agents badge on the kanban header. Caller must be a member. Mirrors GET /v1/orgs/{id}/presence/agents.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"}},"required":["org_id"],"title":"list_live_agentsArguments","type":"object"}},{"name":"create_invitation","description":"Invite an email address into a workspace. Caller must be owner or admin in the org. Writes a PENDING invitation — no membership and no billable seat exist until the invitee accepts. Re-inviting a still-pending address resends and rotates the token (the previous link stops working); inviting somebody who is already a member is a no-op that returns their existing membership. Mirrors POST /v1/orgs/{id}/invitations.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"email":{"title":"Email","type":"string"},"role":{"default":"member","title":"Role","type":"string"}},"required":["org_id","email"],"title":"create_invitationArguments","type":"object"}},{"name":"list_invitations","description":"List a workspace's invitations, newest first. Caller must be owner or admin. Defaults to the pending slice; pass status='all' for the full history, or a single status (pending|accepted|declined|revoked|expired). Mirrors GET /v1/orgs/{id}/invitations.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"status":{"default":"pending","title":"Status","type":"string"}},"required":["org_id"],"title":"list_invitationsArguments","type":"object"}},{"name":"revoke_invitation","description":"Withdraw a pending invitation. Caller must be owner or admin in the org. Terminal and seatless — a revoked invitation never created a membership. The emailed token stops resolving immediately. Mirrors DELETE /v1/orgs/{id}/invitations/{invitation_id}.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"invitation_id":{"format":"uuid","title":"Invitation Id","type":"string"}},"required":["org_id","invitation_id"],"title":"revoke_invitationArguments","type":"object"}},{"name":"invite_to_board","description":"Invite an email address straight to ONE board — the agent equivalent of the share dialog's invite-by-email. Caller must be a board MANAGER and hold a non-guest membership in the board's workspace (org admin is NOT required; a guest is refused 403). Writes a PENDING guest invitation carrying the board grant: the membership and the board_grants row are created together at accept, so decline/revoke/expiry leave no board access behind and no seat moves until accept. ``board_role`` is one of viewer | commenter | editor (manager is not invitable). Inviting somebody who is already a member is a no-op that applies the board grant immediately and returns their membership plus the grant. A pending invitation for a different board, or a workspace invitation, is a 409. NOT a share link: this creates a real, attributable account — use create_share_link for a seatless read-only client view. Mirrors POST /v1/boards/{board_id}/invitations.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"email":{"title":"Email","type":"string"},"board_role":{"default":"commenter","title":"Board Role","type":"string"}},"required":["board_id","email"],"title":"invite_to_boardArguments","type":"object"}},{"name":"list_board_invitations","description":"List invitations sent to one board, newest first. Caller must be a board MANAGER — the list is a directory of addresses the board is courting. Defaults to the pending slice; pass status='all' for the full history. Mirrors GET /v1/boards/{board_id}/invitations.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"status":{"default":"pending","title":"Status","type":"string"}},"required":["board_id"],"title":"list_board_invitationsArguments","type":"object"}},{"name":"revoke_board_invitation","description":"Withdraw a pending invitation to one board. Caller must be a board MANAGER (not necessarily an org admin — whoever can send must be able to take it back). An invitation that is not this board's is 404. Terminal and seatless, and it leaves no board grant behind because none was written. Mirrors DELETE /v1/boards/{board_id}/invitations/{invitation_id}.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"invitation_id":{"format":"uuid","title":"Invitation Id","type":"string"}},"required":["board_id","invitation_id"],"title":"revoke_board_invitationArguments","type":"object"}},{"name":"list_my_invitations","description":"List pending invitations addressed to the calling account, across every workspace — including workspaces the caller does not belong to yet, which is the whole point. Mirrors GET /v1/me/invitations.","input_schema":{"properties":{},"title":"list_my_invitationsArguments","type":"object"}},{"name":"accept_invitation","description":"Accept an invitation addressed to the calling account — the moment the membership is created and a billable seat is consumed. Requires being signed in as the invited address; anything else is 404. Returns the new membership. 402 plan_limit_reached when the workspace's free-tier seat cap would be breached. Mirrors POST /v1/invitations/{id}:accept.","input_schema":{"properties":{"invitation_id":{"format":"uuid","title":"Invitation Id","type":"string"}},"required":["invitation_id"],"title":"accept_invitationArguments","type":"object"}},{"name":"decline_invitation","description":"Decline an invitation addressed to the calling account. Terminal, and it never touched a seat. Mirrors POST /v1/invitations/{id}:decline.","input_schema":{"properties":{"invitation_id":{"format":"uuid","title":"Invitation Id","type":"string"}},"required":["invitation_id"],"title":"decline_invitationArguments","type":"object"}},{"name":"leave_org","description":"Leave a workspace you belong to, releasing the seat and revoking your board grants in it. No admin action required (§4.8: leaving is self-service). 409 if you are the sole remaining owner — transfer ownership first. Mirrors DELETE /v1/orgs/{id}/members/me.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"}},"required":["org_id"],"title":"leave_orgArguments","type":"object"}},{"name":"pause_agent","description":"Instantly pause an agent — its integration tokens stop authenticating (DB-backed, Redis-independent); reversible via resume_agent. Owner/admin only. Scoped to one agent in ``org_id``: it never logs out humans, other agents, or touches other clients' boards. Available on ALL plans — safety is never paywalled. Optional ``reason`` (trimmed to 200 chars) is echoed on the paused state. Mirrors POST /v1/orgs/{org_id}/agents/{agent_user_id}:pause.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"agent_user_id":{"format":"uuid","title":"Agent User Id","type":"string"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Reason"}},"required":["org_id","agent_user_id"],"title":"pause_agentArguments","type":"object"}},{"name":"resume_agent","description":"Reverse a pause — re-admit an agent so its existing tokens authenticate again (no token rotation needed). Owner/admin only; scoped to one agent in ``org_id``. Available on ALL plans. Mirrors POST /v1/orgs/{org_id}/agents/{agent_user_id}:resume.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"agent_user_id":{"format":"uuid","title":"Agent User Id","type":"string"}},"required":["org_id","agent_user_id"],"title":"resume_agentArguments","type":"object"}},{"name":"set_agent_budget","description":"Set an agent's caps (PUT-replace; pass ``null`` / omit a field to CLEAR that cap). ``writes_per_day`` = rolling-24h write cap; ``requests_per_sec`` = per-agent req/s cap. Each must be null or a non-negative int. ``auto_pause`` (default false): when true, tripping this row's writes/day cap auto-pauses the agent org-wide (it trips its own kill switch). Owner/admin only; the target must be a ``kind='agent'`` member of ``org_id``. Pass ``board_id`` to set a PER-BOARD override (writes/day only; per-board req/s is deferred); omit it for the org-wide budget. GATED to the Scale/Agency tier — an un-entitled org gets 403 ``governance_not_entitled``. Mirrors PUT /v1/orgs/{org_id}/agents/{agent_user_id}/budget[?board_id=].","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"agent_user_id":{"format":"uuid","title":"Agent User Id","type":"string"},"writes_per_day":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"title":"Writes Per Day"},"requests_per_sec":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"title":"Requests Per Sec"},"auto_pause":{"default":false,"title":"Auto Pause","type":"boolean"},"board_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Board Id"}},"required":["org_id","agent_user_id"],"title":"set_agent_budgetArguments","type":"object"}},{"name":"get_agent_budget","description":"Read an agent's budget caps + read-only kill-switch state (paused / paused_reason / paused_at) + live counter usage. Owner/admin only; the target must be a ``kind='agent'`` member of ``org_id``. Pass ``board_id`` for the PER-BOARD override budget; omit it for the org-wide budget. GATED to the Scale/Agency tier (403 ``governance_not_entitled`` otherwise). Mirrors GET /v1/orgs/{org_id}/agents/{agent_user_id}/budget[?board_id=].","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"agent_user_id":{"format":"uuid","title":"Agent User Id","type":"string"},"board_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Board Id"}},"required":["org_id","agent_user_id"],"title":"get_agent_budgetArguments","type":"object"}},{"name":"get_agent_activity","description":"Buyer-facing agent activity ledger — a summary (by_action + errors) plus a reverse-chron, cursor-paginated feed. Pass ``agent_user_id`` for one agent's ledger (the US-2 'Claude Drafter: 47 drafts, 3 card-moves, 0 errors in 24h' view); omit it for the all-agents org rollup. ``window`` is '24h' or '7d'; ``board_id`` scopes the feed to one client board. Owner/admin only; per-agent reads require a ``kind='agent'`` member of ``org_id``. GATED to the Scale/Agency tier (403 ``governance_not_entitled`` otherwise). Mirrors GET /v1/orgs/{org_id}/agents/{agent_user_id}/activity and GET /v1/orgs/{org_id}/agents/activity.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"agent_user_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Agent User Id"},"window":{"default":"24h","title":"Window","type":"string"},"board_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Board Id"},"cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Cursor"},"limit":{"default":25,"title":"Limit","type":"integer"}},"required":["org_id"],"title":"get_agent_activityArguments","type":"object"}},{"name":"list_boards","description":"List boards in the caller's org. Defaults to non-archived. Mirrors GET /v1/boards.","input_schema":{"properties":{"archived":{"default":false,"title":"Archived","type":"boolean"},"page_size":{"default":25,"title":"Page Size","type":"integer"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"title":"list_boardsArguments","type":"object"}},{"name":"get_board","description":"Fetch a board by id. Mirrors GET /v1/boards/{id}. Set ``include_archived`` to also see ARCHIVED property definitions (§7.1) in the ``properties`` map, each tagged ``archived: true``. Deleting a ``unique_id`` property archives it rather than removing it, so its id, prefix and numbering survive; re-adding the same name via update_board reinstates all of them. Archived definitions are hidden by default because the ``properties`` map is the board's LIVE schema — an archived property allocates no ids, validates no card writes, and cannot be filtered or sorted on. Safe to round-trip: passing this ``properties`` map back to update_board leaves archived entries archived (an echoed ``archived: true`` is a no-op), so editing one property does not silently reinstate the others. Reinstating is always explicit — send a body under the archived name WITHOUT the flag.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"include_archived":{"default":false,"title":"Include Archived","type":"boolean"}},"required":["board_id"],"title":"get_boardArguments","type":"object"}},{"name":"list_board_templates","description":"List curated board-template presets (§7.8) an agent can pass to create_board's ``template`` param. Each entry has a ``key``, display ``name``, one-line ``description``, ``property_validation`` mode, and a preview of the columns + properties it would create. Standard list envelope (results/has_more/next_cursor); the whole catalogue always fits one page. Mirrors GET /v1/board-templates. Distinct from card_templates (recurring-card RRULE materializer).","input_schema":{"properties":{},"title":"list_board_templatesArguments","type":"object"}},{"name":"create_board","description":"Create a kanban board in the caller's org. Caller must be owner or admin. By default the three starter columns (To Do / In Progress / Done) are created. ``template`` (§7.8): seed the board from a preset — one of: ``bug_tracker`` — Backlog → In Progress → In Review → Done with ID, Priority, and Type; ``content_pipeline`` — Idea → Draft → In Review → Scheduled → Published with Channel and Publish date. All presets use strict validation; call list_board_templates for full previews. The preset seeds columns + status groups + the properties schema + strict validation; an explicit ``properties`` map merges on top (explicit wins per-key, new keys appended). ``unique_id_prefix`` overrides the template's unique_id ID prefix (else derived from the board name); it is a 400 without a template that has a unique_id property. Optional access controls (§4.6): ``visibility`` (``org`` default | ``restricted`` — governs internal access only; external link-sharing is a separate axis), ``workspace_access`` (``edit`` default | ``view`` — read-only workspace), and ``permissions`` (a capability→min-role map, e.g. ``{\"edit_cards\": \"manager\"}``). ``property_validation`` (§7.7): ``permissive`` (default, pass-through) | ``strict`` (reject card writes whose select/status value isn't a defined option or whose number/checkbox/date/url/email/phone value has the wrong type). Mirrors POST /v1/boards.","input_schema":{"properties":{"name":{"title":"Name","type":"string"},"slug":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Slug"},"description":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Description"},"icon":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Icon"},"cover":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Cover"},"status_groups":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"default":null,"title":"Status Groups"},"properties":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Properties"},"create_default_columns":{"default":true,"title":"Create Default Columns","type":"boolean"},"visibility":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Visibility"},"workspace_access":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Workspace Access"},"permissions":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"default":null,"title":"Permissions"},"property_validation":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Property Validation"},"template":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Template"},"unique_id_prefix":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Unique Id Prefix"}},"required":["name"],"title":"create_boardArguments","type":"object"}},{"name":"update_board","description":"Patch a board's name, description, icon, cover, status_groups, or Notion-shape properties schema. Caller must be owner or admin. The ``properties`` patch is keyed by property name; pass ``None`` for a key to remove it. The reserved name ``Status`` is silently ignored — the column-derived status (NOV-82) wins. Access controls (§4.6, manager-only): ``visibility`` (``org`` | ``restricted`` — governs internal access only; external link-sharing is a separate axis via /share-links), ``workspace_access`` (``edit`` | ``view``), and ``permissions`` (capability→min-role map). ``property_validation`` (§7.7): ``permissive`` (default, pass-through) | ``strict`` (reject card writes whose select/status value isn't a defined option or whose number/checkbox/date/url/email/phone value has the wrong type). Mirrors PATCH /v1/boards/{id}.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Name"},"description":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Description"},"icon":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Icon"},"cover":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Cover"},"status_groups":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"default":null,"title":"Status Groups"},"properties":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Properties"},"visibility":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Visibility"},"workspace_access":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Workspace Access"},"permissions":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"default":null,"title":"Permissions"},"property_validation":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Property Validation"}},"required":["board_id"],"title":"update_boardArguments","type":"object"}},{"name":"purge_archived_board_property","description":"PERMANENTLY delete an ARCHIVED property definition from a board's schema. THIS CANNOT BE UNDONE. Mirrors POST /v1/boards/{id}/properties:purge; manager role required.\n\nThis is NOT how you remove a property. Removing a property is update_board with ``properties={'ID': None}``, which for a ``unique_id`` property ARCHIVES it: the definition, its id, its prefix and its numbering all survive, and re-adding the same name reinstates every one of them, so numbering continues where it left off. Purging destroys that. Afterwards the name is free for any type and re-adding it mints a brand-new property.\n\nCard values already stored under the purged name are left alone — only the definition is deleted. Errors: 404 if the board has no property with that name; 422 if it has a LIVE one (archive it first). Use get_board(include_archived=True) to see what is archived before calling this.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"name":{"title":"Name","type":"string"}},"required":["board_id","name"],"title":"purge_archived_board_propertyArguments","type":"object"}},{"name":"archive_board","description":"Soft-delete a board by archiving it. Caller must be owner or admin. Mirrors DELETE /v1/boards/{id}.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"}},"required":["board_id"],"title":"archive_boardArguments","type":"object"}},{"name":"list_columns","description":"List columns on a board, ordered by position. Mirrors GET /v1/boards/{id}/columns.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"page_size":{"default":50,"title":"Page Size","type":"integer"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"required":["board_id"],"title":"list_columnsArguments","type":"object"}},{"name":"list_board_labels","description":"List the labels actually in use on a board — the board's label vocabulary — as ``{name, color, count}``, most-used first. CALL THIS BEFORE LABELLING A CARD: labels are free-text on the card (``card.labels``), not a validated board schema property, so writing ``video`` when the board already says ``Video`` forks the vocabulary's colors and spellings. Reuse an existing entry's exact name AND color instead of inventing a new one. Names are merged case- and space-insensitively; a name used with several colors reports its most-used color. ``count`` is the number of distinct non-archived CARDS carrying the name. Mirrors GET /v1/boards/{id}/labels.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"page_size":{"default":100,"maximum":200,"minimum":1,"title":"Page Size","type":"integer"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"required":["board_id"],"title":"list_board_labelsArguments","type":"object"}},{"name":"create_column","description":"Add a column to a board. Caller must be owner or admin. The ``status_group`` must be one of the board's existing status_groups keys. Mirrors POST /v1/boards/{id}/columns.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"name":{"title":"Name","type":"string"},"status_group":{"title":"Status Group","type":"string"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Color"},"position":{"anyOf":[{"type":"number"},{"type":"null"}],"default":null,"title":"Position"}},"required":["board_id","name","status_group"],"title":"create_columnArguments","type":"object"}},{"name":"update_column","description":"Patch a column's name, color, position, or status_group. Caller must be owner or admin. Mirrors PATCH /v1/boards/{board_id}/columns/{column_id}.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"column_id":{"format":"uuid","title":"Column Id","type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Name"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Color"},"position":{"anyOf":[{"type":"number"},{"type":"null"}],"default":null,"title":"Position"},"status_group":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Status Group"}},"required":["board_id","column_id"],"title":"update_columnArguments","type":"object"}},{"name":"delete_column","description":"Hard-delete a column. Cards in that column remain (their ``column_id`` becomes null) — move them first to keep the board tidy. Mirrors DELETE /v1/boards/{board_id}/columns/{column_id}.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"column_id":{"format":"uuid","title":"Column Id","type":"string"}},"required":["board_id","column_id"],"title":"delete_columnArguments","type":"object"}},{"name":"list_board_access","description":"List who has access to a board, plus the board's access posture. Returns each grant as ``{id, user:{id, kind, display_name, avatar}, role, granted_by, granted_at}`` alongside the board's ``visibility`` / ``workspace_access`` / ``permissions``, plus the CALLER's own effective board role (``your_role``) and a derived ``can_manage`` flag. Any board participant (viewer and up) may read this. Mirrors GET /v1/boards/{id}/access.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"}},"required":["board_id"],"title":"list_board_accessArguments","type":"object"}},{"name":"grant_board_access","description":"Grant (or re-grant) a user a board ``role`` — one of ``viewer``, ``commenter``, ``editor``, ``manager``. Manager-only. Idempotent upsert on (board, user): re-granting updates the role in place. The grantee must already be a member (human or agent) of the board's org — a non-member is rejected 400. Pure access control: no seat is consumed. Mirrors POST /v1/boards/{id}/access.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"user_id":{"format":"uuid","title":"User Id","type":"string"},"role":{"title":"Role","type":"string"}},"required":["board_id","user_id","role"],"title":"grant_board_accessArguments","type":"object"}},{"name":"update_board_access","description":"Change a user's board ``role``. Manager-only. A downgrade that would leave the board with no manager is rejected 409 ``last_manager``. 404 if the user has no grant on the board. Mirrors PATCH /v1/boards/{id}/access/{user_id}.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"user_id":{"format":"uuid","title":"User Id","type":"string"},"role":{"title":"Role","type":"string"}},"required":["board_id","user_id","role"],"title":"update_board_accessArguments","type":"object"}},{"name":"revoke_board_access","description":"Revoke a user's board grant. Manager-only. Removing the sole remaining manager is rejected 409 ``last_manager``. 404 if the user has no grant on the board. No seat is touched. Mirrors DELETE /v1/boards/{id}/access/{user_id}.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"user_id":{"format":"uuid","title":"User Id","type":"string"}},"required":["board_id","user_id"],"title":"revoke_board_accessArguments","type":"object"}},{"name":"list_cards","description":"List cards on a board with optional filter/sort/pagination. Mirrors POST /v1/boards/{id}/query. Filter accepts either the Notion-shape grammar (``and``/``or``/``property``/``timestamp`` per FUNCTIONAL_SPEC §8.3) or the legacy flat-key shape (column_id, status, assignee, archived, due_date).\nEach leaf is ``{property: <name>, <type>: {<operator>: <value>}}``. Property names are the board's own property names (case-insensitive); call GET /v1/property-types for the operator vocabulary per type. Common tracker filters:\n  select (e.g. Priority): {property: 'Priority', select: {equals: 'P0'}} — also does_not_equal / is_empty / is_not_empty. Combine values with or:[...] for any-of.\n  status/column: {property: 'Status', status: {does_not_equal: 'Done'}}.\n  multi_select: {property: 'Components', multi_select: {contains: 'backend'}} — also does_not_contain / is_empty / is_not_empty.\n  checkbox: {property: 'Resolved', checkbox: {equals: false}}.\n  number: {property: 'Estimate', number: {greater_than_or_equal_to: 3}}.\n  unique_id: {property: 'Bug ID', unique_id: {equals: 'BUG-12'}} — values may be the property's full prefixed id ('BUG-12', case-insensitive; a WRONG prefix like 'TASK-12' is a 400), a bare integer (12), or a digit string ('12'); same forms for greater_than/less_than(_or_equal_to).\nExample — open P0 bugs: {and: [{property: 'Priority', select: {equals: 'P0'}}, {property: 'Resolved', checkbox: {equals: false}}]}.\nSorts accept a list of ``{property|timestamp, direction}`` — title/Name and any board-defined property name are valid sort keys. unique_id sorts NUMERICALLY by counter (BUG-9 before BUG-10), not lexicographically.\nAssignee-kind facet (NOV-90): pass ``{property: 'Assignee kind', assignee_kind: {equals: 'humans'|'agents'|'any'}}`` inside the Notion-shape filter to restrict cards by whether at least one assignee is a human or an agent. 'any' is a no-op (equivalent to omitting the filter). Each result carries a top-level ``unique_id`` — the resolved human key (\"BUG-12\"), or null when the board defines no unique_id property.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"filter":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Filter"},"sorts":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"default":null,"title":"Sorts"},"page_size":{"default":25,"title":"Page Size","type":"integer"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"required":["board_id"],"title":"list_cardsArguments","type":"object"}},{"name":"get_card","description":"Fetch a single card by id. Mirrors GET /v1/cards/{id}. The response carries a top-level ``unique_id`` — the resolved human key (\"BUG-12\"), or null when the board defines no unique_id property.","input_schema":{"properties":{"card_id":{"format":"uuid","title":"Card Id","type":"string"}},"required":["card_id"],"title":"get_cardArguments","type":"object"}},{"name":"list_card_autopilot_runs","description":"Autopilot runs that wrote content on this card, newest first (§25.9 provenance). Each row carries ``automation_run_id`` — the id an ``autopilot_rating`` joins back against — plus the model and the outcome. Runs that wrote nothing (``failed``) and runs with no ``automation_run_id`` are excluded, so every row returned is one that can be rated. Read-only, and gated on **board access**, not the org admin role: it carries NO token counts and NO cost, which is what keeps it separate from the admin-gated governance feed at GET /v1/orgs/{org_id}/ai-run-usage. Not cursor-paginated: ``limit`` is capped at 10 and ``next_cursor`` is always null, so there is no second page to ask for — but ``has_more`` is true when the cap truncated the list, so a caller is never told a partial list is complete. Mirrors GET /v1/cards/{card_id}/autopilot-runs.","input_schema":{"properties":{"card_id":{"format":"uuid","title":"Card Id","type":"string"},"limit":{"default":10,"maximum":10,"minimum":1,"title":"Limit","type":"integer"}},"required":["card_id"],"title":"list_card_autopilot_runsArguments","type":"object"}},{"name":"create_card","description":"Create a card on a board. ``column_id`` is optional — when omitted the card lands on the board's default column (NOV-379), so it is visible on the kanban board immediately. A column-less card only results from a board with no columns (or later deleting its column). A ``links`` schema property (spec §7.1) carries provenance/resource URLs: pass its value as an ordered list of ``{\"url\": \"https://…\", \"title\"?: \"…\", \"kind\"?: \"gmail|drive|copper|slack|url|…\"}`` objects (``kind`` is an open string). ``url`` must be http(s); ``javascript:``/``data:`` URLs are rejected 422. Mirrors POST /v1/cards. The response carries a top-level ``unique_id`` — the resolved human key (\"BUG-12\"), or null when the board defines no unique_id property.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"title":{"title":"Title","type":"string"},"column_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Column Id"},"properties":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Properties"},"position":{"anyOf":[{"type":"number"},{"type":"null"}],"default":null,"title":"Position"},"assignees":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"default":null,"title":"Assignees"},"due_date":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Due Date"},"labels":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"default":null,"title":"Labels"}},"required":["board_id","title"],"title":"create_cardArguments","type":"object"}},{"name":"update_card","description":"Patch a card. Set ``merge_properties=true`` to merge the ``properties`` map key-by-key instead of replacing it (Notion-shape fix from spec §8). Under merge, array-typed properties (multi_select / people / files / links per the board schema) element-UNION with the stored value — deduped by option id/name, user id, file id/url/name, or (for links) url — so two agents appending concurrently never clobber each other (spec §7.3). A ``links`` value is an ordered list of ``{\"url\": \"https://…\", \"title\"?: \"…\", \"kind\"?: \"gmail|drive|copper|slack|url|…\"}`` (``url`` must be http(s); deduped by url, case-sensitive). Per-key explicit removal: pass ``{\"add\": [...], \"remove\": [...]}`` as the property value (either key optional; ``{\"remove\": [...]}`` alone subtracts). Removes apply FIRST, then adds — so remove+add of the same identity in ONE write is the update idiom: ``{\"remove\": [{\"url\": X}], \"add\": [{\"url\": X, \"title\": \"new\"}]}`` atomically replaces that link's metadata. A bare re-add of an existing element (no matching remove) is a no-op — the stored form wins. Optional ``if_match`` enforces optimistic concurrency against the card's current etag — pass the etag you read from the prior get_card / update_card response to fail fast (HTTP 412 ``etag_mismatch``) if another writer changed the card in the meantime. Omit ``if_match`` for last-write-wins, which is the default agent behavior in v1 (spec §11.4 / NOV-103). Mirrors PATCH /v1/cards/{id}. The response carries a top-level ``unique_id`` — the resolved human key (\"BUG-12\"), or null when the board defines no unique_id property.","input_schema":{"properties":{"card_id":{"format":"uuid","title":"Card Id","type":"string"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Title"},"properties":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Properties"},"assignees":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"default":null,"title":"Assignees"},"due_date":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Due Date"},"labels":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"default":null,"title":"Labels"},"is_archived":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":null,"title":"Is Archived"},"merge_properties":{"default":false,"title":"Merge Properties","type":"boolean"},"if_match":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}],"default":null,"title":"If Match"}},"required":["card_id"],"title":"update_cardArguments","type":"object"}},{"name":"move_card","description":"Move a card to a different column and/or position. At least one of ``column_id`` or ``position`` must be provided. Optional ``if_match`` enforces optimistic concurrency (HTTP 412 on stale etag — see ``update_card``). Mirrors POST /v1/cards/{id}:move. The response carries a top-level ``unique_id`` — the resolved human key (\"BUG-12\"), or null when the board defines no unique_id property.","input_schema":{"properties":{"card_id":{"format":"uuid","title":"Card Id","type":"string"},"column_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Column Id"},"position":{"anyOf":[{"type":"number"},{"type":"null"}],"default":null,"title":"Position"},"if_match":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}],"default":null,"title":"If Match"}},"required":["card_id"],"title":"move_cardArguments","type":"object"}},{"name":"archive_card","description":"Soft-delete a card by archiving it. Optional ``if_match`` enforces optimistic concurrency. Mirrors DELETE /v1/cards/{id}.","input_schema":{"properties":{"card_id":{"format":"uuid","title":"Card Id","type":"string"},"if_match":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}],"default":null,"title":"If Match"}},"required":["card_id"],"title":"archive_cardArguments","type":"object"}},{"name":"batch_update_cards","description":"Transactional bulk operation. ``ops`` is a list of {id, op, ...} where op is 'set' | 'move' | 'delete'. Any failure aborts the whole batch. ``merge_properties=true`` applies the same per-key merge + array element-union semantics as update_card (spec §7.3) to every 'set' op. Mirrors POST /v1/cards:batchUpdate. Each ``results[].card`` carries a top-level ``unique_id`` — the resolved human key (\"BUG-12\"), or null when the board defines no unique_id property.","input_schema":{"properties":{"ops":{"items":{"additionalProperties":true,"type":"object"},"title":"Ops","type":"array"},"merge_properties":{"default":false,"title":"Merge Properties","type":"boolean"}},"required":["ops"],"title":"batch_update_cardsArguments","type":"object"}},{"name":"set_card_agent_activity","description":"Publish an agent's claim on a card — 'I'm working on this' (NOV-91). Last-write-wins; a second agent overwrites the first. The server stamps ``started_at`` so reads expire stale claims after 10 minutes. The next card mutation (update / move / archive) automatically clears the field. Use this when a Lambda or worker picks up a card; pair with ``clear_card_agent_activity`` on graceful completion. Mirrors POST /v1/cards/{id}/agent_activity. The response carries a top-level ``unique_id`` — the resolved human key (\"BUG-12\"), or null when the board defines no unique_id property.","input_schema":{"properties":{"card_id":{"format":"uuid","title":"Card Id","type":"string"},"agent_name":{"title":"Agent Name","type":"string"},"doing":{"title":"Doing","type":"string"}},"required":["card_id","agent_name","doing"],"title":"set_card_agent_activityArguments","type":"object"}},{"name":"clear_card_agent_activity","description":"Explicitly clear an agent's claim on a card (NOV-91). Idempotent. The lifecycle says the next card mutation will also clear it — this DELETE is the polite explicit path. Mirrors DELETE /v1/cards/{id}/agent_activity. The response carries a top-level ``unique_id`` — the resolved human key (\"BUG-12\"), or null when the board defines no unique_id property.","input_schema":{"properties":{"card_id":{"format":"uuid","title":"Card Id","type":"string"}},"required":["card_id"],"title":"clear_card_agent_activityArguments","type":"object"}},{"name":"search_cards","description":"Full-text search over cards in the caller's org (title + properties + body). Ranked by Postgres ts_rank_cd. Optionally scope to a single board. Mirrors POST /v1/search. Each result carries a top-level ``unique_id`` — the resolved human key (\"BUG-12\"), or null when the board defines no unique_id property.","input_schema":{"properties":{"query":{"title":"Query","type":"string"},"board_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Board Id"},"page_size":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"title":"Page Size"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"required":["query"],"title":"search_cardsArguments","type":"object"}},{"name":"claim_next_card","description":"Atomically lease the next eligible card in a column, draining it as a durable work queue. Mirrors POST /v1/boards/{board_id}/columns/{column_id}:claim-next. Returns a lease with a one-time ``lease_token`` receipt plus the full card — present that token on every later renew/complete/fail call for this lease. When the queue is drained returns ``{object:'lease', card:null}`` (a single content-type, cheap to busy-poll — never an error). ``lease_ttl_seconds`` overrides the column contract's TTL for this claim (and defaults every subsequent renew). Requires board ``edit_cards`` permission. The nested ``card`` carries a top-level ``unique_id`` — the resolved human key (\"BUG-12\"), or null when the board defines no unique_id property.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"column_id":{"format":"uuid","title":"Column Id","type":"string"},"lease_ttl_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"title":"Lease Ttl Seconds"}},"required":["board_id","column_id"],"title":"claim_next_cardArguments","type":"object"}},{"name":"renew_lease","description":"Extend a held lease's TTL and stamp its heartbeat — the keep-alive an agent calls periodically while working a card so the lease doesn't expire and get re-claimed. Doubles as ``:heartbeat`` (same operation). Mirrors POST /v1/leases/{lease_id}:renew. Present the ``lease_token`` receipt from the claim; a stale token (the card was reaped or re-claimed by someone else) is refused with ``409 lease_lost``. ``lease_ttl_seconds`` overrides the extension length. Silent: bumps no card etag and emits no event.","input_schema":{"properties":{"lease_id":{"format":"uuid","title":"Lease Id","type":"string"},"lease_token":{"title":"Lease Token","type":"string"},"lease_ttl_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"title":"Lease Ttl Seconds"}},"required":["lease_id","lease_token"],"title":"renew_leaseArguments","type":"object"}},{"name":"complete_lease","description":"Complete a held lease: optionally patch the card (``properties`` is merged), advance it to the contract's ``on_success_column_id``, and release the lease — all in one transaction. Returns the released lease plus the updated card. Mirrors POST /v1/leases/{lease_id}:complete. Present the ``lease_token`` receipt from the claim; a stale token is refused with ``409 lease_lost``. Idempotent: a second complete with the same token on an already-completed lease is a no-op (the card is not moved twice). ``output`` is stored on the lease row. The nested ``card`` carries a top-level ``unique_id`` — the resolved human key (\"BUG-12\"), or null when the board defines no unique_id property.","input_schema":{"properties":{"lease_id":{"format":"uuid","title":"Lease Id","type":"string"},"lease_token":{"title":"Lease Token","type":"string"},"output":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Output"},"properties":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Properties"}},"required":["lease_id","lease_token"],"title":"complete_leaseArguments","type":"object"}},{"name":"fail_lease","description":"Release a held lease as failed. With ``requeue=true`` (default) and attempts remaining, the card is re-queued behind a backoff window; once attempts are exhausted it dead-letters in place. Returns the released lease. Mirrors POST /v1/leases/{lease_id}:fail. Present the ``lease_token`` receipt from the claim; a stale token is refused with ``409 lease_lost``. Idempotent: a second fail with the same token on an already-failed lease is a no-op. ``reason`` is stashed on the lease row for the audit trail.","input_schema":{"properties":{"lease_id":{"format":"uuid","title":"Lease Id","type":"string"},"lease_token":{"title":"Lease Token","type":"string"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Reason"},"requeue":{"default":true,"title":"Requeue","type":"boolean"}},"required":["lease_id","lease_token"],"title":"fail_leaseArguments","type":"object"}},{"name":"get_lease","description":"Fetch one durable-work lease by id — read-only queue observability. Mirrors GET /v1/leases/{lease_id}. Returns the lease row (holder, attempt, expires_at, released_at/release_reason, not_before). Requires board READ access (viewer+) via the lease's card; a lease in another org, or on a restricted board you cannot see, returns 404 (existence never revealed). A read: never blocked for a past_due org.","input_schema":{"properties":{"lease_id":{"format":"uuid","title":"Lease Id","type":"string"}},"required":["lease_id"],"title":"get_leaseArguments","type":"object"}},{"name":"list_leases","description":"List a board's leases as a Notion-shape ``{object:'list', results, has_more, next_cursor}`` envelope — read-only queue observability for the board UI and Maya's 'who is stuck' view. Mirrors GET /v1/boards/{board_id}/leases. ``state`` is one of 'active' (default — currently held: not released and not yet expired), 'expired' (lapsed but not yet reaped/released — the stuck queue), or 'all' (full history incl. released rows); an unknown value is a 422. ``column_id`` narrows to one column's queue. Results are ordered soonest-to-expire first; page with ``page_size`` + the opaque ``start_cursor`` from a prior ``next_cursor``. Requires board READ access (viewer+) — a restricted board you cannot see returns 404. A read: never blocked for a past_due org.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"column_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Column Id"},"state":{"default":"active","title":"State","type":"string"},"page_size":{"default":50,"title":"Page Size","type":"integer"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"required":["board_id"],"title":"list_leasesArguments","type":"object"}},{"name":"get_column_contract","description":"Read a column's job contract — the durable-work queue config (enabled, lease_ttl_seconds, max_attempts, backoff_seconds, concurrency_limit, on_success_column_id, on_failure_column_id, input_schema, output_schema). Mirrors GET /v1/boards/{board_id}/columns/{column_id}/contract. Returns the fields as stored (a column that isn't a queue reads back as the empty shape, every field null). MANAGER-only: a non-manager board role is refused (403); a board in another org, or a column not on this board, returns 404 (existence never revealed). A read: never blocked for a past_due org.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"column_id":{"format":"uuid","title":"Column Id","type":"string"}},"required":["board_id","column_id"],"title":"get_column_contractArguments","type":"object"}},{"name":"set_column_contract","description":"Write a column's job contract (full replace), turning the column into — or reconfiguring — a durable agent-work queue. Mirrors PUT /v1/boards/{board_id}/columns/{column_id}/contract. ``contract`` is an object with any of: enabled (bool), lease_ttl_seconds, max_attempts, concurrency_limit (positive ints), backoff_seconds (list of non-negative ints), on_success_column_id / on_failure_column_id (column ids on THIS board), input_schema / output_schema (JSON Schema objects). Only the keys you send are stored; a key set to null clears that override (reverting to the platform default). Rejects a malformed input_schema/output_schema or an off-board routing target with 422 invalid_contract, and an unknown field with 422. MANAGER-only (403 for a lower role; 404 hides a board/column you can't see). Emits column.contract.updated.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"},"column_id":{"format":"uuid","title":"Column Id","type":"string"},"contract":{"additionalProperties":true,"title":"Contract","type":"object"}},"required":["board_id","column_id","contract"],"title":"set_column_contractArguments","type":"object"}},{"name":"add_comment","description":"Post a comment on a card — a flat per-card chat, no threading. ``text`` is plain text; it is stored as a single Notion-shape paragraph block. Requires the board's ``comment`` capability (default role: commenter or above). Mirrors POST /v1/comments.","input_schema":{"properties":{"card_id":{"format":"uuid","title":"Card Id","type":"string"},"text":{"title":"Text","type":"string"}},"required":["card_id","text"],"title":"add_commentArguments","type":"object"}},{"name":"list_comments","description":"List a card's comments, oldest first. Archived (deleted) comments are excluded. Mirrors GET /v1/comments?card_id={id}.","input_schema":{"properties":{"card_id":{"format":"uuid","title":"Card Id","type":"string"},"page_size":{"default":25,"title":"Page Size","type":"integer"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"required":["card_id"],"title":"list_commentsArguments","type":"object"}},{"name":"update_comment","description":"Edit a comment's text. Author-only — even a board admin cannot rewrite someone else's comment (they can only delete it). Optional ``if_match`` enforces optimistic concurrency against the comment's current etag (HTTP 412 ``etag_mismatch`` on a stale value, same contract as ``update_card``); omit it for last-write-wins. Mirrors PATCH /v1/comments/{comment_id}.","input_schema":{"properties":{"comment_id":{"format":"uuid","title":"Comment Id","type":"string"},"text":{"title":"Text","type":"string"},"if_match":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}],"default":null,"title":"If Match"}},"required":["comment_id","text"],"title":"update_commentArguments","type":"object"}},{"name":"delete_comment","description":"Soft-delete (archive) a comment. The author or a board admin/owner may delete; other commenters cannot. Optional ``if_match`` enforces optimistic concurrency (see ``update_comment``). Idempotent — deleting an already-archived comment is a no-op. Mirrors DELETE /v1/comments/{comment_id}.","input_schema":{"properties":{"comment_id":{"format":"uuid","title":"Comment Id","type":"string"},"if_match":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}],"default":null,"title":"If Match"}},"required":["comment_id"],"title":"delete_commentArguments","type":"object"}},{"name":"list_notifications","description":"List the calling user's notifications, newest first. Set ``unread=true`` to return only unread rows. Cursor-paginated via ``start_cursor`` / ``next_cursor``. Mirrors GET /v1/notifications?unread={bool}.","input_schema":{"properties":{"unread":{"default":false,"title":"Unread","type":"boolean"},"page_size":{"default":25,"title":"Page Size","type":"integer"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"title":"list_notifications_toolArguments","type":"object"}},{"name":"get_unread_notification_count","description":"Return the number of unread notifications for the calling user. Mirrors GET /v1/notifications/count.","input_schema":{"properties":{},"title":"get_unread_notification_countArguments","type":"object"}},{"name":"mark_notification_read","description":"Mark one of the calling user's notifications read. Idempotent. Returns 404 if the id isn't the caller's (a cross-user or cross-org id is indistinguishable from a missing one). Mirrors POST /v1/notifications/{id}:read.","input_schema":{"properties":{"notification_id":{"format":"uuid","title":"Notification Id","type":"string"}},"required":["notification_id"],"title":"mark_notification_read_toolArguments","type":"object"}},{"name":"mark_all_notifications_read","description":"Mark every unread notification for the calling user read. Returns the number marked. Mirrors POST /v1/notifications:readAll.","input_schema":{"properties":{},"title":"mark_all_notifications_read_toolArguments","type":"object"}},{"name":"requeue_failed_email","description":"Requeue one failed notification-email outbox row for immediate retry. Owner/admin only, org-scoped, and idempotent if repeated after the row is already pending. Sent or suppressed rows are rejected. Mirrors POST /v1/orgs/{org_id}/email-outbox/{outbox_id}:requeue.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"outbox_id":{"format":"uuid","title":"Outbox Id","type":"string"}},"required":["org_id","outbox_id"],"title":"requeue_failed_email_toolArguments","type":"object"}},{"name":"list_block_children","description":"Fetch a card or block's body tree (Notion-shape, spec §6.4). ``parent_id`` is the card id (== Notion's page id) OR a block id — block ids return their nested children. ``depth`` recurses up to 10 levels in one round trip. Mirrors GET /v1/blocks/{parent_id}/children?depth=N.","input_schema":{"properties":{"parent_id":{"format":"uuid","title":"Parent Id","type":"string"},"depth":{"default":1,"title":"Depth","type":"integer"}},"required":["parent_id"],"title":"list_block_childrenArguments","type":"object"}},{"name":"append_blocks","description":"Append blocks under ``parent_id``. Each child carries ``type`` and the matching ``<type>: {...}`` Notion-shape payload (paragraph, heading_1, callout, code, image, …). Pass ``after`` to insert a block AFTER a specific sibling instead of at the end. This call does NOT recurse: a nested ``children`` array inside a block's own payload is rejected with 400 — build a tree one level per call, parenting each call on an id the previous one returned. Mirrors PATCH /v1/blocks/{parent_id}/children.","input_schema":{"properties":{"parent_id":{"format":"uuid","title":"Parent Id","type":"string"},"children":{"items":{"additionalProperties":true,"type":"object"},"title":"Children","type":"array"},"after":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"After"}},"required":["parent_id","children"],"title":"append_blocksArguments","type":"object"}},{"name":"update_block","description":"Modify a block in place. The payload carries the new ``<type>: {...}`` content keyed by the block's type. Pass ``archived: true`` to soft-delete. A nested ``children`` array in that content is rejected with 400 (a block's subtree is edited through its children's own ids), as is a ``parent`` key — use move_block to re-parent. Mirrors PATCH /v1/blocks/{block_id}.","input_schema":{"properties":{"block_id":{"format":"uuid","title":"Block Id","type":"string"},"payload":{"additionalProperties":true,"title":"Payload","type":"object"}},"required":["block_id","payload"],"title":"update_blockArguments","type":"object"}},{"name":"move_block","description":"Re-parent and/or reorder a block WITHOUT changing its id. ``parent`` is {'type': 'block_id', 'block_id': '<uuid>'} to nest it under another block, or {'type': 'card_id', 'card_id': '<uuid>'} to put it back at the card's top level. ``after`` names the sibling to land behind; omit it (or pass null) to become the FIRST child — note that is the OPPOSITE of ``after`` on append_blocks, where omitting it appends at the end. The target parent must be in the same card, and a block cannot move under its own descendant, nor under a parent that sits inside an archived subtree (that would hide the content from every reader). Descendants ride along. Use this instead of delete_block + append_blocks when restructuring a body: the block keeps its id and audit history, and the change reads as one block.updated. Mirrors POST /v1/blocks/{block_id}:move.","input_schema":{"properties":{"block_id":{"format":"uuid","title":"Block Id","type":"string"},"parent":{"additionalProperties":true,"title":"Parent","type":"object"},"after":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"After"}},"required":["block_id","parent"],"title":"move_blockArguments","type":"object"}},{"name":"delete_block","description":"Soft-archive a block + descendants. Mirrors DELETE /v1/blocks/{block_id}.","input_schema":{"properties":{"block_id":{"format":"uuid","title":"Block Id","type":"string"}},"required":["block_id"],"title":"delete_blockArguments","type":"object"}},{"name":"presign_file_upload","description":"Mint a presigned multipart-POST URL to attach a file to a card. Returns the file envelope (``file_id``, status='pending') plus ``upload_url``, ``upload_url_fields`` (signed policy fields the client must include in the multipart form), and ``upload_url_expires_in``. POST the bytes directly to ``upload_url`` as multipart/form-data — including every entry from ``upload_url_fields`` and the file under a ``file`` field. Don't go through the MCP channel for the bytes. Then call ``commit_file`` to finalize. Mirrors POST /v1/files:upload.","input_schema":{"properties":{"card_id":{"format":"uuid","title":"Card Id","type":"string"},"filename":{"title":"Filename","type":"string"},"content_type":{"title":"Content Type","type":"string"},"size_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"title":"Size Bytes"}},"required":["card_id","filename","content_type"],"title":"presign_file_uploadArguments","type":"object"}},{"name":"commit_file","description":"Finalize a pending upload after the bytes have been PUT to S3. The server HeadObjects the upload to verify content-type matches the declared one and flips status='available'. Mirrors POST /v1/files/{id}:commit.","input_schema":{"properties":{"file_id":{"format":"uuid","title":"File Id","type":"string"}},"required":["file_id"],"title":"commit_fileArguments","type":"object"}},{"name":"get_file","description":"Fetch a file's metadata plus a short-lived presigned GET URL for downloading the bytes. The URL field is only present for available files. Mirrors GET /v1/files/{id}.","input_schema":{"properties":{"file_id":{"format":"uuid","title":"File Id","type":"string"}},"required":["file_id"],"title":"get_fileArguments","type":"object"}},{"name":"list_card_attachments","description":"Notion-shape list of files attached to a card. Mirrors GET /v1/cards/{id}/attachments.","input_schema":{"properties":{"card_id":{"format":"uuid","title":"Card Id","type":"string"}},"required":["card_id"],"title":"list_card_attachmentsArguments","type":"object"}},{"name":"delete_file","description":"Detach + soft-archive a file from a card. Fires a card.updated webhook for the attachment removal. Mirrors DELETE /v1/files/{id}.","input_schema":{"properties":{"file_id":{"format":"uuid","title":"File Id","type":"string"}},"required":["file_id"],"title":"delete_fileArguments","type":"object"}},{"name":"search","description":"Full-text search across the caller's org. Identical to ``search_cards`` today; will gain block and comment search as those features ship. Mirrors POST /v1/search.","input_schema":{"properties":{"query":{"title":"Query","type":"string"},"types":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"default":null,"title":"Types"},"board_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Board Id"},"page_size":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"title":"Page Size"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"required":["query"],"title":"searchArguments","type":"object"}},{"name":"list_integrations","description":"List integration tokens for an org. Plaintext is NEVER returned by this endpoint — only metadata + the last 4 chars. Mirrors GET /v1/orgs/{id}/integrations.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"}},"required":["org_id"],"title":"list_integrationsArguments","type":"object"}},{"name":"mint_integration","description":"Create a new integration token for an org. WARNING: the plaintext ``token`` field is returned ONCE — it cannot be retrieved later. Treat the response like a password. Caller must be owner or admin. ``role`` is the token's assigned org role ('member' default | 'admin'); minting an 'admin' token requires an admin/owner caller. Mirrors POST /v1/orgs/{id}/integrations.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"name":{"title":"Name","type":"string"},"role":{"default":"member","title":"Role","type":"string"}},"required":["org_id","name"],"title":"mint_integrationArguments","type":"object"}},{"name":"list_webhooks","description":"List webhook subscriptions for an org, newest first (keyset pagination). Any org member may read. Mirrors GET /v1/webhooks.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"page_size":{"default":25,"title":"Page Size","type":"integer"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"required":["org_id"],"title":"list_webhooksArguments","type":"object"}},{"name":"get_webhook","description":"Get a single webhook subscription by id. Any org member may read. 404 if the webhook belongs to another org. Mirrors GET /v1/webhooks/{id}.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"webhook_id":{"format":"uuid","title":"Webhook Id","type":"string"}},"required":["org_id","webhook_id"],"title":"get_webhookArguments","type":"object"}},{"name":"create_webhook","description":"Create a webhook subscription on an org. Owner/admin only. Returns the signing secret plaintext EXACTLY ONCE (only secret_last4 is readable afterwards). `events` maps to the subscription's event_types; empty means all events. When `notion_handshake` is true the subscription is created inactive and only activates after the receiver echoes the challenge token. Mirrors POST /v1/webhooks.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"url":{"title":"Url","type":"string"},"events":{"items":{"type":"string"},"title":"Events","type":"array"},"board_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Board Id"},"notion_handshake":{"default":false,"title":"Notion Handshake","type":"boolean"}},"required":["org_id","url","events"],"title":"create_webhookArguments","type":"object"}},{"name":"update_webhook","description":"Update a webhook subscription. Owner/admin only. Only provided fields change; omitted fields are left as-is (`events` maps to event_types; board_id cannot be nulled via update). 404 if the webhook belongs to another org. Mirrors PATCH /v1/webhooks/{id}.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"webhook_id":{"format":"uuid","title":"Webhook Id","type":"string"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Url"},"events":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"default":null,"title":"Events"},"board_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Board Id"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":null,"title":"Is Active"}},"required":["org_id","webhook_id"],"title":"update_webhookArguments","type":"object"}},{"name":"rotate_webhook_secret","description":"Rotate a webhook's signing secret. Owner/admin only. Returns the new secret plaintext EXACTLY ONCE (only secret_last4 is readable afterwards); the old secret stops signing new deliveries. 404 if the webhook belongs to another org. Mirrors POST /v1/webhooks/{id}:rotate-secret.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"webhook_id":{"format":"uuid","title":"Webhook Id","type":"string"}},"required":["org_id","webhook_id"],"title":"rotate_webhook_secretArguments","type":"object"}},{"name":"delete_webhook","description":"Delete a webhook subscription. Owner/admin only. 404 if the webhook belongs to another org. Mirrors DELETE /v1/webhooks/{id}.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"webhook_id":{"format":"uuid","title":"Webhook Id","type":"string"}},"required":["org_id","webhook_id"],"title":"delete_webhookArguments","type":"object"}},{"name":"list_deliveries","description":"List delivery attempts for a webhook, newest first (keyset pagination). Any org member may read. 404 if the webhook belongs to another org. Mirrors GET /v1/webhooks/{id}/deliveries.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"webhook_id":{"format":"uuid","title":"Webhook Id","type":"string"},"page_size":{"default":25,"title":"Page Size","type":"integer"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"required":["org_id","webhook_id"],"title":"list_deliveriesArguments","type":"object"}},{"name":"replay_delivery","description":"Re-enqueue a past webhook delivery by id (same event_id so receivers dedup correctly). Owner/admin only. Resets the delivery to pending/attempts=0. 404 if the delivery (or its webhook) belongs to another org. Mirrors POST /v1/webhooks/{webhook_id}/deliveries/{delivery_id}:replay.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"delivery_id":{"format":"uuid","title":"Delivery Id","type":"string"}},"required":["org_id","delivery_id"],"title":"replay_deliveryArguments","type":"object"}},{"name":"list_automations","description":"List an org's board automations, newest first (keyset pagination). Owner/admin only. Mirrors GET /v1/orgs/{org_id}/automations.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"page_size":{"default":25,"title":"Page Size","type":"integer"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"required":["org_id"],"title":"list_automations_toolArguments","type":"object"}},{"name":"create_automation","description":"Create a board automation rule. Owner/admin only. `trigger` is `{event_type, filters?}` (event_type must be a known event type); `actions` is a non-empty ordered list, each with a known `type` (invoke_agent | notify | move_card | set_property); `conditions` is an optional predicate list. A `notify` action's `params` accept `recipient` ('owner' (default) | 'assignees' — the card's current assignees) and `channels` (any of 'comment' (default) | 'in_app' | 'email'); in_app/email skip agent recipients, email honors each human's per-type opt-out. Omitting both keys = comment-to-owner (the historical default). `board_id` scopes the rule to one board (null = org-wide); `run_as_agent_id` is the scoped agent actions execute as. An `invoke_agent` action dispatches a SIGNED job (HTTP POST) to the target agent's registered runtime URL; the agent runtime must verify the `X-Novum-Signature` header (`t=<unix_ts>,v1=<hex>`, HMAC-SHA256 over the raw request body) using its connect-time `agent_dispatch_secret`. The job body's `job_id` equals the AutomationRun dedup key (a uuid5 that is STABLE across that run's retries), so a retried run re-POSTs the SAME `job_id`; agent runtimes should dedupe on `job_id` to make delivery idempotent. Mirrors POST /v1/orgs/{org_id}/automations.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"name":{"title":"Name","type":"string"},"trigger":{"additionalProperties":true,"title":"Trigger","type":"object"},"actions":{"items":{},"title":"Actions","type":"array"},"conditions":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"default":null,"title":"Conditions"},"board_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Board Id"},"run_as_agent_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Run As Agent Id"}},"required":["org_id","name","trigger","actions"],"title":"create_automation_toolArguments","type":"object"}},{"name":"update_automation","description":"Patch a board automation. Owner/admin only. Only the fields you pass are changed; omit a field (leave it null) to keep it unchanged. `is_active` toggles the rule on/off. `trigger` / `actions` / `conditions` are re-validated (a `notify` action's `recipient` / `channels` config too — unknown values are rejected here, not at run time). 404 if the automation belongs to another org. Mirrors PATCH /v1/orgs/{org_id}/automations/{id}.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"automation_id":{"format":"uuid","title":"Automation Id","type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Name"},"board_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Board Id"},"trigger":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Trigger"},"conditions":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"default":null,"title":"Conditions"},"actions":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"default":null,"title":"Actions"},"run_as_agent_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Run As Agent Id"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":null,"title":"Is Active"}},"required":["org_id","automation_id"],"title":"update_automation_toolArguments","type":"object"}},{"name":"delete_automation","description":"Delete a board automation. Owner/admin only. 404 if the automation belongs to another org. Mirrors DELETE /v1/orgs/{org_id}/automations/{id}.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"automation_id":{"format":"uuid","title":"Automation Id","type":"string"}},"required":["org_id","automation_id"],"title":"delete_automation_toolArguments","type":"object"}},{"name":"list_automation_runs","description":"List one automation's run ledger, newest first (keyset pagination). Owner/admin only. 404 if the automation belongs to another org. Mirrors GET /v1/orgs/{org_id}/automations/{id}/runs.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"automation_id":{"format":"uuid","title":"Automation Id","type":"string"},"page_size":{"default":25,"title":"Page Size","type":"integer"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"required":["org_id","automation_id"],"title":"list_automation_runs_toolArguments","type":"object"}},{"name":"list_playbooks","description":"List an org's Autopilot Playbooks, newest first (keyset pagination). Owner/admin only. Mirrors GET /v1/orgs/{org_id}/playbooks.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"page_size":{"default":25,"title":"Page Size","type":"integer"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"required":["org_id"],"title":"list_playbooks_toolArguments","type":"object"}},{"name":"create_playbook","description":"Create an Autopilot Playbook — a reusable, ordered prompt chain an Autopilot column runs through Claude when a card enters it. Owner/admin only. `steps` is a non-empty ordered list; each step is `{prompt_md, name?, model?}` (prompt_md required — the instruction for that step; step N receives step N-1's output). `output_target` is 'append_body' (default) or 'replace_body' — where the final output lands on the card body. `on_success_move_to` is the column id a card advances to on success (null = stay). Mirrors POST /v1/orgs/{org_id}/playbooks.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"name":{"title":"Name","type":"string"},"steps":{"items":{},"title":"Steps","type":"array"},"output_target":{"default":"append_body","title":"Output Target","type":"string"},"on_success_move_to":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"On Success Move To"}},"required":["org_id","name","steps"],"title":"create_playbook_toolArguments","type":"object"}},{"name":"update_playbook","description":"Patch an Autopilot Playbook. Owner/admin only. Only the fields you pass are changed; omit a field (leave it null) to keep it unchanged. `steps` / `output_target` are re-validated. 404 if the Playbook belongs to another org. Mirrors PATCH /v1/orgs/{org_id}/playbooks/{id}.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"playbook_id":{"format":"uuid","title":"Playbook Id","type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Name"},"steps":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"default":null,"title":"Steps"},"output_target":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Output Target"},"on_success_move_to":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"On Success Move To"}},"required":["org_id","playbook_id"],"title":"update_playbook_toolArguments","type":"object"}},{"name":"delete_playbook","description":"Delete an Autopilot Playbook. Owner/admin only. Any column pointing at it is detached (playbook_id set null). 404 if the Playbook belongs to another org. Mirrors DELETE /v1/orgs/{org_id}/playbooks/{id}.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"playbook_id":{"format":"uuid","title":"Playbook Id","type":"string"}},"required":["org_id","playbook_id"],"title":"delete_playbook_toolArguments","type":"object"}},{"name":"grant_org_comp","description":"Grant a billing comp/benefit to an org (platform-admin only). grant_type is one of comped_subscription | plan_override | free_seats | percent_off_coupon | governance. free_seats requires free_seat_count >= 1; percent_off_coupon requires percent_off in 1..100; comped_subscription is always 100% off; governance flips the Scale/Agency agent-governance entitlement (no extra fields). reason is required. Mirrors POST /v1/admin/orgs/{org_id}/grants.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"grant_type":{"title":"Grant Type","type":"string"},"reason":{"title":"Reason","type":"string"},"idempotency_key":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Idempotency Key"},"free_seat_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"title":"Free Seat Count"},"percent_off":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"title":"Percent Off"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Expires At"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Metadata"}},"required":["org_id","grant_type","reason"],"title":"grant_org_compArguments","type":"object"}},{"name":"list_org_grants","description":"List an org's billing grants (platform-support+). Mirrors GET /v1/admin/orgs/{org_id}/grants.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"include_history":{"default":true,"title":"Include History","type":"boolean"}},"required":["org_id"],"title":"list_org_grantsArguments","type":"object"}},{"name":"revoke_org_grant","description":"Revoke an active billing grant on an org (platform-admin only). Never deletes history — flips the grant to revoked and reverts its effect. Mirrors DELETE /v1/admin/orgs/{org_id}/grants/{grant_id}.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"grant_id":{"format":"uuid","title":"Grant Id","type":"string"}},"required":["org_id","grant_id"],"title":"revoke_org_grantArguments","type":"object"}},{"name":"create_import_job","description":"Create + start an import for an org. Owner/admin only. Two sources. source='notion_api' (default): pass the transient Notion integration `token` (never stored, logged, or echoed) with either `database_id` (single database) or `whole_workspace=true` (all shared databases → one board each with best-effort inter-DB relation links); optional `column_property` names the Notion status/select property whose options become the board's columns (blank = auto-detect). source='export_file' (NOV-297): import a Notion export .zip previously uploaded via `create_import_upload` — pass `file_id` (no token). REQUIRED SEQUENCE for export_file: call create_import_upload -> PUT the bytes to the returned upload_url -> commit_file(file_id) -> THEN create_import_job(source='export_file', file_id=...). The file is importable as soon as commit_file returns status=='available'. Returns the queued job WITHOUT any token. Mirrors POST /v1/orgs/{org_id}/import-jobs.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"source":{"default":"notion_api","title":"Source","type":"string"},"token":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Token"},"file_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"File Id"},"database_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Database Id"},"column_property":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Column Property"},"whole_workspace":{"default":false,"title":"Whole Workspace","type":"boolean"}},"required":["org_id"],"title":"create_import_jobArguments","type":"object"}},{"name":"create_import_upload","description":"Mint a presigned upload for a Notion export .zip (source='export_file' imports). Owner/admin only. Only application/zip is accepted. Returns a file envelope with `id`, `upload_url`, and `upload_url_fields` (a presigned POST). SEQUENCE: create_import_upload -> POST the bytes to upload_url with the given form fields -> commit_file(id) -> create_import_job(source='export_file', file_id=id). The file is importable as soon as commit_file returns status=='available'. Mirrors POST /v1/orgs/{org_id}/import-jobs:upload.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"filename":{"title":"Filename","type":"string"},"content_type":{"default":"application/zip","title":"Content Type","type":"string"},"size_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"title":"Size Bytes"}},"required":["org_id","filename"],"title":"create_import_uploadArguments","type":"object"}},{"name":"list_import_jobs","description":"List an org's Notion import jobs, newest first (keyset pagination). Owner/admin only. Mirrors GET /v1/orgs/{org_id}/import-jobs.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"page_size":{"default":25,"title":"Page Size","type":"integer"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"required":["org_id"],"title":"list_import_jobsArguments","type":"object"}},{"name":"get_import_job","description":"Fetch one import job's status/counts/error_log/board_id (poll this to monitor progress). Owner/admin only. 404 if the job belongs to another org. Mirrors GET /v1/orgs/{org_id}/import-jobs/{id}.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"job_id":{"format":"uuid","title":"Job Id","type":"string"}},"required":["org_id","job_id"],"title":"get_import_jobArguments","type":"object"}},{"name":"cancel_import_job","description":"Cancel a queued/running import. Owner/admin only. A terminal job (succeeded/failed/partial) → 409 conflict; a queued/running job is landed in 'failed' and its transient token dropped so an unclaimed job never runs (cancelling an already-running job is best-effort). 404 if the job belongs to another org. Mirrors POST /v1/orgs/{org_id}/import-jobs/{id}:cancel.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"job_id":{"format":"uuid","title":"Job Id","type":"string"}},"required":["org_id","job_id"],"title":"cancel_import_jobArguments","type":"object"}},{"name":"list_notion_databases","description":"List the Notion databases the given integration token can see, to pick one to import. Owner/admin only. The transient token is used once and never stored, logged, or echoed. Returns [{id, title}]. Mirrors POST /v1/orgs/{org_id}/import-jobs:databases.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"},"token":{"title":"Token","type":"string"}},"required":["org_id","token"],"title":"list_notion_databasesArguments","type":"object"}},{"name":"submit_feedback","description":"File a bug report or idea — the agent equivalent of the in-app feedback panel. type is 'bug_report' (requires severity: blocker|major|minor) or 'idea' (requires category: suggestion|feature_request|compliment|other); the human-survey types (nps, upgrade_exit, autopilot_rating) are NOT submittable over MCP. content is required. Put the request_ids of any failed calls into context — that is the single highest-value field an agent can hand back. Returns the FB- reference to quote, never a Jira key. Same per-user/per-org hourly caps as the REST path, and keeps working while a workspace is past_due (§25.4 allow-lists feedback submission). Mirrors POST /v1/feedback.","input_schema":{"properties":{"type":{"title":"Type","type":"string"},"content":{"title":"Content","type":"string"},"expected":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Expected"},"severity":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Severity"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Category"},"route":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Route"},"board_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Board Id"},"card_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"default":null,"title":"Card Id"},"context":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"default":null,"title":"Context"}},"required":["type","content"],"title":"submit_feedbackArguments","type":"object"}},{"name":"list_my_feedback","description":"List the submissions filed by the principal this token identifies, newest first. Optional type / status filters, cursor-paginated via start_cursor/next_cursor. Never returns another user's rows, even in the same org — and a token with no user identity of its own (a legacy integration row with no attached agent user) has no history to list, not the minting human's. Mirrors GET /v1/feedback.","input_schema":{"properties":{"type":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Type"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Status"},"page_size":{"default":25,"maximum":100,"minimum":1,"title":"Page Size","type":"integer"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Start Cursor"}},"title":"list_my_feedbackArguments","type":"object"}},{"name":"get_feedback","description":"Fetch one submission filed by the principal this token identifies, by id. Someone else's id — even in the same org — is 404, never 403, and so is any id for a token with no user identity of its own. Mirrors GET /v1/feedback/{id}.","input_schema":{"properties":{"feedback_id":{"format":"uuid","title":"Feedback Id","type":"string"}},"required":["feedback_id"],"title":"get_feedback_toolArguments","type":"object"}},{"name":"export_org","description":"Export an entire workspace as one self-describing JSON document (schema 'novumos.export'): org, members, boards, columns, cards, Notion-shape block bodies, and comments. Caller must be owner or admin in the org. Secrets (password/token hashes, webhook secrets, Stripe ids) are never included. Returns 413 export_too_large for a workspace past the size cap — export board by board instead. Mirrors GET /v1/orgs/{id}/export.","input_schema":{"properties":{"org_id":{"format":"uuid","title":"Org Id","type":"string"}},"required":["org_id"],"title":"export_orgArguments","type":"object"}},{"name":"export_board","description":"Export ONE board as a self-describing JSON document (schema 'novumos.export'): the board, its columns, every card, each card's Notion-shape block body and comments. Caller must hold the board's 'manager' role. Mirrors GET /v1/boards/{id}/export.","input_schema":{"properties":{"board_id":{"format":"uuid","title":"Board Id","type":"string"}},"required":["board_id"],"title":"export_boardArguments","type":"object"}}]}