Skip to main content
Glama
HyperBDR

beacon-mcp

by HyperBDR
README.md
# beacon-mcp

MCP server for the [beacon](https://github.com/HyperBDR/beacon) log analytics platform.

Exposes beacon's REST API as [Model Context Protocol](https://modelcontextprotocol.io) tools and resources, so any MCP-compatible AI agent (Claude Desktop, Cursor, Cline, Continue, VS Code) can query and analyse AI-assistant usage data through a typed, validated interface.

The MCP server is a thin client over beacon's existing `/api/v1` endpoints — it does **not** duplicate SQL, Parquet, or storage logic. It can run standalone, or be launched lazily by the beacon Go API and exposed through `/api/v1/mcp`.

---

## Features

- **11 tools** covering organisation discovery, health checks, configuration, full dashboard, dashboard sub-sections, 5 summary dimensions, raw event query, and per-session event chain.
- **3 resources** (`beacon://orgs`, `beacon://config`, `beacon://dashboard`) for context that should be cached client-side.
- **Two transports**: stdio (default) for Claude Desktop / Cursor / Cline, HTTP+SSE (`--http`) for remote agents.
- **Strict types & validation** via [Zod](https://zod.dev/) — every argument is checked at the protocol boundary.
- **Unified filter arguments**: `org`, `from`, `to`, `project`, `model`, `user`, `status`.
- **Smart summaries**: each tool returns a Markdown summary **plus** the raw JSON payload, so LLMs can both skim and re-parse.
- **API integration**: works against the public `/api/v1` API and can be proxied by the beacon Go backend at `/api/v1/mcp`; auth via `BEACON_API_KEY` if you front beacon with a reverse proxy.

---

## Quick start

### Option A — `npx` (recommended, no install)

Run directly with `npx` from a beacon checkout or any directory:

```bash
BEACON_BASE_URL=http://127.0.0.1:8080 \
BEACON_ORG=default \
npx -y @beacon/mcp-server
```

The `-y` flag auto-confirms the install prompt. The first invocation downloads the package (~22 kB) and starts the stdio transport immediately. Subsequent invocations are instant.

### Option B — `npm install` (long-lived install)

```bash
npm install -g @beacon/mcp-server
# or, locally inside a project:
npm install @beacon/mcp-server
```

Then run with the `beacon-mcp` binary:

```bash
BEACON_BASE_URL=http://127.0.0.1:8080 \
BEACON_ORG=default \
beacon-mcp
```

### Option C — from source (for development)

```bash
git clone https://github.com/HyperBDR/beacon-mcp.git
cd beacon-mcp
npm install
npm run dev                # stdio, with tsx — no build step
npm run dev:http           # HTTP+SSE on $MCP_HTTP_PORT (default 8765)
```

`npm install` is only required for development. End users never compile anything — the published package ships pre-built `dist/`.

---

## Running modes

| Command | What it does | When to use |
|---|---|---|
| `npx -y @beacon/mcp-server` | stdio transport (JSON-RPC over stdin/stdout) | Claude Desktop / Cursor / Cline / VS Code / Continue |
| `npx -y @beacon/mcp-server --http` | HTTP+SSE transport | Remote agents or browser-based MCP clients |
| `beacon-mcp --help` | Print CLI usage and exit | Sanity check |
| `npm run dev` (from source) | stdio via `tsx` (no build) | Developing the server itself |
| `npm run dev:http` (from source) | HTTP+SSE via `tsx` | Developing the server itself |
| `npm start` (from source, after `npm run build`) | Production stdio from compiled `dist/` | Verifying the published binary locally |

CLI flags:

```
--http                Run HTTP+SSE transport (default: stdio)
--host <addr>         HTTP host (default: 127.0.0.1 or $MCP_HTTP_HOST)
--port <number>       HTTP port (default: 8765 or $MCP_HTTP_PORT)
--help, -h            Show this help
```

---

## Configuration

All settings come from environment variables. See [`.env.example`](.env.example) for the full list.

| Variable | Default | Description |
|---|---|---|
| `BEACON_BASE_URL` | `http://127.0.0.1:8080` | Beacon REST API base URL |
| `BEACON_ORG` | `default` | Default organisation ID; tools can override per-call via the `org` argument |
| `BEACON_TIMEOUT_MS` | `30000` | Per-request timeout in milliseconds |
| `BEACON_API_KEY` | _(unset)_ | Optional bearer token (sent as `Authorization: Bearer …`) |
| `BEACON_PROXY` | _(unset)_ | Optional HTTP proxy for requests to `BEACON_BASE_URL`. Accepts `http://`, `https://`, `socks5://`. Useful for corporate egress proxies. Example: `http://proxy.corp.local:8080`. |
| `MCP_HTTP_HOST` | `127.0.0.1` | HTTP transport host (only with `--http`) |
| `MCP_HTTP_PORT` | `8765` | HTTP transport port (only with `--http`) |

---

## Client configuration

Below are the most common client integrations. After editing the config, **fully restart the client** (Claude Desktop, Cursor) so it picks up the new MCP server.

### Claude Desktop

Config file:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Linux: `~/.config/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`

`npx` version (recommended, no global install):

```json
{
  "mcpServers": {
    "beacon": {
      "command": "npx",
      "args": ["-y", "@beacon/mcp-server"],
      "env": {
        "BEACON_BASE_URL": "http://127.0.0.1:8080",
        "BEACON_ORG": "default"
      }
    }
  }
}
```

Globally installed version:

```json
{
  "mcpServers": {
    "beacon": {
      "command": "beacon-mcp",
      "args": [],
      "env": {
        "BEACON_BASE_URL": "http://127.0.0.1:8080",
        "BEACON_ORG": "default"
      }
    }
  }
}
```

Development version (from a beacon-mcp source checkout, with `npm install` already run):

```json
{
  "mcpServers": {
    "beacon-dev": {
      "command": "npx",
      "args": ["tsx", "/absolute/path/to/beacon-mcp/src/index.ts"],
      "env": {
        "BEACON_BASE_URL": "http://127.0.0.1:8080",
        "BEACON_ORG": "default"
      }
    }
  }
}
```

### Cursor

`Settings → MCP → Add new global MCP server`. Same JSON shape as Claude Desktop (the `mcpServers` map is the standard).

A typical `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "beacon": {
      "command": "npx",
      "args": ["-y", "@beacon/mcp-server"],
      "env": {
        "BEACON_BASE_URL": "http://127.0.0.1:8080",
        "BEACON_ORG": "default"
      }
    }
  }
}
```

### Cline (VS Code)

Open the Cline panel → MCP Servers → "Configure MCP Servers". Same JSON shape.

### Continue (VS Code JetBrains)

Add to `~/.continue/config.json` under `experimental.modelContextProtocolServers`:

```json
[
  {
    "name": "beacon",
    "command": "npx",
    "args": ["-y", "@beacon/mcp-server"],
    "env": {
      "BEACON_BASE_URL": "http://127.0.0.1:8080",
      "BEACON_ORG": "default"
    }
  }
]
```

### Remote agents (HTTP+SSE)

After `npx -y @beacon/mcp-server --http --host 0.0.0.0 --port 8765`, the endpoint is:

```
http://<host>:8765/mcp
```

Use any MCP HTTP client (the SDK ships Python/TS/Go/Kotlin clients). CORS is open by default — set up a reverse proxy with auth in production.

The server is a long-running process. Common deployment patterns:

```bash
# systemd unit
[Service]
ExecStart=/usr/bin/env npx -y @beacon/mcp-server --http --host 0.0.0.0 --port 8765
Environment=BEACON_BASE_URL=http://beacon.internal:8080
Environment=BEACON_ORG=production
Restart=always
```

```yaml
# docker-compose snippet
beacon-mcp:
  image: node:22-alpine
  command: ["npx", "-y", "@beacon/mcp-server", "--http", "--host", "0.0.0.0", "--port", "8765"]
  environment:
    BEACON_BASE_URL: http://beacon:8080
    BEACON_ORG: production
  ports:
    - "8765:8765"
  restart: unless-stopped
```

---

## Tool reference

### Org & config

| Tool | Description |
|---|---|
| `list_organizations` | List all beacon orgs available via the configured API. |
| `health_check` | Probe `GET /health` for an org. |
| `get_config` | Read public dashboard config (model pricing, min session event count). |

### Dashboard

| Tool | Description |
|---|---|
| `get_dashboard` | Fetch the full dashboard payload, **or** a single sub-section (`metrics`, `activity`, `traffic`, `distributions`, `sessions`, `projects`). |

### Summary (5 dimensions)

| Tool | Description |
|---|---|
| `query_project_summary` | Daily per-project token/event rollup. |
| `query_language_summary` | Daily per-language rollup (from session detection). |
| `query_prompt_style_summary` | Daily per-prompt-style rollup. |
| `query_employee_hourly_summary` | Per-user, per-hour breakdown by tool and model. |
| `query_session_summary` | Per-session rollup (heaviest sessions, models, timestamps). |

### Events & sessions

| Tool | Description |
|---|---|
| `query_events` | Raw event query with `from`/`to`/`project`/`model`/`user`/`status` filters and pagination (`limit`, `all`). |
| `get_session_events` | Fetch the full event chain for a single session, given `(user, session_id, project)`. |

### Common arguments

Almost every tool accepts:

- `org` — organisation ID; falls back to `$BEACON_ORG`.
- `from` / `to` — `YYYY-MM-DD` (inclusive).
- `project` — exact project name, or `"all"` to disable.
- `model` — substring match, or `"all"`.
- `user` — substring match against `source_user_name` or `source_user_id`.
- `status` — `"errors_only"` or `"success_only"`.

`query_events` additionally accepts `limit` (1-500, default 100) and `all` (boolean).

---

## Resources

| URI | Description |
|---|---|
| `beacon://orgs` | List of organisations (cacheable). |
| `beacon://config` | Default org's dashboard config. |
| `beacon://dashboard` | Full default-org dashboard payload. |

For per-org resources, call the `get_config` / `get_dashboard` tools with the `org` argument.

---

## Output format

Every tool returns a single MCP `content` block with a Markdown summary followed by a fenced JSON payload. Example:

```markdown
## Summary

### Overview
- Events: **12,480** (requests: 9,201, sessions: 318)
- Tokens: **42.1M** (prompt 30.5M + completion 11.6M)
- ...

### Top projects
| Project | Tokens | Events | Requests | Share |
| --- | --- | --- | --- | --- |
| beacon | 18,205,440 | 4,820 | 3,612 | 43.2% |
| ... |

## Data (JSON)
```json
{ "code": 0, "message": "success", "data": [...], "meta": {...} }
```
```

This dual format lets the model either skim the Markdown (low token cost) or re-parse the JSON (precise). Errors are returned as `isError: true` with a plain-text message.

---

## Troubleshooting

### "Failed to connect to 127.0.0.1 port 8080"

The beacon API isn't running, or `BEACON_BASE_URL` is wrong.

```bash
curl $BEACON_BASE_URL/api/v1/health
# expected: {"code":0,"message":"success","data":{"status":"ok","time":"..."}}
```

### "organization \"X\" not found"

`$BEACON_ORG` (or the `org` argument) is not registered in the beacon API. Run `list_organizations` first to see what's available.

### "context deadline exceeded"

`BEACON_TIMEOUT_MS` is too low for the query. Try increasing it (default 30s) or narrowing the date range / using `section` on `get_dashboard`.

### Claude Desktop: "MCP server disconnected"

1. Check the config file path is correct.
2. Run the command from a terminal first to surface any error output:
   ```bash
   npx -y @beacon/mcp-server
   ```
3. Fully quit and re-open Claude Desktop (config changes do not hot-reload).
4. On macOS, look at the Claude Desktop log: `~/Library/Logs/Claude/mcp*.log`.

### HTTP+SSE: CORS or 401 errors

The server ships with CORS wide open for browser clients. If you front it with nginx/traefik, configure `Authorization: Bearer $BEACON_API_KEY` forwarding at the proxy. The MCP SDK does not enforce auth itself — protect the endpoint with a reverse-proxy in production.

### Behind a corporate proxy

Set `BEACON_PROXY` to route beacon traffic through the proxy. This affects
**only the MCP server → beacon** direction (not the MCP client ↔ MCP server
transport). The agent that runs the MCP client (Claude Desktop, Cursor,
etc.) is unaffected.

```json
{
  "mcpServers": {
    "beacon": {
      "command": "npx",
      "args": ["-y", "@beacon/mcp-server"],
      "env": {
        "BEACON_BASE_URL": "http://beacon.internal:8080",
        "BEACON_ORG": "default",
        "BEACON_PROXY": "http://proxy.corp.local:8080"
      }
    }
  }
}
```

Supported schemes: `http://`, `https://`, `socks5://`. The `socks5://` form
requires Node 18+ which uses undici 5+ under the hood. If the proxy
requires authentication, embed it in the URL: `http://user:pass@host:port`.

To verify the proxy is being used, tail the beacon server's access log
while invoking any tool — requests will arrive from the proxy's IP, not
the agent host.

### Beacon is reachable but everything is empty

Check that the collector + analyzer pipelines have run. Raw events need to be aggregated by the analyzer before the summary endpoints return data. Run `go run ./cmd/analyzer -config testdata/collector.yaml` (in the beacon repo) periodically.

---

## Development

```bash
npm install
npm run typecheck   # tsc --noEmit
npm test            # vitest run
npm run build       # tsc → dist/ (mirrors what `npm publish` will do via the `prepare` script)
```

Watch mode for tests:

```bash
npm run test:watch
```

### Layout

```
src/
  index.ts          # entry point, CLI parsing, transport selection
  server.ts         # McpServer construction; registers all tool modules
  client.ts         # BeaconClient — typed wrapper over beacon's REST API
  config.ts         # env + CLI arg parsing (zod-validated)
  filters.ts        # shared zod schemas (BaseFilter, EventFilter, SessionKey)
  formatting.ts     # JSON block + Markdown summary helpers
  tools/
    orgs.ts         # list_organizations, health_check, get_config + resources
    dashboard.ts    # get_dashboard + beacon://dashboard
    summary.ts      # 5 query_*_summary tools
    events.ts       # query_events
    session.ts      # get_session_events
tests/
  setup.ts          # vitest setup
  client.test.ts    # BeaconClient unit tests
  config.test.ts    # config + CLI parsing tests
  tools.test.ts     # end-to-end tool tests over an in-memory MCP transport
```

### Adding a new tool

1. Pick or create a file under `src/tools/`.
2. Write a `registerXxxTools(server: McpServer, client: BeaconClient): void` function.
3. Use the shared zod schemas in `filters.ts` for inputs.
4. Format output with `resultBlocks(summary, payload)` from `formatting.ts`.
5. Wire the registration into `server.ts`.
6. Add a test in `tests/tools.test.ts` that mocks the beacon response with `msw`.

### Publish flow

```bash
# Bump version
npm version patch   # or minor / major

# Publish (the `prepare` script auto-runs `tsc` before upload)
npm login
npm publish --access public
```

The published tarball contains only `dist/`, `README.md`, `LICENSE`, and `package.json` (controlled by `package.json#files` and `.npmignore`).

---

## License

MIT — see [LICENSE](./LICENSE).

TDQS

A3.9/5.0

Scored across 11 tools

Disambiguation5/5

Each tool targets a distinct aspect of the beacon API: configuration, dashboards, session details, health, organizations, and various summaries (employee, events, language, project, prompt style, sessions). There is no overlap in purpose.

Naming Consistency4/5

Most tools follow a consistent verb_prefix pattern: get_ for specific fetches, list_ for listings, query_ for summaries. However, health_check deviates from the pattern, and get_session_events vs query_session_summary have similar domains with different prefixes.

Tool Count5/5

11 tools is well-scoped for an analytics server. It covers essential operations (config, health, org listing, session detail, and multiple summary queries) without being excessive.

Completeness4/5

The tool set appears complete for read-only analytics: it covers configuration, health, organization selection, raw events with pagination, and various rollups. Missing create/update/delete tools, but those are likely out of scope for an analytics API.

Maintenance

ActivityInactive
ResponsivenessNo issues