php-mcp/src/Mod/Mcp/Models/McpToolCall.php
2026-01-26 20:57:41 +00:00

161 lines
4.3 KiB
PHP

<?php
declare(strict_types=1);
namespace Core\Mod\Mcp\Models;
use Core\Mod\Tenant\Concerns\BelongsToWorkspace;
use Core\Mod\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* MCP Tool Call - logs individual MCP tool invocations.
*
* Tracks tool usage for analytics and debugging.
* Updates daily aggregates automatically.
*
* @property int $id
* @property int|null $workspace_id
* @property string $server_id
* @property string $tool_name
* @property string|null $session_id
* @property array|null $input_params
* @property bool $success
* @property int|null $duration_ms
* @property string|null $error_message
* @property string|null $error_code
* @property array|null $result_summary
* @property string|null $agent_type
* @property string|null $plan_slug
* @property \Carbon\Carbon|null $created_at
* @property \Carbon\Carbon|null $updated_at
*/
class McpToolCall extends Model
{
use BelongsToWorkspace;
protected $fillable = [
'workspace_id',
'server_id',
'tool_name',
'session_id',
'input_params',
'success',
'duration_ms',
'error_message',
'error_code',
'result_summary',
'agent_type',
'plan_slug',
];
protected $casts = [
'input_params' => 'array',
'result_summary' => 'array',
'success' => 'boolean',
'duration_ms' => 'integer',
];
// Relationships
public function workspace(): BelongsTo
{
return $this->belongsTo(Workspace::class);
}
// Scopes
public function scopeForServer(Builder $query, string $serverId): Builder
{
return $query->where('server_id', $serverId);
}
public function scopeForTool(Builder $query, string $toolName): Builder
{
return $query->where('tool_name', $toolName);
}
public function scopeSuccessful(Builder $query): Builder
{
return $query->where('success', true);
}
public function scopeFailed(Builder $query): Builder
{
return $query->where('success', false);
}
public function scopeRecent(Builder $query, int $hours = 24): Builder
{
return $query->where('created_at', '>=', now()->subHours($hours));
}
public function scopeToday(Builder $query): Builder
{
return $query->whereDate('created_at', today());
}
public function scopeThisWeek(Builder $query): Builder
{
return $query->where('created_at', '>=', now()->startOfWeek());
}
/**
* Log a tool call and update daily stats.
*/
public static function log(
string $serverId,
string $toolName,
array $params = [],
bool $success = true,
?int $durationMs = null,
?string $errorMessage = null,
?string $errorCode = null,
?array $resultSummary = null,
?string $sessionId = null,
?string $agentType = null,
?string $planSlug = null,
?int $workspaceId = null
): self {
$call = static::create([
'workspace_id' => $workspaceId,
'server_id' => $serverId,
'tool_name' => $toolName,
'input_params' => $params,
'success' => $success,
'duration_ms' => $durationMs,
'error_message' => $errorMessage,
'error_code' => $errorCode,
'result_summary' => $resultSummary,
'session_id' => $sessionId,
'agent_type' => $agentType,
'plan_slug' => $planSlug,
]);
// Update daily stats
McpToolCallStat::incrementForCall($call);
return $call;
}
// Helpers
public function getDurationForHumans(): string
{
if (! $this->duration_ms) {
return '-';
}
if ($this->duration_ms < 1000) {
return $this->duration_ms.'ms';
}
return round($this->duration_ms / 1000, 2).'s';
}
public function getStatusBadge(): string
{
return $this->success
? '<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800">Success</span>'
: '<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-red-100 text-red-800">Failed</span>';
}
}