Changes:
- Create Gitea workflow for ai-stack-deployer
- Trigger on main branch (default branch)
- Use oussamadouhou + REGISTRY_TOKEN for authentication
- Build from ./Dockerfile
This enables :latest tag creation via {{is_default_branch}}.
Tags created:
- git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:latest
- git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:<sha>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
470 lines
12 KiB
Markdown
470 lines
12 KiB
Markdown
# AI Stack Deployer - MCP Server Guide
|
|
|
|
## Overview
|
|
|
|
This project now includes a **Model Context Protocol (MCP) Server** that exposes deployment functionality to Claude Code and other MCP-compatible clients.
|
|
|
|
### What is MCP?
|
|
|
|
The Model Context Protocol is a standardized way for AI assistants to interact with external tools and services. By implementing an MCP server, this project allows Claude Code to:
|
|
|
|
- Deploy new AI stacks programmatically
|
|
- Check deployment status
|
|
- Verify name availability
|
|
- Test API connections
|
|
- List all deployments
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────────────────┐
|
|
│ CLAUDE CODE (MCP Client) │
|
|
│ - Discovers available tools │
|
|
│ - Calls tools with parameters │
|
|
│ - Receives structured responses │
|
|
└────────────────────────┬─────────────────────────────────────┘
|
|
│
|
|
│ MCP Protocol (stdio)
|
|
│
|
|
┌────────────────────────▼─────────────────────────────────────┐
|
|
│ AI Stack Deployer MCP Server │
|
|
│ (src/mcp-server.ts) │
|
|
│ │
|
|
│ Available Tools: │
|
|
│ ✓ deploy_stack │
|
|
│ ✓ check_deployment_status │
|
|
│ ✓ list_deployments │
|
|
│ ✓ check_name_availability │
|
|
│ ✓ test_api_connections │
|
|
└────────────────────────┬─────────────────────────────────────┘
|
|
│
|
|
│ Uses existing API clients
|
|
│
|
|
┌────────────────────────▼─────────────────────────────────────┐
|
|
│ Existing Infrastructure │
|
|
│ - Hetzner DNS API (src/api/hetzner.ts) │
|
|
│ - Dokploy API (src/api/dokploy.ts) │
|
|
└───────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## What Was Created
|
|
|
|
### 1. MCP Server Implementation (`src/mcp-server.ts`)
|
|
|
|
A fully-functional MCP server that:
|
|
- Integrates with existing Hetzner and Dokploy API clients
|
|
- Validates stack names according to project rules
|
|
- Tracks deployment state in memory
|
|
- Handles errors gracefully
|
|
- Returns structured JSON responses
|
|
|
|
### 2. Project Configuration (`.mcp.json`)
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"ai-stack-deployer": {
|
|
"command": "bun",
|
|
"args": ["run", "src/mcp-server.ts"],
|
|
"env": {}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
This file tells Claude Code how to start the MCP server.
|
|
|
|
### 3. Package Script (`package.json`)
|
|
|
|
Added `"mcp": "bun run src/mcp-server.ts"` to scripts for easy testing.
|
|
|
|
---
|
|
|
|
## How to Enable in Claude Code
|
|
|
|
### Step 1: Restart Claude Code
|
|
|
|
After creating the `.mcp.json` file, you need to restart Claude Code for it to discover the MCP server.
|
|
|
|
```bash
|
|
# If Claude Code is running, exit and restart
|
|
opencode
|
|
```
|
|
|
|
### Step 2: Approve the MCP Server
|
|
|
|
When Claude Code starts in this directory, it will detect the `.mcp.json` file and prompt you to approve the MCP server.
|
|
|
|
**You'll see a prompt like:**
|
|
```
|
|
Found MCP server configuration:
|
|
- ai-stack-deployer
|
|
|
|
Would you like to enable this MCP server? (y/n)
|
|
```
|
|
|
|
Type `y` to approve.
|
|
|
|
### Step 3: Verify MCP Server is Running
|
|
|
|
Claude Code will automatically start the MCP server when needed. You can verify it's working by asking Claude Code:
|
|
|
|
```
|
|
Can you list the available MCP tools?
|
|
```
|
|
|
|
You should see the 5 tools from the AI Stack Deployer.
|
|
|
|
---
|
|
|
|
## Available Tools
|
|
|
|
### 1. `deploy_stack`
|
|
|
|
Deploys a new AI coding assistant stack.
|
|
|
|
**Parameters:**
|
|
- `name` (string, required): Username for the stack (3-20 chars, lowercase alphanumeric and hyphens)
|
|
|
|
**Returns:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"deploymentId": "dep_1704830000000_abc123",
|
|
"name": "john",
|
|
"status": "completed",
|
|
"url": "https://john.ai.flexinit.nl",
|
|
"message": "Stack successfully deployed at https://john.ai.flexinit.nl"
|
|
}
|
|
```
|
|
|
|
**Example usage in Claude Code:**
|
|
```
|
|
Deploy an AI stack for user "alice"
|
|
```
|
|
|
|
---
|
|
|
|
### 2. `check_deployment_status`
|
|
|
|
Check the status of a deployment.
|
|
|
|
**Parameters:**
|
|
- `deploymentId` (string, required): The deployment ID from `deploy_stack`
|
|
|
|
**Returns:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"deployment": {
|
|
"id": "dep_1704830000000_abc123",
|
|
"name": "john",
|
|
"status": "completed",
|
|
"url": "https://john.ai.flexinit.nl",
|
|
"createdAt": "2026-01-09T17:30:00.000Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Possible statuses:**
|
|
- `initializing` - Starting deployment
|
|
- `creating_dns` - Creating DNS records
|
|
- `creating_project` - Creating Dokploy project
|
|
- `creating_application` - Creating application
|
|
- `deploying` - Deploying container
|
|
- `completed` - Successfully deployed
|
|
- `failed` - Deployment failed
|
|
|
|
---
|
|
|
|
### 3. `list_deployments`
|
|
|
|
List all recent deployments.
|
|
|
|
**Parameters:** None
|
|
|
|
**Returns:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"deployments": [
|
|
{
|
|
"id": "dep_1704830000000_abc123",
|
|
"name": "john",
|
|
"status": "completed",
|
|
"url": "https://john.ai.flexinit.nl",
|
|
"createdAt": "2026-01-09T17:30:00.000Z"
|
|
}
|
|
],
|
|
"total": 1
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 4. `check_name_availability`
|
|
|
|
Check if a stack name is available and valid.
|
|
|
|
**Parameters:**
|
|
- `name` (string, required): The name to check
|
|
|
|
**Returns:**
|
|
```json
|
|
{
|
|
"available": true,
|
|
"valid": true,
|
|
"name": "john"
|
|
}
|
|
```
|
|
|
|
Or if invalid:
|
|
```json
|
|
{
|
|
"available": false,
|
|
"valid": false,
|
|
"error": "Name must be between 3 and 20 characters"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 5. `test_api_connections`
|
|
|
|
Test connections to Hetzner DNS and Dokploy APIs.
|
|
|
|
**Parameters:** None
|
|
|
|
**Returns:**
|
|
```json
|
|
{
|
|
"hetzner": {
|
|
"success": true,
|
|
"message": "Connected to Hetzner Cloud DNS API. Zone \"flexinit.nl\" has 75 RRSets.",
|
|
"recordCount": 75,
|
|
"zoneName": "flexinit.nl"
|
|
},
|
|
"dokploy": {
|
|
"success": true,
|
|
"message": "Connected to Dokploy API. Found 12 projects.",
|
|
"projectCount": 12
|
|
},
|
|
"overall": true
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Testing the MCP Server
|
|
|
|
### Manual Test (Direct Invocation)
|
|
|
|
You can test the MCP server directly:
|
|
|
|
```bash
|
|
# Start the MCP server
|
|
bun run mcp
|
|
|
|
# It will wait for JSON-RPC messages on stdin
|
|
# Press Ctrl+C to exit
|
|
```
|
|
|
|
### Test via Claude Code
|
|
|
|
Once enabled in Claude Code, you can test it by asking:
|
|
|
|
```
|
|
Test the API connections for the AI Stack Deployer
|
|
```
|
|
|
|
Claude Code will invoke the `test_api_connections` tool and show you the results.
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### MCP Server Not Appearing in Claude Code
|
|
|
|
1. **Check `.mcp.json` exists** in the project root
|
|
2. **Restart Claude Code** completely
|
|
3. **Check for syntax errors** in `.mcp.json`
|
|
4. **Ensure Bun is installed** and in PATH
|
|
|
|
### Tools Not Working
|
|
|
|
1. **Check environment variables** in `.env`:
|
|
```bash
|
|
cat .env
|
|
```
|
|
|
|
2. **Test API connections**:
|
|
```bash
|
|
bun run src/test-clients.ts
|
|
```
|
|
|
|
3. **Check Dokploy token** (common issue):
|
|
- Navigate to https://deploy.intra.flexinit.nl
|
|
- Settings → Profile → API Tokens
|
|
- Generate new token if expired
|
|
|
|
### Deployment Fails
|
|
|
|
1. **DNS issues**: Verify Hetzner API token is valid
|
|
2. **Dokploy issues**: Verify Dokploy API token and URL
|
|
3. **Name conflicts**: Check if name already exists
|
|
4. **Permissions**: Ensure API tokens have required permissions
|
|
|
|
---
|
|
|
|
## Security Considerations
|
|
|
|
### Environment Variables
|
|
|
|
The MCP server inherits environment variables from the parent process. The `.mcp.json` file has an empty `env` object, which means it will use:
|
|
|
|
1. Variables from `.env` file (loaded by Bun)
|
|
2. Variables from the shell environment
|
|
|
|
**Never commit** `.env` file to version control!
|
|
|
|
### API Token Safety
|
|
|
|
- Hetzner and Dokploy API tokens are read from environment variables
|
|
- Tokens are never exposed in MCP responses
|
|
- All API calls are authenticated
|
|
|
|
---
|
|
|
|
## Integration Examples
|
|
|
|
### Example 1: Deploy Stack from Claude Code
|
|
|
|
```
|
|
User: Deploy an AI stack for user "bob"
|
|
|
|
Claude: I'll deploy an AI stack for "bob" using the deploy_stack tool.
|
|
[Calls deploy_stack with name="bob"]
|
|
|
|
Result:
|
|
✓ Deployment successful!
|
|
- Deployment ID: dep_1704830000000_xyz789
|
|
- URL: https://bob.ai.flexinit.nl
|
|
- Status: completed
|
|
```
|
|
|
|
### Example 2: Check All Deployments
|
|
|
|
```
|
|
User: Show me all recent deployments
|
|
|
|
Claude: I'll list all deployments using the list_deployments tool.
|
|
[Calls list_deployments]
|
|
|
|
Result:
|
|
Total: 3 deployments
|
|
1. alice - https://alice.ai.flexinit.nl (completed)
|
|
2. bob - https://bob.ai.flexinit.nl (completed)
|
|
3. charlie - https://charlie.ai.flexinit.nl (failed)
|
|
```
|
|
|
|
### Example 3: Validate Name Before Deploying
|
|
|
|
```
|
|
User: Can I use the name "test" for a new stack?
|
|
|
|
Claude: Let me check if "test" is available.
|
|
[Calls check_name_availability with name="test"]
|
|
|
|
Result: ❌ Name "test" is reserved and cannot be used.
|
|
Reserved names: admin, api, www, root, system, test, demo, portal
|
|
```
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
### Enhance the MCP Server
|
|
|
|
Consider adding these tools:
|
|
|
|
1. **`delete_stack`** - Remove a deployed stack
|
|
2. **`get_stack_logs`** - Retrieve application logs
|
|
3. **`restart_stack`** - Restart a deployed stack
|
|
4. **`list_available_images`** - Show available Docker images
|
|
5. **`get_stack_metrics`** - Show resource usage
|
|
|
|
### Production Deployment
|
|
|
|
1. **Add authentication** to the MCP server
|
|
2. **Rate limiting** for deployments
|
|
3. **Persistent storage** for deployment state (currently in-memory)
|
|
4. **Webhooks** for deployment status updates
|
|
5. **Audit logging** for all operations
|
|
|
|
---
|
|
|
|
## Technical Details
|
|
|
|
### Protocol Used
|
|
|
|
- **Transport**: stdio (standard input/output)
|
|
- **Message Format**: JSON-RPC 2.0
|
|
- **SDK**: `@modelcontextprotocol/sdk` v1.25.2
|
|
|
|
### State Management
|
|
|
|
Currently, deployment state is stored in-memory using a `Map`:
|
|
- ✅ Fast access
|
|
- ✅ Simple implementation
|
|
- ❌ Lost on server restart
|
|
- ❌ Not shared across instances
|
|
|
|
For production, consider:
|
|
- Redis for distributed state
|
|
- PostgreSQL for persistent storage
|
|
- File-based storage for simplicity
|
|
|
|
### Error Handling
|
|
|
|
The MCP server wraps all tool calls in try-catch blocks and returns structured errors:
|
|
|
|
```json
|
|
{
|
|
"success": false,
|
|
"error": "Name already taken"
|
|
}
|
|
```
|
|
|
|
This ensures Claude Code always receives parseable responses.
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
✅ **MCP Server**: Fully implemented in `src/mcp-server.ts`
|
|
✅ **Configuration**: Added `.mcp.json` for Claude Code
|
|
✅ **Tools**: 5 tools for deployment management
|
|
✅ **Integration**: Uses existing API clients
|
|
✅ **Testing**: Server starts successfully
|
|
✅ **Documentation**: This guide
|
|
|
|
**You can now use Claude Code to deploy and manage AI stacks through natural language commands!**
|
|
|
|
---
|
|
|
|
## Support
|
|
|
|
For issues or questions:
|
|
1. Check this guide first
|
|
2. Review `TESTING.md` for API connection issues
|
|
3. Check Claude Code logs: `~/.config/claude/debug/`
|
|
4. Test API clients directly: `bun run src/test-clients.ts`
|
|
|
|
---
|
|
|
|
**Built with ❤️ by Oussama Douhou**
|