Skip to content

Commit de9c384

Browse files
committed
Apply compatibility fixes
1 parent e91739b commit de9c384

3 files changed

Lines changed: 80 additions & 3 deletions

File tree

McpServerFactory.php

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
namespace Piwik\Plugins\McpServer;
1313

14+
use Matomo\Dependencies\McpServer\Mcp\Capability\Attribute\McpTool;
15+
use Matomo\Dependencies\McpServer\Mcp\Capability\Discovery\DocBlockParser;
16+
use Matomo\Dependencies\McpServer\Mcp\Capability\Discovery\SchemaGenerator;
1417
use Matomo\Dependencies\McpServer\Mcp\Capability\Registry;
1518
use Matomo\Dependencies\McpServer\Mcp\Capability\Registry\ReferenceHandler;
1619
use Matomo\Dependencies\McpServer\Mcp\Schema\ServerCapabilities;
@@ -28,7 +31,18 @@
2831
use Piwik\Plugins\McpServer\McpTools\ApiCallUpdate;
2932
use Piwik\Plugins\McpServer\McpTools\ApiGet;
3033
use Piwik\Plugins\McpServer\McpTools\ApiList;
34+
use Piwik\Plugins\McpServer\McpTools\DimensionGet;
35+
use Piwik\Plugins\McpServer\McpTools\DimensionList;
36+
use Piwik\Plugins\McpServer\McpTools\GoalGet;
37+
use Piwik\Plugins\McpServer\McpTools\GoalList;
38+
use Piwik\Plugins\McpServer\McpTools\ReportList;
39+
use Piwik\Plugins\McpServer\McpTools\ReportMetadata;
3140
use Piwik\Plugins\McpServer\McpTools\ReportProcessed;
41+
use Piwik\Plugins\McpServer\McpTools\SegmentGet;
42+
use Piwik\Plugins\McpServer\McpTools\SegmentList;
43+
use Piwik\Plugins\McpServer\McpTools\SiteGet;
44+
use Piwik\Plugins\McpServer\McpTools\SiteList;
45+
use Piwik\Plugins\McpServer\McpTools\SiteSearch;
3246
use Piwik\Plugins\McpServer\Schemas\Api\ApiCallToolInputSchema;
3347
use Piwik\Plugins\McpServer\Schemas\Api\ApiCallToolOutputSchema;
3448
use Piwik\Plugins\McpServer\Schemas\Api\ApiGetToolInputSchema;
@@ -70,7 +84,6 @@ public function createServer(): Server
7084
->setRegistry($registry)
7185
->setSession($this->sessionStore)
7286
->setContainer($this->container)
73-
->setDiscovery(__DIR__, ['McpTools'])
7487
->setCapabilities(new ServerCapabilities(
7588
tools: true,
7689
// Use null to avoid advertising listChanged capabilities we don't implement.
@@ -84,6 +97,8 @@ public function createServer(): Server
8497
completions: false,
8598
));
8699

100+
$this->registerAttributeTools($builder);
101+
87102
$builder->addTool(
88103
handler: [ReportProcessed::class, 'get'],
89104
name: ReportProcessed::TOOL_NAME,
@@ -107,6 +122,7 @@ public function createServer(): Server
107122
$builder->addTool(
108123
[ApiGet::class, 'get'],
109124
ApiGet::TOOL_NAME,
125+
null,
110126
"Use when: you already know the Matomo API method name and need its exact signature.\n"
111127
. "Purpose: return one authoritative API method summary with parameter metadata.\n"
112128
. "Do not use: for broad discovery across APIs; use " . ApiList::TOOL_NAME . ' instead.',
@@ -124,6 +140,7 @@ public function createServer(): Server
124140
$builder->addTool(
125141
[ApiList::class, 'list'],
126142
ApiList::TOOL_NAME,
143+
null,
127144
"Use when: you need discoverable Matomo API methods and parameter metadata.\n"
128145
. "Purpose: return paginated API method summaries aligned with Matomo API docs visibility.\n"
129146
. "Next: choose a method and map parameters for subsequent raw API tooling.",
@@ -161,12 +178,68 @@ public function createServer(): Server
161178
return $builder->build();
162179
}
163180

181+
private function registerAttributeTools(Builder $builder): void
182+
{
183+
$schemaGenerator = new SchemaGenerator(new DocBlockParser());
184+
185+
$tools = [
186+
[DimensionGet::class, 'get'],
187+
[DimensionList::class, 'list'],
188+
[GoalGet::class, 'get'],
189+
[GoalList::class, 'list'],
190+
[ReportList::class, 'list'],
191+
[ReportMetadata::class, 'get'],
192+
[SegmentGet::class, 'get'],
193+
[SegmentList::class, 'list'],
194+
[SiteGet::class, 'get'],
195+
[SiteList::class, 'list'],
196+
[SiteSearch::class, 'search'],
197+
];
198+
199+
foreach ($tools as [$className, $methodName]) {
200+
$this->registerAttributeTool($builder, $schemaGenerator, $className, $methodName);
201+
}
202+
}
203+
204+
/**
205+
* @param class-string $className
206+
*/
207+
private function registerAttributeTool(
208+
Builder $builder,
209+
SchemaGenerator $schemaGenerator,
210+
string $className,
211+
string $methodName,
212+
): void {
213+
$method = new \ReflectionMethod($className, $methodName);
214+
$attribute = $method->getAttributes(McpTool::class, \ReflectionAttribute::IS_INSTANCEOF)[0] ?? null;
215+
216+
if ($attribute === null) {
217+
throw new \LogicException(sprintf('Missing McpTool attribute on %s::%s.', $className, $methodName));
218+
}
219+
220+
/** @var McpTool $tool */
221+
$tool = $attribute->newInstance();
222+
223+
$builder->addTool(
224+
[$className, $methodName],
225+
$tool->name,
226+
$tool->title,
227+
$tool->description,
228+
$tool->annotations,
229+
$schemaGenerator->generate($method),
230+
$tool->icons,
231+
$tool->meta,
232+
$schemaGenerator->generateOutputSchema($method),
233+
);
234+
}
235+
164236
private function registerRawApiCallTools(Builder $builder, string $rawApiAccessMode): void
165237
{
166238
if (RawApiAccessMode::allowsCategory($rawApiAccessMode, RawApiAccessMode::READ)) {
167239
$builder->addTool(
168240
[ApiCallRead::class, 'call'],
169241
ApiCallRead::TOOL_NAME,
242+
null,
170243
"Use when: you need to execute a known read-only Matomo API method directly.\n"
171244
. "Purpose: call one allowed read method and return its result plus the resolved method metadata.\n"
172245
. "Next: use " . ApiGet::TOOL_NAME . ' or ' . ApiList::TOOL_NAME
@@ -188,6 +261,7 @@ private function registerRawApiCallTools(Builder $builder, string $rawApiAccessM
188261
$builder->addTool(
189262
[ApiCallCreate::class, 'call'],
190263
ApiCallCreate::TOOL_NAME,
264+
null,
191265
"Use when: you need to execute a known create-style Matomo API method directly.\n"
192266
. "Purpose: call one allowed create method and return its result plus the"
193267
. " resolved method metadata.\n"
@@ -210,6 +284,7 @@ private function registerRawApiCallTools(Builder $builder, string $rawApiAccessM
210284
$builder->addTool(
211285
[ApiCallUpdate::class, 'call'],
212286
ApiCallUpdate::TOOL_NAME,
287+
null,
213288
"Use when: you need to execute a known update-style Matomo API method directly.\n"
214289
. "Purpose: call one allowed update method and return its result plus the"
215290
. " resolved method metadata.\n"
@@ -232,6 +307,7 @@ private function registerRawApiCallTools(Builder $builder, string $rawApiAccessM
232307
$builder->addTool(
233308
[ApiCallDelete::class, 'call'],
234309
ApiCallDelete::TOOL_NAME,
310+
null,
235311
"Use when: you need to execute a known delete-style Matomo API method directly.\n"
236312
. "Purpose: call one allowed delete method and return its result plus the"
237313
. " resolved method metadata.\n"
@@ -254,6 +330,7 @@ private function registerRawApiCallTools(Builder $builder, string $rawApiAccessM
254330
$builder->addTool(
255331
[ApiCallFull::class, 'call'],
256332
ApiCallFull::TOOL_NAME,
333+
null,
257334
"Use when: you need to execute a known Matomo API method directly and"
258335
. " it is not safely covered by one CRUD-specific tool.\n"
259336
. "Purpose: call one allowed full-access API method and return its result"

tests/Unit/Server/Handler/Request/CompatibleCallToolHandlerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public function testRejectsNonEmptyListApiParametersAgainstObjectOnlySchema(): v
9191

9292
private function createTool(string $name): \Matomo\Dependencies\McpServer\Mcp\Schema\Tool
9393
{
94-
return new \Matomo\Dependencies\McpServer\Mcp\Schema\Tool($name, [
94+
return new \Matomo\Dependencies\McpServer\Mcp\Schema\Tool($name, null, [
9595
'type' => 'object',
9696
'properties' => [
9797
'idSite' => ['type' => 'integer'],

tests/Unit/Server/Handler/Request/ObservedCallToolHandlerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ public function testFailureLogsAtDebugWhenVerboseConfigured(): void
447447
*/
448448
private function createTool(string $name, array $inputSchema): \Matomo\Dependencies\McpServer\Mcp\Schema\Tool
449449
{
450-
return new \Matomo\Dependencies\McpServer\Mcp\Schema\Tool($name, $inputSchema, null, null);
450+
return new \Matomo\Dependencies\McpServer\Mcp\Schema\Tool($name, null, $inputSchema, null, null);
451451
}
452452

453453
private function createSession(string $uuid): Session

0 commit comments

Comments
 (0)