@lacneu/atrium-mcp 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +160 -0
- package/dist/cli.d.ts +23 -0
- package/dist/cli.js +220 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +59 -0
- package/dist/config.js +103 -0
- package/dist/config.js.map +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.js +250 -0
- package/dist/server.js.map +1 -0
- package/dist/tools.d.ts +341 -0
- package/dist/tools.js +389 -0
- package/dist/tools.js.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# atrium-mcp
|
|
2
|
+
|
|
3
|
+
A thin, dependency-light **MCP server + CLI** that proxy the atrium
|
|
4
|
+
`/api/v1` observability surface using an `oc_live_` Bearer API key.
|
|
5
|
+
|
|
6
|
+
It speaks **HTTP only** — it imports nothing from the Convex app. Each tool maps
|
|
7
|
+
to one `/api/v1` route and one permission, which is enforced **server-side** by the
|
|
8
|
+
deployment (`requirePermission`). A scoped key (e.g. an `observer`) therefore simply
|
|
9
|
+
gets a `403` for routes it isn't allowed to call.
|
|
10
|
+
|
|
11
|
+
Runtime dependencies: `@modelcontextprotocol/sdk` and `zod` only.
|
|
12
|
+
|
|
13
|
+
## Configuration (environment)
|
|
14
|
+
|
|
15
|
+
| Variable | Required | Default | Meaning |
|
|
16
|
+
| ---------------------------- | -------- | ------------------------ | -------------------------------------------------------- |
|
|
17
|
+
| `OPENCLAW_WEBCHAT_API_BASE` | no | `http://127.0.0.1:3213` | Deployment `.site` origin, **without** `/api/v1`. |
|
|
18
|
+
| `OPENCLAW_WEBCHAT_API_KEY` | **yes** | — | The `oc_live_...` Bearer key. |
|
|
19
|
+
|
|
20
|
+
The `/api/v1` prefix is added internally; you point `API_BASE` at the bare
|
|
21
|
+
`.site` origin. The key is only ever sent in the `Authorization` header — never
|
|
22
|
+
in a URL/query string and never logged.
|
|
23
|
+
|
|
24
|
+
## Install (released artifact)
|
|
25
|
+
|
|
26
|
+
Published on every tagged release as **`@lacneu/atrium-mcp`** (npm, OIDC +
|
|
27
|
+
provenance), at the same lockstep version as the app images. Being HTTP-only and
|
|
28
|
+
provider-neutral, the same package serves any MCP-capable gateway (OpenClaw,
|
|
29
|
+
Hermes, Claude Code, …).
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# ad hoc (any MCP client with stdio command support)
|
|
33
|
+
npx -y @lacneu/atrium-mcp
|
|
34
|
+
|
|
35
|
+
# pinned, baked into a gateway image
|
|
36
|
+
npm install -g @lacneu/atrium-mcp@<version> # provides `atrium-mcp` + `atrium` (CLI)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Example OpenClaw declaration (`mcp.servers`, stdio) — one server per deployment,
|
|
40
|
+
credentials injected via a wrapper or the process environment, never in config:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcp": {
|
|
45
|
+
"servers": {
|
|
46
|
+
"atrium-prod": { "command": "atrium-mcp-prod" }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Build (from source)
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
cd mcp
|
|
56
|
+
npm install
|
|
57
|
+
npm run build # emits dist/ (with shebangs preserved)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Other scripts: `npm run typecheck`, `npm test`.
|
|
61
|
+
|
|
62
|
+
## Tools (MCP) / Commands (CLI)
|
|
63
|
+
|
|
64
|
+
| MCP tool | CLI command | Route | Permission | Purpose |
|
|
65
|
+
| ---------------------- | ---------------- | ----------------------------- | ------------------ | ------- |
|
|
66
|
+
| `health` | `health` | `GET /api/v1/health` | none | Liveness probe for the API surface. |
|
|
67
|
+
| `get_compat` | `compat` | `GET /api/v1/compat` | `bridge.read` | Bridge version + per-instance gateway versions/capabilities. |
|
|
68
|
+
| `get_integrations` | — | `GET /api/v1/integrations` | `traces.read` | Opik/Langfuse configured/enabled + shipping cursors. No keys. |
|
|
69
|
+
| `get_chat_state` | — | `GET /api/v1/chat-state` | `traces.read` | Per-message lifecycle of one chat (metadata only). |
|
|
70
|
+
| `get_trace_enrichment` | — | `GET /api/v1/trace-enrichment` | `traces.read` | SOC2-safe span structure from Opik/Langfuse, keyed by `correlationId`. |
|
|
71
|
+
| `diagnose_chat` | — | `GET /api/v1/diagnose` | `traces.read` | One assessment of a chat + a suggested action/tool. |
|
|
72
|
+
| `reconcile_chat` | — | `POST /api/v1/reconcile-chat` | `selfheal` | Release a chat's stuck `streaming` message (text preserved). Audited. |
|
|
73
|
+
| `list_traces` | `traces` | `GET /api/v1/traces` | `traces.read` | Recent trace events, with filtering. |
|
|
74
|
+
| `get_kpi` | `kpi` | `GET /api/v1/kpi` | `kpi.read` | KPI rollups. |
|
|
75
|
+
| `query_openclaw` | `query-openclaw` | `POST /api/v1/openclaw/query` | `openclaw.query` | Query OpenClaw via the bridge. |
|
|
76
|
+
| `list_anomalies` | `anomalies` | `GET /api/v1/anomalies` | `anomalies.read` | Detected anomalies, with status/severity/source filtering. |
|
|
77
|
+
| `report_anomaly` | `report-anomaly` | `POST /api/v1/anomalies` | `anomalies.report` | Record an anomaly / self-repair signal. |
|
|
78
|
+
|
|
79
|
+
Tools without a CLI command are MCP-only. Every tool's permission is enforced by
|
|
80
|
+
the deployment: a key lacking a tool's permission gets a `403` the tool surfaces
|
|
81
|
+
as an error.
|
|
82
|
+
|
|
83
|
+
The four read-only diagnostic tools (`get_integrations`, `diagnose_chat`,
|
|
84
|
+
`get_chat_state`, `get_trace_enrichment`) plus the bounded corrective
|
|
85
|
+
`reconcile_chat` compose into a closed loop an agent can drive from a single user
|
|
86
|
+
report. See **[the self-correction loop](../docs/SELF_CORRECTION_LOOP.md)** for how
|
|
87
|
+
they fit together and the SOC2-safe trace catalog (including the `documentary.*`
|
|
88
|
+
document-fetch traces). The whole surface is metadata-only — never message text,
|
|
89
|
+
filenames, URLs, or keys.
|
|
90
|
+
|
|
91
|
+
## CLI usage
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
export OPENCLAW_WEBCHAT_API_BASE=http://127.0.0.1:3213
|
|
95
|
+
export OPENCLAW_WEBCHAT_API_KEY=oc_live_xxxxxxxxxxxx
|
|
96
|
+
|
|
97
|
+
node dist/cli.js health
|
|
98
|
+
node dist/cli.js compat
|
|
99
|
+
node dist/cli.js traces --limit 20
|
|
100
|
+
node dist/cli.js traces --kind api.call --correlation-id abc123
|
|
101
|
+
|
|
102
|
+
# filtering (traces): --q substring, time range, structured fields
|
|
103
|
+
# --from/--to accept epoch ms OR a Grafana relative token (now, now-<N><unit>)
|
|
104
|
+
node dist/cli.js traces --from now-24h --to now --status-class 4xx --kind api.call --q foo
|
|
105
|
+
node dist/cli.js traces --status 404 --principal-type service --role-key admin
|
|
106
|
+
|
|
107
|
+
node dist/cli.js kpi --metric api.calls --since 2026-06-01T00:00
|
|
108
|
+
node dist/cli.js kpi --metric api.calls --from now-24h --to now
|
|
109
|
+
node dist/cli.js anomalies --limit 50 --status open
|
|
110
|
+
node dist/cli.js anomalies --from now-1h --severity critical --source detector --q spike
|
|
111
|
+
node dist/cli.js query-openclaw --prompt "summarize last run" --chat-id c1
|
|
112
|
+
node dist/cli.js report-anomaly --kind latency.spike --severity warn --message "p99 > 5s"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
If installed/published, the same is available as the `atrium` bin
|
|
116
|
+
(e.g. `atrium traces --limit 20`).
|
|
117
|
+
|
|
118
|
+
## OpenClaw MCP wiring
|
|
119
|
+
|
|
120
|
+
Register the stdio server in `~/.openclaw/openclaw.json`. Because this package
|
|
121
|
+
is not published, run the built file directly:
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"mcpServers": {
|
|
126
|
+
"atrium": {
|
|
127
|
+
"command": "node",
|
|
128
|
+
"args": ["/absolute/path/to/atrium/mcp/dist/server.js"],
|
|
129
|
+
"env": {
|
|
130
|
+
"OPENCLAW_WEBCHAT_API_BASE": "http://127.0.0.1:3213",
|
|
131
|
+
"OPENCLAW_WEBCHAT_API_KEY": "oc_live_..."
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
After publishing to a registry, the `npx` form works too:
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"mcpServers": {
|
|
143
|
+
"atrium": {
|
|
144
|
+
"command": "npx",
|
|
145
|
+
"args": ["-y", "atrium-mcp"],
|
|
146
|
+
"env": {
|
|
147
|
+
"OPENCLAW_WEBCHAT_API_BASE": "https://<deployment>.convex.site",
|
|
148
|
+
"OPENCLAW_WEBCHAT_API_KEY": "oc_live_..."
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Security
|
|
156
|
+
|
|
157
|
+
- The API key lives in env only — never committed, never logged, never in a URL.
|
|
158
|
+
- Stdio transport avoids the DNS-rebinding surface of local HTTP MCP servers.
|
|
159
|
+
- Permission scoping is enforced by the deployment, not the client: a key that
|
|
160
|
+
lacks a tool's permission gets a `403` that the tool surfaces as an error.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* atrium CLI — a thin shell client over the same /api/v1 calls the
|
|
4
|
+
* MCP server exposes. Same env-var auth (OPENCLAW_WEBCHAT_API_BASE +
|
|
5
|
+
* OPENCLAW_WEBCHAT_API_KEY); the Bearer key is only ever sent in the
|
|
6
|
+
* Authorization header (never echoed, never in a URL).
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* atrium health
|
|
10
|
+
* atrium traces [--limit N] [--q TEXT] [--from T] [--to T] [--kind K]
|
|
11
|
+
* [--status CODE] [--status-class 2xx|4xx|5xx] [--direction D]
|
|
12
|
+
* [--principal-type T] [--role-key K] [--correlation-id ID]
|
|
13
|
+
* atrium kpi [--metric M] [--since ISO] [--from T] [--to T]
|
|
14
|
+
* atrium anomalies [--limit N] [--since ISO] [--q TEXT] [--from T]
|
|
15
|
+
* [--to T] [--status S] [--severity S] [--source S] [--kind K]
|
|
16
|
+
* atrium query-openclaw [--question TEXT]
|
|
17
|
+
* atrium report-anomaly --kind K --severity S --message M [--correlation-id ID]
|
|
18
|
+
* atrium bridge-status
|
|
19
|
+
* atrium sync --instance NAME
|
|
20
|
+
*
|
|
21
|
+
* --from/--to accept epoch ms OR a Grafana relative token (e.g. now-24h, now).
|
|
22
|
+
*/
|
|
23
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* atrium CLI — a thin shell client over the same /api/v1 calls the
|
|
4
|
+
* MCP server exposes. Same env-var auth (OPENCLAW_WEBCHAT_API_BASE +
|
|
5
|
+
* OPENCLAW_WEBCHAT_API_KEY); the Bearer key is only ever sent in the
|
|
6
|
+
* Authorization header (never echoed, never in a URL).
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* atrium health
|
|
10
|
+
* atrium traces [--limit N] [--q TEXT] [--from T] [--to T] [--kind K]
|
|
11
|
+
* [--status CODE] [--status-class 2xx|4xx|5xx] [--direction D]
|
|
12
|
+
* [--principal-type T] [--role-key K] [--correlation-id ID]
|
|
13
|
+
* atrium kpi [--metric M] [--since ISO] [--from T] [--to T]
|
|
14
|
+
* atrium anomalies [--limit N] [--since ISO] [--q TEXT] [--from T]
|
|
15
|
+
* [--to T] [--status S] [--severity S] [--source S] [--kind K]
|
|
16
|
+
* atrium query-openclaw [--question TEXT]
|
|
17
|
+
* atrium report-anomaly --kind K --severity S --message M [--correlation-id ID]
|
|
18
|
+
* atrium bridge-status
|
|
19
|
+
* atrium sync --instance NAME
|
|
20
|
+
*
|
|
21
|
+
* --from/--to accept epoch ms OR a Grafana relative token (e.g. now-24h, now).
|
|
22
|
+
*/
|
|
23
|
+
import { ApiError, resolveConfig } from "./config.js";
|
|
24
|
+
import { bridgeStatus, getCompat, getKpi, getSchema, health, listAnomalies, listSchemas, listTraces, queryOpenClaw, reportAnomaly, syncInstance, } from "./tools.js";
|
|
25
|
+
/** Parse `--key value`, `--key=value`, and bare `--flag` arguments. */
|
|
26
|
+
function parseArgs(argv) {
|
|
27
|
+
const positionals = [];
|
|
28
|
+
const flags = {};
|
|
29
|
+
for (let i = 0; i < argv.length; i++) {
|
|
30
|
+
const token = argv[i];
|
|
31
|
+
if (token.startsWith("--")) {
|
|
32
|
+
const eq = token.indexOf("=");
|
|
33
|
+
if (eq !== -1) {
|
|
34
|
+
flags[token.slice(2, eq)] = token.slice(eq + 1);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const next = argv[i + 1];
|
|
38
|
+
if (next !== undefined && !next.startsWith("--")) {
|
|
39
|
+
flags[token.slice(2)] = next;
|
|
40
|
+
i++;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
flags[token.slice(2)] = true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
positionals.push(token);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { positionals, flags };
|
|
52
|
+
}
|
|
53
|
+
function num(value) {
|
|
54
|
+
if (typeof value !== "string")
|
|
55
|
+
return undefined;
|
|
56
|
+
const n = Number(value);
|
|
57
|
+
return Number.isFinite(n) ? n : undefined;
|
|
58
|
+
}
|
|
59
|
+
function str(value) {
|
|
60
|
+
return typeof value === "string" ? value : undefined;
|
|
61
|
+
}
|
|
62
|
+
const USAGE = `atrium — thin CLI over the /api/v1 observability surface
|
|
63
|
+
|
|
64
|
+
Commands:
|
|
65
|
+
health GET /health
|
|
66
|
+
compat GET /compat (bridge.read)
|
|
67
|
+
bridge-status GET /bridge-status (bridge.read)
|
|
68
|
+
schemas GET /schemas (public)
|
|
69
|
+
schema --id NAME GET /schemas/:id (public)
|
|
70
|
+
sync --instance NAME POST /instances/sync (selfheal)
|
|
71
|
+
traces [--limit N] [--q TEXT] [--from T] [--to T] [--kind K] [--status CODE]
|
|
72
|
+
[--status-class 2xx|4xx|5xx] [--direction D] [--principal-type T]
|
|
73
|
+
[--role-key K] [--correlation-id ID]
|
|
74
|
+
GET /traces (traces.read)
|
|
75
|
+
kpi [--metric M] [--since ISO] [--from T] [--to T]
|
|
76
|
+
GET /kpi (kpi.read)
|
|
77
|
+
anomalies [--limit N] [--since ISO] [--q TEXT] [--from T] [--to T]
|
|
78
|
+
[--status S] [--severity S] [--source S] [--kind K]
|
|
79
|
+
GET /anomalies (anomalies.read)
|
|
80
|
+
query-openclaw [--question TEXT] POST /openclaw/query (openclaw.query)
|
|
81
|
+
report-anomaly --kind K --severity info|warn|critical --message M [--correlation-id ID]
|
|
82
|
+
POST /anomalies (anomalies.report)
|
|
83
|
+
|
|
84
|
+
Filters:
|
|
85
|
+
--from / --to accept epoch ms (e.g. 1717372800000) OR a Grafana relative
|
|
86
|
+
token: now, or now-<N><unit> with unit s|m|h|d|w (e.g. --from now-24h --to now).
|
|
87
|
+
Example: traces --from now-24h --to now --status-class 4xx --kind api.call --q foo
|
|
88
|
+
|
|
89
|
+
Environment:
|
|
90
|
+
OPENCLAW_WEBCHAT_API_BASE deployment .site origin (default http://127.0.0.1:3213)
|
|
91
|
+
OPENCLAW_WEBCHAT_API_KEY oc_live_ Bearer key (required)`;
|
|
92
|
+
async function dispatch(config, command, flags) {
|
|
93
|
+
switch (command) {
|
|
94
|
+
case "health":
|
|
95
|
+
return health(config);
|
|
96
|
+
case "compat":
|
|
97
|
+
return getCompat(config);
|
|
98
|
+
case "bridge-status":
|
|
99
|
+
return bridgeStatus(config);
|
|
100
|
+
case "schemas":
|
|
101
|
+
return listSchemas(config);
|
|
102
|
+
case "schema": {
|
|
103
|
+
const id = str(flags.id);
|
|
104
|
+
if (!id) {
|
|
105
|
+
throw new Error("schema requires --id NAME (e.g. --id provenance.v1)");
|
|
106
|
+
}
|
|
107
|
+
return getSchema(config, { id });
|
|
108
|
+
}
|
|
109
|
+
case "sync": {
|
|
110
|
+
const instance = str(flags.instance);
|
|
111
|
+
if (!instance) {
|
|
112
|
+
throw new Error("sync requires --instance NAME");
|
|
113
|
+
}
|
|
114
|
+
return syncInstance(config, { instance });
|
|
115
|
+
}
|
|
116
|
+
case "traces": {
|
|
117
|
+
const statusClass = str(flags["status-class"]);
|
|
118
|
+
if (statusClass !== undefined &&
|
|
119
|
+
statusClass !== "2xx" &&
|
|
120
|
+
statusClass !== "4xx" &&
|
|
121
|
+
statusClass !== "5xx") {
|
|
122
|
+
throw new Error("--status-class must be one of 2xx|4xx|5xx");
|
|
123
|
+
}
|
|
124
|
+
return listTraces(config, {
|
|
125
|
+
limit: num(flags.limit),
|
|
126
|
+
q: str(flags.q),
|
|
127
|
+
from: str(flags.from),
|
|
128
|
+
to: str(flags.to),
|
|
129
|
+
kind: str(flags.kind),
|
|
130
|
+
status: num(flags.status),
|
|
131
|
+
statusClass,
|
|
132
|
+
direction: str(flags.direction),
|
|
133
|
+
principalType: str(flags["principal-type"]),
|
|
134
|
+
roleKey: str(flags["role-key"]),
|
|
135
|
+
correlationId: str(flags["correlation-id"]),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
case "kpi":
|
|
139
|
+
return getKpi(config, {
|
|
140
|
+
metric: str(flags.metric),
|
|
141
|
+
since: str(flags.since),
|
|
142
|
+
from: str(flags.from),
|
|
143
|
+
to: str(flags.to),
|
|
144
|
+
});
|
|
145
|
+
case "anomalies":
|
|
146
|
+
return listAnomalies(config, {
|
|
147
|
+
limit: num(flags.limit),
|
|
148
|
+
since: str(flags.since),
|
|
149
|
+
q: str(flags.q),
|
|
150
|
+
from: str(flags.from),
|
|
151
|
+
to: str(flags.to),
|
|
152
|
+
status: str(flags.status),
|
|
153
|
+
severity: str(flags.severity),
|
|
154
|
+
source: str(flags.source),
|
|
155
|
+
kind: str(flags.kind),
|
|
156
|
+
});
|
|
157
|
+
case "query-openclaw":
|
|
158
|
+
return queryOpenClaw(config, {
|
|
159
|
+
question: str(flags.question),
|
|
160
|
+
});
|
|
161
|
+
case "report-anomaly": {
|
|
162
|
+
const kind = str(flags.kind);
|
|
163
|
+
if (!kind) {
|
|
164
|
+
throw new Error("report-anomaly requires --kind");
|
|
165
|
+
}
|
|
166
|
+
const severity = str(flags.severity);
|
|
167
|
+
if (severity !== "info" && severity !== "warn" && severity !== "critical") {
|
|
168
|
+
throw new Error("report-anomaly requires --severity info|warn|critical");
|
|
169
|
+
}
|
|
170
|
+
const message = str(flags.message);
|
|
171
|
+
if (!message) {
|
|
172
|
+
throw new Error("report-anomaly requires --message");
|
|
173
|
+
}
|
|
174
|
+
return reportAnomaly(config, {
|
|
175
|
+
kind,
|
|
176
|
+
severity,
|
|
177
|
+
message,
|
|
178
|
+
correlationId: str(flags["correlation-id"]),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
default:
|
|
182
|
+
throw new Error(`Unknown command: ${command}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async function main() {
|
|
186
|
+
const { positionals, flags } = parseArgs(process.argv.slice(2));
|
|
187
|
+
const command = positionals[0];
|
|
188
|
+
if (!command || command === "help" || flags.help) {
|
|
189
|
+
process.stdout.write(`${USAGE}\n`);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
let config;
|
|
193
|
+
try {
|
|
194
|
+
config = resolveConfig();
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
// Config error (e.g. missing key). Message names the env var, not its value.
|
|
198
|
+
process.stderr.write(`${err.message}\n`);
|
|
199
|
+
process.exitCode = 2;
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
const result = await dispatch(config, command, flags);
|
|
204
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
if (err instanceof ApiError) {
|
|
208
|
+
const bodyText = typeof err.body === "string"
|
|
209
|
+
? err.body
|
|
210
|
+
: JSON.stringify(err.body, null, 2);
|
|
211
|
+
process.stderr.write(`API error ${err.status}: ${bodyText}\n`);
|
|
212
|
+
process.exitCode = 1;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
process.stderr.write(`${err.message}\n`);
|
|
216
|
+
process.exitCode = 1;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
void main();
|
|
220
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, testable HTTP client for the atrium /api/v1 surface.
|
|
3
|
+
*
|
|
4
|
+
* This module never imports anything from the Convex app — it only speaks HTTP.
|
|
5
|
+
* It reads two env vars:
|
|
6
|
+
* - OPENCLAW_WEBCHAT_API_BASE : the deployment `.site` origin, WITHOUT the
|
|
7
|
+
* `/api/v1` suffix (e.g. http://127.0.0.1:3213). The `/api/v1` prefix is
|
|
8
|
+
* added here so tool/CLI call-sites stay clean (`apiFetch(cfg, "/traces")`).
|
|
9
|
+
* - OPENCLAW_WEBCHAT_API_KEY : the `oc_live_...` Bearer key.
|
|
10
|
+
*
|
|
11
|
+
* The API key is only ever placed in the `Authorization` header — never in a
|
|
12
|
+
* URL/query string and never logged.
|
|
13
|
+
*/
|
|
14
|
+
/** Default base origin for a local Convex deployment (.site origin). */
|
|
15
|
+
export declare const DEFAULT_API_BASE = "http://127.0.0.1:3213";
|
|
16
|
+
/** Path prefix for the observability API. Kept here so call-sites omit it. */
|
|
17
|
+
export declare const API_PREFIX = "/api/v1";
|
|
18
|
+
export interface Config {
|
|
19
|
+
/** Base origin (no trailing slash, no /api/v1). */
|
|
20
|
+
base: string;
|
|
21
|
+
/** The oc_live_ Bearer key. */
|
|
22
|
+
apiKey: string;
|
|
23
|
+
}
|
|
24
|
+
/** Minimal env shape so the resolver is injectable/testable. */
|
|
25
|
+
export type Env = Record<string, string | undefined>;
|
|
26
|
+
/**
|
|
27
|
+
* Structured error for any non-2xx response (or a fetch/transport failure).
|
|
28
|
+
* Carries the HTTP `status` and the parsed/raw `body` so callers can surface a
|
|
29
|
+
* clear message without re-reading the response. Never contains the API key.
|
|
30
|
+
*/
|
|
31
|
+
export declare class ApiError extends Error {
|
|
32
|
+
readonly status: number;
|
|
33
|
+
readonly body: unknown;
|
|
34
|
+
constructor(status: number, body: unknown, message?: string);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Resolve config from an env bag (defaults to `process.env`). Throws a clear
|
|
38
|
+
* error naming the missing variable — without ever printing its value.
|
|
39
|
+
*/
|
|
40
|
+
export declare function resolveConfig(env?: Env): Config;
|
|
41
|
+
/** Build the absolute URL for an API path (prepending `${base}/api/v1`). */
|
|
42
|
+
export declare function buildUrl(base: string, path: string): string;
|
|
43
|
+
/** Options for {@link apiFetch}. */
|
|
44
|
+
export interface ApiFetchOptions {
|
|
45
|
+
/** Injected fetch implementation. Defaults to the global `fetch`. */
|
|
46
|
+
fetchImpl?: typeof fetch;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Call the API at `path` (relative to `${base}/api/v1`), attaching the Bearer
|
|
50
|
+
* header, and parse the JSON response.
|
|
51
|
+
*
|
|
52
|
+
* - 2xx -> returns the parsed JSON (or `null` for an empty body).
|
|
53
|
+
* - non-2xx -> throws {@link ApiError} with `{status, body}` (body parsed as
|
|
54
|
+
* JSON when possible, otherwise the raw text).
|
|
55
|
+
* - transport failure -> throws {@link ApiError} with status 0.
|
|
56
|
+
*
|
|
57
|
+
* `fetch` is injectable for unit testing; no network call happens in tests.
|
|
58
|
+
*/
|
|
59
|
+
export declare function apiFetch<T = unknown>(config: Config, path: string, init?: RequestInit, options?: ApiFetchOptions): Promise<T>;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, testable HTTP client for the atrium /api/v1 surface.
|
|
3
|
+
*
|
|
4
|
+
* This module never imports anything from the Convex app — it only speaks HTTP.
|
|
5
|
+
* It reads two env vars:
|
|
6
|
+
* - OPENCLAW_WEBCHAT_API_BASE : the deployment `.site` origin, WITHOUT the
|
|
7
|
+
* `/api/v1` suffix (e.g. http://127.0.0.1:3213). The `/api/v1` prefix is
|
|
8
|
+
* added here so tool/CLI call-sites stay clean (`apiFetch(cfg, "/traces")`).
|
|
9
|
+
* - OPENCLAW_WEBCHAT_API_KEY : the `oc_live_...` Bearer key.
|
|
10
|
+
*
|
|
11
|
+
* The API key is only ever placed in the `Authorization` header — never in a
|
|
12
|
+
* URL/query string and never logged.
|
|
13
|
+
*/
|
|
14
|
+
/** Default base origin for a local Convex deployment (.site origin). */
|
|
15
|
+
export const DEFAULT_API_BASE = "http://127.0.0.1:3213";
|
|
16
|
+
/** Path prefix for the observability API. Kept here so call-sites omit it. */
|
|
17
|
+
export const API_PREFIX = "/api/v1";
|
|
18
|
+
/**
|
|
19
|
+
* Structured error for any non-2xx response (or a fetch/transport failure).
|
|
20
|
+
* Carries the HTTP `status` and the parsed/raw `body` so callers can surface a
|
|
21
|
+
* clear message without re-reading the response. Never contains the API key.
|
|
22
|
+
*/
|
|
23
|
+
export class ApiError extends Error {
|
|
24
|
+
status;
|
|
25
|
+
body;
|
|
26
|
+
constructor(status, body, message) {
|
|
27
|
+
super(message ?? `API request failed with status ${status}`);
|
|
28
|
+
this.name = "ApiError";
|
|
29
|
+
this.status = status;
|
|
30
|
+
this.body = body;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolve config from an env bag (defaults to `process.env`). Throws a clear
|
|
35
|
+
* error naming the missing variable — without ever printing its value.
|
|
36
|
+
*/
|
|
37
|
+
export function resolveConfig(env = process.env) {
|
|
38
|
+
const base = (env.OPENCLAW_WEBCHAT_API_BASE ?? DEFAULT_API_BASE).replace(/\/+$/, "");
|
|
39
|
+
const apiKey = env.OPENCLAW_WEBCHAT_API_KEY;
|
|
40
|
+
if (!apiKey) {
|
|
41
|
+
throw new Error("OPENCLAW_WEBCHAT_API_KEY is required (set it in the environment).");
|
|
42
|
+
}
|
|
43
|
+
return { base, apiKey };
|
|
44
|
+
}
|
|
45
|
+
/** Build the absolute URL for an API path (prepending `${base}/api/v1`). */
|
|
46
|
+
export function buildUrl(base, path) {
|
|
47
|
+
const normalizedBase = base.replace(/\/+$/, "");
|
|
48
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
49
|
+
return `${normalizedBase}${API_PREFIX}${normalizedPath}`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Call the API at `path` (relative to `${base}/api/v1`), attaching the Bearer
|
|
53
|
+
* header, and parse the JSON response.
|
|
54
|
+
*
|
|
55
|
+
* - 2xx -> returns the parsed JSON (or `null` for an empty body).
|
|
56
|
+
* - non-2xx -> throws {@link ApiError} with `{status, body}` (body parsed as
|
|
57
|
+
* JSON when possible, otherwise the raw text).
|
|
58
|
+
* - transport failure -> throws {@link ApiError} with status 0.
|
|
59
|
+
*
|
|
60
|
+
* `fetch` is injectable for unit testing; no network call happens in tests.
|
|
61
|
+
*/
|
|
62
|
+
export async function apiFetch(config, path, init = {}, options = {}) {
|
|
63
|
+
const doFetch = options.fetchImpl ?? globalThis.fetch;
|
|
64
|
+
if (typeof doFetch !== "function") {
|
|
65
|
+
throw new ApiError(0, null, "No fetch implementation available (Node >=18 or inject fetchImpl).");
|
|
66
|
+
}
|
|
67
|
+
const url = buildUrl(config.base, path);
|
|
68
|
+
const headers = new Headers(init.headers);
|
|
69
|
+
headers.set("Authorization", `Bearer ${config.apiKey}`);
|
|
70
|
+
headers.set("Accept", "application/json");
|
|
71
|
+
if (init.body !== undefined && !headers.has("Content-Type")) {
|
|
72
|
+
headers.set("Content-Type", "application/json");
|
|
73
|
+
}
|
|
74
|
+
let res;
|
|
75
|
+
try {
|
|
76
|
+
res = await doFetch(url, { ...init, headers });
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
// Transport-level failure (DNS, connection refused, etc.). Surface it as a
|
|
80
|
+
// structured error with status 0 so callers handle it uniformly. Never
|
|
81
|
+
// include the request headers (which hold the key) in the message.
|
|
82
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
83
|
+
throw new ApiError(0, null, `Network error reaching API: ${message}`);
|
|
84
|
+
}
|
|
85
|
+
const raw = await res.text();
|
|
86
|
+
const body = parseJsonSafe(raw);
|
|
87
|
+
if (!res.ok) {
|
|
88
|
+
throw new ApiError(res.status, body, `API ${res.status} ${res.statusText} for ${path}`);
|
|
89
|
+
}
|
|
90
|
+
return body;
|
|
91
|
+
}
|
|
92
|
+
/** Parse a string as JSON; fall back to the raw string when it isn't JSON. */
|
|
93
|
+
function parseJsonSafe(raw) {
|
|
94
|
+
if (raw.length === 0)
|
|
95
|
+
return null;
|
|
96
|
+
try {
|
|
97
|
+
return JSON.parse(raw);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return raw;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* atrium MCP server (stdio).
|
|
4
|
+
*
|
|
5
|
+
* A thin proxy over our /api/v1 observability surface. It carries an `oc_live_`
|
|
6
|
+
* Bearer key (from OPENCLAW_WEBCHAT_API_KEY) against the deployment `.site`
|
|
7
|
+
* origin (OPENCLAW_WEBCHAT_API_BASE) and exposes traces/KPIs/OpenClaw queries/
|
|
8
|
+
* anomalies as MCP tools for OpenClaw agents.
|
|
9
|
+
*
|
|
10
|
+
* It imports NOTHING from the Convex app — HTTP only. Each tool maps 1:1 to a
|
|
11
|
+
* permission enforced server-side (`requirePermission`), so a scoped key simply
|
|
12
|
+
* gets a 403 for tools it isn't allowed to call. A tool whose route is not yet
|
|
13
|
+
* deployed returns the API's response/error gracefully rather than crashing.
|
|
14
|
+
*/
|
|
15
|
+
export {};
|