Skip to content

Commit 9c64fef

Browse files
authored
Use MCP example with Claude CLI (#3274)
1 parent 1887149 commit 9c64fef

1 file changed

Lines changed: 168 additions & 72 deletions

File tree

docs/ai/mcp/mcp_usage.md

Lines changed: 168 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Additionally, you can create your own capabilities (tools, prompts, and resource
1313

1414
The [[= product_name =]] MCP server framework (`ibexa/mcp`) is built on top of the [official PHP SDK for MCP (`mcp/sdk`)](https://github.com/modelcontextprotocol/php-sdk).
1515

16-
A PHP class that implements MCP server capabilities such as tools, prompts, or resources, must:
16+
A PHP class that implements MCP server capabilities such as tools, prompts, or resources must:
1717

1818
- implement [`Ibexa\Contracts\Mcp\McpCapabilityInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Mcp-McpCapabilityInterface.html) so that it can be scanned for capabilities
1919
- use attributes from the [`Ibexa\Contracts\Mcp\Attribute` namespace](/api/php_api/php_api_reference/namespaces/ibexa-contracts-mcp-attribute.html) to declare capabilities
@@ -25,7 +25,7 @@ It accepts the following optional arguments:
2525

2626
- `servers` - array of server identifiers the tool is assigned to
2727
<br>For more information, see [tools configuration](mcp_config.md#tool-configuration).
28-
- `name` - tool codename - if not set, function name is used
28+
- `name` - tool codename - if not set, the function name is used
2929
- `title` - tool title for user interfaces - if not set, the `name` is used
3030
- `description` - tool description, used by AI agents to understand the tool's purpose
3131
- `icons` - array of [`Mcp\Schema\Icon`](https://github.com/modelcontextprotocol/php-sdk/blob/main/src/Schema/Icon.php) instances
@@ -53,7 +53,7 @@ Methods that return a prompt are marked with the [`Ibexa\Contracts\Mcp\Attribute
5353
It accepts several arguments that describe how the prompt is used:
5454

5555
- `servers` - array of server identifiers exposing this prompt - required for prompts
56-
- `name` (optional) - prompt codename - if not set, method name is used
56+
- `name` (optional) - prompt codename - if not set, the method name is used
5757
- `title` (optional) - prompt title - if not set, `name` is used
5858
- `description` (optional) - human-readable prompt description
5959
- `icons` (optional) - array of [`Mcp\Schema\Icon`](https://github.com/modelcontextprotocol/php-sdk/blob/main/src/Schema/Icon.php) instances
@@ -63,7 +63,7 @@ It accepts several arguments that describe how the prompt is used:
6363

6464
The framework automatically builds the `arguments` array from the method arguments and their types.
6565
Prompt method arguments must be strings to comply with the [`GetPromptRequestParams` schema](https://modelcontextprotocol.io/specification/2025-11-25/schema#getpromptrequestparams).
66-
To add argument descriptions, use DocBlock `@param` tags, it's mapped to the `description` defined by the [`PromptArgument` schema](https://modelcontextprotocol.io/specification/2025-11-25/schema#promptargument).
66+
To add argument descriptions, use DocBlock `@param` tags, which are mapped to the `description` defined by the [`PromptArgument` schema](https://modelcontextprotocol.io/specification/2025-11-25/schema#promptargument).
6767

6868
## Example
6969

@@ -99,7 +99,7 @@ In a new `config/packages/mcp.yaml` file, define a new MCP server for the `defau
9999
[[= include_code('code_samples/mcp/config/packages/mcp.yaml') =]]
100100
```
101101

102-
Adapt the `allowed_hosts` to your case, for example, if you want to use the DDEV `.ddev.site` domain instead of its `127.0.0.1` address equivalent.
102+
Adapt the `allowed_hosts` to your case, for example, if you want to use the DDEV `.ddev.site` domain instead of the equivalent `127.0.0.1` address.
103103

104104
An `ibexa.mcp.example` route is now available:
105105

@@ -113,15 +113,15 @@ Create an `ExampleCapabilities` class that implements `McpCapabilityInterface`.
113113

114114
The class contains:
115115

116-
- a method marked with an [`McpTool` attribute](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Mcp-Attribute-McpTool.html) that associates it to the `example` server as `greet` tool
116+
- a method marked with an [`McpTool` attribute](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Mcp-Attribute-McpTool.html) that associates it with the `example` server as the `greet` tool
117117
- a method marked with an [`McpPrompt` attribute](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Mcp-Attribute-McpPrompt.html) that provides a prompt template to users
118118

119119
``` php
120120
[[= include_code('code_samples/mcp/src/Mcp/ExampleCapabilities.php') =]]
121121
```
122122

123123
In this example, the `servers` attribute parameter associates only this tool with the `example` server.
124-
Alternatively, you can assign all tools from the class to a server by using the `tools` parameter in server configuration.
124+
Alternatively, you can assign all tools from the class to a server by using the `tools` parameter in the server configuration.
125125
For more information, see [tools configuration](mcp_config.md#tool-configuration).
126126

127127
For the prompt, the `servers` parameter is required.
@@ -249,7 +249,7 @@ You can test your server with the [MCP Inspector](https://modelcontextprotocol.i
249249
You can even use the inspector as a DDEV add-on with [`michtio/ddev-mcp-inspector`](https://github.com/michtio/ddev-mcp-inspector).
250250
You still need to ask for a JWT token through REST or GraphQL APIs, and use it in the MCP Inspector configuration to connect to the server.
251251

252-
You can use a Web interface to obtain the JWT token:
252+
You can use a web interface to obtain the JWT token:
253253

254254
- [REST live documentation](rest_api_authentication.md#jwt-token-obtained-through-rest-documentation)
255255
- [GraphiQL](graphql.md#jwt-authentication)
@@ -283,21 +283,23 @@ You can select and test it in the right column.
283283

284284
![Right panel of MCP Inspector with a list of prompts obtained from the MCP server, and the test of the `greet` prompt](img/mcp-inspector-greet-prompt.png "MCP Inspector `greet` prompt test")
285285

286-
### Perform Copilot CLI test
286+
### Perform Copilot or Claude Code test
287287

288-
#### Add MCP server to Copilot CLI
288+
You can test your MCP server with [Copilot CLI](https://docs.github.com/en/copilot/concepts/agents/copilot-cli/about-copilot-cli) or [Claude Code CLI](https://code.claude.com/docs/en/overview), as illustrated here, or with any other agent or interface.
289289

290-
For the sake of the [Copilot CLI](https://docs.github.com/en/copilot/concepts/agents/copilot-cli/about-copilot-cli) test in this example, you configure the MCP server in an `.mcp.json` file at the [[= product_name =]] project root.
291-
This way it is only available for a session opened from there.
290+
#### Add MCP server to agent CLI
291+
292+
For the sake of the agent test, in this example, you configure the MCP server in an `.mcp.json` file at the [[= product_name =]] project root.
293+
This way, it is only available for a session opened from there.
292294

293295
You can handle the JWT token for this test in the following ways:
294296

295-
- Hard code the JWT token into the configuration and update it at every expiration.
296-
- Wrap a JWT token request and an MCP server call into a script.
297+
- [Hard-code the JWT token](#hard-coded-variant) into the configuration and update it at every expiration.
298+
- [Wrap a JWT token request and an MCP server call into a script](#fully-scripted-variant).
297299

298-
##### Hard coded variant
300+
##### Hard-coded variant
299301

300-
The hard coded JWT token configuration in `.mcp.json` looks as follows:
302+
The hard-coded JWT token configuration in `.mcp.json` looks as follows:
301303

302304
``` json
303305
[[= include_code('code_samples/mcp/http.mcp.json') =]]
@@ -307,12 +309,24 @@ The `.mcp.json` file must be edited to update the JWT token each time it expires
307309
You can request a token by using the GraphiQL web interface or a `curl` command, and then edit the file manually.
308310
Alternatively, you can configure a shell script to request the JWT token, extract it from the response, and replace it in the file.
309311

310-
When Copilot complains that it can't communicate with the MCP server:
312+
When Copilot or Claude Code complains that it can't communicate with the MCP server:
311313

312-
- Update the JWT token in the `.mcp.json` file.
313-
- Reload the MCP servers in Copilot CLI with one of these methods:
314-
- Run `/mcp reload` command to reload all MCP servers.
315-
- Run `/mcp disable ibexa-example` and `/mcp enable ibexa-example` to only reload the `ibexa-example` server.
314+
=== "Copilot CLI"
315+
316+
- Update the JWT token in the `.mcp.json` file.
317+
- Reload the MCP servers in Copilot CLI with one of these methods:
318+
- Run `/mcp reload` command to reload all MCP servers.
319+
- Run `/mcp disable ibexa-example` and `/mcp enable ibexa-example` to only reload the `ibexa-example` server.
320+
321+
!!! note "Reloading multiple MCP servers"
322+
323+
If you have several MCP servers enabled globally, reloading all of them at the same time can be time-consuming.
324+
Consider reloading them one by one.
325+
326+
=== "Claude Code CLI"
327+
328+
- Update the JWT token in the `.mcp.json` file.
329+
- Run `/mcp reconnect ibexa-example` command to reconnect the `ibexa-example` MCP server.
316330

317331
##### Fully scripted variant
318332

@@ -330,92 +344,174 @@ For example, thanks to [`npx`](https://www.npmjs.com/package/npx), you can do it
330344
[[= include_code('code_samples/mcp/mcp-ibexa-example-wrapper.sh') =]]
331345
```
332346

333-
When Copilot complains that it can't communicate with the MCP server, reload the MCP servers in Copilot CLI with one of these methods:
347+
When the agent complains that it can't communicate with the MCP server, reload it:
334348

335-
- Run `/mcp reload` command to reload all MCP servers.
336-
- Run `/mcp disable ibexa-example` and `/mcp enable ibexa-example` to only reload the `ibexa-example` server.
349+
=== "Copilot CLI"
337350

338-
!!! note "Reloading multiple MCP servers"
351+
Reload the MCP servers in Copilot CLI with one of these methods:
339352

340-
If you have several MCP servers enabled globally, reloading all of them at the same time can be time consuming.
341-
Consider reloading them one by one.
353+
- Run `/mcp reload` command to reload all MCP servers.
354+
- Run `/mcp disable ibexa-example` and `/mcp enable ibexa-example` to only reload the `ibexa-example` server.
342355

343-
#### Run MCP server test with Copilot CLI
356+
!!! note "Reloading multiple MCP servers"
344357

345-
Launch Copilot CLI at the project root, where the `.mcp.json` file is located:
358+
If you have several MCP servers enabled globally, reloading all of them at the same time can be time-consuming.
359+
Consider reloading them one by one.
346360

347-
```bash
348-
cd /path/to/project
349-
copilot
350-
```
361+
=== "Claude Code CLI"
362+
363+
Run `/mcp reconnect ibexa-example` command to reconnect the `ibexa-example` MCP server.
364+
365+
#### Run MCP server test with Copilot CLI or Claude Code CLI
366+
367+
Launch the agent CLI at the project root, where the `.mcp.json` file is located:
368+
369+
=== "Copilot CLI"
370+
371+
```bash
372+
cd /path/to/project
373+
copilot
374+
```
375+
376+
=== "Claude Code CLI"
377+
378+
```bash
379+
cd /path/to/project
380+
claude
381+
```
351382

352383
If prompted, confirm that you trust the files in this folder.
353384
You may choose to have your choice remembered for the future.
354385

355-
Run the `/mcp show ibexa-example` to check the MCP server status and details:
386+
You can check the MCP server status and details with the `/mcp` command:
356387

357-
```text
358-
MCP Server: ibexa-example
388+
=== "Copilot CLI"
359389

360-
Type: http
361-
URL: http://localhost/mcp/example
362-
Status: ✓ Connected
363-
Source: /path/to/project/.mcp.json
390+
Run the `/mcp show ibexa-example` command to check the MCP server status and details:
364391

365-
Tools (1/1 enabled):
366-
✓ greet: Greet a user by name
367-
```
392+
``` text
393+
MCP Server: ibexa-example
394+
395+
Type: stdio
396+
Command: bash
397+
Status: ✓ Connected
398+
Source: /path/to/project/.mcp.json
399+
400+
Tools (1/1 enabled):
401+
✓ greet: Greet a user by name (70 tokens)
402+
```
403+
404+
=== "Claude Code CLI"
368405

369-
You can prompt Copilot to greet you.
406+
Run the `/mcp` command and select ibexa-example to check the MCP server status and details:
407+
408+
``` text
409+
Ibexa-example MCP Server
410+
411+
Status: ✔ connected
412+
Command: bash
413+
Args: mcp-ibexa-example-wrapper.sh
414+
Config location: /path/to/project/.mcp.json
415+
Capabilities: tools
416+
Tools: 1 tool
417+
```
418+
419+
You can prompt the agent to greet you.
370420
It should use the `greet` tool to do so.
371421

372422
- You can ask it "Please, greet me." and it might respond with a request for your name if it doesn't already know it.
373-
- You can also give it another name to greet, like in the prompt template.
423+
- You can also give it another name to greet, as shown in the prompt template.
374424

375425
During a morning session, the interaction could look like this:
376426

377-
``` text
378-
❯ Hi. Please, greet me.
427+
=== "Copilot CLI"
379428

380-
◐ The user wants to be greeted. I'll use the ibexa-example-greet tool, but I don't know their name. Let me ask them first... actually, the tool requires a name. Let me ask the user for their name.
429+
```text
430+
❯ Hi. Please, greet me.
381431

382-
Wait, actually I can just greet them without knowing their name using a general greeting, but the tool requires a name parameter. Let me ask for their name.
432+
◐ The user wants to be greeted. I'll use the ibexa-example-greet tool, but I don't know their name. Let me ask them first... actually, the tool requires a name. Let me ask the user for their name.
383433

384-
● Asked user What's your name?
385-
└ Pulley
434+
Wait, actually I can just greet them without knowing their name using a general greeting, but the tool requires a name parameter. Let me ask for their name.
386435

387-
● Greet a user (MCP: ibexa-example) · name: "Pulley"
388-
└ {"general":"Hello, Pulley!","close":"Hey, Pulley!","morning":"Good morning, P...
436+
● Asked user What's your name?
437+
└ Pulley
389438

390-
● Good morning, Pulley! ☀️
439+
● Greet a user (MCP: ibexa-example) · name: "Pulley"
440+
└ {"general":"Hello, Pulley!","close":"Hey, Pulley!","morning":"Good morning, P...
391441

392-
❯ Hi. Please, greet me. We're close friends.
442+
● Good morning, Pulley! ☀️
393443

394-
◐ The user wants a close friend greeting. From the previous response, I can see the "close" greeting is "Hey, Pulley!"
444+
❯ Hi. Please, greet me. We're close friends.
395445

396-
● Greet a user (MCP: ibexa-example) · name: "Pulley"
397-
└ {"general":"Hello, Pulley!","close":"Hey, Pulley!","morning":"Good morning, P...
446+
◐ The user wants a close friend greeting. From the previous response, I can see the "close" greeting is "Hey, Pulley!"
398447

399-
● Hey, Pulley! 😄
448+
● Greet a user (MCP: ibexa-example) · name: "Pulley"
449+
└ {"general":"Hello, Pulley!","close":"Hey, Pulley!","morning":"Good morning, P...
400450

401-
❯ Please, greet someone named "ElePHPant" in a general way (so I could reuse it at any time)
451+
● Hey, Pulley! 😄
402452

403-
◐ The user wants a general greeting for someone named "ElePHPant".
453+
❯ Please, greet someone named "ElePHPant" in a general way (so I could reuse it at any time)
404454

405-
● greet (MCP: ibexa-example) · name: "ElePHPant"
406-
└ {"general":"Hello, ElePHPant!","close":"Hey, ElePHPant!","morning":"Good morn...
455+
◐ The user wants a general greeting for someone named "ElePHPant".
407456

408-
● Hello, ElePHPant! 🐘
457+
● greet (MCP: ibexa-example) · name: "ElePHPant"
458+
└ {"general":"Hello, ElePHPant!","close":"Hey, ElePHPant!","morning":"Good morn...
409459

410-
❯ Please, greet someone named "ElePHPant" like it's 22:30
460+
● Hello, ElePHPant! 🐘
411461

412-
◐ 22:30 is evening time, so I'll use the evening greeting.
462+
❯ Please, greet someone named "ElePHPant" like it's 22:30
413463

414-
● greet (MCP: ibexa-example) · name: "ElePHPant"
415-
└ {"general":"Hello, ElePHPant!","close":"Hey, ElePHPant!","morning":"Good morn...
464+
◐ 22:30 is evening time, so I'll use the evening greeting.
416465

417-
● Good evening, ElePHPant! 🌙
418-
```
466+
● greet (MCP: ibexa-example) · name: "ElePHPant"
467+
└ {"general":"Hello, ElePHPant!","close":"Hey, ElePHPant!","morning":"Good morn...
468+
469+
● Good evening, ElePHPant! 🌙
470+
```
471+
472+
=== "Claude Code CLI"
473+
474+
```text
475+
❯ Hi. Please, greet me.
476+
477+
⏺ What's your name?
478+
479+
✻ Worked for 3s
480+
481+
❯ Pulley
482+
483+
Called ibexa-example
484+
485+
⏺ Hello, Pulley! 👋
486+
487+
✻ Churned for 4s
488+
489+
❯ Hi. Please, greet me. We're close friends now.
490+
491+
Called ibexa-example
492+
493+
⏺ Hey, Pulley! 👋
494+
495+
✻ Baked for 4s
496+
497+
❯ Please, greet someone named "ElePHPant" in a general way (so I could reuse it at any time)
498+
499+
Called ibexa-example
500+
501+
⏺ Hello, ElePHPant!
502+
503+
✻ Brewed for 5s
504+
505+
❯ Please, greet someone named "ElePHPant" like it's 22:30
506+
507+
⏺ That falls under the "evening" variant: Good evening, ElePHPant!
508+
509+
✻ Sautéed for 2s
510+
```
511+
512+
The agent's reflections, reaction times, and final responses, including the improvised emojis, may differ from those examples.
513+
The key point is that the agent decides to use the `greet` tool, calls it with the right argument, and then uses the call result in its final output.
419514

420-
The Copilot's reflection and its final response, including the improvised emoji, may differ from this example.
421-
The key point is that Copilot CLI decides to use the `greet` tool, calls it with the right argument, displays the call result, and then uses it in its final output.
515+
You can fine-tune the prompt, or remove unnecessary variants if needed.
516+
For example, you could instruct the agent to always use the time-of-day variants, or simply remove the `general` and `close` variants.
517+
Removing what's unnecessary is more efficient than extending the instructions.

0 commit comments

Comments
 (0)