Compare commits

..

No commits in common. "main" and "dev" have entirely different histories.
main ... dev

5 changed files with 129 additions and 143 deletions

View file

@ -8,31 +8,29 @@ return new class extends Migration
{
public function up(): void
{
if (! Schema::hasTable('mcp_api_requests')) {
Schema::create('mcp_api_requests', function (Blueprint $table) {
$table->id();
$table->string('request_id', 32)->unique();
$table->foreignId('workspace_id')->nullable()->constrained('workspaces')->nullOnDelete();
$table->foreignId('api_key_id')->nullable()->constrained('api_keys')->nullOnDelete();
$table->string('method', 10);
$table->string('path', 255);
$table->json('headers')->nullable();
$table->json('request_body')->nullable();
$table->unsignedSmallInteger('response_status');
$table->json('response_body')->nullable();
$table->unsignedInteger('duration_ms')->default(0);
$table->string('server_id', 64)->nullable();
$table->string('tool_name', 128)->nullable();
$table->text('error_message')->nullable();
$table->string('ip_address', 45)->nullable();
$table->timestamps();
Schema::create('mcp_api_requests', function (Blueprint $table) {
$table->id();
$table->string('request_id', 32)->unique();
$table->foreignId('workspace_id')->nullable()->constrained('workspaces')->nullOnDelete();
$table->foreignId('api_key_id')->nullable()->constrained('api_keys')->nullOnDelete();
$table->string('method', 10);
$table->string('path', 255);
$table->json('headers')->nullable();
$table->json('request_body')->nullable();
$table->unsignedSmallInteger('response_status');
$table->json('response_body')->nullable();
$table->unsignedInteger('duration_ms')->default(0);
$table->string('server_id', 64)->nullable();
$table->string('tool_name', 128)->nullable();
$table->text('error_message')->nullable();
$table->string('ip_address', 45)->nullable();
$table->timestamps();
$table->index(['workspace_id', 'created_at']);
$table->index(['server_id', 'tool_name']);
$table->index('created_at');
$table->index('response_status');
});
}
$table->index(['workspace_id', 'created_at']);
$table->index(['server_id', 'tool_name']);
$table->index('created_at');
$table->index('response_status');
});
}
public function down(): void

View file

@ -8,40 +8,36 @@ return new class extends Migration
{
public function up(): void
{
if (! Schema::hasTable('mcp_tool_metrics')) {
Schema::create('mcp_tool_metrics', function (Blueprint $table) {
$table->id();
$table->string('tool_name');
$table->string('workspace_id')->nullable();
$table->unsignedInteger('call_count')->default(0);
$table->unsignedInteger('error_count')->default(0);
$table->unsignedInteger('total_duration_ms')->default(0);
$table->unsignedInteger('min_duration_ms')->nullable();
$table->unsignedInteger('max_duration_ms')->nullable();
$table->date('date');
$table->timestamps();
Schema::create('mcp_tool_metrics', function (Blueprint $table) {
$table->id();
$table->string('tool_name');
$table->string('workspace_id')->nullable();
$table->unsignedInteger('call_count')->default(0);
$table->unsignedInteger('error_count')->default(0);
$table->unsignedInteger('total_duration_ms')->default(0);
$table->unsignedInteger('min_duration_ms')->nullable();
$table->unsignedInteger('max_duration_ms')->nullable();
$table->date('date');
$table->timestamps();
$table->unique(['tool_name', 'workspace_id', 'date']);
$table->index(['date', 'tool_name']);
$table->index('workspace_id');
});
}
$table->unique(['tool_name', 'workspace_id', 'date']);
$table->index(['date', 'tool_name']);
$table->index('workspace_id');
});
// Table for tracking tool combinations (tools used together in sessions)
if (! Schema::hasTable('mcp_tool_combinations')) {
Schema::create('mcp_tool_combinations', function (Blueprint $table) {
$table->id();
$table->string('tool_a');
$table->string('tool_b');
$table->string('workspace_id')->nullable();
$table->unsignedInteger('occurrence_count')->default(0);
$table->date('date');
$table->timestamps();
Schema::create('mcp_tool_combinations', function (Blueprint $table) {
$table->id();
$table->string('tool_a');
$table->string('tool_b');
$table->string('workspace_id')->nullable();
$table->unsignedInteger('occurrence_count')->default(0);
$table->date('date');
$table->timestamps();
$table->unique(['tool_a', 'tool_b', 'workspace_id', 'date']);
$table->index(['date', 'occurrence_count']);
});
}
$table->unique(['tool_a', 'tool_b', 'workspace_id', 'date']);
$table->index(['date', 'occurrence_count']);
});
}
public function down(): void

View file

@ -8,20 +8,18 @@ return new class extends Migration
{
public function up(): void
{
if (! Schema::hasTable('mcp_usage_quotas')) {
Schema::create('mcp_usage_quotas', function (Blueprint $table) {
$table->id();
$table->foreignId('workspace_id')->constrained('workspaces')->cascadeOnDelete();
$table->string('month', 7); // YYYY-MM format
$table->unsignedBigInteger('tool_calls_count')->default(0);
$table->unsignedBigInteger('input_tokens')->default(0);
$table->unsignedBigInteger('output_tokens')->default(0);
$table->timestamps();
Schema::create('mcp_usage_quotas', function (Blueprint $table) {
$table->id();
$table->foreignId('workspace_id')->constrained('workspaces')->cascadeOnDelete();
$table->string('month', 7); // YYYY-MM format
$table->unsignedBigInteger('tool_calls_count')->default(0);
$table->unsignedBigInteger('input_tokens')->default(0);
$table->unsignedBigInteger('output_tokens')->default(0);
$table->timestamps();
$table->unique(['workspace_id', 'month']);
$table->index('month');
});
}
$table->unique(['workspace_id', 'month']);
$table->index('month');
});
}
public function down(): void

View file

@ -8,70 +8,66 @@ return new class extends Migration
{
public function up(): void
{
if (! Schema::hasTable('mcp_audit_logs')) {
Schema::create('mcp_audit_logs', function (Blueprint $table) {
$table->id();
Schema::create('mcp_audit_logs', function (Blueprint $table) {
$table->id();
// Tool execution details
$table->string('server_id')->index();
$table->string('tool_name')->index();
$table->unsignedBigInteger('workspace_id')->nullable()->index();
$table->string('session_id')->nullable()->index();
// Tool execution details
$table->string('server_id')->index();
$table->string('tool_name')->index();
$table->unsignedBigInteger('workspace_id')->nullable()->index();
$table->string('session_id')->nullable()->index();
// Input/output (stored as JSON, may be redacted)
$table->json('input_params')->nullable();
$table->json('output_summary')->nullable();
$table->boolean('success')->default(true);
$table->unsignedInteger('duration_ms')->nullable();
$table->string('error_code')->nullable();
$table->text('error_message')->nullable();
// Input/output (stored as JSON, may be redacted)
$table->json('input_params')->nullable();
$table->json('output_summary')->nullable();
$table->boolean('success')->default(true);
$table->unsignedInteger('duration_ms')->nullable();
$table->string('error_code')->nullable();
$table->text('error_message')->nullable();
// Actor information
$table->string('actor_type')->nullable(); // user, api_key, system
$table->unsignedBigInteger('actor_id')->nullable();
$table->string('actor_ip', 45)->nullable(); // IPv4 or IPv6
// Actor information
$table->string('actor_type')->nullable(); // user, api_key, system
$table->unsignedBigInteger('actor_id')->nullable();
$table->string('actor_ip', 45)->nullable(); // IPv4 or IPv6
// Sensitive tool flagging
$table->boolean('is_sensitive')->default(false)->index();
$table->string('sensitivity_reason')->nullable();
// Sensitive tool flagging
$table->boolean('is_sensitive')->default(false)->index();
$table->string('sensitivity_reason')->nullable();
// Hash chain for tamper detection
$table->string('previous_hash', 64)->nullable(); // SHA-256 of previous entry
$table->string('entry_hash', 64)->index(); // SHA-256 of this entry
// Hash chain for tamper detection
$table->string('previous_hash', 64)->nullable(); // SHA-256 of previous entry
$table->string('entry_hash', 64)->index(); // SHA-256 of this entry
// Agent context
$table->string('agent_type')->nullable();
$table->string('plan_slug')->nullable();
// Agent context
$table->string('agent_type')->nullable();
$table->string('plan_slug')->nullable();
// Timestamps (immutable - no updated_at updates after creation)
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->nullable();
// Timestamps (immutable - no updated_at updates after creation)
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->nullable();
// Foreign key constraint
$table->foreign('workspace_id')
->references('id')
->on('workspaces')
->nullOnDelete();
// Foreign key constraint
$table->foreign('workspace_id')
->references('id')
->on('workspaces')
->nullOnDelete();
// Composite indexes for common queries
$table->index(['workspace_id', 'created_at']);
$table->index(['tool_name', 'created_at']);
$table->index(['is_sensitive', 'created_at']);
$table->index(['actor_type', 'actor_id']);
});
}
// Composite indexes for common queries
$table->index(['workspace_id', 'created_at']);
$table->index(['tool_name', 'created_at']);
$table->index(['is_sensitive', 'created_at']);
$table->index(['actor_type', 'actor_id']);
});
// Table for tracking sensitive tool definitions
if (! Schema::hasTable('mcp_sensitive_tools')) {
Schema::create('mcp_sensitive_tools', function (Blueprint $table) {
$table->id();
$table->string('tool_name')->unique();
$table->string('reason');
$table->json('redact_fields')->nullable(); // Fields to redact in audit logs
$table->boolean('require_explicit_consent')->default(false);
$table->timestamps();
});
}
Schema::create('mcp_sensitive_tools', function (Blueprint $table) {
$table->id();
$table->string('tool_name')->unique();
$table->string('reason');
$table->json('redact_fields')->nullable(); // Fields to redact in audit logs
$table->boolean('require_explicit_consent')->default(false);
$table->timestamps();
});
}
public function down(): void

View file

@ -8,32 +8,30 @@ return new class extends Migration
{
public function up(): void
{
if (! Schema::hasTable('mcp_tool_versions')) {
Schema::create('mcp_tool_versions', function (Blueprint $table) {
$table->id();
$table->string('server_id', 64)->index();
$table->string('tool_name', 128);
$table->string('version', 32); // semver: 1.0.0, 2.1.0-beta, etc.
$table->json('input_schema')->nullable();
$table->json('output_schema')->nullable();
$table->text('description')->nullable();
$table->text('changelog')->nullable();
$table->text('migration_notes')->nullable(); // guidance for upgrading from previous version
$table->boolean('is_latest')->default(false);
$table->timestamp('deprecated_at')->nullable();
$table->timestamp('sunset_at')->nullable(); // after this date, version is blocked
$table->timestamps();
Schema::create('mcp_tool_versions', function (Blueprint $table) {
$table->id();
$table->string('server_id', 64)->index();
$table->string('tool_name', 128);
$table->string('version', 32); // semver: 1.0.0, 2.1.0-beta, etc.
$table->json('input_schema')->nullable();
$table->json('output_schema')->nullable();
$table->text('description')->nullable();
$table->text('changelog')->nullable();
$table->text('migration_notes')->nullable(); // guidance for upgrading from previous version
$table->boolean('is_latest')->default(false);
$table->timestamp('deprecated_at')->nullable();
$table->timestamp('sunset_at')->nullable(); // after this date, version is blocked
$table->timestamps();
// Unique constraint: one version per tool per server
$table->unique(['server_id', 'tool_name', 'version'], 'mcp_tool_versions_unique');
// Unique constraint: one version per tool per server
$table->unique(['server_id', 'tool_name', 'version'], 'mcp_tool_versions_unique');
// Index for finding latest versions
$table->index(['server_id', 'tool_name', 'is_latest'], 'mcp_tool_versions_latest');
// Index for finding latest versions
$table->index(['server_id', 'tool_name', 'is_latest'], 'mcp_tool_versions_latest');
// Index for finding deprecated/sunset versions
$table->index(['deprecated_at', 'sunset_at'], 'mcp_tool_versions_lifecycle');
});
}
// Index for finding deprecated/sunset versions
$table->index(['deprecated_at', 'sunset_at'], 'mcp_tool_versions_lifecycle');
});
}
public function down(): void