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) โ
# 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-sdkStep 2: Create the Server File (3 minutes) โ
Create personal-assistant-server.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) โ
chmod +x personal-assistant-server.phpStep 4: Test Your Server (2 minutes) โ
Option A: Test with MCP Inspector (Recommended) โ
# Install MCP Inspector (requires Node.js)
npm install -g @modelcontextprotocol/inspector
# Test your server
mcp-inspector ./personal-assistant-server.phpThis 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:
#!/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();chmod +x test-client.php
php test-client.phpStep 5: Understanding Your Server (3 minutes) โ
Let's break down what you just built:
๐๏ธ Server Structure โ
// 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:
- Name - Unique identifier (
'calculate') - Description - Human-readable description
- Schema - JSON Schema for input validation
- Handler - Function that does the work
$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:
$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:
$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) โ
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);Add More Resources:
php// Configuration files $server->resource('config', 'config://{env}', 'application/json', $configHandler); // Log files $server->resource('logs', 'logs://{date}', 'text/plain', $logHandler);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 โ
- ๐ Learn Core Concepts: Understanding MCP
- ๐ง Advanced Tools: Creating Servers
- ๐ Add Security: Authentication
- ๐ Web Deployment: Transports
- ๐๏ธ Framework Integration: Laravel Integration
Real-World Examples โ
- ๐ Data Pipeline - Data analysis tools
- ๐ Blog CMS - Content management
- ๐ค AI Assistant - AI-powered tools
- ๐ฑ API Gateway - Service orchestration
๐ Need Help? โ
Common Issues โ
Server won't start:
# 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.phpClient connection fails:
# 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.phpTool errors:
- Check parameter names match the schema
- Verify required parameters are provided
- Look at error messages for clues
Getting Help โ
- ๐ Complete Documentation
- ๐ Report Issues
- ๐ฌ Community Discussions
๐ฏ 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!