Skip to main content
Glama
KiaTheRandomGuy

PasarGuard MCP

README.md
# PasarGuard MCP

PasarGuard MCP is a local Model Context Protocol server for managing one or
more PasarGuard panels through their REST APIs. It is intentionally limited to
panel operations. It does not install servers, manage Vultr, SSH into nodes,
manage Cloudflare/Fastly, or change provider infrastructure.

The server supports both reading and writing. Writes use a two-step plan/apply
workflow: the MCP reads the current object, creates a short-lived confirmation
token, and only applies the saved change after the user confirms it. Update
plans preserve fields that were not changed, which is important because
PasarGuard core and host updates expect complete valid objects.

## Install

```bash
cd projects/pasarguard-mcp
python3 -m venv .venv
.venv/bin/python -m pip install -e '.[dev]'
```

Python 3.11 or newer is required.

## Configure panels

Static configuration is optional. The MCP can also load a credential file
from a chat-driven tool call; see the next section.

Copy `config/panels.example.json` to a file outside Git, for example
`~/.config/pasarguard-mcp/panels.json`, and set the password in an environment
variable:

```bash
export PASARGUARD_MCP_CONFIG="$HOME/.config/pasarguard-mcp/panels.json"
export PASARGUARD_EXAMPLE_PASSWORD='your-panel-password'
```

The configuration accepts multiple named panels:

```json
{
  "panels": {
    "main": {
      "base_url": "https://panel.example.com",
      "username": "admin",
      "password_env": "PASARGUARD_MAIN_PASSWORD",
      "verify_tls": true,
      "timeout": 30
    },
    "staging": {
      "base_url": "https://staging-panel.example.com",
      "username_env": "PASARGUARD_STAGING_USERNAME",
      "password_env": "PASARGUARD_STAGING_PASSWORD"
    }
  }
}
```

An existing bearer token can be configured with `token_env` instead of
`username`/`password`. Do not commit panel URLs, credentials, tokens, node API
keys, certificates, or exported panel objects to this repository.

The local state directory stores confirmation plans and before-snapshots with
mode `0700`/`0600`. Override it with `PASARGUARD_MCP_STATE_DIR` when needed.

## MCP client configuration

For a stdio MCP client, use the installed executable:

```json
{
  "mcpServers": {
    "pasarguard": {
      "command": "/absolute/path/to/projects/pasarguard-mcp/.venv/bin/pasarguard-mcp",
      "env": {
        "PASARGUARD_MCP_CONFIG": "/absolute/path/to/panels.json",
        "PASARGUARD_MAIN_PASSWORD": "set-this-in-your-local-client-config"
      }
    }
  }
}
```

Prefer the client's secret/environment mechanism where it provides one. The
MCP server never prints passwords or bearer tokens to stdout.

## Chat-based credential files

You can tell Codex where an existing local credential file is instead of
putting it in the MCP setup. Ask it to use the `load_panel_credentials` tool,
for example:

```text
Use the PasarGuard credentials in
/Users/me/workspace/memory/30-clients/example/credentials.local.md for the
panel named example. Keep them session-only and test the connection.
```

The tool reads the file locally and extracts only recognized fields. Supported
formats are `.env`, `.md`, and `.markdown`; common keys include `PANEL_URL`,
`PASARGUARD_BASE_URL`, `username`, `password`, and `token`. The password is
never returned by the tool and is kept only in the MCP process by default.

If a Markdown file contains multiple named accounts, select the intended
credential block explicitly. For example, a file with a
`flowship_super_admin` block should be loaded with
`credential_name=flowship_super_admin`. The parser strips one matching pair of
Markdown backticks or quote characters around values. This matters for entries
such as `Password: \`...\``; sending the backticks as part of the password
causes PasarGuard to return HTTP 401. When multiple complete credential blocks
exist, omitting `credential_name` now fails safely instead of merging the
username from one account with the password or URL from another.

The tool accepts `persist=true` only when you explicitly want the profile
written to the protected local JSON config. The default is `persist=false`.
Use absolute local paths and keep credential files outside Git repositories.

### URL, credential, filtering, and output normalization

- Dashboard URLs are normalized to the PasarGuard site root. For example,
  `https://flowshipnet.com/dashboard/` becomes `https://flowshipnet.com`
  before API paths are appended.
- Markdown `##` through `######` headings are selectable credential blocks in
  addition to named list blocks. This supports files where a section such as
  `## PasarGuard Panel (production)` directly contains URL, username, and
  password fields.
- List query keys that correspond to returned item fields are also enforced
  locally with exact matching. This protects callers when a panel silently
  ignores a filter such as `inbound_tag`. Pagination/control keys that are not
  item fields remain upstream-only.
- PasarGuard's `/api/hosts` endpoint returns a bare top-level JSON list, unlike
  the wrapped node/core/group/user endpoints. The MCP normalizes bare lists to
  an object keyed by the resource, such as `{"hosts": [...], "total": N}`.
  FastMCP otherwise emits one text content block per list item, which makes a
  large host inventory easy for downstream tool renderers to split or
  truncate. The normalized object produces one coherent structured block.
- Apply responses are compact receipts. Full updated core objects are not
  returned, and recursive redaction covers Reality `privateKey`/`shortIds` in
  read results. Verification should use the read tools after apply.

## Tool groups

Read operations:

- `list_panels`, `load_panel_credentials`, `test_panel`
- `get_panel_inventory`
- `list_panel_resource`, `get_panel_resource`
- `get_node_realtime_stats`, `get_panel_system_status`

Write planning operations:

- Nodes: create, update, delete, reconnect, sync, reset usage, update core
- Cores: create and update, with explicit optional node restart on updates
- Hosts: create and update
- Groups: create and update
- Users: create and update by numeric ID
- All supported resources can be planned for deletion

Write execution and recovery:

- `apply_change` applies one still-valid confirmation token.
- `inspect_change` shows a safe summary without the stored payload.
- `plan_rollback` creates a new confirmation plan from an applied update's
  before-snapshot.

The normal sequence is:

1. Inspect the panel or resource.
2. Call the relevant `plan_*` tool.
3. Show the returned summary and ask the user for confirmation.
4. Call `apply_change` with the returned token.
5. Verify the resulting resource and node status.

## API compatibility

The implementation follows the current public PasarGuard panel API layout,
including `POST /api/admin/token`, `/api/nodes`, `/api/cores`, `/api/hosts`,
`/api/groups`, `/api/users`, node action endpoints, and ID-based user routes
under `/api/user/by-id/{user_id}`. The panel API can evolve, so endpoint
changes should be updated in `src/pasarguard_mcp/server.py` and covered by
tests before release.

## Development

```bash
.venv/bin/python -m pytest
.venv/bin/python -m compileall -q src tests
.venv/bin/ruff check src tests
```

Tests use an in-memory HTTP transport and never contact a real panel.

### Scoped direct-API fallback

When the hosted MCP approval gateway is unavailable, the repository includes
`scripts/apply_reality_expansion.py`. It uses the same `PasarGuardClient`,
loads session credentials from the Lexo, Flowship, and VSL247 credential files,
checks every requested port/tag for collisions, saves each core once with
`restart_nodes=false` so its inbound tags exist, updates the groups, then
saves each core once with `restart_nodes=true`. Without `--apply` it only
reads and validates the live state; `--verify-only` reads back the current
core/group/node state; `--restart-cores panel:id,...` performs targeted
restart-enabled saves when a previous request timed out. It never creates
hosts.

The hosted connector and local elevated execution were both blocked by an
external HTTP 403 approval-gateway response on 2026-08-18; until that gate is
available, do not report the planned ports as live.

For a targeted Microsoft REALITY target/SNI repair across the same four panel
cores, use `scripts/repair_reality_sni.py`. It validates the exact inbound tag,
port, transport, and old/new SNI before changing only `target` and
`serverNames`, then sends one complete-core `PUT` with `restart_nodes=true` per
target and verifies the read-back. Run without `--apply` for a dry-run,
`--verify-only` for current state, or `--apply` for the authorized repair.

## Security boundary

This is an operations tool with write access. Run it locally over stdio, use a
least-privileged PasarGuard administrator when possible, keep TLS verification
enabled, and review every plan before applying it. Credential-file loading
extracts only known fields and does not return passwords. The MCP deliberately
does not provide a generic arbitrary-URL request tool.

TDQS

B3/5.0

Scored across 22 tools

Disambiguation4/5

Most tools target distinct resource-action pairs (e.g., plan_create_node vs. plan_update_core), and read tools are clearly separated from planning tools. Some ambiguity exists between plan_node_action and plan_update_core since plan_node_action can also update a core, and plan_delete_resource is generic, but descriptions mitigate this.

Naming Consistency4/5

The naming follows a consistent verb_noun pattern (e.g., plan_create_node, get_panel_inventory, apply_change). Minor inconsistencies include 'list_panel_resource' (singular generic) and 'plan_rollback' not matching the plan_create/update pattern, but overall the convention is predictable.

Tool Count3/5

With 22 tools, the server is on the heavier side, which can be overwhelming but is justified by the breadth of PasarGuard management. The count is within the 'heavy' range (16-25) rather than extreme, so it earns a middle score.

Completeness4/5

The tool surface covers create, read, update, delete, planning, applying, rollback, and monitoring for core PasarGuard resources. Minor gaps exist, such as no explicit tool to list planned changes or cancel a plan, but these are workarounds via inspect_change and apply_change.

Maintenance

ActivityMaintained
ResponsivenessNo issues