Skip to main content
Glama
am3cramirez

saas-alerts-mcp

by am3cramirez
README.md
# saas-alerts-mcp

A **fully self-contained** [Model Context Protocol](https://modelcontextprotocol.io) server for
[Kaseya SaaS Alerts](https://saasalerts.com/) — the SaaS security monitoring platform for
Microsoft 365 and Google Workspace events.

It wraps the **SaaS Alerts External Partner API (Reports) v0.20.0** and exposes it as **30 MCP tools**
plus 3 workflow prompts. The API client is vendored in-tree (`src/sdk`), so the server builds and runs
with a single public dependency (`@modelcontextprotocol/sdk`) — no private packages, no external SDK to
install.

## Features

| Domain | Tools |
|--------|-------|
| **events** | Query/count security events by severity, customer, user, event type; advanced Elasticsearch queries; scroll pagination; recommended actions |
| **customers** | List, get, create, update, delete customers; set IP/country and account whitelists |
| **users** | MSP user info, partner users, customer users |
| **devices** | Unify mapped/unmapped/ignored devices, device organizations |
| **billing** | Billing details by date, billing date history |
| **reports** | List/get/create/delete scheduled reports |
| **partner** | Partner profile, branding settings |

- **Two navigation helpers** — `saas_alerts_navigate` (browse a domain's tools) and `saas_alerts_status`
  (connectivity check against the authenticated MSP user).
- **Three prompts** — `alert-triage`, `customer-alert-summary`, `cross-tenant-anomaly`.
- **Write/destructive guards** — mutating tools carry `annotations.destructiveHint` and, where
  irreversible, a `⚠ DESTRUCTIVE` description prefix. They request confirmation via MCP
  [elicitation](https://modelcontextprotocol.io/docs/concepts/elicitation) and **fail open** (proceed)
  if the client doesn't support it.
- **Empty-result guard** — list tools return `isError` with a clear "No … found" message instead of an
  empty payload, to stop the model hallucinating results.

## Authentication

SaaS Alerts authenticates with an API key sent as the **`api_key`** HTTP header (not `Authorization:
Bearer`). Generate one in the SaaS Alerts UI under **Settings → API Keys**.

Set it via the `SAAS_ALERTS_API_KEY` environment variable.

## Quick start

```bash
npm install      # also builds via the `prepare` script
npm run build    # or build explicitly
export SAAS_ALERTS_API_KEY=your-api-key
node dist/index.js          # stdio transport
```

## Importing into Hermes (or any MCP host)

The server speaks standard MCP over **stdio**, so it drops into any MCP host that uses the common
`mcpServers` config block. Build it once (`npm run build`), then point your host at `dist/index.js`.

Copy [`.mcp.json.example`](./.mcp.json.example) into your host's MCP config and edit the path + key:

```json
{
  "mcpServers": {
    "saas-alerts": {
      "command": "node",
      "args": ["/absolute/path/to/saas-alerts-mcp/dist/index.js"],
      "env": {
        "SAAS_ALERTS_API_KEY": "your-saas-alerts-api-key",
        "MCP_TRANSPORT": "stdio"
      }
    }
  }
}
```

After import, the host will discover all 30 `saas_alerts_*` tools. Call `saas_alerts_status` first to
confirm the key is valid and connectivity is up.

> **Tip:** if your Hermes deployment prefers a network endpoint over a spawned process, run the server
> in HTTP mode (below) and register the `http://host:8080/mcp` URL instead.

## HTTP mode

```bash
export SAAS_ALERTS_API_KEY=your-api-key
export MCP_TRANSPORT=http
export MCP_HTTP_PORT=8080
node dist/http.js
```

- `POST /mcp` — MCP endpoint (stateless streamable-HTTP, JSON responses).
- `GET /health` — liveness probe (does not require credentials).

### Gateway mode (multi-tenant)

Set `AUTH_MODE=gateway`. The server then reads the per-request API key from the
**`X-SaaS-Alerts-API-Key`** header instead of the environment, and isolates each request's credentials
in an `AsyncLocalStorage` context. The HTTP transport is intentionally kept **stateless** to preserve
that per-request tenant isolation.

## Running with Docker

```bash
export SAAS_ALERTS_API_KEY=your-api-key
docker compose up --build
```

The container runs HTTP mode on port 8080 with a `/health` healthcheck, as a non-root user.

## Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `SAAS_ALERTS_API_KEY` | — | **Required** (env/stdio mode). Sent as the `api_key` header. |
| `MCP_TRANSPORT` | `stdio` | `stdio` or `http`. |
| `MCP_HTTP_PORT` | `8080` | HTTP mode listen port. |
| `MCP_HTTP_HOST` | `0.0.0.0` | HTTP mode bind host. |
| `AUTH_MODE` | `env` | `env` reads the key locally; `gateway` reads `X-SaaS-Alerts-API-Key` per request. |
| `LOG_LEVEL` | `info` | `debug` \| `info` \| `warn` \| `error` (logs go to stderr). |

## Development

```bash
npm install
npm run build              # bundle to dist/ (ESM, with .d.ts)
npm test                   # vitest — SDK transport/resources + MCP domain handlers
npm run lint               # tsc --noEmit
npm run lint:destructive   # assert destructive tools carry warnings
```

### Architecture

```
src/
  index.ts        stdio entrypoint
  http.ts         streamable-HTTP entrypoint (+ gateway mode)
  server.ts       MCP Server wiring: tools/list, tools/call, prompts
  prompts.ts      3 workflow prompts
  domains/        one handler per API domain (events, customers, …)
  elicitation/    destructive-action confirmation guard (fail-open)
  utils/          request-scoped credentials, result helpers, logger
  sdk/            vendored typed client for the SaaS Alerts Partner API
    http.ts       fetch transport: retries, timeouts, key redaction, typed errors
    resources/    one class per domain, mapping methods → REST endpoints
    client.ts     SaasAlertsClient façade
```

The API base URL defaults to the production host
(`https://us-central1-the-byway-248217.cloudfunctions.net/reportApi/api/v1`) and can be overridden by
constructing `SaasAlertsClient` with a `baseUrl` (dev/QA hosts are documented in the OpenAPI spec).

## Attribution

The MCP surface design (domain layout, tool naming, destructive-action conventions) follows the
open-source [`wyre-technology/saas-alerts-mcp`](https://github.com/wyre-technology/saas-alerts-mcp)
project. This build vendors the API client in-tree so it is fully buildable and importable standalone.

## License

Apache-2.0 — see [LICENSE](LICENSE).

TDQS

A3.6/5.0

Scored across 30 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: customers, devices, events, billing, reports, and partner operations are all separated. Even similar tools like events_query and events_query_advanced are differentiated by the use of raw Elasticsearch versus standard filters. No two tools appear to do the same thing.

Naming Consistency5/5

All tools follow the consistent pattern 'saas_alerts_<resource>_<action>', using snake_case throughout. Actions like list, get, create, update, delete, and set are used predictably. The few top-level tools (navigate, status) still retain the 'saas_alerts' prefix and are easy to understand.

Tool Count2/5

With 30 tools, the server exceeds the 25+ threshold that indicates an overly large tool set. The broad scope of SaaS Alerts management justifies many tools, but there is some redundancy (e.g., multiple device list tools and separate navigation/status tools) that could be consolidated.

Completeness2/5

Customer CRUD is well covered, but there are significant gaps: devices have only list operations (no mapping or ignoring actions), scheduled reports lack an update tool, and users have no management beyond listing. These missing operations create dead ends for agents trying to perform common workflows.

Maintenance

ActivityStale
ResponsivenessNo issues