Skip to content

Latest commit

 

History

History
1835 lines (1413 loc) · 47.4 KB

File metadata and controls

1835 lines (1413 loc) · 47.4 KB

Symaira Vault MCP API Documentation

This document provides comprehensive API documentation for the Symaira Vault Model Context Protocol (MCP) server, enabling agent developers to integrate with Symaira Vault securely.

Table of Contents

  1. Overview
  2. Authentication
  3. Transport Modes
  4. Tool Reference
  5. Error Handling
  6. Rate Limiting
  7. Agent Configuration
  8. Field Redaction with redactFields
  9. Examples

Overview

Symaira Vault exposes a Model Context Protocol (MCP) server that allows AI agents to securely read and optionally write password vault entries. The MCP server supports both stdio (standard input/output) and HTTP transports.

Key Features

  • Structured API: Type-safe tool invocations with JSON schemas
  • Per-Agent Access Control: Each agent can have different permissions
  • Bearer Authentication: HTTP mode uses token-based auth
  • Audit Logging: All operations are logged for security monitoring
  • Metadata Support: Version tracking for credential caching

Available Tools

Tool Description Write Operation
health Check server health status No
get_auth_status Check Symaira Vault unlock auth status No
set_auth_method Change unlock auth method Config write
list_entries List all vault entries No
get_entry Retrieve entry contents No
get_entry_metadata Get entry metadata without sensitive data No
find_entries Search entries by path No
search Search vault entries by query (OpenAI Company Knowledge format) No
fetch Fetch vault entry by path/id (OpenAI Company Knowledge format) No
generate_password Generate secure passwords No
generate_totp Generate TOTP codes No
copy_to_clipboard Copy entry password to system clipboard (auto-clears) No
autotype Type entry field as keyboard input into focused app No
set_entry_field Store or update a field Yes
run_command Execute command with secret env injection Yes
delete_entry Delete an entry Yes
symvault_delete Deprecated alias for delete_entry Yes
secure_input Prompt user for sensitive data via TTY or native GUI dialog Yes
request_credential Agent-initiated: native dialog for a missing credential, stored without exposure Yes
prepare_payment Validate a payment entry, show approval prompt, autotype card/bank fields — card values never returned No

Authentication

HTTP Mode

HTTP mode requires bearer token authentication. The token is auto-generated on first server start.

Token Location

<vault>/mcp-token

Authentication Header

Authorization: Bearer <token>

Agent Identification Header

X-Symaira-Agent: <agent-profile-name>

Example Request

curl -H "Authorization: Bearer $(cat ~/.symvault/mcp-token)" \
     -H "X-Symaira-Agent: claude-code" \
     -H "Content-Type: application/json" \
     -X POST \
     -d '{"tool": "list_entries", "arguments": {}}' \
     http://127.0.0.1:8080/mcp

Stdio Mode

Stdio mode does not use HTTP authentication. The agent is identified via the --agent flag:

symvault serve --stdio --agent claude-code

The agent name must match a profile in the vault configuration.


OAuth 2.1 with PKCE and DCR

Symaira Vault implements OAuth 2.1 with PKCE and Dynamic Client Registration (DCR). This allows MCP clients that support DCR (such as opencode) to automatically register with the Symaira Vault MCP server and obtain scoped tokens without manual token management.

OAuth Flow Overview

  1. Discovery: The client fetches /.well-known/oauth-authorization-server to find the authorize, token, and registration endpoints.
  2. Registration: The client sends a POST /oauth/register with its redirect URIs. The server returns a client_id.
  3. Authorization: The client redirects the user to /mcp/oauth/authorize with PKCE challenge. The server prompts for user consent via TTY and issues a short-lived authorization code.
  4. Token Exchange: The client exchanges the authorization code for an access token + refresh token at /mcp/oauth/token.
  5. Refresh: When the access token expires, the client uses the refresh token to obtain a new pair without user interaction.

Endpoint Reference

Endpoint Method Status Description
/.well-known/oauth-protected-resource GET 200 Protected resource metadata (RFC 9728)
/.well-known/oauth-authorization-server GET 200 Authorization server metadata (RFC 8414)
/oauth/register POST 201 Dynamic client registration (RFC 7591)
/mcp/oauth/authorize GET 302/400 Authorization request (RFC 6749 §4.1.1)
/mcp/oauth/token POST 200/400 Token exchange and refresh (RFC 6749 §4.1.3, §6)

Discovery Endpoints

GET /.well-known/oauth-protected-resource
HTTP/1.1 200 OK
Content-Type: application/json

{
  "resource": "http://127.0.0.1:8080/mcp",
  "bearer_methods_supported": ["header"],
  "resource_name": "Symaira Vault MCP Server"
}
GET /.well-known/oauth-authorization-server
HTTP/1.1 200 OK
Content-Type: application/json

{
  "issuer": "http://127.0.0.1:8080",
  "authorization_endpoint": "http://127.0.0.1:8080/mcp/oauth/authorize",
  "token_endpoint": "http://127.0.0.1:8080/mcp/oauth/token",
  "registration_endpoint": "http://127.0.0.1:8080/oauth/register",
  "response_types_supported": ["code"],
  "code_challenge_methods_supported": ["S256"],
  "token_endpoint_auth_methods_supported": ["none"],
  "grant_types_supported": ["authorization_code", "refresh_token"]
}

Client Registration

POST /oauth/register
Content-Type: application/json

{
  "redirect_uris": ["http://127.0.0.1:4321/callback"]
}

Response (201 Created):

{
  "client_id": "a1b2c3d4e5f6a7b8",
  "client_id_issued_at": 1700000000,
  "client_secret_expires_at": 0,
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "redirect_uris": ["http://127.0.0.1:4321/callback"]
}

Client registrations are persistent across server restarts (stored in <vault-dir>/mcp-oauth-clients.json).

Token Exchange (authorization_code)

POST /mcp/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=AUTH_CODE&
code_verifier=VERIFIER

Response (200 OK):

{
  "access_token": "a1b2c3d4...",
  "token_type": "Bearer",
  "expires_in": 86400,
  "refresh_token": "e5f6a7b8..."
}

Token Refresh

POST /mcp/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&
refresh_token=REFRESH_TOKEN

Response (200 OK):

{
  "access_token": "new_access_token...",
  "token_type": "Bearer",
  "expires_in": 86400,
  "refresh_token": "new_refresh_token..."
}

Token rotation: each refresh invalidates the previous access token and refresh token (single-use pattern).

Configuration

OAuth token TTLs are configurable via config.yaml:

mcp:
  oauth:
    access_token_ttl: 1h    # default: 24h
    refresh_token_ttl: 720h # default: 720h (30 days)

opencode Integration

opencode can use either OAuth with DCR or static bearer token auth.

OAuth with DCR (recommended):

{
  "mcpServers": {
    "symvault": {
      "type": "remote",
      "url": "http://127.0.0.1:8080/mcp",
      "oauth": true
    }
  }
}

With this configuration, opencode mcp auth symvault will:

  1. Discover the authorization server metadata
  2. Register a client via DCR
  3. Walk through the PKCE authorization flow (user consent via TTY)
  4. Receive scoped access+refresh tokens
  5. Automatically refresh tokens when they expire

Bearer token auth (legacy):

{
  "mcpServers": {
    "symvault": {
      "type": "remote",
      "url": "http://127.0.0.1:8080/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_SYMVAULT_TOKEN",
        "X-Symaira-Agent": "opencode"
      },
      "oauth": false
    }
  }
}

Transport Modes

Stdio Mode (Recommended for Local Agents)

Best for: Local AI agents with direct process communication.

Advantages:

  • No network exposure
  • Simpler configuration
  • Lower latency
  • No token management

Start the server:

symvault serve --stdio --agent <profile-name>

Generate configuration:

symvault agent install <agent-name> --config-only

HTTP Mode

Best for: Remote agents, multiple clients, or service integration.

Advantages:

  • Network accessible
  • Multiple concurrent clients
  • Compatible with HTTP-based tools

Start the server:

symvault serve --port 8080 --agent <profile-name>

Generate configuration (token redacted by default):

symvault agent install <agent-name> --http --config-only

Include token in output:

symvault agent token <agent-name> new --tools list_entries,get_entry --expires 24h

Tool Reference

health

Check the MCP server health status.

Request:

{
  "tool": "health",
  "arguments": {}
}

Response:

{
  "status": "healthy",
  "timestamp": "2026-04-21T10:30:00Z",
  "version": "1.0.0"
}

HTTP Endpoint: GET /health (no authentication required)


get_auth_status

Return the configured unlock method, Touch ID availability, and session cache backend.

Request:

{
  "tool": "get_auth_status",
  "arguments": {}
}

set_auth_method

Set the unlock method to passphrase or touchid. The calling agent profile must set canManageConfig: true. MCP never accepts a passphrase from the agent; Touch ID setup requires an already active Symaira Vault session.

Request:

{
  "tool": "set_auth_method",
  "arguments": {
    "method": "touchid"
  }
}

list_entries

List all password entries in the vault.

Request:

{
  "tool": "list_entries",
  "arguments": {}
}

Response:

{
  "entries": [
    {
      "path": "github",
      "modified": "2026-01-15T14:32:00Z"
    },
    {
      "path": "work/aws",
      "modified": "2026-02-20T09:15:00Z"
    }
  ]
}

Notes:

  • Returns paths relative to vault root
  • Does not include entry contents
  • Sorted by path

get_entry

Retrieve the contents of a password entry.

Request:

{
  "tool": "get_entry",
  "arguments": {
    "path": "github",
    "include_metadata": false
  }
}

Parameters:

Parameter Type Required Description
path string Yes Entry path (e.g., "github" or "work/aws")
include_metadata boolean No Include creation/update metadata

Response (without metadata):

{
  "path": "github",
  "data": {
    "password": "mysecretpassword",
    "username": "myuser",
    "url": "https://github.com"
  }
}

Response (with metadata):

{
  "path": "github",
  "data": {
    "password": "mysecretpassword",
    "username": "myuser",
    "url": "https://github.com"
  },
  "meta": {
    "created": "2026-01-15T14:32:00Z",
    "updated": "2026-04-21T09:45:00Z",
    "version": 5
  }
}

Errors:

  • not_found: Entry does not exist
  • access_denied: Agent profile restricts access to this path
  • vault_locked: Vault is locked, run symvault unlock.

get_entry_metadata

Get entry metadata without retrieving sensitive data. Useful for cache validation.

Request:

{
  "tool": "get_entry_metadata",
  "arguments": {
    "path": "api/kimi-key"
  }
}

Parameters:

Parameter Type Required Description
path string Yes Entry path

Response:

{
  "path": "api/kimi-key",
  "exists": true,
  "created": "2026-01-15T14:32:00Z",
  "updated": "2026-04-21T09:45:00Z",
  "version": 5
}

Use Case: Credential Cache Validation

Agents can compare the version field with their cached version to determine if credentials need refresh:

// Check if cached credentials are stale
{
  "tool": "get_entry_metadata",
  "arguments": {
    "path": "api/kimi-key"
  }
}

find_entries

Search for entries by path substring.

Request:

{
  "tool": "find_entries",
  "arguments": {
    "query": "aws"
  }
}

Parameters:

Parameter Type Required Description
query string Yes Search string to match against entry paths

Response:

{
  "entries": [
    {
      "path": "work/aws",
      "modified": "2026-02-20T09:15:00Z"
    },
    {
      "path": "work/aws-staging",
      "modified": "2026-03-10T11:20:00Z"
    }
  ]
}

search

Search vault entries by query. Returns results in OpenAI Company Knowledge compatible format with both structured and text content.

Request:

{
  "tool": "search",
  "arguments": {
    "query": "aws"
  }
}

Parameters:

Parameter Type Required Description
query string Yes Search query to match against entry paths and field values

Response:

The response includes both structuredContent and content fields simultaneously:

{
  "structuredContent": {
    "results": [
      {
        "id": "work/aws",
        "title": "aws",
        "url": "symvault://entry/work/aws"
      },
      {
        "id": "work/aws-staging",
        "title": "aws-staging",
        "url": "symvault://entry/work/aws-staging"
      }
    ]
  },
  "content": [
    {
      "type": "text",
      "text": "[{\"id\":\"work/aws\",\"title\":\"aws\",\"url\":\"symvault://entry/work/aws\"},{\"id\":\"work/aws-staging\",\"title\":\"aws-staging\",\"url\":\"symvault://entry/work/aws-staging\"}]"
    }
  ]
}

Notes:

  • Uses the same search engine as find_entries
  • Each result includes id (entry path), title (entry name), and url (vault URL)
  • Scope filtering respects the agent's allowedPaths configuration
  • Read-only operation (no write permissions required)

fetch

Fetch a vault entry by path/id. Returns full entry content with metadata and values in OpenAI Company Knowledge compatible format.

Request:

{
  "tool": "fetch",
  "arguments": {
    "id": "work/aws"
  }
}

Parameters:

Parameter Type Required Description
id string Yes Entry path or id to fetch (e.g., "github" or "work/aws")

Response:

The response includes both structuredContent and content fields simultaneously:

{
  "structuredContent": {
    "id": "work/aws",
    "title": "aws",
    "url": "symvault://entry/work/aws",
    "metadata": {
      "created": "2026-01-15T14:32:00Z",
      "updated": "2026-04-21T09:45:00Z",
      "version": 5,
      "type": "password"
    },
    "values": {
      "password": "...",
      "username": "admin"
    }
  },
  "content": [
    {
      "type": "text",
      "text": "{\"id\":\"work/aws\",\"title\":\"aws\",\"url\":\"symvault://entry/work/aws\",\"metadata\":{\"created\":\"2026-01-15T14:32:00Z\",\"updated\":\"2026-04-21T09:45:00Z\",\"version\":5,\"type\":\"password\"},\"values\":{\"password\":\"...\",\"username\":\"admin\"}}"
    }
  ]
}

Notes:

  • The metadata field always contains created, updated, version, and type
  • The values field is only included when the agent's profile has value access (standard/admin tier or canReadValues: true)
  • Scope filtering respects the agent's allowedPaths configuration
  • Read-only operation (no write permissions required)

Errors:

  • access_denied: Entry path is outside the agent's allowed scope
  • not_found: Entry does not exist

generate_password

Generate a cryptographically secure password.

Request:

{
  "tool": "generate_password",
  "arguments": {
    "length": 20,
    "include_symbols": true
  }
}

Parameters:

Parameter Type Required Default Description
length integer No 16 Password length (8-128)
symbols boolean No true Include special characters

Response:

{
  "password": "xK9#mP2$vL7@nQ4!aB8&"
}

generate_totp

Generate a Time-based One-Time Password (TOTP) code from a stored TOTP secret.

Request:

{
  "tool": "generate_totp",
  "arguments": {
    "path": "github"
  }
}

Parameters:

Parameter Type Required Description
path string Yes Entry path containing TOTP secret

Response:

{
  "code": "123456",
  "expires_at": "2026-04-21T10:35:00Z",
  "period": 30
}

Security Note: This tool only returns the generated code, not the underlying TOTP secret. Use redactFields in agent configuration to prevent access to raw secrets while still allowing code generation.


copy_to_clipboard

Copy a vault entry's password field to the system clipboard without exposing the value to the agent. The clipboard auto-clears after 30 seconds.

Request:

{
  "tool": "copy_to_clipboard",
  "arguments": {
    "path": "github"
  }
}

Parameters:

Parameter Type Required Description
path string Yes Entry path whose password field to copy

Response:

{
  "success": true,
  "path": "github",
  "clears_at": "2026-05-05T15:30:00Z"
}

Notes:

  • Requires canUseClipboard: true in agent profile (separate from canWrite)
  • The password value is never exposed in the MCP response
  • Clipboard is automatically cleared after 30 seconds
  • Only copies the password field of the entry

Errors:

  • clipboard_denied: Agent profile has canUseClipboard: false
  • not_found: Entry does not exist

autotype

Type a vault entry's field value as keyboard input into the currently focused application without exposing the value to the agent.

Request:

{
  "tool": "autotype",
  "arguments": {
    "path": "github",
    "field": "password"
  }
}

Parameters:

Parameter Type Required Default Description
path string Yes - Entry path
field string No password Field name to type (e.g., password, username)

Response:

{
  "success": true,
  "path": "github",
  "field": "password"
}

Notes:

  • Requires canUseAutotype: true in agent profile (separate from canWrite)
  • The field value is never exposed in the MCP response
  • Types into the currently focused application window
  • Cross-platform: macOS, Linux (via xdotool), Windows (via AutoIt)
  • Falls back gracefully on unsupported platforms

Errors:

  • autotype_denied: Agent profile has canUseAutotype: false
  • not_found: Entry or field does not exist

secure_input

Prompt the user for sensitive data via an interactive TTY and store it without exposing the value to the agent. Only available in stdio mode with a TTY.

Request:

{
  "tool": "secure_input",
  "arguments": {
    "path": "new-service",
    "field": "password",
    "description": "Enter the password for new-service"
  }
}

Parameters:

Parameter Type Required Description
path string Yes Entry path to store the value
field string Yes Field name to store the value under
description string No Optional description shown to the user in the prompt

Response:

{
  "success": true,
  "path": "new-service",
  "field": "password"
}

Notes:

  • Available whenever any secure-input backend is reachable: an interactive TTY (stdio mode), or a native GUI dialog (macOS osascript, Linux zenity/kdialog, Windows Get-Credential). Set SYMVAULT_SECUREUI=tty|gui|none to override the auto-detected backend.
  • The agent never sees the value being stored
  • Requires canWrite: true in agent profile
  • Triggers automatic git commit (if enabled)

request_credential

Agent-initiated counterpart to secure_input. Use this when, during a task, the agent discovers an expected vault entry is missing. The user gets a native input dialog with the agent's stated reason; the value is stored at the requested path and never returned to the agent.

Request:

{
  "tool": "request_credential",
  "arguments": {
    "path": "github/api-token",
    "field": "token",
    "reason": "Needed to push to main on the symvault repo"
  }
}

Parameters:

Parameter Type Required Description
path string Yes Vault path to store the new credential
field string Yes Field name (e.g. token, password, api_key)
reason string Yes Short reason shown verbatim in the dialog

Response:

{
  "success": true,
  "path": "github/api-token",
  "field": "token"
}

Notes:

  • Same backend rules as secure_input (TTY or native GUI; SYMVAULT_SECUREUI override applies)
  • reason is shown to the user — agents should write it as a clear, human-readable sentence
  • Recommended call site: after find_entries / get_entry returns nothing for an expected path, instead of asking the user for the secret in chat

prepare_payment

Validate a payment entry, show a native approval prompt with merchant/amount/currency details, and on user approval autotype the card or bank account fields into the focused checkout window. Card number, CVC, and IBAN values are never returned in the tool response.

Request:

{
  "tool": "prepare_payment",
  "arguments": {
    "entry_path": "payments/mycard",
    "merchant": "shop.example",
    "amount": "75.00",
    "currency": "EUR"
  }
}

Parameters:

Parameter Type Required Default Description
entry_path string Yes - Vault path of the payment entry
merchant string Yes - Merchant name or origin (e.g. shop.example)
amount string Yes - Payment amount (e.g. 75.00)
currency string Yes - Currency code (e.g. EUR, USD)
description string No - Optional description shown in the approval prompt

Response:

{
  "success": true
}

Security guarantees:

  • Card number, CVC, and IBAN values are never returned in the MCP response
  • The user sees a native approval prompt (e.g. "Allow payment of EUR 75.00 to shop.example?") before any autotyping occurs
  • Approval mode must not be deny; the tool requires user interaction
  • Risk level: Critical — cannot be remembered across sessions
  • Requires canUseAutotype: true in agent profile

Autotype field order:

  • Card entries: card_numberexpiry_monthexpiry_yearcvc
  • Bank account entries: iban

Notes:

  • Entry must have type: "payment" in its secret metadata
  • Payment subtype (card or bank_account) is read from the entry's subtype field or the request subtype argument
  • Same autotype backend as autotype tool (macOS, Linux, Windows)
  • Falls back gracefully on unsupported platforms

Errors:

  • denied: User declined the approval prompt
  • not_a_payment_entry: Entry type is not payment
  • not_found: Entry does not exist
  • outside_allowed_scope: Path is outside the agent's allowed scope
  • payment.policy_denied: Payment policy check failed (merchant not allowed, currency mismatch, or amount limit exceeded)

Payment Policies

Payment policies provide declarative guardrails on prepare_payment requests. When an agent profile references a policy, the enforcer checks merchant allowlists, per-transaction limits, per-day limits, and currency requirements before the native approval prompt is shown.

Configuration

Define policies under paymentPolicies in config.yaml:

paymentPolicies:
  shopping-limited:
    instrument: "payments/visa"                    # vault entry path of the payment instrument
    allowed_merchants:
      - "amazon.de"
      - "otto.de"
      - "mediamarkt.de"
    max_amount:
      per_transaction: "75.00"
      per_day: "150.00"
    currency: "EUR"

Link a policy to an agent profile:

agents:
  hermes:
    allowedPaths: ["*"]
    canWrite: true
    approvalMode: none
    paymentPolicy: "shopping-limited"             # reference the policy by name

Policy Fields

Field Type Required Description
instrument string Yes Vault entry path of the payment instrument (card/bank account)
allowed_merchants array No Allowlist of merchant names (case-insensitive exact match)
max_amount.per_transaction string No Maximum single-transaction amount (decimal string, e.g. "75.00")
max_amount.per_day string No Maximum total per calendar day (decimal string, e.g. "150.00")
currency string Yes when limits set Required ISO-4217 currency code (e.g. "EUR", "USD")

How Enforcement Works

  1. The agent calls prepare_payment with entry_path, merchant, amount, currency.
  2. If the agent has a paymentPolicy, the enforcer checks:
    • Merchant allowlist: if allowed_merchants is non-empty, the merchant must match (case-insensitive).
    • Currency: must match the policy's currency when limits are set.
    • Per-transaction: amount must not exceed per_transaction.
    • Per-day: amount + today's accumulated total must not exceed per_day.
  3. If any check fails, the request is rejected with a payment.policy_denied audit event. The native approval prompt is never shown.
  4. If all checks pass, the native approval prompt is shown as usual. On approval, the daily total is incremented.

Per-Day Persistence

Per-day totals are stored on disk at $XDG_DATA_HOME/symaira-vault/payment-state/<vault-hash>/<policy>/daily-totals.json. Entries older than today are automatically expired on load and save. Totals survive daemon restarts.

Error Responses

Reason Description
merchant_not_allowed Merchant is not in the policy's allowed_merchants list
currency_mismatch Requested currency does not match the policy's required currency
over_per_transaction Amount exceeds the per-transaction limit
over_per_day Amount + today's total would exceed the per-day limit

set_entry_field

Store or update a single field in an entry. Requires write permission.

Request:

{
  "tool": "set_entry_field",
  "arguments": {
    "path": "new-service",
    "field": "password",
    "value": "secure-password-here"
  }
}

Parameters:

Parameter Type Required Description
path string Yes Entry path
field string Yes Field name (e.g., "password", "username", "api_key")
value string Yes Field value

Response:

{
  "success": true,
  "path": "new-service",
  "field": "password",
  "version": 6
}

Notes:

  • Creates entry if it doesn't exist
  • Updates existing field or adds new one
  • Triggers automatic git commit (if enabled)
  • Requires canWrite: true in agent profile

Errors:

  • access_denied: Agent profile has canWrite: false
  • approval_required: Agent profile has approvalMode: prompt (degrades to deny in MCP)

run_command

Execute a command on the host with secrets injected as environment variables.

Request:

{
  "tool": "run_command",
  "arguments": {
    "command": ["curl", "-H", "Authorization: Bearer $API_KEY", "https://api.github.com/user"],
    "env": {
      "API_KEY": "github.api_key"
    },
    "working_dir": "/tmp",
    "timeout": 30
  }
}

Some consumers need a file path, not an environment variable — e.g. a certificate a tool opens by path. files materializes each referenced secret into an ephemeral, 0600, owner-only file for the lifetime of the command, exposed as $SYMVAULT_FILE_<name>; the file is shredded and removed once the command finishes, whether it succeeds, fails, or times out. A plain string value is treated as raw text (same convention as env); use the {"ref": ..., "encoding": "base64"} form to decode base64-encoded binary content (e.g. a PKCS#12 certificate) before it is written to disk:

{
  "tool": "run_command",
  "arguments": {
    "command": ["ericctl", "send", "--cert", "$SYMVAULT_FILE_CERT"],
    "files": {
      "CERT": {"ref": "elster/org-zertifikat.pfx", "encoding": "base64"}
    },
    "env": {
      "ELSTER_PIN": "elster/org-zertifikat.pin"
    }
  }
}

Parameters:

Parameter Type Required Default Description
command array Yes - Command and arguments as strings
env object No {} Map of env var names to secret refs (e.g. {"API_KEY": "github.api_key"})
files object No {} Map of names to secret refs (string) or {"ref", "encoding"} objects, materialized as ephemeral $SYMVAULT_FILE_<name> files
working_dir string No current dir Working directory for the command
timeout number No 30 Timeout in seconds

Response:

{
  "exit_code": 0,
  "stdout": "...",
  "stderr": "",
  "duration_ms": 245
}

Notes:

  • Requires canRunCommands: true in agent profile (separate from canWrite)
  • Each secret ref (env and files) is scope-checked individually
  • Secret values are never exposed in the MCP response or audit logs — audit logs record files refs, never their content
  • Output is capped at 100KB per stream to prevent context bloat
  • Timeout kills the process with exit code -1, and any files are still removed

Errors:

  • run_denied: Agent profile has canRunCommands: false
  • scope_denied: Secret ref path is outside agent's allowed scope
  • approval_required: Agent profile requires approval

delete_entry

Delete a password entry. Requires write permission.

Request:

{
  "tool": "delete_entry",
  "arguments": {
    "path": "old-service"
  }
}

Parameters:

Parameter Type Required Description
path string Yes Entry path to delete

Response:

{
  "success": true,
  "path": "old-service",
  "deleted_at": "2026-04-21T10:30:00Z"
}

Notes:

  • Permanent deletion (no recycle bin)
  • Triggers automatic git commit (if enabled)
  • Requires canWrite: true in agent profile

symvault_delete

Deprecated: Use delete_entry instead. This is a legacy alias maintained for backward compatibility.


MCP Prompts

Symaira Vault advertises the MCP prompts capability. In MCP clients that surface prompts as slash commands (Claude Code, OpenCode, Hermes, …) four guided credential workflows become available once the server is connected. The server implements the standard prompts/list and prompts/get JSON-RPC methods.

prompts/list

Returns all available prompts with their argument schemas.

Request:

{"jsonrpc": "2.0", "id": 1, "method": "prompts/list"}

Response:

{
  "prompts": [
    {
      "name": "add-credential",
      "description": "Guided workflow to add a new credential …",
      "arguments": [
        {"name": "service_name", "description": "", "required": false},
        {"name": "path", "description": "", "required": false}
      ]
    },
    ...
  ]
}

prompts/get

Renders the prompt body for a given prompt and argument map. The MCP client injects the returned messages into the conversation.

Request:

{
  "jsonrpc": "2.0", "id": 2,
  "method": "prompts/get",
  "params": {
    "name": "add-credential",
    "arguments": {"service_name": "GitHub"}
  }
}

Response:

{
  "description": "Guided workflow to add a new credential …",
  "messages": [
    {"role": "user", "content": {"type": "text", "text": "Add a new credential …"}}
  ]
}

Available Prompts

Name Required args Description
add-credential Walks the agent through adding a vault entry. Sensitive fields routed through request_credential. Optional args: service_name, path.
rotate-credential path Generates a new password, stores it, reminds the user to update the remote service. Optional: length (default 32).
find-and-use query Searches the vault and suggests the right consumption tool (copy_to_clipboard, autotype, execute_with_secret). Optional: task.
share-credential path, to_agent Creates a share grant and explains the human-approval flow. Optional: ttl (default 1h), secret_field.

In Claude Code the prompts appear as /mcp__symvault__add-credential (and similar). The displayed argument form is generated automatically from each prompt's argument schema.


Error Handling

Error Response Format

{
  "error": {
    "code": "error_code",
    "message": "Human-readable error description",
    "details": {}
  }
}

Error Codes

Code HTTP Status Description Resolution
not_found 404 Entry or resource not found Verify the path exists with list_entries
access_denied 403 Agent not authorized for this operation Check agent profile in config.yaml
vault_locked 403 Vault is locked Run symvault unlock
invalid_request 400 Malformed request Check JSON syntax and parameters
missing_parameter 400 Required parameter missing Include all required fields
invalid_parameter 400 Parameter value invalid Check parameter constraints
write_denied 403 Agent cannot write Set canWrite: true in profile
run_denied 403 Agent cannot execute commands Set canRunCommands: true in profile
approval_required 403 Operation requires approval approvalMode: prompt degrades to deny in MCP
rate_limited 429 Too many requests Wait and retry
internal_error 500 Server error Check server logs, restart server
not_implemented 501 Tool not available Verify Symaira Vault version

Common Error Scenarios

Vault Locked

{
  "error": {
    "code": "vault_locked",
    "message": "Vault is locked. Please run symvault unlock. first.",
    "details": {}
  }
}

Resolution:

symvault unlock
# Enter passphrase

Access Denied

{
  "error": {
    "code": "access_denied",
    "message": "Agent 'readonly-agent' is not allowed to access path 'work/aws'",
    "details": {
      "agent": "readonly-agent",
      "path": "work/aws",
      "allowed_paths": ["personal/*"]
    }
  }
}

Resolution: Update agent profile allowedPaths to include the path pattern.

Write Denied

{
  "error": {
    "code": "write_denied",
    "message": "Agent 'claude-code' does not have write permission",
    "details": {}
  }
}

Resolution: Set canWrite: true in the agent profile.


Rate Limiting

Symaira Vault implements rate limiting to prevent abuse and ensure fair resource usage.

Limits

Operation Type Limit Window
Read operations 100 60 seconds
Write operations 20 60 seconds
Password generation 50 60 seconds
Health checks Unlimited -

Rate Limit Response

When rate limit is exceeded:

{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded. Retry after 30 seconds.",
    "details": {
      "retry_after": 30
    }
  }
}

Best Practices

  1. Cache credentials: Don't fetch the same entry repeatedly
  2. Use metadata for cache validation: Check version before fetching full entry
  3. Batch operations: Minimize individual tool calls
  4. Handle rate limits gracefully: Implement exponential backoff

Agent Configuration

Agent profiles are defined in the vault configuration file (~/.symvault/config.yaml).

Configuration Location

~/.symvault/config.yaml

Profile Schema

agents:
  <profile-name>:
    allowedPaths: ["*"]           # Path patterns agent can access
    canWrite: false               # Whether agent can create/update/delete entries
    canRunCommands: false         # Whether agent can execute commands with secrets
    canUseClipboard: false        # Whether agent can copy passwords to clipboard
    canUseAutotype: false         # Whether agent can type passwords via autotype
    approvalMode: "none"          # Approval behavior: none | deny | prompt
    redactFields: []              # Fields to redact from responses

Field Descriptions

Field Type Default Description
allowedPaths array ["*"] Path patterns the agent can access. Use * for all paths, or prefixes like ["work/", "personal/"]
canWrite boolean false Whether the agent can modify vault entries
canRunCommands boolean false Whether the agent can execute commands with secret env injection via run_command
canUseClipboard boolean false Whether the agent can copy passwords to clipboard via copy_to_clipboard
canUseAutotype boolean false Whether the agent can type passwords via keyboard input through autotype
approvalMode string "none" Write approval behavior: none (allow), deny (reject), prompt (degrades to deny in MCP)
redactFields array [] Field names to redact from get_entry responses (e.g., ["totp.secret"] shows [REDACTED])
exposePaymentValues boolean false When true, payment entries (type: payment) expose sensitive fields (card_number, cvc, iban). Disabled by default — these fields are always redacted for agents unless this flag is set

Built-in Profiles

Symaira Vault includes several pre-configured profiles:

Profile allowedPaths canWrite canRunCommands canUseClipboard canUseAutotype Use Case
default ["*"] false false false false Read-only access to all entries
claude-code ["*"] true false false false Full vault access for Claude Code
codex ["*"] false false false false Read-only access for Codex
hermes ["*"] true false false false Full vault access for Hermes
openclaw ["*"] true false false false Full vault access for OpenClaw
opencode ["*"] false false false false Read-only access for OpenCode

Custom Profile Example

agents:
  # Read-only agent for production secrets only
  prod-reader:
    allowedPaths: ["production/*"]
    canWrite: false
    approvalMode: "deny"

  # Write agent for development secrets
  dev-writer:
    allowedPaths: ["development/*", "staging/*"]
    canWrite: true
    approvalMode: "none"

  # TOTP-only agent (cannot read TOTP secrets)
  totp-agent:
    allowedPaths: ["*"]
    canWrite: false
    redactFields: ["totp.secret"]

  # Clipboard agent (can copy but not read passwords)
  clipboard-agent:
    allowedPaths: ["*"]
    canWrite: false
    canUseClipboard: true

  # Full automation agent (run commands + autotype)
  automation-agent:
    allowedPaths: ["*"]
    canWrite: false
    canRunCommands: true
    canUseAutotype: true

Path Pattern Syntax

Pattern Matches
* All paths
work/* All entries under work/
work/aws Exact path work/aws
api/* All entries under api/
["work/*", "personal/*"] Both work and personal directories

Field Redaction with redactFields

The redactFields agent profile setting controls which entry fields are hidden from get_entry responses. Redacted fields appear as [REDACTED] instead of their actual values.

Payment Entry Automatic Redaction

Entries with secret_meta.type = "payment" have their sensitive fields automatically redacted in all agent responses, independent of the profile's redactFields configuration:

Sensitive Field Description
card_number Full card number (e.g., 4111111111111111)
cvc Card verification code
iban International bank account number

Non-sensitive payment fields (cardholder, expiry_month, expiry_year, bic, subtype) are always returned.

To opt out and expose raw payment values, set exposePaymentValues: true on the agent profile. This requires the agent to also have canReadValues: true or value-tool access.

What Gets Redacted

Redaction is applied only to get_entry responses. It does not affect:

  • list_entries, find_entries, or get_entry_metadata (these never return field values)
  • generate_totp (reads the secret directly from the vault)
  • generate_password (creates new values)
  • Write operations such as set_entry_field or secure_input

Pattern Syntax

redactFields is an array of field name patterns. Patterns are matched against the fully-qualified field path using dot notation for nested maps.

Pattern Matches Example
"password" Exact top-level field password
"totp.secret" Exact nested field totp.secret
"*" All fields everything
"totp.*" All fields under the totp map totp.secret, totp.issuer, totp.algorithm

TOTP Secret Redaction (Recommended)

The most common use case is preventing agents from reading raw TOTP secrets while still allowing them to generate codes:

agents:
  totp-only:
    allowedPaths: ["*"]
    canWrite: false
    redactFields: ["totp.secret"]

With this profile:

  • get_entry github returns all fields, but totp.secret shows [REDACTED]
  • generate_totp github still works normally and returns the current TOTP code
  • The agent can use TOTP-based authentication without ever seeing the underlying seed

Other Common Examples

agents:
  # Redact all TOTP-related fields
  totp-redacted:
    allowedPaths: ["*"]
    canWrite: false
    redactFields: ["totp.*"]

  # Redact password and TOTP secret
  limited-reader:
    allowedPaths: ["*"]
    canWrite: false
    redactFields: ["password", "totp.secret", "api_key"]

  # Redact everything (metadata-only access)
  metadata-only:
    allowedPaths: ["*"]
    canWrite: false
    redactFields: ["*"]

Response Example with Redaction

Profile:

agents:
  readonly-agent:
    allowedPaths: ["*"]
    canWrite: false
    redactFields: ["totp.secret"]

Request:

{
  "tool": "get_entry",
  "arguments": {
    "path": "github"
  }
}

Response:

{
  "path": "github",
  "data": {
    "password": "mysecretpassword",
    "username": "myuser",
    "url": "https://github.com",
    "totp": {
      "secret": "[REDACTED]",
      "issuer": "GitHub",
      "algorithm": "SHA1"
    }
  }
}

Note that generate_totp github continues to work because it reads the TOTP secret directly from the encrypted vault entry, bypassing the get_entry redaction layer.


Examples

Complete Workflow: Read and Update Entry

// 1. List entries
{
  "tool": "list_entries",
  "arguments": {}
}

// 2. Get entry metadata for cache check
{
  "tool": "get_entry_metadata",
  "arguments": {
    "path": "api/service-key"
  }
}

// 3. Get full entry (if cache miss)
{
  "tool": "get_entry",
  "arguments": {
    "path": "api/service-key",
    "include_metadata": true
  }
}

// 4. Update field (requires write permission)
{
  "tool": "set_entry_field",
  "arguments": {
    "path": "api/service-key",
    "field": "api_key",
    "value": "new-api-key-value"
  }
}

HTTP Mode Complete Example

# Set variables
TOKEN=$(cat ~/.symvault/mcp-token)
AGENT="claude-code"
BASE_URL="http://127.0.0.1:8080"

# Health check
curl -s "$BASE_URL/health" | jq .

# List entries
curl -s -X POST "$BASE_URL/mcp" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Symaira-Agent: $AGENT" \
  -H "Content-Type: application/json" \
  -d '{"tool": "list_entries", "arguments": {}}' | jq .

# Get entry
curl -s -X POST "$BASE_URL/mcp" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Symaira-Agent: $AGENT" \
  -H "Content-Type: application/json" \
  -d '{"tool": "get_entry", "arguments": {"path": "github"}}' | jq .

# Generate password
curl -s -X POST "$BASE_URL/mcp" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Symaira-Agent: $AGENT" \
  -H "Content-Type: application/json" \
  -d '{"tool": "generate_password", "arguments": {"length": 20}}' | jq .

Stdio Mode Complete Example

# Start the server
symvault serve --stdio --agent claude-code

# Send MCP request (via stdin)
echo '{"tool": "list_entries", "arguments": {}}' | symvault serve --stdio --agent claude-code

# Or with a proper MCP client
# The MCP client handles the JSON-RPC framing

Credential Cache Pattern

// Pseudocode for credential caching with Symaira Vault
async function getCredential(path) {
  const cached = cache.get(path);
  
  // Check if cached version is stale
  const metadata = await mcp.call('get_entry_metadata', { path });
  
  if (!cached || cached.version !== metadata.version) {
    // Fetch fresh credential
    const entry = await mcp.call('get_entry', { 
      path, 
      include_metadata: true 
    });
    
    cache.set(path, {
      data: entry.data,
      version: entry.meta.version
    });
    
    return entry.data;
  }
  
  return cached.data;
}

Related Documentation

Security Considerations

  1. Token Security: The mcp-token file contains authentication credentials. Protect it like a password.

  2. Network Binding: HTTP mode binds to 127.0.0.1 by default. Do not expose to public networks without additional security.

  3. Agent Isolation: Use separate agent profiles for different security contexts.

  4. Audit Logging: All MCP operations are logged. Monitor logs for unauthorized access attempts.

  5. Write Permissions: Grant write permissions sparingly. Prefer read-only agents when possible.

  6. Credential Rotation: If an agent may have logged credentials to chat logs or terminals, rotate those credentials immediately.


Changelog

Version Changes
2.5.0 Added search and fetch tools for OpenAI Company Knowledge compatibility; added structuredContent field support in tool responses
2.2.0 Added copy_to_clipboard, autotype, and run_command tools; added canUseClipboard and canUseAutotype agent permissions; added scoped token management
2.0.0 Added run_command tool, canRunCommands permission, and HTTP MCP authentication
1.0.0 Initial MCP API documentation

Maintainer: Symaira Vault Team
Repository: https://github.com/danieljustus/symaira-vault
Security: Report security issues via GitHub Security Advisories