Moved from go-ai/mcp/ and php-mcp. Produces core-mcp binary. 49 MCP tools including brain, RAG, ML, IDE bridge.
9.8 KiB
9.8 KiB
MCP Package
The MCP (Model Context Protocol) package provides AI agent tools for database queries, commerce operations, and workspace management with built-in security and quota enforcement.
Installation
composer require host-uk/core-mcp
Quick Start
<?php
namespace Mod\Blog;
use Core\Events\McpToolsRegistering;
class Boot
{
public static array $listens = [
McpToolsRegistering::class => 'onMcpTools',
];
public function onMcpTools(McpToolsRegistering $event): void
{
$event->tool('blog:create-post', Tools\CreatePostTool::class);
$event->tool('blog:list-posts', Tools\ListPostsTool::class);
}
}
Key Features
Database Tools
- Query Database - SQL query execution with validation and security
- SQL Validation - Prevent destructive queries and SQL injection
- EXPLAIN Plans - Query optimization analysis
Commerce Tools
- Get Billing Status - Current billing and subscription status
- List Invoices - Invoice history and details
- Upgrade Plan - Tier upgrades with entitlement validation
Workspace Tools
- Workspace Context - Automatic workspace/namespace resolution
- Quota Enforcement - Tool usage limits and monitoring
- Tool Analytics - Usage tracking and statistics
Developer Tools
- Tool Discovery - Automatic tool registration
- Dependency Management - Tool dependency resolution
- Error Handling - Consistent error responses
Creating Tools
Basic Tool
<?php
namespace Mod\Blog\Tools;
use Core\Mcp\Tools\BaseTool;
class ListPostsTool extends BaseTool
{
public function getName(): string
{
return 'blog:list-posts';
}
public function getDescription(): string
{
return 'List all blog posts with optional filters';
}
public function getParameters(): array
{
return [
'status' => [
'type' => 'string',
'description' => 'Filter by status',
'enum' => ['published', 'draft'],
'required' => false,
],
'limit' => [
'type' => 'integer',
'description' => 'Number of posts to return',
'default' => 10,
'required' => false,
],
];
}
public function execute(array $params): array
{
$query = Post::query();
if (isset($params['status'])) {
$query->where('status', $params['status']);
}
$posts = $query->limit($params['limit'] ?? 10)->get();
return [
'posts' => $posts->map(fn ($post) => [
'id' => $post->id,
'title' => $post->title,
'slug' => $post->slug,
'status' => $post->status,
])->toArray(),
];
}
}
Tool with Workspace Context
<?php
namespace Mod\Blog\Tools;
use Core\Mcp\Tools\BaseTool;
use Core\Mcp\Tools\Concerns\RequiresWorkspaceContext;
class CreatePostTool extends BaseTool
{
use RequiresWorkspaceContext;
public function getName(): string
{
return 'blog:create-post';
}
public function execute(array $params): array
{
// Workspace context automatically validated
$workspace = $this->getWorkspaceContext();
$post = Post::create([
'title' => $params['title'],
'content' => $params['content'],
'workspace_id' => $workspace->id,
]);
return [
'success' => true,
'post_id' => $post->id,
];
}
}
Tool with Dependencies
<?php
namespace Mod\Blog\Tools;
use Core\Mcp\Tools\BaseTool;
use Core\Mcp\Dependencies\HasDependencies;
use Core\Mcp\Dependencies\ToolDependency;
class ImportPostsTool extends BaseTool
{
use HasDependencies;
public function getDependencies(): array
{
return [
new ToolDependency('blog:list-posts', DependencyType::REQUIRED),
new ToolDependency('media:upload', DependencyType::OPTIONAL),
];
}
public function execute(array $params): array
{
// Dependencies automatically validated
// ...
}
}
Query Database Tool
Execute SQL queries with built-in security:
use Core\Mcp\Tools\QueryDatabase;
$tool = new QueryDatabase();
$result = $tool->execute([
'query' => 'SELECT * FROM posts WHERE status = ?',
'bindings' => ['published'],
'connection' => 'mysql',
]);
// Returns:
// [
// 'rows' => [...],
// 'count' => 10,
// 'execution_time_ms' => 5.23
// ]
Security Features
- Whitelist-based validation - Only SELECT queries allowed by default
- No destructive operations - DROP, TRUNCATE, DELETE blocked
- Binding enforcement - Prevents SQL injection
- Connection validation - Only allowed connections accessible
- EXPLAIN analysis - Query optimization insights
Learn more about SQL Security →
Quota System
Enforce tool usage limits per workspace:
// config/mcp.php
'quotas' => [
'enabled' => true,
'limits' => [
'free' => ['calls' => 100, 'per' => 'day'],
'pro' => ['calls' => 1000, 'per' => 'day'],
'business' => ['calls' => 10000, 'per' => 'day'],
'enterprise' => ['calls' => null], // Unlimited
],
],
Check quota before execution:
use Core\Mcp\Services\McpQuotaService;
$quotaService = app(McpQuotaService::class);
if (!$quotaService->canExecute($workspace, 'blog:create-post')) {
throw new QuotaExceededException('Daily tool quota exceeded');
}
$quotaService->recordExecution($workspace, 'blog:create-post');
Tool Analytics
Track tool usage and performance:
use Core\Mcp\Services\ToolAnalyticsService;
$analytics = app(ToolAnalyticsService::class);
// Get tool stats
$stats = $analytics->getToolStats('blog:create-post', period: 'week');
// Returns: ToolStats with executions, errors, avg_duration_ms
// Get workspace usage
$usage = $analytics->getWorkspaceUsage($workspace, period: 'month');
// Get most used tools
$topTools = $analytics->getTopTools(limit: 10, period: 'week');
Configuration
// config/mcp.php
return [
'enabled' => true,
'tools' => [
'auto_discover' => true,
'cache_enabled' => true,
],
'query_database' => [
'allowed_connections' => ['mysql', 'pgsql'],
'forbidden_keywords' => [
'DROP', 'TRUNCATE', 'DELETE', 'UPDATE', 'INSERT',
'ALTER', 'CREATE', 'GRANT', 'REVOKE',
],
'max_execution_time' => 5000, // ms
'enable_explain' => true,
],
'quotas' => [
'enabled' => true,
'limits' => [
'free' => ['calls' => 100, 'per' => 'day'],
'pro' => ['calls' => 1000, 'per' => 'day'],
'business' => ['calls' => 10000, 'per' => 'day'],
'enterprise' => ['calls' => null],
],
],
'analytics' => [
'enabled' => true,
'retention_days' => 90,
],
];
Middleware
use Core\Mcp\Middleware\ValidateWorkspaceContext;
use Core\Mcp\Middleware\CheckMcpQuota;
use Core\Mcp\Middleware\ValidateToolDependencies;
Route::middleware([
ValidateWorkspaceContext::class,
CheckMcpQuota::class,
ValidateToolDependencies::class,
])->group(function () {
// MCP tool routes
});
Best Practices
1. Use Workspace Context
// ✅ Good - workspace aware
class CreatePostTool extends BaseTool
{
use RequiresWorkspaceContext;
}
// ❌ Bad - no workspace context
class CreatePostTool extends BaseTool
{
public function execute(array $params): array
{
$post = Post::create($params); // No workspace_id!
}
}
2. Validate Parameters
// ✅ Good - strict validation
public function getParameters(): array
{
return [
'title' => [
'type' => 'string',
'required' => true,
'maxLength' => 255,
],
];
}
3. Handle Errors Gracefully
// ✅ Good - clear error messages
public function execute(array $params): array
{
try {
return ['success' => true, 'data' => $result];
} catch (\Exception $e) {
return [
'success' => false,
'error' => $e->getMessage(),
'code' => 'TOOL_EXECUTION_FAILED',
];
}
}
4. Document Tools Well
// ✅ Good - comprehensive description
public function getDescription(): string
{
return 'Create a new blog post with title, content, and optional metadata. '
. 'Requires workspace context. Validates entitlements before creation.';
}
Testing
<?php
namespace Tests\Feature\Mcp;
use Tests\TestCase;
use Mod\Blog\Tools\ListPostsTool;
class ListPostsToolTest extends TestCase
{
public function test_lists_posts(): void
{
Post::factory()->count(5)->create(['status' => 'published']);
$tool = new ListPostsTool();
$result = $tool->execute([
'status' => 'published',
'limit' => 10,
]);
$this->assertArrayHasKey('posts', $result);
$this->assertCount(5, $result['posts']);
}
}