Available Tools¶
Complete catalog of tools available through the MCP server and the TUI AI assistant. Tool availability depends on the safety mode.
Cloud Resources¶
DNS & Domains¶
| Tool | Safety | Description |
|---|---|---|
list_dns_zones | Read | List all DNS zones in the organization |
list_dns_records | Read | List DNS records for a specific zone |
list_domains | Read | List all registered domains with verification status |
create_dns_record | Write | Create a DNS record in a zone |
delete_dns_record | Destructive | Delete a DNS record |
Compute¶
| Tool | Safety | Description |
|---|---|---|
list_servers | Read | List all servers in the current project |
list_volumes | Read | List all block storage volumes |
list_k8s_clusters | Read | List all Kubernetes clusters |
Networking¶
| Tool | Safety | Description |
|---|---|---|
list_networks | Read | List all networks in the project |
list_routers | Read | List all routers in the project |
list_load_balancers | Read | List all load balancers in the project |
Billing¶
| Tool | Safety | Description |
|---|---|---|
get_billing_usage | Read | Get billing usage and cost breakdown |
list_invoices | Read | List all invoices for the organization |
Organization¶
| Tool | Safety | Description |
|---|---|---|
list_members | Read | List organization members |
list_projects | Read | List all projects in the organization |
Tickets¶
| Tool | Safety | Description |
|---|---|---|
list_tickets | Read | List support tickets |
get_ticket | Read | Get ticket details with comments |
create_ticket | Write | Create a new support ticket |
add_ticket_comment | Write | Add a comment to a ticket |
delete_ticket | Destructive | Delete a support ticket |
OpenProject (Project Management)¶
These tools talk to a self-hosted OpenProject instance. They are only available when openproject-url and openproject-api-key are configured — see the OpenProject integration guide.
Destructive operations (delete work package, archive project, delete time entry) are deliberately not implemented — use the OpenProject web UI.
Local conventions live in the mission repo
:pa reads OpenProject workflow conventions (status flows, required custom fields, time-entry rules, naming) from tools/operated/openproject.md in the mission control repo. Its body is delivered to the AI via mission_load_context (as the slug=openproject entry in the tools array) and can be refreshed mid-session with mission_tools slug=openproject. To customise how the assistant uses OpenProject in your org, edit that markdown file — no code change required.
Read¶
| Tool | Safety | Description |
|---|---|---|
op_list_projects | Read | List projects (filter by name substring or archived; default sort: name asc) |
op_get_project | Read | Get one project by numeric ID or string identifier |
op_list_work_packages | Read | List work packages. Default sort: updatedAt desc — most recently changed first, so "show me the 5 most recent" is just limit=5 (filters: project, assignee, status, type, query, open-only, updated-since) |
op_get_work_package | Read | Get one work package's metadata by ID. Comments/journal are NOT included — use op_list_work_package_activities. |
op_list_work_package_activities | Read | List the journal of one work package — comments and field-change history, oldest first. Each entry is typed (comment, change, or comment+change). |
op_list_time_entries | Read | List time entries (default sort: spentOn desc; filters: user, project, work package, date range) |
op_list_enums | Read | List statuses / types / priorities / time-entry activities — needed for filtering and creating |
op_list_users | Read | List active users; filter by name substring |
Conveniences:
- Pass
assignee=meoruser=meand the AI resolves it to your user ID via/users/me. - Every list tool accepts
sort_by(server field name) andsort_order(asc/desc). Defaults are picked so the AI usually doesn't have to specify them: WPs come back most-recently-updated first, time entries newest first, projects alphabetical. queryonop_list_work_packagesis a substring match against subject text (also matches numeric IDs); it does NOT search description bodies.
Write¶
Every write tool requires explicit per-call confirmation, even when the AI session has autoAcceptWrites enabled. Free-text fields written by the AI (descriptions, comments, time-entry notes) are auto-suffixed with — via bnerd.ai so a future reader can grep OpenProject for AI-authored content. Successful writes append a JSONL entry to ~/.bnerd/openproject-audit.jsonl for local review.
| Tool | Safety | Description |
|---|---|---|
op_create_work_package | Write | Create a work package in a project (subject + type required) |
op_update_work_package | Write | Update fields on a work package (subject, description, status, type, priority, assignee, dates). Optimistic concurrency handled internally via lockVersion. |
op_add_comment | Write | Post a journal comment on a work package |
op_log_time | Write | Create a time entry (decimal hours, e.g. 0.5 = 30 minutes) |
op_update_time_entry | Write | Edit an existing time entry's hours / date / comment / activity |
Pickup timers (:pa only)¶
These three wrap OpenProject's native ongoing time entries — a running timer is an OP time entry with ongoing=true, visible in the OP web UI from the moment it starts. Single timer per user; the wrapper enforces this client-side. Unlike the other write tools, timer tools are NOT confirm-gated — they fire on explicit pickup/finish chat actions.
| Tool | Safety | Description |
|---|---|---|
op_timer_start | Write | Start a pickup timer (creates an ongoing time entry). Errors if one is already running for the current user. |
op_timer_stop | Write | Stop the active timer: computes elapsed from startTime → now, PATCHes ongoing=false + hours. Refuses elapsed under 1 minute or over 24 hours; pass hours=<decimal> to override and finalise anyway (also dismisses a stuck timer with corrupt/empty startTime, e.g. hours=0.01). |
op_timer_status | Read | Report the active timer (id, work package, project, started_at, live elapsed) or {"active": false}. |
Ticket-draft preview (:pa only)¶
pa_propose_ticket is a no-op-on-the-server stub that the chat session intercepts to render a full work-package draft for review before op_create_work_package ever fires. Unlike the standard write-confirmation modal — which truncates the description body — this view shows every populated field and the entire markdown body without any line cap.
| Tool | Safety | Description |
|---|---|---|
pa_propose_ticket | Read | Present a full ticket draft for review. Returns one of three JSON decisions: {"decision":"create"}, {"decision":"refine","feedback":"…"}, or {"decision":"cancel"}. |
The PA system prompt requires the AI to call pa_propose_ticket first whenever you ask for a new ticket. On create, the AI then calls op_create_work_package with the same payload — that tool's standard confirmation still fires once as an integrity check. On refine, your typed notes are returned to the AI and it drafts a v2. On cancel, the AI drops it.
Inbox triage (multi-messenger, :pa only)¶
Source-agnostic triage surface across every configured messaging backend (Slack today, Email next, future Mattermost/Teams). Dispatched through the in-process inbox.Hub; each backend (a "source") implements the same MessageSource contract so the same tool call works regardless of which messenger an entry came from. Identifiers are namespaced as source:account:conv:msg (e.g. slack:default:C0123:1700.000200) so the AI can pass them back without ambiguity.
| Tool | Safety | Description |
|---|---|---|
inbox_list | Read | Newest-first entries across all sources. Optional source / account / limit filters. Each item carries explicit source and account fields plus the canonical id. |
inbox_search | Read | Filter by channel (conversation id), user (sender id), text substring, since/until (RFC3339), include_archived. Optional source/account scoping. |
inbox_unread | Read | Unread count (optionally scoped by source/account) and, with include_entries=true, the unread entries themselves. |
inbox_mark_read | Write | Mark one entry read by id. The hub parses source/account from the id and routes to the right inbox. |
inbox_dismiss | Write | Hide an entry from default lists. Still searchable via include_archived. |
inbox_get_thread | Read | Fetch a thread root + replies via the source's native API (Slack conversations.replies, IMAP FETCH of the References chain). Takes explicit source/account/conv_id/thread_key. |
inbox_propose_reply | Read | Source-aware full-preview proposal block (intercepted by the chat session). Returns {"decision":"send"} / {"decision":"refine","feedback":"…"} / {"decision":"cancel"}. ALWAYS call before inbox_send_reply — the body renders untruncated and the email path additionally surfaces Subject + Cc rows so the user can vet the full header. On send the AI calls inbox_send_reply with the matching source/account/conv_id/thread_key. |
inbox_send_reply | Write | Post a reply via the right backend (Slack chat.postMessage, SMTP for email). Required: source, account, conv_id, body. Optional: thread_key, subject/cc for email. The configured signature suffix is appended automatically. |
After acting on an item, the AI is expected to call inbox_mark_read or inbox_dismiss so the unread indicator stays accurate. The reply flow follows the same shape as ticket creation: inbox_propose_reply → user picks Send → AI calls inbox_send_reply (one extra confirm fires as integrity check). On refine, the user's notes are returned to the AI for a v2 draft.
Peek overlay (Ctrl-S) — multi-source surface. The TUI peek shows the merged stream across every configured source with [S] / [E] source badges. Keys 1 / 2 filter to Slack / Email; 0 returns to the merged view. The filter persists across overlay open/close so a focused triage session stays focused.
Slack triage (:pa only) — source-specific escape hatches¶
Optional integration that runs only when slack.enabled: true and a bot token are configured (see Slack setup). The Slack stack implements the unified inbox_* surface above; the per-source tools below remain registered for behaviour that has no source-agnostic equivalent (subscriptions, native search, watched threads) plus a deprecation window for the pre-unified inbox/reply tools.
Listener behaviour: filters incoming Slack events down to mentions, DMs, and replies in tracked threads, then persists them to a durable JSONL inbox under ~/.local/state/bnerd/slack-inbox/default/. Messages never auto-inject into the chat log — the indicator (📬 N in the header) and the Ctrl-S peek overlay are the user-facing surfaces; the AI triages on demand via the tools above.
Inbox reads & state (deprecated — prefer inbox_*)¶
| Tool | Safety | Description |
|---|---|---|
slack_inbox_list | Read | List recent inbox entries (newest-first JSON) including id, status (unread/read/dismissed), and via (live/catchup/backfill). Prefer inbox_list for new sessions. |
slack_inbox_search | Read | Filter the local inbox by channel, user, text substring, since/until (RFC3339), and include_archived. Prefer inbox_search. |
slack_inbox_unread | Read | Returns the unread count and (with include_entries=true) the unread entries themselves. Prefer inbox_unread. |
slack_inbox_mark_read | Write | Mark one inbox entry (id from slack_inbox_list) as read. Prefer inbox_mark_read. |
slack_inbox_dismiss | Write | Mark one inbox entry as explicitly noise. Prefer inbox_dismiss. |
slack_get_thread | Read | Fetch a thread's root + replies from the Slack API for context (channel, thread_ts). Prefer inbox_get_thread. |
slack_search | Read | Hits Slack's own search.messages index for queries that go beyond the local inbox window. Requires the optional slack.user-token (xoxp-…). No source-agnostic equivalent — Slack search is genuinely Slack-specific. |
Outbound replies & ticket conversion¶
| Tool | Safety | Description |
|---|---|---|
slack_propose_reply | Read | Intercepted by the chat session — renders a full untruncated preview of the proposed reply with Send / Refine / Cancel options. Returns the user's decision as JSON, mirroring pa_propose_ticket. The unified inbox_propose_reply arrives in the unified-peek phase. |
slack_send_reply | Write | Post the approved reply via Slack's chat.postMessage. The configured signature suffix (default — via bnerd.ai) is appended automatically. Prefer inbox_send_reply. |
Watched threads (Slack-specific)¶
| Tool | Safety | Description |
|---|---|---|
slack_watch_thread | Write | Add a thread to the local watched-thread set so future replies surface in the inbox. Local-only persistence; no remote effect. Idempotent. |
slack_unwatch_thread | Write | Remove a thread from the watched-thread set. |
The reply flow mirrors ticket creation: slack_propose_reply → user picks Send → AI calls slack_send_reply (one extra confirm). On Refine, the user's notes are returned to the AI for a v2 draft. Posting in a channel auto-watches that thread so follow-ups land in the inbox.
After acting on an item (replying, ticketing, or deciding it's noise), the AI is expected to call slack_inbox_mark_read or slack_inbox_dismiss to keep the unread indicator accurate.
In :pa you can also press r to ask the AI to draft a reply to the latest Slack message, or t to turn the latest thread into an OpenProject ticket via slack_get_thread + pa_propose_ticket. The Ctrl-S peek overlay's r/t hotkeys do the same but target the row under the cursor instead of the most-recent event.
Relations & dependencies¶
Relations link two work packages — parent/child, blocks/blocked, precedes/follows, relates, duplicates/duplicated, includes/partof, requires/required. They answer "what's blocking the release?" and let the AI build dependency trees.
| Tool | Safety | Description |
|---|---|---|
op_list_relations | Read | All relations declared on one work package (both directions) |
op_create_relation | Write | Link two work packages with a relation type (optional lag for follows/precedes) |
op_delete_relation | Destructive | Remove a relation by ID. Unlinks two work packages — does NOT delete either work package. Reversible by re-creating the relation. |
Watchers¶
| Tool | Safety | Description |
|---|---|---|
op_list_watchers | Read | Users currently watching a work package |
op_add_watcher | Write | Subscribe a user (or me) to a work package |
op_remove_watcher | Destructive | Unsubscribe a user. Reversible. |
The "Destructive" classification on op_delete_relation and op_remove_watcher is intentional — they are DELETE operations under the hood and the safety model treats DELETE uniformly. They differ from primary-data deletes (which are not implemented at all) because the entities they unlink remain intact.
Versions, memberships, categories¶
Release planning + team visibility. Versions (a.k.a. milestones / releases) live inside a project; work packages can be assigned to a version via op_create_work_package/op_update_work_package. Memberships answer "who's on this project, in what role". Categories are project-level classifications optionally with a default assignee.
| Tool | Safety | Description |
|---|---|---|
op_list_versions | Read | List versions (filterable by project) |
op_create_version | Write | Create a milestone in a project (name, dates, status, sharing) |
op_list_memberships | Read | List project memberships + roles. Filter by project and/or principal (me) |
op_list_categories | Read | List categories defined in one project |
op_create_work_package and op_update_work_package accept version and category (numeric IDs). Pass empty string to clear an existing version/category on update.
Users (admin)¶
User CRUD against /api/v3/users. All write operations require admin rights on the configured OpenProject API key — non-admin tokens get an actionable "permission denied" error. Every write is gated by the standard per-call confirmation prompt and appends to the audit log.
| Tool | Safety | Description |
|---|---|---|
op_get_user | Read | Fetch one user (any status) by numeric ID. Use this before op_unlock_user since op_list_users only shows active accounts. |
op_create_user | Write | Create a new user (login, first_name, last_name, email required). Omit password for SSO/LDAP instances; supply one for built-in auth. Optional admin, language, status. |
op_update_user | Write | Sparse update — only supplied fields change. Includes optimistic concurrency via the resource's lockVersion. |
op_set_user_password | Write | Reset a user's password to a new value. The password is sent to OpenProject but never written to the audit log (recorded as ***). Communicate the new password to the user out-of-band. |
op_lock_user | Write | Lock the account (blocks login, preserves history). Reversible via op_unlock_user. |
op_unlock_user | Write | Restore login access to a locked account. Implemented as DELETE /users/{id}/lock per the OP REST shape. |
Notes:
- Hard
DELETE /users/{id}is intentionally not exposed — locking preserves references and is reversible. - Resending invitation emails is not in the OpenProject v3 API (only available in the admin web UI). The AI will advise you to use the OP UI for that workflow.
Groups (admin)¶
Group CRUD against /api/v3/groups. The same admin/confirmation/audit guarantees as users apply.
| Tool | Safety | Description |
|---|---|---|
op_list_groups | Read | List groups (id + name only). Use op_get_group for the member list. |
op_get_group | Read | Fetch one group including its member user IDs. |
op_create_group | Write | Create a group. Pass members as a comma-separated list of user IDs (e.g. "3,7,12"); omit for an empty group. |
op_update_group | Write | Rename and/or replace the full member list. Omit members to leave members alone; pass empty string to clear them. Prefer the per-user tools below for single-member edits. |
op_delete_group | Destructive | Dissolve a group. Member users are NOT deleted — only the group itself. |
op_add_user_to_group | Write | Append one user to a group's members. No-op if already a member. Internally does a GET + PATCH using lockVersion for optimistic concurrency. |
op_remove_user_from_group | Write | Drop one user from a group's members. No-op if not a member. |
Form schemas & custom fields¶
Form-schema introspection lets the AI discover what fields exist before writing — eliminating "guess and 422" loops. Custom fields are supported on both create and update.
| Tool | Safety | Description |
|---|---|---|
op_describe_work_package_form | Read | Returns the schema for a work-package form: required/writable flags, allowed values for constrained fields (status, type, priority, …), and any custom fields the project defines. Pass project= for a new-WP schema, work_package= to introspect an existing WP, optionally narrow with type=. |
op_create_work_package and op_update_work_package accept a custom_fields parameter — a JSON-stringified object mapping customFieldN keys to values:
- Scalar values (string, number, bool) for text/integer/date/boolean custom fields → routed to the request body
- HAL hrefs (e.g.
"/api/v3/users/3"or"/api/v3/projects/3") for user/list/enum custom fields → routed to_linksautomatically
Recommended flow: call op_describe_work_package_form project=X type=Y first to see which custom fields exist and whether they expect scalars or hrefs, then pass them via custom_fields on the create/update call.
Notifications, saved queries, attachments¶
The leadership inbox + reusable filters + read-only file attachments.
| Tool | Safety | Description |
|---|---|---|
op_list_notifications | Read | Your in-app notifications, newest first. Filter unread_only=true and/or reason=mentioned (or assigned/watched/commented/…) |
op_mark_notification_read | Write | Mark one notification read. Reversible by re-opening it in the UI |
op_list_queries | Read | List saved queries (filters) the user has stored. Pass project= to scope. |
op_run_query | Read | Execute a saved query and return its work packages — useful for reusing "My open bugs", "This sprint", etc. instead of reconstructing the filter |
op_list_attachments | Read | Files attached to a work package — filename, size, content-type, download URL. Upload is not supported. |
Kubernetes (Direct Cluster Access)¶
These tools interact directly with a Kubernetes cluster via kubeconfig.
Read Operations¶
| Tool | Safety | Description |
|---|---|---|
kube_cluster_info | Read | Cluster overview: version, nodes, capacity |
kube_list_namespaces | Read | List namespaces with status and age |
kube_list_nodes | Read | List nodes with status, roles, resources |
kube_list_pods | Read | List pods (all namespaces or filtered) |
kube_get_pod | Read | Detailed pod info: containers, conditions, events |
kube_get_pod_logs | Read | Get pod logs (last N lines) |
kube_list_deployments | Read | List deployments with replica counts |
kube_list_services | Read | List services with type, IPs, ports |
kube_list_ingresses | Read | List ingresses with hosts and TLS status |
kube_list_configmaps | Read | List configmaps (keys only, not values) |
kube_list_secrets | Read | List secrets (names and types only) |
kube_list_pvcs | Read | List PersistentVolumeClaims |
kube_list_statefulsets | Read | List StatefulSets |
kube_list_daemonsets | Read | List DaemonSets |
kube_list_jobs | Read | List Jobs |
kube_list_events | Read | List recent events (up to 50) |
kube_list_hpa | Read | List HorizontalPodAutoscalers |
kube_describe | Read | Describe any resource (via kubectl) |
kube_read_file | Read | Read a file inside a running pod |
kube_list_dir | Read | List directory contents in a pod |
kube_find_file | Read | Find files by pattern in a pod |
Write Operations¶
| Tool | Safety | Description |
|---|---|---|
kube_scale | Write | Scale a Deployment/StatefulSet/ReplicaSet |
kube_rollout_restart | Write | Rolling restart of a workload |
kube_set_image | Write | Update container image |
kube_label | Write | Add/update resource labels |
kube_annotate | Write | Add/update resource annotations |
kube_cordon | Write | Mark node as unschedulable |
kube_uncordon | Write | Mark node as schedulable |
kube_exec | Write | Execute command in a pod |
kube_apply | Write | Apply a Kubernetes manifest |
kube_apply_dry_run | Write | Dry-run validate a manifest |
Destructive Operations¶
| Tool | Safety | Description |
|---|---|---|
kube_delete | Destructive | Delete a Kubernetes resource |
kube_drain | Destructive | Drain a node (evict pods) |
Helm¶
| Tool | Safety | Description |
|---|---|---|
helm_list | Read | List Helm releases |
helm_status | Read | Get release status |
helm_get_values | Read | Get release values |
helm_upgrade | Write | Install or upgrade a release |
helm_rollback | Destructive | Rollback to a previous revision |
helm_uninstall | Destructive | Uninstall a release |
File System¶
| Tool | Safety | Description |
|---|---|---|
fs_read_file | Read | Read a local file (max 128KB) |
fs_list_dir | Read | List directory contents |
fs_search_content | Read | Search file contents with regex |
fs_glob | Read | Find files matching a glob pattern |
fs_write_file | Write | Create or overwrite a file (not available in :pa) |
fs_patch_file | Write | Patch a file with exact text replacement (not available in :pa) |
Mission Control (:pa write surface)¶
:pa cannot write outside the mission repo. Writes flow through two families: typed proposals for the scheduling files (schema-validated and auto-archiving), and generic mission-scoped writes for everything else.
| Tool | Safety | Description |
|---|---|---|
mission_propose_today | Write | Propose / write today.md (typed; auto-archives prior day) |
mission_propose_week | Write | Propose / write current.md (typed; auto-archives prior week) |
mission_write_file | Write | Create or overwrite any other file inside the mission repo (rejects paths outside the repo and the two scheduling files) |
mission_patch_file | Write | Surgical exact-text replacement inside any other mission-repo file (same path restrictions as mission_write_file) |
Git¶
| Tool | Safety | Description |
|---|---|---|
git_status | Read | Show working tree status |
git_diff | Read | Show changes (staged or unstaged) |
git_log | Read | Show recent commit history |
git_init | Write | Initialize a new repository |
git_add | Write | Stage files for commit |
git_commit | Write | Create a commit |
git_push | Destructive | Push commits to remote |
Shell¶
| Tool | Safety | Description |
|---|---|---|
shell_exec | Write | Execute a shell command |
Web¶
| Tool | Safety | Description |
|---|---|---|
web_research | Read | Search the web and read top results |
web_fetch | Read | Fetch a web page by URL |
AI Internal¶
| Tool | Safety | Description |
|---|---|---|
think | Read | Think through a problem step-by-step |
task_notes_write | Read | Save persistent working notes |
task_notes_read | Read | Read saved working notes |
get_current_context | Read | Get current session context |
ask_question | Read | Ask the user a clarifying question |
task_create | Read | Create a task to track progress |
task_update | Read | Update task status |
task_list | Read | List all tasks |
save_report | Write | Save an investigation report |