diff --git a/code_samples/mcp/config/packages/mcp.yaml b/code_samples/mcp/config/packages/mcp.yaml index f71a60e119..05781ee8f4 100644 --- a/code_samples/mcp/config/packages/mcp.yaml +++ b/code_samples/mcp/config/packages/mcp.yaml @@ -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: diff --git a/code_samples/mcp/mcp.matrix.yaml b/code_samples/mcp/mcp.matrix.yaml index 012d588e07..c5d1371e91 100644 --- a/code_samples/mcp/mcp.matrix.yaml +++ b/code_samples/mcp/mcp.matrix.yaml @@ -13,6 +13,8 @@ ibexa: session: type: # Session options… + allowed_hosts: + - '' mcp_psr16: discovery_cache: cache.redis.mcp session: diff --git a/code_samples/mcp/mcp.sh b/code_samples/mcp/mcp.sh index 27a1592f5e..797c65983a 100644 --- a/code_samples/mcp/mcp.sh +++ b/code_samples/mcp/mcp.sh @@ -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" \ diff --git a/code_samples/mcp/src/Mcp/ExampleCapabilities.php b/code_samples/mcp/src/Mcp/ExampleCapabilities.php index 20568de5c3..682f65807c 100644 --- a/code_samples/mcp/src/Mcp/ExampleCapabilities.php +++ b/code_samples/mcp/src/Mcp/ExampleCapabilities.php @@ -18,6 +18,7 @@ #[McpTool( servers: ['example'], name: 'greet', + title: 'User greeting', description: 'Greet a user by name', annotations: new ToolAnnotations( readOnlyHint: true, @@ -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', diff --git a/docs/ai/mcp/mcp_config.md b/docs/ai/mcp/mcp_config.md index 0ac3841cf7..44ec3eaeeb 100644 --- a/docs/ai/mcp/mcp_config.md +++ b/docs/ai/mcp/mcp_config.md @@ -60,8 +60,8 @@ You define MCP servers within a repository configuration and then assign those s ``` 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. @@ -72,16 +72,17 @@ You can list them by running the following command: ### 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 | -| [`discovery_cache`](#discovery-cache) | 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 | +| `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 | +| [`discovery_cache`](#discovery-cache) | 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 | `[`
`'localhost',`
`'127.0.0.1',`
`'[::1]'`
`]` | Accepted `Host` headers | !!! note "New servers are disabled by default" @@ -108,9 +109,24 @@ There are two ways to associate tools with a server: MCP Servers LTS Update comes with the following built-in tools: +- `Ibexa\Mcp\Tool\ContentType\ContentTypeTools` + - `get_content_type_list` - TODO: gets content types by IDs + - `get_content_type` - gets a content type by ID + - `get_content_type_by_identifier` - gets a content type by identifier + - `get_content_type_draft` - TODO: get an existing content type draft + - `create_content_type` - TODO: get a draft for a new content type + - TODO: `create_content_type_draft` - creates a draft for a given content type + - `publish_content_type_draft` - publishes a content type draft +- `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_content_languages` - lists languages in which given content item has translations + - `list_non_translated_content_ids` - lists IDs of content with 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) @@ -128,7 +144,7 @@ You must provide a PSR-6 or PSR-16 cache pool for this caching. 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. @@ -163,8 +179,8 @@ Optionally, you could use a more specific `prefix` option than the default `mcp_ 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 @@ -176,5 +192,20 @@ Such setup is suitable for development environments. In this example, sessions are stored in the `var/cache//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. + +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: + +``` yaml +[[= include_code('code_samples/mcp/mcp.matrix.yaml', 16, 16) =]] + - 'admin.example.com' + - '127.0.0.1' + - 'my-ddev-project.ddev.site' ``` diff --git a/docs/ai/mcp/mcp_usage.md b/docs/ai/mcp/mcp_usage.md index 954361c22f..a513a50a79 100644 --- a/docs/ai/mcp/mcp_usage.md +++ b/docs/ai/mcp/mcp_usage.md @@ -25,7 +25,8 @@ It accepts the following optional arguments: - `servers` - array of server identifiers the tool is assigned to
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 +- `title` - tool title for user interfaces - if not set, the `name` is used - `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
For more information, see the [`icons` specification](https://modelcontextprotocol.io/specification/latest/basic/index#icons). @@ -52,7 +53,8 @@ Methods that return a prompt are marked with the [`Ibexa\Contracts\Mcp\Attribute 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 +- `title` (optional) - prompt title - if not set, `name` is used - `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
For more information, see the [`icons` specification](https://modelcontextprotocol.io/specification/latest/basic/index#icons). @@ -97,6 +99,8 @@ In a new `config/packages/mcp.yaml` file, define a new MCP server for the `defau [[= 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 @@ -242,7 +246,7 @@ Get the [list of prompts](https://modelcontextprotocol.io/specification/latest/s ### 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: