feat(automations): event trigger support with callback system and runtime SDK#2844
feat(automations): event trigger support with callback system and runtime SDK#2844viktormarinho wants to merge 2 commits intomainfrom
Conversation
… 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>
🧪 BenchmarkShould we run the Virtual MCP strategy benchmark for this PR? React with 👍 to run the benchmark.
Benchmark will run on the next push after you react. |
Release OptionsSuggested: Minor ( React with an emoji to override the release type:
Current version:
|
There was a problem hiding this comment.
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> { |
There was a problem hiding this comment.
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.)
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>
- 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>
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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>
Summary
POST /api/trigger-callbackcallbackUrl/callbackTokenoptional fields toTRIGGER_CONFIGUREschemacreateTriggers()helper so MCPs can implement triggers declaratively with Zod params — handles TRIGGER_LIST, TRIGGER_CONFIGURE tools, andnotify()for fire-and-forget delivery to MeshHow it works
TRIGGER_CONFIGUREon the MCP with a callback URL + tokentriggers.notify(connectionId, type, data)/api/trigger-callbackvalidates the token and fires matching automations viaEventTriggerEngineTest plan
createTriggersunit tests (6 tests passing)🤖 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
z.enumoptions); cards show event type · connection and params.TRIGGER_CONFIGUREacceptscallbackUrl(URL) andcallbackToken(non-empty). Mesh generates credentials on enable and forwards validated events to the EventTriggerEngine.packages/runtimeaddscreateTriggers()to exposeTRIGGER_LIST/TRIGGER_CONFIGUREandnotify(); extracts enum values fromz.enumparams; keeps connection-level credentials on disable (Mesh manages lifecycle).Migration
createTriggers()frompackages/runtime, accept optionalcallbackUrl/callbackTokeninTRIGGER_CONFIGURE, and callnotify()from webhook handlers.Written for commit e953300. Summary will update on new commits.