> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xquik.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Mastra

> Build a TypeScript Twitter agent with Mastra and Xquik's MCP tools

<blockquote className="agent-llms-directive">
  For the complete documentation index, see <a href="/llms.txt">llms.txt</a>.
</blockquote>

Build a Mastra agent in TypeScript that can search tweets, hand off IDs and cursors, post tweets, replay stored monitor events, and run extraction jobs - connected to Xquik's MCP server.

## Prerequisites

* Node.js 18+
* [Xquik API key](/quickstart) (`xq_...`)
* An LLM API key (OpenAI, Anthropic, or any Vercel AI SDK-supported provider)

## Install

```bash theme={null}
npm install @mastra/mcp @mastra/core
```

## Full Example

```typescript theme={null}
import { writeFile } from "node:fs/promises";
import { MCPClient } from "@mastra/mcp";
import { Agent } from "@mastra/core/agent";

const mcp = new MCPClient({
  servers: {
    xquik: {
      url: new URL("https://xquik.com/mcp"),
      requestInit: {
        headers: {
          "x-api-key": process.env.XQUIK_API_KEY!,
        },
      },
    },
  },
});

const agent = new Agent({
  id: "xquik-agent",
  name: "Xquik Agent",
  instructions:
    "You help users interact with X (Twitter) via the Xquik API.",
  model: "anthropic/claude-sonnet-4-20250514",
  tools: await mcp.listTools(),
});

const handoffPrompt = (
  "Search for the latest tweets about AI agents. Return compact JSON " +
  "with query, route_used, tweets[{tweet_id,text,author_username,created_at}], " +
  "has_more, next_cursor, and key influencers."
);
const result = await agent.generate(handoffPrompt);

await writeFile("xquik-mastra-handoff.json", result.text, "utf8");

await mcp.disconnect();
```

The agent auto-discovers all Xquik tools and can call any of the 120 API endpoints.

Mastra's `MCPClient` loads tools with `listTools()` for agent setup and `listToolsets()` for per-call tools. For HTTP MCP servers, it uses `requestInit` for headers, tries Streamable HTTP from the URL, and falls back to SSE when needed. The MCP runtime returns normalized snake\_case fields through `xquik.request()`, so keep prompts aligned with `tweet_id`, `has_more`, `next_cursor`, and returned job IDs.

## Handoff Checklist

<CardGroup cols={2}>
  <Card title="Tweet search rows" icon="search">
    Store `tweet_id`, `text`, `author_username`, `created_at`, `has_more`, `next_cursor`, and the original `q`.
  </Card>

  <Card title="User profile rows" icon="users">
    Store source `id` as `user_id`, plus `username`, `name`, `followers`, `verified`, `profile_picture`, `has_more`, `next_cursor`, and the source lookup or search query.
  </Card>

  <Card title="Trend rows" icon="trending-up">
    Store each trend `name`, `rank`, `query`, and `description`. Keep response `count`, `woeid`, and the requested region with the run checkpoint.
  </Card>

  <Card title="Monitor and webhook setup" icon="radio">
    Store the returned monitor `id` as `monitor_id`, `event_types`, `next_billing_at`, the returned webhook `id` as `webhook_id`, `url`, and the one-time `secret` in a secret manager. On production deliveries, store `delivery_id` for receiver retry de-dupe and `stream_event_id` when one monitor event should process once across endpoint changes.
  </Card>

  <Card title="Stored event replay" icon="activity">
    Store `event_id`, `type`, `monitor_id`, `monitor_type`, `occurred_at`, `has_more`, `next_cursor`, and the `after` query for the next page.
  </Card>

  <Card title="Extraction jobs" icon="database">
    Store `extraction_id`, `status`, `poll`, and `export_after_complete`; poll before loading CSV, JSON, or XLSX rows.
  </Card>

  <Card title="Writes" icon="send">
    Store `tweet_id` or `write_action_id`, `reply_to_tweet_id`, `status`, `charged_credits`, and `poll`; do not resend pending writes.
  </Card>

  <Card title="Media attachments" icon="image">
    For tweets or replies, pass public URLs in `media` and store `tweet_id` or `write_action_id`. For DMs, upload first, pass one `media_id` in `media_ids`, store `message_id`, and leave `reply_to_message_id` unset.
  </Card>
</CardGroup>

## Streaming Responses

```typescript theme={null}
import { writeFile } from "node:fs/promises";

const stream = await agent.stream(
  "What are the trending topics right now?"
);

let handoff = "";
for await (const chunk of stream.textStream) {
  handoff += chunk;
}

await writeFile("xquik-mastra-stream-handoff.json", handoff, "utf8");
```

## Dynamic Toolsets (Per-User API Keys)

For multi-tenant apps, create an `MCPClient` per request with the user's API key:

```typescript theme={null}
import { writeFile } from "node:fs/promises";
import { MCPClient } from "@mastra/mcp";
import { Agent } from "@mastra/core/agent";

const agent = new Agent({
  id: "xquik-agent",
  name: "Xquik Agent",
  instructions: "You help users with X automation.",
  model: "openai/gpt-4o",
});

async function handleRequest(prompt: string, userApiKey: string) {
  const mcp = new MCPClient({
    servers: {
      xquik: {
        url: new URL("https://xquik.com/mcp"),
        requestInit: {
          headers: { "x-api-key": userApiKey },
        },
      },
    },
  });

  try {
    const response = await agent.generate(prompt, {
      toolsets: await mcp.listToolsets(),
    });

    await writeFile("xquik-mastra-user-handoff.json", response.text, "utf8");
    return response.text;
  } finally {
    await mcp.disconnect();
  }
}
```

## Dynamic Headers with Custom Fetch

For request-context-aware headers (e.g., forwarding cookies):

```typescript theme={null}
const mcp = new MCPClient({
  servers: {
    xquik: {
      url: new URL("https://xquik.com/mcp"),
      fetch: async (url, init, requestContext) => {
        const headers = new Headers(init?.headers);
        headers.set("x-api-key", process.env.XQUIK_API_KEY!);
        return fetch(url, { ...init, headers });
      },
    },
  },
});
```

## Environment Variables

```bash .env theme={null}
XQUIK_API_KEY=xq_YOUR_KEY_HERE
ANTHROPIC_API_KEY=sk-ant-...
```

## Package Versions

| Package                     | Version |
| --------------------------- | ------- |
| `@mastra/mcp`               | 1.4.2+  |
| `@mastra/core`              | 1.24.1+ |
| `@modelcontextprotocol/sdk` | 1.27.1+ |
