How to integrate Close MCP with Mastra AI

Connect Mastra AI to Close MCP. Create a new sales lead for acme corp, log an outbound call with follow-up notes, and more using natural language, with authentication handled for you.

Close logoClose
Api Key

Close is a CRM platform built for sales teams, combining calling, email automation, and predictive dialers. It streamlines sales workflows and boosts productivity with all-in-one communication tools.

70 Tools45 Triggers

Introduction

This guide walks you through connecting Close to Mastra AI using the Composio tool router. By the end, you'll have a working Close agent that can create leads and tasks, log calls and emails, send SMS messages, track opportunities, and review activities through natural language commands.

This guide will help you understand how to give your Mastra AI agent real control over a Close account through Composio's Close MCP server.

Before we dive in, let's take a quick look at the key ideas and tools involved.

Also integrate Close with

TL;DR

Here's what you'll learn:
  • Set up your environment so Mastra, OpenAI, and Composio work together
  • Create a Tool Router session in Composio that exposes Close tools
  • Connect Mastra's MCP client to the Composio generated MCP URL
  • Fetch Close tool definitions and attach them as a toolset
  • Build a Mastra agent that can reason, call tools, and return structured results
  • Run an interactive CLI where you can chat with your Close agent

What is Mastra AI?

Mastra AI is a TypeScript framework for building AI agents with tool support. It provides a clean API for creating agents that can use external services through MCP.

Key features include:

  • MCP Client: Built-in support for Model Context Protocol servers
  • Toolsets: Organize tools into logical groups
  • Step Callbacks: Monitor and debug agent execution
  • OpenAI Integration: Works with OpenAI models via @ai-sdk/openai

What is the Close MCP server, and what's possible with it?

The Close MCP server is an implementation of the Model Context Protocol that connects your AI agent and assistants like Claude, Cursor, etc directly to your Close account. It provides structured and secure access to your sales pipeline, so your agent can perform actions like creating leads, logging calls, sending SMS, managing tasks, and retrieving notes on your behalf.

  • Lead creation and management: Instantly create new leads with company details and contact information, keeping your CRM fresh and organized without manual entry.
  • Automated call logging: Have your agent log both inbound and outbound calls to specific leads, capturing call notes, durations, and recordings for seamless tracking.
  • SMS communication: Direct your agent to send or log SMS messages with leads, enabling timely follow-ups and improved client engagement.
  • Task management: Let your agent create tasks, set due dates, and track completion, making sure nothing slips through the cracks in your sales workflow.
  • Note retrieval and analysis: Fetch notes by lead or date, so your agent can summarize past interactions, surface important insights, or prepare for upcoming calls.

What is the Composio tool router, and how does it fit here?

What is Composio SDK?

Composio's Composio SDK helps agents find the right tools for a task at runtime. You can plug in multiple toolkits (like Gmail, HubSpot, and GitHub), and the agent will identify the relevant app and action to complete multi-step workflows. This can reduce token usage and improve the reliability of tool calls. Read more here: Getting started with Composio SDK

The tool router generates a secure MCP URL that your agents can access to perform actions.

How the Composio SDK works

The Composio SDK follows a three-phase workflow:

  1. Discovery: Searches for tools matching your task and returns relevant toolkits with their details.
  2. Authentication: Checks for active connections. If missing, creates an auth config and returns a connection URL via Auth Link.
  3. Execution: Executes the action using the authenticated connection.

Step-by-step Guide

Step by step09 STEPS
1

Prerequisites

Before starting, make sure you have:
  • Node.js 18 or higher
  • A Composio account with an active API key
  • An OpenAI API key
  • Basic familiarity with TypeScript
2

Getting API Keys for OpenAI and Composio

OpenAI API Key
  • Go to the OpenAI dashboard and create an API key.
  • You need credits or a connected billing setup to use the models.
  • Store the key somewhere safe.
Composio API Key
  • Log in to the Composio dashboard.
  • Go to Settings and copy your API key.
  • This key lets your Mastra agent talk to Composio and reach Close through MCP.
3

Install dependencies

bash
npm install @composio/core @mastra/core @mastra/mcp @ai-sdk/openai dotenv

Install the required packages.

What's happening:

  • @composio/core is the Composio SDK for creating MCP sessions
  • @mastra/core provides the Agent class
  • @mastra/mcp is Mastra's MCP client
  • @ai-sdk/openai is the model wrapper for OpenAI
  • dotenv loads environment variables from .env
4

Set up environment variables

bash
COMPOSIO_API_KEY=your_composio_api_key_here
COMPOSIO_USER_ID=your_user_id_here
OPENAI_API_KEY=your_openai_api_key_here

Create a .env file in your project root.

What's happening:

  • COMPOSIO_API_KEY authenticates your requests to Composio
  • COMPOSIO_USER_ID tells Composio which user this session belongs to
  • OPENAI_API_KEY lets the Mastra agent call OpenAI models
5

Import libraries and validate environment

typescript
import "dotenv/config";
import { openai } from "@ai-sdk/openai";
import { Agent } from "@mastra/core/agent";
import { MCPClient } from "@mastra/mcp";
import { Composio } from "@composio/core";
import * as readline from "readline";

import type { AiMessageType } from "@mastra/core/agent";

const openaiAPIKey = process.env.OPENAI_API_KEY;
const composioAPIKey = process.env.COMPOSIO_API_KEY;
const composioUserID = process.env.COMPOSIO_USER_ID;

if (!openaiAPIKey) throw new Error("OPENAI_API_KEY is not set");
if (!composioAPIKey) throw new Error("COMPOSIO_API_KEY is not set");
if (!composioUserID) throw new Error("COMPOSIO_USER_ID is not set");

const composio = new Composio({
  apiKey: composioAPIKey as string,
});
What's happening:
  • dotenv/config auto loads your .env so process.env.* is available
  • openai gives you a Mastra compatible model wrapper
  • Agent is the Mastra agent that will call tools and produce answers
  • MCPClient connects Mastra to your Composio MCP server
  • Composio is used to create a Tool Router session
6

Create a Tool Router session for Close

typescript
async function main() {
  const session = await composio.create(
    composioUserID as string,
    {
      toolkits: ["close"],
    },
  );

  const composioMCPUrl = session.mcp.url;
  console.log("Close MCP URL:", composioMCPUrl);
What's happening:
  • create spins up a short-lived MCP HTTP endpoint for this user
  • The toolkits array contains "close" for Close access
  • session.mcp.url is the MCP URL that Mastra's MCPClient will connect to
7

Configure Mastra MCP client and fetch tools

typescript
const mcpClient = new MCPClient({
    id: composioUserID as string,
    servers: {
      nasdaq: {
        url: new URL(composioMCPUrl),
        requestInit: {
          headers: session.mcp.headers,
        },
      },
    },
    timeout: 30_000,
  });

console.log("Fetching MCP tools from Composio...");
const composioTools = await mcpClient.getTools();
console.log("Number of tools:", Object.keys(composioTools).length);
What's happening:
  • MCPClient takes an id for this client and a list of MCP servers
  • The headers property includes the x-api-key for authentication
  • getTools fetches the tool definitions exposed by the Close toolkit
8

Create the Mastra agent

typescript
const agent = new Agent({
    name: "close-mastra-agent",
    instructions: "You are an AI agent with Close tools via Composio.",
    model: "openai/gpt-5",
  });
What's happening:
  • Agent is the core Mastra agent
  • name is just an identifier for logging and debugging
  • instructions guide the agent to use tools instead of only answering in natural language
  • model uses openai("gpt-5") to configure the underlying LLM
9

Set up interactive chat interface

typescript
let messages: AiMessageType[] = [];

console.log("Chat started! Type 'exit' or 'quit' to end.\n");

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  prompt: "> ",
});

rl.prompt();

rl.on("line", async (userInput: string) => {
  const trimmedInput = userInput.trim();

  if (["exit", "quit", "bye"].includes(trimmedInput.toLowerCase())) {
    console.log("\nGoodbye!");
    rl.close();
    process.exit(0);
  }

  if (!trimmedInput) {
    rl.prompt();
    return;
  }

  messages.push({
    id: crypto.randomUUID(),
    role: "user",
    content: trimmedInput,
  });

  console.log("\nAgent is thinking...\n");

  try {
    const response = await agent.generate(messages, {
      toolsets: {
        close: composioTools,
      },
      maxSteps: 8,
    });

    const { text } = response;

    if (text && text.trim().length > 0) {
      console.log(`Agent: ${text}\n`);
        messages.push({
          id: crypto.randomUUID(),
          role: "assistant",
          content: text,
        });
      }
    } catch (error) {
      console.error("\nError:", error);
    }

    rl.prompt();
  });

  rl.on("close", async () => {
    console.log("\nSession ended.");
    await mcpClient.disconnect();
    process.exit(0);
  });
}

main().catch((err) => {
  console.error("Fatal error:", err);
  process.exit(1);
});
What's happening:
  • messages keeps the full conversation history in Mastra's expected format
  • agent.generate runs the agent with conversation history and Close toolsets
  • maxSteps limits how many tool calls the agent can take in a single run
  • onStepFinish is a hook that prints intermediate steps for debugging

Complete Code

Here's the complete code to get you started with Close and Mastra AI:

typescript
import "dotenv/config";
import { openai } from "@ai-sdk/openai";
import { Agent } from "@mastra/core/agent";
import { MCPClient } from "@mastra/mcp";
import { Composio } from "@composio/core";
import * as readline from "readline";

import type { AiMessageType } from "@mastra/core/agent";

const openaiAPIKey = process.env.OPENAI_API_KEY;
const composioAPIKey = process.env.COMPOSIO_API_KEY;
const composioUserID = process.env.COMPOSIO_USER_ID;

if (!openaiAPIKey) throw new Error("OPENAI_API_KEY is not set");
if (!composioAPIKey) throw new Error("COMPOSIO_API_KEY is not set");
if (!composioUserID) throw new Error("COMPOSIO_USER_ID is not set");

const composio = new Composio({ apiKey: composioAPIKey as string });

async function main() {
  const session = await composio.create(composioUserID as string, {
    toolkits: ["close"],
  });

  const composioMCPUrl = session.mcp.url;

  const mcpClient = new MCPClient({
    id: composioUserID as string,
    servers: {
      close: {
        url: new URL(composioMCPUrl),
        requestInit: {
          headers: session.mcp.headers,
        },
      },
    },
    timeout: 30_000,
  });

  const composioTools = await mcpClient.getTools();

  const agent = new Agent({
    name: "close-mastra-agent",
    instructions: "You are an AI agent with Close tools via Composio.",
    model: "openai/gpt-5",
  });

  let messages: AiMessageType[] = [];

  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    prompt: "> ",
  });

  rl.prompt();

  rl.on("line", async (input: string) => {
    const trimmed = input.trim();
    if (["exit", "quit"].includes(trimmed.toLowerCase())) {
      rl.close();
      return;
    }

    messages.push({ id: crypto.randomUUID(), role: "user", content: trimmed });

    const { text } = await agent.generate(messages, {
      toolsets: { close: composioTools },
      maxSteps: 8,
    });

    if (text) {
      console.log(`Agent: ${text}\n`);
      messages.push({ id: crypto.randomUUID(), role: "assistant", content: text });
    }

    rl.prompt();
  });

  rl.on("close", async () => {
    await mcpClient.disconnect();
    process.exit(0);
  });
}

main();

Conclusion

You've built a Mastra AI agent that can interact with Close through Composio's Tool Router. You can extend this further by:
  • Adding other toolkits like Gmail, Slack, or GitHub
  • Building a web-based chat interface around this agent
  • Using multiple MCP endpoints to enable cross-app workflows
TOOLS & TRIGGERS

Supported Tools and Triggers

Every Close action and event your agent gets out of the box.

Create Call

Creates a new call record in Close.

Create Contact

Create a contact on an existing Close lead, or create a new lead named after the contact when lead_id is omitted.

Create Custom Activity

Create a draft or published custom activity instance on a Close lead.

Create Custom Field

Create a custom-field definition for Close leads or opportunities.

Create Email

Create or log a Close email activity.

Create Lead

Creates a new lead in Close CRM.

Create Note

Create a plaintext or rich-text note activity on a Close lead.

Create Opportunity

Create a Close sales opportunity on an existing lead, or create an Untitled lead for it when lead_id is omitted.

Create Sequence

Create a Close sales sequence with ordered call or email steps.

Create SMS Message

Create an SMS activity on a Close lead to log received, sent, or draft messages, or to send or schedule a message.

Create Task

Create a Close task associated with a lead, using task text and an actionable date, with optional completion status and assignment.

Delete Activity Custom Field

Permanently delete one Close activity custom-field definition by ID.

Delete Call

This tool allows you to delete a specific call activity in Close.

Delete Custom Activity

Permanently delete one Close custom activity instance by ID.

Delete Email

This tool allows you to permanently delete a specific email activity in Close.

Delete Lead

Permanently delete one Close lead by ID.

Delete Lead Custom Field

Permanently delete a Close lead custom-field definition and its values.

Delete Note

Permanently delete one Close note activity by ID.

Delete Opportunity

Permanently delete one Close opportunity by ID.

Delete Opportunity Status

Permanently delete an unused Close opportunity status; this cannot be undone.

Delete SMS

Permanently delete one Close SMS activity by ID.

Download File

Download bytes from a provider-issued Close Files API URL.

Get Activity Custom Field

Retrieve one Close activity custom-field definition by ID.

Get Call

Retrieve one Close call activity by ID with optional field projection.

Get Contact

Retrieve one Close contact by ID, optionally limiting returned fields.

Get Created Activity

Retrieve one Close lead-creation activity with optional field projection.

Get Email

Retrieve one Close email activity by ID with optional field projection.

Get Event

Retrieve one Close event-log record by event ID.

Get Lead

Retrieve one Close lead by ID with optional field projection.

Get Lead Custom Field

Retrieve one Close lead custom-field definition by ID.

Get Notes

This tool retrieves a list of note activities from Close.

Get Note by ID

Retrieve one Close note activity by ID with optional field projection.

Get Opportunity

Retrieve one Close opportunity by ID with optional field projection.

Get Sequence

Retrieve one Close sequence by ID with its workflow configuration.

Get Shared Custom Field

Retrieve one Close shared custom-field definition by ID.

Get SMS

Retrieve one Close SMS activity by ID with optional field projection.

Get Task

Retrieve one Close task by ID with optional field projection.

Get User

Retrieve one Close organization user by ID with optional field projection.

List Activities

Lists one page of Close email or call activities using documented filters.

List Contacts

List Close contacts with lead filtering, projection, and offset pagination.

List Custom Activities

List Close custom activity instances with filters and offset pagination.

List Custom Activity Types

This tool lists the custom activity types defined in a Close organization, returning each type's id and name.

List Custom Objects

List custom object instances attached to a Close lead, optionally by type.

List Custom Object Types

This tool lists the custom object types defined in a Close organization, returning each type's id and name.

List Events

List Close change events with filters, projection, and cursor pagination.

List Lead Custom Fields

List Close lead custom-field definitions with projection and pagination.

List Leads

List or text-search Close leads with field projection and offset pagination.

List Lead Statuses

List the configured lead statuses for the connected organization.

List Opportunities

List a page of Close opportunities with offset pagination.

List Opportunity Custom Fields

List Close opportunity custom-field definitions with projection and pagination.

List Opportunity Statuses

List the configured opportunity statuses for the connected organization.

List Outcomes

List Close call and meeting outcomes with projection and offset pagination.

List Pipelines

List pipelines and their ordered opportunity statuses.

List Sequences

List Close sales sequences with offset pagination.

List Sequence Subscriptions

List Close sequence subscriptions.

List Tasks

List Close tasks using task, assignee, lead, date, and pagination filters.

List Unified Activities

List one page of Close activities across all types and matching filters.

List Users

List users in the connected Close organization with offset pagination.

List Webhook Subscriptions

List webhook subscriptions configured for the Close organization.

Search Data

Search Close objects using the structured Advanced Filtering query language.

Update Activity Custom Field

Update mutable settings of one Close activity custom-field definition.

Update Contact

Update standard and custom fields on one existing Close contact.

Update Custom Activity

Update standard and custom fields on one Close custom activity instance.

Update Email

Update an existing Close email activity.

Update Lead Custom Field

Update a Close lead custom-field definition.

Update Note

Update the mutable content and metadata of one Close note activity.

Update Opportunity

Update standard and custom fields on one Close opportunity.

Update Sequence

Update a Close sequence name or replace its entire ordered step list.

Update Task

Update assignment, actionable date, or completion state on a Close task; lead tasks can also update text.

Who Am I

Return the identity (email, name) of the connected Close account.

FAQ

Frequently asked questions

Start with Close.It takes 30 seconds.

Managed auth, hosted MCP servers, and every Close tool your agent needs.Free to start.

Start building