# Usage Quotas Tier-based rate limiting and usage quotas for MCP tools. ## Overview The quota system enforces usage limits based on workspace subscription tiers: **Tiers:** - **Free:** 60 requests/hour, 500 queries/day - **Pro:** 600 requests/hour, 10,000 queries/day - **Enterprise:** Unlimited ## Quota Enforcement ### Middleware ```php use Core\Mcp\Middleware\CheckMcpQuota; Route::middleware([CheckMcpQuota::class]) ->post('/mcp/tools/{tool}', [McpController::class, 'execute']); ``` **Process:** 1. Identifies workspace from context 2. Checks current usage against tier limits 3. Allows or denies request 4. Records usage on success ### Manual Checking ```php use Core\Mcp\Services\McpQuotaService; $quota = app(McpQuotaService::class); // Check if within quota if (!$quota->withinLimit($workspace)) { return response()->json([ 'error' => 'Quota exceeded', 'message' => 'You have reached your hourly limit', 'reset_at' => $quota->resetTime($workspace), ], 429); } // Record usage $quota->recordUsage($workspace, 'query_database'); ``` ## Quota Configuration ```php // config/mcp.php return [ 'quotas' => [ 'free' => [ 'requests_per_hour' => 60, 'queries_per_day' => 500, 'max_query_rows' => 1000, ], 'pro' => [ 'requests_per_hour' => 600, 'queries_per_day' => 10000, 'max_query_rows' => 10000, ], 'enterprise' => [ 'requests_per_hour' => null, // Unlimited 'queries_per_day' => null, 'max_query_rows' => 100000, ], ], ]; ``` ## Usage Tracking ### Current Usage ```php use Core\Mcp\Services\McpQuotaService; $quota = app(McpQuotaService::class); // Get current hour's usage $hourlyUsage = $quota->getHourlyUsage($workspace); // Get current day's usage $dailyUsage = $quota->getDailyUsage($workspace); // Get usage percentage $percentage = $quota->usagePercentage($workspace); ``` ### Usage Response Headers ``` X-RateLimit-Limit: 60 X-RateLimit-Remaining: 45 X-RateLimit-Reset: 1706274000 X-RateLimit-Reset-At: 2026-01-26T13:00:00Z ``` **Implementation:** ```php use Core\Mcp\Middleware\CheckMcpQuota; class CheckMcpQuota { public function handle($request, Closure $next) { $workspace = $request->workspace; $quota = app(McpQuotaService::class); $response = $next($request); // Add quota headers $response->headers->set('X-RateLimit-Limit', $quota->getLimit($workspace)); $response->headers->set('X-RateLimit-Remaining', $quota->getRemaining($workspace)); $response->headers->set('X-RateLimit-Reset', $quota->resetTime($workspace)->timestamp); return $response; } } ``` ## Quota Exceeded Response ```json { "error": "quota_exceeded", "message": "You have exceeded your hourly request limit", "current_usage": 60, "limit": 60, "reset_at": "2026-01-26T13:00:00Z", "upgrade_url": "https://example.com/billing/upgrade" } ``` **HTTP Status:** 429 Too Many Requests ## Upgrading Tiers ```php use Mod\Tenant\Models\Workspace; $workspace = Workspace::find($id); // Upgrade to Pro $workspace->update([ 'subscription_tier' => 'pro', ]); // New limits immediately apply $quota = app(McpQuotaService::class); $newLimit = $quota->getLimit($workspace); // 600 ``` ## Quota Monitoring ### Admin Dashboard ```php class QuotaUsage extends Component { public function render() { $quota = app(McpQuotaService::class); $workspaces = Workspace::all()->map(function ($workspace) use ($quota) { return [ 'name' => $workspace->name, 'tier' => $workspace->subscription_tier, 'hourly_usage' => $quota->getHourlyUsage($workspace), 'hourly_limit' => $quota->getLimit($workspace, 'hourly'), 'daily_usage' => $quota->getDailyUsage($workspace), 'daily_limit' => $quota->getLimit($workspace, 'daily'), ]; }); return view('mcp::admin.quota-usage', compact('workspaces')); } } ``` **View:** ```blade Workspace Tier Hourly Usage Daily Usage @foreach($workspaces as $workspace) {{ $workspace['name'] }} {{ ucfirst($workspace['tier']) }} {{ $workspace['hourly_usage'] }} / {{ $workspace['hourly_limit'] ?? '∞' }} {{ $workspace['daily_usage'] }} / {{ $workspace['daily_limit'] ?? '∞' }} @endforeach ``` ### Alerts Send notifications when nearing limits: ```php use Core\Mcp\Services\McpQuotaService; $quota = app(McpQuotaService::class); $usage = $quota->usagePercentage($workspace); if ($usage >= 80) { // Alert: 80% of quota used $workspace->owner->notify( new QuotaWarningNotification($workspace, $usage) ); } if ($usage >= 100) { // Alert: Quota exceeded $workspace->owner->notify( new QuotaExceededNotification($workspace) ); } ``` ## Custom Quotas Override default quotas for specific workspaces: ```php use Core\Mcp\Models\McpUsageQuota; // Set custom quota McpUsageQuota::create([ 'workspace_id' => $workspace->id, 'requests_per_hour' => 1000, // Custom limit 'queries_per_day' => 50000, 'expires_at' => now()->addMonths(3), // Temporary increase ]); // Custom quota takes precedence over tier defaults ``` ## Resetting Quotas ```bash # Reset all quotas php artisan mcp:reset-quotas # Reset specific workspace php artisan mcp:reset-quotas --workspace=123 # Reset specific period php artisan mcp:reset-quotas --period=hourly ``` ## Bypass Quotas (Admin) ```php // Bypass quota enforcement $result = $tool->execute($params, [ 'bypass_quota' => true, // Requires admin permission ]); ``` **Use cases:** - Internal tools - Admin operations - System maintenance - Testing ## Testing ```php use Tests\TestCase; use Core\Mcp\Services\McpQuotaService; class QuotaTest extends TestCase { public function test_enforces_hourly_limit(): void { $workspace = Workspace::factory()->create(['tier' => 'free']); $quota = app(McpQuotaService::class); // Exhaust quota for ($i = 0; $i < 60; $i++) { $quota->recordUsage($workspace, 'test'); } $this->assertFalse($quota->withinLimit($workspace)); } public function test_resets_after_hour(): void { $workspace = Workspace::factory()->create(); $quota = app(McpQuotaService::class); // Use quota $quota->recordUsage($workspace, 'test'); // Travel 1 hour $this->travel(1)->hour(); $this->assertTrue($quota->withinLimit($workspace)); } public function test_enterprise_has_no_limit(): void { $workspace = Workspace::factory()->create(['tier' => 'enterprise']); $quota = app(McpQuotaService::class); // Use quota 1000 times for ($i = 0; $i < 1000; $i++) { $quota->recordUsage($workspace, 'test'); } $this->assertTrue($quota->withinLimit($workspace)); } } ``` ## Best Practices ### 1. Check Quotas Early ```php // ✅ Good - check before processing if (!$quota->withinLimit($workspace)) { return response()->json(['error' => 'Quota exceeded'], 429); } $result = $tool->execute($params); // ❌ Bad - check after processing $result = $tool->execute($params); if (!$quota->withinLimit($workspace)) { // Too late! } ``` ### 2. Provide Clear Feedback ```php // ✅ Good - helpful error message return response()->json([ 'error' => 'Quota exceeded', 'reset_at' => $quota->resetTime($workspace), 'upgrade_url' => route('billing.upgrade'), ], 429); // ❌ Bad - generic error return response()->json(['error' => 'Too many requests'], 429); ``` ### 3. Monitor Usage Patterns ```php // ✅ Good - alert at 80% if ($usage >= 80) { $this->notifyUser(); } // ❌ Bad - only alert when exhausted if ($usage >= 100) { // User already hit limit } ``` ### 4. Use Appropriate Limits ```php // ✅ Good - reasonable limits 'free' => ['requests_per_hour' => 60], 'pro' => ['requests_per_hour' => 600], // ❌ Bad - too restrictive 'free' => ['requests_per_hour' => 5], // Unusable ``` ## Learn More - [Analytics →](/packages/mcp/analytics) - [Security →](/packages/mcp/security) - [Multi-Tenancy →](/packages/core/tenancy)