MCP Server (AI Agents)
Give AI coding assistants scoped, audited access to your secrets — hosted OAuth 2.1 endpoint or local stdio server, scoped tools, per-team grants.
Connect AI coding assistants like Claude Code and Cursor to varsafe via the Model Context Protocol. Your AI agent can list projects, read secrets, set values, compare environments, and export configurations — all with fine-grained permissions and a full audit trail.
Ways to Connect
varsafe offers two MCP transports, and the hosted one accepts two kinds of credential. They are near-identical, but not interchangeable — two tools exist on only one transport, so check the tool reference before committing:
| Option | Transport | Auth | Best for |
|---|---|---|---|
| Hosted + OAuth | HTTPS | OAuth 2.1 | Zero-config: browser-based consent, no tokens to manage |
| Hosted + API token | HTTPS | API token | Headless machines, or a different account from the one your CLI uses |
| Local stdio | stdin/out | API token | Local inspection, self-hosting, or MCP clients without URL support |
The two overlap heavily but are not identical: varsafe_generate_secret exists only on the hosted transport, and shared tools take different parameter names (the hosted endpoint uses environmentSlug, the stdio server uses environment). The stdio server also accepts a teamId argument on every tool, required when your token spans multiple teams. The tool reference lists both signatures per tool.
The Hosted option is the default recommendation — most users should start there.
How It Works (Hosted)
- Discover — Your AI tool reads the MCP server URL and discovers OAuth endpoints automatically
- Consent — Your browser opens a consent screen where you choose teams, scopes, and optional restrictions
- Authorize — The agent receives an OAuth 2.1 access token (PKCE-secured, 1-hour expiry, auto-refreshed)
- Use — The agent calls varsafe tools with the token; every action is audited
Quick Setup
Add the varsafe MCP server to your AI tool’s configuration.
Claude Code
Run the claude mcp add command:
claude mcp add --scope user --transport http varsafe https://api.varsafe.dev/mcp
--scope user registers the server globally for every project (written to ~/.claude.json). Drop it to add the server to the current project’s .mcp.json instead, which you can commit to share with teammates.
Prefer to manage the config by hand? Drop this into a .mcp.json at the repo root:
{
"mcpServers": {
"varsafe": {
"type": "http",
"url": "https://api.varsafe.dev/mcp"
}
}
}
Cursor
Add to .cursor/mcp.json (project) or ~/.cursor/mcp.json (global):
{
"mcpServers": {
"varsafe": {
"type": "url",
"url": "https://api.varsafe.dev/mcp"
}
}
}
On the first tool call, your browser opens a consent screen. Pick your teams and scope preset, and you’re connected.
Connect With an API Token
The OAuth flow opens a browser and waits for the redirect to come back to the machine your MCP client is running on. That machine and the browser have to be the same one. If you start the flow on a headless server over SSH and open the link on your laptop, the callback lands on your laptop’s localhost, where nothing is listening, and authorization never completes.
An API token connects to the same hosted endpoint without a browser. It is also how you run your agent as a different account from your CLI — the token is independent of whatever identity varsafe login stored in your keychain.
1. Create a token with MCP scopes
In the dashboard, go to API Tokens, create a token, and grant it the MCP scopes the agent needs. A token without MCP scopes is refused by the MCP endpoint — existing tokens are unaffected and cannot drive an agent until you say so.
Scopes are bounded by the token’s access level and by your own role: a read-only token cannot hold secrets:write, and narrowing a token to read-only later removes that scope automatically.
2. Point your client at the hosted endpoint with the token attached
claude mcp add --scope user --transport http varsafe https://api.varsafe.dev/mcp \
--header "Authorization: Bearer vs_at_..."
Or in configuration:
{
"mcpServers": {
"varsafe": {
"type": "http",
"url": "https://api.varsafe.dev/mcp",
"headers": { "Authorization": "Bearer vs_at_..." }
}
}
}
Because the request already carries a credential, the endpoint never asks for authorization — your client shows the server as connected rather than prompting you to authenticate.
Actions taken through a token are audited as the token, not as the person who created it. The creator’s team membership still governs the token: if they leave the team or are deactivated, the token stops working.
Local Stdio Alternative
If you prefer to run the MCP server as a local subprocess — useful for self-hosted instances, inspection, or clients that don’t support "type": "url" — use the @varsafe/mcp-server npm package (it also installs a varsafe-mcp binary if you prefer a global install over npx).
1. Create an API token
In the dashboard, go to API Tokens, select the team, and click Create API Token. Copy the token — it’s only shown once.
2. Add the local stdio config
Claude Code — run:
claude mcp add --scope user --env VARSAFE_API_TOKEN=vs_at_... varsafe -- npx -y @varsafe/mcp-server
Or commit a project-scope .mcp.json at the repo root:
{
"mcpServers": {
"varsafe": {
"command": "npx",
"args": ["-y", "@varsafe/mcp-server"],
"env": {
"VARSAFE_API_TOKEN": "vs_at_..."
}
}
}
}
Cursor — add to .cursor/mcp.json:
{
"mcpServers": {
"varsafe": {
"command": "npx",
"args": ["-y", "@varsafe/mcp-server"],
"env": {
"VARSAFE_API_TOKEN": "vs_at_..."
}
}
}
}
3. Environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
VARSAFE_API_TOKEN |
Yes | — | varsafe API token (vs_at_...). VARSAFE_TOKEN is accepted as an alias. |
VARSAFE_API_URL |
No | https://api.varsafe.dev |
Override the API base URL (self-hosted instances, staging, etc.). |
VARSAFE_MCP_DEBUG |
No | unset | When set to any truthy value, logs every API call to stderr. |
Trade-offs
- Hosted is faster to set up (no token), auto-refreshes, and gets new tools without any update. Preferred for most users.
- Local stdio keeps the token under your control, works offline against self-hosted instances, and is inspectable — every file is readable in the
@varsafe/mcp-serverpackage on npm or GitHub. - Both share the same audit trail server-side, so you can switch freely.
Consent Screen
When your AI tool connects for the first time, your browser opens a consent screen where you control exactly what the agent can access.
Scope Presets
Choose how much access to grant:
| Preset | What the agent can do |
|---|---|
| Read Only | List projects, environments, and secret keys (no values) |
| Secrets Full | Everything in Read Only + read secret values, write secrets, export |
| Full Access | Everything in Secrets Full + audit log access |
Granular Restrictions
You can further limit what the agent accesses:
- Team selection — Choose which teams to grant access to
- Project restrictions — Limit to specific projects within a team
- Environment restrictions — Limit to specific environments (e.g., only
development, neverproduction)
When no project or environment restriction is set, the agent can access all projects and environments in the granted teams — subject to your own role permissions.
Client Verification
The consent screen shows whether the connecting client is a verified application:
- Verified (green shield) — Pre-registered clients like Claude Code (Anthropic) and Cursor
- Third-party — Dynamically registered MCP clients; review carefully before approving
Available Tools
varsafe exposes 11 MCP tools across two transports:
| Transport | Endpoint | Tools |
|---|---|---|
| Remote | https://api.varsafe.dev/mcp |
11 |
| Subprocess | npx @varsafe/mcp-server |
10 |
The two transports are not interchangeable. 1 tool(s) exist on only one transport:
varsafe_generate_secret(remote only). Check the Available on line before relying on a tool.
Parameter names differ between transports for
varsafe_list_secrets,varsafe_diff_secrets,varsafe_get_secret_value,varsafe_get_secret_values,varsafe_export_secrets,varsafe_set_secret,varsafe_unset_secret. Each entry below lists both. The remote transport also accepts ateamIdon every tool, which is required when the access token spans multiple teams.
varsafe_whoami
View current identity, granted teams, and scopes
Available on: remote + subprocess · Scopes: identity:read · Category: read
Remote parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
teamId |
string | no | Team ID to operate on. Required when the access token spans multiple teams; optional when only one team was consented (defaults to that team). |
Subprocess parameters
Takes no parameters.
varsafe_list_projects
List all projects in a team
Available on: remote + subprocess · Scopes: projects:read · Category: read
Remote parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
teamId |
string | no | Team ID to operate on. Required when the access token spans multiple teams; optional when only one team was consented (defaults to that team). |
Subprocess parameters
Takes no parameters.
varsafe_list_environments
List environments in a project
Available on: remote + subprocess · Scopes: projects:read · Category: read
Remote parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project ID |
teamId |
string | no | Team ID to operate on. Required when the access token spans multiple teams; optional when only one team was consented (defaults to that team). |
Subprocess parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project UUID — obtain via varsafe_list_projects. |
varsafe_list_secrets
List secrets in an environment (metadata only, no values)
Available on: remote + subprocess · Scopes: secrets:read · Category: read
Remote parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project ID |
environmentSlug |
string | yes | Environment slug (e.g. “production”) |
teamId |
string | no | Team ID to operate on. Required when the access token spans multiple teams; optional when only one team was consented (defaults to that team). |
Subprocess parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project UUID. |
environment |
string | yes | Environment slug, e.g. “development”, “staging”, “production”. |
varsafe_diff_secrets
Compare secret keys between two environments
Available on: remote + subprocess · Scopes: secrets:read · Category: read
Remote parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project ID |
sourceEnvSlug |
string | yes | Source environment slug |
targetEnvSlug |
string | yes | Target environment slug |
teamId |
string | no | Team ID to operate on. Required when the access token spans multiple teams; optional when only one team was consented (defaults to that team). |
Subprocess parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project UUID. |
sourceEnv |
string | yes | Source environment slug to compare from. |
targetEnv |
string | yes | Target environment slug to compare against. |
varsafe_get_secret_value
Read the decrypted value of ONE secret, by key. Prefer this over varsafe_get_secret_values, which returns every value in the environment.
Available on: remote + subprocess · Scopes: secrets:read_values · Category: export
Remote parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project ID |
environmentSlug |
string | yes | Environment slug (e.g. “production”) |
key |
string | yes | Key of the single secret to read, in UPPER_SNAKE_CASE |
teamId |
string | no | Team ID to operate on. Required when the access token spans multiple teams; optional when only one team was consented (defaults to that team). |
Subprocess parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project UUID. |
environment |
string | yes | Environment slug, e.g. “production”. |
key |
string | yes | Key of the single secret to read, in UPPER_SNAKE_CASE. |
varsafe_get_secret_values
List secrets with their decrypted values in an environment
Available on: remote + subprocess · Scopes: secrets:read_values · Category: export
Remote parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project ID |
environmentSlug |
string | yes | Environment slug (e.g. “production”) |
teamId |
string | no | Team ID to operate on. Required when the access token spans multiple teams; optional when only one team was consented (defaults to that team). |
Subprocess parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project UUID. |
environment |
string | yes | Environment slug, e.g. “production”. |
varsafe_export_secrets
Export all secrets as key-value pairs for injection (e.g. into shell environment)
Available on: remote + subprocess · Scopes: secrets:run · Category: export
Remote parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project ID |
environmentSlug |
string | yes | Environment slug (e.g. “production”) |
includePattern |
string | no | Optional glob pattern to filter keys (e.g. “DB_*”); * is the only wildcard |
teamId |
string | no | Team ID to operate on. Required when the access token spans multiple teams; optional when only one team was consented (defaults to that team). |
Subprocess parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project UUID. |
environment |
string | yes | Environment slug, e.g. “production”. |
varsafe_set_secret
Create or update a secret in an environment
Available on: remote + subprocess · Scopes: secrets:write · Category: write
Remote parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project ID |
environmentSlug |
string | yes | Environment slug (e.g. “production”) |
key |
string | yes | Secret key name (e.g. “DATABASE_URL”) |
value |
string | yes | Secret value |
teamId |
string | no | Team ID to operate on. Required when the access token spans multiple teams; optional when only one team was consented (defaults to that team). |
Subprocess parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project UUID. |
environment |
string | yes | Environment slug, e.g. “production”. |
key |
string | yes | Secret key name in UPPER_SNAKE_CASE. |
value |
string | yes | The secret value to store. |
varsafe_unset_secret
Delete a secret from an environment
Available on: remote + subprocess · Scopes: secrets:write · Category: write
Remote parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project ID |
environmentSlug |
string | yes | Environment slug (e.g. “production”) |
key |
string | yes | Secret key name to delete |
disposition |
flatten | cascade |
no | Required only if other secrets are composed from this one: “flatten” copies this secret’s current value into each of them, “cascade” deletes them too |
teamId |
string | no | Team ID to operate on. Required when the access token spans multiple teams; optional when only one team was consented (defaults to that team). |
Subprocess parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project UUID. |
environment |
string | yes | Environment slug, e.g. “production”. |
key |
string | yes | Secret key name to delete. |
disposition |
flatten | cascade |
no | Required only when other secrets are composed from this one. “flatten” copies this secret’s current value into each of them so they keep working; “cascade” deletes them too. |
varsafe_generate_secret
Server-generates a secret value with the chosen shape and stores it (create or update). Plaintext never appears in the conversation transcript — generated and stored in one call. Use for HMAC keys, signing keys, webhook secrets, UUIDs, random passwords. Returns metadata + a sha256 fingerprint of the value for rotation verification.
Available on: remote only · Scopes: secrets:write · Category: write
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId |
string | yes | Project ID |
environmentSlug |
string | yes | Environment slug (e.g. “production”) |
key |
string | yes | Secret key name (e.g. “SECRET_OPERATION_HMAC_KEY”) |
format |
random_bytes | uuid | alphanumeric | hex_token |
no | Output shape: random_bytes (default, base64-encoded entropy), uuid (RFC 4122 v4), alphanumeric ([A-Za-z0-9]^length), hex_token (hex-encoded entropy). — default "random_bytes" |
bytes |
number | no | Random byte count for random_bytes / hex_token. Default 32 (256-bit). |
encoding |
base64 | hex | base64url |
no | Encoding for random_bytes (base64 default). |
length |
number | no | Character length for alphanumeric. Default 32. |
prefix |
string | no | Optional literal prefix prepended to the generated value (e.g. “vk_”, “whsec_”). |
teamId |
string | no | Team ID to operate on. Required when the access token spans multiple teams; optional when only one team was consented (defaults to that team). |
Scopes Reference
| Scope | Description |
|---|---|
identity:read |
View user identity and granted teams |
projects:read |
List projects and environments |
secrets:read |
List secret keys and metadata (no values) |
secrets:read_values |
Read decrypted secret values |
secrets:write |
Create, update, and delete secrets |
secrets:run |
Export secrets for injection |
audit:read |
View audit logs |
Managing Connections
Viewing Active Connections
Go to MCP in the dashboard sidebar to see all active agent connections. Each connection shows:
- Client name and verification status
- Team access
- Granted scopes
- Connection date and last-used date
Revoking Access
Click Revoke on any connection to immediately disconnect the agent. The agent will need to re-authorize through the consent screen to reconnect.
Auto-Expiration
Connections that haven’t been used for 90 days are automatically expired. Re-authorize by using the agent again — the consent flow will re-trigger.
Security Model
OAuth 2.1 with PKCE
MCP uses the OAuth 2.1 authorization code flow with Proof Key for Code Exchange (PKCE S256). This means:
- No client secrets are stored on your machine
- Authorization codes are single-use (5-minute expiry)
- Access tokens are short-lived (1 hour) and auto-refreshed
- Refresh tokens are rotated on every use (old tokens are invalidated)
Audit Trail
Every MCP tool call is logged in the audit trail, just like CLI and dashboard actions. You can see:
- Which agent performed the action
- Which team, project, and environment were accessed
- The specific operation (list, read values, write, export)
- Timestamp and IP address
Rate Limits
MCP tools are rate-limited per connection:
| Category | Limit | Burst |
|---|---|---|
| Read operations | 120/min | 30 |
| Write operations | 60/min | 15 |
| Export operations | 20/min | 5 |
When rate-limited, the agent receives a 429 response with a Retry-After header.
Permission Boundaries
MCP grants are bounded by your own permissions:
- If you’re a Developer, the agent can’t write to protected environments — even with
secrets:writescope - If you restrict the grant to specific projects, the agent can’t access other projects
- Your role’s access level is the ceiling; MCP scopes can only narrow it further
Example Workflows
Ask your agent to check for missing secrets
“Compare the secrets between staging and production for the api-backend project. Are there any keys missing in production?”
The agent uses varsafe_list_projects to find the project, then varsafe_diff_secrets to compare environments.
Set a secret from your editor
“Set the
OPENAI_API_KEYtosk-proj-abc123in the development environment of my api project.”
The agent uses varsafe_set_secret to create or update the secret.
Export secrets for a local script
“Export all secrets matching
DB_*from the staging environment.”
The agent uses varsafe_export_secrets with the includePattern parameter to filter by glob.
Troubleshooting
No MCP connections’ after setup
The consent flow only triggers on the first tool call, not when the config is added. Ask your AI agent to do something with varsafe (e.g., “list my varsafe projects”) to trigger the authorization.
Consent screen doesn’t open
Make sure you’re logged into the varsafe dashboard in the same browser that will handle the OAuth redirect. The MCP server redirects to your dashboard for consent.
Agent says ‘access denied’ for a project
Check your grant restrictions. If you limited the grant to specific projects or environments during consent, the agent can’t access anything outside those restrictions. Revoke the connection and re-authorize with broader access.
Agent stopped working after a while
Connections auto-expire after 90 days of inactivity. The agent will re-trigger the consent flow automatically — just use it again.
Rate limit exceeded
MCP tools are rate-limited. Wait for the Retry-After period and try again. If you’re hitting limits regularly, batch your requests or space them out.
Related Documentation
- CLI Reference — Managing secrets from the command line
- API Tokens — Programmatic access for CI/CD
- Security Model — How your secrets stay safe
- Core Concepts — Teams, projects, and environments