Skip to content

MCP server usage

MCP Servers LTS Update comes with few built-in tools. You can create your own capabilities (tools, prompts, and resources) to expose custom features to AI agents through your MCP servers.

MCP server capabilities

The Ibexa DXP MCP server framework (ibexa/mcp) is built on top of the official PHP SDK for MCP (mcp/sdk)

A PHP class implementing MCP server capabilities like tools, prompts, or resources, must:

Tools

The Ibexa\Contracts\Mcp\Attribute\McpTool attribute declares a method as an MCP tool. It has several arguments to describe the tool usage and output:

  • servers (optional): an array of identifiers of servers this tool is assigned to - for more information, see tools configuration
  • name (optional): the name of the tool - if not set, the function name is used as the tool name
  • description (optional): description of the tool, used by the AI agent to understand the tool's purpose
  • icons (optional): an array of Mcp\Schema\Icon instances - for more information, see icons specification
  • outputSchema (optional): for JSON object output, an associative array describing this object
  • annotations (optional): a Mcp\Schema\ToolAnnotations instance - for more information, see ToolAnnotations specification
  • meta (optional): a free-form array for any additional metadata - for more information, see _meta specification

An inputSchema is automatically built from the function arguments and their types. To override or complement the automatically generated input schema, you can use a DocBlock comment with @param tags to add descriptions, or use the Schema attribute. If an argument is an enum, its possible values are listed in the schema (UntitledSingleSelectEnumSchema).

Prompts

MCP servers can also provide prompt templates to guide the user interacting with the AI having this MCP server at its disposal.

The Ibexa\Contracts\Mcp\Attribute\McpPrompt attribute defines a method as returning a prompt.

It has several arguments to describe the prompt usage:

  • servers: an array of identifiers of servers proposing this prompt - notice that this is required for prompts
  • name (optional): the name of the prompt - if not set, the function name is used as the prompt name
  • description (optional): a human-readable description of the prompt
  • icons (optional): an array of Mcp\Schema\Icon instances - for more information, see icons specification
  • meta (optional): a rarely used free-form array for any additional metadata - for more information, see _meta specification

An arguments array is automatically built from the function arguments and their types. The prompt's function arguments must be strings (to respect the GetPromptRequestParams schema). To add descriptions (as in the PromptArgument schema), use a DocBlock comment with @param tags.

Example

To focus on the MCP server configuration and capabilities creation, this example doesn't even interact with Ibexa DXP repository.

User account

In this example, the MCP server uses JWT tokens created with a dedicated account.

In Ibexa DXP's back office, create a user, for example, in Guest accounts user group, with login ibexa-example, and password Ibexa-3xample.

Configure MCP server

This example introduce an example MCP server with a single greet tool. It's enabled on the default repository and all SiteAccesses. It's accessible with the path /mcp/example (for example, on http://localhost/mcp/example and http://localhost/admin/mcp/example). It uses files for both discovery cache and session storage. (Redis/Valkey would be better for session storage in production, but file storage is easier for this example and testing.)

In a new config/packages/mcp.yaml file, define a new MCP server for the default repository and assign it to all SiteAccesses:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
ibexa:
    repositories:
        default:
            mcp:
                example:
                    path: /mcp/example
                    enabled: true
                    description: 'Example MCP Server'
                    instructions: 'Use this server to greet someone.'
                    discovery_cache: cache.tagaware.filesystem
                    session:
                        type: psr16
                        directory: cache.tagaware.filesystem
    system:
        default:
            mcp:
                servers:
                    - example

An ibexa.mcp.example route is now available:

1
php bin/console debug:router ibexa.mcp.example

Create capability class

An ExampleCapabilities class implementing the McpCapabilityInterface is created.

It contains a function with an McpTool attribute associating it to the example server as greet tool for the AI.

It also contains a function with the McpPrompt attribute to provide a prompt template to the user.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
<?php declare(strict_types=1);

namespace App\Mcp;

use Ibexa\Contracts\Mcp\Attribute\McpPrompt;
use Ibexa\Contracts\Mcp\Attribute\McpTool;
use Ibexa\Contracts\Mcp\McpCapabilityInterface;
use Mcp\Schema\Icon;
use Mcp\Schema\ToolAnnotations;

final readonly class ExampleCapabilities implements McpCapabilityInterface
{
    /**
     * @param string $name The name of the person to greet
     *
     * @return array<string, string>
     */
    #[McpTool(
        servers: ['example'],
        name: 'greet',
        description: 'Greet a user by name',
        annotations: new ToolAnnotations(
            readOnlyHint: true,
            destructiveHint: false,
            idempotentHint: true,
            openWorldHint: false,
        ),
        icons: [new Icon(
            src: 'https://openmoji.org/data/color/svg/1F44B.svg',
        )],
        outputSchema: [
            'type' => 'object',
            'properties' => [
                'general' => [
                    'type' => 'string',
                    'description' => 'the safe way to greet someone',
                ],
                'close' => [
                    'type' => 'string',
                    'description' => 'when you\'re close to the person, like friends or relatives',
                ],
                'morning' => [
                    'type' => 'string',
                    'description' => 'when it\'s in the morning',
                ],
                'afternoon' => [
                    'type' => 'string',
                    'description' => 'when it\'s the afternoon',
                ],
                'evening' => [
                    'type' => 'string',
                    'description' => 'when it\'s late in the day',
                ],
            ],
        ],
    )]
    public function greetByName(string $name): array
    {
        return [
            'general' => sprintf('Hello, %s!', $name),
            'close' => sprintf('Hey, %s!', $name),
            'morning' => sprintf('Good morning, %s!', $name),
            'afternoon' => sprintf('Good afternoon, %s!', $name),
            'evening' => sprintf('Good evening, %s!', $name),
        ];
    }

    /**
     * @param string $name The name you want to be greeted by
     *
     * @return array<string, mixed>
     */
    #[McpPrompt(
        servers: ['example'],
        name: 'greet',
        description: 'Prompt to invoke the `greet` tool',
        icons: [new Icon(
            src: 'https://openmoji.org/data/color/svg/1F91D.svg',
        )],
    )]
    public function getGreetPrompt(string $name): array
    {
        return [
            'role' => 'user',
            'content' => [
                'type' => 'text',
                'text' => "Hi. My name is $name. Please, greet me.",
            ],
        ];
    }
}

For the example, servers attribute parameter is used to associate only this tool to the example server. All tools from this class could be added to a server by using the tools parameter in server configuration. For more information, see tools configuration.

For prompt, the servers parameter is required. So, the example prompt has to use it to be associated with the example server.

During development and testing, you may have to clear the cache to make sure new or modified capabilities are properly re-discovered. In this example, regarding its configuration, php bin/console cache:pool:clear cache.tagaware.filesystem has to be used.

Cache clearing

Have no mercy for the cache during development. But use the right commands to be sure to delete it. The following pair of commands ensure all types of caches are cleared wherever stored:

1
2
php bin/console cache:clear
php bin/console cache:pool:clear --all

Create MCP server list command

To check the server configuration, a short command using the MCP server configuration registry (injected through McpServerConfigurationRegistryInterface and autowiring):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php declare(strict_types=1);

namespace App\Command;

use Ibexa\Contracts\Mcp\McpServerConfigurationRegistryInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(name: 'app:mcp:server_list', description: 'List MCP servers')]
class McpServerListCommand
{
    public function __construct(private readonly McpServerConfigurationRegistryInterface $configRegistry)
    {
    }

    public function __invoke(SymfonyStyle $io): int
    {
        foreach($this->configRegistry->getServerConfigurations() as $serverConfiguration) {
            $io->title($serverConfiguration->identifier);
            dump($serverConfiguration);
        }

        return Command::SUCCESS;
    }
}

curl test

To test the example MCP server, a sequence of curl commands is used to simulate an AI client to MCP server communication.

  • Ask for a JWT token through REST
  • Initialize a connection to the MCP server
  • Validate the MCP Session ID
  • List the available tools
  • Call a tool

jq, grep, and sed are also used to parse or display outputs.

First, the shell script set the Ibexa DXP base URL and the user credentials into variables for easier reuse:

1
2
3
baseUrl='http://localhost' # Adapt to your test case
username='ibexa-example'
password='Ibexa-3xample'

Before communicating with the MCP server, the request of a JWT token through REST API:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
curl -s -X 'POST' \
  "$baseUrl/api/ibexa/v2/user/token/jwt" \
  -H 'Content-Type: application/vnd.ibexa.api.JWTInput+json' \
  -H 'Accept: application/vnd.ibexa.api.JWT+json' \
  -d "{
        \"JWTInput\": {
          \"_media-type\": \"application/vnd.ibexa.api.JWTInput+json\",
          \"username\": \"$username\",
          \"password\": \"$password\"
        }
      }" > response.tmp.txt

cat response.tmp.txt | jq
jwtToken=$(cat response.tmp.txt | jq -r .JWT.token)
rm response.tmp.txt
1
2
3
4
5
6
7
{
  "JWT": {
    "_media-type": "application/vnd.ibexa.api.JWT+json",
    "_token": "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ.abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz1234567890ABCD.EFGHIJKL-MNOPQRSTUVWXYZ12345678901234567890",
    "token": "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ.abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz1234567890ABCD.EFGHIJKL-MNOPQRSTUVWXYZ12345678901234567890"
  }
}

The initialization to get an MCP session ID:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
cat response.tmp.txt | jq
jwtToken=$(cat response.tmp.txt | jq -r .JWT.token)
rm response.tmp.txt

curl -s -i -X 'POST' "$baseUrl/mcp/example" \
  -H "Authorization: Bearer $jwtToken" \
  -d '{
        "jsonrpc": "2.0",
        "id": 1,
        "method": "initialize",
        "params": {
          "protocolVersion": "2025-03-26",
          "capabilities": {},
          "clientInfo": {
            "name": "test-curl-client",
            "version": "1.0.0"
          }
        }
      }' > response.tmp.txt

sed '$d' response.tmp.txt
tail -n 1 response.tmp.txt | jq
mcpSessionId=$(cat response.tmp.txt | grep 'Mcp-Session-Id:' | sed 's/Mcp-Session-Id: \([0-9a-f-]*\).*/\1/')
rm response.tmp.txt
1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
Access-Control-Allow-Headers: Content-Type, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID, Authorization, Accept
Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Mcp-Session-Id
Cache-Control: no-cache, private
Content-Type: application/json
Date: Tue, 28 Apr 2026 09:53:27 GMT
Mcp-Session-Id: 12345678-9abc-def0-1234-56789abcdef0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-06-18",
    "capabilities": {
      "logging": {},
      "completions": {},
      "prompts": {
        "listChanged": true
      },
      "resources": {
        "listChanged": true
      },
      "tools": {
        "listChanged": true
      }
    },
    "serverInfo": {
      "name": "example",
      "version": "1.0.0",
      "description": "Example MCP Server"
    },
    "instructions": "Use this server to greet someone."
  }
}

The validation of the initialization:

1
2
3
4
5
6
7
curl -s -i -X 'POST' "$baseUrl/mcp/example" \
  -H "Authorization: Bearer $jwtToken" \
  -H "Mcp-Session-Id: $mcpSessionId" \
  -d '{
        "jsonrpc": "2.0",
        "method": "notifications/initialized"
      }'
1
2
3
4
5
HTTP/1.1 202 Accepted
Access-Control-Allow-Headers: Content-Type, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID, Authorization, Accept
Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Mcp-Session-Id

The list of tools:

1
2
3
4
5
6
7
8
curl -s -X 'POST' "$baseUrl/mcp/example" \
  -H "Authorization: Bearer $jwtToken" \
  -H "Mcp-Session-Id: $mcpSessionId" \
  -d '{
        "jsonrpc": "2.0",
        "id": 2,
        "method": "tools/list"
      }' | jq
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "tools": [
      {
        "name": "greet",
        "inputSchema": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string",
              "description": "The name of the person to greet"
            }
          },
          "required": [
            "name"
          ]
        },
        "description": "Greet a user by name",
        "annotations": {
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        },
        "icons": [
          {
            "src": "https://openmoji.org/data/color/svg/1F44B.svg"
          }
        ],
        "outputSchema": {
          "type": "object",
          "properties": {
            "general": {
              "type": "string",
              "description": "the safe way to greet someone"
            },
            "close": {
              "type": "string",
              "description": "when you're close to the person, like friends or relatives"
            },
            "morning": {
              "type": "string",
              "description": "when it's in the morning"
            },
            "afternoon": {
              "type": "string",
              "description": "when it's the afternoon"
            },
            "evening": {
              "type": "string",
              "description": "when it's late in the day"
            }
          }
        }
      }
    ]
  }
}

The greet tool call:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
curl -s -X 'POST' "$baseUrl/mcp/example" \
  -H "Authorization: Bearer $jwtToken" \
  -H "Mcp-Session-Id: $mcpSessionId" \
  -d '{
        "jsonrpc": "2.0",
        "id": 3,
        "method": "tools/call",
        "params": {
          "name": "greet",
          "arguments": {
            "name": "World"
          }
        }
      }' | jq
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\n    \"general\": \"Hello, World!\",\n    \"close\": \"Hey, World!\",\n    \"morning\": \"Good morning, World!\",\n    \"afternoon\": \"Good afternoon, World!\",\n    \"evening\": \"Good evening, World!\"\n}"
      }
    ],
    "isError": false,
    "structuredContent": {
      "general": "Hello, World!",
      "close": "Hey, World!",
      "morning": "Good morning, World!",
      "afternoon": "Good afternoon, World!",
      "evening": "Good evening, World!"
    }
  }
}

The list of prompts:

1
2
3
4
5
6
7
8
curl -s -X 'POST' "$baseUrl/mcp/example" \
  -H "Authorization: Bearer $jwtToken" \
  -H "Mcp-Session-Id: $mcpSessionId" \
  -d '{
        "jsonrpc": "2.0",
        "id": 4,
        "method": "prompts/list"
      }' | jq
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "prompts": [
      {
        "name": "greet",
        "description": "Prompt to be greeted by the `greet` tool",
        "arguments": [
          {
            "name": "name",
            "description": "The name you want to be greeted by",
            "required": true
          }
        ],
        "icons": [
          {
            "src": "https://openmoji.org/data/color/svg/1F91D.svg"
          }
        ]
      }
    ]
  }
}

The greet prompt obtainment:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
curl -s -X 'POST' "$baseUrl/mcp/example" \
  -H "Authorization: Bearer $jwtToken" \
  -H "Mcp-Session-Id: $mcpSessionId" \
  -d '{
        "jsonrpc": "2.0",
        "id": 5,
        "method": "prompts/get",
        "params": {
          "name": "greet",
          "arguments": {
            "name": "Firstname Lastname"
          }
        }
      }' | jq
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "jsonrpc": "2.0",
  "id": 5,
  "result": {
    "messages": [
      {
        "role": "user",
        "content": {
          "type": "text",
          "text": "Hi. My name is Firstname Lastname. Please, greet me."
        }
      }
    ]
  }
}

MCP Inspector test

To test your server, you can use the MCP Inspector. It's even possible to use it as a DDEV add-on with craftpulse/ddev-mcp-inspector. You still need to ask for a JWT token through REST or GraphQL, and use it in the MCP Inspector configuration to connect to your server.

You can use a Web interface to obtain a JWT token:

MCP server settings

To use the MCP Inspector for this example, the settings are:

  • Transport Type: Streamable HTTP
  • URL: actual domain and server path, for example http://localhost/mcp/example
  • Connection Type: Via Proxy
  • Authentication:
    • Custom Headers:
      • Authorization
      • Bearer <JWT token>
    • OAuth 2.0 Flow: left unedited

Screenshot of the left pannel of the MCP Inspector with the connection settings for the example MCP server

MCP server test within MCP Inspector

In the right panel, in the Tools tab, click List Tools button in the left column. The greet tool appears preceded by its icon. It can be selected and tested in the right column.

Screenshot of the right pannel of the MCP Inspector with the list of tools obtained from the example MCP server, and the test of the greet tool

In the Prompts tab, click List Prompts button in the left column. The greet prompt appears preceded by its icon. It can be selected and tested in the right column.

Screenshot of the right pannel of the MCP Inspector with the list of prompts obtained from the example MCP server, and the test of the greet prompt

Copilot CLI test

MCP server addition to Copilot CLI

For this example test with Copilot CLI, the MCP server configuration is done in an .mcp.json file at the Ibexa DXP project root to make it only available for a session opened from there.

There is two ways of dealing with the JWT token for this test:

  • to hard code the JWT token in the configuration and update it at every expiration
  • to wrap JWT token request and MCP server call into a script
Hard coded

The hard coded JWT token configuration in .mcp.json:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "mcpServers": {
    "ibexa-example": {
      "type": "http",
      "url": "http://localhost/mcp/example",
      "headers": {
        "Authorization": "Bearer <JWT token>"
      },
      "tools": ["*"]
    }
  }
}

The .mcp.json file must be edited to update the JWT token each time it expires. You can ask a token using, for example, GraphiQL web interface or a curl command, then edit the file manually. Or you can have a shell script doing the JWT token request, extracting it from the response, and replace it in the file.

When Copilot complains that it can't communicate with the MCP server:

  • update the JWT token in the .mcp.json file
  • reload the MCP servers in Copilot CLI with one of those methods:
    • run /mcp reload command which reload all MCP servers
    • run /mcp disable ibexa-example then /mcp enable ibexa-example to only reload the ibexa-example server
Fully scripted

The wrapping script configuration in .mcp.json:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "mcpServers": {
    "ibexa-example": {
      "type": "stdio",
      "command": "bash",
      "args": ["mcp-ibexa-example-wrapper.sh"],
      "tools": ["*"]
    }
  }
}

The mcp-ibexa-example-wrapper.sh is a script asking for a JWT token then establishing a connection with the MCP server.

For example, this can be achieved with Supergateway without local installation thanks to npx:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
set -e

baseUrl='http://localhost' # Adapt to your test case

jwtToken=$(curl -s -X 'POST' \
  "$baseUrl/api/ibexa/v2/user/token/jwt" \
  -H 'Content-Type: application/vnd.ibexa.api.JWTInput+json' \
  -H 'Accept: application/vnd.ibexa.api.JWT+json' \
  -d '{
        "JWTInput": {
          "_media-type": "application/vnd.ibexa.api.JWTInput+json",
          "username": "ibexa-example",
          "password": "Ibexa-3xample"
        }
      }' | jq -r .JWT.token)

exec npx -y supergateway \
  --streamableHttp "$baseUrl/mcp/example" \
  --oauth2Bearer "$jwtToken" \
  --logLevel none

When Copilot complains that it can't communicate with the MCP server, reload the MCP servers in Copilot CLI with one of those methods:

  • run /mcp reload command which reload all MCP servers (which can be annoying if you have several MCP servers globally enabled)
  • run /mcp disable ibexa-example then /mcp enable ibexa-example to only reload the ibexa-example server

MCP server test with Copilot CLI

Launch Copilot CLI at the project root (where the .mcp.json file is located):

1
2
cd /path/to/project
copilot

If needed, confirm that you trust the files in this folder when prompted (with or without remembering the answer for the future).

With the command /mcp show ibexa-example, you can check server status and details:

1
2
3
4
5
6
7
8
9
 MCP Server: ibexa-example

 Type:     http
 URL:      http://localhost/mcp/example
 Status:   ✓ Connected
 Source:   /path/to/project/.mcp.json

 Tools (1/1 enabled):
  ✓ greet: Greet a user by name

You can prompt Copilot to greet you. It should take the initiative to use the greet tool to do so.

  • You can ask it "Please, greet me." and it might ask you your name if it doesn't already know it.
  • You can additionally give it a name to greet (like in the prompt template).

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
❯ Hi. Please, greet me.

◐ 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.

  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.

● Asked user What's your name?
  └ Pulley

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

● Good morning, Pulley! ☀️

❯ Hi. Please, greet me. We're close friends.

◐ The user wants a close friend greeting. From the previous response, I can see the "close" greeting is "Hey, Pulley!"

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

● Hey, Pulley! 😄

❯ Please, greet someone named "ElePHPant" in a general way (so I could reuse it at any time)

◐ The user wants a general greeting for someone named "ElePHPant".

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

● Hello, ElePHPant! 🐘

❯ Please, greet someone named "ElePHPant" like it's 22:30

◐ 22:30 is evening time, so I'll use the evening greeting.

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

● Good evening, ElePHPant! 🌙

The Copilot reflexion and its final answer, like the improvised emoji, might differ from this session example. The important part is that Copilot CLI thinks to use the greet tool, calls it with the right argument, displays the call result, and uses it.