Skip to content

Latest commit

 

History

History
485 lines (384 loc) · 11.6 KB

File metadata and controls

485 lines (384 loc) · 11.6 KB

MCP Server Integration Guide

Contents: What is MCP? · Quick Start · Configuration · Usage · Troubleshooting

This guide explains how to create, configure, and use custom MCP (Model Context Protocol) servers with Maestro.

What is MCP?

MCP (Model Context Protocol) is an open protocol that allows AI assistants to interact with external tools and data sources. Maestro supports MCP servers, enabling you to extend its capabilities with custom tools.

Quick Start

1. Install an existing MCP server

# Example: GitHub MCP server
npm install -g @modelcontextprotocol/server-github

2. Configure Maestro to use it

Create ~/.maestro/mcp.json:

{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_TOKEN": "ghp_your_token_here"
      }
    }
  }
}

3. Verify connection

maestro
/mcp

You should see:

Model Context Protocol

● github
    Tools: list_issues, create_issue, get_repository, ...

Configuration

File Locations

  • Global config: ~/.maestro/mcp.json (applies to all projects)
  • Project config: .maestro/mcp.json (project-specific, overrides global)

Configuration Formats

Maestro supports two configuration formats:

Format 1: Claude Desktop Style (Recommended)

{
  "mcpServers": {
    "server-name": {
      "command": "node",
      "args": ["path/to/server.js"],
      "env": {
        "API_KEY": "your-key"
      },
      "cwd": "/path/to/working/directory"
    }
  }
}

Format 2: Array Style

{
  "servers": [
    {
      "name": "server-name",
      "transport": "stdio",
      "command": "node",
      "args": ["path/to/server.js"],
      "env": {
        "API_KEY": "your-key"
      }
    }
  ]
}

Configuration Options

Option Type Description Required
command string Executable to run Yes (for stdio)
args string[] Command arguments No
env object Environment variables No
cwd string Working directory No
url string Server URL (for HTTP/SSE) Yes (for HTTP/SSE)
headers object HTTP headers No
disabled boolean Disable this server No
timeout number Connection timeout (ms) No (default: 30000)

Transport Types

Maestro auto-detects the transport type:

  • stdio: Default when command is provided
  • sse: When URL contains /sse or sse subdomain
  • http: For other URLs

EvalOps Cerebro MCP

Managed EvalOps launches can attach the Cerebro world-model MCP server without adding a project config file. Configure one of:

  • MAESTRO_PLATFORM_MCP_URL
  • MAESTRO_AGENT_MCP_URL
  • MAESTRO_EVALOPS_AGENT_MCP_URL
  • MAESTRO_PLATFORM_MCP_MANIFEST_URL

The URL may be the public app base URL, the /mcp endpoint, or the EvalOps agent MCP manifest at /.well-known/evalops/agent-mcp.json; Maestro normalizes those forms to the HTTP MCP endpoint. Maestro forwards the bearer token from MAESTRO_PLATFORM_MCP_TOKEN, MAESTRO_AGENT_MCP_TOKEN, MAESTRO_EVALOPS_ACCESS_TOKEN, or EVALOPS_TOKEN. It also forwards X-EvalOps-Workspace-Id, X-EvalOps-Session-Id, X-EvalOps-Agent-Id, X-EvalOps-Agent-Run-Id, trace/request IDs, and X-EvalOps-Scopes.

For Cerebro, set scopes deliberately:

  • cerebro:read exposes cerebro_search, cerebro_gather_facts, cerebro_debug_beliefs, and the other read tools.
  • cerebro:assert additionally exposes cerebro_assert_fact for explicit, evidence-backed session learnings.

Agents should search or gather facts before asserting. Use cerebro_assert_fact only when the session learned durable context that future agents should recall, and always include a stable dimension, confidence reason, and evidence.

Creating a Custom MCP Server

Minimal TypeScript Server

// my-tools-server.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

const server = new Server(
  { name: "my-tools", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

// Define available tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "greet",
      description: "Generate a greeting message",
      inputSchema: {
        type: "object",
        properties: {
          name: {
            type: "string",
            description: "Name to greet",
          },
        },
        required: ["name"],
      },
    },
    {
      name: "calculate",
      description: "Perform basic math operations",
      inputSchema: {
        type: "object",
        properties: {
          operation: {
            type: "string",
            enum: ["add", "subtract", "multiply", "divide"],
          },
          a: { type: "number" },
          b: { type: "number" },
        },
        required: ["operation", "a", "b"],
      },
    },
  ],
}));

// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  switch (name) {
    case "greet":
      return {
        content: [
          { type: "text", text: `Hello, ${args.name}! Welcome to Maestro.` },
        ],
      };

    case "calculate": {
      const { operation, a, b } = args as {
        operation: string;
        a: number;
        b: number;
      };
      let result: number;
      switch (operation) {
        case "add":
          result = a + b;
          break;
        case "subtract":
          result = a - b;
          break;
        case "multiply":
          result = a * b;
          break;
        case "divide":
          result = b !== 0 ? a / b : NaN;
          break;
        default:
          throw new Error(`Unknown operation: ${operation}`);
      }
      return {
        content: [{ type: "text", text: `Result: ${result}` }],
      };
    }

    default:
      throw new Error(`Unknown tool: ${name}`);
  }
});

// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);

Build and Run

# Install dependencies
npm install @modelcontextprotocol/sdk

# Build
npx tsc my-tools-server.ts --module nodenext --moduleResolution nodenext

# Test locally
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node my-tools-server.js

Register with Maestro

{
  "mcpServers": {
    "my-tools": {
      "command": "node",
      "args": ["/path/to/my-tools-server.js"]
    }
  }
}

Tool Annotations

MCP tools can include behavior hints that Maestro respects:

{
  name: "delete_file",
  description: "Delete a file from the filesystem",
  inputSchema: { /* ... */ },
  annotations: {
    destructiveHint: true,   // May perform destructive actions
    readOnlyHint: false,     // Modifies environment
    idempotentHint: false,   // Multiple calls have different effects
    openWorldHint: true,     // Interacts with external systems
  }
}
Annotation Meaning
readOnlyHint Tool doesn't modify its environment
destructiveHint Tool may perform destructive updates
idempotentHint Safe to call repeatedly with same args
openWorldHint Tool interacts with external systems

Resources and Prompts

MCP servers can also provide resources (data) and prompts (templates):

Listing Resources

/mcp resources

Reading a Resource

/mcp resources my-server resource://path/to/resource

Listing Prompts

/mcp prompts

Getting a Prompt

/mcp prompts my-server prompt-name

Example MCP Servers

Database Query Server

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { Pool } from "pg";

const pool = new Pool({ connectionString: process.env.DATABASE_URL });

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "query") {
    const { sql } = request.params.arguments as { sql: string };

    // Safety: Only allow SELECT queries
    if (!sql.trim().toLowerCase().startsWith("select")) {
      return {
        content: [{ type: "text", text: "Error: Only SELECT queries allowed" }],
        isError: true,
      };
    }

    const result = await pool.query(sql);
    return {
      content: [
        { type: "text", text: JSON.stringify(result.rows, null, 2) },
      ],
    };
  }
});

API Integration Server

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "fetch_weather") {
    const { city } = request.params.arguments as { city: string };

    const response = await fetch(
      `https://api.weatherapi.com/v1/current.json?key=${process.env.WEATHER_API_KEY}&q=${city}`
    );
    const data = await response.json();

    return {
      content: [
        {
          type: "text",
          text: `Weather in ${city}: ${data.current.condition.text}, ${data.current.temp_c}°C`,
        },
      ],
    };
  }
});

File Watcher Server

import { watch } from "fs";

// Notify Maestro when files change
watch("./src", { recursive: true }, (event, filename) => {
  server.notification({
    method: "notifications/resources/list_changed",
  });
});

Troubleshooting

Server not connecting

  1. Check server is executable:

    node /path/to/server.js
  2. Verify config syntax:

    cat ~/.maestro/mcp.json | jq .
  3. Check Maestro logs:

    MAESTRO_LOG_LEVEL=debug maestro

Tools not appearing

  1. Verify server implements tools/list:

    echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node server.js
  2. Check for connection errors in /mcp output

Environment variables not passed

  • Only explicitly configured env vars are passed to stdio servers
  • System env vars are NOT inherited (security measure)
  • Add required vars to the env config block

Server crashes on startup

  • Check stderr output from the server
  • Ensure all dependencies are installed
  • Verify the working directory (cwd) is correct

Security Considerations

  1. Environment Isolation: MCP servers only receive explicitly configured env vars
  2. Input Validation: Always validate tool inputs before execution
  3. Principle of Least Privilege: Only expose necessary tools
  4. Secrets Management: Use env vars for API keys, never hardcode

API Reference

MCP SDK

npm install @modelcontextprotocol/sdk

Key imports:

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
  ListResourcesRequestSchema,
  ReadResourceRequestSchema,
  ListPromptsRequestSchema,
  GetPromptRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

Maestro MCP Commands

Command Description
/mcp Show server status and tools
/mcp resources List all resources
/mcp resources <server> <uri> Read a specific resource
/mcp prompts List all prompts
/mcp prompts <server> <name> Get a specific prompt

Further Resources