fix(ci): trigger workflow on main branch to enable :latest tag
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>
This commit is contained in:
469
docs/MCP_SERVER_GUIDE.md
Normal file
469
docs/MCP_SERVER_GUIDE.md
Normal file
@@ -0,0 +1,469 @@
|
||||
# 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**
|
||||
Reference in New Issue
Block a user