Skip to content

Build Your First MCP Server in 10 Minutes โ€‹

Welcome to MCP! In this tutorial, you'll build a working MCP server from scratch in just 10 minutes. By the end, you'll have a server that provides useful tools and understand the core concepts.

๐ŸŽฏ What You'll Build โ€‹

A Personal Assistant Server that provides:

  • โœ… Calculator tool - Perform mathematical calculations
  • โœ… Note-taking tool - Save and retrieve notes
  • โœ… System info resource - Get system information
  • โœ… Help prompt - Guide users on available features

๐Ÿ“‹ Prerequisites โ€‹

  • PHP 8.1+ installed
  • Composer installed
  • 10 minutes of your time
  • Basic PHP knowledge (arrays, functions, classes)

๐Ÿš€ Let's Build! โ€‹

Step 1: Create Project Structure (1 minute) โ€‹

bash
# Create a new directory for your server
mkdir my-first-mcp-server
cd my-first-mcp-server

# Initialize composer project
composer init --name="my-company/mcp-server" --no-interaction

# Install PHP MCP SDK
composer require dalehurley/php-mcp-sdk

Step 2: Create the Server File (3 minutes) โ€‹

Create personal-assistant-server.php:

php
#!/usr/bin/env php
<?php

require_once __DIR__ . '/vendor/autoload.php';

use MCP\Server\McpServer;
use MCP\Server\Transport\StdioServerTransport;
use MCP\Types\Implementation;
use MCP\Types\McpError;
use MCP\Types\ErrorCode;
use function Amp\async;

// Create server with implementation info
$server = new McpServer(
    new Implementation(
        'personal-assistant',           // Server name
        '1.0.0',                       // Version
        'Personal Assistant MCP Server' // Description
    )
);

// In-memory storage for notes (in production, use a database)
$notes = [];

// ๐Ÿงฎ TOOL 1: Calculator
$server->tool(
    'calculate',
    'Perform mathematical calculations',
    [
        'type' => 'object',
        'properties' => [
            'expression' => [
                'type' => 'string',
                'description' => 'Mathematical expression to evaluate (e.g., "2 + 2", "10 * 5")'
            ]
        ],
        'required' => ['expression']
    ],
    function (array $params): array {
        $expression = $params['expression'];

        // Basic security: only allow safe mathematical operations
        if (!preg_match('/^[0-9+\-*\/\(\)\.\s]+$/', $expression)) {
            throw new McpError(
                ErrorCode::InvalidParams,
                'Expression contains invalid characters. Only numbers and basic operators (+, -, *, /, parentheses) are allowed.'
            );
        }

        try {
            // Evaluate the expression safely
            $result = eval("return {$expression};");

            return [
                'content' => [[
                    'type' => 'text',
                    'text' => "Calculation: {$expression} = {$result}"
                ]]
            ];
        } catch (\ParseError $e) {
            throw new McpError(
                ErrorCode::InvalidParams,
                "Invalid mathematical expression: {$expression}"
            );
        }
    }
);

// ๐Ÿ“ TOOL 2: Note Management
$server->tool(
    'save-note',
    'Save a note with a title and content',
    [
        'type' => 'object',
        'properties' => [
            'title' => [
                'type' => 'string',
                'description' => 'Title of the note'
            ],
            'content' => [
                'type' => 'string',
                'description' => 'Content of the note'
            ]
        ],
        'required' => ['title', 'content']
    ],
    function (array $params) use (&$notes): array {
        $title = $params['title'];
        $content = $params['content'];
        $timestamp = date('Y-m-d H:i:s');

        $notes[$title] = [
            'content' => $content,
            'created' => $timestamp,
            'updated' => $timestamp
        ];

        return [
            'content' => [[
                'type' => 'text',
                'text' => "Note '{$title}' saved successfully at {$timestamp}"
            ]]
        ];
    }
);

$server->tool(
    'get-note',
    'Retrieve a saved note by title',
    [
        'type' => 'object',
        'properties' => [
            'title' => [
                'type' => 'string',
                'description' => 'Title of the note to retrieve'
            ]
        ],
        'required' => ['title']
    ],
    function (array $params) use (&$notes): array {
        $title = $params['title'];

        if (!isset($notes[$title])) {
            throw new McpError(
                ErrorCode::InvalidParams,
                "Note '{$title}' not found"
            );
        }

        $note = $notes[$title];

        return [
            'content' => [[
                'type' => 'text',
                'text' => "Note: {$title}\nCreated: {$note['created']}\nUpdated: {$note['updated']}\n\nContent:\n{$note['content']}"
            ]]
        ];
    }
);

$server->tool(
    'list-notes',
    'List all saved notes',
    [
        'type' => 'object',
        'properties' => []
    ],
    function (array $params) use (&$notes): array {
        if (empty($notes)) {
            return [
                'content' => [[
                    'type' => 'text',
                    'text' => 'No notes saved yet. Use save-note to create your first note!'
                ]]
            ];
        }

        $notesList = "Saved Notes:\n\n";
        foreach ($notes as $title => $note) {
            $notesList .= "๐Ÿ“ {$title}\n";
            $notesList .= "   Created: {$note['created']}\n";
            $notesList .= "   Preview: " . substr($note['content'], 0, 50) . "...\n\n";
        }

        return [
            'content' => [[
                'type' => 'text',
                'text' => $notesList
            ]]
        ];
    }
);

// ๐Ÿ’ป RESOURCE: System Information
$server->resource(
    'system-info',
    'system://info',
    'application/json',
    function (string $uri): array {
        $systemInfo = [
            'server_name' => 'Personal Assistant MCP Server',
            'version' => '1.0.0',
            'php_version' => PHP_VERSION,
            'memory_usage' => memory_get_usage(true),
            'peak_memory' => memory_get_peak_usage(true),
            'uptime' => time() - $_SERVER['REQUEST_TIME'],
            'timestamp' => date('c'),
            'tools_count' => 4, // We have 4 tools
            'resources_count' => 1 // We have 1 resource
        ];

        return [
            'contents' => [[
                'uri' => $uri,
                'mimeType' => 'application/json',
                'text' => json_encode($systemInfo, JSON_PRETTY_PRINT)
            ]]
        ];
    }
);

// ๐Ÿ’ก PROMPT: Help and Usage Guide
$server->prompt(
    'help',
    'Get help on how to use the personal assistant',
    [
        [
            'name' => 'topic',
            'description' => 'Specific topic to get help with (optional)',
            'required' => false
        ]
    ],
    function (array $arguments): array {
        $topic = $arguments['topic'] ?? 'general';

        $helpContent = match ($topic) {
            'calculator' => "๐Ÿงฎ Calculator Help:\n\nUse the 'calculate' tool to perform mathematical calculations.\n\nExamples:\n- calculate('2 + 2')\n- calculate('10 * 5 + 3')\n- calculate('(100 - 20) / 4')\n\nSupported operations: +, -, *, /, parentheses",

            'notes' => "๐Ÿ“ Notes Help:\n\nManage your notes with these tools:\n\n- save-note(title, content) - Save a new note\n- get-note(title) - Retrieve a note by title\n- list-notes() - See all your notes\n\nNotes are stored in memory and will be lost when the server restarts.",

            'system' => "๐Ÿ’ป System Help:\n\nAccess system information:\n\n- Read resource 'system://info' to get server status and system information\n\nThis includes PHP version, memory usage, uptime, and more.",

            default => "๐Ÿค– Personal Assistant Help:\n\nWelcome! I'm your personal assistant MCP server. Here's what I can help you with:\n\n๐Ÿงฎ **Calculator**: Perform mathematical calculations\n๐Ÿ“ **Notes**: Save, retrieve, and list personal notes\n๐Ÿ’ป **System Info**: Get server and system information\n\n**Available Tools:**\n- calculate - Mathematical calculations\n- save-note - Save a note\n- get-note - Retrieve a note\n- list-notes - List all notes\n\n**Available Resources:**\n- system://info - System information\n\n**Getting Help:**\nUse this prompt with different topics:\n- help(topic='calculator') - Calculator help\n- help(topic='notes') - Notes help\n- help(topic='system') - System help\n\nHappy assisting! ๐Ÿš€"
        };

        return [
            'description' => "Help for {$topic}",
            'messages' => [[
                'role' => 'assistant',
                'content' => [[
                    'type' => 'text',
                    'text' => $helpContent
                ]]
            ]]
        ];
    }
);

// ๐Ÿš€ Start the server
echo "๐Ÿค– Starting Personal Assistant MCP Server...\n";
echo "Available capabilities:\n";
echo "  ๐Ÿงฎ Calculator tool\n";
echo "  ๐Ÿ“ Note management tools\n";
echo "  ๐Ÿ’ป System information resource\n";
echo "  ๐Ÿ’ก Interactive help prompt\n";
echo "\nโœ… Server ready for connections!\n";

async(function() use ($server) {
    $transport = new StdioServerTransport();
    $server->connect($transport)->await();
})->await();

Step 3: Make It Executable (30 seconds) โ€‹

bash
chmod +x personal-assistant-server.php

Step 4: Test Your Server (2 minutes) โ€‹

bash
# Install MCP Inspector (requires Node.js)
npm install -g @modelcontextprotocol/inspector

# Test your server
mcp-inspector ./personal-assistant-server.php

This opens a web interface where you can:

  • โœ… View all available tools and resources
  • โœ… Test tool calls with different parameters
  • โœ… Inspect JSON-RPC messages
  • โœ… Debug any issues

Option B: Test with a Simple Client โ€‹

Create test-client.php:

php
#!/usr/bin/env php
<?php

require_once __DIR__ . '/vendor/autoload.php';

use MCP\Client\Client;
use MCP\Client\Transport\StdioClientTransport;
use MCP\Types\Implementation;
use function Amp\async;

echo "๐Ÿงช Testing Personal Assistant Server\n";
echo "===================================\n\n";

$client = new Client(new Implementation('test-client', '1.0.0'));

$transport = new StdioClientTransport([
    'command' => 'php',
    'args' => [__DIR__ . '/personal-assistant-server.php']
]);

async(function() use ($client, $transport) {
    try {
        // Connect to server
        echo "๐Ÿ”Œ Connecting to server...\n";
        $client->connect($transport)->await();
        echo "โœ… Connected successfully!\n\n";

        // Test calculator tool
        echo "๐Ÿงฎ Testing calculator...\n";
        $calcResult = $client->callTool('calculate', ['expression' => '15 + 27'])->await();
        echo "Result: {$calcResult['content'][0]['text']}\n\n";

        // Test note saving
        echo "๐Ÿ“ Testing note saving...\n";
        $saveResult = $client->callTool('save-note', [
            'title' => 'My First Note',
            'content' => 'This is my first note created with MCP!'
        ])->await();
        echo "Result: {$saveResult['content'][0]['text']}\n\n";

        // Test note retrieval
        echo "๐Ÿ“– Testing note retrieval...\n";
        $getResult = $client->callTool('get-note', ['title' => 'My First Note'])->await();
        echo "Result:\n{$getResult['content'][0]['text']}\n\n";

        // Test system resource
        echo "๐Ÿ’ป Testing system resource...\n";
        $sysResult = $client->readResource('system://info')->await();
        echo "System Info:\n{$sysResult['contents'][0]['text']}\n\n";

        // Test help prompt
        echo "๐Ÿ’ก Testing help prompt...\n";
        $helpResult = $client->getPrompt('help')->await();
        echo "Help Content:\n{$helpResult['messages'][0]['content'][0]['text']}\n\n";

        // Clean shutdown
        echo "๐Ÿ”Œ Disconnecting...\n";
        $client->close()->await();
        echo "โœ… Test completed successfully!\n";

    } catch (\Exception $e) {
        echo "โŒ Test failed: {$e->getMessage()}\n";
    }
})->await();
bash
chmod +x test-client.php
php test-client.php

Step 5: Understanding Your Server (3 minutes) โ€‹

Let's break down what you just built:

๐Ÿ—๏ธ Server Structure โ€‹

php
// 1. Server Creation
$server = new McpServer(
    new Implementation('name', 'version', 'description')
);

// 2. Tool Registration
$server->tool($name, $description, $schema, $handler);

// 3. Resource Registration
$server->resource($name, $uri, $mimeType, $handler);

// 4. Prompt Registration
$server->prompt($name, $description, $arguments, $handler);

// 5. Start Server
$server->connect($transport)->await();

๐Ÿ”ง Tool Components โ€‹

Every tool has four parts:

  1. Name - Unique identifier ('calculate')
  2. Description - Human-readable description
  3. Schema - JSON Schema for input validation
  4. Handler - Function that does the work
php
$server->tool(
    'tool-name',                    // 1. Name
    'What the tool does',           // 2. Description
    [                               // 3. Schema
        'type' => 'object',
        'properties' => [/* ... */]
    ],
    function (array $params): array { // 4. Handler
        // Your logic here
        return ['content' => [/* results */]];
    }
);

๐Ÿ“ฆ Resource Components โ€‹

Resources provide data through URI templates:

php
$server->resource(
    'resource-name',               // Resource name
    'scheme://path',               // URI template
    'application/json',            // MIME type
    function (string $uri): array { // Content provider
        return ['contents' => [/* data */]];
    }
);

๐Ÿ’ญ Prompt Components โ€‹

Prompts help LLMs understand how to use your server:

php
$server->prompt(
    'prompt-name',                 // Prompt name
    'What the prompt does',        // Description
    [                              // Arguments
        [
            'name' => 'arg1',
            'description' => 'First argument',
            'required' => true
        ]
    ],
    function (array $args): array { // Content generator
        return [
            'description' => 'Prompt description',
            'messages' => [/* chat messages */]
        ];
    }
);

๐ŸŽ‰ Congratulations! โ€‹

You've just built your first MCP server! Here's what you accomplished:

โœ… Created a working MCP server with multiple capabilities
โœ… Implemented tools that perform useful functions
โœ… Added resources that provide system information
โœ… Created prompts that guide user interaction
โœ… Tested everything with a client

๐Ÿš€ Next Steps โ€‹

Now that you have a working server, here are some ideas to explore:

Immediate Enhancements (10 minutes each) โ€‹

  1. Add More Tools:

    php
    // Weather tool
    $server->tool('get-weather', 'Get weather information', $schema, $weatherHandler);
    
    // File operations
    $server->tool('read-file', 'Read file contents', $schema, $fileHandler);
    
    // Database queries
    $server->tool('query-db', 'Query database', $schema, $dbHandler);
  2. Add More Resources:

    php
    // Configuration files
    $server->resource('config', 'config://{env}', 'application/json', $configHandler);
    
    // Log files
    $server->resource('logs', 'logs://{date}', 'text/plain', $logHandler);
  3. Persist Notes (use SQLite or files):

    php
    // Replace in-memory storage with SQLite
    $pdo = new PDO('sqlite:notes.db');
    $pdo->exec('CREATE TABLE IF NOT EXISTS notes (title TEXT PRIMARY KEY, content TEXT, created TEXT)');

Learning Path โ€‹

  1. ๐Ÿ“– Learn Core Concepts: Understanding MCP
  2. ๐Ÿ”ง Advanced Tools: Creating Servers
  3. ๐Ÿ” Add Security: Authentication
  4. ๐ŸŒ Web Deployment: Transports
  5. ๐Ÿ—๏ธ Framework Integration: Laravel Integration

Real-World Examples โ€‹

๐Ÿ†˜ Need Help? โ€‹

Common Issues โ€‹

Server won't start:

bash
# Check PHP version
php --version  # Should be 8.1+

# Check syntax
php -l personal-assistant-server.php

# Run with debug
DEBUG=1 php personal-assistant-server.php

Client connection fails:

bash
# Check if server is executable
ls -la personal-assistant-server.php

# Test server directly
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}' | php personal-assistant-server.php

Tool errors:

  • Check parameter names match the schema
  • Verify required parameters are provided
  • Look at error messages for clues

Getting Help โ€‹

๐ŸŽฏ What's Next? โ€‹

You're now ready to build more sophisticated MCP servers! Here are some paths to explore:

  • ๐Ÿ”ง Advanced Server Development: Build production-ready servers with databases, APIs, and complex logic
  • ๐Ÿ“ฑ Client Development: Create clients that connect to multiple servers
  • ๐ŸŒ Web Integration: Deploy your servers with HTTP transport for web access
  • ๐Ÿค– AI Integration: Connect your servers to OpenAI, Claude, or other LLMs
  • ๐Ÿ—๏ธ Framework Integration: Integrate with Laravel, Symfony, or other PHP frameworks

Ready for the next challenge? โ†’ Build Your First Client


๐ŸŽ‰ You've taken your first step into the MCP ecosystem. Welcome aboard!