Skip to content
Open
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
4 changes: 3 additions & 1 deletion code_samples/mcp/config/packages/mcp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ ibexa:
discovery_cache: cache.tagaware.filesystem
session:
type: psr16
directory: cache.tagaware.filesystem
service: cache.tagaware.filesystem
allowed_hosts:
- '127.0.0.1'
system:
default:
mcp:
Expand Down
2 changes: 2 additions & 0 deletions code_samples/mcp/mcp.matrix.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ ibexa:
session:
type: <psr16|file>
# Session options…
allowed_hosts:
- '<domain_name>'
mcp_psr16:
discovery_cache: cache.redis.mcp
session:
Expand Down
2 changes: 1 addition & 1 deletion code_samples/mcp/mcp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ curl -s -i -X 'POST' "$baseUrl/mcp/example" \

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/')
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" \
Expand Down
2 changes: 2 additions & 0 deletions code_samples/mcp/src/Mcp/ExampleCapabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#[McpTool(
servers: ['example'],
name: 'greet',
title: 'User greeting',
description: 'Greet a user by name',
annotations: new ToolAnnotations(
readOnlyHint: true,
Expand Down Expand Up @@ -73,6 +74,7 @@ public function greetByName(string $name): array
#[McpPrompt(
servers: ['example'],
name: 'greet',
title: 'Be greeted',
description: 'Prompt to invoke the `greet` tool',
icons: [new Icon(
src: 'https://openmoji.org/data/color/svg/1F91D.svg',
Expand Down
83 changes: 62 additions & 21 deletions docs/ai/mcp/mcp_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@

``` yaml
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 1, 8) =]]
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 12, 15) =]]
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 29, 33) =]]
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 12, 17) =]]
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 31, 35) =]]
```

Routes are built automatically from MCP server `path` configs.
Expand All @@ -72,16 +72,17 @@

### MCP server options

| Option | Type | Required | Default | Description |
|-----------------------------------------------------------------------------------------------------------------|---------|----------|---------|------------------------------------------------------------------|
| `path` | string | Yes | | MCP server endpoint path (appended to SiteAccess-aware base URL) |
| `enabled` | boolean | No | `false` | Server state: decides whether it is enabled or disabled |
| `version` | string | No | `1.0.0` | MCP server version |
| [`description`](https://modelcontextprotocol.io/specification/2025-11-25/schema#implementation-description) | string | No | `null` | Server implementation description |
| [`instructions`](https://modelcontextprotocol.io/specification/2025-11-25/schema#initializeresult-instructions) | string | No | `null` | Prompt-like instructions provided to the AI agent |
| [`tools`](#tool-configuration) | string | 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 |
| Option | Type | Required | Default | Description |
|-----------------------------------------------------------------------------------------------------------------|---------|----------|--------------------------------------------------------------------------|------------------------------------------------------------------|
| `path` | string | Yes | | MCP server endpoint path (appended to SiteAccess-aware base URL) |
| `enabled` | boolean | No | `false` | Server state: decides whether it is enabled or disabled |

Check notice on line 78 in docs/ai/mcp/mcp_config.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/ai/mcp/mcp_config.md#L78

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/ai/mcp/mcp_config.md", "range": {"start": {"line": 78, "column": 246}}}, "severity": "INFO"}
| `version` | string | No | `1.0.0` | MCP server version |
| [`description`](https://modelcontextprotocol.io/specification/2025-11-25/schema#implementation-description) | string | No | `null` | Server implementation description |
| [`instructions`](https://modelcontextprotocol.io/specification/2025-11-25/schema#initializeresult-instructions) | string | No | `null` | Prompt-like instructions provided to the AI agent |
| [`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 |

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

Expand All @@ -106,20 +107,43 @@

#### Built-in tools

MCP Servers LTS Update comes with the following built-in tools:

MCP Servers LTS Update comes with the following **experimental** built-in tools:

- `Ibexa\Mcp\Tool\ContentType\ContentTypeTools`
- `get_content_type` - gets a content type by its ID.
- `get_content_type_by_identifier` - gets a content type by its identifier.
- `get_content_type_list` - gets content types by their IDs.
- `create_content_type` - creates a content type draft.
- `get_content_type_draft` - gets a content type draft by content type ID.
- `publish_content_type_draft` - publishes a content type draft by content type ID.
- `Ibexa\Mcp\Tool\ContentType\FieldDefinitionTools`
- `add_field_definition` - adds a field definition to a content type draft.
- `update_field_definition` - updates a field definition in a content type draft.
- `remove_field_definition` - removes a field definition from a content type draft.
- `Ibexa\Mcp\Tool\ContentType\ContentTypeGroupTools`
- `get_content_type_groups` - gets all content type groups.
- `Ibexa\Mcp\Tool\TranslationTools`
- `list_languages` - lists all languages in the current SiteAccess
- `list_content_translations` - lists languages in which given content item has translations
- `list_languages` - lists all languages in the current SiteAccess.
- `list_content_languages` - lists languages which have translations for a given content item.
- `list_non_translated_content_ids` - lists IDs of content which have missing translations for a given language code.
- `Ibexa\Mcp\Tool\SeoTools`
- `get_non_seo_content_ids` - returns IDs of content items that are missing SEO optimization (no meta title tag)
- `get_non_seo_content_ids` - returns IDs of content items that are missing SEO optimization (no meta title tag). Useful for identifying content that needs SEO attention.

Check warning on line 130 in docs/ai/mcp/mcp_config.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/ai/mcp/mcp_config.md#L130

[Ibexa.VerySimply] Avoid using 'Useful'.
Raw output
{"message": "[Ibexa.VerySimply] Avoid using 'Useful'.", "location": {"path": "docs/ai/mcp/mcp_config.md", "range": {"start": {"line": 130, "column": 119}}}, "severity": "WARNING"}

``` yaml hl_lines="5-7"
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 4, 7) =]]
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 9, 11) =]]
# …
```

!!! caution "Experimental tools"

The built-in tools are experimental and may change in future releases.
They are provided as examples of how to implement tools and how to configure them in an MCP server.
As-is, they may not cover all your needs or may not be practical to all AI agents.
If you use them, be prepared to update your MCP server configuration and tool usage when upgrading to a new version of [[= product_name =]].

See how to build your own tools in [Work with MCP servers](mcp_usage.md).

### Discovery cache

Discovery is cached to avoid scanning for capabilities on every request.
Expand All @@ -128,7 +152,7 @@
For example, you could set up a dedicated Redis/Valkey:

``` yaml
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 17, 17) =]]
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 19, 19) =]]
```

For a production cluster, it's recommended to use a Redis/Valkey cache pool so the cache can be shared by all nodes.
Expand Down Expand Up @@ -163,8 +187,8 @@
Such setup is suitable for production environments.

``` yaml
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 18, 21) =]]
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 34, 43) =]]
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 20, 23) =]]
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 36, 45) =]]
```

#### File
Expand All @@ -176,5 +200,22 @@
In this example, sessions are stored in the `var/cache/<environment>/mcp/sessions/` directory (for example, `var/cache/dev/mcp/session/` for the `dev` environment, and `var/cache/prod/mcp/sessions/` for the `prod` environment):

``` yaml
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 23, 25) =]]
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 25, 27) =]]
```

### Allowed hosts

This parameter lists the domains, the `Host` headers, accepted by the MCP server.
The port is not part of the matching.
There is no joker, all cases must be listed.

Check notice on line 210 in docs/ai/mcp/mcp_config.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/ai/mcp/mcp_config.md#L210

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/ai/mcp/mcp_config.md", "range": {"start": {"line": 210, "column": 35}}}, "severity": "INFO"}
Comment thread
adriendupuis marked this conversation as resolved.
As item, you can use a hostname, an IP, or an IPv6.
IPv6 addresses must be bracketed, for example `[::1]`.

Check notice on line 212 in docs/ai/mcp/mcp_config.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/ai/mcp/mcp_config.md#L212

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/ai/mcp/mcp_config.md", "range": {"start": {"line": 212, "column": 21}}}, "severity": "INFO"}

In this example, only requests from `admin.example.com` domain, `my-ddev-project.ddev.site` domain, or from 127.0.0.1 IP are accepted:

Check notice on line 214 in docs/ai/mcp/mcp_config.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/ai/mcp/mcp_config.md#L214

[Ibexa.VersionNumber] Always start product version number with 'v'
Raw output
{"message": "[Ibexa.VersionNumber] Always start product version number with 'v'", "location": {"path": "docs/ai/mcp/mcp_config.md", "range": {"start": {"line": 214, "column": 109}}}, "severity": "INFO"}

Check notice on line 214 in docs/ai/mcp/mcp_config.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/ai/mcp/mcp_config.md#L214

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/ai/mcp/mcp_config.md", "range": {"start": {"line": 214, "column": 122}}}, "severity": "INFO"}

``` yaml
[[= include_code('code_samples/mcp/mcp.matrix.yaml', 16, 16) =]]
- 'admin.example.com'
- '127.0.0.1'
- 'my-ddev-project.ddev.site'
```
10 changes: 7 additions & 3 deletions docs/ai/mcp/mcp_usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@

- `servers` - array of server identifiers the tool is assigned to
<br>For more information, see [tools configuration](mcp_config.md#tool-configuration).
- `name` - tool name (if not set, function name is used)
- `name` - tool codename - if not set, function name is used

Check notice on line 28 in docs/ai/mcp/mcp_usage.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/ai/mcp/mcp_usage.md#L28

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/ai/mcp/mcp_usage.md", "range": {"start": {"line": 28, "column": 54}}}, "severity": "INFO"}
- `title` - tool title for user interfaces - if not set, the `name` is used

Check notice on line 29 in docs/ai/mcp/mcp_usage.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/ai/mcp/mcp_usage.md#L29

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/ai/mcp/mcp_usage.md", "range": {"start": {"line": 29, "column": 69}}}, "severity": "INFO"}
- `description` - tool description, used by AI agents to understand the tool's purpose
- `icons` - array of [`Mcp\Schema\Icon`](https://github.com/modelcontextprotocol/php-sdk/blob/main/src/Schema/Icon.php) instances
<br>For more information, see the [`icons` specification](https://modelcontextprotocol.io/specification/latest/basic/index#icons).
Expand All @@ -52,7 +53,8 @@
It accepts several arguments that describe how the prompt is used:

- `servers` - array of server identifiers exposing this prompt - required for prompts
- `name` (optional) - prompt name - if not set, method name is used
- `name` (optional) - prompt codename - if not set, method name is used

Check notice on line 56 in docs/ai/mcp/mcp_usage.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/ai/mcp/mcp_usage.md#L56

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/ai/mcp/mcp_usage.md", "range": {"start": {"line": 56, "column": 65}}}, "severity": "INFO"}
- `title` (optional) - prompt title - if not set, `name` is used

Check notice on line 57 in docs/ai/mcp/mcp_usage.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/ai/mcp/mcp_usage.md#L57

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/ai/mcp/mcp_usage.md", "range": {"start": {"line": 57, "column": 58}}}, "severity": "INFO"}
- `description` (optional) - human-readable prompt description
- `icons` (optional) - array of [`Mcp\Schema\Icon`](https://github.com/modelcontextprotocol/php-sdk/blob/main/src/Schema/Icon.php) instances
<br>For more information, see the [`icons` specification](https://modelcontextprotocol.io/specification/latest/basic/index#icons).
Expand Down Expand Up @@ -97,6 +99,8 @@
[[= include_code('code_samples/mcp/config/packages/mcp.yaml') =]]
```

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.

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

```bash
Expand Down Expand Up @@ -242,7 +246,7 @@
### Perform MCP Inspector test

You can test your server with the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector).
You can even use the inspector as a DDEV add-on with [`craftpulse/ddev-mcp-inspector`](https://github.com/craftpulse/ddev-mcp-inspector).
You can even use the inspector as a DDEV add-on with [`michtio/ddev-mcp-inspector`](https://github.com/michtio/ddev-mcp-inspector).
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.

You can use a Web interface to obtain the JWT token:
Expand Down
Loading