Skip to content

(DHQ-501) Add global env vars CRUD, list_ssh_keys, and sync HTTP transport - #4

Merged
facundofarias merged 10 commits into
mainfrom
feature/global-environment-variables
Mar 27, 2026
Merged

facundofarias merged 10 commits into
mainfrom
feature/global-environment-variables

Conversation

@MartaKar

@MartaKar MartaKar commented Mar 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Add global environment variables CRUD tools (list, create, update, delete)
  • Add list_ssh_keys tool to expose account SSH public keys via MCP
  • Sync HTTP transport handler with all 12 tools (was missing env vars, SSH keys, deployment log)

Details

Global Environment Variables (4 tools):

  • list_global_environment_variables - List all account-level env vars
  • create_global_environment_variable - Create with name, value, locked, build_pipeline
  • update_global_environment_variable - Update by identifier
  • delete_global_environment_variable - Delete by identifier (with read-only mode guard)

SSH Keys (1 tool):

  • list_ssh_keys - Returns public keys, fingerprints, titles, key types. Never exposes private keys. Enables agents to programmatically authorize DeployHQ on servers.

HTTP Transport:

  • Added all missing tool handlers so HTTP clients have parity with stdio/SSE

Test plan

  • 216 tests pass locally
  • list_ssh_keys returns correct keys for accounts with 0, 1, and multiple keys
  • Global env var CRUD works end-to-end
  • HTTP transport handles all 12 tools
  • Read-only mode blocks mutating tools

Summary by CodeRabbit

  • New Features

    • Expanded toolset (7 → 17): manage SSH keys, global environment variables (list/create/update/delete), and global config files (list/get/create/update/delete).
    • Extended client API and public types for SSH keys, environment variables, and config files.
    • Improved deployment revision semantics (commit SHA, "latest"/HEAD/branch and previous/current translations).
    • Read-only mode now blocks mutations with clear FORBIDDEN guidance.
  • Tests

    • Large test expansion covering all new tools and input schemas.
  • Documentation

    • Updated docs, examples, and quick-start to reflect new tools and the read-only flag.

@coderabbitai

coderabbitai Bot commented Mar 13, 2026 •

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds account-scoped resources: SSH key listing, global environment variable CRUD, and global config file CRUD across client API, tool schemas, MCP server handlers, HTTP transport, tests, and docs; exports ListSshKeysSchema and expands the public toolset from 7 to 17 tools. (34 words)

Changes

Cohort / File(s) Summary
API Client
src/api-client.ts
Adds public interfaces and client methods for SSH keys, global environment variables, and global config files (list/get/create/update/delete).
Tool Definitions & Schemas
src/tools.ts
Adds Zod schemas and tool entries for SSH keys, global env vars, and global config files (list/get/create/update/delete); exports ListSshKeysSchema and other new schemas.
MCP Server Handlers
src/mcp-server.ts
Implements new MCP tool handlers for the added tools with schema validation, apiClient calls, logging, revision translation logic, structured error responses, and read-only guards for mutations.
HTTP Transport
src/transports/http-handler.ts
Wires new tool endpoints into HTTP handling: imports new schemas, validates args, invokes MCP/client operations, wraps results into MCP content, and enforces read-only checks for mutating tools.
Tests
src/__tests__/tools.test.ts
Exports/validates ListSshKeysSchema, expands expected tools from 7→17, and updates tests to assert new tool names, descriptions, and input schemas.
Docs / README
README.md
Updates tool catalog and examples to reflect 17 tools, documents DEPLOYHQ_READ_ONLY behavior, and adds descriptions/usage for SSH keys, global env vars, and config file templates.

Sequence Diagram(s)

sequenceDiagram
  participant Client as HTTP Client
  participant HTTP as HTTP Handler
  participant MCP as MCP Server
  participant API as DeployHQ API Client

  Client->>HTTP: POST /mcp/tools/call { tool: "list_global_config_files" }
  HTTP->>MCP: validate args & forward tool call
  MCP->>API: apiClient.listGlobalConfigFiles()
  API-->>MCP: [ConfigFile[]] result
  MCP-->>HTTP: MCP content { result }
  HTTP-->>Client: 200 OK with MCP content
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • facundofarias
  • thdurante
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the three main additions: global environment variables CRUD operations, list_ssh_keys tool, and HTTP transport synchronization.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/global-environment-variables
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

@MartaKar
MartaKar requested a review from facundofarias March 13, 2026 08:51

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/transports/http-handler.ts (1)

176-202: Inconsistent error messages for read-only mode.

The error messages for global environment variable mutations are terse compared to create_deployment (lines 143-151) which provides detailed instructions on how to disable read-only mode. Consider aligning for consistency:

♻️ Suggested fix for create_global_environment_variable
             case 'create_global_environment_variable': {
               if (config.readOnlyMode) {
-                throw new Error('FORBIDDEN: Server is running in read-only mode.');
+                log.info('⚠️  Global environment variable creation blocked by read-only mode');
+                throw new Error(
+                  'FORBIDDEN: Server is running in read-only mode. ' +
+                  'Global environment variable creation is disabled for security.\n\n' +
+                  'To disable read-only mode:\n' +
+                  '- Set environment variable: DEPLOYHQ_READ_ONLY=false\n' +
+                  '- Or use CLI flag: --read-only=false'
+                );
               }

Apply similar changes to update_global_environment_variable and delete_global_environment_variable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/transports/http-handler.ts` around lines 176 - 202, Replace the terse
read-only errors in the RPC handlers for 'create_global_environment_variable',
'update_global_environment_variable', and 'delete_global_environment_variable'
with the same detailed message used in the create_deployment handler: when
config.readOnlyMode is true, throw an Error that explains the server is running
in read-only mode, instructs how to re-enable writes (e.g., set READ_ONLY=false
or update the admin setting), and suggests contacting an administrator if
needed; update the error text in the case blocks for
createGlobalEnvironmentVariable, updateGlobalEnvironmentVariable (handler using
UpdateGlobalEnvironmentVariableSchema and
client.updateGlobalEnvironmentVariable), and deleteGlobalEnvironmentVariable
(handler using DeleteGlobalEnvironmentVariableSchema and
client.deleteGlobalEnvironmentVariable) so all three messages match the
create_deployment wording and guidance.
src/__tests__/tools.test.ts (1)

256-266: Test coverage for new tool names is incomplete.

The test validates list_ssh_keys but omits the four new global environment variable tools. Consider adding assertions for all new tools:

         expect(toolNames).toContain('list_ssh_keys');
+        expect(toolNames).toContain('list_global_environment_variables');
+        expect(toolNames).toContain('create_global_environment_variable');
+        expect(toolNames).toContain('update_global_environment_variable');
+        expect(toolNames).toContain('delete_global_environment_variable');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/tools.test.ts` around lines 256 - 266, Update the 'should have
correct tool names' test that builds toolNames from the tools array to also
assert the four new global environment-variable tool names; locate the test that
maps tools -> toolNames and add expect(toolNames).toContain('<new_tool_name>')
for each of the four new tool names (use the exact names you added to the tools
array) so the test covers those new tools.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/api-client.ts`:
- Around line 117-125: The EnvironmentVariable interface currently declares
identifier as number but the API schemas and methods expect a string; update the
interface by changing EnvironmentVariable.identifier from number to string so it
matches
UpdateGlobalEnvironmentVariableSchema/DeleteGlobalEnvironmentVariableSchema and
the methods that accept string ids (e.g., any functions that take an id from the
list response for update/delete).

---

Nitpick comments:
In `@src/__tests__/tools.test.ts`:
- Around line 256-266: Update the 'should have correct tool names' test that
builds toolNames from the tools array to also assert the four new global
environment-variable tool names; locate the test that maps tools -> toolNames
and add expect(toolNames).toContain('<new_tool_name>') for each of the four new
tool names (use the exact names you added to the tools array) so the test covers
those new tools.

In `@src/transports/http-handler.ts`:
- Around line 176-202: Replace the terse read-only errors in the RPC handlers
for 'create_global_environment_variable', 'update_global_environment_variable',
and 'delete_global_environment_variable' with the same detailed message used in
the create_deployment handler: when config.readOnlyMode is true, throw an Error
that explains the server is running in read-only mode, instructs how to
re-enable writes (e.g., set READ_ONLY=false or update the admin setting), and
suggests contacting an administrator if needed; update the error text in the
case blocks for createGlobalEnvironmentVariable, updateGlobalEnvironmentVariable
(handler using UpdateGlobalEnvironmentVariableSchema and
client.updateGlobalEnvironmentVariable), and deleteGlobalEnvironmentVariable
(handler using DeleteGlobalEnvironmentVariableSchema and
client.deleteGlobalEnvironmentVariable) so all three messages match the
create_deployment wording and guidance.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 28a64d3c-e72a-46f7-90ce-87618820f224

📥 Commits

Reviewing files that changed from the base of the PR and between 7df29d9 and d14dd17.

📒 Files selected for processing (5)
  • src/__tests__/tools.test.ts
  • src/api-client.ts
  • src/mcp-server.ts
  • src/tools.ts
  • src/transports/http-handler.ts

Comment thread src/api-client.ts
@MartaKar MartaKar changed the title feat: add global env vars CRUD, list_ssh_keys, and sync HTTP transport (DHQ-501) Add global env vars CRUD, list_ssh_keys, and sync HTTP transport Mar 13, 2026
@linear

linear Bot commented Mar 13, 2026

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d14dd172fe

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/tools.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/transports/http-handler.ts (1)

176-229: Read-only guards correctly applied; consider extracting the repeated error message.

The read-only mode checks are correctly implemented for all mutating operations (create, update, delete), matching the existing pattern from create_deployment. The error messages are helpful and provide clear remediation steps.

However, the error message body is nearly identical across all four mutating tools (lines 143-151, 179-187, 197-205, 216-224), differing only in the operation description. This could be extracted into a helper function to reduce duplication and simplify future updates.

♻️ Optional: Extract read-only error helper
// Add near the top of the file or in a shared utility
function throwReadOnlyError(operation: string): never {
  throw new Error(
    `FORBIDDEN: Server is running in read-only mode. ` +
    `${operation} is disabled for security.\n\n` +
    `To enable mutations:\n` +
    `- Set environment variable: DEPLOYHQ_READ_ONLY=false\n` +
    `- Or use CLI flag: --read-only=false\n\n` +
    `Read-only mode is enabled by default to prevent ` +
    `accidental changes when using AI assistants.`
  );
}

Then use it in each handler:

case 'create_global_environment_variable': {
  if (config.readOnlyMode) {
    log.info('⚠️  Global environment variable creation blocked by read-only mode');
    throwReadOnlyError('Global environment variable creation');
  }
  // ...
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/transports/http-handler.ts` around lines 176 - 229, Extract the
duplicated read-only Error text into a single helper (e.g.,
throwReadOnlyError(operation: string): never) and replace the inline throws in
the mutating handlers (cases 'create_global_environment_variable',
'update_global_environment_variable', 'delete_global_environment_variable' and
the existing 'create_deployment' usage) with a call to that helper; keep the
existing log.info messages and only change the throw sites to call
throwReadOnlyError with an appropriate operation string like "Global environment
variable creation" / "update" / "deletion". Ensure the helper is exported or
placed near the top of the file so it’s accessible to these handlers and
preserve the same error text and formatting when constructing the Error inside
throwReadOnlyError.
src/__tests__/tools.test.ts (1)

10-12: Add unit test coverage for global environment variable schemas.

ListSshKeysSchema is imported and tested in this file, but the global environment variable schemas (ListGlobalEnvironmentVariablesSchema, CreateGlobalEnvironmentVariableSchema, UpdateGlobalEnvironmentVariableSchema, DeleteGlobalEnvironmentVariableSchema) are not.

UpdateGlobalEnvironmentVariableSchema and DeleteGlobalEnvironmentVariableSchema use z.coerce.string() for the id field, and CreateGlobalEnvironmentVariableSchema has required name and value fields. These validation behaviors should have dedicated test coverage following the pattern established by ListSshKeysSchema tests to prevent regressions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/tools.test.ts` around lines 10 - 12, Add unit tests mirroring
the ListSshKeysSchema tests to cover the global environment variable schemas:
ListGlobalEnvironmentVariablesSchema, CreateGlobalEnvironmentVariableSchema,
UpdateGlobalEnvironmentVariableSchema, and
DeleteGlobalEnvironmentVariableSchema. Specifically, assert that
CreateGlobalEnvironmentVariableSchema requires both name and value fields and
rejects missing values; assert that UpdateGlobalEnvironmentVariableSchema and
DeleteGlobalEnvironmentVariableSchema coerce the id field to string (use
non-string inputs and verify coercion/validation succeeds); and include a basic
validity test for ListGlobalEnvironmentVariablesSchema. Follow the existing test
patterns (setup, valid/invalid examples, and expect parse to throw or succeed)
used for ListSshKeysSchema to implement these cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/__tests__/tools.test.ts`:
- Around line 10-12: Add unit tests mirroring the ListSshKeysSchema tests to
cover the global environment variable schemas:
ListGlobalEnvironmentVariablesSchema, CreateGlobalEnvironmentVariableSchema,
UpdateGlobalEnvironmentVariableSchema, and
DeleteGlobalEnvironmentVariableSchema. Specifically, assert that
CreateGlobalEnvironmentVariableSchema requires both name and value fields and
rejects missing values; assert that UpdateGlobalEnvironmentVariableSchema and
DeleteGlobalEnvironmentVariableSchema coerce the id field to string (use
non-string inputs and verify coercion/validation succeeds); and include a basic
validity test for ListGlobalEnvironmentVariablesSchema. Follow the existing test
patterns (setup, valid/invalid examples, and expect parse to throw or succeed)
used for ListSshKeysSchema to implement these cases.

In `@src/transports/http-handler.ts`:
- Around line 176-229: Extract the duplicated read-only Error text into a single
helper (e.g., throwReadOnlyError(operation: string): never) and replace the
inline throws in the mutating handlers (cases
'create_global_environment_variable', 'update_global_environment_variable',
'delete_global_environment_variable' and the existing 'create_deployment' usage)
with a call to that helper; keep the existing log.info messages and only change
the throw sites to call throwReadOnlyError with an appropriate operation string
like "Global environment variable creation" / "update" / "deletion". Ensure the
helper is exported or placed near the top of the file so it’s accessible to
these handlers and preserve the same error text and formatting when constructing
the Error inside throwReadOnlyError.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 07c7521b-1521-49fe-add3-8fdbdc764a9a

📥 Commits

Reviewing files that changed from the base of the PR and between d14dd17 and 47111ab.

📒 Files selected for processing (3)
  • src/__tests__/tools.test.ts
  • src/tools.ts
  • src/transports/http-handler.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/tools.ts

@MartaKar
MartaKar requested a review from thdurante March 16, 2026 09:28
…nt revision handling

Add 5 new MCP tools for global config file templates (list, get, create,
update, delete) with full read-only mode support across stdio and HTTP
transports. Add DEPLOYHQ_URL env var for custom API base URLs. Fix
deployment creation to translate symbolic end_revision values (latest,
HEAD, branch names) to __CURRENT__ constant. Update README with all 17
tools and new configuration options.
This was included by mistake in the previous commit. Remove the
DEPLOYHQ_URL environment variable and baseUrl parameter from the
API client, MCP server factory, and stdio entry point.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/__tests__/tools.test.ts (1)

239-274: Please add direct cases for the new env/config schemas.

This file only adds ListSshKeysSchema coverage plus tool-name assertions. The new global environment variable and global config file schemas in src/tools.ts still have no direct valid/invalid contract tests here, so regressions like empty update payloads or wrong required fields will slip through.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/tools.test.ts` around lines 239 - 274, Add direct unit tests
that validate the new global environment variable and global config file Zod
schemas: exercise both valid and invalid payloads for
GlobalEnvironmentVariableSchema, CreateGlobalEnvironmentVariableSchema (or
similar create schema), UpdateGlobalEnvironmentVariableSchema,
GlobalConfigFileSchema, CreateGlobalConfigFileSchema, and
UpdateGlobalConfigFileSchema; include positive cases with required fields
present and negative cases such as empty objects, missing required fields, and
empty update payloads to ensure those schemas reject invalid input, and add
assertions that safeParse(...).success is true for valid examples and false for
invalid ones while referencing the schema names above and the tools array
entries (create_global_environment_variable, update_global_environment_variable,
create_global_config_file, update_global_config_file) to locate where to add the
tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/mcp-server.ts`:
- Around line 187-205: The DEBUG request-level logging currently prints raw args
(including secret-bearing fields) and must be protected: before any call to
log.debug('Tool arguments:', JSON.stringify(args)) ensure sensitive fields are
redacted (mask or remove keys named "value" and "body") or skip logging when
those keys exist; implement this in the request-handler/middleware that triggers
the DEBUG log and also add the same sanitization for the handlers that call
createGlobalEnvironmentVariable and the other tool handlers referenced (handlers
handling create_global_environment_variable and the blocks around the other
ranges), i.e., sanitize args in the scope of the
create_global_environment_variable handler and the handlers for the affected
cases so that validatedArgs/value/body are replaced with a fixed mask (e.g.,
"***REDACTED***") before any log.debug of args.
- Around line 151-163: The current isHexSha check silently rewrites
non-lowercase hex SHAs and branch names (e.g., "main", "feature/foo", "ABC123")
into DeployHQ constants; update the logic around deploymentParams.end_revision
and deploymentParams.start_revision so branch names are preserved by moving
non-hex branch values into deploymentParams.branch before you set
deploymentParams.end_revision = '__CURRENT__', and ensure uppercase hex strings
are normalized to lowercase before isHexSha is evaluated (or adjust the regex)
when handling deploymentParams.start_revision with deploymentParams.use_latest
=== '1'. Mirror the same normalization rules in src/transports/http-handler.ts
so HTTP handling matches stdio/SSE, and if branch-name support is not intended
then update the public contract in src/tools.ts instead of silently transforming
branch values.

In `@src/tools.ts`:
- Around line 59-65: The UpdateGlobalEnvironmentVariableSchema currently allows
{ id } only updates; change the schema to require at least one mutable field
alongside id by adding a Zod refinement on UpdateGlobalEnvironmentVariableSchema
that asserts at least one of the optional fields (name, value, locked,
build_pipeline) is present; do the same for the corresponding
UpdateGlobalConfigFileSchema and the other update schemas referenced (the
definitions around the other ranges) so the local contract rejects empty PATCHes
instead of allowing { id } only.

In `@src/transports/http-handler.ts`:
- Around line 181-233: The three read-only branches in the switch (cases
'create_global_environment_variable', 'update_global_environment_variable',
'delete_global_environment_variable') currently throw a plain Error causing a
500; change them to return/throw a 403-style error (e.g., throw an
HttpError/ForbiddenError that the outer catch maps to 403 or directly
short-circuit the response with status 403) and update the message to remove any
claim that readOnlyMode is the default (state it is enabled and blocks
mutations). Keep the same validation steps
(CreateGlobalEnvironmentVariableSchema.parse,
UpdateGlobalEnvironmentVariableSchema.parse,
DeleteGlobalEnvironmentVariableSchema.parse) and then either throw the typed 403
error or call the HTTP response helper so clients receive HTTP 403 instead of
500.

---

Nitpick comments:
In `@src/__tests__/tools.test.ts`:
- Around line 239-274: Add direct unit tests that validate the new global
environment variable and global config file Zod schemas: exercise both valid and
invalid payloads for GlobalEnvironmentVariableSchema,
CreateGlobalEnvironmentVariableSchema (or similar create schema),
UpdateGlobalEnvironmentVariableSchema, GlobalConfigFileSchema,
CreateGlobalConfigFileSchema, and UpdateGlobalConfigFileSchema; include positive
cases with required fields present and negative cases such as empty objects,
missing required fields, and empty update payloads to ensure those schemas
reject invalid input, and add assertions that safeParse(...).success is true for
valid examples and false for invalid ones while referencing the schema names
above and the tools array entries (create_global_environment_variable,
update_global_environment_variable, create_global_config_file,
update_global_config_file) to locate where to add the tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 92dba00a-b411-42b8-b393-5479b23fdc16

📥 Commits

Reviewing files that changed from the base of the PR and between 47111ab and 709f1f7.

📒 Files selected for processing (6)
  • README.md
  • src/__tests__/tools.test.ts
  • src/api-client.ts
  • src/mcp-server.ts
  • src/tools.ts
  • src/transports/http-handler.ts

Comment thread src/mcp-server.ts Outdated
Comment thread src/mcp-server.ts
Comment thread src/tools.ts Outdated
Comment thread src/transports/http-handler.ts
Language is a UI concern (editor highlighting) not needed in the API.
Agents can infer language from the file path extension.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/tools.ts (1)

59-65: ⚠️ Potential issue | 🟠 Major

Enforce at least one mutable field for update tools.

Line 59 and Line 84 currently allow { id } with no actual changes. The same gap exists in Line 306 and Line 423 MCP inputSchema, so clients can submit empty updates and fail later downstream.

Proposed contract fix (Zod + MCP inputSchema)
 export const UpdateGlobalEnvironmentVariableSchema = z.object({
   id: z.coerce.string().describe('Environment variable identifier'),
   name: z.string().optional().describe('Environment variable name'),
   value: z.string().optional().describe('Environment variable value'),
   locked: z.boolean().optional().describe('Whether the variable is locked'),
   build_pipeline: z.boolean().optional().describe('Whether the variable is available in the build pipeline'),
-});
+}).refine(
+  (data) =>
+    data.name !== undefined ||
+    data.value !== undefined ||
+    data.locked !== undefined ||
+    data.build_pipeline !== undefined,
+  { message: 'Provide at least one field to update' }
+);

 export const UpdateGlobalConfigFileSchema = z.object({
   id: z.string().describe('Config file identifier (UUID)'),
   path: z.string().optional().describe('File path for the config file'),
   body: z.string().optional().describe('File contents'),
   description: z.string().optional().describe('Description of the config file'),
   build: z.boolean().optional().describe('Whether the config file is used during builds'),
-});
+}).refine(
+  (data) =>
+    data.path !== undefined ||
+    data.body !== undefined ||
+    data.description !== undefined ||
+    data.build !== undefined,
+  { message: 'Provide at least one field to update' }
+);

   {
     name: 'update_global_environment_variable',
@@
       required: ['id'],
+      anyOf: [
+        { required: ['name'] },
+        { required: ['value'] },
+        { required: ['locked'] },
+        { required: ['build_pipeline'] },
+      ],
     },
   },

   {
     name: 'update_global_config_file',
@@
       required: ['id'],
+      anyOf: [
+        { required: ['path'] },
+        { required: ['body'] },
+        { required: ['description'] },
+        { required: ['build'] },
+      ],
     },
   },
#!/bin/bash
# Verify both update schemas still allow id-only objects and MCP inputSchema lacks anyOf guards.
rg -n "export const UpdateGlobal(EnvironmentVariable|ConfigFile)Schema" src/tools.ts -A20
rg -n "name: 'update_global_(environment_variable|config_file)'" src/tools.ts -A80 | rg -n "required: \['id'\]|anyOf:"

As per coding guidelines, "Use Zod schemas for runtime validation of all MCP tool inputs. Define schemas once in src/tools.ts for both TypeScript type inference and runtime validation in tool handlers."

Also applies to: 84-90, 306-331, 423-448

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tools.ts` around lines 59 - 65, The Update schemas permit an id-only
payload; modify UpdateGlobalEnvironmentVariableSchema and
UpdateGlobalConfigFileSchema to require at least one mutable field by adding a
refinement (e.g., .refine(obj => obj.name !== undefined || obj.value !==
undefined || obj.locked !== undefined || obj.build_pipeline !== undefined, {
message: 'At least one mutable field must be provided' } ) for environment
variables, and the analogous check for config files) so Zod rejects id-only
updates, and update the MCP inputSchema entries for the tools named
update_global_environment_variable and update_global_config_file to include an
anyOf guard (or equivalent validation rule) that enforces presence of at least
one mutable field in addition to required: ['id'] so clients cannot submit empty
updates.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/tools.ts`:
- Line 47: The schema for the use_latest field currently allows any string
(z.string().optional()) but the code and docs only accept the sentinel value
"1"; update the schema in src/tools.ts to enforce z.literal('1').optional()
(i.e., replace z.string().optional() for the use_latest property with
z.literal('1').optional()) so inputs other than "1" are rejected; ensure both
occurrences of the use_latest definition (the one near the existing describe
text and the other set of lines mentioned) are changed to
z.literal('1').optional() to match the handler logic that checks for the exact
'1' sentinel (and the documented ___PREVIOUS___ behavior).

---

Duplicate comments:
In `@src/tools.ts`:
- Around line 59-65: The Update schemas permit an id-only payload; modify
UpdateGlobalEnvironmentVariableSchema and UpdateGlobalConfigFileSchema to
require at least one mutable field by adding a refinement (e.g., .refine(obj =>
obj.name !== undefined || obj.value !== undefined || obj.locked !== undefined ||
obj.build_pipeline !== undefined, { message: 'At least one mutable field must be
provided' } ) for environment variables, and the analogous check for config
files) so Zod rejects id-only updates, and update the MCP inputSchema entries
for the tools named update_global_environment_variable and
update_global_config_file to include an anyOf guard (or equivalent validation
rule) that enforces presence of at least one mutable field in addition to
required: ['id'] so clients cannot submit empty updates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 438e4940-d529-4f46-ab27-28af761b4e68

📥 Commits

Reviewing files that changed from the base of the PR and between 709f1f7 and 4e2a6e1.

📒 Files selected for processing (2)
  • src/api-client.ts
  • src/tools.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/api-client.ts

Comment thread src/tools.ts Outdated
- Remove dangerous revision rewriting that silently changed branch
  names into DeployHQ magic constants
- Redact env var values and config file bodies from DEBUG logs
- Return 403 (not 500) for read-only mode blocks in HTTP transport
- Require at least one field in update schemas (reject empty PATCHes)
Comment thread README.md
Comment thread src/tools.ts
Comment thread src/transports/http-handler.ts Outdated
Comment thread src/mcp-server.ts Outdated
The /deployments/:id/log route doesn't exist. Deployment logs are
accessed through step-level endpoints. Now fetches the deployment to
get its steps, iterates each step's logs, and combines them into a
single formatted output.
- Add create_ssh_key tool (POST /ssh_keys) for creating SSH key pairs
- Extract sendReadOnlyError() helper in http-handler.ts (replaces 8 inline blocks)
- Extract readOnlyError() helper in mcp-server.ts (replaces 8 inline strings)
- Addresses PR review feedback from thdurante and facundofarias
@facundofarias
facundofarias merged commit 3385a68 into main Mar 27, 2026
4 checks passed
@facundofarias
facundofarias deleted the feature/global-environment-variables branch March 27, 2026 06:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants