Skip to main content
Glama
MSPbotsAI

wasabi-mcp

by MSPbotsAI
README.md
# wasabi-mcp

MCP server for **Wasabi** (S3-compatible hot cloud storage). Exposes the
Wasabi **Stats API**'s bucket utilization data as an MCP tool.

## Overview

- Stateless HTTP service. No credentials are ever persisted — each request
  supplies its own credentials via headers, used only for the lifetime of
  that single request.
- Supports concurrent requests; per-request credential isolation is done via
  Python `contextvars`, not a global/shared client instance.
- Entry points: `POST /mcp` (MCP protocol) and `GET /health` (health check).
- Default port: `8080` (configurable via `MCP_HTTP_PORT`).

## Authentication

The Wasabi Stats API (`stats.wasabisys.com`) — a separate, simpler API
from the S3-compatible object storage API — uses a single static header,
with no login/token exchange:

```
Authorization: <AccessKey>:<SecretKey>
```

The keys used must be Wasabi Root keys, or sub-user keys with an attached
billing/stats policy (`WasabiAccountStatsAccess` or
`WasabiBucketStatsAccess`) — plain storage-only keys are rejected. This is
already fully stateless by the vendor's own design; nothing is cached
across MCP calls.

### HEADER 授权参数说明

| Header | 类型 | 是否必填 | 默认值 | 枚举值 | 字段描述 | Example |
|---|---|---|---|---|---|---|
| `X-Wasabi-Access-Key` | string | 是 | 无 | 无 | Wasabi API Access Key(需为 Root key 或带 billing/stats 权限策略的 key) | `AGWZ1234567890ABCDEF` |
| `X-Wasabi-Secret-Key` | string | 是 | 无 | 无 | 对应 Secret Key | `a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8g9h0` |

Missing either header returns `401`:
```json
{
  "error": "Missing credentials",
  "message": "This server requires the X-Wasabi-Access-Key and X-Wasabi-Secret-Key headers",
  "required_headers": ["X-Wasabi-Access-Key", "X-Wasabi-Secret-Key"],
  "optional_headers": []
}
```

## Environment Variables

| Variable | 类型 | 是否必填 | 默认值 | 说明 |
|---|---|---|---|---|
| `MCP_HTTP_PORT` | int | 否 | `8080` | HTTP 监听端口 |
| `MCP_HTTP_HOST` | string | 否 | `0.0.0.0` | HTTP 监听地址 |
| `WASABI_STATS_BASE_URL` | string | 否 | `https://stats.wasabisys.com` | Wasabi Stats API 基础 URL(与 bucket 所在 region 无关,固定不变) |

## MCP Endpoint

- `POST /mcp` — MCP protocol (streamable HTTP transport)
- `GET /health` — health check, returns `{"status": "ok"}` (pure local probe, does not call the Wasabi API)

## Tool List

| Tool | 功能 | 参数 |
|---|---|---|
| `wasabi_get_bucket_utilizations` | 列出账号下所有 bucket 的每日存储/API 调用用量记录 | `page_size`(可选,默认 100,硬上限 100 — Wasabi Stats API 本身的每页上限)、`page_num`、`from_date`、`to_date`(均可选) |

Responses are the vendor's raw JSON (`{"PageInfo": {...}, "Records": [...]}`), serialized compactly (no indentation, `ensure_ascii=False`) and capped at 20,000 characters — if the `Records` list would push the response past that, it is truncated with `truncated`/`truncated_field`/`original_count` markers added to the payload.

## Error Handling

On failure, tools return a JSON error envelope (as a string, not a protocol-level error) instead of raising:

```json
{"error": {"code": "upstream_error", "message": "...", "retryable": true}}
```

`code` is one of a fixed vocabulary: `not_configured`, `unauthorized`, `not_found`, `invalid_argument`, `rate_limited`, `upstream_error`. `retryable` tells the caller whether retrying is worthwhile. Empty result sets (no usage records for the requested range) are returned as a normal empty result, not as an error.

## 测试示例

```bash
# Health check
curl -s http://localhost:8080/health

# Call a tool via the MCP protocol (streamable HTTP) — requires an
# initialize handshake first per the MCP spec; abbreviated example below
# shows the tool-call request body only:
curl -s -X POST http://localhost:8080/mcp \
  -H "X-Wasabi-Access-Key: <your-access-key>" \
  -H "X-Wasabi-Secret-Key: <your-secret-key>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "mcp-session-id: <session-id-from-initialize>" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "wasabi_get_bucket_utilizations",
      "arguments": {}
    }
  }'
```

**Live-verified** (2026-07-30) against a real Wasabi account:
`wasabi_get_bucket_utilizations` called end-to-end through this running
server returned real data — 540 real bucket-utilization records across
270 pages, including real bucket names (e.g. "oraclegroup",
"centrewestib") in the `ap-southeast-2` region, with real storage/API-call
metrics.

## API Reference

- Public, no login required:
  - Overview: https://docs.wasabi.com/apidocs/wasabi-stats-api
  - Authentication: https://docs.wasabi.com/apidocs/authentication-with-wasabi-stats-api
  - This endpoint: https://docs.wasabi.com/apidocs/stats-api-get-v1standalone-bucket-utilizations

## Known Gaps

- **Scope is exactly MSPbots' 1 configured endpoint, not the vendor's full
  API surface** — the Wasabi Stats API also offers an account-level
  summary endpoint (`GET /v1/standalone/utilizations`) and per-bucket
  detail lookups by name or number; those are out of scope here. The
  Wasabi S3-compatible object storage API (bucket/object CRUD) is an
  entirely separate API and also out of scope.
- **The `region` field MSPbots also stores for this integration is not
  used by this server** — the one endpoint MSPbots actually uses (Stats
  API bucket utilizations) has a fixed base URL
  (`https://stats.wasabisys.com`) regardless of which Wasabi region a
  given bucket lives in; each returned record already includes its own
  `Region` field. `region` would only matter for direct S3-compatible
  object storage calls, which this server does not make.
- **`from`/`to` date range defaults are vendor-controlled** — if omitted,
  Wasabi defaults to "one month ago" through "today"; this server does not
  apply its own default and simply omits the parameter when not provided.

Maintenance

ActivityMaintained
ResponsivenessNo issues