@borgresearch/odin-mcp
v0.3.2
Published
MCP server for the Odin security API
Readme
@borgresearch/odin-mcp
Model Context Protocol server for the Odin security platform by Borg. It gives coding agents organisation-scoped finding context and a safe GitHub-first remediation loop.
The MCP can inspect a finding such as ODI-12, show the remediation and suggested branch, link an existing accessible GitHub pull request, and report automatic retest history. It never creates commits, pushes branches, or creates pull requests.
Prerequisites
- Node.js 24 or newer.
- An Odin account with at least one organisation.
- An Odin API key. Read only is enough for discovery, remediation context, and retest reads. Read & Write is required for status changes and linking an existing pull request.
Quick start
- Sign in to https://odin.borghq.io, open Workspace > API Keys, and create a key. The dialog defaults to Read & Write for the remediation workflow; choose Read only when the agent should only inspect data.
- Copy the full key — it is shown once.
- Add the server to your client:
ODIN_API_KEY=odin_... npx @borgresearch/odin-mcp@latestYou can also install globally:
npm install -g @borgresearch/odin-mcp@latest
ODIN_API_KEY=odin_... odin-mcpTo update an existing setup, change the package in your client config to @borgresearch/odin-mcp@latest and restart the client. For a global install, run npm install -g @borgresearch/odin-mcp@latest.
Restart your MCP client after changing its configuration. 30 tools should be available after startup.
Remote connection
Connect an HTTP-capable MCP client to https://odin.borghq.io/api/mcp using Streamable HTTP and the header Authorization: Bearer odin_.... Remote connections do not need Node.js or a local package install. Supply the key through your client's secret or environment settings; do not put it in the URL or commit it.
Clients that support MCP sign-in (OAuth), such as Claude, Claude Code and VS Code, can instead connect without a key: add the URL without an Authorization header and sign in with your Odin account. Workspace > AI agents in Odin has the steps for each client.
Every request must carry the key. Revocation takes effect on the next request, and each request gets a fresh server instance. The endpoint returns JSON responses without persistent sessions, resumable streams, or a standalone SSE subscription. Authenticated GET and DELETE requests return 405. Requests with an untrusted browser Origin return 403; native clients can omit Origin. Cross-origin browser access is not enabled.
An example initial request (set ODIN_API_KEY privately first):
curl https://odin.borghq.io/api/mcp \
-H "Authorization: Bearer $ODIN_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
--data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"odin-client","version":"1.0.0"}}}'Use the negotiated MCP-Protocol-Version header on subsequent requests. Both transports expose the same 30 tools. Remote discovery consumes one request from the existing 100-per-minute API-key allowance. A tool call consumes one transport authentication request plus one API-handler request, sharing that allowance with direct API and stdio use. Rate-limit responses preserve Retry-After; tool-level refusals are MCP errors with retry guidance.
Configuration
| Variable | Required | Notes |
|----------|----------|-------|
| ODIN_API_KEY | yes | An organisation-scoped key with read; write is required for mutations. |
| ODIN_BASE_URL | no | Odin API base URL. Defaults to https://odin.borghq.io. |
Each call sends Authorization: Bearer ${ODIN_API_KEY}. Keys belong to one organisation. If you belong to multiple organisations, create one key per organisation and run a separate MCP server entry for each.
Tools
All tools accept one JSON object and return both a Markdown summary and a structured payload. The same payload is also returned as JSON text for clients that only read text content. Read tools are annotated readOnlyHint: true; every Read & Write tool is annotated destructiveHint: true, so clients ask before running it.
| Name | Permission | Description |
|------|------------|-------------|
| list_findings | Read | List findings with pagination, multiple severities/statuses, source, confirmation type, assignee, CVSS, dormant assets, repository, and title/identifier search. |
| get_finding | Read | Get bounded remediation context, revision content, suggested branch, linked PRs, workflow readiness, and latest retest state. |
| update_finding_status | Read & Write | Move a finding to MITIGATING, OPEN_FOR_RETEST, or ACKNOWLEDGED. Cookie-authenticated callers also need member role. |
| link_finding_pull_request | Read & Write | Link an existing accessible GitHub PR. It does not create, modify, merge, or push a PR. |
| get_finding_retests | Read | Read retest history; it never triggers a retest or spends credits. |
| list_pentests | Read | List pentests for the organisation. |
| get_pentest | Read | Get a pentest's status, schedule, and scope. |
| list_assets | Read | List attack-surface assets with filters and pagination. |
| get_asset | Read | Get an asset and the findings that reference it. |
| get_finding_stats | Read | Aggregate counts by severity and status, optionally for a pentest. |
| list_finding_repositories | Read | Discover repositories available as finding filters. |
| list_finding_assets | Read | Discover assets referenced by findings. |
| export_findings | Read | Export matching findings with revision content; narrow filters for large exports. |
| get_finding_retest_summaries | Read | Latest retest summaries for up to 200 finding IDs. |
| get_finding_discussion_summaries | Read | Message counts and unresolved state for up to 200 finding IDs. |
| assign_finding | Read & Write | Assign an organisation member, or clear the assignee. |
| get_finding_discussion | Read | Read native and Slack discussion blocks and their messages. |
| create_finding_message | Read & Write | Post a message as the API key creator. |
| edit_finding_message | Read & Write | Edit a message authored by the API key creator. |
| delete_finding_message | Read & Write | Delete a message authored by the API key creator. |
| update_finding_discussion | Read & Write | Resolve or reopen the native discussion. |
| list_finding_mentionables | Read | Find authorised people and entities to mention. |
| disconnect_finding_slack_thread | Read & Write | Stop Slack synchronisation while retaining imported messages. |
| prepare_finding_attachment_upload | Read & Write | Get a signed upload URL; the caller uploads the bytes separately. |
| get_finding_attachment_url | Read | Resolve a private attachment to a temporary download URL. |
| get_finding_slack_url | Read | Resolve a synced message or thread to its Slack permalink. |
| list_finding_pull_requests | Read | Find accessible GitHub pull requests to link. |
| unlink_finding_pull_request | Read & Write | Remove a manually linked pull request. |
| follow_finding | Read & Write | Follow or unfollow a finding as the API key creator. |
| request_finding_retest | Read & Write | Queue a manual retest; spends credits. |
Finding references
Finding references are organisation-local. Prefer a human identifier such as ODI-12; an internal findingId is retained for chaining tool calls. Odin looks up the numeric suffix only within the organisation belonging to the API key and returns the canonical stored identifier. It never searches across organisations or reveals a cross-organisation match through an error.
list_findings
Optional parameters include pentestId, assetId, repository, severity (CRITICAL | HIGH | MEDIUM | LOW | INFORMATIONAL), status (REPORTED | ACKNOWLEDGED | MITIGATING | OPEN_FOR_RETEST | NEEDS_REVISION | FIXED_AND_RETESTED), search, sortBy (severity | createdAt), sortOrder, limit (1–200, default 50), and offset.
get_finding
Provide identifier or findingId (at least one is required). The response includes the current status and severity, revision summary/description/impact/details/remediation, affected assets, code reference where available, repository, linked GitHub PR metadata, suggestedBranch, retest state, and workflow blockers.
Example prompt:
Explain
ODI-12, show me the suggested branch, and tell me what must be ready before I open a PR.
GitHub remediation loop
- Ask
get_findingabout the human identifier. - Create the returned branch, make and test the fix, commit it, push it, and open the GitHub PR yourself in Cursor or your normal development environment.
- Explicitly ask Odin to link that existing PR with
link_finding_pull_requestand its URL. The operation is idempotent and verifies that the organisation's GitHub App installation can access the repository. - When the linked PR merges, automatic webhook processing may queue a retest. Use
get_finding_reteststo readQUEUED,RUNNING,COMPLETED,SKIPPED, orFAILEDentries and their sanitised verdicts.
Odin does not infer permission from a matching PR. Ask before linking a PR or changing status. The MCP does not create commits, push branches, create PRs or merge PRs. It requests a paid manual retest only through request_finding_retest, which needs a Read & Write key; ask the user first.
update_finding_status
Provide identifier or findingId and one of:
| Status | Use it when |
|--------|-------------|
| MITIGATING | You have started work on the fix. |
| OPEN_FOR_RETEST | The fix is ready for verification. |
| ACKNOWLEDGED | You accept the risk for now. |
A Read-only key receives an actionable 403. Create a new Read & Write key for mutations; existing key permissions are not silently upgraded. NEEDS_REVISION and FIXED_AND_RETESTED are controlled by Borg's retest process.
link_finding_pull_request
Provide identifier or findingId and an existing GitHub pull request url. The GitHub integration must be active and able to access the repository. The tool returns the canonical finding reference, verified PR metadata, resulting status, and workflow readiness. It does not modify GitHub.
get_finding_retests
Provide identifier or findingId. The tool is read-only and distinguishes no history from a history containing queued/running, completed, skipped, or failed runs. It does not call the manual retest endpoint and never spends credits.
Other tools
list_pentests takes no parameters and get_pentest requires pentestId. list_assets and get_asset retain their existing type, source, status, search, sorting, and pagination parameters.
Instructions and prompts
The server sends short instructions when a client connects. They explain what Odin is, how the tools fit together, and the rules an agent should follow. Links in them point to ODIN_BASE_URL.
It also offers two prompts:
setupchecks what the organisation has set up, looks at the current repository, recommends the Odin setup that fits it, and helps the user reach a first result.fix_findingtakes an optionalfindingID such asODI-12. It fixes one finding, opens a pull request with the user's agreement, and links it so Odin can retest the fix.
See the Guide for AI agents for the full guidance.
Client setup
The sections below run the server locally over stdio. Cursor, VS Code, Claude Code and Codex can use the remote connection instead, with no local install. The AI agents page in Odin and the Odin MCP documentation have the remote setup for each client, with sign-in where the client supports it, plus Add to Cursor and Add to VS Code buttons that never put a key in a link.
Use with Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"odin": {
"command": "npx",
"args": ["-y", "@borgresearch/odin-mcp@latest"],
"env": {
"ODIN_API_KEY": "odin_..."
}
}
}
}Use with Claude Code
claude mcp add --transport stdio --env ODIN_API_KEY=odin_... odin -- npx -y @borgresearch/odin-mcp@latestUse with Codex
Add to ~/.codex/config.toml, then restart Codex:
[mcp_servers.odin]
command = "npx"
args = ["-y", "@borgresearch/odin-mcp@latest"]
env = { ODIN_API_KEY = "odin_..." }Use with Cursor
Open Settings > MCP, or add this to .cursor/mcp.json:
{
"mcpServers": {
"odin": {
"command": "npx",
"args": ["-y", "@borgresearch/odin-mcp@latest"],
"env": {
"ODIN_API_KEY": "odin_..."
}
}
}
}Use with Windsurf
Add to ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"odin": {
"command": "npx",
"args": ["-y", "@borgresearch/odin-mcp@latest"],
"env": {
"ODIN_API_KEY": "odin_..."
}
}
}
}Use with VS Code (GitHub Copilot)
Add to .vscode/mcp.json:
{
"servers": {
"odin": {
"command": "npx",
"args": ["-y", "@borgresearch/odin-mcp@latest"],
"env": {
"ODIN_API_KEY": "odin_..."
}
}
}
}Use with Cline
Open Settings > MCP Servers > Edit MCP Settings and add the same mcpServers.odin JSON shape shown for Cursor. Pass ODIN_API_KEY in the server environment and restart the client after editing its MCP configuration.
Troubleshooting
ODIN_API_KEY is not set or invalid — check the key under Workspace > API Keys, confirm the client config sets env.ODIN_API_KEY, and restart the client.
API key does not have write permission — the agent attempted a status change or PR link with a Read-only key. Create a new key with Read & Write permissions.
Finding not found — check the ODI-12 reference and the organisation associated with the key. Keys and human identifiers are organisation-scoped by design.
Retest history is unavailable — the organisation's retest automation capability is gated or not configured. Check the GitHub and automatic-retest settings.
The server starts but no tools appear — most clients require a full restart after changing configuration. Confirm Node.js 24 or newer is on the client's PATH and inspect its MCP logs.
Development
pnpm test:run
pnpm typecheck
pnpm lint
pnpm buildReleasing
Releases go out from package.json's version when it reaches production:
- A pull request that changes what npm ships (
src/outside tests,package.json,README.md,LICENSE,server.json,tsup.config.tsortsconfig.json) needs a version npm doesn't have yet. The Check the odin-mcp version check fails otherwise. Bumpversioninpackage.json, andversionandpackages[0].versioninserver.json, to the next release. Changes made before a release share one bump. - When that version reaches
production, the Publish @borgresearch/odin-mcp workflow tests, builds and publishes it to npm, tags itodin-mcp@<version>and publishes it to the MCP Registry. A version npm already has is skipped.
Publishing uses npm trusted publishing from the npm GitHub environment, which only production can deploy to; no npm token is stored in GitHub. You can also run the workflow by hand on production. A manual run is a dry run unless you turn it off. Tags record releases and don't trigger publishing.
License
MIT. See LICENSE.
Collaboration and attachment safety
Assignment, messages, edits, deletion and discussion actions can notify people, send email, synchronise Slack, or update connected issue trackers. Ask the user before making these changes. Posting a message is not idempotent. Edits and deletion are limited to messages authored by the API key creator. API keys have no personal unread state.
Attachment tools return temporary signed URLs. They do not upload or download bytes. Upload the exact file with the returned required headers, without an Odin API key, before attaching its private gs:// reference to a message. Treat signed URLs as secrets. The server validates that attachments belong to the finding and message.
The tool set covers supported API-key finding workflows and asset/pentest reads. Private intelligence features, parked maps, asset changes, scan launches, and API-key administration are excluded. request_finding_retest is the one tool that spends credits. Finding list totals use the filtered pagination count; aggregate statistics can cover a wider scope.
