Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.23% covered (success)
94.23%
49 / 52
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
ToolRunner
94.23% covered (success)
94.23%
49 / 52
33.33% covered (danger)
33.33%
1 / 3
15.04
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 run
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
3.00
 actionResponseToToolResult
94.59% covered (success)
94.59%
35 / 37
0.00% covered (danger)
0.00%
0 / 1
11.02
1<?php
2namespace Apie\McpServer\Tool;
3
4use Apie\Common\ApieFacade;
5use Apie\Core\Actions\ActionResponse;
6use Apie\Core\Actions\ActionResponseStatus;
7use Apie\Core\ContextBuilders\ContextBuilderFactory;
8use Apie\Core\ContextConstants;
9use Apie\Core\ValueObjects\Utils;
10use Mcp\Types\CallToolResult;
11use Mcp\Types\TextContent;
12use Mcp\Types\Tool;
13use Psr\Http\Message\ServerRequestInterface;
14use ReflectionClass;
15
16class ToolRunner
17{
18    public function __construct(
19        private readonly ContextBuilderFactory $contextBuilder,
20        private readonly ApieFacade $apieFacade,
21    ) {
22    }
23    public function run(Tool $tool, array $params, ?ServerRequestInterface $request = null): CallToolResult
24    {
25        $meta = Utils::toArray($tool->_meta);
26        $action = new ReflectionClass($meta["x-definition"]);
27        $fields = Utils::toArray($meta["x-fields"]);
28        $fields[ContextConstants::MCP_SERVER] = true;
29        $fields[Tool::class] = true;
30        $fields[ContextConstants::RAW_CONTENTS] = $params;
31        if (isset($params['id'])) {
32            $fields[ContextConstants::RESOURCE_ID] = $params['id'];
33        }
34        $context = $request
35            ? $this->contextBuilder->createFromRequest($request, $fields)
36            : $this->contextBuilder->createGeneralContext($fields);
37        $action = $this->apieFacade->createAction($context);
38        /** @var ActionResponse $data */
39        $data = ($action)($context, $params);
40        return $this->actionResponseToToolResult($data);
41    }
42
43    private function actionResponseToToolResult(ActionResponse $input): CallToolResult
44    {
45        switch ($input->status) {
46            case ActionResponseStatus::CLIENT_ERROR:
47            case ActionResponseStatus::AUTHORIZATION_ERROR:
48            case ActionResponseStatus::OUTPUT_ERROR:
49            case ActionResponseStatus::PERISTENCE_ERROR:
50            case ActionResponseStatus::SERVER_ERROR:
51                return new CallToolResult(
52                    [
53                        new TextContent(json_encode($input->getResultAsNativeData())),
54                    ],
55                    true
56                );
57
58            case ActionResponseStatus::DELETED:
59                return new CallToolResult(
60                    [
61                        new TextContent(json_encode("Resource was deleted correctly.")),
62                    ],
63                    false
64                );
65            case ActionResponseStatus::NOT_FOUND:
66                return new CallToolResult(
67                    [
68                        new TextContent(json_encode("Resource was not found.")),
69                    ],
70                    true
71                );
72            
73            case ActionResponseStatus::CREATED:
74            case ActionResponseStatus::SUCCESS:
75                if (isset($input->error)) {
76                    throw $input->error;
77                }
78                return new CallToolResult(
79                    [
80                        new TextContent(json_encode($input->getResultAsNativeData())),
81                    ],
82                    false
83                );
84        }
85        throw new \LogicException('Unknown status ' . $input->status->value);
86    }
87}