Connect AI agents (MCP)
Firefight ships a Model Context Protocol server, so AI agents like Claude can take part in your incidents directly. An agent connected over MCP can search incidents, pull a full incident timeline, inspect alerts and how they routed, look up ownership in the service catalog, find what runs where on the resource map, and dry-run your alert routing rules. It can ask Halon, Firefight’s own agent, a question or start an investigation, and read logs, metrics and deploys of anything on the map through your integrations. With permission it can also work an incident the way a person does: declare one, raise and pick up work, claim a runbook step, pull people into the channel, ask a named person to respond, and close it out. It can maintain your catalog, runbooks and routing rules, and resolve approvals, too. Everything an agent does is recorded under Gateway → Activity.
Endpoint
Section titled “Endpoint”The server speaks Streamable HTTP at a single endpoint.
POST https://app.firefight.app/mcpAll data is scoped to the workspace of the token you authenticate with.
Authenticating
Section titled “Authenticating”There are three ways to connect, depending on what is doing the connecting.
OAuth, for interactive clients
Section titled “OAuth, for interactive clients”Clients that support MCP OAuth, such as Claude Code, connect without you ever handling a token. Add the server and start using it.
claude mcp add --transport http firefight https://app.firefight.app/mcpOn first use, your browser opens Firefight’s consent screen. It names the client asking for access, the workspace it would reach and who you are signed in as, says what the client will be able to do, and shows where your browser goes once you authorize, such as an app on your own computer or the client’s website. Check that address before you click, and click Deny if you did not start the connection. If you belong to more than one workspace, pick the one you want it to reach. Click Authorize and the client is connected. The client registers itself automatically through dynamic client registration, PKCE is required, and access tokens are short-lived with refresh rotation. The connection acts as you, with the same access you have in the workspace.
Agent token, for an AI acting as itself
Section titled “Agent token, for an AI acting as itself”An AI that takes part in incidents gets its own identity rather than borrowing yours. Create it under Gateway → Agents, which hands you its token once, and configure your client to send that.
claude mcp add --transport http firefight https://app.firefight.app/mcp \ --header "Authorization: Bearer ff_4kWm2xPqR8vNcT6yBhJd3fLzGaU9sEnQoXwA"Everything the agent does is recorded under its own name, so an incident it declared says the agent declared it, and an action item it picked up shows the agent holding it. It holds only the abilities you granted it, whatever access you have yourself. Agent tokens do not expire. Rotate one at any time from its row. See Agents.
API key, for pipelines and CI
Section titled “API key, for pipelines and CI”Automation with nothing to say on a timeline passes a Firefight API key as a Bearer token instead. Create one under Developer → API Keys, then configure your client to send it.
claude mcp add --transport http firefight https://app.firefight.app/mcp \ --header "Authorization: Bearer ff_4kWm2xPqR8vNcT6yBhJd3fLzGaU9sEnQoXwA"For Cursor, put the same thing in .cursor/mcp.json.
{ "mcpServers": { "firefight": { "url": "https://app.firefight.app/mcp", "headers": { "Authorization": "Bearer ff_4kWm2xPqR8vNcT6yBhJd3fLzGaU9sEnQoXwA" } } }}Any other MCP client works the same way, either through OAuth discovery at /.well-known/oauth-protected-resource or with the Authorization: Bearer header.
Multiple workspaces
Section titled “Multiple workspaces”A connection reaches one workspace, whichever way you authenticated. An OAuth connection is granted for the single workspace you picked on the consent screen, and an API key belongs to the workspace you created it in. There is no setting that widens a connection to cover more.
To give an agent a second workspace, add the server again under a different name.
claude mcp add --transport http firefight-acme https://app.firefight.app/mcpAuthorize that one and pick the second workspace on the consent screen. For a headless agent, create an API key in the second workspace and pass it the same way. Each connection stands on its own, so its tools appear under the name you gave it, and revoking one leaves the other working.
An agent is told which workspace it reaches as soon as it connects, so you can ask it directly rather than working it out from what it returns.
The Connected agents list under Developer → API Keys shows the connections for the workspace you are viewing. Switch workspaces to see the rest.
These tools read your workspace and change nothing.
| Tool | What it returns |
|---|---|
search_incidents | Incident summaries, newest first. Filter by status, severity, lifecycle stage, free-text query, and declared-at time range. |
get_incident | One incident in full, by ID or identifier like INC-42. Details, timeline events (each update with its message and the fields it changed), postmortem status, attached alerts, every incident role with whoever holds it, the open action items with any tracked issue’s link, the attached runbooks with their steps, and its pages in PagerDuty or Opsgenie with where each stands. |
search_similar | Incidents, postmortems and findings that read like a situation you describe, nearest first, each saying whether it is still open. Findings are included when the key may read investigations. |
search_alerts | Ingested alerts, newest first. Each shows its source, firing status, routing state, the rule that matched, and the incident it attached to. |
search_catalog | Catalog entries with their attributes and relationships, such as which team owns a service. Filter by type, name query, or exact slug. |
get_resource_map | The first place to find which provider and account hold a named domain, service or database. Without a resource, every resource grouped by provider and account, with how each connection last read the map. Once the map holds 300 resources or more, it returns counts instead of a list, by provider, kind, environment and health, with the resources most others depend on, and names the tools below for finding the rest. With a resource’s name or ID, its fact sheet: where it runs, its page, the catalog services it runs and who owns them, what people confirmed about it, recent incidents on those services, what normal looks like for its metrics over the last week, the log lines it usually prints, its key checks, and every resource within two links with how each link was found. Only what runs in the environments the caller can read, see Permissions. |
find_resources | Resources on the map, 50 to a page by default and up to 200. Filter by provider, kind, account, environment, status, health, owning team, catalog entry, provider tag (team or team=payments), exact details, the start of a name, or what changed since a time. Removed resources are left out unless you ask for them. Sort by name or by how many resources depend on each. Each row has the resource’s ID, which the other map tools take, and the total counts up to 10,000. Pass next_cursor back for the next page. |
get_resource | One resource by its name, its provider’s ID or its ID on the map. The same fact sheet as get_resource_map without the resources two links away, plus how many links lead in and out of it by relation. |
get_resource_links | The links into and out of one resource, 100 to a page by default and up to 500. Choose the direction, the relations, and whether you want confirmed links, suggestions or both. Each link reads as a sentence, such as “web uses orders-db”, and says how it was found. A suggestion also says how sure Firefight is and why. |
get_resource_neighbours | The resources one link away from a resource, what it depends on and what depends on it, grouped by relation. Confirmed links only unless you ask for suggestions, and it says how many suggestions it left out. Up to 200 resources. |
traverse_resource_map | Follows links from a resource towards what it depends on or what depends on it, 2 links by default and up to 6. Each resource reached says how many links away it is and which link reached it. Narrow it by relation or by kind. Up to 200 resources, and it says how many more it reached. |
blast_radius | Everything that fails with a resource, up to 10 links away, counted by kind, provider and environment, the catalog services they run with the teams that own them, the open incidents on those services, and the 25 dependents most others rely on. What unconfirmed suggestions would add is counted separately and never in the total. |
resource_map_stats | The map in numbers. Counts resources by provider, kind, account, environment, status or health, with the same filters as find_resources, and says how each connection last read the map, how many suggested links are waiting for someone to review, and what changed in the last day. |
search_map | One search across the map, the catalog and confirmed memories, ranked together with an exact name or id first. Give a query, such as a name, part of one, a tag or what a service does, and optionally types (resource, catalog_entry, memory) and the map’s filters, such as provider, kind, environment or owner. Each result says what it is, why it matched and links to the page that opens it. Only the kinds the credential may read are searched, and the response names any it left out. Page with next_cursor. |
evaluate_routing | A dry run of alert routing against hypothetical alert fields. Returns the matching rule, the outcome, a per-condition trace, and warnings when a routing role your rules depend on is not set on any catalog attribute. Nothing is created or notified. |
search_runbooks | Runbook listings with name, summary, other names, step count, whether Halon can run it, and the workflow each runs with whether it can run now. Filter by a query that matches the name, summary or another name. |
get_runbook | One runbook in full, by slug. The complete procedure content, ordered steps (with the tool and arguments of a step Halon runs), external link, for a runbook Halon can run its inputs, other names and what it watches afterwards, and the workflow it runs with that workflow’s steps, questions and page. |
list_skills | Your workspace’s own skills, with when Halon uses each, whether it is on, its version and how many workflows run it. |
get_skill | One skill in full, by name or ID: its tools, its steps and the version to send back when changing it. |
list_workflows | Your workflows, each with its name and other names, what starts it, whether it is live, when it runs next, how its last run went, its steps and the inputs a run asks for. |
list_handbook_pages | The handbook pages in the order Halon reads them, each with its kind, link and length. |
get_handbook_page | One handbook page in full, by its ID or title, with the wording_id an edit sends back. |
search_approvals | Approval requests and their status, each naming who asked in principal_name, such as Halon, for Maya Ortiz. A request for a connection’s tool names the connection the way you tell it apart, such as Faylee (Northflank), and its provider. |
list_abilities | Everything that can be granted, with its risk level and whether an approval rule can hold it. |
list_principals | The people, agents and service keys of the workspace, and Firefight’s own built-in agents, each with the grants it holds. |
list_member_access | What members can read and change in each connected tool, the rows of Who can do what on the Permissions screen. Each row’s read and change says everyone, nobody or chosen with the people chosen, and who a row leaves out but still reaches the tool through another permission set. |
search_activity | The gateway’s audit log, filtered by decision or ability, each row naming who made it in principal_name. A call through a connection names that connection, such as Faylee (Northflank), and its provider. |
list_unattended_rules | The changes Halon may make on its own, each with why it cannot act yet and how many times Halon acted under it. See Halon on call. |
list_allowances | The risky changes Halon may make in a person’s chat without asking, each with how many changes Halon made under it, and whether safe changes run without asking. See Changes without asking. |
get_form | One lifecycle form and every field on it, hidden ones included, with each field’s type, whether it is visible, required, or locked, whether the form asks it right now and why not, and any conditions gating it. |
get_workspace_config | How the workspace is set up, in one call: severities, statuses with their lifecycle stage, incident types, incident roles, alert sources and webhooks. Disabled entries are included and marked. A caller who may read the workspace settings, which is an admin’s connection, gets those too. |
list_integrations | What can be connected to Firefight and what already is, by category, such as Cloud and hosting or Observability. Without a category, what is connected in each one. It never connects anything. Connecting happens under Configure → Integrations. For GitHub, GitLab, Bitbucket and Azure DevOps it also lists each connection’s paths Halon may not change. |
list_agents | The agents in the workspace, each with its live tokens and how many abilities it holds. |
list_api_keys | The workspace’s service keys, with the abilities each holds and when it was last used. Never the token itself. |
get_postmortem | The postmortem written for an incident, as HTML, with its status and who wrote it. |
list_notices | The problems Halon raised on its own from scheduled checks and leaked secrets, newest first, with their urgency, the day each becomes a problem, and when Halon last said it. See Monitoring and security. |
get_incident_transcript | What people said in the incident’s channel, oldest last, with who said it and when. Needs its own ability and the workspace setting. |
what_changed | One list, newest first, of what changed around a resource, a catalog service, or the whole workspace: deploys and runs, settings and status changes, changes a provider reported, and changes made through Firefight, each with its link. See What changed. |
check_status_page | An outside provider’s public status page, read now, by the provider’s name or by an address your errors mention. |
blind_spots | The outside systems your apps use that nothing connects, with where each was seen and how to connect it. |
Tools that change things
Section titled “Tools that change things”These need a credential with permission to make the change, and every call is recorded.
| Tool | What it does |
|---|---|
update_workspace_settings | Changes the settings under Settings → Workspace, such as transcript access, how long after an incident ends its channel is archived, web search, and which issue tracker incident items are kept in step with. Admin-only. |
update_protected_paths | Adds or removes paths Halon may not change in a GitHub, GitLab, Bitbucket or Azure DevOps connection’s repositories, the list under Code changes on the connection. Give the connection and the paths to add or remove. Answers with the whole list. Admin-only. |
upsert_catalog_entry | Creates or updates a catalog entry |
suggest_resource_link | Suggests a link between two resources on the map, with the evidence. It shows as a suggestion until someone confirms or dismisses it |
delete_catalog_entry | Removes a catalog entry |
upsert_catalog_type | Creates or changes a kind of thing the catalog holds, and the attributes its entries carry |
delete_catalog_type | Removes a catalog type and the entries under it |
upsert_routing_rule | Creates or updates an alert routing rule |
delete_routing_rule | Removes a routing rule |
update_routing_config | Changes routing configuration |
upsert_runbook | Creates or updates a runbook |
upsert_handbook_page | Adds a handbook page, or changes one by its ID or title. With wording_id, an edit made over somebody else’s is refused |
delete_handbook_page | Deletes a handbook page and its history |
upsert_skill / delete_skill | Creates, changes, turns on or off, or removes one of your workspace’s skills |
upsert_custom_field | Creates or updates a custom field and its options |
upsert_form_field | Attaches a field to a form, or changes its visibility, required setting, or conditions |
upsert_severity / delete_severity | Creates, changes or removes a severity |
upsert_status / delete_status | Creates, changes or removes a status |
upsert_incident_type / delete_incident_type | Creates, changes or removes an incident type |
upsert_incident_role / delete_incident_role | Creates, changes or removes an incident role |
upsert_alert_source / delete_alert_source | Creates, changes or removes an alert source |
upsert_outbound_webhook / delete_outbound_webhook | Creates, changes or removes a Firefight outbound webhook, which posts your incident events to a URL you own |
test_outbound_webhook | Sends a test delivery to a Firefight outbound webhook, replaying the newest matching event from your workspace |
upsert_agent / delete_agent | Creates, changes or removes an agent |
rotate_agent_token / revoke_agent_token | Issues a new token for an agent, or ends one |
upsert_api_key / delete_api_key | Creates, changes or removes a service key |
declare_incident | Opens a new incident and its channel |
post_incident_update | Posts an update on an incident |
resolve_incident | Closes an incident |
cancel_incident | Cancels an incident that turned out not to be one |
reopen_incident | Reopens an incident that came back |
create_action_item | Adds a piece of work to an incident and posts it in the channel |
assign_action_item | Takes a piece of work, or hands it to someone else |
complete_action_item | Marks a piece of work done |
rename_action_item | Changes what a piece of work says needs doing |
reopen_action_item | Opens a piece of work that was done again |
unassign_action_item | Lets go of a piece of work, so nobody holds it |
create_action_item_issue | Opens a piece of work’s issue in your issue tracker, or tries again after it failed |
claim_runbook_step | Takes one step of an attached runbook |
escalate_incident | Asks a named person to pick the incident up |
page_on_call | Raises a page in PagerDuty or Opsgenie on the service or team that has the person on call, kept in step with the incident |
invite_responders | Brings people into the incident channel |
link_incident | Links two incidents, or marks one a duplicate of the other |
give_shoutout | Thanks someone for their work on the incident |
post_to_channel | Posts a message as Firefight in any channel it can post in, such as release notes in #general. Name the channel by its name, ID or link. A private channel needs Firefight invited first. The post names who asked for it |
assign_incident_role | Puts one person in an incident role, or clears it |
attach_runbook | Attaches a runbook to an incident |
dismiss_timeline_note | Removes one AI note from an incident’s timeline |
approve_approval | Approves a pending request |
deny_approval | Denies a pending request |
upsert_permission_set | Creates or updates a permission set. A built-in pack cannot be changed |
delete_permission_set | Removes a permission set from everyone holding it. A built-in pack cannot be deleted |
grant_ability | Grants an ability or a set to a person, agent or key |
revoke_grant | Revokes a grant |
set_member_access | Sets what members can read or change in one connected tool, the same as a Who can do what dropdown. Give the row’s permission_set from list_member_access and everyone, nobody or chosen with people |
upsert_approval_rule | Creates or updates an approval rule. Refused with a token, since approval rules are changed only by a person |
delete_approval_rule | Removes an approval rule. Refused with a token |
upsert_unattended_rule | Creates or updates an unattended rule, or turns one on or off. Refused with a token |
delete_unattended_rule | Removes an unattended rule Halon never acted under. Refused with a token |
upsert_allowance | Adds or changes an entry of what Halon may do without asking, or turns one on or off. Refused with a token, so only Halon uses it, in a chat, once you confirm it |
delete_allowance | Removes an entry Halon never made a change under. Refused with a token |
start_postmortem | Opens the postmortem for a resolved incident, empty or drafted by AI |
update_postmortem | Replaces the body of a postmortem |
set_postmortem_status | Moves a postmortem to draft, in progress, in review or completed |
create_workflow | Makes a workflow, from a template in the library or from a trigger and steps. It goes live when you may publish it, and otherwise waits as a draft while your admins are asked |
run_workflow | Runs a published workflow by its name, with a value for each input it asks for, and answers with the run’s page |
update_workflow | Changes one workflow’s settings, the same ones its Settings page holds, such as its most runs per hour and spending ceiling per run |
update_workflow_settings | Sets how many days a workflow run keeps what each step did, from 1 to 400. Admin-only |
upsert_catalog_entry takes attributes by name, and an attribute pointing at another entry accepts that entry’s slug, so an agent can set which team owns a service without looking up an ID.
upsert_catalog_type shapes the catalog itself, adding a kind of thing such as Datastore and saying what every entry under it carries. Attributes are matched by name, so resending a list renames rather than replaces and the entries already holding a value keep it, and sending no attributes leaves the shape alone. A reference attribute names the type it points at by slug. Built-in types keep their slug and their own attributes, and delete_catalog_type refuses a type another type points at, naming the attribute in the way. See Entries, attributes & relationships.
upsert_custom_field and upsert_form_field let an agent change what responders are asked during an incident, so they are worth granting deliberately. Options are matched by label, which means resending a list renames rather than replaces, and the incidents already holding an option keep pointing at it. Conditions accept slugs, so you can gate a field on production without looking up an ID first. A condition on a custom field names that field by its key and takes option labels or catalog entry slugs as its values, so gating on an Affected service field being checkout needs no IDs either. Severity and Status refuse to be hidden or made optional here exactly as they do in the dashboard. Call get_form first to see what a form holds, including hidden fields, because an update replaces the set rather than adding to it.
The gateway tools let an agent administer access the same way an admin does on the dashboard: see who holds what, bundle abilities into sets, grant and revoke, and decide which abilities wait for approval. They check permissions, which only an admin holds and which cannot be granted to a key, so they work over a personal token or an OAuth connection made by an admin, never a service key. Every one of these that changes something waits for an admin to approve it, whatever the connection holds and whatever your approval rules say, so a person sees each change to who may do what before it runs. The tool result gives the approval id, the request is decided on the Approvals page or in Slack, and the agent retries the same call with approval_id within the hour. The same goes for approve_approval and deny_approval, for the API key, agent and webhook tools, and for creating or rotating a token. A request that was made with a token is approved only by a person on the Approvals page or in Slack, never with approve_approval. Approval rules, what Halon may change on its own and what it may do without asking are read here but changed only by a person, so upsert_approval_rule, delete_approval_rule, upsert_unattended_rule, delete_unattended_rule, upsert_allowance and delete_allowance refuse a token with a message saying so. Connected as you, an agent can still be your chief of staff, spotting a request that names you in search_approvals and telling you. See Permissions and Approvals.
attach_runbook takes the incident and a runbook slug, and posts that runbook’s steps in the incident channel. Runbooks whose conditions match attach on their own, so this is for the ones that do not. Attaching the same runbook twice does nothing the second time. It needs incidents: update, not a runbooks permission, because it changes the incident rather than the runbook.
dismiss_timeline_note takes the incident and the id of a note, and removes that note from the timeline. It is for the case where a note reads a conversation wrong, crediting the wrong person or taking a joke for a decision. The note stops coming back from get_incident. Note ids come from that same timeline, where each note also names its kind, so an agent can ask for the root cause of a past incident directly. See Timeline notes.
Working an incident
Section titled “Working an incident”The incident tools are the same operations a person has in Slack, so an agent using them is a responder rather than a reporter. What it does shows up in the incident channel as it happens, and on the incident’s timeline under the agent’s own name.
declare_incident, post_incident_update, resolve_incident, cancel_incident and reopen_incident all ask exactly what your workspace’s incident forms ask a person, custom fields included. Call get_form first with the form you are about to submit, then pass the answers keyed the way it named them. A field the form does not ask for is refused rather than quietly dropped, and a missing required field comes back naming what is missing. A field get_form marks as not asked right now is one the form will not take, so leave it out. When you update, resolve or cancel an incident, a required answer you leave out keeps the incident’s current value, so send only what changes.
create_action_item adds a piece of work and posts it in the channel, exactly like /ff action. Pass kind as action for work during the incident or followup for work after it, and member to hand it straight to someone.
assign_action_item takes a piece of work when you leave member out, which is the I can take this button, and hands it over when you name someone else, which announces the handover in the channel. complete_action_item marks it done and posts the completion. Both take the item’s id, which comes from get_incident.
rename_action_item, reopen_action_item and unassign_action_item change an item the way its menu does on the dashboard, and its issue follows when it has one. A title can be up to 3,000 characters. create_action_item_issue opens an item’s issue in the tracker your workspace keeps items in step with, and the item then shows the issue’s key and link in get_incident. It is refused, with the reason, when the item already has one or your workspace opens none. See Issue tracking.
claim_runbook_step takes one step of a runbook already attached to the incident, which creates the action item behind that step, or hands over the one that already exists. Runbook and step ids come from get_incident.
invite_responders brings people into the incident channel so they can see what is happening. escalate_incident is the stronger one. It asks one named person to pick the incident up, posts the ask in the channel, messages them directly with an acknowledge button, and reminds them if they do not answer. Reach for it when the agent has gone as far as it can on its own.
page_on_call wakes whoever is on call through your on-call tool. Give the incident, the target (in PagerDuty a service, in Opsgenie a team, escalation, schedule or person, as the tool’s schema lists for each connection) and the reason the person needs. With several on-call tools connected, name one in tool. It answers whether the page is paging, waiting for approval, or was not raised and why. See Halon on call.
Escalating, inviting and giving a shoutout all post in the incident channel, so all three refuse an incident that has been resolved or canceled, and one whose channel Firefight is still creating. The refusal names which it is.
link_incident records a related link on both incidents’ timelines and changes nothing else. Passing duplicate instead also cancels this incident, naming the one that absorbed it, so use it only when the two really are the same event seen twice.
give_shoutout thanks a named person in the incident channel, the same as /ff shoutout.
Every one of these needs incidents: create or incidents: update, so an agent granted only incidents: read can follow an incident and change nothing about it.
assign_incident_role takes the incident, the role slug, and the person as an email address or Slack user ID. Leave the person out to clear the role. Each role holds one person, so assigning hands the role over from whoever had it. Call get_incident first to see which roles exist and who currently holds them. Every role change announces itself in the incident channel, so the tool refuses any of them on a resolved or canceled incident and names the role it would have changed. See Incident roles.
Writing the postmortem
Section titled “Writing the postmortem”An incident that has been resolved can have its write-up started, edited and moved along without anyone opening the dashboard. start_postmortem opens it. Leave generate out for an empty one you write yourself, or pass it to have Firefight draft the first version from the incident.
Generating takes a moment, so the response comes back immediately with a generation state to poll on. Read it back with get_postmortem until the state clears.
update_postmortem sends the whole document as HTML and replaces what was there rather than appending to it, so read the current one first if you mean to add a section. Every version is kept, so a rewrite can be compared and rolled back from the dashboard. set_postmortem_status moves it to draft, in_progress, in_review or completed, which is how your team knows whether it still needs writing or reading.
get_postmortem gives you a version, and update_postmortem takes it back. This matters because a person is often writing the same document in the dashboard at the same time. If they saved while the agent was composing, the agent’s write is refused rather than replacing what they wrote, and the agent reads again and reapplies. Moving the status needs no version, since it changes nothing anybody is typing.
Starting one is refused while the incident is still open, and refused outright for a canceled incident, which has nothing to write up. An incident that already has a postmortem is refused too, so read before you start. All four tools check incidents, not a permission of their own. See Postmortems.
Reading the conversation
Section titled “Reading the conversation”get_incident_transcript returns what people actually said in the incident channel, which is where the reasoning lives that a timeline only summarises. Read it when you know what happened and need to know why.
It needs the Incident Transcripts ability, which is separate from incidents on purpose, and the workspace has to have turned transcript access on under Settings → Workspace. An agent granted every incident ability reads nothing until both are true. See Incident conversations.
Messages come back oldest last, 100 at a time and 500 at most, with a more_before cursor for walking further into the past. Each message says whether anything in it was redacted.
Setting the workspace up
Section titled “Setting the workspace up”Every settings screen has tools behind it. That means you can ask an agent connected as you to do the setup, rather than clicking through it. “Add a SEV0 above our current top severity, and a Mitigating status in the active stage” is one conversation instead of two screens.
Call get_workspace_config first. It returns your severities, statuses, incident types, incident roles, alert sources and webhooks in one response, and the slugs it gives back are what every other tool takes.
Each list has its own pair of tools, since the lists do not ask for the same things. A status belongs to a lifecycle stage, a severity’s position says how severe it is, and only some are colored or have a default. Each of these four lists takes position, 1 being first, so “add a SEV0 above our current top severity” is position: 1.
Passing a slug changes the one that already has it, and leaving the slug out creates a new one. Renaming never moves the slug, because the slug is what your existing incidents point at. A slug that matches nothing comes back as an error rather than quietly creating a second entry under a fresh name.
Deleting is refused while anything still points at the entry, and the refusal says how many. Disabling is what you want then. A disabled entry keeps its slug and stops being offered to responders, and get_workspace_config still returns it so an agent can turn it back on.
The workspace settings have a tool too. update_workspace_settings changes the settings under Settings → Workspace. Give only the settings you want to change. Turning Halon makes safe changes without asking on or off is refused with a token, since only a person in Firefight decides what Halon does without asking.
| Setting | What it changes |
|---|---|
transcript_access_enabled | Whether incident transcripts may be read at all |
transcript_retention_days | How many days a transcript is kept after the incident ends. null keeps them forever |
archive_channel_delay | How long after an incident ends its channel is archived |
web_search_enabled | Whether Halon may search and read the public web |
halon_regression_enabled | Whether Firefight may test new versions of Halon on answers your team rated |
memory_expiry_days | How many days a memory Halon learned is used without anyone confirming it. null keeps using it until someone decides |
code_fix_agent | Which connected coding agent writes a fix’s code changes, by its connection. null for Firefight’s own |
code_fix_budget_cents | How much, in cents, Firefight’s own agent may spend on one code fix before it pauses and asks you to continue. 200 by default |
issue_tracker | Which connected issue tracker incident items are kept in step with. null for none |
issue_creation | When a new item gets an issue there |
issue_tracker_target | Where new issues go, by the tracker’s own fields |
issue_webhook_secret | The signing secret of the tracker’s webhook. It is never read back |
alert_investigations_enabled | Whether an alert that opens an incident starts Halon on it, once per alert group. Off by default |
alert_storm_ceiling_cents | How much, in cents, the runs alerts start may spend together in any hour. Past it, an alert opens its incident without starting Halon |
on_call_paging_enabled | Whether Halon, in a run an alert started, escalates the incident to whoever it found on call. Off by default |
safe_changes_without_asking | Whether Halon makes a safe change in a person’s chat without asking first. On by default. Refused with a token, so only Halon sets it, in a chat, once you confirm it |
It needs the workspace permission, which only admins hold, and an agent in chat asks you to confirm before it changes any of them. See Incident conversations for what turning transcript access on means.
Agents and keys
Section titled “Agents and keys”Creating an agent, rotating its token and minting a service key are all available here, and they behave differently from everything else on this page. They check permissions and api_keys, which are admin-only and cannot be granted to anyone. A connection acting as an admin reaches them. A service key or an agent never can, whatever it has been granted, so an agent cannot create another agent or widen its own access.
A token made here is never shown to the agent that asked for it. Once an admin approves the call, upsert_agent, rotate_agent_token and upsert_api_key show the new token once to the person behind the connection, in a Slack message only they see, and the agent reads only that it was shown. Copy it from Slack straight away, since the message is gone once Slack reloads and Firefight keeps no copy. If Firefight cannot reach you in Slack, the call is refused and says to create the token under Gateway → Agents or Developer → API Keys instead, where it is shown to you once. If Slack fails to show it, the token is switched off at once and you issue a new one. Neither list_agents nor list_api_keys ever carries a token.
Rotating is an overlap rather than a swap. The new token works immediately while the old one keeps working, so the agent stays up while you update its configuration, and revoke_agent_token ends the old one when you are ready. See Agents.
Seven tools put Halon, Firefight’s own agent, in front of the agent you connect, such as one in your editor. Each runs as whoever the credential belongs to. A personal token or OAuth connection runs as you, an agent token as the agent, and a service key as itself.
| Tool | What it does |
|---|---|
ask_halon | Asks Halon a question and waits up to 20 seconds for the answer. Most answers take ten seconds to a minute, so one still in progress comes back with status set to working and a conversation_id. Name an incident to ask about it. Questions from the same credential carry on in one chat. Halon acts with exactly the permissions of whoever the credential belongs to |
get_halon_answer | Waits up to 20 seconds more for an answer ask_halon was still working on, by its conversation_id. It answers with the same statuses, so call it again while it says working |
start_investigation | Starts Halon investigating an incident, or a problem nobody has declared an incident for. Without an incident, symptom is required. You can also pass started_around, names of the services, apps or repositories involved, and error_text. It runs in the background for minutes and posts its finding in the incident’s channel when there is one |
get_investigation | One investigation by id, or an incident’s newest: its status, each theory with the steps behind it, every step it took, and the finding with each claim and what it rests on, including the fix it proposes |
get_halon_performance | How Halon has done over the last 7, 30 or 90 days, 30 by default: how many investigations ran and reached an answer, the median time to one, what your team said of the answers, what came of its fixes, the answers marked wrong with what Halon learned from each, and the problems its pull request reviews prevented, 20 to a page by prevented_page |
list_watches | The watches Halon keeps for you, such as one you started by asking ask_halon to tell you when a deploy finishes: its name, such as Watch “Deploy finished”, what each follows, how long it watches and why, its ceiling on reads and how many it made, each step as it stands with when it was last read and the run’s page when there is one, everything it said and how it ended. Pass active for only the ones still going. See Halon watching for you |
stop_watch | Stops one of your watches by its id from list_watches. It says it stopped wherever the watch reports |
One investigation runs per incident at a time, so a second start_investigation is told which one is running. Poll get_investigation to follow it. See Investigations and Halon’s performance.
An MCP call has no Approve button, so when Halon reaches a change it would ask you about in a chat, ask_halon does not make it. It comes back with status set to waiting and the questions in waiting_on, in the words the dashboard shows, such as “Api request on Faylee (Northflank), project faylee?”. Approve it in Firefight, then ask again. See What Halon can do.
ask_halon, get_halon_answer and start_investigation need investigations: create, the same as asking Halon in the dashboard. Every member holds it without a grant unless an admin took it away, so an OAuth connection or personal token can ask from the start, and an agent or service key needs it granted. get_investigation, get_halon_performance and list_watches need investigations: read, and stop_watch needs investigations: create. A watch reads with the credential’s own permissions, so an agent or service key watches only what its grants reach.
Tools from your integrations
Section titled “Tools from your integrations”Every tool switched on for a connection under Configure → Integrations also appears here, so an agent can query PlanetScale or open a Linear issue through the same connection. A connection’s tools arrive switched on, a tool you switch off disappears, and who may call each is set in Permissions. An agent’s tool list only carries the capabilities its credential may call, so a narrowly scoped key never sees the rest of your connections. See Connect an integration.
A capability’s tool is named after its connection and the capability, joined with an underscore, such as northflank_search_logs for the Northflank connection’s search_logs. A second account connected under the name Faylee gives faylee_search_logs. Its description says which connection it goes through. Wherever a call through a connection is shown to a person, in approval requests, in Activity, and in what search_approvals and search_activity return, the connection is named the way you tell it apart, such as Faylee (Northflank).
A connection wired to more than one environment takes an environment parameter naming which one to use, and the tool lists the choices. Leave it out when the connection has a single environment.
A connection’s tool that changes who can reach something or sets a secret, such as GitHub’s set_actions_secret or adding someone to a status page, always waits for an admin to approve it when an agent or a token calls it, whatever the credential was granted and whatever your approval rules say. The tool result gives the approval id, and the agent retries the same call with approval_id once it is approved.
Tools for anything on the resource map
Section titled “Tools for anything on the resource map”These tools work on any service, database, Worker, site or zone on the map, whichever connection runs it or watches it. The agent names the resource, and Firefight sends the request to that connection. They answer for every connected provider that offers the capability, and What Halon can do lists which providers can roll back, restart or scale.
| Tool | What it does |
|---|---|
search_logs | Log lines of a resource, newest first, filtered by text or a regular expression. It can read what the app prints, its builds, or the requests reaching it, where the provider keeps them. With the build logs, deployment reads one deployment’s or build’s log by the id run_history gives, rather than the newest |
query_metrics | Metrics of a resource over time, such as CPU, memory, requests and 5xx responses, with the low, average, high and latest value of each |
search_errors | Errors a resource raised, newest first, grouped by kind with how often each happened |
search_traces | Request traces through a resource, slowest or failed first, with where the time went |
recent_deploys | What went out to a resource, newest first, with the id rollback takes |
resource_status | How a resource is set up and how it stands now |
run_history | Recent CI runs, builds or deploys of a resource, newest first, each with its status, when it started and finished and how long it took, and how long finished ones usually take. name keeps only runs whose workflow, pipeline or kind contains it, and run reads one run by its id with its jobs, steps or phases where the provider lists them |
rollback | Puts a resource back on an earlier build or version |
restart | Restarts a resource that is stuck while its code is fine |
scale | Sets how many instances a resource runs |
run_key_query | Runs one of a resource’s key checks, such as error_rate, latency_p95 or throttles. Give resource, query and optionally minutes. The answer starts with how the reading compares with what is normal for the resource over the last week, then the reading itself |
new_log_patterns | Reads a resource’s recent logs and answers which kinds of line it did not print in the last week, and which of its error lines are usual. Give resource and optionally minutes. It never returns the raw lines |
Each tool takes resource, the name or id the map shows. When more than one connection could answer, the agent also gives connection, and the tool’s description lists the choices. A tool that only reads also takes connection: all, which asks every connection that could answer and heads each answer with the connection it came from. When two resources share a name, the agent names the one it means by its id.
When an observability tool such as Datadog watches a resource that a platform runs, logs, metrics, traces and errors come from the observability tool, and the platform answers instead when the observability tool has nothing, saying so in the answer.
A call through one of these tools counts as a call to the connection’s own tool. It needs the same permission, it waits for the same approval rules, and it shows in Activity under that tool. A tool appears in an agent’s list once a connection tool that can answer it is switched on under Configure → Integrations and the agent may call it. A connection tool whose name is the same as one of these is left out of the list. If the connection tool that would answer is switched off, the call says so.
Result caps
Section titled “Result caps”Search tools return 25 results by default and accept a limit of up to 50. When more results exist than the cap allows, the response includes a truncated flag set to true, so agents know to narrow their filters instead of assuming they saw everything. Incident timelines in get_incident are capped at 50 events with their own timeline_truncated flag. Each token may make up to 1,000 MCP requests per minute.
Permissions
Section titled “Permissions”What a connected agent can query depends on the credential it authenticates with.
| Credential | Access |
|---|---|
| OAuth connection | Acts as you, with your access |
| Personal token | Acts as you, with your access |
| Agent token | Acts as the agent, with only what the agent was granted |
| Service key | Only what it has been granted, and nothing by default |
Agent tokens and service keys map to tools by resource.
| Tool | Required permission |
|---|---|
search_incidents, get_incident, get_workspace_config, get_postmortem | incidents: read |
search_similar | incidents: read, and findings only with investigations: read |
get_incident_transcript | incident_transcripts: read, with transcript access turned on |
search_alerts | alerts: read |
search_catalog | catalog: read |
get_resource_map, search_map, find_resources, get_resource, get_resource_links, get_resource_neighbours, traverse_resource_map, blast_radius, resource_map_stats | map: read |
evaluate_routing | policies: read |
search_runbooks, get_runbook | runbooks: read |
list_handbook_pages / get_handbook_page | handbook: read |
search_approvals | approvals: read |
get_form | forms: read |
list_integrations | integrations: read, admin-only and ungrantable |
get_investigation, get_halon_performance, list_watches | investigations: read |
ask_halon, get_halon_answer, start_investigation, stop_watch | investigations: create |
search_logs, query_metrics, search_errors, search_traces, recent_deploys, resource_status, run_history, rollback, restart, scale, run_key_query, new_log_patterns | The permission for the connection tool that answers |
upsert_catalog_entry | catalog: create or catalog: update |
delete_catalog_entry | catalog: delete |
upsert_catalog_type | catalog: update |
delete_catalog_type | catalog: delete |
suggest_resource_link | catalog: update |
upsert_routing_rule | policies: create or policies: update |
delete_routing_rule | policies: delete |
update_routing_config | policies: update |
upsert_runbook | runbooks: create or runbooks: update |
upsert_handbook_page | handbook: create or handbook: update |
delete_handbook_page | handbook: delete |
list_skills / get_skill | skills: read |
upsert_skill / delete_skill | skills: create, update or delete |
upsert_custom_field | custom_fields: create or custom_fields: update |
upsert_form_field | forms: update |
update_workspace_settings | workspace: update, admin-only and ungrantable |
update_protected_paths | integrations: update, admin-only and ungrantable |
upsert_severity / delete_severity | severities: create, update or delete |
upsert_status / delete_status | statuses: create, update or delete |
upsert_incident_type / delete_incident_type | incident_types: create, update or delete |
upsert_incident_role / delete_incident_role | incident_roles: create, update or delete |
upsert_alert_source / delete_alert_source | alerts: create, update or delete |
upsert_outbound_webhook / delete_outbound_webhook | webhooks: create, update or delete |
test_outbound_webhook | webhooks: update |
list_agents, upsert_agent, rotate_agent_token, revoke_agent_token, delete_agent | permissions, admin-only and ungrantable |
list_api_keys, upsert_api_key, delete_api_key | api_keys, admin-only and ungrantable |
declare_incident | incidents: create |
post_incident_update, resolve_incident, cancel_incident, reopen_incident | incidents: update |
create_action_item, assign_action_item, complete_action_item, rename_action_item, reopen_action_item, unassign_action_item, create_action_item_issue | incidents: update |
claim_runbook_step, escalate_incident, invite_responders, link_incident, give_shoutout, assign_incident_role, attach_runbook, dismiss_timeline_note | incidents: update |
start_postmortem, update_postmortem, set_postmortem_status | incidents: update |
approve_approval, deny_approval | approvals: update |
list_abilities, list_principals, list_member_access, search_activity | permissions: read |
set_member_access | permissions: update |
post_to_channel | channel_posts: create |
list_notices | monitoring: read |
list_workflows | workflows: read |
create_workflow, run_workflow | workflows: create |
update_workflow | workflows: update |
update_workflow_settings | workspace: update, admin-only and ungrantable |
what_changed | map: read, plus the read permission of each provider tool it runs |
check_status_page | None. It reads only public pages |
blind_spots | map: read |
upsert_permission_set | permissions: create or permissions: update |
delete_permission_set | permissions: delete |
grant_ability | permissions: create |
revoke_grant | permissions: delete |
upsert_approval_rule | permissions: create or permissions: update |
delete_approval_rule | permissions: delete |
| A connection’s own tools | The permission for that tool, which exists once you switch it on |
list_unattended_rules | permissions: read |
upsert_unattended_rule | permissions: create or permissions: update |
delete_unattended_rule | permissions: delete |
list_allowances | permissions: read |
upsert_allowance | permissions: create or permissions: update |
delete_allowance | permissions: delete |
A tool call without the needed permission returns an error naming the missing permission, and the agent can keep using the tools it does have. A call an approval rule holds changes nothing and returns an error carrying the approval’s id. Once someone approves it, retry the identical call with approval_id set to that id. A call one of Firefight’s own rules stops, such as a push to a protected branch, returns an error that starts with “Firefight’s own rule refused this call, not the provider, and it is final.” and then the reason. Tell the person why rather than looking for another way around it. A refused call shows in Activity as deny, a call one of Firefight’s own rules stopped shows as refused, and a call the connected tool answered with an error of its own shows as error with the first line of what it said. See API overview for how keys and permissions work.
Managing connected agents
Section titled “Managing connected agents”Everything connected over OAuth appears under Developer → API Keys in the Connected agents section, where you can revoke any connection. Revoking immediately invalidates its tokens, and the client has to go through consent again to reconnect. Automation using an API key is managed like any other key on the same page, so deactivate or delete the key to cut it off. An AI with its own identity lives under Gateway → Agents instead, where you rotate or revoke its tokens, change what it may do, or switch it off without losing what it did. See Agents.