Skip to content

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

bash
composer require host-uk/core-mcp

Quick Start

php
<?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

Commerce Tools

Workspace Tools

Developer Tools

Creating Tools

Basic Tool

php
<?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
<?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
<?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:

php
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:

php
// 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:

php
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');

Learn more about Quotas →

Tool Analytics

Track tool usage and performance:

php
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');

Learn more about Analytics →

Configuration

php
// 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

php
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

php
// ✅ 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

php
// ✅ Good - strict validation
public function getParameters(): array
{
    return [
        'title' => [
            'type' => 'string',
            'required' => true,
            'maxLength' => 255,
        ],
    ];
}

3. Handle Errors Gracefully

php
// ✅ 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

php
// ✅ 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
<?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']);
    }
}

Learn More

Released under the EUPL-1.2 License.