Connect an agent
Register an agent and get its key, send events to sentinel.verops.io or connect over MCP, the four rules that decide evaluation quality, the schema 2.0 envelope, the eight MCP tools, and how to verify the first run.
Step 1 — Register the agent
- Open Sentinel → Agents and click Register agent.
- Give it an Agent name and choose a Profile. The profile selects the evaluator set: every agent is judged on the five base dimensions, and
marketing,coding,support,raganddataadd domain dimensions on top.genericis the base set alone. You can also define your own under Configuration → Evaluators. Optionally place it in a fleet (all support bots, say) so it can be compared to its peers. - Copy the Sentinel key that appears. It is shown once; the platform stores only a hash. Rotate it from the agent's page if it is lost or leaked.
The key identifies this agent. It goes in the X-Sentinel-Key header on every request, whether you send events directly or connect over MCP.
Step 2 — Choose how to connect
| Path | How it works | Best for |
|---|---|---|
| Send events | Your agent POSTs JSON events to the ingest endpoint as the run progresses. No SDK — one HTTP call per event. | Any agent, any language. Production services that already have an HTTP client. |
| MCP server | Add Sentinel as an MCP server. The agent gets eight tools: it reports its own runs, reads back its evaluation, asks for its rubric and coaching, and can critique a draft before shipping it. | MCP-capable agents — Claude Code and Desktop, Cursor, custom agents — and any agent you want to self-correct rather than merely be observed. |
Both paths land in the same place and are evaluated the same way. The Agents page shows a ready-to-paste snippet for each, with the key filled in, right after registration.
Step 3a — Send events
The endpoint is on the dedicated Sentinel host, not the app host:
POST https://sentinel.verops.io/api/v1.0/sentinel/events
X-Sentinel-Key: <your agent key>
Content-Type: application/json
{
"event_type": "response.final",
"execution_id": "exec-8f3a",
"campaign_id": "summer-sale",
"payload": { "answer": "Returns are accepted within 30 days with a receipt." }
}
→ { "accepted": 1 }
| Field | Meaning | |
|---|---|---|
event_type | required | A stable, dotted, semantic name: run.started, tool.called, retrieval.done, response.final, campaign.generated. |
execution_id | strongly recommended | One stable id for one logical run. Every event of the run carries it; the engine judges them together. |
payload | recommended | The artifact to judge — the answer, the generated copy, the decision, the tool result. Evaluators read event_type: payload lines. |
campaign_id | optional | Groups related runs: a campaign, a workflow, a conversation. |
trace_id | optional | Correlates the run with your traces so root-cause analysis can line them up. |
timestamp | optional | Event time in epoch milliseconds; defaults to receive time. |
The four rules that decide evaluation quality
- One logical run = one
execution_id. Reuse it from request received, through every step, to the final output. A different id per event means nothing can be judged coherently. - Put the judged artifact in
payload. Metadata about the answer is not the answer. - Emit at lifecycle points, and always emit a terminal event. Run start (task, prompt, inputs), each meaningful step (tool call and result, retrieval, decision), and the final output before you return. The terminal event is what most evaluators score — if you send only one event, send that one.
- Name event types semantically and keep them stable, so dimensions stay comparable across versions and weeks.
The structured envelope (schema 2.0)
Events with no declared version are accepted as they always were. Declare "schema_version": "2.0" and the following become first-class, typed fields — filterable in the Explorer, broken out on dashboards, and used by cost and latency monitors — instead of free-form payload:
{
"schema_version": "2.0",
"event_type": "tool.called",
"execution_id": "exec-8f3a",
"agent": { "version": "1.4.2", "environment": "production" },
"model": { "provider": "anthropic", "name": "claude-opus-5",
"input_tokens": 812, "output_tokens": 140, "latency_ms": 935 },
"tool": { "name": "search", "status": "ok" },
"error": { "type": "timeout" },
"trace": { "trace_id": "…", "span_id": "…" },
"payload": { … }
}
Only the blocks that apply need to be present: a model call carries model, a tool call carries tool, a failure carries error. Flat agent_version, environment and trace_id keys are accepted too, and camelCase spellings of every field work, so an existing integration can adopt the envelope one field at a time.
High-cardinality values are budgeted: a dimension that grows past its budget folds new values into __other__ and keeps the original beside it, so an agent that mints a fresh tool name per call cannot blow up the aggregates. Per-run ids are never budgeted.
Step 3b — Connect over MCP
Add this to your agent's MCP configuration (the Agents page renders it with your key):
{
"mcpServers": {
"verops-sentinel": {
"url": "https://sentinel.verops.io/api/v1.0/sentinel/mcp",
"headers": { "X-Sentinel-Key": "<your agent key>" }
}
}
}
| Tool | What it does |
|---|---|
report_run | Report a run (its events) for evaluation — the MCP equivalent of sending events. |
get_evaluation | Read a run's scores, breach status, rationale and recommendations. |
evaluate_run | Trigger evaluation now and wait briefly for the result. |
critique_draft | Pre-flight: grade a draft against the rubric before shipping it. |
get_rubric | The dimensions and rubric this agent is judged on — so it can align itself. |
get_improvement_guidance | Coaching: instructions to improve, derived from this agent's own history. |
get_agent_health | The funnel for this agent: received, evaluated, clean, breached. |
apply_recommendation | Approve a recovery recommendation (delivers it through a connector). |
Every MCP call is audited against the agent that made it. The tools speak JSON-RPC over HTTP; ids are passed through untouched and errors never carry a result, so any conforming client works.
Step 4 — Verify
- Send one run with at least a terminal event. The response is
{"accepted": 1}. - Open Sentinel → Runs. The run appears within seconds in state Receiving, then Quiescing once events stop.
- About 90 seconds after the last event it is evaluated once. Open it: every dimension has a score, a rationale and — if a breach — an incident. To skip the wait, use Evaluate now on the run, or the
evaluate_runtool.
Then set the score that counts as a breach and decide what may happen automatically — Monitors, actions and recovery. If nothing arrives, Troubleshooting starts with exactly that case.