Repository navigation
(DHQ-501) Add global env vars CRUD, list_ssh_keys, and sync HTTP transport - #4
Conversation
…sync HTTP transport
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds 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 Changes
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Comment |
There was a problem hiding this comment.
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_variableanddelete_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_keysbut 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
📒 Files selected for processing (5)
src/__tests__/tools.test.tssrc/api-client.tssrc/mcp-server.tssrc/tools.tssrc/transports/http-handler.ts
There was a problem hiding this comment.
💡 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".
…, add test coverage
There was a problem hiding this comment.
🧹 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.
ListSshKeysSchemais imported and tested in this file, but the global environment variable schemas (ListGlobalEnvironmentVariablesSchema,CreateGlobalEnvironmentVariableSchema,UpdateGlobalEnvironmentVariableSchema,DeleteGlobalEnvironmentVariableSchema) are not.
UpdateGlobalEnvironmentVariableSchemaandDeleteGlobalEnvironmentVariableSchemausez.coerce.string()for theidfield, andCreateGlobalEnvironmentVariableSchemahas requirednameandvaluefields. These validation behaviors should have dedicated test coverage following the pattern established byListSshKeysSchematests 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
📒 Files selected for processing (3)
src/__tests__/tools.test.tssrc/tools.tssrc/transports/http-handler.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/tools.ts
…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.
There was a problem hiding this comment.
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
ListSshKeysSchemacoverage plus tool-name assertions. The new global environment variable and global config file schemas insrc/tools.tsstill 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
📒 Files selected for processing (6)
README.mdsrc/__tests__/tools.test.tssrc/api-client.tssrc/mcp-server.tssrc/tools.tssrc/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.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/tools.ts (1)
59-65:⚠️ Potential issue | 🟠 MajorEnforce 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 MCPinputSchema, 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
📒 Files selected for processing (2)
src/api-client.tssrc/tools.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/api-client.ts
- 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)
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
Summary
list_ssh_keystool to expose account SSH public keys via MCPDetails
Global Environment Variables (4 tools):
list_global_environment_variables- List all account-level env varscreate_global_environment_variable- Create with name, value, locked, build_pipelineupdate_global_environment_variable- Update by identifierdelete_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:
Test plan
list_ssh_keysreturns correct keys for accounts with 0, 1, and multiple keysSummary by CodeRabbit
New Features
Tests
Documentation