'object', 'properties' => [ 'content' => [ 'type' => 'string', 'description' => 'The knowledge to remember (max 50,000 characters)', 'maxLength' => 50000, ], 'type' => [ 'type' => 'string', 'description' => 'Memory type classification', 'enum' => BrainMemory::VALID_TYPES, ], 'tags' => [ 'type' => 'array', 'items' => ['type' => 'string'], 'description' => 'Optional tags for categorisation', ], 'project' => [ 'type' => 'string', 'description' => 'Optional project scope (e.g. repo name)', ], 'confidence' => [ 'type' => 'number', 'description' => 'Confidence level from 0.0 to 1.0 (default: 0.8)', 'minimum' => 0.0, 'maximum' => 1.0, ], 'supersedes' => [ 'type' => 'string', 'format' => 'uuid', 'description' => 'UUID of an older memory this one replaces', ], 'expires_in' => [ 'type' => 'integer', 'description' => 'Hours until this memory expires (null = never)', 'minimum' => 1, ], ], 'required' => ['content', 'type'], ]; } public function handle(array $args, array $context = []): array { try { $content = $this->requireString($args, 'content', 50000); $type = $this->requireEnum($args, 'type', BrainMemory::VALID_TYPES); } catch (\InvalidArgumentException $e) { return $this->error($e->getMessage()); } $workspaceId = $context['workspace_id'] ?? null; if ($workspaceId === null) { return $this->error('workspace_id is required. Ensure you have authenticated with a valid API key. See: https://host.uk.com/ai'); } $agentId = $context['agent_id'] ?? $context['session_id'] ?? 'anonymous'; $tags = $this->optional($args, 'tags'); $project = $this->optionalString($args, 'project'); $supersedes = $this->optionalString($args, 'supersedes'); $expiresIn = $this->optionalInt($args, 'expires_in', null, 1); $confidence = $this->optional($args, 'confidence', 0.8); if (! is_numeric($confidence) || $confidence < 0.0 || $confidence > 1.0) { return $this->error('confidence must be a number between 0.0 and 1.0'); } $confidence = (float) $confidence; if (is_array($tags)) { foreach ($tags as $tag) { if (! is_string($tag)) { return $this->error('Each tag must be a string'); } } } if ($supersedes !== null) { $existing = BrainMemory::where('id', $supersedes) ->where('workspace_id', $workspaceId) ->first(); if (! $existing) { return $this->error("Memory '{$supersedes}' not found in this workspace"); } } return $this->withCircuitBreaker('brain', function () use ( $content, $type, $workspaceId, $agentId, $tags, $project, $confidence, $supersedes, $expiresIn ) { $memory = app(BrainService::class)->remember([ 'workspace_id' => $workspaceId, 'agent_id' => $agentId, 'type' => $type, 'content' => $content, 'tags' => $tags, 'project' => $project, 'confidence' => $confidence, 'supersedes_id' => $supersedes, 'expires_at' => $expiresIn ? now()->addHours($expiresIn) : null, ]); return $this->success([ 'memory' => $memory->toMcpContext(), ]); }, fn () => $this->error('Brain service temporarily unavailable. Memory could not be stored.', 'service_unavailable')); } }