diff --git a/src/Mcp/Console/Commands/CleanupToolCallLogsCommand.php b/src/Mcp/Console/Commands/CleanupToolCallLogsCommand.php index d56a8a6..41cfd21 100644 --- a/src/Mcp/Console/Commands/CleanupToolCallLogsCommand.php +++ b/src/Mcp/Console/Commands/CleanupToolCallLogsCommand.php @@ -5,9 +5,9 @@ namespace Core\Mcp\Console\Commands; use Illuminate\Console\Command; -use Mod\Mcp\Models\McpApiRequest; -use Mod\Mcp\Models\McpToolCall; -use Mod\Mcp\Models\McpToolCallStat; +use Core\Mcp\Models\McpApiRequest; +use Core\Mcp\Models\McpToolCall; +use Core\Mcp\Models\McpToolCallStat; /** * Cleanup old MCP tool call logs and API request logs. diff --git a/src/Mcp/Console/Commands/McpMonitorCommand.php b/src/Mcp/Console/Commands/McpMonitorCommand.php index 52415e7..758266d 100644 --- a/src/Mcp/Console/Commands/McpMonitorCommand.php +++ b/src/Mcp/Console/Commands/McpMonitorCommand.php @@ -5,8 +5,8 @@ namespace Core\Mcp\Console\Commands; use Illuminate\Console\Command; -use Mod\Mcp\Services\McpMetricsService; -use Mod\Mcp\Services\McpMonitoringService; +use Core\Mcp\Services\McpMetricsService; +use Core\Mcp\Services\McpMonitoringService; /** * MCP Monitor Command. diff --git a/src/Mcp/Controllers/McpApiController.php b/src/Mcp/Controllers/McpApiController.php index 4ec00c5..0ceab53 100644 --- a/src/Mcp/Controllers/McpApiController.php +++ b/src/Mcp/Controllers/McpApiController.php @@ -2,17 +2,19 @@ declare(strict_types=1); -namespace Mod\Api\Controllers; +namespace Core\Mcp\Controllers; use Core\Front\Controller; +use Core\Mcp\Models\McpApiRequest; +use Core\Mcp\Models\McpToolCall; use Core\Mcp\Services\McpQuotaService; +use Core\Mcp\Services\McpWebhookDispatcher; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; -use Mod\Api\Models\ApiKey; -use Core\Mcp\Models\McpApiRequest; -use Core\Mcp\Models\McpToolCall; -use Core\Mcp\Services\McpWebhookDispatcher; +use Illuminate\Support\Facades\RateLimiter; +use Illuminate\Support\Str; +use Core\Mod\Api\Models\ApiKey; use Symfony\Component\Yaml\Yaml; /** @@ -86,8 +88,8 @@ public function tools(Request $request, string $id): JsonResponse public function callTool(Request $request): JsonResponse { $validated = $request->validate([ - 'server' => 'required|string|max:64', - 'tool' => 'required|string|max:128', + 'server' => 'required|string|max:64|regex:/^[a-z0-9-]+$/', + 'tool' => 'required|string|max:128|regex:/^[a-zA-Z0-9_-]+$/', 'arguments' => 'nullable|array', ]); @@ -116,6 +118,28 @@ public function callTool(Request $request): JsonResponse $apiKey = $request->attributes->get('api_key'); $workspace = $apiKey?->workspace; + // Quota check + if ($workspace) { + $quotaCheck = app(McpQuotaService::class)->checkQuotaDetailed($workspace); + if (! ($quotaCheck['allowed'] ?? true)) { + return response()->json([ + 'error' => 'quota_exceeded', + 'message' => $quotaCheck['reason'] ?? 'Monthly quota exceeded', + 'quota' => $quotaCheck, + ], 403); + } + } + + // Rate limiting + $rateKey = 'mcp_api_tool_call:'.($workspace?->id ?: $request->ip()); + if (RateLimiter::tooManyAttempts($rateKey, 60)) { + return response()->json([ + 'error' => 'too_many_requests', + 'message' => 'Rate limit exceeded. Please try again later.', + ], 429); + } + RateLimiter::hit($rateKey, 60); + $startTime = microtime(true); try { @@ -223,9 +247,12 @@ protected function executeToolViaArtisan(string $server, string $tool, array $ar ], ]; - // Execute via process + // Execute via process safely by splitting command into arguments + $commandParts = Str::of($command)->explode(' ')->filter()->toArray(); + $cmd = array_merge(['php', 'artisan'], $commandParts); + $process = proc_open( - ['php', 'artisan', $command], + $cmd, [ 0 => ['pipe', 'r'], 1 => ['pipe', 'w'], diff --git a/src/Mcp/Services/AgentSessionService.php b/src/Mcp/Services/AgentSessionService.php index dac3aad..cc1031a 100644 --- a/src/Mcp/Services/AgentSessionService.php +++ b/src/Mcp/Services/AgentSessionService.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace Mod\Mcp\Services; +namespace Core\Mcp\Services; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Cache; -use Mod\Agentic\Models\AgentPlan; -use Mod\Agentic\Models\AgentSession; +use Core\Mod\Agentic\Models\AgentPlan; +use Core\Mod\Agentic\Models\AgentSession; /** * Agent Session Service - manages session persistence for agent continuity. diff --git a/src/Mcp/Services/AgentToolRegistry.php b/src/Mcp/Services/AgentToolRegistry.php index 22b102f..9248993 100644 --- a/src/Mcp/Services/AgentToolRegistry.php +++ b/src/Mcp/Services/AgentToolRegistry.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Mod\Mcp\Services; +namespace Core\Mcp\Services; use Core\Mcp\Dependencies\HasDependencies; use Core\Mcp\Services\ToolDependencyService; use Illuminate\Support\Collection; -use Mod\Api\Models\ApiKey; -use Mod\Mcp\Tools\Agent\Contracts\AgentToolInterface; +use Core\Mod\Api\Models\ApiKey; +use Core\Mcp\Tools\Agent\Contracts\AgentToolInterface; /** * Registry for MCP Agent Server tools. diff --git a/src/Mcp/Services/CircuitBreaker.php b/src/Mcp/Services/CircuitBreaker.php index f6deabb..f33e405 100644 --- a/src/Mcp/Services/CircuitBreaker.php +++ b/src/Mcp/Services/CircuitBreaker.php @@ -7,7 +7,7 @@ use Closure; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Log; -use Mod\Mcp\Exceptions\CircuitOpenException; +use Core\Mcp\Exceptions\CircuitOpenException; use Throwable; /** diff --git a/src/Mcp/Tests/Unit/McpQuotaServiceTest.php b/src/Mcp/Tests/Unit/McpQuotaServiceTest.php index d8f64bf..9d5c0bc 100644 --- a/src/Mcp/Tests/Unit/McpQuotaServiceTest.php +++ b/src/Mcp/Tests/Unit/McpQuotaServiceTest.php @@ -25,7 +25,6 @@ use Core\Tenant\Services\EntitlementService; use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; -use Mockery; // ============================================================================= // Usage Recording Tests diff --git a/src/Mcp/Tests/Unit/ValidateWorkspaceContextMiddlewareTest.php b/src/Mcp/Tests/Unit/ValidateWorkspaceContextMiddlewareTest.php index 20bb189..3752efa 100644 --- a/src/Mcp/Tests/Unit/ValidateWorkspaceContextMiddlewareTest.php +++ b/src/Mcp/Tests/Unit/ValidateWorkspaceContextMiddlewareTest.php @@ -11,8 +11,8 @@ use Core\Tenant\Models\User; use Core\Tenant\Models\Workspace; use Illuminate\Http\Request; -use Mod\Mcp\Context\WorkspaceContext; -use Mod\Mcp\Middleware\ValidateWorkspaceContext; +use Core\Mcp\Context\WorkspaceContext; +use Core\Mcp\Middleware\ValidateWorkspaceContext; describe('ValidateWorkspaceContext Middleware', function () { beforeEach(function () { diff --git a/src/Mcp/Tests/Unit/WorkspaceContextSecurityTest.php b/src/Mcp/Tests/Unit/WorkspaceContextSecurityTest.php index 78d1eb0..29d5804 100644 --- a/src/Mcp/Tests/Unit/WorkspaceContextSecurityTest.php +++ b/src/Mcp/Tests/Unit/WorkspaceContextSecurityTest.php @@ -18,10 +18,10 @@ use Core\Tenant\Models\User; use Core\Tenant\Models\Workspace; use Illuminate\Http\Request; -use Mod\Mcp\Context\WorkspaceContext; -use Mod\Mcp\Exceptions\MissingWorkspaceContextException; -use Mod\Mcp\Middleware\ValidateWorkspaceContext; -use Mod\Mcp\Tools\Concerns\RequiresWorkspaceContext; +use Core\Mcp\Context\WorkspaceContext; +use Core\Mcp\Exceptions\MissingWorkspaceContextException; +use Core\Mcp\Middleware\ValidateWorkspaceContext; +use Core\Mcp\Tools\Concerns\RequiresWorkspaceContext; // Test class using the trait class TestToolWithWorkspaceContext diff --git a/src/Mcp/Tools/Commerce/GetBillingStatus.php b/src/Mcp/Tools/Commerce/GetBillingStatus.php index 31e313d..e9bea14 100644 --- a/src/Mcp/Tools/Commerce/GetBillingStatus.php +++ b/src/Mcp/Tools/Commerce/GetBillingStatus.php @@ -9,7 +9,7 @@ use Laravel\Mcp\Request; use Laravel\Mcp\Response; use Laravel\Mcp\Server\Tool; -use Mod\Mcp\Tools\Concerns\RequiresWorkspaceContext; +use Core\Mcp\Tools\Concerns\RequiresWorkspaceContext; /** * Get billing status for the authenticated workspace. diff --git a/src/Mcp/Tools/Commerce/ListInvoices.php b/src/Mcp/Tools/Commerce/ListInvoices.php index 4f9a6e7..40ea625 100644 --- a/src/Mcp/Tools/Commerce/ListInvoices.php +++ b/src/Mcp/Tools/Commerce/ListInvoices.php @@ -9,7 +9,7 @@ use Laravel\Mcp\Request; use Laravel\Mcp\Response; use Laravel\Mcp\Server\Tool; -use Mod\Mcp\Tools\Concerns\RequiresWorkspaceContext; +use Core\Mcp\Tools\Concerns\RequiresWorkspaceContext; /** * List invoices for the authenticated workspace. diff --git a/src/Mcp/Tools/Commerce/UpgradePlan.php b/src/Mcp/Tools/Commerce/UpgradePlan.php index 9c95228..9dce042 100644 --- a/src/Mcp/Tools/Commerce/UpgradePlan.php +++ b/src/Mcp/Tools/Commerce/UpgradePlan.php @@ -11,7 +11,7 @@ use Laravel\Mcp\Request; use Laravel\Mcp\Response; use Laravel\Mcp\Server\Tool; -use Mod\Mcp\Tools\Concerns\RequiresWorkspaceContext; +use Core\Mcp\Tools\Concerns\RequiresWorkspaceContext; /** * Preview or execute a plan upgrade/downgrade for the authenticated workspace. diff --git a/src/Website/Mcp/Controllers/McpRegistryController.php b/src/Website/Mcp/Controllers/McpRegistryController.php index f0ad09c..38c504c 100644 --- a/src/Website/Mcp/Controllers/McpRegistryController.php +++ b/src/Website/Mcp/Controllers/McpRegistryController.php @@ -7,8 +7,8 @@ use Core\Front\Controller; use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; -use Mod\Mcp\Models\McpToolCall; -use Mod\Mcp\Services\OpenApiGenerator; +use Core\Mcp\Models\McpToolCall; +use Core\Mcp\Services\OpenApiGenerator; use Symfony\Component\Yaml\Yaml; /** diff --git a/src/Website/Mcp/Routes/web.php b/src/Website/Mcp/Routes/web.php index c6c575d..b66b47f 100644 --- a/src/Website/Mcp/Routes/web.php +++ b/src/Website/Mcp/Routes/web.php @@ -1,8 +1,8 @@ UpstreamTodo::pending()->count(), 'quick_wins' => UpstreamTodo::quickWins()->count(), 'security_updates' => UpstreamTodo::pending()->where('type', 'security')->count(), - 'recent_releases' => \Mod\Uptelligence\Models\VersionRelease::recent(7)->count(), + 'recent_releases' => \Core\Mod\Uptelligence\Models\VersionRelease::recent(7)->count(), 'in_progress' => UpstreamTodo::inProgress()->count(), ]; } catch (\Illuminate\Database\QueryException $e) {