Documentation

Manual setup

Everything npx zevruna does, as separate steps you can run yourself, for locked-down environments, unusual repo layouts, or when you simply want to see what the wizard is doing before you let it. The quickstart is faster if you do not need this.

Every step ends in something you can check. Skip a step and the one after it will tell you.

1

Create a project and hold its token

A project is the unit everything else belongs to. The token is its credential; the account it belongs to is how you get back in if the token is lost.

Either sign in at zevruna.com/signup with Google, GitHub or an emailed link and copy the token from Settings, or run the device flow and approve in the browser, which signs you in on the way through:

npx zevruna init

Write it where your tools will find it. The CLI reads .env.zevruna; the SDK reads the environment.

echo "ZEVRUNA_TOKEN=zv_live_…" > .env.zevruna
echo ".env.zevruna" >> .gitignore

Done when `zevruna doctor` prints “Authenticated as <project>”.

2

Register the servers you depend on

Registering is what puts a server on the poller. Public HTTPS endpoints are polled for you; anything on your machine is not reachable from the cloud and takes the push path instead.

curl -X POST https://zevruna.com/api/servers \
  -H "Authorization: Bearer $ZEVRUNA_TOKEN" \
  -H "content-type: application/json" \
  -d '{"name":"acme-crm","endpoint":"https://acme.example.com/mcp"}'

Local or stdio servers: omit endpoint entirely. That registers a push-only server, one Zevruna never tries to reach, whose contract you send from your own machine or CI in step 3.

curl -X POST https://zevruna.com/api/servers \
  -H "Authorization: Bearer $ZEVRUNA_TOKEN" \
  -H "content-type: application/json" \
  -d '{"name":"local-tools"}'

Done when `zevruna doctor` reports “n server(s) registered”.

3

Take the first snapshot

A contract with no baseline cannot drift, there is nothing to compare against. This is the reading everything later is diffed from.

zevruna snapshot https://acme.example.com/mcp --push   # remote
zevruna snapshot local-tools --push                     # local or stdio, by name

A bare name resolves against your client config, so a server the cloud cannot reach is handshaked locally and only its contract is uploaded. Re-run it from CI so a push-only server stays current, nothing else will update it.

Done when the command prints a hash, and the dashboard shows it against the server.

4

Describe your call sites

A contract change on its own is noise. The manifest is what turns “a field moved” into “these two agents break, at these lines”.

zevruna scan --server acme-crm --consumer support-agent

This writes agents/support-agent.yaml and uploads it. A file in agents/ that never reached the project computes no blast radius, which is why doctor checks for the upload rather than the file.

Done when `zevruna doctor` reports “n manifest(s) declared”, not just files on disk.

5

Install the SDK and wrap exactly one run

Steps 1–4 tell you a contract moved. This tells you what your agent actually did, including runs that failed while every call returned 200.

Three SDKs, one behaviour. Pick the one your agent is written in, or skip this step entirely if you already export OpenTelemetry — see OTLP ingestion.

npm i @zevruna/observe            # or: npm i @zevruna/observe -w @your/api
pip install zevruna
go get github.com/Kevin-Benelli/zevruna-go

In a Node monorepo, install it in the workspace that imports it. A root install still resolves by hoisting, so the import works and tsc stays quiet, until a clean install elsewhere, where it does not.

import { observeAgent, instrumentMcpClient } from "@zevruna/observe";

const client = instrumentMcpClient(mcpClient, "acme-crm");
await observeAgent({ name: "support-agent" }, () => runSupportAgent(input));
from zevruna import observe_agent, instrument_mcp_client

client = instrument_mcp_client(mcp_client, "acme-crm")
async with observe_agent("support-agent"):
    await run_support_agent(user_input)

In Node, wrap on the server. That SDK needs node:async_hooks, which a bundler stubs to an empty object, so a browser-side wrap builds green and then throws when the chunk evaluates. If your turn loop is client-side, wrap the route it calls. Python and Go have no equivalent trap.

One boundary only. Wrapping a function and something it calls nests the spans and counts every run twice. Pick the outermost place a run begins.

The token has to reach that process. The SDK reads ZEVRUNA_TOKEN from the environment and stays silent without it, telemetry must never take down the agent it watches, so a missing token looks exactly like a wrap that never ran.

Done when `zevruna doctor` reports telemetry_received. Not when the diff looks right, when a run has arrived.

6

Gate the build

An alert is easy to ignore; a red check is not. This is also the only thing that resolves an incident, a green check is the resolution.

# .github/workflows/contracts.yml
name: contracts
on:
  push: { branches: [main] }
  pull_request:
  schedule: [{ cron: "0 6 * * *" }]   # nightly: the server drifts, your code doesn't

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npx --yes zevruna@latest check --manifest "agents/*.yaml" --against pinned
        env:
          ZEVRUNA_TOKEN: ${{ secrets.ZEVRUNA_TOKEN }}

Keep the nightly run. It is the one that catches a server drifting while nothing in your repo changed, which no test you own can see, because nothing you own changed.

Done when a pull request shows a “contracts” check.

Checking your work

One command verifies all six against reality rather than against your config, and names the single next step in dependency order, so you fix the thing that unblocks the others, not the one that looks most actionable.

zevruna doctor          # exits 1 while anything blocks, so it works as a gate
zevruna doctor --json   # same verdict, for a script or a coding agent

Prefer not to do any of this by hand? The quickstart is one command, and your coding agent can drive it, see setting up with AI.