lattice-mcp 1.0.1 → 1.1.1

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.
Files changed (4) hide show
  1. package/AGENTS.md +245 -0
  2. package/README.md +220 -32
  3. package/index.js +791 -3
  4. package/package.json +3 -2
package/AGENTS.md ADDED
@@ -0,0 +1,245 @@
1
+ # AGENTS.md — lattice-mcp
2
+
3
+ > `lattice-mcp` is the **Model Context Protocol server for Lattice**, the container
4
+ > orchestration platform that runs every `appleby.cloud` service. It exposes the
5
+ > `lattice-api` admin surface to Claude Code as **125 typed tools** — workers, stacks,
6
+ > containers, deployments, databases, registries, networks, volumes and instance config.
7
+ > This file orients any agent/worker before touching code in this repo.
8
+ >
9
+ > **⚠️ Golden rule — keep this file current:** any change that adds, removes or retypes a
10
+ > tool, changes the auth model, or drifts from `lattice-api`'s route surface MUST update this
11
+ > AGENTS.md in the SAME change. Stale context here misleads every future agent. If you finish
12
+ > work and haven't touched AGENTS.md, confirm that's actually correct.
13
+
14
+ ---
15
+
16
+ ## What this repo is
17
+
18
+ A single-file Node ESM program (`index.js`, ~1,180 lines) that speaks MCP over stdio and
19
+ translates tool calls into HTTP requests against `lattice-api`. It is published to npm as
20
+ `lattice-mcp` and consumed via `npx -y lattice-mcp` from `~/.mcp.json`.
21
+
22
+ It owns **only the translation layer**: tool names, argument schemas, descriptions, and URL
23
+ construction. It holds no business logic, no caching, and no state. Every behaviour an agent
24
+ observes — pagination limits, validation messages, side effects — comes from `lattice-api`.
25
+
26
+ It does **not** own: the Lattice data model, deployment mechanics, or the worker protocol.
27
+ Those live in [`lattice-api`](https://github.com/aidenappl/lattice-api) and
28
+ [`lattice-runner`](https://github.com/aidenappl/lattice-runner).
29
+
30
+ ## Stack & dependencies
31
+
32
+ - **Runtime:** Node ≥18 (needs global `fetch` and `AbortSignal.timeout`). `"type": "module"` —
33
+ ESM only, top-level `await` is used at the bottom of `index.js`.
34
+ - **`@modelcontextprotocol/sdk` ^1.29.0** — `McpServer` + `StdioServerTransport`.
35
+ - **`zod` ^4.4.3** — argument schemas. Declared explicitly as of **1.1.1** (it was previously
36
+ only resolved transitively through the MCP SDK, a latent fragility); the range matches the
37
+ version the SDK resolves.
38
+ - No build step, no bundler, no tests, no lint config. `node --check index.js` is the only
39
+ static gate.
40
+
41
+ ## Project structure
42
+
43
+ | Path | Role |
44
+ |------|------|
45
+ | `index.js` | Everything: `--setup` flow, config read, `api()` HTTP helper, `text()`/`body()` helpers, all 125 `server.tool(...)` registrations, transport connect. |
46
+ | `package.json` | npm metadata. `bin.lattice-mcp` → `index.js`, so `npx lattice-mcp` works. |
47
+ | `README.md` | User-facing setup + full tool table. |
48
+ | `AGENTS.md` | This file. |
49
+
50
+ `index.js` is organised top-to-bottom as: setup block → config/guard → helpers → tools grouped
51
+ by domain under `// ───` banner comments → transport. **Keep new tools inside the matching
52
+ banner group**; do not append to the bottom.
53
+
54
+ ## Running, building & testing
55
+
56
+ There is no `Devfile.yaml` and no `dev` CLI wiring here — it is a single script.
57
+
58
+ ```bash
59
+ node --check index.js # syntax gate — the only static check that exists
60
+ npm install # needed before running locally (deps are not vendored)
61
+ node index.js --setup # interactive: writes the lattice block into ~/.mcp.json
62
+ LATTICE_API_URL=... LATTICE_API_TOKEN=... node index.js # run the server on stdio
63
+ ```
64
+
65
+ **Smoke-testing without an MCP client.** The server speaks JSON-RPC over stdio, so you can
66
+ drive it with a shell pipeline. This is the standard way to verify a change registers cleanly:
67
+
68
+ ```bash
69
+ { echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"probe","version":"1"}}}'
70
+ sleep 2
71
+ echo '{"jsonrpc":"2.0","method":"notifications/initialized"}'
72
+ echo '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
73
+ sleep 2; } | LATTICE_API_URL=x LATTICE_API_TOKEN=x node index.js
74
+ ```
75
+
76
+ The `sleep`s matter — without them the requests race the handshake and you get nothing back.
77
+ For a live call, swap `tools/list` for
78
+ `{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"lattice_get_anomalies","arguments":{}}}`
79
+ and supply the real token.
80
+
81
+ ## How code is written here
82
+
83
+ - **Every tool follows one shape.** Deviating makes the file harder to scan:
84
+ ```js
85
+ server.tool("lattice_<verb>_<noun>", "<description>", { /* zod schema */ }, async (args) => {
86
+ const res = await api("<METHOD>", `/admin/<path>`, params, body);
87
+ return { content: text(res) };
88
+ });
89
+ ```
90
+ - **Naming:** `lattice_` prefix on every tool, then `list|get|create|update|delete|<verb>` then
91
+ the noun (`lattice_list_database_instances`). The prefix is what disambiguates these from
92
+ `monitor_*` and `forta_*` tools in a shared client.
93
+ - **`api(method, path, params, body)`** — `params` become query string entries (undefined/null
94
+ are dropped), `body` is JSON-encoded. Failures are returned as tool output, never thrown, so
95
+ they surface to the agent rather than crashing the transport. A network-level failure returns
96
+ `{success:false, error_message: "API request failed: …"}`; a response that arrives but is not
97
+ JSON (e.g. a 502/504 HTML page from the TLS proxy) returns
98
+ `{success:false, error_code: <http status>, error_message: "API returned a non-JSON response …"}`
99
+ with a truncated body — bodies are never logged, only returned, since they can carry secrets.
100
+ - **`body(obj)`** strips `undefined` keys. **Always use it on PUT/PATCH tools.** The API treats
101
+ a present-but-null field as an explicit clear, so forwarding raw `{...fields}` on an update
102
+ would wipe every field the caller didn't pass.
103
+ - **Path params are template literals**, query params go through the `params` argument. Never
104
+ hand-concatenate a query string.
105
+ - **Descriptions are the contract.** An agent picks tools from the description alone, so it
106
+ must state what the tool does, when to reach for it, and — for anything destructive — the
107
+ blast radius. Compare `lattice_stop_container` ("stops it") with `lattice_delete_container`
108
+ ("Destructive — lattice_stop_container only stops it").
109
+ - **Read the handler before adding a tool.** Struct field names are not the request contract;
110
+ handlers frequently override or ignore them. Two live examples from `lattice-api`:
111
+ `HandleUpdateCompose` has a `containerConfigFingerprint` helper with short JSON keys
112
+ (`i`, `t`, `pm`) that is **not** the request body — the body is just `{compose_yaml}`; and
113
+ `HandleDatabaseAction` derives its action from the **last URL path segment**, not from a body
114
+ field.
115
+
116
+ ## Domain & architecture
117
+
118
+ **Auth.** A single long-lived bearer token (`LATTICE_API_TOKEN`) from
119
+ `lattice-api`'s `/admin/api-tokens`, sent on every request. There is no refresh, no expiry
120
+ handling, and no login flow. A 401 means the token was revoked or expired — the fix is a new
121
+ token via `--setup`, not a code change.
122
+
123
+ **Config.** Read once at startup from `LATTICE_API_URL` and `LATTICE_API_TOKEN`; the process
124
+ exits immediately if either is missing. In practice these come from the `env` block of the
125
+ `lattice` entry in `~/.mcp.json`.
126
+
127
+ **Tool groups**, in file order:
128
+
129
+ The counts below sum to **125**, matching the header and `grep -c 'server.tool(' index.js`. The
130
+ first six rows are the original, pre-`1.1.0` tools (registered top-of-file with no banner comment);
131
+ every bolded row corresponds to a `// ───` banner group and matches its exact in-file name.
132
+
133
+ | Group | Tools | Notes |
134
+ |-------|-------|-------|
135
+ | Overview & health | 2 | `lattice_overview`, `lattice_health` |
136
+ | Workers | 3 + 4 actions | list/get/metrics; reboot, upgrade, stop-all, start-all |
137
+ | Stacks | 2 + 5 actions | list/get; deploy, restart, stop, start, update |
138
+ | Containers | 4 + 8 actions | list/get/logs/lifecycle; start, stop, restart, kill, pause, unpause, remove, recreate. (`lattice_get_container_metrics` is *not* here — it lives under **Discovery & diagnostics**.) |
139
+ | Deployments | 4 | list/get/logs, rollback. (`lattice_approve_deployment` is *not* here — it lives under **Stacks — lifecycle, compose & deploy tokens**.) |
140
+ | Instance self-update | 2 | `lattice_update_api`, `lattice_update_web` — tell the API/web container to pull its latest image and redeploy itself |
141
+ | Audit & API tokens | 4 | `lattice_get_audit_log`; API token list/create/delete |
142
+ | **Database instances** | **11** | CRUD, `lattice_database_action` (start/stop/restart/remove enum), `lattice_get_database_credentials`, snapshot list/create/restore/delete |
143
+ | **Backup destinations** | **6** | list/get/create/update/delete + `lattice_test_backup_destination` |
144
+ | **Registries** | **8** | list/create/update/delete, `lattice_test_registry`, `lattice_test_registry_inline`, `lattice_list_registry_repositories`, `lattice_list_registry_tags` |
145
+ | **Discovery & diagnostics** | **7** | `lattice_search`, `lattice_get_anomalies`, `lattice_get_fleet_metrics`, `lattice_get_versions`, `lattice_refresh_versions`, `lattice_get_container_metrics`, `lattice_get_self` |
146
+ | **Stacks — lifecycle, compose & deploy tokens** | **13** | create/delete, `lattice_get_stack_containers`, compose update/sync/import, export/import, `lattice_save_stack_as_template`, deploy-token list/create/delete, `lattice_approve_deployment` |
147
+ | **Containers — definition CRUD** | **3** | `lattice_create_container`, `lattice_update_container`, `lattice_delete_container` |
148
+ | **Workers — registration, tokens, volumes, networks** | **16** | worker create/update/delete, `lattice_get_worker_container_stats`, worker-token ×3, volume ×3, worker-network ×3, `lattice_list_all_networks`, `lattice_delete_network`, `lattice_force_remove_container` |
149
+ | **Global env vars, templates, webhooks** | **12** | env-var CRUD (4), template list/create/delete (3), webhook list/create/update/delete/test (5) |
150
+ | **Users & instance configuration** | **11** | user CRUD (4); SSO get/update (2); SMTP get/update/test (3); notification-prefs get/update (2) |
151
+
152
+ Bolded groups were added in **1.1.0**, closing a gap where the MCP had drifted roughly two
153
+ months behind `lattice-api` — database instances shipped in the API in May 2026 and were
154
+ entirely unreachable from the MCP until then.
155
+
156
+ **1.1.1** is a bug-fix release (no tool count change). It corrects three request-shape/route bugs
157
+ verified against `lattice-api` handlers: `lattice_get_self` now calls `GET /auth/self` (the old
158
+ `GET /admin/self` route never existed and always 404'd — the endpoint is `HandleAuthSelf` on the
159
+ bearer-authed `/auth` subrouter); `lattice_force_remove_container` now sends `{container_name}` in
160
+ the JSON body (`HandleForceRemoveContainer` reads it there, not from query params, so the old call
161
+ always 400'd); and `lattice_test_backup_destination` now marks `worker_id` as **required** (a query
162
+ param the handler 400s without — the test dispatches over the worker's WebSocket). It also hardens
163
+ `api()` against non-JSON responses (see *How code is written here*) and declares `zod` explicitly.
164
+
165
+ **Consolidations.** Where `lattice-api` exposes several paths served by one handler, this repo
166
+ exposes one tool with an enum rather than N tools. `lattice_database_action` covers
167
+ `/start`, `/stop`, `/restart` and `/remove`. Worker actions are the historical exception — they
168
+ predate this convention and remain separate tools.
169
+
170
+ ## Ecosystem & related repos
171
+
172
+ | Repo | Relationship |
173
+ |------|--------------|
174
+ | [`lattice-api`](https://github.com/aidenappl/lattice-api) | The API this wraps. Its `main.go` route table is the source of truth for coverage. |
175
+ | [`lattice-web`](https://github.com/aidenappl/lattice-web) | Next.js dashboard over the same API. |
176
+ | [`lattice-runner`](https://github.com/aidenappl/lattice-runner) | Agent on each worker VM; WebSocket back to `lattice-api`. |
177
+ | [`monitor-mcp`](https://github.com/aidenappl/monitor-mcp) | Sibling MCP, same single-file structure — keep them stylistically aligned. |
178
+ | `forta-mcp` / `keyring-mcp` / `openbucket-mcp` | Newer siblings; they carry a `body()` helper and destructive-blast-radius descriptions that originated here. |
179
+
180
+ ## Operations
181
+
182
+ - **Published to npm** as `lattice-mcp` (public). Consumers run `npx -y lattice-mcp`, which
183
+ resolves the latest published version — so **publishing is deployment**. A bug shipped to npm
184
+ reaches every user on their next MCP server start.
185
+ - **Publishing requires 2FA via passkey.** `npm publish` must run from an interactive terminal:
186
+ npm's web auth flow needs to open a browser, and from a non-TTY subprocess it degrades to
187
+ demanding an OTP that a passkey-only account cannot produce.
188
+ - **In-session staleness:** a running MCP server process does not pick up a new npm version.
189
+ After publishing, the client must be restarted before the new tools/schemas appear.
190
+ - **Common failure modes:**
191
+ - *All tools return `API request failed: fetch failed`* — `LATTICE_API_URL` is wrong or the
192
+ TLS proxy in front of Lattice has an expired cert.
193
+ - *All tools return 401* — token revoked or expired; re-run `--setup`.
194
+ - *One tool 404s while others work* — the MCP is ahead of the deployed `lattice-api`, or the
195
+ route moved.
196
+
197
+ ## Rules & guardrails
198
+
199
+ - **Never hardcode a token, URL or hostname.** Everything comes from env.
200
+ - **Never log request or response bodies.** Responses routinely contain env vars, registry
201
+ credentials and database passwords. `lattice_get_database_credentials` returns live secrets
202
+ by design — do not add convenience logging anywhere in `api()`.
203
+ - **Never add a tool without reading its handler in `lattice-api`.** Inferring a request shape
204
+ from a struct has produced real, shipped bugs across this family of servers.
205
+ - **Do not break tool names.** They are a public contract: renaming one silently breaks any
206
+ saved workflow or prompt that referenced it. Add a new tool and deprecate in the description
207
+ instead.
208
+ - **`zod` is declared explicitly** in `package.json` (`^4.4.3`) as of **1.1.1**. It used to
209
+ resolve only transitively through the MCP SDK, which meant a SDK change that dropped or hoisted
210
+ it differently would break every tool schema at startup. Keep the declared range aligned with
211
+ the version the SDK actually resolves (check `package-lock.json`).
212
+ - **Keep destructive descriptions honest.** If a tool destroys data, the description must say so
213
+ and name the safer alternative.
214
+ - Publishing is outward-facing and effectively irreversible (npm unpublish is restricted after
215
+ 72 hours) — do not publish without explicit instruction.
216
+
217
+ ## Verification — always before "done"
218
+
219
+ ```bash
220
+ node --check index.js # must pass
221
+ grep -c 'server.tool(' index.js # tool count matches what you expect
222
+ ```
223
+
224
+ Then the stdio handshake from *Running, building & testing* above, asserting:
225
+ - the server registers **without stderr output**,
226
+ - the tool count is what you expect,
227
+ - **no duplicate tool names** (`server.tool` silently accepts a duplicate; the last registration
228
+ wins and the earlier tool disappears — this will not error).
229
+
230
+ For any tool you added or changed, make **one real call against the live API** and confirm the
231
+ response shape. Schema-only verification is not enough: it catches typos, not wrong units,
232
+ wrong enum values, or parameters the handler ignores.
233
+
234
+ **Never report work complete on the strength of `tools/list` alone.**
235
+
236
+ ## Keeping this file updated
237
+
238
+ Update this AGENTS.md in the same change when you:
239
+ - **Add/remove/rename a tool** → update the tool-group table and the count in the header.
240
+ - **Change the auth model or config vars** → update *Domain & architecture*.
241
+ - **Change the `api()`/`body()`/`text()` helpers** → update *How code is written here*.
242
+ - **Bump the version or publish** → note behavioural changes under the relevant group.
243
+ - **Notice `lattice-api` has gained routes** → either add the tools or record the gap here
244
+ explicitly, so the next agent knows it was a decision and not an oversight.
245
+ - Also keep `README.md`'s tool tables in sync — it is the user-facing surface and drifts fastest.
package/README.md CHANGED
@@ -1,18 +1,55 @@
1
1
  # lattice-mcp
2
2
 
3
- MCP server for the [Lattice](https://github.com/aidenappl/lattice-api) container orchestration platform. Gives Claude Code direct access to manage workers, stacks, containers, and deployments.
3
+ Model Context Protocol server for [Lattice](https://github.com/aidenappl/lattice-api), the container orchestration platform that runs every `appleby.cloud` service. Gives Claude Code direct, typed access to workers, stacks, containers, deployments, databases, registries, networks, volumes and instance configuration.
4
4
 
5
- ## Quick Start
5
+ > **appleby.cloud platform** · MCP server · published to npm as `lattice-mcp` · consumed via `npx -y lattice-mcp`
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ `lattice-mcp` is a single-file Node ESM program (`index.js`) that speaks MCP over stdio and translates tool calls into HTTP requests against the `lattice-api` admin surface. It exposes **125 typed tools** and holds no business logic, caching or state of its own — every behaviour (pagination, validation, side effects) comes from `lattice-api`.
12
+
13
+ Once configured, ask Claude Code things like:
14
+
15
+ - "What's the status of all stacks?"
16
+ - "Show me logs for the forta-api container"
17
+ - "Which containers are unhealthy?" (`lattice_get_anomalies` is the best first call)
18
+ - "Deploy stack 5" / "Rollback the last deployment on stack 12"
19
+ - "What image tags can I deploy from the registry?"
20
+
21
+ ## Role in the appleby.cloud ecosystem
22
+
23
+ | Repo | Relationship |
24
+ |------|--------------|
25
+ | [`lattice-api`](https://github.com/aidenappl/lattice-api) | The API this wraps — its route table is the source of truth for tool coverage. |
26
+ | [`lattice-web`](https://github.com/aidenappl/lattice-web) | Next.js dashboard over the same API. |
27
+ | [`lattice-runner`](https://github.com/aidenappl/lattice-runner) | Agent on each worker VM; WebSocket back to `lattice-api`. |
28
+ | `monitor-mcp` / `forta-mcp` / `keyring-mcp` / `openbucket-mcp` | Sibling MCP servers, same single-file structure. |
29
+
30
+ ## Tech stack
31
+
32
+ - **Node ≥18** (needs global `fetch` and `AbortSignal.timeout`), ESM (`"type": "module"`).
33
+ - **`@modelcontextprotocol/sdk` ^1.29.0** — `McpServer` + `StdioServerTransport`.
34
+ - **`zod` ^4.4.3** for argument schemas (a declared dependency as of 1.1.1).
35
+ - No build step, no bundler. `node --check index.js` is the only static gate.
36
+
37
+ ## Getting started
38
+
39
+ ### Prerequisites
40
+
41
+ - Node ≥18.
42
+ - A Lattice API URL and API token. Generate a token from the Lattice web dashboard under **Settings > API Tokens**.
43
+
44
+ ### Setup
45
+
46
+ Quickest — interactive setup writes the `lattice` block into `~/.mcp.json`:
6
47
 
7
48
  ```bash
8
49
  npx lattice-mcp --setup
9
50
  ```
10
51
 
11
- This prompts for your Lattice API URL and API token, writes the config to `~/.mcp.json`, and you're ready to go. Restart Claude Code after setup.
12
-
13
- ## Manual Setup
14
-
15
- Add to `~/.mcp.json`:
52
+ Or configure it manually in `~/.mcp.json`:
16
53
 
17
54
  ```json
18
55
  {
@@ -29,21 +66,41 @@ Add to `~/.mcp.json`:
29
66
  }
30
67
  ```
31
68
 
32
- Generate an API token from the Lattice web dashboard under **Settings > API Tokens**.
69
+ Restart Claude Code after setup so the new server and tools are picked up.
70
+
71
+ ### Environment variables
72
+
73
+ | Variable | Required | Description |
74
+ |----------|----------|-------------|
75
+ | `LATTICE_API_URL` | Yes | Lattice API base URL |
76
+ | `LATTICE_API_TOKEN` | Yes | Bearer token for authentication (sent on every request) |
77
+
78
+ ## Development
79
+
80
+ | Command | What it does |
81
+ |---------|--------------|
82
+ | `node index.js --setup` | Interactive setup — writes the `lattice` block into `~/.mcp.json` |
83
+ | `npm install` | Install dependencies (not vendored) |
84
+ | `node --check index.js` | Syntax gate — the only static check that exists |
85
+ | `LATTICE_API_URL=… LATTICE_API_TOKEN=… node index.js` | Run the server on stdio |
86
+ | `grep -c 'server.tool(' index.js` | Confirm the tool count (should be 125) |
87
+ | `npm publish` | Publish to npm — **this is deployment** (requires 2FA passkey from an interactive terminal) |
33
88
 
34
89
  ## Tools
35
90
 
36
- ### Overview & Health
91
+ All 125 tools, grouped as they appear in `index.js`. ⚠️ marks destructive tools; their descriptions state the blast radius.
92
+
93
+ ### Overview & health
37
94
  | Tool | Description |
38
95
  |------|-------------|
39
- | `lattice_overview` | Fleet overview — worker counts, stack counts, failed stacks, CPU/memory |
96
+ | `lattice_overview` | Fleet overview — worker/stack/container counts, failed stacks, CPU/memory |
40
97
  | `lattice_health` | API health and database connectivity |
41
98
 
42
99
  ### Workers
43
100
  | Tool | Description |
44
101
  |------|-------------|
45
- | `lattice_list_workers` | List workers with status, IP, versions |
46
- | `lattice_get_worker` | Detailed worker info |
102
+ | `lattice_list_workers` | List workers with status, IP, versions, heartbeat |
103
+ | `lattice_get_worker` | Detailed worker info including metrics |
47
104
  | `lattice_get_worker_metrics` | CPU, memory, disk, network metrics |
48
105
  | `lattice_reboot_worker` | Reboot a worker machine |
49
106
  | `lattice_upgrade_worker` | Upgrade worker runner to latest |
@@ -55,11 +112,11 @@ Generate an API token from the Lattice web dashboard under **Settings > API Toke
55
112
  |------|-------------|
56
113
  | `lattice_list_stacks` | List stacks with status and worker assignment |
57
114
  | `lattice_get_stack` | Full stack details including compose YAML |
58
- | `lattice_update_stack` | Update stack configuration |
59
115
  | `lattice_deploy_stack` | Deploy a stack (all or specific containers) |
60
116
  | `lattice_restart_stack` | Restart all containers in a stack |
61
117
  | `lattice_stop_stack` | Stop all containers in a stack |
62
118
  | `lattice_start_stack` | Start all containers in a stack |
119
+ | `lattice_update_stack` | Update stack configuration |
63
120
 
64
121
  ### Containers
65
122
  | Tool | Description |
@@ -74,8 +131,8 @@ Generate an API token from the Lattice web dashboard under **Settings > API Toke
74
131
  | `lattice_kill_container` | Force kill a container |
75
132
  | `lattice_pause_container` | Pause a running container |
76
133
  | `lattice_unpause_container` | Unpause a paused container |
77
- | `lattice_remove_container` | Remove a container |
78
- | `lattice_recreate_container` | Remove and recreate a container |
134
+ | `lattice_remove_container` | Remove a container ⚠️ |
135
+ | `lattice_recreate_container` | Remove and recreate a container ⚠️ |
79
136
 
80
137
  ### Deployments
81
138
  | Tool | Description |
@@ -83,32 +140,163 @@ Generate an API token from the Lattice web dashboard under **Settings > API Toke
83
140
  | `lattice_list_deployments` | List deployments with status and timing |
84
141
  | `lattice_get_deployment` | Deployment details with container-level status |
85
142
  | `lattice_get_deployment_logs` | Pull, create, start, swap events with timing |
86
- | `lattice_rollback_deployment` | Rollback to previous state |
143
+ | `lattice_rollback_deployment` | Rollback to previous state ⚠️ |
144
+
145
+ ### Instance self-update
146
+ | Tool | Description |
147
+ |------|-------------|
148
+ | `lattice_update_api` | Trigger the Lattice API container to self-update |
149
+ | `lattice_update_web` | Trigger the Lattice web container to update |
87
150
 
88
- ### System
151
+ ### Audit & API tokens
89
152
  | Tool | Description |
90
153
  |------|-------------|
91
- | `lattice_get_audit_log` | Recent audit log entries |
92
- | `lattice_update_api` | Trigger API self-update |
93
- | `lattice_update_web` | Trigger web container update |
154
+ | `lattice_get_audit_log` | Recent audit log entries (who did what, when) |
94
155
  | `lattice_list_api_tokens` | List API tokens |
95
156
  | `lattice_create_api_token` | Create a new API token |
96
- | `lattice_delete_api_token` | Delete an API token |
157
+ | `lattice_delete_api_token` | Delete an API token ⚠️ |
158
+
159
+ ### Database instances
160
+ | Tool | Description |
161
+ |------|-------------|
162
+ | `lattice_list_database_instances` | List managed databases (filter by worker, engine, status) |
163
+ | `lattice_get_database_instance` | Full instance config |
164
+ | `lattice_create_database_instance` | Provision mysql/mariadb/postgres on a worker |
165
+ | `lattice_update_database_instance` | Update config, limits, snapshot schedule |
166
+ | `lattice_delete_database_instance` | Delete an instance ⚠️ |
167
+ | `lattice_database_action` | start / stop / restart / remove ⚠️ |
168
+ | `lattice_get_database_credentials` | Connection credentials (returns secrets) |
169
+ | `lattice_list_database_snapshots` | Snapshots for an instance |
170
+ | `lattice_create_database_snapshot` | Take a snapshot now |
171
+ | `lattice_restore_database_snapshot` | Restore from a snapshot ⚠️ |
172
+ | `lattice_delete_database_snapshot` | Delete a snapshot ⚠️ |
97
173
 
98
- ## Example Prompts
174
+ ### Backup destinations
175
+ | Tool | Description |
176
+ |------|-------------|
177
+ | `lattice_list_backup_destinations` | List backup destinations |
178
+ | `lattice_get_backup_destination` | One destination's configuration |
179
+ | `lattice_create_backup_destination` | Create a destination |
180
+ | `lattice_update_backup_destination` | Update a destination |
181
+ | `lattice_delete_backup_destination` | Delete a destination ⚠️ |
182
+ | `lattice_test_backup_destination` | Test connectivity without writing a backup (requires a connected `worker_id`) |
99
183
 
100
- - "What's the status of all stacks?"
101
- - "Show me logs for the forta-api container"
102
- - "Deploy stack 5"
103
- - "Which containers are unhealthy?"
104
- - "Rollback the last deployment on stack 12"
184
+ ### Registries
185
+ | Tool | Description |
186
+ |------|-------------|
187
+ | `lattice_list_registries` | Configured container registries |
188
+ | `lattice_create_registry` | Add a registry |
189
+ | `lattice_update_registry` | Update a registry |
190
+ | `lattice_delete_registry` | Delete a registry ⚠️ |
191
+ | `lattice_test_registry` | Test a saved registry's stored credentials |
192
+ | `lattice_test_registry_inline` | Test registry credentials before saving |
193
+ | `lattice_list_registry_repositories` | What images exist |
194
+ | `lattice_list_registry_tags` | **What versions are deployable** |
105
195
 
106
- ## Environment Variables
196
+ ### Discovery & diagnostics
197
+ | Tool | Description |
198
+ |------|-------------|
199
+ | `lattice_search` | Search workers, stacks and containers in one call |
200
+ | `lattice_get_anomalies` | **Restart loops, unhealthy containers, offline workers — best first call** |
201
+ | `lattice_get_fleet_metrics` | Aggregated fleet CPU/memory/disk/network |
202
+ | `lattice_get_versions` | Runner versions and what's outdated |
203
+ | `lattice_refresh_versions` | Re-poll every worker for its current runner version |
204
+ | `lattice_get_container_metrics` | Per-container metrics over time |
205
+ | `lattice_get_self` | Which user the token authenticates as |
107
206
 
108
- | Variable | Required | Description |
109
- |----------|----------|-------------|
110
- | `LATTICE_API_URL` | Yes | Lattice API base URL |
111
- | `LATTICE_API_TOKEN` | Yes | API token for authentication |
207
+ ### Stacks — lifecycle, compose & deploy tokens
208
+ | Tool | Description |
209
+ |------|-------------|
210
+ | `lattice_create_stack` | Create an empty stack |
211
+ | `lattice_delete_stack` | Delete a stack and all its containers ⚠️ |
212
+ | `lattice_get_stack_containers` | Containers in a stack |
213
+ | `lattice_update_stack_compose` | Replace a stack's compose YAML |
214
+ | `lattice_sync_stack_compose` | Reconcile container records against stored compose YAML |
215
+ | `lattice_import_compose` | Create a stack from compose YAML |
216
+ | `lattice_export_stack` | Export a stack's full definition as portable JSON |
217
+ | `lattice_import_stack_export` | Recreate a stack from an export document |
218
+ | `lattice_save_stack_as_template` | Save a stack as a reusable template |
219
+ | `lattice_list_deploy_tokens` | CI deploy tokens — `last_used_at` shows whether CI reaches Lattice |
220
+ | `lattice_create_deploy_token` | Create a CI deploy token |
221
+ | `lattice_delete_deploy_token` | Delete a CI deploy token ⚠️ |
222
+ | `lattice_approve_deployment` | Approve a deployment awaiting approval |
223
+
224
+ ### Container definitions
225
+ | Tool | Description |
226
+ |------|-------------|
227
+ | `lattice_create_container` | Add a container definition to a stack |
228
+ | `lattice_update_container` | Update a container definition |
229
+ | `lattice_delete_container` | Delete definition and its running container ⚠️ |
230
+
231
+ ### Workers — registration, tokens, volumes, networks
232
+ | Tool | Description |
233
+ |------|-------------|
234
+ | `lattice_create_worker` | Register a worker |
235
+ | `lattice_update_worker` | Update a worker's name, hostname, IP, status, labels |
236
+ | `lattice_delete_worker` | Delete a worker ⚠️ |
237
+ | `lattice_get_worker_container_stats` | Live per-container stats from one worker |
238
+ | `lattice_list_worker_tokens` | Worker registration tokens |
239
+ | `lattice_create_worker_token` | Create a registration token for a worker |
240
+ | `lattice_delete_worker_token` | Delete a worker token ⚠️ |
241
+ | `lattice_list_worker_volumes` | Docker volumes on a worker |
242
+ | `lattice_create_worker_volume` | Create a Docker volume on a worker |
243
+ | `lattice_delete_worker_volume` | Delete a Docker volume ⚠️ |
244
+ | `lattice_list_worker_networks` | Docker networks on a worker |
245
+ | `lattice_create_worker_network` | Create a Docker network on a worker |
246
+ | `lattice_delete_worker_network` | Delete a Docker network ⚠️ |
247
+ | `lattice_list_all_networks` | Every tracked network across the fleet |
248
+ | `lattice_delete_network` | Delete a tracked network by Lattice ID ⚠️ |
249
+ | `lattice_force_remove_container` | Force-remove a wedged container ⚠️ |
250
+
251
+ ### Env vars, templates & webhooks
252
+ | Tool | Description |
253
+ |------|-------------|
254
+ | `lattice_list_env_vars` | Global `${VAR}` interpolation values (secrets masked) |
255
+ | `lattice_create_env_var` | Create a global environment variable |
256
+ | `lattice_update_env_var` | Update a global environment variable |
257
+ | `lattice_delete_env_var` | Delete a global environment variable ⚠️ |
258
+ | `lattice_list_templates` | Saved stack templates |
259
+ | `lattice_create_template` | Create a stack template |
260
+ | `lattice_delete_template` | Delete a stack template ⚠️ |
261
+ | `lattice_list_webhooks` | Outbound event webhooks |
262
+ | `lattice_create_webhook` | Create an outbound webhook |
263
+ | `lattice_update_webhook` | Update a webhook |
264
+ | `lattice_delete_webhook` | Delete a webhook ⚠️ |
265
+ | `lattice_test_webhook` | Send a test payload to a webhook |
266
+
267
+ ### Users & instance configuration
268
+ | Tool | Description |
269
+ |------|-------------|
270
+ | `lattice_list_users` | List Lattice users with roles and status |
271
+ | `lattice_create_user` | Create a local Lattice user |
272
+ | `lattice_update_user` | Update a user's name, role or active flag ⚠️ |
273
+ | `lattice_delete_user` | Delete a Lattice user ⚠️ |
274
+ | `lattice_get_sso_config` | Get the Forta SSO configuration |
275
+ | `lattice_update_sso_config` | Update SSO config ⚠️ (can lock out SSO users) |
276
+ | `lattice_get_smtp_config` | Get the SMTP configuration |
277
+ | `lattice_update_smtp_config` | Update the SMTP configuration |
278
+ | `lattice_test_smtp` | Send a test email with the saved SMTP config |
279
+ | `lattice_get_notification_prefs` | Per-event notification preferences |
280
+ | `lattice_update_notification_prefs` | Update notification preferences |
281
+
282
+ ## Project structure
283
+
284
+ Everything lives in one file:
285
+
286
+ | Path | Role |
287
+ |------|------|
288
+ | `index.js` | The whole server: `--setup` flow, config read, `api()` HTTP helper, `text()`/`body()` helpers, all 125 `server.tool(...)` registrations, transport connect. |
289
+ | `package.json` | npm metadata; `bin.lattice-mcp` → `index.js`. |
290
+ | `AGENTS.md` | Contributor/agent guide — conventions, handler contracts, verification. |
291
+ | `README.md` | This file. |
292
+
293
+ ## Deployment
294
+
295
+ Published to npm as `lattice-mcp` (public). Consumers run `npx -y lattice-mcp`, which resolves the latest published version — so **publishing is deployment**, and a running MCP server must be restarted to pick up a new version. `npm publish` requires 2FA via passkey from an interactive terminal.
296
+
297
+ ## Contributing & further reading
298
+
299
+ Read [`AGENTS.md`](./AGENTS.md) before changing code — it documents the one-shape tool pattern, the `body()`/`api()` helpers, the "read the handler before adding a tool" rule, and the verification steps. Related repos: [`lattice-api`](https://github.com/aidenappl/lattice-api), [`lattice-web`](https://github.com/aidenappl/lattice-web), [`lattice-runner`](https://github.com/aidenappl/lattice-runner).
112
300
 
113
301
  ## License
114
302