How to integrate Elastic Email MCP with LlamaIndex

This guide walks you through connecting Elastic Email to LlamaIndex using the Composio tool router. By the end, you'll have a working Elastic Email agent that can add your contacts, create contact lists, build campaigns, make templates, and set up domains through natural language commands. This guide will help you understand how to give your LlamaIndex agent real control over a Elastic Email account through Composio's Elastic Email MCP server. Before we dive in, let's take a quick look at the key ideas and tools involved.

Elastic Email logoElastic Email
Api Key

Elastic Email is an email delivery and marketing platform for transactional emails, campaigns, contacts, and reporting. Use it to send reliable emails, manage audiences, verify addresses, and track performance.

91 Tools

Introduction

This guide walks you through connecting Elastic Email to LlamaIndex using the Composio tool router. By the end, you'll have a working Elastic Email agent that can add your contacts, create contact lists, build campaigns, make templates, and set up domains through natural language commands.

This guide will help you understand how to give your LlamaIndex agent real control over a Elastic Email account through Composio's Elastic Email MCP server.

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

Also integrate Elastic Email with

TL;DR

Here's what you'll learn:
  • Set your OpenAI and Composio API keys
  • Install LlamaIndex and Composio packages
  • Create a Composio Tool Router session for Elastic Email
  • Connect LlamaIndex to the Elastic Email MCP server
  • Build a Elastic Email-powered agent using LlamaIndex
  • Interact with Elastic Email through natural language

What is LlamaIndex?

LlamaIndex is a data framework for building LLM applications. It provides tools for connecting LLMs to external data sources and services through agents and tools.

Key features include:

  • ReAct Agent: Reasoning and acting pattern for tool-using agents
  • MCP Tools: Native support for Model Context Protocol
  • Context Management: Maintain conversation context across interactions
  • Async Support: Built for async/await patterns

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

The Elastic Email MCP server is an implementation of the Model Context Protocol that connects your AI agent and assistants like Claude, Cursor, etc directly to your Elastic Email account. It provides structured and secure access to your contacts, campaigns, templates, domains, delivery records, and account settings, so your agent can organize audiences, create campaigns, build templates, check delivery status, and manage sending domains on your behalf.

  • Contact and audience management: Have your agent add or remove contacts, create contact lists, assign existing contacts to lists, and build dynamic segments using defined rules.
  • Campaign and template creation: Direct your agent to create campaigns, prepare reusable email templates, retrieve campaign details, or remove campaigns and templates you no longer need.
  • Delivery tracking and message review: Let the agent check email job status, review recipient delivery details, and retrieve rendered message previews with attachment information.
  • Domain and inbound email setup: Instruct your agent to add sending or tracking domains, review their DNS and certificate state, check domain restrictions, and configure inbound email routes.
  • Suppression, verification, and event management: Have your agent maintain suppression records, retrieve saved email verification results, monitor export jobs, and create or remove webhooks for selected email events.

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 step10 STEPS
1

Prerequisites

Before you begin, make sure you have:
  • Python 3.8/Node 16 or higher installed
  • A Composio account with the API key
  • An OpenAI API key
  • A Elastic Email account and project
  • Basic familiarity with async Python/Typescript
2

Getting API Keys for OpenAI, Composio, and Elastic Email

OpenAI API key (OPENAI_API_KEY)
  • Go to the OpenAI dashboard
  • Create an API key if you don't have one
  • Assign it to OPENAI_API_KEY in .env
Composio API key and user ID
  • Log into the Composio dashboard
  • Copy your API key from Settings
    • Use this as COMPOSIO_API_KEY
  • Pick a stable user identifier (email or ID)
    • Use this as COMPOSIO_USER_ID
3

Installing dependencies

npm install @composio/llamaindex @llamaindex/openai @llamaindex/tools @llamaindex/workflow dotenv

Create a new Typescript project and install the necessary dependencies:

  • @composio/llamaindex: Composio's LlamaIndex integration
  • @llamaindex/openai: OpenAI LLM integration
  • @llamaindex/tools: MCP client for LlamaIndex
  • @llamaindex/workflow: Workflow framework for LlamaIndex
  • dotenv: Environment variable management
4

Set environment variables

bash
OPENAI_API_KEY=your-openai-api-key
COMPOSIO_API_KEY=your-composio-api-key
COMPOSIO_USER_ID=your-user-id

Create a .env file in your project root:

These credentials will be used to:

  • Authenticate with OpenAI's GPT-5 model
  • Connect to Composio's Tool Router
  • Identify your Composio user session for Elastic Email access
5

Import modules

import "dotenv/config";
import readline from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";

import { Composio } from "@composio/core";

import { mcp } from "@llamaindex/tools";
import { agent as createAgent } from "@llamaindex/workflow";
import { openai } from "@llamaindex/openai";

dotenv.config();

Create a new file called elastic email_llamaindex_agent.ts and import the required modules:

Key imports:

  • dotenv.config loads .env at runtime
  • readline gives us a simple CLI chat loop
  • Composio is the main Composio SDK client
  • mcp connects to an MCP endpoint
  • createAgent builds a LlamaIndex agent
  • openai configures the LLM backend
6

Load environment variables and initialize Composio

const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
const COMPOSIO_API_KEY = process.env.COMPOSIO_API_KEY;
const COMPOSIO_USER_ID = process.env.COMPOSIO_USER_ID;

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

What's happening:

This ensures missing credentials cause early, clear errors before the agent attempts to initialise.

7

Create a Tool Router session and build the agent function

async function buildAgent() {

  console.log(`Initializing Composio client...${COMPOSIO_USER_ID!}...`);
  console.log(`COMPOSIO_USER_ID: ${COMPOSIO_USER_ID!}...`);

  const composio = new Composio({
    apiKey: COMPOSIO_API_KEY,
    provider: new LlamaindexProvider(),
  });

  const session = await composio.create(
    COMPOSIO_USER_ID!,
    {
      toolkits: ["elastic_email"],
    },
  );

  const mcpUrl = session.mcp.url;
  console.log(`Composio Tool Router MCP URL: ${mcpUrl}`);

  const server = mcp({
    url: mcpUrl,
    clientName: "composio_tool_router_with_llamaindex",
    requestInit: {
      headers: {
        "x-api-key": COMPOSIO_API_KEY!,
      },
    },
    // verbose: true,
  });

  const tools = await server.tools();

  const llm = openai({ apiKey: OPENAI_API_KEY, model: "gpt-5" });

  const agent = createAgent({
    name: "composio_tool_router_with_llamaindex",
        description : "An agent that uses Composio Tool Router MCP tools to perform actions.",
    systemPrompt:
      "You are a helpful assistant connected to Composio Tool Router."+
"Use the available tools to answer user queries and perform Elastic Email actions." ,
    llm,
    tools,
  });

  return agent;
}

What's happening here:

  • We create a Composio client using your API key and configure it with the LlamaIndex provider
  • We then create a tool router MCP session for your user, specifying the toolkits we want to use (in this case, elastic email)
  • The session returns an MCP HTTP endpoint URL that acts as a gateway to all your configured tools
  • LlamaIndex will connect to this endpoint to dynamically discover and use the available Elastic Email tools.
  • The MCP tools are mapped to LlamaIndex-compatible tools and plug them into the Agent.
8

Create an interactive chat loop

async function chatLoop(agent: ReturnType<typeof createAgent>) {
  const rl = readline.createInterface({ input, output });

  console.log("Type 'quit' or 'exit' to stop.");

  while (true) {
    let userInput: string;

    try {
      userInput = (await rl.question("\nYou: ")).trim();
    } catch {
      console.log("\nAgent: Bye!");
      break;
    }

    if (!userInput) {
      continue;
    }

    const lower = userInput.toLowerCase();
    if (lower === "quit" || lower === "exit") {
      console.log("Agent: Bye!");
      break;
    }

    try {
      process.stdout.write("Agent: ");

      const stream = agent.runStream(userInput);
      let finalResult: any = null;

      for await (const event of stream) {
        // The event.data contains the streamed content
        const data: any = event.data;

        // Check for streaming delta content
        if (data?.delta) {
          process.stdout.write(data.delta);
        }

        // Store final result for fallback
        if (data?.result || data?.message) {
          finalResult = data;
        }
      }

      // If no streaming happened, show the final result
      if (finalResult) {
        const answer =
          finalResult.result ??
          finalResult.message?.content ??
          finalResult.message ??
          "";
        if (answer && typeof answer === "string" && !answer.includes("[object")) {
          process.stdout.write(answer);
        }
      }

      console.log(); // New line after streaming completes
    } catch (err: any) {
      console.error("\nAgent error:", err?.message ?? err);
    }
  }

  rl.close();
}

What's happening:

  • We're creating a direct terminal interface to chat with Elastic Email
  • The LLM's responses are streamed to the CLI for faster interaction.
  • The agent uses context to maintain conversation history
  • The agent processes the request, selects appropriate Elastic Email tools, and returns a result
  • We extract the answer from the result data structure and display it to the user
  • You can type 'quit' or 'exit' to stop the chat loop gracefully
  • Agent responses and any errors are streamed in a clear, readable format
9

Define the main entry point

async function main() {
  try {
    const agent = await buildAgent();
    await chatLoop(agent);
  } catch (err) {
    console.error("Failed to start agent:", err);
    process.exit(1);
  }
}

main();

What's happening here:

  • We're orchestrating the entire application flow
  • The agent gets built with proper error handling
  • Then we kick off the interactive chat loop so you can start talking to Elastic Email
10

Run the agent

npx ts-node llamaindex-agent.ts

When prompted, authenticate and authorise your agent with Elastic Email, then start asking questions.

Complete Code

Here's the complete code to get you started with Elastic Email and LlamaIndex:

import "dotenv/config";
import readline from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";

import { Composio } from "@composio/core";
import { LlamaindexProvider } from "@composio/llamaindex";

import { mcp } from "@llamaindex/tools";
import { agent as createAgent } from "@llamaindex/workflow";
import { openai } from "@llamaindex/openai";

dotenv.config();

const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
const COMPOSIO_API_KEY = process.env.COMPOSIO_API_KEY;
const COMPOSIO_USER_ID = process.env.COMPOSIO_USER_ID;

if (!OPENAI_API_KEY) {
    throw new Error("OPENAI_API_KEY is not set in the environment");
  }
if (!COMPOSIO_API_KEY) {
    throw new Error("COMPOSIO_API_KEY is not set in the environment");
  }
if (!COMPOSIO_USER_ID) {
    throw new Error("COMPOSIO_USER_ID is not set in the environment");
  }

async function buildAgent() {

  console.log(`Initializing Composio client...${COMPOSIO_USER_ID!}...`);
  console.log(`COMPOSIO_USER_ID: ${COMPOSIO_USER_ID!}...`);

  const composio = new Composio({
    apiKey: COMPOSIO_API_KEY,
    provider: new LlamaindexProvider(),
  });

  const session = await composio.create(
    COMPOSIO_USER_ID!,
    {
      toolkits: ["elastic_email"],
    },
  );

  const mcpUrl = session.mcp.url;
  console.log(`Composio Tool Router MCP URL: ${mcpUrl}`);

  const server = mcp({
    url: mcpUrl,
    clientName: "composio_tool_router_with_llamaindex",
    requestInit: {
      headers: {
        "x-api-key": COMPOSIO_API_KEY!,
      },
    },
    // verbose: true,
  });

  const tools = await server.tools();

  const llm = openai({ apiKey: OPENAI_API_KEY, model: "gpt-5" });

  const agent = createAgent({
    name: "composio_tool_router_with_llamaindex",
    description:
      "An agent that uses Composio Tool Router MCP tools to perform actions.",
    systemPrompt:
      "You are a helpful assistant connected to Composio Tool Router."+
"Use the available tools to answer user queries and perform Elastic Email actions." ,
    llm,
    tools,
  });

  return agent;
}

async function chatLoop(agent: ReturnType<typeof createAgent>) {
  const rl = readline.createInterface({ input, output });

  console.log("Type 'quit' or 'exit' to stop.");

  while (true) {
    let userInput: string;

    try {
      userInput = (await rl.question("\nYou: ")).trim();
    } catch {
      console.log("\nAgent: Bye!");
      break;
    }

    if (!userInput) {
      continue;
    }

    const lower = userInput.toLowerCase();
    if (lower === "quit" || lower === "exit") {
      console.log("Agent: Bye!");
      break;
    }

    try {
      process.stdout.write("Agent: ");

      const stream = agent.runStream(userInput);
      let finalResult: any = null;

      for await (const event of stream) {
        // The event.data contains the streamed content
        const data: any = event.data;

        // Check for streaming delta content
        if (data?.delta) {
          process.stdout.write(data.delta);
        }

        // Store final result for fallback
        if (data?.result || data?.message) {
          finalResult = data;
        }
      }

      // If no streaming happened, show the final result
      if (finalResult) {
        const answer =
          finalResult.result ??
          finalResult.message?.content ??
          finalResult.message ??
          "";
        if (answer && typeof answer === "string" && !answer.includes("[object")) {
          process.stdout.write(answer);
        }
      }

      console.log(); // New line after streaming completes
    } catch (err: any) {
      console.error("\nAgent error:", err?.message ?? err);
    }
  }

  rl.close();
}

async function main() {
  try {
    const agent = await buildAgent();
    await chatLoop(agent);
  } catch (err: any) {
    console.error("Failed to start agent:", err?.message ?? err);
    process.exit(1);
  }
}

main();

Conclusion

You've successfully connected Elastic Email to LlamaIndex through Composio's Tool Router MCP layer. Key takeaways:
  • Tool Router dynamically exposes Elastic Email tools through an MCP endpoint
  • LlamaIndex's ReActAgent handles reasoning and orchestration; Composio handles integrations
  • The agent becomes more capable without increasing prompt size
  • Async Python provides clean, efficient execution of agent workflows
You can easily extend this to other toolkits like Gmail, Notion, Stripe, GitHub, and more by adding them to the toolkits parameter.
TOOLS

Supported Tools

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

Add Contact List Members

Add selected existing contacts to a named Elastic Email contact list.

Add Contacts

Add up to 1000 Elastic Email contacts and optionally assign them to existing lists.

Add Suppressions

Add up to 1000 email addresses to Elastic Email's bounce, complaint, or unsubscribe suppression records.

Adjust Subaccount Credits

Add or remove email credits from a managed Elastic Email subaccount.

Check Domain Restriction

Check whether Elastic Email restricts a domain or identifies it as a free-provider domain.

Create Campaign

Create a campaign.

Create Contact List

Create a contact list, optionally with initial email members.

Create Domain

Create a sending or tracking domain in the connected Elastic Email account.

Create Inbound Route

Create a new inbound email route.

Create Segment

Create a dynamic contact segment from a name and SQL-like rule.

Create Subaccount

Create a managed subaccount with optional initial email settings.

Create Template

Create a new email template with its name, content, and scope.

Create Webhook

Create a webhook subscription for selected email events.

Delete Campaign

Delete one campaign by exact name.

Delete Contact

Delete one contact by exact email address.

Delete Contact List

Delete one contact list by exact name.

Delete Contacts

Permanently delete 1-10 contacts by exact email address.

Delete Domain

Delete one configured domain from the connected Elastic Email account.

Delete Email Verification

Delete one saved email verification result by exact email address.

Delete File

Permanently delete one stored file by its exact name.

Delete Inbound Route

Permanently delete one inbound email route by its public ID.

Delete Segment

Delete one Elastic Email dynamic contact segment by its exact name.

Delete Suppression

Remove one email address from Elastic Email suppression records.

Delete Template

Delete one Elastic Email template by its exact name.

Delete Verification File

Delete one uploaded verification file job by its VerificationID.

Delete Webhook

Delete one webhook subscription by its public WebhookID.

Download File

Download a stored Elastic Email file by its exact name.

Download Verification File Results

Download the ZIP result archive for a completed verification file job.

Get Campaign

Return one Elastic Email campaign by its exact name.

Get Contact

Return one Elastic Email contact by email address.

Get Contact Export Status

Get the current status of an asynchronous contact export job.

Get Contact List

Return one Elastic Email contact list by its exact name.

Get Credential Metadata

Get one Elastic Email API-key or SMTP credential's audit metadata without returning its secret token.

Get Domain

Return detailed DNS and certificate state for one Elastic Email domain.

Get Email Job Status

Return delivery status and optional recipient-level details for an Elastic Email transaction.

Get Email Preview

Return the rendered preview, attachment metadata, and delivery status for an Elastic Email message ID.

Get Email Verification

Return the saved Elastic Email verification result for one email address.

Get Event Export Status

Get the current status of an asynchronous global or channel event export.

Get File Info

Return metadata for a stored Elastic Email file without downloading it.

Get Inbound Route

Return one Elastic Email inbound route by its public ID.

Get Segment

Return one Elastic Email dynamic contact segment by its exact name.

Get Account Statistics

Return aggregate Elastic Email delivery statistics for a time interval.

Get Statistics Breakdown

Return one Elastic Email campaign or channel delivery summary by exact name.

Get Subaccount

Return one managed Elastic Email subaccount by its login email.

Get Suppression

Return suppression details for one Elastic Email address.

Get Template

Return one Elastic Email template by its exact name.

Get Verification File Results

Return one page of detailed results and metadata for a verification file.

Get Webhook

Return one Elastic Email webhook subscription by its public ID.

Import Contacts

Start an asynchronous contact import using exactly one source: an uploaded CSV file or a provider-accessible CSV URL.

Import Suppressions

Upload CSV content containing bounced, complained, or unsubscribed email addresses for asynchronous suppression import.

List Campaigns

Find campaigns by optional name fragment and return one page.

List Contact List Members

Return one page of contacts in a named Elastic Email contact list.

List Contact Lists

Return one page of contact lists from the connected Elastic Email account.

List Contacts

Return one page of contacts from the connected Elastic Email account.

List Credential Metadata

List API-key or SMTP credential metadata without returning secret tokens.

List Domains

Return all sending and tracking domains configured for the account.

List Email Verifications

Return one page of individual email verification results.

List Files

Return one page of stored files and metadata.

List Inbound Routes

Return all inbound email routing rules in their current order.

List Segments

Return one page of dynamic contact segments.

List Statistics Breakdowns

Return one page of campaign or channel delivery summaries.

List Subaccounts

Return one page of managed subaccounts without credentials or passwords.

List Suppressions

Return one page of all suppressed addresses or only addresses suppressed for bounce, complaint, or unsubscribe reasons.

List Templates

Return one page of templates for the required visibility scopes.

List Verification Files

Return all uploaded verification file jobs and their current statuses.

List Webhooks

Return one page of webhook subscriptions.

Pause Campaign

Pause an active Elastic Email campaign by exact name, cancelling messages that are still waiting to be sent.

Remove Contact List Members

Remove selected contacts from a named Elastic Email contact list without deleting the contacts.

Reorder Inbound Routes

Set the processing order of Elastic Email inbound routing rules.

Search Delivery Events

Search all delivery events, one transaction's events, or one channel's events and return one page.

Send Email

Send an email to one or more recipients using inline content or a saved template.

Send Merge File Email

Send personalized email to recipients listed in a CSV merge-data file.

Send Transactional Email

Send one transactional message with To, CC, and BCC recipient groups.

Set Default Sender Domain

Set the domain of a verified sender email as the account default.

Start Contact Export

Start an asynchronous export of all contacts or contacts selected by one filter rule or a list of email addresses.

Start Event Export

Start an asynchronous export for all events or one channel.

Start Verification File

Start bulk verification for a previously uploaded CSV file.

Trigger Campaign Automation

Enter one contact email into an irreversible automation that may send email or execute later steps and cannot be undone.

Update Campaign

Update an existing active or paused campaign.

Update Contact

Update the first name, last name, or custom fields for one Elastic Email contact.

Update Contact List

Update an Elastic Email contact list by providing at least one change: rename it with NewListName or change whether contacts may unsubscribe with AllowUnsubscribe.

Update Domain

Update certificate, VERP, or custom bounce settings for an Elastic Email domain.

Update Inbound Route

Replace an existing inbound route definition by public ID.

Update Segment

Replace an existing dynamic contact segment's name and rule.

Update Subaccount Email Settings

Replace email limits and sender settings for a managed subaccount.

Update Template

Replace an existing email template's name, content, and scope.

Update Webhook

Update a webhook URL, event notification flags, name, or enabled state.

Upload File

Store a base64-encoded file in the connected Elastic Email account.

Upload Verification File

Upload a CSV containing an email column for a later bulk verification job.

Verify Domain

Ask Elastic Email to re-check a domain's DNS configuration.

Verify Email

Run verification for one email address and return its deliverability assessment.

FAQ

Frequently asked questions

With a standalone Elastic Email MCP server, the agents and LLMs can only access a fixed set of Elastic Email tools tied to that server. However, with the Composio Tool Router, agents can dynamically load tools from Elastic Email and many other apps based on the task at hand, all through a single MCP endpoint.

Yes, you can. LlamaIndex fully supports MCP integration. You get structured tool calling, message history handling, and model orchestration while Tool Router takes care of discovering and serving the right Elastic Email tools.

Yes, absolutely. You can configure which Elastic Email scopes and actions are allowed when connecting your account to Composio. You can also bring your own OAuth credentials or API configuration so you keep full control over what the agent can do.

All sensitive data such as tokens, keys, and configuration is fully encrypted at rest and in transit. Composio is SOC 2 Type 2 compliant and follows strict security practices so your Elastic Email data and credentials are handled as safely as possible.

Start with Elastic Email.It takes 30 seconds.

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

Start building