MCP server
The Keel MCP server is a thin Model Context Protocol wrapper over the Keel REST API. It lets an MCP-compatible client, such as Claude Desktop, Claude Code, or Cursor, read and act on your Keel workspace in natural language. Every tool maps to a real API endpoint and is scoped to your API key’s organization, so the MCP server grants no more access than the key already has.
Install and run
Section titled “Install and run”The server runs over stdio and is launched by your MCP client. It needs one environment variable, a workspace API key (create one in the app under Integrations).
KEEL_API_KEY=your_key npx keelgrc-mcpConfiguration (environment):
| Variable | Required | Default | Purpose |
|---|---|---|---|
KEEL_API_KEY |
Yes | none | A workspace API key from Integrations |
KEEL_BASE_URL |
No | https://app.keelgrc.com |
Your workspace origin |
Configure your client
Section titled “Configure your client”Most MCP clients take a command, arguments, and environment. For example, a client configuration entry looks like:
{ "mcpServers": { "keel": { "command": "npx", "args": ["keelgrc-mcp"], "env": { "KEEL_API_KEY": "your_key" } } }}Point your client at that entry, restart it, and the Keel tools become available to the agent. This page describes keelgrc-mcp 0.8.0, the current release on npm.
Hosted endpoint
Section titled “Hosted endpoint”If your client can reach a remote MCP server, you can skip the install. Keel runs the same tools over Streamable HTTP at:
https://app.keelgrc.com/mcpSend your workspace API key on every request as Authorization: Bearer <key>. The endpoint keeps
no session: each request is answered with the key it carried, and every tool call runs through the
same REST API checks as a direct API call, so the hosted server grants no more access than the key
has. It takes one JSON-RPC message per request. Its MCP Server Card is published at
/.well-known/mcp/server-card.json.
Available tools
Section titled “Available tools”Every tool calls a real REST API endpoint. Tools are grouped by resource rather than one tool
per operation: you call keel_policies with an action of list, get, create, update or
delete, instead of five separate tools. That keeps the tool list short enough for a client to
use well, and it means a new verb on a resource does not add a new tool.
Where a resource is missing an action, the tool says so and why. keel_tasks has no delete
because no Keel surface deletes a task, in the app or the API; close one by setting its status
instead. keel_webhooks has no get or update because the REST API has neither; list the
subscriptions to read one. create returns the subscription’s signing secret once, and rotate
replaces it with a window in which both secrets sign; see
rotating a secret. event must be “all” or a
name Keel emits, and any other value is refused.
There are two ways to add controls. keel_controls create adds one control of your own, as
Controls → Add a control does in the app. keel_starter_controls adds a framework’s
recommended controls already mapped to its clauses, as Controls → Add recommended controls
does.
Applying a framework stays in the app. keel_starter_controls and the map action refuse a
framework the workspace has not applied.
| Tool | Actions |
|---|---|
keel_whoami |
Connection test and the organization the key belongs to |
keel_members |
The workspace’s members (id, email, name, role), read-only |
keel_frameworks |
The frameworks the workspace has applied, each with its readiness |
keel_readiness |
ISO/IEC 27001 readiness summary. This endpoint covers that framework only, not whichever framework the workspace has applied |
keel_controls |
list, get, create, update, delete, mappings, map, unmap |
keel_starter_controls |
None. Takes frameworkKey and adds that framework’s recommended controls, already mapped (owner or admin) |
keel_tasks |
list, get, create, update |
keel_risks |
list, get, create, update, delete |
keel_vendors |
list, get, create, update, delete |
keel_people |
list, get, create, update, delete |
keel_policies |
list, get, create, update, delete |
keel_evidence |
list, get, create, update, delete |
keel_webhooks |
list, create, rotate, delete |
Arguments
Section titled “Arguments”Each tool refuses an argument it does not accept, so a misspelled or unsupported field is an error rather than something silently dropped. The REST API does the same on its side (see request rules).
keel_memberslists the people with a Keel seat. Its emails are whatassigneeEmailonkeel_tasksandownerEmailonkeel_policiesmust match, or the call fails and nothing is written.keel_peopleis the HR directory, a different list.keel_peoplecreate,updateanddeleteneed a key created by an owner or admin.keel_evidencereturns each item’s linkedcontrols, andupdatelinks and unlinks controls withlinkControlsandunlinkControls(control ids or keys). A control that is not in the workspace is an error, and nothing changes.keel_controlscreatetakesname(required),descriptionandkey, your own identifier for the control. Omitkeyand Keel generates one. A key the workspace already uses is an error that carries the existing control, and nothing is overwritten. The new control starts atnot_startedwith no owner and no mappings: set those afterwards withupdateandmap. Creating needs the owner or admin role.keel_controlsgetreturnscrosswalks, the clauses the control is mapped to, in the shapemappingsreturns them.listdoes not.keel_controlsmappingslists the clauses a control is mapped to.mapandunmaptake the controlid, aframeworkKeyaskeel_frameworkslists it, and arequirementRefwritten exactly as Keel writes it (for example4.1).maprefuses a section heading, because readiness never scores one, and mapping the same clause twice reportscreated: false.unmapon a clause the control is not mapped to is an error. Both need the owner or admin role.keel_starter_controlsis safe to repeat: a second call adds nothing, and the counts it returns (controlsAdded,mappingsAddedand the rest) say so. A control the workspace already has keeps its name, description and state and only gains the mappings.keel_riskscan return a risk with no score. A risk imported from another platform without a likelihood or impact haslikelihood,impact,inherentScoreandlevelset tonull, and the register shows it as Needs scoring until someone rates it.keel_taskslisttakes an optionalquery, matched against the title. It takesassigneeEmailoncreateandupdate. AdueAtthat is not a valid date is refused, and tasks have no start date.
The server is versioned independently of the app, so check the tool list your client reports rather than trusting this table.
A key acts as the member who created it, and carries that member’s role. A role-gated action refuses a key whose member cannot perform it in the app, so the MCP and the browser give the same person the same answer. The role is re-read on every call, so a change to somebody’s role takes effect immediately rather than when their key is next replaced.
A key created before this behavior existed carries no member and therefore no role. It can read, and it is refused on every action that changes anything, including creating a record and unsubscribing a webhook. Replace it under Settings if you hit that.
Security notes
Section titled “Security notes”- The MCP server holds no credentials of its own; it uses the API key you provide, and inherits that key’s workspace scope and permissions.
- Because every call goes through the same API surface, the same tenant isolation applies. An agent can only see and change what the key already allows.
- Treat the API key like a password. Rotate it under Integrations if it is exposed.
Next steps
Section titled “Next steps”- REST API reference: the endpoints the MCP tools call.
- AI tools & credits: Keel’s own built-in AI actions inside the app.