A Model Context Protocol (MCP) server for interacting with MAAS (Metal as a Service) through a standardized JSON-RPC 2.0 interface.
The MAAS MCP Server implements the Model Context Protocol specification to enable AI assistants to interact with MAAS (Metal as a Service) infrastructure. It provides a standardized interface for machine management, network configuration, storage management, and more through a JSON-RPC 2.0 API.
This server can operate in two transport modes:
- HTTP/HTTPS - For web-based interactions and SSE (Server-Sent Events) streaming
- stdin/stdout - For direct integration with AI assistants and CLI tools
- Go 1.22 or later
- MAAS server with API access
- MAAS API key in the format "consumer:token:secret"
curl -sSL https://raw.githubusercontent.com/lspecian/maas-mcp-server/main/scripts/install.sh | bash
-
Clone the repository:
git clone https://github.com/lspecian/maas-mcp-server.git cd maas-mcp-server
-
Build the server:
./build.sh build
-
Configure the server:
cp config/config.yaml.example config/config.yaml cp .env.example .env
-
Edit
config/config.yaml
and update:- MAAS API URL
- MAAS API key
- Server host/port
- Logging configuration
The server requires the following environment variables:
MAAS_API_URL
- The URL of your MAAS API endpointMAAS_API_KEY
- Your MAAS API key in the format "consumer:token:secret"LOG_LEVEL
- (Optional) The logging level (default: "info")LOG_FORMAT
- (Optional) The logging format (default: "json")AUTH_ENABLED
- (Optional) Whether authentication is enabled (default: "false")
These can be set in the .env
file or directly in your environment.
For integration with Roo, you can configure the server using the .roo/mcp.json
file. Here's an example configuration:
{
"mcpServers": {
"maas-server": {
"command": "./maas-mcp-server",
"args": [
"stdio"
],
"protocol": "stdio",
"jsonrpc": "2.0",
"readyMessage": "MCP server ready",
"env": {
"MAAS_API_URL": "http://your-maas-server:5240/MAAS",
"MAAS_API_KEY": "consumer:token:secret",
"LOG_LEVEL": "debug",
"LOG_FORMAT": "json",
"AUTH_ENABLED": "false"
},
"disabled": false,
"alwaysAllow": [
"maas_list_machines",
"maas_get_machine_details",
"maas_power_on_machine",
"maas_power_off_machine",
"list_machines"
]
}
}
}
The environment variables in the env
section are used to configure the server. The server will read these variables and use them to configure itself.
-
Build the Docker image:
./build-docker.sh
-
Run the container:
docker run -p 8081:8081 -v $(pwd)/config:/app/config maas-mcp-server
./build.sh run
The server will be available at http://localhost:8081/mcp.
./build.sh run-mcp-stdio
Or directly:
./maas-mcp-server stdio
In this mode, the server reads JSON-RPC requests from stdin and writes responses to stdout, making it suitable for integration with AI assistants.
To display the version information:
./maas-mcp-server --version
The server communicates using the JSON-RPC 2.0 protocol. Here's the basic message format:
{
"jsonrpc": "2.0",
"method": "method_name",
"params": {
"param1": "value1",
"param2": "value2"
},
"id": "request-id"
}
{
"jsonrpc": "2.0",
"result": {
"key1": "value1",
"key2": "value2"
},
"id": "request-id"
}
{
"jsonrpc": "2.0",
"error": {
"code": -32000,
"message": "Error message",
"data": {}
},
"id": "request-id"
}
To discover the capabilities of the MCP server, send a discovery request:
{
"jsonrpc": "2.0",
"method": "discover",
"params": {},
"id": "1"
}
The server will respond with information about available tools and resources:
{
"jsonrpc": "2.0",
"result": [
{
"name": "maas_list_machines",
"description": "List all machines managed by MAAS with filtering and pagination",
"parameters": {
"type": "object",
"properties": {
"hostname": { "type": "string" },
"zone": { "type": "string" },
"pool": { "type": "string" },
"status": { "type": "string" },
"power_state": { "type": "string" },
"system_id": { "type": "string" },
"architecture": { "type": "string" },
"tags": { "type": "array", "items": { "type": "string" } },
"limit": { "type": "integer" },
"page": { "type": "integer" }
}
}
},
{
"name": "maas_get_machine_details",
"description": "Get detailed information about a specific machine",
"parameters": {
"type": "object",
"properties": {
"system_id": { "type": "string" }
},
"required": ["system_id"]
}
}
],
"id": "1"
}
Lists all machines managed by MAAS with optional filtering and pagination.
Parameter | Type | Description |
---|---|---|
hostname | string | Filter by hostname |
zone | string | Filter by zone |
pool | string | Filter by resource pool |
status | string | Filter by status |
power_state | string | Filter by power state |
system_id | string | Filter by system ID |
architecture | string | Filter by architecture |
tags | array | Filter by tags |
storage_constraints | object | Filter by storage constraints |
limit | number | Maximum number of results to return |
page | number | Page number for pagination |
{
"jsonrpc": "2.0",
"method": "maas_list_machines",
"params": {
"zone": "default",
"limit": 10,
"page": 1
},
"id": "2"
}
{
"jsonrpc": "2.0",
"result": {
"machines": [
{
"id": "abc123",
"name": "machine-1",
"fqdn": "machine-1.maas",
"status": "Ready",
"architecture": "amd64/generic",
"power_state": "off",
"zone": "default",
"pool": "default",
"tags": ["virtual"],
"cpu_count": 4,
"memory_mb": 8192,
"os_info": {
"system": "ubuntu",
"distribution": "ubuntu",
"release": "22.04",
"version": "22.04 LTS"
},
"last_updated": "2025-05-15T12:00:00Z"
}
],
"total_count": 1,
"limit": 10,
"page": 1,
"page_count": 1
},
"id": "2"
}
Gets detailed information about a specific machine.
Parameter | Type | Description |
---|---|---|
system_id | string | The system ID of the machine |
{
"jsonrpc": "2.0",
"method": "maas_get_machine_details",
"params": {
"system_id": "abc123"
},
"id": "3"
}
{
"jsonrpc": "2.0",
"result": {
"id": "abc123",
"name": "machine-1",
"fqdn": "machine-1.maas",
"status": "Ready",
"architecture": "amd64/generic",
"power_state": "off",
"zone": "default",
"pool": "default",
"tags": ["virtual"],
"network_interfaces": [
{
"id": "eth0",
"name": "eth0",
"type": "physical",
"mac_address": "52:54:00:12:34:56",
"ip_address": "192.168.1.100",
"cidr": "192.168.1.0/24",
"subnet": "192.168.1.0/24",
"enabled": true,
"primary": true
}
],
"block_devices": [
{
"id": "sda",
"name": "sda",
"type": "physical",
"path": "/dev/sda",
"size_bytes": 107374182400,
"used_bytes": 107374182400,
"available_bytes": 0,
"model": "QEMU HARDDISK",
"partitions": [
{
"id": "sda1",
"number": 1,
"size_bytes": 1073741824,
"path": "/dev/sda1",
"filesystem": {
"type": "ext4",
"mount_point": "/boot"
}
},
{
"id": "sda2",
"number": 2,
"size_bytes": 106300440576,
"path": "/dev/sda2",
"filesystem": {
"type": "ext4",
"mount_point": "/"
}
}
]
}
],
"cpu_count": 4,
"memory_mb": 8192,
"os_info": {
"system": "ubuntu",
"distribution": "ubuntu",
"release": "22.04",
"version": "22.04 LTS"
},
"last_updated": "2025-05-15T12:00:00Z"
},
"id": "3"
}
package main
import (
"bufio"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
)
func main() {
// Start the MCP server in stdio mode
cmd := exec.Command("./build.sh", "run-mcp-stdio")
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
cmd.Start()
// Wait for server to start
reader := bufio.NewReader(stdout)
for {
line, _ := reader.ReadString('\n')
if line == "MCP server ready\n" {
break
}
}
// Send list machines request
request := map[string]interface{}{
"jsonrpc": "2.0",
"method": "maas_list_machines",
"params": map[string]interface{}{},
"id": "1",
}
requestJSON, _ := json.Marshal(request)
io.WriteString(stdin, string(requestJSON)+"\n")
// Read response
responseStr, _ := reader.ReadString('\n')
var response map[string]interface{}
json.Unmarshal([]byte(responseStr), &response)
// Pretty print the response
prettyJSON, _ := json.MarshalIndent(response, "", " ")
fmt.Println(string(prettyJSON))
cmd.Process.Kill()
}
package main
import (
"bufio"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
)
func main() {
// Start the MCP server in stdio mode
cmd := exec.Command("./build.sh", "run-mcp-stdio")
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
cmd.Start()
// Wait for server to start
reader := bufio.NewReader(stdout)
for {
line, _ := reader.ReadString('\n')
if line == "MCP server ready\n" {
break
}
}
// Get machine details
detailsRequest := map[string]interface{}{
"jsonrpc": "2.0",
"method": "maas_get_machine_details",
"params": map[string]string{"system_id": "abc123"},
"id": "1",
}
requestJSON, _ := json.Marshal(detailsRequest)
io.WriteString(stdin, string(requestJSON)+"\n")
// Read response
responseStr, _ := reader.ReadString('\n')
var response map[string]interface{}
json.Unmarshal([]byte(responseStr), &response)
// Check if machine is powered off
result := response["result"].(map[string]interface{})
if result["power_state"] == "off" {
// Power on the machine
powerRequest := map[string]interface{}{
"jsonrpc": "2.0",
"method": "maas_power_on_machine",
"params": map[string]string{"system_id": "abc123"},
"id": "2",
}
requestJSON, _ = json.Marshal(powerRequest)
io.WriteString(stdin, string(requestJSON)+"\n")
// Read power on response
powerResponseStr, _ := reader.ReadString('\n')
fmt.Println(powerResponseStr)
}
cmd.Process.Kill()
}
# Build the server
./build.sh build
# Build the clean architecture version
./build.sh build-mcp
# Run all tests
./build.sh test
# Run specific tests
go test -v ./internal/service/...
# Run the test client
go run test/go/test-stdio-client.go
The project uses GitHub Actions to automatically build and publish binaries for multiple platforms when a new tag is pushed to the repository.
- Update the version in
internal/version/version.go
- Update the
CHANGELOG.md
with the changes in the new version - Commit the changes
- Create and push a new tag:
git tag v1.0.0 git push origin v1.0.0
- The GitHub Actions workflow will automatically:
- Build binaries for Linux (amd64, arm64), macOS (amd64, arm64), and Windows (amd64)
- Create a GitHub Release with the tag name
- Upload the binaries as assets
- Generate SHA256 checksums for all binaries
- Add release notes based on the CHANGELOG.md
You can also trigger a manual release using the GitHub Actions workflow:
- Go to the GitHub repository
- Click on the "Actions" tab
- Select the "Manual Release" workflow
- Click on "Run workflow"
- Enter the version number (e.g., "1.1.0")
- Select whether this is a pre-release
- Click "Run workflow"
The workflow will:
- Build binaries for all supported platforms
- Create a GitHub Release with the specified version
- Upload the binaries as assets
- Generate SHA256 checksums for all binaries
- Add release notes from the CHANGELOG.md
Binaries follow a consistent naming pattern:
maas-mcp-server-{version}-{os}-{arch}[.exe]
For example:
maas-mcp-server-1.0.0-linux-amd64
maas-mcp-server-1.0.0-linux-arm64
maas-mcp-server-1.0.0-darwin-amd64
maas-mcp-server-1.0.0-darwin-arm64
maas-mcp-server-1.0.0-windows-amd64.exe
You can build the server for different platforms using the Go cross-compilation feature:
# Build for Linux AMD64
GOOS=linux GOARCH=amd64 go build -o maas-mcp-server-linux-amd64 pkg/mcp/cmd/main.go
# Build for Linux ARM64
GOOS=linux GOARCH=arm64 go build -o maas-mcp-server-linux-arm64 pkg/mcp/cmd/main.go
# Build for macOS AMD64
GOOS=darwin GOARCH=amd64 go build -o maas-mcp-server-darwin-amd64 pkg/mcp/cmd/main.go
# Build for macOS ARM64 (Apple Silicon)
GOOS=darwin GOARCH=arm64 go build -o maas-mcp-server-darwin-arm64 pkg/mcp/cmd/main.go
# Build for Windows AMD64
GOOS=windows GOARCH=amd64 go build -o maas-mcp-server-windows-amd64.exe pkg/mcp/cmd/main.go
Code | Message | Description |
---|---|---|
-32700 | Parse error | Invalid JSON was received |
-32600 | Invalid request | The JSON sent is not a valid Request object |
-32601 | Method not found | The method does not exist / is not available |
-32602 | Invalid params | Invalid method parameter(s) |
-32603 | Internal error | Internal JSON-RPC error |
-32000 | Authentication failed | Failed to authenticate with MAAS |
-32001 | Rate limit exceeded | Too many requests |
-32002 | Version not supported | The requested MCP version is not supported |
-32003 | Resource not found | The requested resource was not found |
-32004 | Operation failed | The requested operation failed |
Method | Description |
---|---|
discover | Discover server capabilities |
maas_list_machines | List all machines managed by MAAS |
maas_get_machine_details | Get detailed information about a specific machine |
Contributions are welcome! Please see CONTRIBUTING.md for details.
This project is licensed under the MIT License - see the LICENSE file for details.