AI Workflows

How to Build Intelligent Agents With Mastra and MCP Integration

Learn how to create agents using Mastra with tools and MCP integration. Follow step-by-step guidance for a functional setup.

7 min read

Mastra allows developers to design state-of-the-art intelligent agents using tools, large language models (LLMs), memory, and event workflows. In this guide, you will build a functional personal assistant targeted at meeting preparation using Mastra and MCP integration.

The main features of the intelligent assistant, which will be created step-by-step, include:

  • Using mastra createTool for tool creation with Zod schemas.
  • Integrating MCP servers for efficient agent-to-agent communication.
  • Adding Slack and web research services like Exa.ai for real-time interactions.
  • Leveraging Mastra's memory systems to make interactions consistent and context-aware.
  • Event-based task scheduling for intelligent agent-driven workflows.

Prerequisites for Setting Up Mastra Agents

Before you start building intelligent agents with Mastra, ensure you have the following:

prerequisites

  • Installed Node.js (v18+) and npm (latest version) on your machine.
  • Installed Mastra CLI using npm install -g mastra-cli or run npx create-mastra.
  • Valid API keys for services like Anthropic, cal.com, Slack, and Exa.ai.
  • An Ngrok account for secure tunneling to expose your local development server.
  • Familiarity with TypeScript, JSON, and npm-based project structures.

Step-by-Step Guide to Bootstrap a Mastra Project

In this section, you'll initialize a new project and set up the basic components.

steps

  1. Create a new Mastra project:

    bash
    npx create-mastra
    • Choose a project name (e.g., meeting-assistant) and where to initialize the files.
  2. Select a model provider during setup (e.g., Anthropic or OpenAI) and provide its API key.

  3. Enable the recommended Mastra tools library for agent skills.

  4. Initialize Git for your project, and install dependencies:

    bash
    npm install
  5. Start the development server:

    bash
    npm run dev

    Mastra Studio should now be available at http://localhost:4111. The Studio interface lets you manage agents, tools, memory, and workflows.

Configuring the Intelligent Agent

Define the initial properties and purpose of your agent.

steps

  1. Edit your agent configuration file in Mastra (e.g., index.ts) to initialize the agent:

    typescript
    import { createAgent } from "mastra";
    
    const meetingAssistant = createAgent({
      id: "meeting-assistant",
      name: "Meeting Assistant",
      systemPrompt: `
        You are a personal meeting assistant. Your job is to help the user prepare
        for meetings by researching the people they'll meet and providing concise, actionable briefs.
        Your responses should be scannable with bullet points.
      `,
      model: "anthropic:claude-sonnet-4.5",
    });
    
    export default meetingAssistant;
  2. Define the functionality your agent will serve (summarizing meeting information, facilitating event preparation, and more).

  3. Restart the Mastra server using:

    bash
    npm run dev

    Confirm the agent appears in Mastra Studio under Agents.

Integrating Slack for Agent Interaction

Enable Slack bot support to interact with your agent via chat.

steps

  1. Install Vercel's Chat SDK and Slack adapter:

    bash
    npm install @vercel/chat-sdk @vercel/chat-sdk-slack
  2. Configure Ngrok for local testing:

    bash
    ngrok http 4111
  3. Create a Slack app via the Slack developer console:

    • Use a manifest to define bot permissions (chat:write, app_mentions:read, channels:history, etc.).
    • Update the bot's event subscription URL with the Ngrok URL (e.g., https://.ngrok.io/webhook/slack).
    • Paste this URL into the Event Subscription section of your Slack app manifest.
  4. Install your Slack app in a workspace, and copy the Bot Token and Signing Secret into .env:

    SLACK_BOT_TOKEN=your-bot-token
    SLACK_SIGNING_SECRET=your-signing-secret
  5. Add Slack interaction logic to the chatbot:

    typescript
    import { createSlackAdapter } from "@vercel/chat-sdk-slack";
    import { Chat } from "@vercel/chat-sdk";
    
    const slackAdapter = createSlackAdapter({
      token: process.env.SLACK_BOT_TOKEN as string,
      signingSecret: process.env.SLACK_SIGNING_SECRET as string,
    });
    
    const bot = new Chat({
      adapters: [slackAdapter],
    });
    
    bot.on("message", async (message) => {
      if (message.text.includes("prep")) {
        const response = await meetingAssistant.generate({ prompt: "Please prepare for the meeting." });
        await message.reply(response.text);
      }
    });
    
    export default bot;
  6. Restart the server and test your bot in Slack by mentioning it with a message like:

    @Meeting Assistant Prepare for a meeting

    Verify the bot successfully responds.

Adding Intelligent Research Tools

You'll next add tools to enable your agent to research using the Exa.ai API.

steps

  1. Install the Exa.js library:

    bash
    npm install exa.js
  2. Add your EXA_API_KEY to .env:

    EXA_API_KEY=your_exa_api_key
  3. Create a tool using mastra createTool and Zod for schema validation:

    typescript
    import { createTool } from "mastra";
    import { z } from "zod";
    import Exa from "exa.js";
    
    const exa = new Exa(String(process.env.EXA_API_KEY));
    
    const searchWeb = createTool({
      id: "search-web",
      description: "Search the web for information on a person, company, or topic.",
      inputSchema: z.object({
        query: z.string(),
        numResults: z.number().default(5),
      }),
      execute: async (input) => {
        const results = await exa.searchAndContents(input.query, input.numResults);
        return results.map((r) => ({
          title: r.title,
          url: r.url,
          text: r.text,
        }));
      },
    });
    
    export default searchWeb;
  4. Use the tool in your agent's setup:

    typescript
    import searchWeb from "./tools/research-tools";
    
    const meetingAssistant = createAgent({
      id: "meeting-assistant",
      name: "Meeting Assistant",
      tools: [searchWeb],
      model: "anthropic:claude-sonnet-4.5",
    });
  5. Test the new tool in Slack:

    @Meeting Assistant Research information about OpenAI

    Confirm that your agent responds with meaningful research results using Exa.ai.

Enhancing Agents With Memory Systems

Enable memory to provide contextually-aware responses.

steps

  1. Configure episodic memory to track recent chat history:

    typescript
    import { MessageHistoryMemory } from "mastra";
    
    meetingAssistant.memory = new MessageHistoryMemory({ pastMessageCount: 10 });
  2. Add working memory for storing user preferences:

    typescript
    import { WorkingMemory } from "mastra";
    
    meetingAssistant.workingMemory = new WorkingMemory({
      enabled: true,
      template: `
        {
          "name": "<user's name>",
          "role": "<user's role>",
          "company": "<user's company>",
          "topicsOfInterest": []
        }
      `,
    });
  3. Enable semantic memory with embedding-based recall:

    typescript
    import { SemanticMemoryPersistence, FastEmbed } from "mastra";
    
    meetingAssistant.semanticMemory = new SemanticMemoryPersistence({
      embedder: new FastEmbed(),
      options: { topK: 3, messageRange: 2 },
    });
  4. Test memory features in Slack by asking:

    @Meeting Assistant Who have you done research on for me?

Setting Up Event-Based Workflows With a Custom Scheduler

Event-driven workflows can make agents more autonomous by reacting to external triggers like scheduled events or webhooks.

steps

  1. Install Drizzle ORM for database abstraction and SQLite for storage:

    bash
    npm install drizzle-orm sqlite
  2. Create a new SQLite database with a scheduled_tasks schema:

    typescript
    import { drizzle } from "drizzle-orm";
    import schema from "./schema";
    
    export const db = drizzle("sqlite.db", { schema });
  3. Define a task scheduling system:

    typescript
    const TaskHandlers = new Map<string, (payload: any) => Promise<void>>();
    export function registerTask(type: string, handler: (payload: any) => Promise<void>) {
      TaskHandlers.set(type, handler);
    }
    
    export async function scheduleTask({
      type,
      payload,
      scheduledAt,
    }: {
      type: string;
      payload: any;
      scheduledAt: string;
    }) {
      const task = { type, payload: JSON.stringify(payload), scheduledAt, status: "pending" };
      await db.insert("scheduled_tasks").values(task);
    }
  4. Register and process the tasks:

    typescript
    registerTask("follow-up", async (payload) => {
      await bot.postToThread(payload.threadId, payload.message);
    });
    
    setInterval(async () => {
      const tasks = await db
        .selectFrom("scheduled_tasks")
        .where("status", "=", "pending")
        .where("scheduledAt", "<=", new Date().toISOString())
        .selectAll()
        .execute();
    
      for (const task of tasks) {
        const handler = TaskHandlers.get(task.type);
        if (handler) {
          await handler(JSON.parse(task.payload));
          await db
            .updateTable("scheduled_tasks")
            .set({ status: "completed" })
            .where("id", "=", task.id)
            .execute();
        }
      }
    }, 30000); // every 30 seconds
  5. Schedule a follow-up reminder within cal.com webhook:

    typescript
    if (event.action === "booking.created") {
      await scheduleTask({
        type: "follow-up",
        payload: { threadId: threadId, message: "How did your meeting with [attendee name] go?" },
        scheduledAt: new Date(event.endTime).toISOString(),
      });
    }

    Test the functionality by scheduling a meeting, then verifying the follow-up occurs at the expected time.

FAQ

What is mastra createTool?

mastra createTool is a utility in Mastra that allows developers to define custom tools, using JSON input schemas validated with libraries like Zod, which agents can invoke to extend their functionality.

How do I connect MCP to Mastra agents?

To connect MCP to Mastra agents, configure the MCP client in the Mastra setup file by specifying the MCP server URL and authentication parameters. Then, set up inter-agent communication through Mastra's built-in functions for agent collaboration.

Can I use a different LLM provider with Mastra?

Yes. Mastra supports multiple LLM providers, including Anthropic, OpenAI, Groq, Google, and others. You can swap model configurations as needed during the project setup.

How does Mastra handle memory types?

Mastra supports four memory types:

  • Episodic Memory: Tracks recent messages in a conversation.
  • Working Memory: Keeps a persistent user profile with structured attributes.
  • Semantic Memory: Uses embeddings to recall relevant information from old conversations.
  • Observational Memory: Condenses and reflects observations over time.

Official reference: Mastra documentation.