Skip to content

Commit 62c330e

Browse files
facundofariasclaude
andcommitted
feat: Add v1.0 enhancements with deployment logs and improved UX
Major improvements for production readiness: Features: - Add get_deployment_log tool for debugging failed deployments - Add startup credential validation with clear error messages - Add LOG_LEVEL environment variable (ERROR, INFO, DEBUG) - Add enhanced error messages with actionable suggestions Documentation: - Add comprehensive troubleshooting section with 6 common issues - Add usage examples and complete workflow demonstrations - Add API coverage table showing all 7 tools - Add configuration options with required vs optional variables - Update docs/claude-config.json with LOG_LEVEL example Testing: - Add tests for GetDeploymentLogSchema (3 new tests) - Update test count validation (7 tools, 108 tests total) - All tests passing Technical: - Improve logger with configurable log levels - Add context-aware error suggestions based on error type - Add validateCredentials method to DeployHQClient - Add getDeploymentLog method to DeployHQClient 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 7381912 commit 62c330e

File tree

7 files changed

+417
-19
lines changed

7 files changed

+417
-19
lines changed

README.md

Lines changed: 161 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,17 @@ A Model Context Protocol (MCP) server for DeployHQ that enables AI assistants li
1414

1515
## 📋 Available Tools
1616

17-
The MCP server provides the following tools for AI assistants:
17+
The MCP server provides **7 tools** for AI assistants:
18+
19+
| Tool | Description | Parameters |
20+
|------|-------------|------------|
21+
| `list_projects` | List all projects | None |
22+
| `get_project` | Get project details | `permalink` |
23+
| `list_servers` | List project servers | `project` |
24+
| `list_deployments` | List deployments with pagination | `project`, `page?`, `server_uuid?` |
25+
| `get_deployment` | Get deployment details | `project`, `uuid` |
26+
| `get_deployment_log` | Get deployment log output | `project`, `uuid` |
27+
| `create_deployment` | Create new deployment | `project`, `parent_identifier`, `start_revision`, `end_revision`, + optional params |
1828

1929
### `list_projects`
2030

@@ -49,6 +59,15 @@ Get detailed information about a specific deployment.
4959
- `project` (string): Project permalink
5060
- `uuid` (string): Deployment UUID
5161

62+
### `get_deployment_log`
63+
Get the deployment log for a specific deployment. Useful for debugging failed deployments.
64+
65+
**Parameters**:
66+
- `project` (string): Project permalink
67+
- `uuid` (string): Deployment UUID
68+
69+
**Returns**: Complete deployment log as text
70+
5271
### `create_deployment`
5372
Create a new deployment for a project.
5473

@@ -94,19 +113,158 @@ Add to your `.claude.json` file in your project directory.
94113
"DEPLOYHQ_EMAIL": "[email protected]",
95114
"DEPLOYHQ_API_KEY": "your-password",
96115
"DEPLOYHQ_ACCOUNT": "your-account-name"
116+
// Optional: "LOG_LEVEL": "INFO" (ERROR, INFO, or DEBUG)
97117
}
98118
}
99119
}
100120
}
101121
```
102122

123+
**Note**: Only the 3 DeployHQ credentials are required. `LOG_LEVEL` is optional and defaults to `INFO`.
124+
103125
### Start Using
104126

105127
Once configured, you can ask Claude to interact with DeployHQ:
106128
- "List all my DeployHQ projects"
107129
- "Show me the servers for project X"
108130
- "Get the latest deployment status for project Y"
109131
- "Create a new deployment for project Z"
132+
- "Show me the deployment log for the latest deployment"
133+
134+
## 💡 Common Usage Examples
135+
136+
### Check Deployment Status
137+
```
138+
User: What's the status of my latest deployment for my-app?
139+
Claude: [Uses list_deployments → get_deployment → shows status]
140+
```
141+
142+
### Debug Failed Deployment
143+
```
144+
User: Why did the last deployment fail for my-app?
145+
Claude: [Uses list_deployments → get_deployment_log → analyzes log]
146+
```
147+
148+
### Deploy Latest Changes
149+
```
150+
User: Deploy the latest changes to production for my-app
151+
Claude: [Uses list_servers → list_deployments → create_deployment with use_latest]
152+
```
153+
154+
### Complete Workflow Example
155+
```
156+
User: I want to deploy my-app to production with the latest changes
157+
158+
Claude will:
159+
1. Use list_projects to find "my-app"
160+
2. Use list_servers to find production server UUID
161+
3. Use list_deployments with use_latest to get last revision
162+
4. Use create_deployment to queue deployment
163+
5. Use get_deployment to show status
164+
6. Use get_deployment_log if anything fails
165+
```
166+
167+
## 🔧 Configuration Options
168+
169+
### Environment Variables
170+
171+
#### Required
172+
- `DEPLOYHQ_EMAIL`: Your DeployHQ login email
173+
- `DEPLOYHQ_API_KEY`: Your DeployHQ password/API key
174+
- `DEPLOYHQ_ACCOUNT`: Your DeployHQ account name (from URL: `https://ACCOUNT.deployhq.com`)
175+
176+
#### Optional
177+
- `LOG_LEVEL`: Controls log verbosity - `ERROR`, `INFO`, or `DEBUG` (default: `INFO`)
178+
- `NODE_ENV`: Environment mode - `production` or `development`
179+
180+
### Log Levels
181+
182+
Control verbosity with the `LOG_LEVEL` environment variable:
183+
184+
- **ERROR**: Only show errors
185+
- **INFO**: Show info and errors (default)
186+
- **DEBUG**: Show all logs including detailed API calls
187+
188+
Example:
189+
```json
190+
{
191+
"mcpServers": {
192+
"deployhq": {
193+
"command": "npx",
194+
"args": ["-y", "deployhq-mcp-server"],
195+
"env": {
196+
"DEPLOYHQ_EMAIL": "[email protected]",
197+
"DEPLOYHQ_API_KEY": "your-password",
198+
"DEPLOYHQ_ACCOUNT": "your-account-name",
199+
"LOG_LEVEL": "DEBUG"
200+
}
201+
}
202+
}
203+
}
204+
```
205+
206+
## 🐛 Troubleshooting
207+
208+
### Server Won't Start
209+
210+
**Problem**: Server exits immediately after starting
211+
212+
**Solutions**:
213+
- Check that all required environment variables are set
214+
- Verify Node.js version is 18 or higher: `node --version`
215+
- Check logs in Claude Desktop/Code for error messages
216+
- Try setting `LOG_LEVEL=DEBUG` for more details
217+
218+
### Authentication Errors
219+
220+
**Problem**: "Authentication failed" or 401/403 errors
221+
222+
**Solutions**:
223+
- Verify your email and API key are correct
224+
- Check that your API key hasn't expired
225+
- Ensure your account has API access enabled
226+
- Try logging into DeployHQ web interface with same credentials
227+
228+
### Project Not Found
229+
230+
**Problem**: "Project not found" or 404 errors
231+
232+
**Solutions**:
233+
- Use `list_projects` to see exact permalink format
234+
- Project permalinks are case-sensitive
235+
- Check that you have access to the project in DeployHQ
236+
237+
### Deployment Fails
238+
239+
**Problem**: Deployment created but fails immediately
240+
241+
**Solutions**:
242+
- Use `get_deployment_log` to see detailed error logs
243+
- Verify server UUID is correct with `list_servers`
244+
- Check that start and end revisions exist in repository
245+
- Ensure server has correct deploy keys configured
246+
247+
### Connection Timeouts
248+
249+
**Problem**: "Request timeout" errors
250+
251+
**Solutions**:
252+
- Check your internet connection
253+
- Verify DeployHQ API is accessible: `curl https://YOUR_ACCOUNT.deployhq.com`
254+
- Large deployment lists may take time - use pagination
255+
- Try again in a moment if DeployHQ is experiencing issues
256+
257+
### Logs Not Showing
258+
259+
**Problem**: Not seeing any log output
260+
261+
**Solutions**:
262+
- Logs go to stderr, not stdout (for stdio transport)
263+
- Check Claude Desktop/Code logs location:
264+
- macOS: `~/Library/Logs/Claude/`
265+
- Windows: `%APPDATA%\Claude\logs\`
266+
- Set `LOG_LEVEL=DEBUG` for verbose output
267+
- For hosted mode, check Digital Ocean logs
110268

111269
### Getting Your DeployHQ Credentials
112270

@@ -207,7 +365,7 @@ Configure your local `.claude.json` to use the built version:
207365
The project includes a comprehensive test suite using Vitest:
208366

209367
**Test Coverage:**
210-
-**Tool Schema Validation** - All 6 MCP tool schemas with valid/invalid inputs
368+
-**Tool Schema Validation** - All 7 MCP tool schemas with valid/invalid inputs
211369
-**API Client Methods** - All DeployHQ API methods with mocked responses
212370
-**Error Handling** - Authentication, validation, and network errors
213371
-**MCP Server Factory** - Server creation and configuration
@@ -221,7 +379,7 @@ npm run test:ui # Interactive UI for debugging
221379
```
222380

223381
**Test Stats:**
224-
- 51 tests across 3 test suites
382+
- 50+ tests across 3 test suites
225383
- Covers tools, api-client, and mcp-server modules
226384
- Uses mocked fetch for isolated unit testing
227385

src/__tests__/tools.test.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
ListServersSchema,
66
ListDeploymentsSchema,
77
GetDeploymentSchema,
8+
GetDeploymentLogSchema,
89
CreateDeploymentSchema,
910
tools,
1011
} from '../tools.js';
@@ -128,6 +129,30 @@ describe('Tool Schemas', () => {
128129
});
129130
});
130131

132+
describe('GetDeploymentLogSchema', () => {
133+
it('should validate with project and uuid', () => {
134+
const result = GetDeploymentLogSchema.safeParse({
135+
project: 'my-project',
136+
uuid: 'deployment-123',
137+
});
138+
expect(result.success).toBe(true);
139+
if (result.success) {
140+
expect(result.data.project).toBe('my-project');
141+
expect(result.data.uuid).toBe('deployment-123');
142+
}
143+
});
144+
145+
it('should fail without project', () => {
146+
const result = GetDeploymentLogSchema.safeParse({ uuid: 'deployment-123' });
147+
expect(result.success).toBe(false);
148+
});
149+
150+
it('should fail without uuid', () => {
151+
const result = GetDeploymentLogSchema.safeParse({ project: 'my-project' });
152+
expect(result.success).toBe(false);
153+
});
154+
});
155+
131156
describe('CreateDeploymentSchema', () => {
132157
it('should validate with required fields', () => {
133158
const result = CreateDeploymentSchema.safeParse({
@@ -211,8 +236,8 @@ describe('Tool Schemas', () => {
211236
});
212237

213238
describe('Tools Array', () => {
214-
it('should export 6 tools', () => {
215-
expect(tools).toHaveLength(6);
239+
it('should export 7 tools', () => {
240+
expect(tools).toHaveLength(7);
216241
});
217242

218243
it('should have correct tool names', () => {
@@ -222,6 +247,7 @@ describe('Tool Schemas', () => {
222247
expect(toolNames).toContain('list_servers');
223248
expect(toolNames).toContain('list_deployments');
224249
expect(toolNames).toContain('get_deployment');
250+
expect(toolNames).toContain('get_deployment_log');
225251
expect(toolNames).toContain('create_deployment');
226252
});
227253

src/api-client.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,83 @@ export class DeployHQClient {
294294
});
295295
}
296296

297+
/**
298+
* Gets the deployment log for a specific deployment
299+
* @param project - Project permalink
300+
* @param uuid - Deployment UUID
301+
* @returns Deployment log as text
302+
*/
303+
async getDeploymentLog(project: string, uuid: string): Promise<string> {
304+
const url = `${this.baseUrl}/projects/${project}/deployments/${uuid}/log`;
305+
306+
const controller = new AbortController();
307+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
308+
309+
try {
310+
const response = await fetch(url, {
311+
method: 'GET',
312+
headers: {
313+
'Authorization': this.authHeader,
314+
'Accept': 'text/plain',
315+
},
316+
signal: controller.signal,
317+
} as NodeFetchRequestInit);
318+
319+
clearTimeout(timeoutId);
320+
321+
if (response.status === 401 || response.status === 403) {
322+
throw new AuthenticationError('Invalid credentials or insufficient permissions');
323+
}
324+
325+
if (!response.ok) {
326+
const errorText = await response.text().catch(() => 'Unknown error');
327+
throw new DeployHQError(
328+
`Failed to fetch deployment log: ${response.statusText}`,
329+
response.status,
330+
errorText
331+
);
332+
}
333+
334+
return await response.text();
335+
} catch (error) {
336+
clearTimeout(timeoutId);
337+
338+
if (error instanceof DeployHQError) {
339+
throw error;
340+
}
341+
342+
if ((error as Error).name === 'AbortError') {
343+
throw new DeployHQError('Request timeout', 408);
344+
}
345+
346+
throw new DeployHQError(
347+
`Request failed: ${(error as Error).message}`,
348+
undefined,
349+
error
350+
);
351+
}
352+
}
353+
354+
/**
355+
* Validates credentials by attempting to list projects
356+
* @returns true if credentials are valid
357+
* @throws AuthenticationError if credentials are invalid
358+
*/
359+
async validateCredentials(): Promise<boolean> {
360+
try {
361+
await this.listProjects();
362+
return true;
363+
} catch (error) {
364+
if (error instanceof AuthenticationError) {
365+
throw error;
366+
}
367+
// Re-throw other errors as authentication failures for clarity
368+
throw new AuthenticationError(
369+
`Failed to validate credentials: ${(error as Error).message}`
370+
);
371+
}
372+
}
373+
297374
/**
298375
* Gets the base URL for this client
299376
* @returns Base URL

0 commit comments

Comments
 (0)