agent/php/Mcp/Data/AuditEntry.php
Snider 09054fbdab feat(mcp): implement §3 Services (ToolRegistry + McpQuotaService + QueryAuditService + ToolDependencyService) (#851)
Additive-only — no existing files modified.

- ToolRegistry: register/resolve/listTools/buildDependencyGraph
  - Singleton via registerSingleton() entry point (no Boot.php wire-in
    per scope; tests cover the binding path)
- McpQuotaService: workspace-scoped checkQuota/consume/reset
- QueryAuditService: log/query/aggregate (expects mcp_audit_entries
  table; tests create inline as migration was out-of-scope)
- ToolDependencyService: validateDependencies via graph traversal

Data DTOs: ToolMetadata, QuotaResult, AuditEntry as readonly.
Pest Feature tests _Good/_Bad/_Ugly per AX-10.
pest skipped (vendor binaries missing).

Co-authored-by: Codex <noreply@openai.com>
Closes tasks.lthn.sh/view.php?id=851
2026-04-25 05:14:15 +01:00

71 lines
2.3 KiB
PHP

<?php
// SPDX-License-Identifier: EUPL-1.2
declare(strict_types=1);
namespace Core\Mod\Agentic\Mcp\Data;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
final readonly class AuditEntry
{
public function __construct(
public ?int $id,
public ?string $workspaceId,
public ?string $toolName,
public string $query,
public string $queryHash,
public bool $isSafe,
public ?int $resultCount,
public ?int $durationMs,
public array $metadata,
public CarbonImmutable $createdAt,
) {}
public static function fromModel(object $entry): self
{
$createdAt = $entry->created_at ?? null;
if ($createdAt instanceof CarbonImmutable) {
$immutable = $createdAt;
} elseif ($createdAt instanceof CarbonInterface) {
$immutable = CarbonImmutable::instance($createdAt);
} else {
$immutable = CarbonImmutable::parse((string) ($createdAt ?? 'now'));
}
$workspaceId = $entry->workspace_id ?? null;
$toolName = $entry->tool_name ?? null;
return new self(
id: isset($entry->id) ? (int) $entry->id : null,
workspaceId: $workspaceId === null ? null : (string) $workspaceId,
toolName: $toolName === null ? null : (string) $toolName,
query: (string) ($entry->query_text ?? ''),
queryHash: (string) ($entry->query_hash ?? ''),
isSafe: (bool) ($entry->is_safe ?? false),
resultCount: isset($entry->result_count) ? (int) $entry->result_count : null,
durationMs: isset($entry->duration_ms) ? (int) $entry->duration_ms : null,
metadata: (array) ($entry->metadata ?? []),
createdAt: $immutable,
);
}
public function toArray(): array
{
return [
'id' => $this->id,
'workspace_id' => $this->workspaceId,
'tool_name' => $this->toolName,
'query' => $this->query,
'query_hash' => $this->queryHash,
'is_safe' => $this->isSafe,
'result_count' => $this->resultCount,
'duration_ms' => $this->durationMs,
'metadata' => $this->metadata,
'created_at' => $this->createdAt->toIso8601String(),
];
}
}