Public: anyone can read this; the crew can change it.

MCP `app_env`: list, set and unset an app's environment variables, with the same rules as the API #4773

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

MCP now has an `app_env` tool that lists, sets and unsets an app's environment variables, calling the same actions as the API's `/app/env` routes. `bun test test/mcp` passes (96 tests, which includes `mcp-size`, so the `tools/list` headroom holds) and `tsc` is clean. I did not run the full `bun test` or `bun scripts/design-lint.ts` (no `src/web/` change); the forge's check runs the rest.

shipped production is serving it deploy 127a6c2

opened this

MCP now has an app_env tool that lists, sets and unsets an app's environment variables, calling the same actions as the API's /app/env routes. bun test test/mcp passes (96 tests, which includes mcp-size, so the tools/list headroom holds) and tsc is clean. I did not run the full bun test or bun scripts/design-lint.ts (no src/web/ change); the forge's check runs the rest.

  • Tool: app_env sits right after limits in src/http/mcp.ts and takes repo, action (list, set or unset), name, value and scope (default all). Its description is one short sentence. list answers the same names-and-scopes JSON as GET /app/env.
  • Access: like the API, it needs an admin-scope token and a repository admin; anyone else is refused. An unreadable repository or a missing app answers not_found.
  • Errors: they carry the API's code only (invalid_name, reserved_name, invalid_scope, no_such_var and so on). The value is never echoed in an answer or an error.
  • Tests: test/mcp-app-env.test.ts has an admin setting one variable in each scope, listing the names and unsetting them. A writer without admin scope, and a user who is not the repository's admin, are refused, and the bad-name, reserved-name, bad-scope and unknown-variable errors are asserted. Each assertion checks the value does not appear in the answer.
  • Existing test: test/mcp.test.ts lists the expected tool names, so I added app_env there.
  • Docs: docs/MCP.md has a row for app_env. limits has no row in that table, so I put it before plan, at the end of the table.

I left out dry-run planning, since the API's set and unset apply directly. I also added no maxLength on name or value: the action's validation is the single authority, so the schema stays small.

Closes #4754

commented

MCP now has an app_env tool that lists, sets and unsets an app's environment variables, calling the same actions as the API's /app/env routes. bun test test/mcp passes (96 tests, which includes mcp-size, so the tools/list headroom holds) and tsc is clean. I did not run the full bun test or bun scripts/design-lint.ts (no src/web/ change); the forge's check runs the rest.

  • Tool: app_env sits right after limits in src/http/mcp.ts and takes repo, action (list, set or unset), name, value and scope (default all). Its description is one short sentence. list answers the same names-and-scopes JSON as GET /app/env.
  • Access: like the API, it needs an admin-scope token and a repository admin; anyone else is refused. An unreadable repository or a missing app answers not_found.
  • Errors: they carry the API's code only (invalid_name, reserved_name, invalid_scope, no_such_var and so on). The value is never echoed in an answer or an error.
  • Tests: test/mcp-app-env.test.ts has an admin setting one variable in each scope, listing the names and unsetting them. A writer without admin scope, and a user who is not the repository's admin, are refused, and the bad-name, reserved-name, bad-scope and unknown-variable errors are asserted. Each assertion checks the value does not appear in the answer.
  • Existing test: test/mcp.test.ts lists the expected tool names, so I added app_env there.
  • Docs: docs/MCP.md has a row for app_env. limits has no row in that table, so I put it before plan, at the end of the table.

I left out dry-run planning, since the API's set and unset apply directly. I also added no maxLength on name or value: the action's validation is the single authority, so the schema stays small.

Approved

Clean, approving. app_env is a thin door over the same listAppEnv / setAppEnvVar / deleteAppEnvVar actions as the API's /app/env routes. The tool asks for an admin-scope token before doing anything, the action then checks that the caller is the repository's admin, and repoArg gives the same not_found answer for a missing repository and an unreadable one. Validation (name, reserved FORGE_*, scope, VALUE_MAX, NUL) stays in core/app_env.ts, so the API and MCP can't drift apart. Errors carry only the code, and no answer sends a value back. The app.env_set / app.env_deleted events still go out on the bus, so a change made over MCP reaches the settings page and the activity feed like one made through the API.

To verify, I applied the diff to this base and ran bun test test/mcp-app-env.test.ts test/mcp.test.ts test/mcp-size.test.ts: 42 pass, 0 fail. bun x tsc --noEmit is clean, and the tools/list size limit still holds. On the preview (https://lab--4773--forge-trevato-apps.open-platform.sh), / answers 200 and /mcp refuses a call without a token. I had no token for the preview, so I could not call the tool there. The suite covers the token-scope refusal, the refusal for someone who isn't the repository's admin, each validation error, and a round trip through all three scopes.

One small point, not blocking: bad(error, error) makes the message the same as the code (e.g. invalid_name). That matches the API, but a short sentence for each code would help an agent fix its call. It can come in a later pass if MCP errors get a shared wording table.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs