Skip to content

feat(automations): event trigger support with callback system and runtime SDK#2844

Open
viktormarinho wants to merge 2 commits intomainfrom
viktormarinho/event-trigger-auto
Open

feat(automations): event trigger support with callback system and runtime SDK#2844
viktormarinho wants to merge 2 commits intomainfrom
viktormarinho/event-trigger-auto

Conversation

@viktormarinho
Copy link
Contributor

@viktormarinho viktormarinho commented Mar 24, 2026

Summary

  • UI: Event trigger form with connection selector (filtered by TRIGGER_BINDING), event type picker, and dynamic params form
  • Callback system: SHA-256 hashed tokens for secure MCP→Mesh event delivery via POST /api/trigger-callback
  • Binding: Added callbackUrl/callbackToken optional fields to TRIGGER_CONFIGURE schema
  • Runtime SDK: createTriggers() helper so MCPs can implement triggers declaratively with Zod params — handles TRIGGER_LIST, TRIGGER_CONFIGURE tools, and notify() for fire-and-forget delivery to Mesh

How it works

  1. User adds an event trigger in the UI → Mesh calls TRIGGER_CONFIGURE on the MCP with a callback URL + token
  2. When the external event fires, the MCP calls triggers.notify(connectionId, type, data)
  3. Mesh's /api/trigger-callback validates the token and fires matching automations via EventTriggerEngine

Test plan

🤖 Generated with Claude Code


Summary by cubic

Adds end-to-end event trigger support for automations with a secure MCP→Mesh callback flow and a simple runtime SDK. Users can add event triggers in the UI; external MCPs can notify Mesh to fire automations.

  • New Features

    • Event trigger UI: select a connection (filtered by TRIGGER), pick an event type, and fill dynamic params; normalizes trigger definitions (including z.enum options); cards show event type · connection and params.
    • Secure callback flow: POST /api/trigger-callback with Bearer tokens; 1MB limit via Hono bodyLimit; tokens are SHA-256 hashed, scoped per connection+org, rotated on enable with atomic upsert; returns 202.
    • Mesh↔MCP handshake: TRIGGER_CONFIGURE accepts callbackUrl (URL) and callbackToken (non-empty). Mesh generates credentials on enable and forwards validated events to the EventTriggerEngine.
    • Runtime SDK: packages/runtime adds createTriggers() to expose TRIGGER_LIST/TRIGGER_CONFIGURE and notify(); extracts enum values from z.enum params; keeps connection-level credentials on disable (Mesh manages lifecycle).
  • Migration

    • Run migration 052.
    • MCPs: adopt createTriggers() from packages/runtime, accept optional callbackUrl/callbackToken in TRIGGER_CONFIGURE, and call notify() from webhook handlers.

Written for commit e953300. Summary will update on new commits.

… runtime SDK

Add end-to-end event-based trigger support for automations:
- UI: event trigger form with connection selector, event type picker, and params
- Backend: callback token system (SHA-256 hashed) for secure MCP-to-Mesh delivery
- API: POST /api/trigger-callback endpoint for external MCPs to fire automations
- Binding: callbackUrl/callbackToken fields on TRIGGER_CONFIGURE schema
- Runtime: createTriggers() SDK helper so MCPs can implement triggers declaratively

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Contributor

🧪 Benchmark

Should we run the Virtual MCP strategy benchmark for this PR?

React with 👍 to run the benchmark.

Reaction Action
👍 Run quick benchmark (10 & 128 tools)

Benchmark will run on the next push after you react.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 24, 2026

Release Options

Suggested: Minor (2.201.0) — based on feat: prefix

React with an emoji to override the release type:

Reaction Type Next Version
👍 Prerelease 2.200.1-alpha.1
🎉 Patch 2.200.1
❤️ Minor 2.201.0
🚀 Major 3.0.0

Current version: 2.200.0

Note: If multiple reactions exist, the smallest bump wins. If no reactions, the suggested bump is used (default: minor).

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 issues found across 21 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/runtime/src/triggers.ts">

<violation number="1" location="packages/runtime/src/triggers.ts:84">
P2: Include enum values from z.enum parameters when building paramsSchema so trigger options aren’t dropped.</violation>

<violation number="2" location="packages/runtime/src/triggers.ts:128">
P2: Don’t delete connection-level callback credentials when disabling a single trigger type, or other enabled trigger types will stop delivering events.</violation>
</file>

<file name="apps/mesh/src/api/routes/trigger-callback.ts">

<violation number="1" location="apps/mesh/src/api/routes/trigger-callback.ts:40">
P2: The `content-length` header check is bypassable because the header is client-controlled and can be omitted or spoofed. Use Hono's `bodyLimit` middleware instead, which enforces the limit at the stream level before the body is fully read.</violation>
</file>

<file name="packages/runtime/src/triggers.test.ts">

<violation number="1" location="packages/runtime/src/triggers.test.ts:79">
P2: Use a short fixed delay around 50ms for the fire-and-forget flush; 10ms can be too short and lead to flaky tests.

(Based on your team's feedback about allowing a ~50ms delay to flush fire-and-forget async work in tests.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/mesh/src/storage/trigger-callback-tokens.ts">

<violation number="1" location="apps/mesh/src/storage/trigger-callback-tokens.ts:68">
P1: Replace the non-atomic DELETE + INSERT with a single `onConflict` upsert to avoid a race condition under concurrent token rotation for the same connection. Two concurrent calls can both complete the DELETE, then the second INSERT fails on the unique index.

(Based on your team's feedback about using atomic upserts instead of pre-check + create patterns.) [FEEDBACK_USED]</violation>
</file>

<file name="packages/bindings/src/well-known/trigger.ts">

<violation number="1" location="packages/bindings/src/well-known/trigger.ts:71">
P2: Validate `callbackUrl` as a URL instead of accepting arbitrary strings.</violation>

<violation number="2" location="packages/bindings/src/well-known/trigger.ts:72">
P2: Reject empty `callbackToken` values when the token is provided.</violation>
</file>

<file name="apps/mesh/migrations/052-trigger-callback-tokens.ts">

<violation number="1" location="apps/mesh/migrations/052-trigger-callback-tokens.ts:11">
P1: This migration is not registered in `migrations/index.ts`, so it will never run and the new table won't exist.

(Based on your team's feedback about verifying migration registration in migrations/index.ts.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.


import type { Kysely } from "kysely";

export async function up(db: Kysely<unknown>): Promise<void> {
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: This migration is not registered in migrations/index.ts, so it will never run and the new table won't exist.

(Based on your team's feedback about verifying migration registration in migrations/index.ts.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/migrations/052-trigger-callback-tokens.ts, line 11:

<comment>This migration is not registered in `migrations/index.ts`, so it will never run and the new table won't exist.

(Based on your team's feedback about verifying migration registration in migrations/index.ts.) </comment>

<file context>
@@ -0,0 +1,35 @@
+
+import type { Kysely } from "kysely";
+
+export async function up(db: Kysely<unknown>): Promise<void> {
+  await db.schema
+    .createTable("trigger_callback_tokens")
</file context>
Fix with Cubic

- Extract enum values from z.enum() params in createTriggers paramsSchema
- Don't delete connection-level callback credentials on single trigger disable
- Use Hono bodyLimit middleware instead of client-controlled content-length check
- Atomic upsert (onConflict) for token rotation instead of DELETE+INSERT
- Validate callbackUrl as URL, reject empty callbackToken in binding schema
- Register migration 052 in migrations/index.ts
- Bump test delays to 50ms to avoid flaky fire-and-forget assertions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 6 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/runtime/src/triggers.ts">

<violation number="1" location="packages/runtime/src/triggers.ts:131">
P1: Missing credential cleanup on reconfigure/disable leaves stale callback tokens active, so `notify()` can continue firing events after a trigger should be inactive.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

throw new Error("Connection ID not available");
}

if (context.callbackUrl && context.callbackToken) {
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Missing credential cleanup on reconfigure/disable leaves stale callback tokens active, so notify() can continue firing events after a trigger should be inactive.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/runtime/src/triggers.ts, line 131:

<comment>Missing credential cleanup on reconfigure/disable leaves stale callback tokens active, so `notify()` can continue firing events after a trigger should be inactive.</comment>

<file context>
@@ -117,15 +128,11 @@ export function createTriggers<const TDefs extends TriggerDef[]>(
-        }
-      } else {
-        callbackCredentials.delete(connectionId);
+      if (context.callbackUrl && context.callbackToken) {
+        callbackCredentials.set(connectionId, {
+          callbackUrl: context.callbackUrl,
</file context>
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant