{
  "markdown": "# TractorBeeam365 MCP\n\nA Model Context Protocol server for **Veeam Backup for Microsoft 365 (VB365)**.\nIt talks **directly** to the VB365 REST API so an MCP client (Claude Code / Claude\nDesktop) can **review your backup configuration, find protection gaps, spot\ntrends, verify cloud immutability — and, when you explicitly opt in, run jobs and\norchestrate restores.**\n\n> **Independent project — not affiliated with, endorsed by, or supported by Veeam\n> Software.** \"Veeam\" is a registered trademark of Veeam Software; the \"Beeam\"\n> spelling is a deliberate, distinct play on words. Use at your own risk.\n>\n> 🔱 The name: a *tractor beam* pulls your data back in — and *Beeam* winks at the\n> backup product underneath. Read-only review is the safe default; the \"pull\"\n> (restore/actions) is opt-in.\n\n## Two tiers\n\n| Tier | Default | What it can do |\n|------|---------|----------------|\n| **Read-only review** | ✅ always on | GET-only. Cannot change anything. Config/health, gap-analysis, trends, cloud-lock cross-check. |\n| **Action / Restore** | ⛔ off | Opt-in via `TB_*` env flags. Run/stop jobs, generate reports, sync orgs, **define + execute restores**. Every write is confirm-token gated and audited. |\n\nIf you never set the `TB_*` flags, this is a pure read-only reviewer that\n*physically cannot* modify your environment — the action/restore tools aren't even\nregistered.\n\n## Quick deploy (prebuilt image)\n\nNo clone or build required. From v2.2.0 the image is published **multi-arch\n(amd64 + arm64)** to GitHub Container Registry and Docker Hub, so it runs\nnatively on x86 servers, Apple Silicon, and ARM boxes (Raspberry Pi, ARM cloud).\nOn an ARM host, pin the `:2.2.0` (or later) tag until a multi-arch `:latest` has\nbeen published.\n\n### Persistent HTTP service (Docker Compose)\n\nGrab two files and start it:\n\n```bash\ncurl -fsSLO https://raw.githubusercontent.com/Ringosystems/TractorBeeam-MCP/main/docker-compose.deploy.yml\ncurl -fsSL  https://raw.githubusercontent.com/Ringosystems/TractorBeeam-MCP/main/.env.example -o .env\n# Edit .env: set VB365_HOST / VB365_USERNAME / VB365_PASSWORD and a long MCP_AUTH_TOKEN.\ndocker compose -f docker-compose.deploy.yml up -d\n```\n\nMCP is then live at `http://<host>:8766/mcp`. Attach a client with the bearer\ntoken you set:\n\n```bash\nclaude mcp add --transport http tractorbeeam365 http://<host>:8766/mcp \\\n  --header \"Authorization: Bearer <your MCP_AUTH_TOKEN>\"\n```\n\nUpdate later:\n\n```bash\ndocker compose -f docker-compose.deploy.yml pull && docker compose -f docker-compose.deploy.yml up -d\n```\n\n### Unraid (one click)\n\nIn Unraid open **Docker → Add Container**, and paste this template URL into the\n**Template** field:\n\n```text\nhttps://raw.githubusercontent.com/Ringosystems/TractorBeeam-MCP/main/deploy/unraid/tractorbeeam365.xml\n```\n\nFill in `VB365_HOST`, `VB365_USERNAME`, `VB365_PASSWORD`, and a long\n`MCP_AUTH_TOKEN`, then Apply. The audit and downloads folders map under\n`/mnt/user/appdata/tractorbeeam365/`.\n\n### Single MCP client, no service (stdio)\n\nFor a client that launches the server itself, run the image on demand over stdio:\n\n```bash\ndocker run -i --rm \\\n  -e VB365_HOST=... -e VB365_USERNAME=... -e VB365_PASSWORD=... \\\n  ghcr.io/ringosystems/tractorbeeam-mcp:latest\n```\n\n## Read-only tools (always available)\n\n| Tool | What it returns |\n|------|-----------------|\n| `vb365_health_summary` | One-shot review: orgs, jobs + last status, repos, **cloud storage (B2/S3/Azure)**, proxies, license, **trend anomalies**, and a `flags` list. **Start here.** |\n| `vb365_trends` | Per-job success/failure **drift**, repo **capacity-full projection**, **restore-test cadence** per org, **RPO compliance**. |\n| `gap_coverage_summary` / `gap_unprotected_objects` | **Backup coverage %** and **unprotected** live M365 objects, by comparing Microsoft Graph against what jobs protect *(needs `GRAPH_*`)*. |\n| `cloud_lock_audit` | Cross-checks **Object Lock** mode/retention/versioning at the **bucket** vs what VB365 reports *(needs `B2_*`/`S3_*`)*. |\n| `vb365_api_root` | Resource list + version (confirms connectivity). |\n| `vb365_list_organizations` / `vb365_org_inventory` | Protected orgs; an org's users/groups/sites/teams. |\n| `vb365_list_jobs` / `vb365_get_job` / `vb365_job_scope` | Jobs, one job's config, and exactly what a job protects (selected/excluded). |\n| `vb365_list_job_sessions` | Recent run results (optionally per job). |\n| `vb365_list_repositories` / `vb365_list_restore_points` | Repos (capacity/retention/immutability); restore points. |\n| `vb365_list_object_storage` / `vb365_get_object_storage` | Cloud/object storage (B2/S3/Azure/Wasabi/IBM/Glacier). |\n| `vb365_list_proxies` / `vb365_license` | Proxies + status; license/edition/expiry. |\n| `vb365_get` | Read-only GET to any other `/<version>/...` resource. |\n\n## Action / Restore tools (opt-in, gated)\n\nRegistered only when the matching flag is set. **Two-call confirm pattern:** call\nonce to get a preview + a one-time `confirm_token`; call again with\n`confirm=<token>` to execute. Every execution is appended to `TB_AUDIT_LOG`.\n\n| Tool (flag) | What it does |\n|-------------|--------------|\n| `action_job` (`TB_ALLOW_JOB_CONTROL`) | start / stop / enable / disable a backup job |\n| `action_generate_report` (`TB_ALLOW_REPORTS`) | mailbox/onedrive/sharepoint/teams/user protection, license, storage reports → file |\n| `action_org_sync` (`TB_ALLOW_ORG_SYNC`) | trigger a directory resync for an org |\n| `action_proxy` (`TB_ALLOW_PROXY`) | rescan / maintenance-mode a proxy |\n| `restore_session_start` → `restore_browse` / `restore_search` → `restore_define` → `restore_execute` → `restore_status` → `restore_session_stop` (`TB_ALLOW_RESTORE`) | **Full restore orchestration** across Exchange / OneDrive / SharePoint / Teams: open a point-in-time session, browse/search restorable items, then **define** (pick items + target + options) and **execute** a restore. |\n\n### Restore: define **what**, **where**, **how**\n\n```text\nrestore_session_start(scope=\"job\", scope_id=\"<jobId>\", workload=\"exchange\")\n   → { session_id }                         # opens a point-in-time explore session\nrestore_browse(session_id)                  # list mailboxes\nrestore_browse(session_id, parent_id=<mailboxId>)   # list items in a mailbox\nrestore_define(session_id, parent_id=<mailboxId>, item_ids=[...], target=\"export\")\n   → { confirm_token, preview }             # DRY RUN — nothing changed\nrestore_execute(session_id, confirm=<confirm_token>)   # executes + audits\nrestore_session_stop(session_id)\n```\n\n- **target** = `export` (safest — reads backup to a local file; no M365 creds),\n  `alternate` (writes to a *different* M365 location), or `original` (writes back\n  to the **original** location — destructive; also pass `confirm_overwrite_original=True`).\n- **options** may carry an alternate `mailbox`/`folder` and the M365 target\n  credentials VB365 uses to write back (`userName`/`userPassword`/`applicationId`),\n  or set `VB365_RESTORE_*` in `.env`.\n- **PST export requires 64-bit Outlook 2010+ installed on the VB365 server**\n  (a Veeam requirement); without it, `exportToPst` returns a server-side error.\n\n## Setup\n\n1. **Install Python 3.10+** where Claude Code runs.\n2. From this folder:\n   ```powershell\n   python -m venv .venv\n   .\\.venv\\Scripts\\Activate.ps1\n   pip install -r requirements.txt\n   ```\n3. Copy `.env.example` to `.env` and fill in `VB365_HOST`, `VB365_USERNAME`,\n   `VB365_PASSWORD` (keep `VB365_PORT=4443`, `VB365_API_VERSION=v8` unless yours\n   differ). Optionally add `GRAPH_*`, `B2_*`/`S3_*`, and the `TB_*` flags.\n\n   > The REST API account needs to be a VB365 administrator (or restore\n   > operator) — VB365 has no read-only API role. The read-only **review** tools\n   > are GET-only regardless. `New-VB365ReviewAccount.ps1` creates a dedicated\n   > service account on the VB365 server. Keep `.env` out of source control.\n\n## Register with Claude Code\n\n```powershell\n# Replace <repo> with the absolute path to your clone.\nclaude mcp add tractorbeeam365 --scope user -- \"<repo>\\.venv\\Scripts\\python.exe\" \"<repo>\\server.py\"\n```\n\n> On Linux/macOS use forward slashes and `<repo>/.venv/bin/python`.\n\nIt is also published to the [MCP Registry](https://registry.modelcontextprotocol.io)\nas `io.github.Ringosystems/tractorbeeam-mcp` (an OCI image on GHCR), so MCP\nclients that support the registry can install it directly. To run the published\nimage as a stdio server:\n\n```bash\ndocker run -i --rm \\\n  -e VB365_HOST=... -e VB365_USERNAME=... -e VB365_PASSWORD=... \\\n  ghcr.io/ringosystems/tractorbeeam-mcp:latest\n```\n\nThen ask: **\"Run a VB365 health summary and tell me if anything is misconfigured.\"**\n\n## Enabling the action / restore tier\n\nThe action/restore tools stay hidden until you opt in. In `.env`:\n\n```ini\nTB_ENABLE_ACTIONS=true        # master switch\nTB_ALLOW_JOB_CONTROL=true     # + any of the per-category flags you want\nTB_ALLOW_RESTORE=true\nTB_AUDIT_LOG=audit/actions.jsonl\n```\n\n> The action/restore tools are **not** registered over an HTTP transport unless\n> you also set `TB_ALLOW_WRITE_OVER_HTTP=true`, because that transport has no\n> per-caller identity. Run the operator tier over stdio, or only behind your own\n> authenticating reverse proxy.\n\nSafety model: tools are **only registered when enabled**, every mutation needs a\n**second call with a one-time confirm token**, restores **default to a safe\ntarget** (original-location overwrite needs an explicit acknowledgement), and\n**every action is written to an append-only audit log**. Recommended: run a\nseparate \"operator\" instance with the flags on, and the default instance\nread-only.\n\n## Run as a persistent service (Docker / UNRAID)\n\nThe image itself defaults to the stdio transport (so MCP clients can `docker run\n-i` it). Via `docker compose` it runs `streamable-http` on `0.0.0.0:8000` inside\nthe container, **read-only** (no `TB_*` flags). No credentials are baked in. The\nHTTP transport **fails closed**: it refuses to start unless you set\n`MCP_AUTH_TOKEN` (clients then send `Authorization: Bearer <token>`), or you\nexplicitly accept the risk with `MCP_ALLOW_UNAUTHENTICATED_HTTP=true`.\n\n```bash\n# Put MCP_AUTH_TOKEN=<a long random secret> in .env first.\ndocker compose up -d --build          # builds locally and runs read-only\n# MCP is live at http://<host>:8766/mcp ; attach with:\nclaude mcp add --transport http tractorbeeam365 http://<host>:8766/mcp \\\n  --header \"Authorization: Bearer <token>\"\n```\n\n### Prebuilt images\n\nEach GitHub Release publishes a multi-arch, multi-tag image via CI\n([.github/workflows/publish.yml](.github/workflows/publish.yml)) to GHCR, Docker\nHub, and the MCP Registry. The full release runbook is in\n[PUBLISHING.md](PUBLISHING.md).\n\n```bash\n# GitHub Container Registry (published automatically on every release):\ndocker pull ghcr.io/ringosystems/tractorbeeam-mcp:latest\n# Docker Hub (published when the DOCKERHUB_USERNAME / DOCKERHUB_TOKEN repo\n# secrets are configured):\ndocker pull ringosystems/tractorbeeam365-mcp:latest\n```\n\nTo publish manually instead:\n\n```bash\ndocker build -t <dockerhubuser>/tractorbeeam365-mcp:latest .\ndocker push <dockerhubuser>/tractorbeeam365-mcp:latest\n```\n\n> **Security:** the MCP HTTP transport has no per-caller identity. This build\n> binds loopback by default, requires `MCP_AUTH_TOKEN` before it will serve HTTP,\n> and hides the write/restore tools over HTTP unless `TB_ALLOW_WRITE_OVER_HTTP=true`.\n> Still keep the port on a trusted network and front it with TLS + auth. The audit\n> log and any generated PST/report files live in the mounted `audit/` and\n> `downloads/` volumes. See [SECURITY.md](SECURITY.md).\n\n## Quick connectivity test (no MCP client needed)\n\n```powershell\n.\\.venv\\Scripts\\Activate.ps1\npython -c \"import server; print(server.vb365_api_root())\"\n```\n\n## Notes\n\n- `.gitignore` excludes `.env`, the venv, and the operator-tier `audit/`,\n  `downloads/`, `*.pst` artifacts. Never commit credentials or restored data.\n- VB365 ships a self-signed cert, so `VB365_VERIFY_SSL` defaults to `false`. That\n  leaves the link to VB365 open to an active man-in-the-middle, so prefer setting\n  `VB365_CA_BUNDLE` to the server's certificate (verification on against it), or\n  `VB365_VERIFY_SSL=true` with a trusted cert. The server warns at startup when\n  the channel is unverified.\n- Endpoint names vary slightly by VB365 version. If a curated tool 404s, call\n  `vb365_api_root` and use `vb365_get` with the exact resource name.\n\n## Trademarks\n\nThis is an independent project and is not affiliated with, endorsed by, or\nsponsored by any of the companies below. Product names are used only for\nidentification (nominative use).\n\n- \"Veeam\" is a registered trademark of Veeam Software. The \"Beeam\" spelling is a\n  deliberate, distinct wordplay and is not the trademark.\n- Microsoft, Microsoft 365, Microsoft Graph, Outlook, and the Office Deployment\n  Tool are trademarks of the Microsoft group of companies.\n- Backblaze and B2 are trademarks of Backblaze, Inc.\n- Amazon Web Services, AWS, Amazon S3, and S3 Glacier are trademarks of\n  Amazon.com, Inc. or its affiliates.\n- Wasabi, IBM and IBM Cloud, Azure, and Google Cloud are trademarks of their\n  respective owners.\n\n## License\n\nReleased under the [MIT License](LICENSE). See [SECURITY.md](SECURITY.md) for the\nsecurity model and reporting, [CONTRIBUTING.md](CONTRIBUTING.md) to contribute, and\n[PUBLISHING.md](PUBLISHING.md) for how releases are built and published.\n",
  "bytes": 13419,
  "sha": "377079773c49a9a6941d85f670ed79b4d8eb9093795dc4b40bf45c27ad2d401f",
  "repo_slug": "ringosystems/tractorbeeam-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_ringosystems_tractorbeeam_mcp_3ec15160/readme"
}