agent-frameworks/taskade-autonomous

Taskade Autonomous Pipeline

Use Case: Wire MCP tools into Taskade's autonomous agent pipeline so the agent can discover tools, dispatch tasks, persist results, and recover from failures without human intervention.

Prerequisites

  • A Taskade project with API access enabled
  • MCP server running and exposing tools (see database-queries and api-routers patterns)
  • Node.js 18+ for the bridge server

Setup & Configuration

The bridge sits between Taskade's agent loop and your MCP server. It translates Taskade task events into MCP tool calls and writes results back to the project.

src/bridge/taskade-mcp.tstypescript
import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";

const TaskSchema = z.object({
  taskId: z.string(),
  action: z.enum(["query_database", "bookmark_url", "search_web"]),
  params: z.record(z.unknown()),
});

export const dispatchTask = createServerFn({ method: "POST" })
  .validator((d: unknown) => TaskSchema.parse(d))
  .handler(async ({ data }) => {
    // Route to the correct MCP tool
    switch (data.action) {
      case "query_database":
        return handleDbQuery(data.params);
      case "bookmark_url":
        return handleBookmark(data.params);
      case "search_web":
        return handleWebSearch(data.params);
      default:
        throw new Error(`Unknown action: ${data.action}`);
    }
  });

Core Implementation

Autonomous pipeline with retry, persistence, and result logging:

src/bridge/autonomous-pipeline.tstypescript
interface PipelineTask {
  id: string;
  action: string;
  params: Record<string, unknown>;
  status: "pending" | "running" | "done" | "failed";
  retries: number;
}

const MAX_RETRIES = 3;
const BACKOFF_MS = [1000, 5000, 15000];

export async function runPipeline(task: PipelineTask) {
  const db = getDb();

  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
    try {
      await db.execute({
        sql: "UPDATE pipeline SET status = ? WHERE id = ?",
        args: ["running", task.id],
      });

      const result = await dispatchTask({
        data: { taskId: task.id, action: task.action as any, params: task.params },
      });

      await db.execute({
        sql: "UPDATE pipeline SET status = ?, result = ?, completed_at = datetime('now') WHERE id = ?",
        args: ["done", JSON.stringify(result), task.id],
      });

      return result;
    } catch (error) {
      if (attempt < MAX_RETRIES) {
        await sleep(BACKOFF_MS[attempt]);
        continue;
      }
      await db.execute({
        sql: "UPDATE pipeline SET status = ?, error = ? WHERE id = ?",
        args: ["failed", String(error), task.id],
      });
      throw error;
    }
  }
}

function sleep(ms: number) {
  return new Promise((r) => setTimeout(r, ms));
}

Register the pipeline as a Taskade integration endpoint:

src/bridge/register.tstypescript
// Register with Taskade's webhook endpoint
const TASKADE_API = "https://api.taskade.com/v1";
const TASKADE_TOKEN = process.env.TASKADE_API_TOKEN;

export async function registerWebhook(webhookUrl: string) {
  const res = await fetch(`${TASKADE_API}/webhooks`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${TASKADE_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      url: webhookUrl,
      events: ["task.created", "task.updated"],
      secret: process.env.WEBHOOK_SECRET,
    }),
  });
  if (!res.ok) throw new Error(`Webhook registration failed: ${res.status}`);
  return res.json();
}

Deployment Notes

  • Taskade rate limits: 60 requests per minute for the API. Queue dispatching with a simple debounce.
  • State persistence: The agent state (pending tasks, partial results) is ephemeral in-memory. Use the database-queries pattern to persist checkpoints.
  • Timeout: Taskade expects responses within 30s. For long-running MCP tools, use an async pattern where the bridge acknowledges immediately and the agent polls for results.
  • Error recovery: Transient failures (network, rate limit) should retry with exponential backoff. Hard failures (invalid tool, bad args) should log and skip.
  • Auth: Keep the Taskade API token and MCP server secrets in environment variables. Rotate tokens via a scheduled server function.