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.
Files changed (3) hide show
  1. package/AGENTS.md +36 -0
  2. package/index.js +12 -5
  3. 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.2.0",
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", "Trigger Lattice API self-update", {}, async () => {
501
- const res = await api("POST", "/admin/update/api");
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", "Trigger Lattice web container update", {}, async () => {
506
- const res = await api("POST", "/admin/update/web");
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) };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lattice-mcp",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "MCP server for Lattice container orchestration platform",
5
5
  "type": "module",
6
6
  "main": "index.js",