From e9aaf7dd94d307eb16692e53fc4a2e1fa7ed357e Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 5 Dec 2025 12:19:50 -0800 Subject: [PATCH 1/4] mcp reaction Signed-off-by: Daniel Gerlag --- .../configure-reactions/_index.md | 2 +- .../configure-mcp-reaction/_index.md | 321 ++++++++++++++++++ .../configure-mcp-reaction/grid-image.svg | 6 + 3 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/_index.md create mode 100644 docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/grid-image.svg diff --git a/docs/content/how-to-guides/configure-reactions/_index.md b/docs/content/how-to-guides/configure-reactions/_index.md index a4759cb9..9364daad 100644 --- a/docs/content/how-to-guides/configure-reactions/_index.md +++ b/docs/content/how-to-guides/configure-reactions/_index.md @@ -6,4 +6,4 @@ weight: 60 layout: "reaction_provider_list" description: > Learn how to configure Drasi Reactions ---- \ No newline at end of file +--- diff --git a/docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/_index.md b/docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/_index.md new file mode 100644 index 00000000..dd706e47 --- /dev/null +++ b/docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/_index.md @@ -0,0 +1,321 @@ +--- +type: "docs" +title: "Configure an MCP Reaction" +linkTitle: "Configure an MCP Reaction" +weight: 90 +description: > + Learn how to configure an MCP Reaction +--- + +The MCP Reaction provides a Model Context Protocol (MCP) server that enables MCP clients to connect and subscribe to Drasi queries for real-time updates. MCP clients can use the resource subscription feature to receive notifications when query results change, making it ideal for integrating Drasi with AI agents and other MCP-compatible tools. + +## Requirements + +On the computer from where you will create the Reaction, you need the following software: +- [Drasi CLI](/reference/command-line-interface/) + +## Creating the Reaction + +To create a Reaction, execute the `drasi apply` command as follows: + +```text +drasi apply -f my-reaction.yaml +``` + +The `drasi apply` command is how you create all new Drasi resources (in this case a Reaction). The `-f` flag specifies that the definition of the new Reaction is contained in the referenced YAML file `my-reaction.yaml`. + +## Reaction Definitions + +The YAML file passed to `drasi apply` can contain one or more Reaction definitions. Here is an example of an MCP Reaction definition: + +```yaml +kind: Reaction +apiVersion: v1 +name: mcp-reaction +spec: + kind: MCP + queries: + freezer-alerts: + description: "Freezer temperature alert for when it goes above 32 degrees for more than 10 seconds" + added: + template: | + { + "freezerId": "{{after.id}}", + "temperature": "{{after.temp}}", + "description": "Temperature of freezer {{after.id}} exceeded threshold for more than 10 seconds" + } + updated: + template: | + { + "freezerId": "{{after.id}}", + "previousTemperature": "{{before.temp}}", + "currentTemperature": "{{after.temp}}", + "description": "Temperature of freezer {{after.id}} changed from {{before.temp}} to {{after.temp}}" + } + deleted: + template: | + { + "freezerId": "{{before.id}}", + "temperature": "{{before.temp}}", + "description": "Temperature of freezer {{before.id}} below threshold" + } +``` + +In this definition: +- the **apiVersion** must be **v1**. +- the **kind** property tells Drasi to create a **Reaction** resource. +- the **spec.kind** property tells Drasi the kind of Reaction to create, in this case an **MCP** Reaction. +- the **name** property tells Drasi the identity of the Reaction and must be unique within the scope of Reactions within the target Drasi environment. In the above example, the **name** of the Reaction is **mcp-reaction**. + +This table describes the settings in the **spec** section of the Reaction definition: + +|Property|Description| +|--- |---| +|`queries`| The list of continuous queries you would like the MCP Reaction to expose as resources. Each query becomes an MCP resource that clients can subscribe to.| + +### Per-Query Configuration + +For each query, you can define the following properties: + +|Property|Description| +|--- |---| +|`description`| A human-readable description of the query resource.| +|`added`| Configuration for when items are added to the query result set.| +|`updated`| Configuration for when items in the query result set are modified.| +|`deleted`| Configuration for when items are removed from the query result set.| + +Each change type (`added`, `updated`, `deleted`) supports the following property: + +|Property|Description| +|--- |---| +|`template`| Handlebars template that defines the notification payload format.| + +### Handlebars Templating +The MCP Reaction uses Handlebars templating to format notification payloads. The result of evaluating the Handlebars template is emitted as the resource notification payload to subscribed MCP clients. + +- `{{after}}` - Contains the new/current state of the data +- `{{before}}` - Contains the previous state of the data (available for `updated` and `deleted` operations) + +Example usage: +```yaml +template: | + { + "id": "{{after.userId}}", + "name": "{{after.name}}", + "timestamp": "{{after.updatedAt}}" + } +``` + +## MCP Client Connection + +### Connection Details + +MCP clients can connect to the server using Streamable HTTP transport with Server-Sent Events (SSE): + +``` +HTTP Endpoint: http://:3000/ +Transport: Streamable HTTP (preferred) with SSE fallback +Protocol Version: 2025-03-26 (with 2024-11-05 compatibility) +``` + +### Resource URIs + +Query resources are accessible via URIs in the format: +``` +drasi://query/{queryId} +``` + +Where `{queryId}` is the query name defined in the reaction configuration. + +### Subscription Workflow + +1. Client connects to the MCP server via HTTP +2. Client establishes SSE stream for notifications +3. Client subscribes to specific query resources using `resources/subscribe` +4. When Drasi sends change events for subscribed queries, the server sends `notifications/resources/updated` notifications containing: + - Resource URI (`drasi://query/{queryId}`) + - Operation type (`added`, `updated`, or `deleted`) + - Formatted data based on the Handlebars template + +### Notification Format + +When a subscribed resource changes, MCP clients receive a `notifications/resources/updated` message in the following format: + +For an `added` operation: +```json +{ + "jsonrpc": "2.0", + "method": "notifications/resources/updated", + "params": { + "uri": "drasi://query/freezer-alerts", + "operation": "added", + "data": { + "freezerId": "F-101", + "temperature": "35", + "description": "Temperature of freezer F-101 exceeded threshold for more than 10 seconds" + } + } +} +``` + +For an `updated` operation: +```json +{ + "jsonrpc": "2.0", + "method": "notifications/resources/updated", + "params": { + "uri": "drasi://query/freezer-alerts", + "operation": "updated", + "data": { + "freezerId": "F-101", + "previousTemperature": "35", + "currentTemperature": "38", + "description": "Temperature of freezer F-101 changed from 35 to 38" + } + } +} +``` + +For a `deleted` operation: +```json +{ + "jsonrpc": "2.0", + "method": "notifications/resources/updated", + "params": { + "uri": "drasi://query/freezer-alerts", + "operation": "deleted", + "data": { + "freezerId": "F-101", + "temperature": "30", + "description": "Temperature of freezer F-101 below threshold" + } + } +} +``` + +The `data` field contains the result of evaluating the Handlebars template for the corresponding operation type. + +## Exposing the Endpoint + +### Local dev/test + +For dev/test purposes, you can simply use a `kubectl port-forward` to expose the endpoint via a port on your local machine. + +The following command will open port 3000 on your local machine, which will point to the MCP endpoint: + +``` +kubectl port-forward services/ 3000:3000 -n drasi-system +``` + +Now, you can access the MCP endpoint via `http://localhost:3000`. + +### Production +For production environments, you will need to configure an `Ingress` that points to the `` service on port 3000. This configuration will vary depending on how your cluster is hosted, and there will be specific steps for each cloud provider. + +#### More information +- [Kubernetes Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) +- [AWS: Load Balancer Controller](https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/) +- [AKS: Managed NGINX ingress with the application routing add-on](https://learn.microsoft.com/en-us/azure/aks/app-routing) +- [AKS: Enable application gateway ingress controller add-on](https://learn.microsoft.com/en-ca/azure/application-gateway/tutorial-ingress-controller-add-on-existing) +- [GKE: Configure Ingress for external Application Load Balancers](https://cloud.google.com/kubernetes-engine/docs/how-to/load-balance-ingress) + +## Protocol Support +This implementation follows the MCP specification and supports: +- **Modern Streamable HTTP Transport** (2025-03-26): Primary transport method +- **Legacy SSE Transport** (2024-11-05): Backward compatibility support +- **Session Management**: Proper session ID generation and tracking +- **Resource Management**: Dynamic resource registration and subscription +- **Real-time Notifications**: Standard MCP `notifications/resources/updated` with structured payload +- **Error Handling**: Comprehensive error responses with proper JSON-RPC format + +## Example: AI Agent Integration +Here's an example that exposes inventory alerts to MCP clients: + +```yaml +kind: Reaction +apiVersion: v1 +name: inventory-mcp +spec: + kind: MCP + queries: + low-stock-alerts: + description: "Products with inventory below reorder threshold" + added: + template: | + { + "productId": "{{after.productId}}", + "productName": "{{after.productName}}", + "currentStock": {{after.stock}}, + "reorderLevel": {{after.reorderLevel}}, + "alert": "Product {{after.productName}} is now below reorder level" + } + updated: + template: | + { + "productId": "{{after.productId}}", + "productName": "{{after.productName}}", + "previousStock": {{before.stock}}, + "currentStock": {{after.stock}}, + "reorderLevel": {{after.reorderLevel}}, + "alert": "Stock level changed from {{before.stock}} to {{after.stock}}" + } + deleted: + template: | + { + "productId": "{{before.productId}}", + "productName": "{{before.productName}}", + "alert": "Product {{before.productName}} stock is now above reorder level" + } +``` + +## Inspecting the Reaction +As soon as the Reaction is created it will start running, subscribing to the specified list of Continuous Queries and processing changes to the Continuous Query results. + +You can check the status of the Reaction using the `drasi list` command: + +```text +drasi list reaction +``` + +This will return a simple list of all Reactions in the current namespace and their overall status. For example: + +``` + ID | AVAILABLE +--------------------+------------ + mcp-reaction | true +``` + +If an error has occurred during the creation or operation of a Reaction, the `AVAILABLE` column will contain the error text instead of `true` or `false`. + +For more details about the Reaction you can use the [drasi describe](/reference/command-line-interface#drasi-describe) command: + +```text +drasi describe reaction mcp-reaction +``` + +This will return the full definition used to create the Reaction along with more detailed status information. + +## Modifying the Reaction +To modify the reaction, you can simply use the `drasi apply` command again with the same reaction name that you used before. + +## Deleting the Reaction +To delete a Reaction you use the `drasi delete` command. There are two ways to do this. + +Firstly, you can specify the type of resource (Reaction) and its name, for example: + +```text +drasi delete reaction mcp-reaction +``` + +Secondly, you can refer to the YAML file(s) that contain the definitions used to create the Reaction(s): + +```text +drasi delete -f my-reaction.yaml <...> +``` + +This is a convenience, especially if a single YAML file contains multiple Reaction definitions. + +## Related Resources +- [Model Context Protocol Specification](https://modelcontextprotocol.io/specification/2025-03-26/server/resources) +- [MCP Resources Documentation](https://modelcontextprotocol.io/specification/2025-03-26/server/resources) +- [MCP Subscriptions Documentation](https://modelcontextprotocol.io/specification/2025-03-26/server/resources#subscriptions) diff --git a/docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/grid-image.svg b/docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/grid-image.svg new file mode 100644 index 00000000..2ecc0dd1 --- /dev/null +++ b/docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/grid-image.svg @@ -0,0 +1,6 @@ + + + + + + From c5dd5f683d56c3efab0da657433fa4d915861e31 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 5 Dec 2025 12:29:59 -0800 Subject: [PATCH 2/4] spell dict Signed-off-by: Daniel Gerlag --- .github/config/en-drasi.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/config/en-drasi.txt b/.github/config/en-drasi.txt index 58db26ac..baea5201 100644 --- a/.github/config/en-drasi.txt +++ b/.github/config/en-drasi.txt @@ -218,4 +218,8 @@ JQ jq haltOnError Dataverse's -maxInterval \ No newline at end of file +maxInterval +MCP +mcp +RPC +Streamable \ No newline at end of file From c30c3bc0f94e79b286693ce17f7a23483598ff36 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 5 Dec 2025 12:31:17 -0800 Subject: [PATCH 3/4] Update docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/_index.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../configure-reactions/configure-mcp-reaction/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/_index.md b/docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/_index.md index dd706e47..0498444d 100644 --- a/docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/_index.md +++ b/docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/_index.md @@ -296,7 +296,7 @@ drasi describe reaction mcp-reaction This will return the full definition used to create the Reaction along with more detailed status information. ## Modifying the Reaction -To modify the reaction, you can simply use the `drasi apply` command again with the same reaction name that you used before. +To modify the Reaction, you can simply use the `drasi apply` command again with the same Reaction name that you used before. ## Deleting the Reaction To delete a Reaction you use the `drasi delete` command. There are two ways to do this. From 894c35006d86702a0e414d6374e7e82a7d41c57a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 5 Dec 2025 12:31:26 -0800 Subject: [PATCH 4/4] Update docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/_index.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../configure-reactions/configure-mcp-reaction/_index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/_index.md b/docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/_index.md index 0498444d..0422ac4d 100644 --- a/docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/_index.md +++ b/docs/content/how-to-guides/configure-reactions/configure-mcp-reaction/_index.md @@ -317,5 +317,4 @@ This is a convenience, especially if a single YAML file contains multiple Reacti ## Related Resources - [Model Context Protocol Specification](https://modelcontextprotocol.io/specification/2025-03-26/server/resources) -- [MCP Resources Documentation](https://modelcontextprotocol.io/specification/2025-03-26/server/resources) - [MCP Subscriptions Documentation](https://modelcontextprotocol.io/specification/2025-03-26/server/resources#subscriptions)