Linear
Inbound webhook → swarm tasks. Workflow-state gate, swarm-ready label override, AgentSession activity stream, and how to push outbound updates.
Agent Swarm integrates with Linear as an inbound issue-tracker source: assigning the agent to a Linear issue creates a swarm task and a Linear AgentSession. Activity flows back into the AgentSession as thought / action / response / error events.
Sync direction is INBOUND-ONLY (Linear → swarm). Inbound webhooks create and update swarm tasks. Outbound state changes (e.g. moving an issue to "Done", adding labels, posting plain comments outside an AgentSession) are not automatic — see Outbound updates below.
What it does
- Inbound on assignment. When a user assigns the Linear agent integration to an issue, Linear fires an
AgentSessionwebhook. The handler creates a swarm task linked to the session. - State-gated task creation. Only issues whose
WorkflowState.typeis in the configured allowlist trigger a task. By default that'sunstarted, started, completed, canceled— i.e. everything excepttriageandbacklog. Skipped assignments leave a comment on the AgentSession explaining how to retry. - Label override. A label on the issue (default
swarm-ready, configurable) bypasses the state gate so users can pre-stage backlog issues to auto-trigger when assigned. - AgentSession activity stream. The agent posts thoughts, actions, responses, and errors back to the AgentSession in real time. A
responseactivity auto-completes the session. - Webhook signing + dedup. Inbound webhooks are HMAC-SHA256 verified and deduped by the
Linear-Deliveryheader (5-minute TTL). - Dashboard linking.
externalUrlson each session link to the swarm dashboard's task page.
Setup
1. Create a Linear OAuth App
- Go to Linear → Settings → API → Applications and create a new application:
- Actor: Application
- Callback URL:
<MCP_BASE_URL>/api/trackers/linear/callback - Webhook URL:
<MCP_BASE_URL>/api/trackers/linear/webhook
- Enable Agent session events in the webhook settings.
These URLs are built from PUBLIC_MCP_BASE_URL when it is set, falling back to MCP_BASE_URL otherwise. In split deploys where MCP_BASE_URL is an internal/cluster address, set PUBLIC_MCP_BASE_URL to the public ingress origin so Linear can reach the callback and webhook.
- Copy the Client ID, Client Secret, and Webhook Signing Secret.
2. Configure environment
Add to your .env:
# Required — OAuth + webhook auth
LINEAR_CLIENT_ID=your-client-id
LINEAR_CLIENT_SECRET=your-client-secret
LINEAR_REDIRECT_URI=http://localhost:3013/api/trackers/linear/callback
LINEAR_SIGNING_SECRET=your-webhook-signing-secret
# Optional — gate config (defaults shown)
LINEAR_ALLOWED_STATES=unstarted,started,completed,canceled
LINEAR_SWARM_READY_LABEL=swarm-ready
# Optional — disable the integration entirely
# LINEAR_DISABLE=trueWith portless dev mode (bun run dev:http):
LINEAR_REDIRECT_URI=https://api.swarm.localhost:1355/api/trackers/linear/callback3. Complete OAuth
Start the server and visit <MCP_BASE_URL>/api/trackers/linear/authorize in a browser to complete the OAuth flow. The token is encrypted at rest in swarm_config.
How it works
Inbound flow
State gate
When an issue is assigned, the handler resolves the issue's WorkflowState.type and label list. The decision is:
| Condition | Result |
|---|---|
Issue has the swarm-ready label (or your LINEAR_SWARM_READY_LABEL value) | Create — label override |
state.type ∈ LINEAR_ALLOWED_STATES | Create — ready |
state.type is unset / null | Create — fail-open |
state.type ∉ allowlist | Skip — post a response on the AgentSession explaining how to retry |
LINEAR_ALLOWED_STATES is a comma-separated list of Linear WorkflowState.type values. The full enum is triage, backlog, unstarted, started, completed, canceled. Whitespace and case are normalized.
Examples:
# Default: skip Backlog and Triage
LINEAR_ALLOWED_STATES=unstarted,started,completed,canceled
# Only trigger when actively in progress
LINEAR_ALLOWED_STATES=started
# Accept every workflow state (the full Linear enum)
LINEAR_ALLOWED_STATES=triage,backlog,unstarted,started,completed,canceled
# Close the gate: no workflow state creates a task — only the swarm-ready label override does
LINEAR_ALLOWED_STATES=The Linear AgentSessionEvent payload doesn't include state or labels, so the handler issues a small GraphQL query (issue.state.type + labels.nodes.name) to resolve them. If the OAuth token is missing, the gate fails open.
Skip message
When the gate skips an assignment, the agent posts a response activity to the AgentSession (which auto-completes it):
Agent Swarm received the assignment but skipped — this issue is in Backlog.
To trigger work, move it to an allowed workflow state (e.g. Todo or In Progress), or add the
swarm-readylabel and re-assign the agent.
This is a deliberate design choice: silent no-ops on assignments are confusing. Always leave a trace.
Issue updates
Linear Issue updates that target a tracked issue refresh the swarm task's tracker metadata. State transitions:
| Linear state | Swarm action |
|---|---|
Backlog | log only, no status change |
Todo | log only |
In Progress | log only |
Done | log only — agent decides when work is done |
Canceled / Cancelled | cancel the swarm task |
| Issue deleted | cancel the swarm task |
Follow-up messages
When a user sends a message in the Linear agent chat after the original task is done:
- Linear fires a
promptedAgentSessionEvent. - The handler creates a new swarm task with the follow-up context, repointing the existing
tracker_syncto it. - The agent processes the follow-up via the normal task lifecycle.
If the original task is still in flight, the handler posts a thought activity acknowledging the message but does not create a new task — the message is folded into the existing run.
Stop signal
If the user clicks the stop button in Linear, the agent activity carries signal: "stop". The handler cancels the active swarm task and ends the session with a "Task cancelled by user." response.
Outbound updates
The webhook integration is one-way. To push updates back to Linear from the swarm — creating issues, transitioning states, posting comments outside an AgentSession — use the linear-interaction skill. The skill wraps the Linear GraphQL API:
mutation IssueCreate($input: IssueCreateInput!) {
issueCreate(input: $input) { issue { id identifier } }
}
mutation IssueUpdate($id: String!, $input: IssueUpdateInput!) {
issueUpdate(id: $id, input: $input) { success }
}
mutation CommentCreate($input: CommentCreateInput!) {
commentCreate(input: $input) { comment { id } }
}Authentication uses the swarm's OAuth token from swarm_config (stored encrypted under LINEAR_OAUTH_TOKEN). The lead agent fetches it via db-query; worker agents request it from the lead.
For activity within an existing AgentSession (the agent's running task), use the helpers in src/linear/sync.ts — postAgentSessionResponse, postAgentSessionThought, postAgentSessionAction, postAgentSessionError. These don't need the skill; they're called directly from the worker session.
MCP tools
| Tool | Description |
|---|---|
tracker-status | Check tracker connection status |
tracker-link-task | Link a swarm task to a Linear issue |
tracker-unlink | Remove a tracker link |
tracker-sync-status | View sync status for linked items |
tracker-map-agent | Map a swarm agent to a Linear user for assignment routing |
Architecture
The integration sits on top of a generic tracker abstraction shared with Jira and (eventually) others:
src/oauth/— Reusable OAuth module withoauth_apps/oauth_tokenstables and PKCE support.src/be/db-queries/tracker.ts— Generictracker_syncandtracker_agent_mappingtables.src/linear/— Linear-specific webhook handler (webhook.ts), AgentSession sync (sync.ts), state gate (gate.ts), and prompt templates (templates.ts).
The state gate is exposed as a pure function (shouldCreateTaskFromLinearEvent in src/linear/gate.ts) so it can be unit-tested without spinning up the API.
Related skills & references
linear-interactionskill — canonical procedure for pushing outbound updates (create issues, change status, comment) via the Linear GraphQL API. Invoke with theSkilltool.- Linear API docs — GraphQL schema and authentication reference.
@linear/sdk— official TypeScript SDK; the swarm uses rawfetchagainst the GraphQL endpoint, but the SDK's type definitions are the canonical reference for payload shapes.
Related docs
- Environment Variables — full env reference
- Task Lifecycle — how tasks created from Linear flow through the swarm
- Architecture Overview — system architecture including integrations
Bitbucket Integration
Connect Bitbucket Cloud so a bot mention or review request on a pull request creates a swarm task, and workers clone, push, and open pull requests with git and the Bitbucket REST API
Jira Integration
Connect Agent Swarm to Atlassian Jira Cloud for bidirectional issue tracking via OAuth 3LO and dynamic webhooks