psamvault-mcp
by psam-717
README.md
# psamvault-mcp
**v0.4.4** — MCP server for [psamvault](https://pypi.org/project/psamvault/).
Lets AI agents use your stored credentials without ever seeing their plaintext values. Also integrates with [pv-dotenv](https://pypi.org/project/pv-dotenv/) for runtime credential resolution in your `.env` files.
> **Agents installing or repairing this MCP:** read
> [docs/troubleshooting/MCP-INSTALL-AND-CONNECT.md](docs/troubleshooting/MCP-INSTALL-AND-CONNECT.md)
> before debugging. Common failures are PATH shadowing, corrupt pipx installs,
> missing absolute paths in client config, expired vault sessions, and
> mid-session MCP not reloading until restart.
## Features
Tools are grouped into three categories. Always start in **Entry & Orientation**.
### 🛠 Entry & Orientation
*Discover what tools are available and verify the server is running.*
| Tool | What it does |
|------|-------------|
| **`search_vault_tools`** | Discovery tool — call this first to find the right tool for your task |
| **`get_version`** | Get the installed server version |
### 🔐 Site Authentication
*End-to-end: discover, check, and log into websites.*
| Tool | What it does |
|------|-------------|
| **`list_vault_sites`** | List all stored credential sites (names and username hints only) |
| **`check_credential_exists`** | Check if a credential exists for a site |
| **`get_username_for_site`** | Get stored username (never the password) |
| **`browser_login`** | Opens Chromium, navigates to any site, fills credentials directly in the browser — agent never sees them |
### 🔑 API Key Operations
*All tools that deal with API keys — discover, use, inject, and protect.*
| Tool | What it does |
|------|-------------|
| **`list_api_keys`** | Lists all stored API key names with service hints and project grouping — never returns key values |
| **`use_credential`** | Makes authenticated HTTP requests for you (API keys, bearer tokens, basic auth) — only the HTTP response is returned |
| **`run_with_credential`** | Runs a CLI command with a credential injected via environment variable or stdin — all output redacted of the secret value |
| **`scan_and_protect`** | Scans a project directory for `.env` files, encrypts secrets into psamvault, replaces plaintext with `psamvault:KEY` placeholders |
| **`export_key_to_mcp_config`** | Exports a vault API key directly into a client MCP config file (Hermes, Claude, etc.) — auto-verifies HTTP keys before writing |
| **`export_key_to_env_file`** | Exports a vault API key into an agent's `.env` as an environment variable (default `HERMES_HOME/.env`) — updates in place, backs up, auto-verifies |
| **`verify_api_key`** | Verifies a stored API key is valid against its provider's API — returns status, provider, and verification result |
> **New in v0.4.6:** `export_key_to_mcp_config`, `verify_api_key`, and the auto-verifying export gate (verified-before-write for HTTP keys).
> **Breaking in v0.5.0:** `capture_stripe_credentials` was REMOVED (the Stripe Projects
> capture flow is gone). Use `scan_and_protect` for project `.env` secrets.
>
> **New in v0.5.0:** `export_key_to_env_file` — put a vault key into an agent `.env` as an environment
> variable (default `HERMES_HOME/.env`, explicit `env_path` for other hosts), updating in place with a
> timestamped backup.
>
> **New in v0.4.0:** `use_credential`, `run_with_credential`, `scan_and_protect`, `list_api_keys`, single-process browser architecture (no fragile subprocess daemon), auto-restart on crash.
## How it works
### Browser login flow (`browser_login`)
When an AI agent needs to log you into a website, psamvault opens a real
Chromium browser, navigates to the site, and fills in the credentials directly
inside that browser process.
**The agent never sees the credentials.** It only sees whether the login succeeded.
```
Agent: "Log me into kaggle.com"
↓
psamvault opens Chromium → navigates to kaggle.com → finds the login page
↓
psamvault decrypts credential locally
↓
psamvault fills username + password fields directly in the browser
↓
If a CAPTCHA appears, psamvault takes a screenshot, pauses automation,
and tells you to solve the CAPTCHA and click Sign in manually
↓
Agent receives:
{
"success": true,
"message": "Logged in to github.com successfully.",
"steps_count": 8,
"url": "https://github.com/dashboard",
"captcha_detected": false
}
↓
Browser stays open — you take over from there.
The browser session is saved and reused on subsequent calls to the same site.
```
### API credential flow (`use_credential`)
When an AI agent needs to make an authenticated API call on your behalf:
```
Agent: "Get my top 10 starred repos"
↓
use_credential("github.com", target_url="api.github.com/users/psam-717/starred")
↓
psamvault decrypts the API key locally, makes the HTTP request,
returns only the response — the credential is NEVER in the agent's context
```
Supports three injection modes:
- **Bearer token** — `Authorization: Bearer ***`
- **API key header** — `<custom-header>: <key>`
- **Basic auth** — `Authorization: Basic base64(user:pass)`
The `fields` parameter lets you return only the response keys you need, reducing token usage.
### CLI command flow (`run_with_credential`)
When an agent needs to run a CLI tool that requires a credential (upload to PyPI, push to a private git repo, log into Docker, publish an npm package):
```
Agent: "Upload my package to PyPI"
↓
run_with_credential("pypi", "twine upload dist/*",
inject_as="env", env_var_name="TWINE_PASSWORD")
↓
psamvault decrypts the credential locally, spawns the subprocess
with the credential injected as an env var (or piped via stdin)
↓
All stdout and stderr is scanned for the credential value
and redacted before being returned
↓
Agent receives only the redacted output — the credential NEVER
appears in the agent's context
```
Supports two injection modes:
- **`env`** (default) — credential set as an environment variable (e.g. `TWINE_PASSWORD`, `GITHUB_TOKEN`, `NPM_TOKEN`). When `TWINE_PASSWORD` is used, `TWINE_USERNAME=__token__` is set automatically.
- **`stdin`** — credential piped via stdin (e.g. for `docker login`).
Use cases include: `twine upload`, `git push`, `docker login`, `npm publish`, `pip install` (private repos), and any CLI tool that needs an API key or password.
### Protecting your `.env` files (`scan_and_protect`)
```
Agent: "Protect the secrets in my project"
↓
scan_and_protect scans the project directory for .env files
↓
Detects API keys, passwords, tokens (pattern matching)
↓
Encrypts each secret into the psamvault vault
↓
Replaces plaintext with "psamvault:KEY_NAME" placeholders
↓
Your app resolves them at runtime with pv-dotenv
```
Secrets can be stored under a project namespace by passing `project_name`:
- Keys stored as `project_name/.env/KEY_NAME` for clean per-project organisation
- When omitted, keys are stored as `env/.env/KEY_NAME` (backwards-compatible)
- Use `list_api_keys(project_name="myproject")` to view only that project's keys
After protecting, pair with [pv-dotenv](https://pypi.org/project/pv-dotenv/) — a drop-in replacement for `python-dotenv` that resolves `psamvault:` placeholders at runtime. No code changes needed beyond the import:
```python
# Before:
from dotenv import load_dotenv
# After:
from pv_dotenv import load_dotenv
```
## Prerequisites
- Python ≥ 3.11
- [psamvault](https://pypi.org/project/psamvault/) installed and logged in
```bash
pipx install psamvault
psamvault configure
psamvault login
```
- Playwright Chromium browser
```bash
playwright install chromium
```
## Installation
**Prefer pipx** (isolated venv). Avoid `pip install` into system Python — on
Windows this often leaves a broken shim on PATH that shadows the good install.
```bash
pipx install psamvault-mcp
psamvault-mcp --version # safe smoke test (prints and exits)
psamvault-mcp --help # same — usage text, then exit
```
### After install — resolve the real binary
If `where psamvault-mcp` / `which -a psamvault-mcp` shows **more than one**
path, configure your MCP client with the **absolute path** under your user
local bin (pipx), not the system `Python3xx\Scripts` copy:
| OS | Typical pipx path |
|----|-------------------|
| Windows | `%USERPROFILE%\.local\bin\psamvault-mcp.exe` |
| Linux / macOS | `~/.local/bin/psamvault-mcp` |
### Hardened client config (recommended)
Always pass an absolute `command` and clear `PYTHONPATH` so other tools
(e.g. Hermes) cannot contaminate imports:
```json
{
"mcpServers": {
"psamvault": {
"command": "C:\\Users\\YOU\\.local\\bin\\psamvault-mcp.exe",
"args": [],
"env": { "PYTHONPATH": "" }
}
}
}
```
**Grok Build** (`~/.grok/config.toml`):
```toml
[mcp_servers.psamvault]
command = "C:\\Users\\YOU\\.local\\bin\\psamvault-mcp.exe"
args = []
enabled = true
tool_timeout_sec = 300
[mcp_servers.psamvault.env]
PYTHONPATH = ""
```
Then **restart the agent session** (or refresh MCP). Config edits do not always
reload tools mid-chat. Verify with `get_version`, then `list_vault_sites`.
Full agent playbook (corrupt pipx, PATH shadowing, session timeout, reload):
[docs/troubleshooting/MCP-INSTALL-AND-CONNECT.md](docs/troubleshooting/MCP-INSTALL-AND-CONNECT.md).
## Transport modes
psamvault-mcp primarily uses **stdio transport** (the MCP standard for desktop agents). HTTP/SSE transport is also available as an option.
### stdio (default — for Hermes, Goose, Claude Desktop, Cline, Grok Build)
```bash
psamvault-mcp
```
Starts the MCP server over stdin/stdout. This is the default mode and works with all major MCP desktop clients.
> **Note for agents:** a bare `psamvault-mcp` invocation **waits on stdin** for
> the MCP protocol. That is not a hang — use `--version` / `--help` for smoke
> tests, and let the host spawn the process for real use.
### HTTP/SSE (for custom clients, remote setups, or network-accessible deployments)
```bash
psamvault-mcp --http --port 8433
```
Starts an HTTP server with Server-Sent Events (SSE) transport.
| Option | Default | Description |
|--------|---------|-------------|
| `--http` | off | Enable HTTP/SSE transport |
| `--port` | `8433` | HTTP server port |
| `--host` | `127.0.0.1` | HTTP server bind address |
### Goose setup
#### Option A — One-click deeplink
Click or paste this URL into your browser while Goose Desktop is running:
```
goose://extension?cmd=psamvault-mcp&timeout=300&id=psamvault&name=psamVault&description=Use%20stored%20credentials%20without%20exposing%20them%20to%20the%20agent
```
Goose will prompt you to confirm, then the extension is added instantly.
#### Option B — Goose Desktop UI
1. Open Goose Desktop.
2. Click the **sidebar button** (top-left) → **Extensions**.
3. Click **Add custom extension**.
4. Fill in the form:
| Field | Value |
|---|---|
| **Type** | `Standard IO` |
| **ID** | `psamvault` |
| **Name** | `psamVault` |
| **Description** | `Use stored credentials without exposing them to the agent` |
| **Command** | `psamvault-mcp` |
| **Timeout** | `300` |
5. Click **Add**.
The extension appears in your Extensions list — toggle it on to activate it.
#### Option C — Config file (advanced)
Edit `~/.config/goose/config.yaml` and add the following under `extensions:`:
```yaml
extensions:
psamvault:
name: psamVault
cmd: psamvault-mcp
args: []
enabled: true
type: stdio
timeout: 300
```
Save the file and restart Goose (or reload the session).
#### Verifying the extension works
Once added, start a Goose session and try:
```
What credentials do I have stored in my vault?
```
Goose will call `list_vault_sites` via psamvault-mcp. If you see your stored sites, everything is working.
### Hermes setup
Connect to psamvault-mcp via **stdio transport** (default). Add this block to
`~/.hermes/config.yaml` under `mcp_servers`:
```yaml
mcp_servers:
psamvault:
command: psamvault-mcp
enabled: true
```
If you need HTTP/SSE transport instead (e.g. for remote access), start the server with `--http` and point Hermes at the SSE endpoint:
```yaml
mcp_servers:
psamvault:
url: "http://127.0.0.1:8433/sse"
enabled: true
```
```bash
psamvault-mcp --http --port 8433
```
Restart or reload Hermes — the tools will be discovered automatically.
### Claude Desktop setup
Config file location:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\\Claude\\claude_desktop_config.json`
```json
{
"mcpServers": {
"psamvault": {
"command": "psamvault-mcp"
}
}
}
```
Restart Claude Desktop after saving.
### Grok Build setup
1. Install with pipx (see [Installation](#installation)).
2. Add the server to `~/.grok/config.toml` using the **absolute** pipx path
(example above), or to `~/.mcp.json` if you already use that file.
3. Restart Grok or open `/mcps` and refresh so tools load into the session.
4. Confirm: `grok mcp doctor psamvault` → handshake OK, tools discovered.
5. Ensure the vault CLI session is active: `psamvault login`.
### Other MCP clients
Any MCP client supporting stdio transport can use psamvault-mcp.
**Prefer the absolute pipx path** over a bare command name:
```json
{
"mcpServers": {
"psamvault": {
"command": "/home/YOU/.local/bin/psamvault-mcp",
"env": { "PYTHONPATH": "" }
}
}
}
```
For HTTP/SSE support, point the client at `http://127.0.0.1:8433/sse`.
## Troubleshooting
| Symptom | Guide |
|---------|--------|
| MCP will not start / agent cannot connect | [docs/troubleshooting/MCP-INSTALL-AND-CONNECT.md](docs/troubleshooting/MCP-INSTALL-AND-CONNECT.md) |
| `pydantic` / `pydantic_core` import errors with Hermes | [docs/troubleshooting/PYTHONPATH-CONFLICT.md](docs/troubleshooting/PYTHONPATH-CONFLICT.md) |
| Index of all troubleshooting docs | [docs/troubleshooting/README.md](docs/troubleshooting/README.md) |
Quick recovery for a broken Windows pipx install:
```powershell
pipx uninstall psamvault-mcp
Remove-Item -Recurse -Force "$env:USERPROFILE\pipx\venvs\psamvault-mcp" -ErrorAction SilentlyContinue
pipx install psamvault-mcp
# Point MCP config at: $env:USERPROFILE\.local\bin\psamvault-mcp.exe
```
## Configuration
psamvault-mcp reads its backend URL from `~/.psamvault/config.env`, written
automatically by `psamvault configure`.
| Variable | Default | Description |
|---|---|---|
| `PSAMVAULT_API_URL` | `https://psam-vault-backend.onrender.com` | psamvault backend endpoint |
| `PSAMVAULT_LOG_LEVEL` | `INFO` | Log verbosity. Accepts any standard Python level: `DEBUG`, `INFO`, `WARNING`, `ERROR`. Logs go to stderr. |
To point at a self-hosted backend, set the variable in `~/.psamvault/config.env`:
```
PSAMVAULT_API_URL=https://your-backend.example.com
```
## Available tools
Tools are grouped by purpose so AI agents can find the right tool faster:
### 🛠 Entry & Orientation
*Always start here to discover what tool to use.*
| Tool | Description |
|---|---|
| `search_vault_tools` | Discovery tool — call this first to find the right tool for your task |
| `get_version` | Return the installed psamvault-mcp version |
### 🔐 Site Authentication
*End-to-end: discover, check, and log into websites.*
| Tool | Description |
|---|---|
| `list_vault_sites` | List stored site names with username hints (no passwords). Call before `browser_login` |
| `check_credential_exists` | Check if a credential exists for a site. Returns username hint |
| `get_username_for_site` | Get the username only (not password) for a site |
| `browser_login` | Open a real browser and log into a website — credentials filled silently, never shown to the agent |
### 🔑 API Key Operations
*All tools that deal with API keys — discover, use, inject, and protect.*
| Tool | Description |
|---|---|
| `list_api_keys` | List stored API key names with service hints and project grouping (never key values). Optional `project_name` filter |
| `use_credential` | Make authenticated HTTP requests using stored API keys or site passwords — only the HTTP response is returned |
| `run_with_credential` | Run a CLI command with a credential injected via env var or stdin — all output redacted of the secret |
| `scan_and_protect` | Scan a project for `.env` secrets, encrypt them into psamvault, replace with placeholders. Supports `project_name` for per-project namespacing |
| `export_key_to_mcp_config` | Export a vault API key into a client MCP config file (Hermes, Claude, etc.) — auto-verifies HTTP keys before write, with `skip_verify` / `verify_url` overrides |
| `export_key_to_env_file` | Export a vault API key into an agent `.env` as a variable (`agent="hermes"` → `HERMES_HOME/.env`, or explicit `env_path`) — updates in place, timestamped backup, auto-verifies HTTP keys |
| `verify_api_key` | Verify a stored API key is valid against its provider's API. Returns `success`, `verification`, `provider`, `status`, `detail` |
> **Key verification is mandatory before an export write.** HTTP keys are probed against the
> provider's read-only endpoint, and a failed probe blocks the write —
> `skip_verify=true` cannot override it (it covers only providers that cannot be probed at all, and
> the result then records `verification: skipped`). Invalid keys never reach a config or `.env`.
## Changelog and unreleased tracking
Two files, one rule: **nothing merged to `main` should have to be rediscovered at release time.**
| File | Holds | Who writes it |
|---|---|---|
| [`CHANGELOG.unreleased.md`](CHANGELOG.unreleased.md) | merges that are on `main` but **not yet on PyPI** — the answer to "what is pending for the next release?" | whoever merges the change (usually via a follow-up PR) |
| [`CHANGELOG.md`](CHANGELOG.md) | released history, newest first | rolled over **at release time** from the unreleased file |
At release time the unreleased entries are used twice: pasted into the **GitHub release notes** and
prepended to `CHANGELOG.md` under the new version heading; the unreleased file is then reset to its
header. `scripts/docs-sync-check.py` fails the release when `CHANGELOG.md`'s newest section is not the
release the contract calls newest, so a release that forgets the roll-over cannot ship quietly.
## Version lockstep (MCP ↔ skill)
The server and its [usage skill](https://github.com/psam-717/private-skills) are a **pinned pair**, and
the pairing ships *inside the wheel* as `mcp_server/compatibility.json` (each MCP release → the skill
version that documents it, plus the expected tool fingerprint).
```bash
psamvault-compat --check # exit 0 in sync, 1 drift, 2 refused (breaking), 3 install failed / below-floor skill
psamvault-compat --check --json # machine-readable
psamvault-compat --apply # install the target release and bring the skill up to date
psamvault-compat --apply --allow-breaking # only after approving a release that REMOVES a tool
psamvault-compat --sync-skill # skill-only update: install the clone's newest skill, MCP untouched
psamvault-compat --apply --from-git # install the local repo (merged but not yet released)
psamvault-compat --apply --from-git --pull # ...after stashing local changes and pulling origin/main
```
**The recorded skill version is a floor, not a pin.** Each release says the *minimum* skill version that
documents it, and any skill at or above that floor is healthy:
| State | Meaning |
|---|---|
| skill ≥ floor | ✅ fine — the skill may legitimately move **ahead** of the MCP |
| skill < floor | drift — the skill is older than the server it documents; `--sync-skill` repairs it |
| skill missing | drift (same remedy) |
That is what makes a **skill-only update** possible: improving the description of an existing tool is a
skill change with no MCP release behind it. Edit the skill in the clone, then:
```bash
psamvault-compat --sync-skill # installs the clone's working-tree skill, if it meets the floor
```
`--sync-skill` reads the clone **as-is** (mirrors `--from-git`: uncommitted skill work is installable),
snapshots the current skill first, and applies two guards so the skill can never move backwards
silently:
| Guard | Behaviour |
|---|---|
| floor | refuses when the clone's skill is *below* the floor the installed server requires — and writes nothing |
| no downgrade | refuses when the clone's skill is *older* than the skill already installed (a clone parked on an older branch holds an older skill); the message names the clone, its branch and both versions. `--allow-downgrade` overrides |
`--check` also reports `skill_source` / `skill_source_stale` — when the clone is behind the installed
skill it says so, because a silent downgrade opportunity is exactly what nobody notices.
A contract entry exists the moment a release is **merged**, before it ships. The index pre-check is
deliberately **advisory**: PyPI's JSON API lags an upload (CDN cache), so it never blocks an install
that would succeed — it only explains a real failure (exit 3). Use `--from-git` for the
merged-but-unreleased case.
### Upgrade safety (`--apply`)
Same model as the psamvault CLI's upgrade path: **local work is never lost, and a failed upgrade is
never left installed.**
| Step | What it does |
|---|---|
| Snapshot | copies the installed skill aside (`backups/backup-<stamp>/`, keeps the newest 5) before overwriting it |
| `--pull` | stashes uncommitted work (untracked included), `git pull --ff-only origin main`, then restores it. A failed pull restores immediately and stops; a *conflicting* restore leaves the work parked in a labelled stash and prints the recovery commands |
| Repo report | `--from-git` prints branch, HEAD, dirtiness and position vs `origin/main` (fetched, or marked "as of the last fetch"), and warns when the venv is an editable/source install that a released wheel would detach |
| Smoke test | imports the freshly installed server in a **fresh** interpreter from a neutral cwd — so the repo tree cannot masquerade as the install — and reports its version + tool count |
| Rollback | if the install fails or the smoke test fails, the previously installed release is put back automatically |
- **The installed server wins** — the skill is pulled to match it, never the reverse, and never rolled
back: an older clone skill is refused (see the guards above).
- A release marked **breaking** is never applied without `--allow-breaking`: a silently disappearing
tool is exactly the change a human should see.
- `get_version` reports the pairing (`compatibility.paired_skill_version`, newest release,
`breaking_pending`), so an agent can self-check without extra tooling.
- A tool count alone is **not** a version check: v0.5.0 removed one tool and added another, leaving
the count at 13 — only the fingerprint reveals that.
- `tests/test_compat.py` fails when the newest contract entry disagrees with the code's actual tool
surface, so a release that forgets to record itself cannot ship quietly.
- `scripts/docs-sync-check.py` is the release-time **docs gate**: it fails when any doc still names a
tool the code dropped, claims a stale tool count, omits a tool, or disagrees with the contract.
Run it first in every release — a tool count alone cannot reveal a renamed or removed tool.
## Architecture
The MCP server manages a single Playwright Chromium instance in-process.
No subprocess daemon is used — the browser lives in the same process as the
MCP server. If the browser crashes, it is automatically restarted on the
next `browser_login` call.
This eliminates the fragile 3-process chain (MCP → CLI daemon → browser)
that caused connection errors with certain MCP clients (e.g. Goose's
`ECONNREFUSED` on internal proxy ports).
## Example agent prompts
Once connected, you can ask your agent things like:
- *"What credentials do I have stored in my vault?"*
- *"What API keys do I have stored?"*
- *"Log me into kaggle.com"*
- *"Open github.com and log me in"*
- *"Check if I have a credential stored for z.ai"*
- *"Get my top 10 starred repos from GitHub"*
- *"Upload my package to PyPI"*
- *"Push to my private repo"*
- *"Protect the secrets in my project directory"*
## Related projects
| Package | What It Does |
|---------|-------------|
| [`pv-dotenv`](https://pypi.org/project/pv-dotenv/) | Drop-in replacement for `python-dotenv` — resolves `psamvault:` placeholders at runtime |
| [`psamvault-cli`](https://github.com/psam-717/psamvault-cli) | CLI + vault management — store, list, and manage credentials |
## Testing
```bash
# From the repo root
pytest
```
Tests live in `tests/` and cover crypto primitives, session management, consent
logic, the API client (with httpx mocking), and MCP tool behaviour. The test suite
requires no real network access or OS keychain — all external dependencies are mocked.
## Security
- Credentials are decrypted locally on your machine — never sent to the agent
- The agent only receives HTTP responses or redacted CLI output, never credential values
- All communication with the psamvault backend uses HTTPS
- The browser is managed in-process — no subprocess daemon or internal HTTP proxy
- CLI command output is scanned for the credential value and redacted before returning to the agent
## License
MIT — see [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues