77use KLP \KlpMcpServer \Services \ToolService \BaseToolInterface ;
88use KLP \KlpMcpServer \Services \ToolService \Schema \StructuredSchema ;
99use KLP \KlpMcpServer \Services \ToolService \StreamableToolInterface ;
10+ use KLP \KlpMcpServer \Services \ToolService \ToolRepository ;
1011use Symfony \Component \Console \Attribute \AsCommand ;
1112use Symfony \Component \Console \Command \Command ;
1213use Symfony \Component \Console \Input \InputArgument ;
1920#[AsCommand(name: 'mcp:test-tool ' , description: 'Test an MCP tool with simulated inputs ' )]
2021class TestMcpToolCommand extends Command
2122{
22- public function __construct (private readonly ContainerInterface $ container )
23- {
23+ public function __construct (
24+ private readonly ToolRepository $ toolRepository ,
25+ private readonly ContainerInterface $ container
26+ ) {
2427 parent ::__construct ();
2528 }
2629
@@ -140,7 +143,7 @@ function (array $notification) use (&$sentNotifications) {
140143 /**
141144 * Resolves and retrieves an instance of a tool based on the provided identifier
142145 * from the input or user prompt. The method checks for a matching class name,
143- * an exact tool match, or a case-insensitive tool name match from the configured tools.
146+ * an exact tool match, or a case-insensitive tool name match from the registered tools.
144147 *
145148 * @return StreamableToolInterface Returns the tool instance if found.
146149 *
@@ -155,32 +158,51 @@ public function getToolInstance(): StreamableToolInterface
155158 }
156159
157160 $ toolInstance = null ;
158- // First check if the identifier is a direct class name
161+
162+ // Strategy 1: Check if identifier is a fully-qualified class name
163+ // This allows: php bin/console mcp:test-tool "App\MCP\Tools\MyTool"
159164 if (class_exists ($ identifier )) {
160- $ toolInstance = $ this ->container ->get ($ identifier );
165+ // First check if we have this tool class in ToolRepository
166+ foreach ($ this ->toolRepository ->getTools () as $ tool ) {
167+ if (get_class ($ tool ) === $ identifier ) {
168+ $ toolInstance = $ tool ;
169+ break ;
170+ }
171+ }
172+
173+ // Fallback: Try to instantiate directly from container for backward compatibility
174+ // This handles edge cases where a tool class exists but isn't registered
175+ if (! $ toolInstance ) {
176+ $ toolInstance = $ this ->container ->get ($ identifier );
177+ }
161178 }
162179
180+ // Strategy 2: Look up by tool name or partial class name
163181 if (! $ toolInstance ) {
164- // Load all registered tools from config
165- $ configuredTools = $ this ->container ->getParameter ('klp_mcp_server.tools ' );
166-
167- // Check for the class match
168- foreach ($ configuredTools as $ toolClass ) {
169- $ instance = $ this ->container ->get ($ toolClass );
170- if ( // Check for the exact class match
171- str_ends_with ($ toolClass , "\\{$ identifier }" )
172- || $ toolClass === $ identifier
173- // Check for tool name match (case-insensitive)
174- || strtolower ($ instance ->getName ()) === strtolower ($ identifier )
175- ) {
182+ $ registeredTools = $ this ->toolRepository ->getTools ();
183+
184+ // First try exact tool name match (case-insensitive)
185+ foreach ($ registeredTools as $ toolName => $ instance ) {
186+ if (strtolower ($ toolName ) === strtolower ($ identifier )) {
176187 $ toolInstance = $ instance ;
177188 break ;
178189 }
179190 }
191+
192+ // If not found, try partial class name match
193+ if (! $ toolInstance ) {
194+ foreach ($ registeredTools as $ toolName => $ instance ) {
195+ $ toolClass = get_class ($ instance );
196+ if (str_ends_with ($ toolClass , "\\{$ identifier }" ) || $ toolClass === $ identifier ) {
197+ $ toolInstance = $ instance ;
198+ break ;
199+ }
200+ }
201+ }
180202 }
181203
182- if ( $ toolInstance
183- && ! ($ toolInstance instanceof StreamableToolInterface)) {
204+ // Validate the tool implements StreamableToolInterface
205+ if ( $ toolInstance && ! ($ toolInstance instanceof StreamableToolInterface)) {
184206 $ toolClass = get_class ($ toolInstance );
185207 throw new TestMcpToolCommandException ("The class ' {$ toolClass }' does not implement StreamableToolInterface. " );
186208 }
@@ -332,30 +354,29 @@ public function askForInputData(StructuredSchema|array $schema): ?array
332354 */
333355 private function listAllTools (): int
334356 {
335- $ configuredTools = $ this ->container ->getParameter ('klp_mcp_server.tools ' );
357+ // Get all registered tools from ToolRepository
358+ $ registeredTools = $ this ->toolRepository ->getTools ();
336359
337- if (empty ($ configuredTools )) {
338- $ this ->io ->warning ('No MCP tools are configured. Add tools in config/package/klp-mcp-server.yaml ' );
360+ if (empty ($ registeredTools )) {
361+ $ this ->io ->warning ('No MCP tools are configured. Add tools in config/package/klp-mcp-server.yaml or create a ToolProvider. ' );
339362
340363 return Command::SUCCESS ;
341364 }
342365
343366 $ tools = [];
344367
345- foreach ($ configuredTools as $ toolClass ) {
368+ foreach ($ registeredTools as $ toolName => $ instance ) {
346369 try {
347- if (class_exists ($ toolClass )) {
348- $ instance = $ this ->container ->get ($ toolClass );
349- if ($ instance instanceof BaseToolInterface) {
350- $ tools [] = [
351- 'name ' => $ instance ->getName (),
352- 'class ' => $ toolClass ,
353- 'description ' => substr ($ instance ->getDescription (), 0 , 50 ),
354- ];
355- }
370+ if ($ instance instanceof BaseToolInterface) {
371+ $ tools [] = [
372+ 'name ' => $ instance ->getName (),
373+ 'class ' => get_class ($ instance ),
374+ 'description ' => substr ($ instance ->getDescription (), 0 , 50 ),
375+ ];
356376 }
357377 } catch (\Throwable $ e ) {
358- $ this ->io ->warning ("Couldn't load tool class: {$ toolClass }" );
378+ $ toolClass = get_class ($ instance );
379+ $ this ->io ->warning ("Couldn't load tool: {$ toolClass }" );
359380 }
360381 }
361382
@@ -376,29 +397,27 @@ private function listAllTools(): int
376397 */
377398 protected function askForTool (): ?string
378399 {
379- $ configuredTools = $ this ->container -> getParameter ( ' klp_mcp_server.tools ' );
400+ $ registeredTools = $ this ->toolRepository -> getTools ( );
380401
381- if (empty ($ configuredTools )) {
382- $ this ->io ->warning ('No MCP tools are configured. Add tools in config/package/klp-mcp-server.yaml ' );
402+ if (empty ($ registeredTools )) {
403+ $ this ->io ->warning ('No MCP tools are configured. Add tools in config/package/klp-mcp-server.yaml or create a ToolProvider. ' );
383404
384405 return null ;
385406 }
386407
387408 $ choices = [];
388- $ validTools = [];
409+ $ validToolNames = [];
389410
390- foreach ($ configuredTools as $ toolClass ) {
411+ foreach ($ registeredTools as $ toolName => $ instance ) {
391412 try {
392- if (class_exists ($ toolClass )) {
393- $ instance = $ this ->container ->get ($ toolClass );
394- if ($ instance instanceof BaseToolInterface) {
395- $ name = $ instance ->getName ();
396- $ choices [] = "{$ name } ( {$ toolClass }) " ;
397- $ validTools [] = $ toolClass ;
398- }
413+ if ($ instance instanceof BaseToolInterface) {
414+ $ name = $ instance ->getName ();
415+ $ class = get_class ($ instance );
416+ $ choices [] = "{$ name } ( {$ class }) " ;
417+ $ validToolNames [] = $ name ; // Store tool name instead of class
399418 }
400419 } catch (\Throwable ) {
401- // Skip tools that can't be loaded
420+ // Skip tools that can't be processed
402421 }
403422 }
404423
@@ -413,7 +432,7 @@ protected function askForTool(): ?string
413432 $ choices
414433 );
415434
416- return $ validTools [$ selectedIndex ] ?? null ;
435+ return $ validToolNames [$ selectedIndex ] ?? null ;
417436 }
418437
419438 /**
0 commit comments