Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion code_samples/mcp/mcp-ibexa-example-wrapper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
set -e

baseUrl='http://localhost' # Adapt to your test case
mcpServer="$baseUrl/mcp/example"

jwtToken=$(curl -s -X 'POST' \
"$baseUrl/api/ibexa/v2/user/token/jwt" \
Expand All @@ -16,6 +17,6 @@ jwtToken=$(curl -s -X 'POST' \
}' | jq -r .JWT.token)

exec npx -y supergateway \
--streamableHttp "$baseUrl/mcp/example" \
--streamableHttp "$mcpServer" \
--oauth2Bearer "$jwtToken" \
--logLevel none
13 changes: 7 additions & 6 deletions code_samples/mcp/mcp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set +x
baseUrl='http://localhost' # Adapt to your test case
username='ibexa-example'
password='Ibexa-3xample'
mcpServer="$baseUrl/mcp/example"

curl -s -X 'POST' \
"$baseUrl/api/ibexa/v2/user/token/jwt" \
Expand All @@ -22,7 +23,7 @@ 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" \
curl -s -i -X 'POST' "$mcpServer" \
-H "Authorization: Bearer $jwtToken" \
-d '{
"jsonrpc": "2.0",
Expand All @@ -43,15 +44,15 @@ tail -n 1 response.tmp.txt | jq
mcpSessionId=$(cat response.tmp.txt | grep -i 'Mcp-Session-Id:' | sed 's/Mcp-Session-Id: \([0-9a-f-]*\).*/\1/i')
rm response.tmp.txt

curl -s -i -X 'POST' "$baseUrl/mcp/example" \
curl -s -i -X 'POST' "$mcpServer" \
-H "Authorization: Bearer $jwtToken" \
-H "Mcp-Session-Id: $mcpSessionId" \
-d '{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}'

curl -s -X 'POST' "$baseUrl/mcp/example" \
curl -s -X 'POST' "$mcpServer" \
-H "Authorization: Bearer $jwtToken" \
-H "Mcp-Session-Id: $mcpSessionId" \
-d '{
Expand All @@ -60,7 +61,7 @@ curl -s -X 'POST' "$baseUrl/mcp/example" \
"method": "tools/list"
}' | jq

curl -s -X 'POST' "$baseUrl/mcp/example" \
curl -s -X 'POST' "$mcpServer" \
-H "Authorization: Bearer $jwtToken" \
-H "Mcp-Session-Id: $mcpSessionId" \
-d '{
Expand All @@ -75,7 +76,7 @@ curl -s -X 'POST' "$baseUrl/mcp/example" \
}
}' | jq

curl -s -X 'POST' "$baseUrl/mcp/example" \
curl -s -X 'POST' "$mcpServer" \
-H "Authorization: Bearer $jwtToken" \
-H "Mcp-Session-Id: $mcpSessionId" \
-d '{
Expand All @@ -84,7 +85,7 @@ curl -s -X 'POST' "$baseUrl/mcp/example" \
"method": "prompts/list"
}' | jq

curl -s -X 'POST' "$baseUrl/mcp/example" \
curl -s -X 'POST' "$mcpServer" \
-H "Authorization: Bearer $jwtToken" \
-H "Mcp-Session-Id: $mcpSessionId" \
-d '{
Expand Down
25 changes: 21 additions & 4 deletions docs/ai/mcp/mcp_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,20 @@ You define MCP servers within a repository configuration and then assign those s
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 31, 35) =]]
```

Servers are automatically registered as services with an ID following the pattern `ibexa.mcp.server.<repository_identifier>.<server_identifier>`.
You can list all defined servers by running the following command:

```bash
php bin/console debug:container ibexa.mcp.server
```

Routes are built automatically from MCP server `path` configs.
Those routes are identified as `ibexa.mcp.<server_identifier>`.
You can list them by running the following command:

`php bin/console debug:router --siteaccess=<within_scope_siteaccess> ibexa.mcp`
```bash
php bin/console debug:router --siteaccess=<within_scope_siteaccess> ibexa.mcp`
```

### MCP server options

Expand All @@ -82,7 +91,7 @@ You can list them by running the following command:
| [`tools`](#tool-configuration) | array | No | `[]` | List of tool classes |
| <nobr>[`discovery_cache`](#discovery-cache)</nobr> | string | Yes | | PSR-6 or PSR-16 cache pool service identifier |
| [`session`](#session-storage) | object | Yes | | Session storage configuration |
| [`allowed_hosts`](#allowed-hosts) | array | No | `[`<br><nobr>`'localhost',`</nobr><br>`'127.0.0.1',`<br>`'[::1]'`<br>`]` | Accepted `Host` headers |
| [`allowed_hosts`](#allowed-hosts) | array | No | `[`<br><nobr>`'localhost',`</nobr><br>`'127.0.0.1',`<br>`'[::1]'`<br>`]` | Accepted `Host` headers |

!!! note "New servers are disabled by default"

Expand Down Expand Up @@ -163,6 +172,14 @@ Clear the cache pool after making changes:
php bin/console cache:pool:clear cache.redis.mcp
```

!!! tip

Use `ibexa.cache_pool` as service identifier to have the default [cache service](persistence_cache.md#cache-service).

It can be set to `null` to disable caching to ease development, which isn't recommended for production environment.

See another example of configuration in [Work with MCP servers](mcp_usage.md#configure-mcp-server).

### Session storage

MCP servers store session data in their own way.
Expand Down Expand Up @@ -215,7 +232,7 @@ In this example, only requests from `admin.example.com` domain, `my-ddev-project

``` yaml
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 16, 16) =]]
- 'admin.example.com'
- '127.0.0.1'
- 'www.example.com'
- 'my-ddev-project.ddev.site'
- '127.0.0.1'
```
2 changes: 1 addition & 1 deletion docs/ai/mcp/mcp_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ However, when facing a specific REST or GraphQL API, an agent may misunderstand
MCP servers make the discovery of available capabilities much easier.
They help AI agents translate natural language prompts into concrete actions on the system.

![MCP communication diagram showing AI agent client connecting to MCP Server within Ibexa DXP.](img/mcp-com-diagram.png)
![MCP communication diagram showing AI agent client connecting to MCP server within [[= product_name =]].](img/mcp-com-diagram.png)

An MCP server allows the agent to discover available tools, inspect their parameters, learn how to use them, and select the correct action.

Expand Down
30 changes: 20 additions & 10 deletions docs/ai/mcp/mcp_usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,11 @@ The server:
!!! note "Storage choice recommendations"

Filesystem storage is convenient for the sake of this example and for testing.
For production, it is recommended that you use Redis or Valkey.
For production, it is recommended that you use Redis or Valkey to share cache among the cluster.

`discovery_cache: ~` could have been used for developement so cache pool wouldn't have to be cleared after each change.
But it's not recommended for production.
By using the filesystem storage, this example can illustrate that cache pool needs to be cleared to have the latest capabilities as when deploying into production.

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

Expand All @@ -101,6 +105,12 @@ In a new `config/packages/mcp.yaml` file, define a new MCP server for the `defau

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.

The server is automatically registered as a service with the ID `ibexa.mcp.server.default.example`:

```bash
php bin/console debug:container ibexa.mcp.server.default.example
```

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

```bash
Expand Down Expand Up @@ -163,16 +173,16 @@ To test the `example` MCP server, a sequence of `curl` commands is used to simul

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

First, use the shell script to set the [[= product_name =]]'s base URL and user credentials as variables for easier reuse:
First, use the shell script to set the [[= product_name =]]'s base URL, user credentials, and MCP server URL as variables for easier reuse:

``` bash
[[= include_code('code_samples/mcp/mcp.sh', 5, 7) =]]
[[= include_code('code_samples/mcp/mcp.sh', 5, 8) =]]
```

Before you can communicate with the MCP server, you must first request a JWT token through the REST API:

``` bash
[[= include_code('code_samples/mcp/mcp.sh', 9, 23) =]]
[[= include_code('code_samples/mcp/mcp.sh', 10, 24) =]]
```

``` json
Expand All @@ -182,7 +192,7 @@ Before you can communicate with the MCP server, you must first request a JWT tok
Then, perform [initialization](https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle#initialization) to get an MCP session ID:

``` bash
[[= include_code('code_samples/mcp/mcp.sh', 21, 44) =]]
[[= include_code('code_samples/mcp/mcp.sh', 22, 45) =]]
```

``` http
Expand All @@ -196,7 +206,7 @@ Then, perform [initialization](https://modelcontextprotocol.io/specification/202
Validate the initialization:

``` bash
[[= include_code('code_samples/mcp/mcp.sh', 46, 52) =]]
[[= include_code('code_samples/mcp/mcp.sh', 47, 53) =]]
```

``` http
Expand All @@ -206,7 +216,7 @@ Validate the initialization:
Get the [list of tools](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#listing-tools):

``` bash
[[= include_code('code_samples/mcp/mcp.sh', 54, 61) =]]
[[= include_code('code_samples/mcp/mcp.sh', 55, 62) =]]
```

``` json
Expand All @@ -216,7 +226,7 @@ Get the [list of tools](https://modelcontextprotocol.io/specification/2025-11-25
[Call](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#calling-tools) the `greet` tool:

``` bash
[[= include_code('code_samples/mcp/mcp.sh', 63, 76) =]]
[[= include_code('code_samples/mcp/mcp.sh', 64, 77) =]]
```

``` json
Expand All @@ -226,7 +236,7 @@ Get the [list of tools](https://modelcontextprotocol.io/specification/2025-11-25
Get the [list of prompts](https://modelcontextprotocol.io/specification/2025-11-25/server/prompts#listing-prompts):

``` bash
[[= include_code('code_samples/mcp/mcp.sh', 78, 85) =]]
[[= include_code('code_samples/mcp/mcp.sh', 79, 86) =]]
```

``` json
Expand All @@ -236,7 +246,7 @@ Get the [list of prompts](https://modelcontextprotocol.io/specification/2025-11-
[Get the prompt](https://modelcontextprotocol.io/specification/2025-11-25/server/prompts#getting-a-prompt) of the `greet` method:

``` bash
[[= include_code('code_samples/mcp/mcp.sh', 87, 100) =]]
[[= include_code('code_samples/mcp/mcp.sh', 88, 101) =]]
```

``` json
Expand Down
38 changes: 38 additions & 0 deletions docs/getting_started/install_with_ddev.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,44 @@
ddev config --web-environment-add MAILER_DSN=smtp://localhost:1025
```

#### Configure scheduled tasks (optional)

You can [schedule tasks](install_ibexa_dxp.md#schedule-tasks) using [DDEV Cron add-on](https://addons.ddev.com/addons/ddev/ddev-cron).

Check notice on line 102 in docs/getting_started/install_with_ddev.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/getting_started/install_with_ddev.md#L102

[Ibexa.ByUsing] Prefer 'by using' or 'with' to plain 'using'.
Raw output
{"message": "[Ibexa.ByUsing] Prefer 'by using' or 'with' to plain 'using'.", "location": {"path": "docs/getting_started/install_with_ddev.md", "range": {"start": {"line": 102, "column": 1}}}, "severity": "INFO"}

```bash
ddev add-on get ddev/ddev-cron
basedir='/var/www/html'
echo "* * * * * cd $basedir && php bin/console ibexa:cron:run --quiet --env=prod" >> .ddev/web-build/ibexa.cron
ddev restart
```

For more schedulable tasks and ways to schedule them, see [Additional scheduled tasks and advanced usage](install_ibexa_dxp.md#additional-scheduled-tasks-and-advanced-usage).

You can run the following command to check Cron:

```bash
ddev exec crontab -l
```

#### Configure background tasks (optional)

You can launch [Ibexa Messenger](background_tasks.md) on DDEV project start.

Check failure on line 121 in docs/getting_started/install_with_ddev.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/getting_started/install_with_ddev.md#L121

[Ibexa.VariablesGlobal] Use global variable '[[= product_name_base =]]' instead of 'Ibexa'
Raw output
{"message": "[Ibexa.VariablesGlobal] Use global variable '[[= product_name_base =]]' instead of 'Ibexa'", "location": {"path": "docs/getting_started/install_with_ddev.md", "range": {"start": {"line": 121, "column": 17}}}, "severity": "ERROR"}
Create or edit a DDEV config file, for example `.ddev/config.hooks.yaml`, and add the following [hook](https://docs.ddev.com/en/stable/users/configuration/hooks/):

```yaml
hooks:
post-start:
- exec-host: ddev exec "php bin/console messenger:consume ibexa.messenger.transport --bus=ibexa.messenger.bus --siteaccess=admin --silent" &
```

You can change the verbosity and redirect the output if you want to log into a file:

```yaml
hooks:
post-start:
- exec-host: ddev exec "php bin/console messenger:consume ibexa.messenger.transport --bus=ibexa.messenger.bus --siteaccess=admin -vv > var/log/messenger.log 2>&1" &
```

#### Enable Mutagen (optional)

If you're using macOS or Windows, you might want to enable [Mutagen](https://docs.ddev.com/en/stable/users/install/performance/#mutagen) to improve performance.
Expand Down
Loading