Skip to content

Commit ba326ac

Browse files
committed
fix: clear() wiping dynamically registered tools by tracking discovered state on references
clear() previously removed all non-manual elements, which destroyed dynamically registered tools when setDiscoveryState() was called on the next request cycle. Add isDiscovered flag to ElementReference so clear() only removes elements that were imported via setDiscoveryState(), preserving both manual and dynamic registrations.
1 parent a98e01a commit ba326ac

File tree

10 files changed

+509
-76
lines changed

10 files changed

+509
-76
lines changed

src/Capability/Registry.php

Lines changed: 21 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -161,36 +161,10 @@ public function registerPrompt(
161161

162162
public function clear(): void
163163
{
164-
$clearCount = 0;
165-
166-
foreach ($this->tools as $name => $tool) {
167-
if (!$tool->isManual) {
168-
unset($this->tools[$name]);
169-
++$clearCount;
170-
}
171-
}
172-
foreach ($this->resources as $uri => $resource) {
173-
if (!$resource->isManual) {
174-
unset($this->resources[$uri]);
175-
++$clearCount;
176-
}
177-
}
178-
foreach ($this->prompts as $name => $prompt) {
179-
if (!$prompt->isManual) {
180-
unset($this->prompts[$name]);
181-
++$clearCount;
182-
}
183-
}
184-
foreach ($this->resourceTemplates as $uriTemplate => $template) {
185-
if (!$template->isManual) {
186-
unset($this->resourceTemplates[$uriTemplate]);
187-
++$clearCount;
188-
}
189-
}
190-
191-
if ($clearCount > 0) {
192-
$this->logger->debug(\sprintf('Removed %d discovered elements from internal registry.', $clearCount));
193-
}
164+
$this->tools = array_filter($this->tools, static fn ($t) => !$t->isDiscovered);
165+
$this->resources = array_filter($this->resources, static fn ($r) => !$r->isDiscovered);
166+
$this->prompts = array_filter($this->prompts, static fn ($p) => !$p->isDiscovered);
167+
$this->resourceTemplates = array_filter($this->resourceTemplates, static fn ($t) => !$t->isDiscovered);
194168
}
195169

196170
public function hasTools(): bool
@@ -344,10 +318,10 @@ public function getPrompt(string $name): PromptReference
344318
public function getDiscoveryState(): DiscoveryState
345319
{
346320
return new DiscoveryState(
347-
tools: array_filter($this->tools, static fn ($tool) => !$tool->isManual),
348-
resources: array_filter($this->resources, static fn ($resource) => !$resource->isManual),
349-
prompts: array_filter($this->prompts, static fn ($prompt) => !$prompt->isManual),
350-
resourceTemplates: array_filter($this->resourceTemplates, static fn ($template) => !$template->isManual),
321+
tools: array_filter($this->tools, static fn ($t) => $t->isDiscovered),
322+
resources: array_filter($this->resources, static fn ($r) => $r->isDiscovered),
323+
prompts: array_filter($this->prompts, static fn ($p) => $p->isDiscovered),
324+
resourceTemplates: array_filter($this->resourceTemplates, static fn ($t) => $t->isDiscovered),
351325
);
352326
}
353327

@@ -360,21 +334,29 @@ public function setDiscoveryState(DiscoveryState $state): void
360334
// Clear existing discovered elements
361335
$this->clear();
362336

363-
// Import new discovered elements
337+
// Import new discovered elements — skip any that conflict with manual or dynamic registrations
364338
foreach ($state->getTools() as $name => $tool) {
365-
$this->tools[$name] = $tool;
339+
if (!isset($this->tools[$name])) {
340+
$this->tools[$name] = new ToolReference($tool->tool, $tool->handler, isDiscovered: true);
341+
}
366342
}
367343

368344
foreach ($state->getResources() as $uri => $resource) {
369-
$this->resources[$uri] = $resource;
345+
if (!isset($this->resources[$uri])) {
346+
$this->resources[$uri] = new ResourceReference($resource->resource, $resource->handler, isDiscovered: true);
347+
}
370348
}
371349

372350
foreach ($state->getPrompts() as $name => $prompt) {
373-
$this->prompts[$name] = $prompt;
351+
if (!isset($this->prompts[$name])) {
352+
$this->prompts[$name] = new PromptReference($prompt->prompt, $prompt->handler, completionProviders: $prompt->completionProviders, isDiscovered: true);
353+
}
374354
}
375355

376356
foreach ($state->getResourceTemplates() as $uriTemplate => $template) {
377-
$this->resourceTemplates[$uriTemplate] = $template;
357+
if (!isset($this->resourceTemplates[$uriTemplate])) {
358+
$this->resourceTemplates[$uriTemplate] = new ResourceTemplateReference($template->resourceTemplate, $template->handler, completionProviders: $template->completionProviders, isDiscovered: true);
359+
}
378360
}
379361

380362
// Dispatch events for the imported elements

src/Capability/Registry/ElementReference.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class ElementReference
2424
public function __construct(
2525
public readonly \Closure|array|string $handler,
2626
public readonly bool $isManual = false,
27+
public readonly bool $isDiscovered = false,
2728
) {
2829
}
2930
}

src/Capability/Registry/PromptReference.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ public function __construct(
3131
\Closure|array|string $handler,
3232
bool $isManual = false,
3333
public readonly array $completionProviders = [],
34+
bool $isDiscovered = false,
3435
) {
35-
parent::__construct($handler, $isManual);
36+
parent::__construct($handler, $isManual, $isDiscovered);
3637
}
3738

3839
/**

src/Capability/Registry/ResourceReference.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ public function __construct(
2929
public readonly Resource $resource,
3030
callable|array|string $handler,
3131
bool $isManual = false,
32+
bool $isDiscovered = false,
3233
) {
33-
parent::__construct($handler, $isManual);
34+
parent::__construct($handler, $isManual, $isDiscovered);
3435
}
3536

3637
/**

src/Capability/Registry/ResourceTemplateReference.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ public function __construct(
3838
callable|array|string $handler,
3939
bool $isManual = false,
4040
public readonly array $completionProviders = [],
41+
bool $isDiscovered = false,
4142
) {
42-
parent::__construct($handler, $isManual);
43+
parent::__construct($handler, $isManual, $isDiscovered);
4344

4445
$this->compileTemplate();
4546
}

src/Capability/Registry/ToolReference.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ public function __construct(
2929
public readonly Tool $tool,
3030
callable|array|string $handler,
3131
bool $isManual = false,
32+
bool $isDiscovered = false,
3233
) {
33-
parent::__construct($handler, $isManual);
34+
parent::__construct($handler, $isManual, $isDiscovered);
3435
}
3536

3637
/**
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"resources": [
33
{
4-
"name": "priority_config_discovered",
4+
"name": "priority_config_manual",
55
"uri": "config://priority",
6-
"description": "A resource discovered via attributes.\n\nThis will be overridden by a manual registration with the same URI."
6+
"description": "Manually registered resource that overrides a discovered one."
77
}
88
]
99
}

tests/Inspector/Http/snapshots/HttpCombinedRegistrationTest-resources_read-config_priority.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{
44
"uri": "config://priority",
55
"mimeType": "text/plain",
6-
"text": "Discovered Priority Config: Low"
6+
"text": "Manual Priority Config: HIGH (overrides discovered)"
77
}
88
]
99
}

0 commit comments

Comments
 (0)