# Signal providers **Added in:** `@mastra/core@1.39.0` > **Alpha:** This feature is in alpha. Breaking changes may occur without a major version bump until the API is stable. A signal provider monitors an external source, such as GitHub, Slack, continuous integration (CI), or your own API, and pushes [notification signals](https://mastra.ai/docs/agents/signals) into subscribed agent threads. ## When to use a signal provider Use a signal provider when an external system produces events that an agent should react to, and you want Mastra to manage the subscription bookkeeping for you. - The source emits events tied to a resource a thread cares about, such as a pull request, a channel, or a build. - You want one place that tracks which threads watch which external resources. - You want to receive events by polling, by webhook, or both. If you only need to push a one-off event into a thread, call [`agent.sendNotificationSignal()`](https://mastra.ai/reference/agents/agent) directly instead. ## How signal providers work A signal provider is the producing side of the [signals](https://mastra.ai/docs/agents/signals) system. It brings external events into a thread, while the signal APIs control how the thread consumes them. A signal provider combines three capabilities: - **Subscription tracking:** The `SignalProvider` base class keeps an in-memory registry that maps each agent thread to the external resources it watches. - **Ingestion:** You override `poll()` for pull-based sources or `handleWebhook()` for push-based sources. - **Delivery:** When an event matches a subscription, call the protected `notify()` helper to forward a notification signal to the connected agent's thread. Register a provider by passing it to an agent. The agent connects the provider, starts polling if a `pollInterval` is set, and merges any processors or tools the provider exposes. ```typescript import { Agent } from '@mastra/core/agent' import { CiSignals } from '../signals/ci-signals' export const supportAgent = new Agent({ id: 'support-agent', name: 'Support Agent', instructions: 'Help the user triage updates.', model: 'openai/gpt-5.5', signals: [new CiSignals()], }) ``` > **Note:** Notification delivery requires a storage adapter with notification support, such as [libSQL](https://mastra.ai/reference/storage/libsql), [PostgreSQL](https://mastra.ai/reference/storage/postgresql), or [MongoDB](https://mastra.ai/reference/storage/mongodb). Configure storage on the Mastra instance so `notify()` can store notification records. ## Quickstart The following example demonstrates a polling provider that watches CI pipelines and emits a notification when a subscribed pipeline fails. ```typescript import { SignalProvider } from '@mastra/core/signals' import type { SignalProviderTarget, SignalSubscription } from '@mastra/core/signals' type BuildStatus = { id: string status: 'passed' | 'failed' } const builds = new Map([ ['acme-app-main', { id: 'build_123', status: 'failed' }], ]) async function fetchBuildStatus(pipeline: string): Promise { return builds.get(pipeline) ?? { id: 'build_unknown', status: 'passed' } } export class CiSignals extends SignalProvider<'ci-signals'> { readonly id = 'ci-signals' as const readonly pollInterval = 30_000 watch(target: SignalProviderTarget, pipeline: string) { return this.subscribe(target, pipeline) } unwatch(target: SignalProviderTarget, pipeline: string) { return this.unsubscribe(target, pipeline) } async poll(subscriptions: SignalSubscription[]) { for (const sub of subscriptions) { const build = await fetchBuildStatus(sub.externalResourceId) if (build.status !== 'failed') continue await this.notify( { source: this.id, kind: 'ci-status', priority: 'high', summary: `Build failed for ${sub.externalResourceId}`, payload: build, dedupeKey: `${this.id}:${sub.externalResourceId}:${build.id}`, }, { resourceId: sub.resourceId, threadId: sub.threadId }, ) } } } ``` Register the provider with an agent and subscribe a thread to the pipeline you want to watch. ```typescript import { Agent } from '@mastra/core/agent' import { CiSignals } from '../signals/ci-signals' export const ciSignals = new CiSignals() export const supportAgent = new Agent({ id: 'support-agent', name: 'Support Agent', instructions: 'Help the user triage CI updates.', model: 'openai/gpt-5.5', signals: [ciSignals], }) ciSignals.watch({ resourceId: 'user_123', threadId: 'thread_456' }, 'acme-app-main') ``` Mastra calls `poll()` on the `pollInterval` with all active subscriptions. It skips a cycle when there are no subscriptions and doesn't overlap cycles, so a slow `poll()` doesn't run concurrently with itself. > **Note:** For a complete polling-provider build with notification storage, agent registration, thread subscription, and testing, follow [Building a signal provider](https://mastra.ai/guides/guide/signal-provider). ## Polling and webhook providers Use polling when the external source doesn't push events to your app. Set `pollInterval` and override `poll(subscriptions)`. Each subscription includes the thread target and the external resource id to inspect. Use webhooks when the external source can call your app. Override `handleWebhook(request)`, parse the payload, find matching subscriptions, and call `notify()` for each match. ```typescript import { SignalProvider } from '@mastra/core/signals' import type { SignalProviderWebhookRequest } from '@mastra/core/signals' export class CiSignals extends SignalProvider<'ci-signals'> { readonly id = 'ci-signals' as const async handleWebhook(request: SignalProviderWebhookRequest) { const payload = request.body as { pipeline: string; status: string } const subscriptions = this.getSubscriptionsForResource(payload.pipeline) for (const sub of subscriptions) { await this.notify( { source: this.id, kind: 'ci-status', priority: 'high', summary: `Build ${payload.status} for ${payload.pipeline}`, payload, }, { resourceId: sub.resourceId, threadId: sub.threadId }, ) } return { status: 200, body: { matched: subscriptions.length } } } } ``` `handleWebhook()` is a provider method, not an auto-mounted HTTP route. Invoke it from your own endpoint, passing the request body, headers, and any route params. > **Note:** Visit [`SignalProvider` reference](https://mastra.ai/reference/signals/signal-provider) for subscription, polling, lifecycle, and `notify()` details. For the complete notification payload shape, including deduplication and coalescing fields, visit [`Agent.sendNotificationSignal()` reference](https://mastra.ai/reference/agents/agent). ## Built-in webhook provider For generic webhook sources, use [`WebhookSignalProvider`](https://mastra.ai/reference/signals/webhook-signal-provider) instead of writing a subclass. Configure it with a function that extracts a resource id from the payload, and an optional function that builds the notification. ```typescript import { Agent } from '@mastra/core/agent' import { WebhookSignalProvider } from '@mastra/core/signals' const webhooks = new WebhookSignalProvider({ extractResourceId: payload => (payload as { repository: string }).repository, buildNotification: (payload, sub) => ({ source: 'ci', kind: 'build-status', priority: 'medium', summary: `Build ${(payload as { status: string }).status} for ${sub.externalResourceId}`, }), }) export const supportAgent = new Agent({ id: 'support-agent', name: 'Support Agent', instructions: 'Help the user triage updates.', model: 'openai/gpt-5.5', signals: [webhooks], }) webhooks.subscribeThread({ resourceId: 'user_123', threadId: 'thread_456' }, 'acme/app') ``` When a webhook arrives, call `webhooks.handleWebhook({ body, headers })` from your route. The provider matches the extracted resource id against its subscriptions and notifies each matching thread. ## Advanced provider capabilities A provider can support more than event ingestion. Add only the capabilities your source needs. - **Durable subscriptions:** The base registry is in-memory and per-process. Persist subscriptions yourself when they must survive a restart, then rehydrate them in [`start()`](https://mastra.ai/reference/signals/signal-provider). - **Lifecycle hooks:** Override [`start()`](https://mastra.ai/reference/signals/signal-provider) for async setup and [`stop()`](https://mastra.ai/reference/signals/signal-provider) for cleanup. Call `super.stop()` when overriding `stop()` so the base provider can stop polling and clear its registry. - **Processors and tools:** Return processors from [`getInputProcessors()`](https://mastra.ai/reference/signals/signal-provider) or [`getOutputProcessors()`](https://mastra.ai/reference/signals/signal-provider), and return agent-callable tools from [`getTools()`](https://mastra.ai/reference/signals/signal-provider). The `@mastra/github-signals` package is a production signal provider that watches GitHub pull requests and notifies threads about comments, review state, continuous integration status, and merges. Use it as a reference for polling, durable subscriptions, tools, processors, and lifecycle hooks. ```typescript import { Agent } from '@mastra/core/agent' import { GithubSignals } from '@mastra/github-signals' export const devAgent = new Agent({ id: 'dev-agent', name: 'Dev Agent', instructions: 'Help triage pull request activity.', model: 'openai/gpt-5.5', signals: [new GithubSignals()], }) ``` ## Related - [Guide: Building a signal provider](https://mastra.ai/guides/guide/signal-provider) - [Signals](https://mastra.ai/docs/agents/signals) - [Notification signals](https://mastra.ai/docs/agents/signals) - [`SignalProvider` reference](https://mastra.ai/reference/signals/signal-provider) - [`WebhookSignalProvider` reference](https://mastra.ai/reference/signals/webhook-signal-provider)