diff --git a/code_samples/mcp/config/packages/mcp.dil.yaml b/code_samples/mcp/config/packages/mcp.dil.yaml new file mode 100644 index 0000000000..549f247161 --- /dev/null +++ b/code_samples/mcp/config/packages/mcp.dil.yaml @@ -0,0 +1,32 @@ +when@dev: + ibexa: + repositories: + default: + mcp: + data_intelligence_layer: + allowed_hosts: + - 'my-ddev-project.ddev.site' + - '127.0.0.1' +when@prod: + ibexa: + repositories: + default: + mcp: + data_intelligence_layer: + allowed_hosts: + - 'admin.example.com' +ibexa: + repositories: + default: + mcp: + data_intelligence_layer: + discovery_cache: ibexa.cache_pool + session: + type: psr16 + service: ibexa.cache_pool + prefix: dil_mcp_session_ + system: + admin_group: + mcp: + servers: + - data_intelligence_layer diff --git a/code_samples/mcp/mcp-data-intelligence-wrapper.sh b/code_samples/mcp/mcp-data-intelligence-wrapper.sh new file mode 100644 index 0000000000..b51a2ff3ff --- /dev/null +++ b/code_samples/mcp/mcp-data-intelligence-wrapper.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -e + +export NODE_TLS_REJECT_UNAUTHORIZED=0 + +baseUrl='http://localhost' # Adapt to your test case +mcpServer="$baseUrl/admin/mcp/data-intelligence" + +jwtToken=$(curl -s -X 'POST' \ + "$baseUrl/api/ibexa/v2/user/token/jwt" \ + -H "X-Siteaccess: admin" \ + -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": "admin", + "password": "publish" + } + }' | jq -r .JWT.token) + +exec npx -y supergateway \ + --streamableHttp "$mcpServer" \ + --oauth2Bearer "$jwtToken" \ + --header 'Accept-Language: en' \ + --logLevel none diff --git a/composer.json b/composer.json index 0d241634af..dca65d09fb 100644 --- a/composer.json +++ b/composer.json @@ -10,10 +10,15 @@ } }, "repositories": [ + { + "type": "vcs", + "url": "https://github.com/ibexa/data-intelligence-layer" + }, { "type": "composer", "url": "https://updates.ibexa.co" } + ], "require": { "php": "^8.3" @@ -103,7 +108,8 @@ "ibexa/fastly": "~6.0.x-dev", "ibexa/connect": "~6.0.x-dev", "ibexa/connector-qualifio": "~6.0.x-dev", - "ibexa/design-system-twig": "~6.0.x-dev" + "ibexa/design-system-twig": "~6.0.x-dev", + "ibexa/data-intelligence-layer": "~6.0.x-dev" }, "scripts": { "fix-cs": [ diff --git a/docs/ai/mcp/mcp_config.md b/docs/ai/mcp/mcp_config.md index 8eb974bad3..90600e3c15 100644 --- a/docs/ai/mcp/mcp_config.md +++ b/docs/ai/mcp/mcp_config.md @@ -79,6 +79,13 @@ You can list them by running the following command: php bin/console debug:router --siteaccess= ibexa.mcp` ``` +### Built-in MCP servers + +Some MCP servers are already defined on default installation. + +- `data_intelligence_layer` (`/mcp/data-intelligence`) - The [Data Intelligence Layer](data_intelligence_layer.md) MCP server is bundled with tool sets from `Ibexa\Bundle\DataIntelligenceLayer\Mcp\` namespace and expose metrics about content to AI agent TODO: like the Orchestration chat one. + It's enabled by default but not assigned to any SiteAccess and not allowing other hosts than local. For more information, see [DIL MCP server configuration](dil_config.md#mcp-server-configuration). + ### MCP server options | Option | Type | Required | Default | Description | @@ -137,6 +144,15 @@ MCP Servers LTS Update comes with the following **experimental** built-in tools: - `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). Useful for identifying content that needs SEO attention. +- `Ibexa\Bundle\DataIntelligenceLayer\Mcp\MetricDiscoveryTools` + - `dil_discover_metrics` - TODO +- `Ibexa\Bundle\DataIntelligenceLayer\Mcp\MetricReaderTools` + - `dil_get_metric` - TODO + - `dil_list_entity_metrics` - TODO + - `dil_query_metrics` - TODO + - `dil_count_metrics` - TODO + - `dil_get_translation_backlog` - TODO + - `dil_get_translation_subtree_coverage` - TODO ``` yaml hl_lines="5-7" [[= include_code('code_samples/mcp/mcp.matrix.yaml', 4, 7) =]] diff --git a/docs/ai/mcp/mcp_guide.md b/docs/ai/mcp/mcp_guide.md index 4aa1977b43..8a63cd2094 100644 --- a/docs/ai/mcp/mcp_guide.md +++ b/docs/ai/mcp/mcp_guide.md @@ -40,3 +40,9 @@ With the MCP Servers feature, you can: MCP servers are defined specifically for each [repository](repository_configuration.md) and assigned to individual [SiteAccesses](siteaccess.md) scopes. This way you can build flexible configurations that match different contexts. + +## Built-in MCP servers + +### DIL MCP server + +The [Data Intelligence Layer](data_intelligence_layer.md) MCP server is an already set up server that expose tools to access metrics about content so an AI agent can work on content quality or translation coverage. diff --git a/docs/ai/mcp/mcp_usage.md b/docs/ai/mcp/mcp_usage.md index 9741037921..2177915296 100644 --- a/docs/ai/mcp/mcp_usage.md +++ b/docs/ai/mcp/mcp_usage.md @@ -93,8 +93,8 @@ The server: Filesystem storage is convenient for the sake of this example and for testing. For production, it is recommended that you use Redis or Valkey to share cache among the cluster and improve performance. - For development, you can set `discovery_cache: ~` to avoid clearing the cache after each change. - This example uses the filesystem storage to illustrate that you have to clear the cache pool to refresh the available capabilities, exactly as when deploying into production. + For development, you can set `discovery_cache: ~` to avoid clearing the cache after each change. + This example uses the filesystem storage to illustrate that you have to clear the cache pool to refresh the available capabilities, exactly 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: diff --git a/docs/content_management/data_intelligence_layer/data_intelligence_layer.md b/docs/content_management/data_intelligence_layer/data_intelligence_layer.md new file mode 100644 index 0000000000..c5bd02a6e9 --- /dev/null +++ b/docs/content_management/data_intelligence_layer/data_intelligence_layer.md @@ -0,0 +1,25 @@ +--- +description: TODO. +month_change: true +--- + +# Data Intelligence Layer + +The Data Intelligence Layer (DIL) provides content metrics. + +[[= cards([ +"content_management/data_intelligence_layer/dil_guide", +"content_management/data_intelligence_layer/dil_config", +]) =]] + +TODO: + +- Product guide +- Install & Config +- User doc? +- API: + - PHP API + - REST API + - GraphQL API + - MCP tools +- Extend diff --git a/docs/content_management/data_intelligence_layer/dil_config.md b/docs/content_management/data_intelligence_layer/dil_config.md new file mode 100644 index 0000000000..d8ba0af659 --- /dev/null +++ b/docs/content_management/data_intelligence_layer/dil_config.md @@ -0,0 +1,49 @@ +--- +description: TODO. +month_change: true +--- + +# Data Intelligence Layer configuration + +## Install + +TODO: How is it packaged? Is it installed within regular edition? + +```bash +composer require ibexa/data-intelligence-layer +php bin/console doctrine:query:sql "$(php bin/console ibexa:doctrine:schema:dump-sql vendor/ibexa/data-intelligence-layer/src/bundle/Resources/config/schema.yaml)" +php bin/console doctrine:query:sql "$(php bin/console ibexa:doctrine:schema:dump-sql vendor/ibexa/data-intelligence-layer/src/bundle/Resources/config/schema.translation.yaml)" +php bin/console ibexa:dil:translation:backfill-baseline +``` + +## Background tasks + +Metric data are computed as a [background task using Ibexa Messenger](background_tasks.md), so, make sure it's running. + +You can set the interval between computations of metrics with the following parameters: + +- `ibexa.data_intelligence_layer.freshness.default_interval_days`: Number of days between computation of the metrics - default is 90 days +- `ibexa.data_intelligence_layer.freshness.content_type_intervals`: Custom number of days per content type - default is empty + +```yaml +parameters: + ibexa.data_intelligence_layer.freshness.default_interval_days: 30 # Increase frequency to every 30 days for all content types + ibexa.data_intelligence_layer.freshness.content_type_intervals: # Map content type identifier to custom interval in days + article: 15 # Compute metrics for articles every 15 days +``` + +## MCP server configuration + +The MCP server `data_intelligence_layer` is already configured for the path `/mcp/data-intelligence` and is enabled by default. +It needs + +- to be associated to some SiteAccesses +- to be allowed to be accessed from other hosts than `localhost`, `127.0.0.1`, and `[::1]` +- to have discovery cache enabled in production (by default, it's disabled for development) +- to have another session storage than the default `public/var/` directory + +For example, assign it to the `admin_group`, allow your production admin domain and development domains, use the default cache pool for discovery cache and sessions: + +```yaml +[[= include_code('code_samples/mcp/config/packages/mcp.dil.yaml') =]] +``` diff --git a/docs/content_management/data_intelligence_layer/dil_guide.md b/docs/content_management/data_intelligence_layer/dil_guide.md new file mode 100644 index 0000000000..87bbb0349b --- /dev/null +++ b/docs/content_management/data_intelligence_layer/dil_guide.md @@ -0,0 +1,11 @@ +--- +description: TODO. +month_change: true +--- + +# Data Intelligence Layer product guide + +The Data Intelligence Layer (DIL) systematically collects, aggregates, and exposes editorial and content-centric metrics. +Editors and AI agents can use those metrics to identify possible content improvements. + +TODO: List available metrics and their purpose. diff --git a/docs/content_management/data_intelligence_layer/dil_mcp.md b/docs/content_management/data_intelligence_layer/dil_mcp.md new file mode 100644 index 0000000000..7b26549b58 --- /dev/null +++ b/docs/content_management/data_intelligence_layer/dil_mcp.md @@ -0,0 +1,40 @@ +--- +description: TODO. +month_change: true +--- + +# DIL MCP server + +The Data Intelligence Layer come with a built-in MCP server helpin AI agents to fetch metrics and use them to provide content improvement suggestions or take actions. + +## MCP server configuration + +You can check the service existence with the following command: + +```bash +php bin/console debug:container ibexa.mcp.server.default.data_intelligence_layer +``` + +You can check the route existence with the following command: + +```bash +php bin/console debug:router ibexa.mcp.data_intelligence_layer +``` + +This MCP server need to be associated to some SiteAccesses and allowed to be accessed from some hosts. +For details, see [Data Intelligence Layer configuration](dil_config.md#mcp-server-configuration). + +## MCP server test + +You can use the MCP server from an agent CLI command. +Like in the [Work with MCP servers example](mcp_usage.md#fully-scripted-variant), you can use a wrapper script to ease JWT token acquisition. + +```bash hl_lines="7 11 25" +[[= include_code('code_samples/mcp/mcp-data-intelligence-wrapper.sh') =]] +``` + +Notice: + +- the `admin` in MCP server URL as default [`URIElement: 1` SiteAccess matching](siteaccess_matching.md#urielement) is used in this local test example +- the `X-Siteaccess: admin` header when requiring the JWT token TODO: mention this need in mcp_usage.md example instead. +- the `Accept-Language: en` header when establishing the gateway TODO: Why is it suddenly needed? Because it's the admin SiteAccess? diff --git a/docs/product_guides/product_guides.md b/docs/product_guides/product_guides.md index c6a70510e4..f34a33eaff 100644 --- a/docs/product_guides/product_guides.md +++ b/docs/product_guides/product_guides.md @@ -17,6 +17,7 @@ Discover the primary ones with the help of product guides. Condensed content all "content_management/pages/page_builder_guide", "content_management/forms/form_builder_guide", "content_management/collaborative_editing/collaborative_editing_guide", + "content_management/data_intelligence_layer/dil_guide", "customer_management/customer_portal", "product_catalog/product_catalog_guide", "product_catalog/quable/quable_guide", diff --git a/docs/templating/twig_function_reference/content_twig_functions.md b/docs/templating/twig_function_reference/content_twig_functions.md index c1e9b125be..079bc75dc8 100644 --- a/docs/templating/twig_function_reference/content_twig_functions.md +++ b/docs/templating/twig_function_reference/content_twig_functions.md @@ -81,12 +81,12 @@ If the content item doesn't have a translation in the prioritized or passed lang ``` html+twig {{ ibexa_content_name(product) }} -{{ ibexa_content_name(product, 'fr-FR') }} +{{ ibexa_content_name(product, 'fre-FR') }} ``` ### `ibexa_seo_is_empty()` -`ibexa_seo_is_empty()` returns a Boolean value which indicates whether [SEO]([[= user_docĀ =]]/search_engine_optimization/seo/) data is available for the content item that is passed as an argument. +`ibexa_seo_is_empty()` returns a Boolean value which indicates whether [SEO]([[= user_doc =]]/search_engine_optimization/seo/) data is available for the content item that is passed as an argument. | Argument | Type | Description | |---------------|------|-------------| @@ -102,7 +102,7 @@ If the content item doesn't have a translation in the prioritized or passed lang ### `ibexa_seo()` -`ibexa_seo()` attaches [SEO]([[= user_docĀ =]]/search_engine_optimization/seo/) data to the content item's HTML code. +`ibexa_seo()` attaches [SEO]([[= user_doc =]]/search_engine_optimization/seo/) data to the content item's HTML code. | Argument | Type | Description | |---------------|------|-------------| diff --git a/docs/users/oauth_server.md b/docs/users/oauth_server.md index d61f152fc3..e36f5fe18e 100644 --- a/docs/users/oauth_server.md +++ b/docs/users/oauth_server.md @@ -20,17 +20,9 @@ composer require ibexa/oauth2-server --with-all-dependencies Add the tables needed by the bundle: -=== "MySQL" - - ```bash - php bin/console ibexa:doctrine:schema:dump-sql vendor/ibexa/oauth2-server/src/bundle/Resources/config/schema.yaml | mysql -u -p - ``` - -=== "PostgreSQL" - - ```bash - php bin/console ibexa:doctrine:schema:dump-sql --force-platform=postgres vendor/ibexa/oauth2-server/src/bundle/Resources/config/schema.yaml | psql - ``` +```bash +php bin/console doctrine:query:sql "$(php bin/console ibexa:doctrine:schema:dump-sql vendor/ibexa/oauth2-server/src/bundle/Resources/config/schema.yaml)" +``` Then, in `config/bundles.php`, at the end of an array with a list of bundles, add the following two lines : diff --git a/mkdocs.yml b/mkdocs.yml index 608ef135ea..7d75f5f8cc 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -294,6 +294,11 @@ nav: - Time field type: content_management/field_types/field_type_reference/timefield.md - URL field type: content_management/field_types/field_type_reference/urlfield.md - User field type: content_management/field_types/field_type_reference/userfield.md + - Data Intelligence Layer: + - Data Intelligence Layer: content_management/data_intelligence_layer/data_intelligence_layer.md + - DIL product guide: content_management/data_intelligence_layer/dil_guide.md + - DIL configuration: content_management/data_intelligence_layer/dil_config.md + - DIL MCP server: content_management/data_intelligence_layer/dil_mcp.md - Collaborative editing: - Collaborative editing: content_management/collaborative_editing/collaborative_editing.md - Collaborative editing product guide: content_management/collaborative_editing/collaborative_editing_guide.md