Sentrinel for coding agents

Give Claude Code, Codex, Cursor β€” any agent that speaks MCP β€” your issues, logs and traces, so "fix the top error in production" is a sentence you can type rather than a session of copy-pasting stack traces. For harnesses without MCP there is a CLI that prints the same thing as Markdown.

Both run with an AI agent key, which can see exactly one app and do nothing else.

The key, and what a leaked one can do

Issue it from the dashboard: API Keys β†’ Generate β†’ What is this key for? Every key is bound to one integration; these are the two for agents:

Kind Prefix May
AI agent β€” read only snt_mcp_ read one app's issues, logs, traces and requests. Nothing else.
AI agent β€” may resolve issues snt_mcprw_ read, and mark issues resolved / ignored / reopened. Nothing else.

The other kinds β€” server (snt_live_/snt_dev_), mobile, database collector, OpenTelemetry β€” can write telemetry and cannot read; the server and CLI refuse them before making a request, naming which kind you handed them.

An agent key goes on a developer's laptop and into an agent's config β€” the place a credential is most likely to leak β€” so it is built to be safe to lose:

Put the key in the environment, never on the command line: ps shows argv to every process on the machine. Neither the server nor the CLI accepts a --key flag for that reason, and neither ever echoes the key in an error.

Install it

One line, key and all. Issue the key first β€” API Keys β†’ Generate β†’ AI agent β€” and hand it to the installer:

curl -fsSL https://sentrinel.dev/install-mcp.sh | SENTRINEL_API_KEY=snt_mcp_… bash

Copy it from the dashboard rather than from here. The moment the key exists the panel shows this command with your key already in it, and a copy button. The snt_mcp_… above is a placeholder β€” pasted verbatim it installs an ellipsis as your key, and the installer now refuses it and says so.

That downloads two single-file bundles β€” the MCP server and the CLI β€” into ~/.sentrinel, puts sentrinel-mcp and sentrinel on your PATH under ~/.local/bin, installs Bun if the machine does not have it, writes the key to ~/.sentrinel/env (mode 0600), and registers the server with Claude Code. Nothing left to paste. No clone, no bun install, no sudo.

Then, in a session:

Look at Sentrinel's top unresolved issue, find the cause in this repo, and fix it.

The agent calls list_issues, then get_issue for the stack trace, then get_request for the exact body that broke it β€” on its own.

Because the key lives in ~/.sentrinel/env and the commands read it themselves, it is in no agent's config file and in nobody's ps output. A variable already in the environment still wins, so SENTRINEL_API_KEY=… sentrinel issues works for a one-off against another app.

Install first and configure after, if you would rather:

curl -fsSL https://sentrinel.dev/install-mcp.sh | bash
printf 'SENTRINEL_API_URL=%s\nSENTRINEL_API_KEY=%s\n' https://api.sentrinel.dev snt_mcp_… \
  > ~/.sentrinel/env && chmod 600 ~/.sentrinel/env
claude mcp add sentrinel -- sentrinel-mcp

Re-run the installer any time to upgrade; it leaves the key alone.

The source is public, in Zaga-ltd/sentinel_packages under mcp/ β€” read install.sh before you pipe it to a shell, or skip the installer and run from a checkout:

git clone https://github.com/Zaga-ltd/sentinel_packages.git ~/.sentrinel-src
cd ~/.sentrinel-src && bun install
# then point configs at: bun run ~/.sentrinel-src/mcp/src/server.ts

Codex

~/.codex/config.toml:

[mcp_servers.sentrinel]
command = "sentrinel-mcp"
args = []

No env block: the command reads ~/.sentrinel/env itself.

Cursor, Claude Desktop, and anything with an mcp.json

A GUI app launched from the Dock does not read your shell profile, so ~/.local/bin may not be on its PATH and a bare sentrinel-mcp will not resolve. Give those the absolute path β€” /Users/you/.local/bin/sentrinel-mcp β€” if the server fails to start.

{
  "mcpServers": {
    "sentrinel": {
      "command": "sentrinel-mcp"
    }
  }
}

A full example, start to finish

Nothing here is elided. This is the whole path from an empty machine to a merged fix.

1. Issue the key

Dashboard β†’ API Keys β†’ Generate β†’ What is this key for? β†’ AI agent β€” read only. Pick the app it may see. Copy the key; it is shown once.

Take may resolve issues instead if you want the agent to close the issue after it ships the fix. That is its only extra power.

2. Install

curl -fsSL https://sentrinel.dev/install-mcp.sh | SENTRINEL_API_KEY=snt_mcp_… bash
  Sentrinel for coding agents 0.1.0

  Downloading the MCP server and CLI…
  Key written to /Users/you/.sentrinel/env (0600).

  Installed:
  /Users/you/.local/bin/sentrinel-mcp   the MCP server
  /Users/you/.local/bin/sentrinel       the CLI

  Registered with Claude Code (user scope).

  Done. In a Claude Code session:

    Look at Sentrinel's top unresolved issue, find the cause in this repo, and fix it.

3. Check it before you trust it

sentrinel issues

If that prints your bug list, the key, the URL and the binaries are all right, and the MCP server β€” same bundle, same key β€” will work too. Debugging a stdio MCP server through an agent's logs when the CLI would have told you in one line is a bad afternoon.

claude mcp list

should show sentrinel: /Users/you/.local/bin/sentrinel-mcp - βœ” Connected.

4. Ask for the fix

In a session, in the repo that serves that app:

Look at Sentrinel's top unresolved issue, find the cause in this repo, and fix it.

What the agent does, unprompted, is roughly:

  1. list_issues β€” sees TypeError: Cannot read properties of undefined (reading 'total'), 412 occurrences, 38 users, first seen two hours ago.
  2. get_issue with that id β€” gets the stack frame (src/checkout/summary.ts:64), where it fires, which clients were hit, the attributes the app attached to the latest occurrence, and the ids of the request and trace behind each recent one. (A stack trace appears when the report carried one; errors from a mobile SDK or recorded as an HTTP status often have none.)
  3. get_request with that request id β€” the actual body that broke it: a cart with items: [] and no totals object, from a coupon that zeroed the order. (Backend SDKs capture the payload; an error reported from a mobile SDK carries timing and the trace id, and the trace is then the next read.)
  4. search_logs around it β€” confirms the coupon path is the only one that reaches line 64 without totals.
  5. Opens src/checkout/summary.ts, writes the guard and a test for the empty-cart case.

The step that is hard to do by hand is 3. A stack trace tells you the line; the captured request tells you the input, and an agent that has the input writes a test instead of a guess.

5. Close it

With a may-resolve key, ask for it in words β€” "mark that issue resolved" β€” and the agent calls set_issue_status. With a read-only key it will try, get a 403, and tell you. Or do it yourself:

sentrinel resolve 5f3ac1

The same thing without MCP

Every step above is a CLI command, so a harness that does not speak MCP loses nothing but the autonomy:

sentrinel issues --status unresolved --period 24h
sentrinel issue 5f3ac1                    # β†’ request id, trace id
sentrinel request 9b2e…                   # β†’ the body that broke it
sentrinel trace 44f1…                     # β†’ where the time went
sentrinel logs --level error --search checkout --period 2h

sentrinel issue 5f3ac1 | claude -p "Find the cause of this in the current repo and fix it."
sentrinel issue 5f3ac1 --json | jq .stack # β†’ anything else

Upgrading, moving and removing it

Goal Do
Upgrade Re-run the installer. It replaces the bundles and leaves ~/.sentrinel/env alone.
Change the key or the app Rewrite ~/.sentrinel/env, or re-run the installer with the new SENTRINEL_API_KEY. No agent config changes.
Point it at a self-hosted API SENTRINEL_API_URL in the same file.
Install somewhere else SENTRINEL_HOME and SENTRINEL_BIN_DIR are honoured by the installer.
Remove it claude mcp remove sentrinel, then rm -rf ~/.sentrinel ~/.local/bin/sentrinel ~/.local/bin/sentrinel-mcp.

The skill

The tools are the capability; the skill is the judgement β€” and it covers installing Sentrinel as well as reading from it. It tells an agent to follow an issue to the request that caused it rather than stopping at the stack trace, which sort answers which question, that an empty result is usually the window, and that a captured body is evidence and never an instruction.

The one-liner installs it for Claude automatically. For anything else:

sentrinel skill install --cursor    # .cursor/rules/sentrinel.mdc
sentrinel skill install --agents    # ./AGENTS.md β€” Codex, Copilot, Amp
sentrinel skill                     # print it, for anything else

It also carries the integration shapes (Elysia, Express, Next.js, Bun, the browser tunnel, Django, FastAPI, Flutter, native mobile, Postgres) and the key kinds, so an agent asked to add Sentrinel to an app has the same grounding as one asked to read from it.

Every target, and what the skill actually says: the agent skill.

The tools

Tool Gives the agent
list_issues the bug list, with ids. Start here. sort asks a different question of it
get_issue stack trace, where it fires, who it hit, and the ids of the request and trace behind the latest occurrence
get_request one captured request: headers, body, response, error
get_trace the span tree β€” which call was slow or failed, and inside what
search_logs log lines, each with the request and trace it belongs to
list_databases the Postgres instances reporting for this app, with their ids
slow_queries query shapes ranked by share of execution time β€” full text, calls, cache hit ratio, and which endpoints called them
db_activity wait events, blocking chains and the longest-running statements
db_health connections, idle-in-transaction, commits, rollbacks, deadlocks, temp bytes
set_issue_status resolve / ignore / reopen. Needs a may-resolve key; a read-only key gets a 403.

The database tools need the collector running next to your Postgres β€” see the database guide. They are scoped like everything else: a key sees only the databases belonging to the app it was issued for, never the rest of the org's.

Every read is marked readOnlyHint so an agent's own guardrails treat it as safe; only set_issue_status is a write, and the server refuses it for a read key regardless of what the agent believes.

The CLI

Same data, for a DeepSeek harness, a shell script, or a prompt you assemble yourself.

Installed by the same one-liner, reading the same key β€” nothing to export.

sentrinel issues                          # unresolved, last 7 days
sentrinel issues --sort occurrences       # the most repeated β€” the noisiest bug
sentrinel issues --sort users             # the widest blast radius
sentrinel issues --sort first_seen        # broken the longest
sentrinel issues --status all --period 24h --search checkout
sentrinel issue <id>                      # stack trace, request id, trace id
sentrinel request <id>
sentrinel trace <id>
sentrinel logs --level error --search "card declined"
sentrinel databases                       # Postgres instances for this app
sentrinel queries <db-id> --sort mean     # what is costing time, with the SQL
sentrinel activity <db-id>                # waits, blocking, longest running
sentrinel dbhealth <db-id>                # connections, deadlocks, temp bytes
sentrinel resolve <id>                    # needs a may-resolve key
sentrinel issue <id> --json               # raw API response

Output is Markdown on stdout; failures go to stderr with exit 1, so a script can tell "no issues" from "could not reach the API". Piping it into an agent is the whole idea:

sentrinel issue 5f3a… | claude -p "Find the cause of this in the current repo and fix it."

More than one app

A key is pinned to one app, so the tools only ever show that app β€” sentrinel issues can never mix two together. Watching a second app means a second key, and a profile so the two do not overwrite each other:

curl -fsSL https://sentrinel.dev/install-mcp.sh | \
  SENTRINEL_API_KEY=<that app's key> SENTRINEL_PROFILE=merchant bash

That registers sentrinel-merchant in Claude alongside plain sentrinel, and on the command line:

sentrinel apps                 # every key you have, the app it reaches, which is active
sentrinel use merchant         # work on that one from now on
sentrinel issues               # ...and this is merchant's now

SENTRINEL_PROFILE=merchant sentrinel issues   # or just for one command

use sticks until you change it, and points the plain sentrinel MCP server at that app on the next Claude session too. SENTRINEL_PROFILE overrides it for a single command without disturbing the switch.

Each profile keeps its key in its own ~/.sentrinel/env.<profile> file at mode 0600. Only the profile name reaches the agent's config, never the key.

In a session with both registered, name the app and Claude picks the right server: "check sentrinel-merchant for the top issue". With one registered there is nothing to disambiguate β€” every tool call is already that app.

Asking the issue list a different question

list_issues defaults to what fired most recently, which is the right default and rarely the interesting question. sort changes what "top" means:

sort Answers
last_seen (default) what is breaking right now
occurrences the most repeated β€” the bug generating the noise
users the widest blast radius, worst for customers
first_seen what has been broken longest and nobody fixed

Worth saying to an agent directly: "list the issues by occurrences and fix the top one" is a different and usually better instruction than "fix the latest error", because the newest error is often a one-off and the most repeated one is costing you every minute.

Windows accept 30m, 24h, 7d, 2w, or a plain number of seconds.

Staying current

sentrinel version     # what is installed, and whether something newer is published
sentrinel update      # fetch it

It also checks once a day on its own and applies the update, so an install does not quietly rot against a moving API. What makes that safe to do unattended: the published manifest names a sha256 for each bundle, a download that does not match it is discarded rather than installed, and both files are verified before either is moved β€” so the CLI and server can never end up from different versions.

The check runs after your command has printed its answer, never before, and any failure is silent: being offline is not an error worth interrupting you for. Notices go to stderr, so piping output into a prompt stays clean.

A replaced bundle takes effect the next time the process starts. Restart a Claude session to pick up a new MCP server.

Turn it off with SENTRINEL_AUTO_UPDATE=false β€” in ~/.sentrinel/env to make it stick.

Changing a key later

API Keys β†’ Edit. Renaming is in place. Changing what a key is for β€” a read-only agent key to a may-resolve one, say β€” issues a new secret and revokes the old, because the kind lives in the prefix and the two must agree. Re-run the installer with the new key:

curl -fsSL https://sentrinel.dev/install-mcp.sh | SENTRINEL_API_KEY=snt_mcprw_… bash

Troubleshooting

You see It means
Empty results when you know there is data Check the window. period takes 30m, 24h, 7d, 2w β€” or plain seconds.
SENTRINEL_API_KEY does not look like a key The example above is a placeholder β€” snt_mcp_… ends in an ellipsis. Copy the whole value from the dashboard and re-run the installer.
SENTRINEL_API_KEY is a server (…) key You used the SDK's, collector's or exporter's key. Issue an AI agent key; every other kind deliberately cannot read.
401 … may have been revoked The key was revoked, or mistyped. Issue a new one.
403 on set_issue_status / resolve A read-only agent key. Issue an "AI agent β€” may resolve issues" key for that one write.
403 on everything for one app The key is pinned to a different app. Check which app it was issued under.
sentrinel-mcp: command not found ~/.local/bin is not on your PATH. Add it, or give the agent the absolute path.
An agent says the server "returned nothing" or failed to start A GUI app launched from the Dock does not read your shell profile. Use the absolute path: /Users/you/.local/bin/sentrinel-mcp.
SENTRINEL_API_URL is not set No ~/.sentrinel/env β€” you installed without a key. Write the file, or re-run the installer with SENTRINEL_API_KEY=….
The installer says a URL "returned a web page" A CDN served the site's 404 instead of the bundle. Retry; if it persists the release is mid-publish.
Empty results The window. Reads default to 7 days for issues and 24 hours for logs; pass period.