Install Vesta
Install the SDK and call instrument() once. Pick your language and framework for the exact steps.
Set it up
Install
pip install vesta-sdkRequires Python 3.12 or newer. The only runtime dependency is OpenTelemetry.
Instrument your server
import vesta
from mcp.server.lowlevel import Server
server = Server("my-server")
# register your tools, then:
vesta.instrument(
server,
api_key="vsk_live_...",
session_context=lambda request: {
# who the end user is. Omit on authenticated surfaces — Vesta
# derives a pseudonymous user_id from the token automatically.
"user_id": user_id_from(request),
},
)Call instrument() after your tools are registered and before the server starts serving. The call is idempotent and fail-open; an error in the SDK never reaches the request path. session_context is optional and attaches per-session dimensions.
What you're setting
api_key
Authenticates your surface to Vesta. One key per MCP server, prefixed vsk_. Keep it in an environment variable or secret store, not in your source.
endpoint
The ingest URL traces are sent to. Defaults to Vesta's hosted ingest. Set it to a collector in your own Azure or GCP tenant to keep all trace data inside your boundary.
session_context
A callable, run once per session. Returns a user_id for the end user — plus any user attributes you genuinely segment by (Vesta defines no fixed set). Authenticated surfaces already get a pseudonymous user_id from the OAuth token automatically, so set it yourself only on an unauthenticated surface. Optional.
args / responses
Redaction rules for tool arguments and responses. Both are redacted by default. See Privacy and redaction below to capture or scrub specific fields.
Privacy and redaction
Redaction runs inside your own process, before anything is sent. Raw payloads never leave your environment. By default Vesta redacts every tool argument and response value, so you get the shape of each call, the tool name, the timing and the errors, but not the contents.
If you open capture up, a built-in denylist still scrubs common sensitive fields by name, like email, password, ssn, credit_card, api_keyand auth tokens. It's the lowest-precedence rule, so widening capture doesn't quietly start sending PII unless you name a sensitive field yourself.
Configuring your own rules is recommended for tighter control. Capture the non-sensitive fields you want to analyse, hash the ones you need joinable but not readable, redact or truncate the rest. Rules apply per field, so the policy is yours to set as coarsely or precisely as you like.
from vesta import RedactionConfig, Field
# arguments and responses are redacted by default.
# capture only the non-sensitive fields you want to analyse.
args = RedactionConfig(
default="redact",
rules=[
Field("query").capture(), # the agent's search text
Field("category").capture(),
Field("account_id").hash(), # joinable, not readable
],
)
vesta.instrument(server, api_key="vsk_live_...", args=args)Selectors: Field, Tool, Prompt, Resource, Method. Actions: capture(), redact(), hash(), truncate(n). Pass responses= for response payloads. Run it against a self-hosted endpoint and raw data never leaves your cloud at all.
Languages & frameworks
How it behaves
- Runs inside your server process. Never imports Vesta internals at runtime.
- Idempotent. Call it twice and the second call is a no-op.
- Fail-open. An error in Vesta never reaches your request path.
- OpenTelemetry under the hood. Traces export asynchronously over OTLP.