lattice-mcp 1.2.0 → 1.4.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/AGENTS.md +36 -0
- package/index.js +12 -5
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -227,6 +227,42 @@ kind this repo's rules warn about:
|
|
|
227
227
|
the API allocates a free port from 20000-29999 and returns 409 naming the conflict if you pin one
|
|
228
228
|
that is taken.
|
|
229
229
|
|
|
230
|
+
**1.3.0** masks secrets in every response. No tool count change (133).
|
|
231
|
+
|
|
232
|
+
`api()` now passes every decoded response through `sanitise()` before returning it — see
|
|
233
|
+
*Sensitive value masking* under *How code is written here* for the rules. This closes a real leak
|
|
234
|
+
rather than a theoretical one: this MCP authenticates as a Lattice **admin**, and `lattice-api`
|
|
235
|
+
only masks global env vars server-side for *non-admin* callers
|
|
236
|
+
(`HandleGlobalEnvVars.router.go:39`), so `lattice_list_env_vars` was returning every `is_secret`
|
|
237
|
+
value in plaintext — while its own description claimed they were masked. Container and stack
|
|
238
|
+
`env_vars`, `compose_yaml` environment blocks, database passwords and freshly minted
|
|
239
|
+
deploy/worker/API tokens were all reaching the transcript verbatim too.
|
|
240
|
+
|
|
241
|
+
Masked values keep their **first two characters** and get a fixed-width tail
|
|
242
|
+
(`"supersecret"` → `"su**********"`), so they stay comparable without staying usable. Set
|
|
243
|
+
`LATTICE_ALLOW_SECRET_VALUES=1` to opt out.
|
|
244
|
+
|
|
245
|
+
Three tool descriptions changed to match reality: `lattice_list_env_vars` (which was lying),
|
|
246
|
+
`lattice_reveal_database_credentials`, `lattice_create_deploy_token` and
|
|
247
|
+
`lattice_create_worker_token` now each say the value comes back masked and where to get the real
|
|
248
|
+
one. `verify.mjs` gained a tripwire asserting `sanitise()` is still wired into `api()` and still
|
|
249
|
+
defaults to on — unwiring that single call is a silent, total regression that no other check
|
|
250
|
+
would notice.
|
|
251
|
+
|
|
252
|
+
**1.4.0** exposes parameters added to `lattice-api` alongside the managed-database overhaul. No
|
|
253
|
+
tool-count change.
|
|
254
|
+
|
|
255
|
+
`lattice_create_database_instance` gains `adopt_existing_volume`. It is off by default because a
|
|
256
|
+
reused data volume makes the engine skip initialisation and keep its previous credentials while the
|
|
257
|
+
API records freshly generated ones — the container reports healthy and nothing looks wrong until a
|
|
258
|
+
connection fails.
|
|
259
|
+
|
|
260
|
+
`lattice_update_api` and `lattice_update_web` gain `version` and `force`. The compose file
|
|
261
|
+
interpolates the image tag from an env var, so passing `version` writes it to the compose env file
|
|
262
|
+
and deploys or rolls back that tag in one call, persisting across restarts. Requesting a version
|
|
263
|
+
when the compose file does not interpolate the variable returns a 400 rather than silently
|
|
264
|
+
deploying the old image.
|
|
265
|
+
|
|
230
266
|
**Consolidations.** Where `lattice-api` exposes several paths served by one handler, this repo
|
|
231
267
|
exposes one tool with an enum rather than N tools. `lattice_database_action` covers
|
|
232
268
|
`/start`, `/stop`, `/restart` and `/remove`. Worker actions are the historical exception — they
|
package/index.js
CHANGED
|
@@ -221,7 +221,7 @@ function body(obj) {
|
|
|
221
221
|
|
|
222
222
|
const server = new McpServer({
|
|
223
223
|
name: "lattice",
|
|
224
|
-
version: "1.
|
|
224
|
+
version: "1.4.0",
|
|
225
225
|
});
|
|
226
226
|
|
|
227
227
|
// Overview
|
|
@@ -497,13 +497,19 @@ server.tool("lattice_start_all_worker", "Start all containers on a worker", {
|
|
|
497
497
|
});
|
|
498
498
|
|
|
499
499
|
// Dashboard controls
|
|
500
|
-
server.tool("lattice_update_api", "
|
|
501
|
-
|
|
500
|
+
server.tool("lattice_update_api", "Update the Lattice API container. Pass `version` to deploy or roll back to a specific tag; omit it to take whatever the compose file resolves to. Returns 'already up to date' without restarting when the resolved image is the one already running", {
|
|
501
|
+
version: z.string().optional().describe("Image tag to deploy, e.g. v1.3.21. Written to the compose env file, so it persists across restarts. Omit to follow the existing tag"),
|
|
502
|
+
force: z.boolean().optional().describe("Recreate even when the image is unchanged — use after an environment-variable change"),
|
|
503
|
+
}, async ({ version, force }) => {
|
|
504
|
+
const res = await api("POST", "/admin/update/api", force ? { force: "true" } : null, body({ version }));
|
|
502
505
|
return { content: text(res) };
|
|
503
506
|
});
|
|
504
507
|
|
|
505
|
-
server.tool("lattice_update_web", "
|
|
506
|
-
|
|
508
|
+
server.tool("lattice_update_web", "Update the Lattice web container. Pass `version` to deploy or roll back to a specific tag; omit it to take whatever the compose file resolves to", {
|
|
509
|
+
version: z.string().optional().describe("Image tag to deploy, e.g. v1.3.25. Written to the compose env file, so it persists across restarts. Omit to follow the existing tag"),
|
|
510
|
+
force: z.boolean().optional().describe("Recreate even when the image is unchanged"),
|
|
511
|
+
}, async ({ version, force }) => {
|
|
512
|
+
const res = await api("POST", "/admin/update/web", force ? { force: "true" } : null, body({ version }));
|
|
507
513
|
return { content: text(res) };
|
|
508
514
|
});
|
|
509
515
|
|
|
@@ -575,6 +581,7 @@ server.tool("lattice_create_database_instance", "Provision a database instance o
|
|
|
575
581
|
snapshot_schedule: z.string().optional().describe("Cron expression for automatic snapshots"),
|
|
576
582
|
retention_count: z.number().optional().describe("How many automatic snapshots to keep"),
|
|
577
583
|
backup_destination_id: z.number().optional().describe("Backup destination for snapshots"),
|
|
584
|
+
adopt_existing_volume: z.boolean().optional().describe("Reuse a leftover data volume of the same name. Off by default — the engine skips initialisation when its data directory is non-empty, so it keeps its OLD credentials while the API records the new ones, and nothing looks wrong until a connection fails"),
|
|
578
585
|
}, async (args) => {
|
|
579
586
|
const res = await api("POST", "/admin/database-instances", null, body(args));
|
|
580
587
|
return { content: text(res) };
|