How to integrate Plain MCP with CrewAI

Connect CrewAI to Plain MCP. Add a customer to the enterprise group, fetch company details for acme corp, and more using natural language, with authentication handled for you.

Plain logoPlain
Api Key

Plain is a collaborative support platform for B2B teams, focused on consolidating conversations and boosting efficiency with AI. Enjoy streamlined, modern workflows to assist customers across channels from one place.

119 Tools11 Triggers

Introduction

This guide walks you through connecting Plain to CrewAI using the Composio tool router. By the end, you'll have a working Plain agent that can create and search support threads, reply to customers, assign and label threads, manage customers and tenants, and set SLA policies through natural language commands.

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

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

Also integrate Plain with

TL;DR

Here's what you'll learn:
  • Get a Composio API key and configure your Plain connection
  • Set up CrewAI with an MCP enabled agent
  • Create a Tool Router session or standalone MCP server for Plain
  • Build a conversational loop where your agent can execute Plain operations

What is CrewAI?

CrewAI is a powerful framework for building multi-agent AI systems. It provides primitives for defining agents with specific roles, creating tasks, and orchestrating workflows through crews.

Key features include:

  • Agent Roles: Define specialized agents with specific goals and backstories
  • Task Management: Create tasks with clear descriptions and expected outputs
  • Crew Orchestration: Combine agents and tasks into collaborative workflows
  • MCP Integration: Connect to external tools through Model Context Protocol

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

The Plain MCP server is an implementation of the Model Context Protocol that connects your AI agent and assistants like Claude, Cursor, etc directly to your Plain account. It provides structured and secure access to your B2B support workspace, so your agent can perform actions like managing customers, creating support threads, fetching company details, handling issues, and organizing customer groups on your behalf.

  • Customer management and onboarding: Automatically create new customer records, fetch customer information by email or ID, and add customers to specific support groups for better organization.
  • Support thread creation: Let your agent create new support threads tied to customers, making it easy to kick off or escalate conversations without manual intervention.
  • Issue tracking and retrieval: Fetch all external issue links associated with a customer, helping your team stay on top of ongoing problems and resolutions.
  • Company and tier information access: Retrieve detailed company profiles and tier metadata, including contract value, owner details, and more, to personalize support interactions.
  • User and customer cleanup: Safely delete customers or users from the system when offboarding or data hygiene is needed, all through agent-driven actions.

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

Prerequisites

Before starting, make sure you have:
  • Python 3.9 or higher
  • A Composio account and API key
  • A Plain connection authorized in Composio
  • An OpenAI API key for the CrewAI LLM
  • Basic familiarity with Python
2

Getting API Keys for OpenAI and Composio

OpenAI API Key
  • Go to the OpenAI dashboard and create an API key. You'll need credits to use the models, or you can connect to another model provider.
  • Keep the API key safe.
Composio API Key
  • Log in to the Composio dashboard.
  • Navigate to your API settings and generate a new API key.
  • Store this key securely as you'll need it for authentication.
3

Install dependencies

bash
pip install composio crewai crewai-tools[mcp] python-dotenv
What's happening:
  • composio connects your agent to Plain via MCP
  • crewai provides Agent, Task, Crew, and LLM primitives
  • crewai-tools[mcp] includes MCP helpers
  • python-dotenv loads environment variables from .env
4

Set up environment variables

bash
COMPOSIO_API_KEY=your_composio_api_key_here
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 with Composio
  • USER_ID scopes the session to your account
  • OPENAI_API_KEY lets CrewAI use your chosen OpenAI model
5

Import dependencies

python
import os
from composio import Composio
from crewai import Agent, Task, Crew
from crewai_tools import MCPServerAdapter
import dotenv

dotenv.load_dotenv()

COMPOSIO_API_KEY = os.getenv("COMPOSIO_API_KEY")
COMPOSIO_USER_ID = os.getenv("COMPOSIO_USER_ID")

if not COMPOSIO_API_KEY:
    raise ValueError("COMPOSIO_API_KEY is not set")
if not COMPOSIO_USER_ID:
    raise ValueError("COMPOSIO_USER_ID is not set")
What's happening:
  • CrewAI classes define agents and tasks, and run the workflow
  • MCPServerHTTP connects the agent to an MCP endpoint
  • Composio will give you a short lived Plain MCP URL
6

Create a Composio Tool Router session for Plain

python
composio_client = Composio(api_key=COMPOSIO_API_KEY)
session = composio_client.create(user_id=COMPOSIO_USER_ID, toolkits=["plain"])

url = session.mcp.url
What's happening:
  • You create a Plain only session through Composio
  • Composio returns an MCP HTTP URL that exposes Plain tools
7

Initialize the MCP Server

python
server_params = {
    "url": url,
    "transport": "streamable-http",
    "headers": {"x-api-key": COMPOSIO_API_KEY},
}

with MCPServerAdapter(server_params) as tools:
    agent = Agent(
        role="Search Assistant",
        goal="Help users search the internet effectively",
        backstory="You are a helpful assistant with access to search tools.",
        tools=tools,
        verbose=False,
        max_iter=10,
    )
What's Happening:
  • Server Configuration: The code sets up connection parameters including the MCP server URL, streamable HTTP transport, and Composio API key authentication.
  • MCP Adapter Bridge: MCPServerAdapter acts as a context manager that converts Composio MCP tools into a CrewAI-compatible format.
  • Agent Setup: Creates a CrewAI Agent with a defined role (Search Assistant), goal (help with internet searches), and access to the MCP tools.
  • Configuration Options: The agent includes settings like verbose=False for clean output and max_iter=10 to prevent infinite loops.
  • Dynamic Tool Usage: Once created, the agent automatically accesses all Composio Search tools and decides when to use them based on user queries.
8

Create a CLI Chatloop and define the Crew

python
print("Chat started! Type 'exit' or 'quit' to end.\n")

conversation_context = ""

while True:
    user_input = input("You: ").strip()

    if user_input.lower() in ["exit", "quit", "bye"]:
        print("\nGoodbye!")
        break

    if not user_input:
        continue

    conversation_context += f"\nUser: {user_input}\n"
    print("\nAgent is thinking...\n")

    task = Task(
        description=(
            f"Conversation history:\n{conversation_context}\n\n"
            f"Current request: {user_input}"
        ),
        expected_output="A helpful response addressing the user's request",
        agent=agent,
    )

    crew = Crew(agents=[agent], tasks=[task], verbose=False)
    result = crew.kickoff()
    response = str(result)

    conversation_context += f"Agent: {response}\n"
    print(f"Agent: {response}\n")
What's Happening:
  • Interactive CLI Setup: The code creates an infinite loop that continuously prompts for user input and maintains the entire conversation history in a string variable.
  • Input Validation: Empty inputs are ignored to prevent processing blank messages and keep the conversation clean.
  • Context Building: Each user message is appended to the conversation context, which preserves the full dialogue history for better agent responses.
  • Dynamic Task Creation: For every user input, a new Task is created that includes both the full conversation history and the current request as context.
  • Crew Execution: A Crew is instantiated with the agent and task, then kicked off to process the request and generate a response.
  • Response Management: The agent's response is converted to a string, added to the conversation context, and displayed to the user, maintaining conversational continuity.

Complete Code

Here's the complete code to get you started with Plain and CrewAI:

python
from crewai import Agent, Task, Crew, LLM
from crewai_tools import MCPServerAdapter
from composio import Composio
from dotenv import load_dotenv
import os

load_dotenv()

GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
COMPOSIO_API_KEY = os.getenv("COMPOSIO_API_KEY")
COMPOSIO_USER_ID = os.getenv("COMPOSIO_USER_ID")

if not GOOGLE_API_KEY:
    raise ValueError("GOOGLE_API_KEY is not set in the environment.")
if not COMPOSIO_API_KEY:
    raise ValueError("COMPOSIO_API_KEY is not set in the environment.")
if not COMPOSIO_USER_ID:
    raise ValueError("COMPOSIO_USER_ID is not set in the environment.")

# Initialize Composio and create a session
composio = Composio(api_key=COMPOSIO_API_KEY)
session = composio.create(
    user_id=COMPOSIO_USER_ID,
    toolkits=["plain"],
)
url = session.mcp.url

# Configure LLM
llm = LLM(
    model="gpt-5",
    api_key=os.getenv("OPENAI_API_KEY"),
)

server_params = {
    "url": url,
    "transport": "streamable-http",
    "headers": {"x-api-key": COMPOSIO_API_KEY},
}

with MCPServerAdapter(server_params) as tools:
    agent = Agent(
        role="Search Assistant",
        goal="Help users with internet searches",
        backstory="You are an expert assistant with access to Composio Search tools.",
        tools=tools,
        llm=llm,
        verbose=False,
        max_iter=10,
    )

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

    conversation_context = ""

    while True:
        user_input = input("You: ").strip()

        if user_input.lower() in ["exit", "quit", "bye"]:
            print("\nGoodbye!")
            break

        if not user_input:
            continue

        conversation_context += f"\nUser: {user_input}\n"
        print("\nAgent is thinking...\n")

        task = Task(
            description=(
                f"Conversation history:\n{conversation_context}\n\n"
                f"Current request: {user_input}"
            ),
            expected_output="A helpful response addressing the user's request",
            agent=agent,
        )

        crew = Crew(agents=[agent], tasks=[task], verbose=False)
        result = crew.kickoff()
        response = str(result)

        conversation_context += f"Agent: {response}\n"
        print(f"Agent: {response}\n")

Conclusion

You now have a CrewAI agent connected to Plain through Composio's Tool Router. The agent can perform Plain operations through natural language commands.

Next steps:

  • Add role-specific instructions to customize agent behavior
  • Plug in more toolkits for multi-app workflows
  • Chain tasks for complex multi-step operations
TOOLS & TRIGGERS

Supported Tools and Triggers

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

Add Customer To Group

Add a customer to up to 25 customer groups.

Add Customer To Tenants

Idempotently add a customer to one or more tenants.

Add Generated Reply

Add a suggested reply for teammate review without sending it to the customer.

Add Labels To Thread

Add one or more labels to a thread by label-type ID.

Add Members to Tier

Add or move up to 25 tenants or companies into one tier.

Archive Label Type

Archive a label type while preserving labels already on threads.

Bulk Delete Thread Fields

Sequentially delete thread-field values by thread and schema key.

Bulk Set Thread Fields

Create or replace multiple typed thread-field values.

Change Thread Assignment

Assign or unassign a thread's primary assignee, or add or remove additional assignees.

Create Attachment Download URL

Create a sensitive three-minute URL and return its virus-scan result.

Create Attachment Upload Form

Create a sensitive, two-hour presigned upload capability; no file is uploaded.

Create Autoresponder

Create an autoresponder that automatically sends outbound customer replies.

Create Customer Group

Creates a new customer group in Plain for organizing and segmenting customers.

Create Email/Slack Discussion

Immediately and irreversibly start an external EMAIL or SLACK discussion.

Create Customer Or Thread Event

Create a custom customer-wide or single-thread timeline event.

Create Label Type

Create a label type that can be applied to threads.

Create Note

Create an internal note on an existing customer thread.

Create Tier SLA

Create one legacy tier SLA; next-response requires a first-response SLA on the tier.

Create SLA Policy

Create a named SLA policy with zero or more unique target types.

Create Snippet

Create a reusable plain-text and optional Markdown reply snippet.

Create Task

Create a task with optional company, tenant or account context and an optional assignee.

Create Thread

Create a thread after obtaining a valid customer identifier.

Create Thread Field Schema

Create a custom thread-field schema.

Create Tier

Create a support tier, optionally assigning tenant or company members.

Delete Autoresponder

Permanently delete a native autoresponder.

Delete Customer

Permanently and irreversibly deletes a Plain customer by ID, including all associated threads, timeline entries, and other customer data.

Delete Customer Group

Permanently delete an empty customer group after confirmation.

Delete Note

Soft-delete an internal note.

Delete Tier SLA

Delete a legacy SLA from a tier.

Delete SLA Policy

Delete an SLA policy, its targets, and their trackers.

Delete Snippet

Soft-delete a snippet while preserving historical references.

Delete Task

Soft-delete a task and remove its thread links.

Delete Tenant

Delete a tenant, unlink its customers, and remove its field values.

Delete Tenant Field Value

Clear one tenant field value without deleting its schema.

Delete Tenant Field Schema

Permanently delete a tenant-field schema and all values stored against it.

Delete Thread

Permanently delete a thread and all associated data.

Delete Thread Field Schema

Delete a schema and all values stored against it.

Delete Tier

Permanently delete a tier and all of its memberships.

Delete User

Permanently removes a Plain workspace user by ID.

Escalate Thread Or Select Path

Advance a thread's escalation or attach/detach its escalation path.

Fetch Company

Tool to fetch company details by ID.

Fetch Issues

Fetches external issue tracker links (Jira, Linear, GitHub, etc.

Fetch Tier

Fetch a tier by ID.

Get Autoresponder

Fetch a native Plain autoresponder by ID through a machine-user-safe list query.

Get Customer By Email

Fetch customer details by email address.

Get Customer By External ID

Fetch a customer by the exact stable external ID from your own system.

Get Customer By ID

Retrieve a customer's ID, full name, email address, and creation and update timestamps by customer ID.

Get Customer Group

Fetch one customer group by its Plain ID.

Get Customers

Tool to fetch a list of customers.

Get Email/Slack Discussion

Fetch metadata for one EMAIL or SLACK side-conversation by ID.

Get Knowledge Gap

Fetch one AI-detected knowledge gap and its linked task and signals.

Get Label Type

Fetch one label type by Plain ID or external ID.

Get SLA Policy

Fetch one SLA policy by its Plain ID.

Get Snippet

Fetch one snippet by ID, including a soft-deleted snippet.

Get Task by ID or Ref

Fetch one task by either its Plain ID or human-readable T-123 ref.

Get Tenant

Fetch one tenant by its Plain ID, including current custom field values.

Get Thread By ID

Fetch a thread by ID, including customer info, status, priority, labels, and assignments.

Get Thread By Reference

Fetch a thread by ref or by its customer-scoped external ID.

Get User By ID

Fetch workspace user/team member by ID.

Import Thread

Import one historical thread without triggering SLAs or autoresponders.

Import Thread Messages

Import up to 25 historical messages and return one result per input message.

List AI Feedback

List feedback submitted on Plain AI features, optionally filtered by feature, sentiment, or time.

List Autoresponders

List native Plain autoresponders in priority order.

List Customer Groups

List one paginated page of customer groups with optional external-ID filtering.

List Deleted Threads

List deletion-audit records for threads.

List Email/Slack Discussions

List only EMAIL and SLACK side-conversations with filters and pagination.

List Generated Replies

List current suggested replies for a thread's most recent inbound message.

List Knowledge Gaps

List AI-detected knowledge gaps with status filters and useful sort orders.

List Label Types

List active and/or archived label types.

List SLA Policies

List named SLA policies, optionally filtering by name or target type.

List Snippets

List the workspace snippet library with cursor pagination.

List Tasks

List workspace tasks with pagination, filtering, and sorting.

List Tenant Field Schemas

List tenant-field schemas before reading or writing custom values.

List Tenants

List tenants with current filters and cursor pagination.

List Thread Field Schemas

List the workspace's custom thread-field schemas.

List Threads By External ID

List a page of threads with an external ID across all customers.

List Threads With Rich Filters

List threads with the provider's complete public filter and sort surface.

List Tiers

List tiers with cursor pagination.

Mark Customer As Spam

Idempotently hide a customer's threads as spam and exclude them from metrics.

List Threads

Retrieve up to 50 threads from one page, optionally filtered by status.

Remove Customer From Group

Removes a customer from one or more customer groups in Plain.

Remove Customer From Tenants

Idempotently remove a customer from one or more tenants.

Remove Labels From Thread

Remove one or more concrete labels from a thread.

Remove Members from Tier

Remove up to 25 tenants or companies from their current tiers.

Reorder Autoresponders

Reorder native autoresponders in one call.

Reorder Thread Field Schemas

Reorder one or more thread-field schemas.

Reply To Email

Immediately and irreversibly send an external reply to an inbound email.

Run GraphQL Query

Execute an arbitrary GraphQL document against Plain without a safety gate; mutations can immediately and irreversibly change data.

Search Customers

Search customer profile fields or timeline text using explicit OR conditions.

Search Tenants

Search tenants by name, domain, or exact external ID.

Search Threads

Search thread titles, messages, and customer names or emails.

Send Email/Slack Discussion Message

Immediately and irreversibly deliver a message in an existing discussion.

Send Message

Immediately and irreversibly deliver a message through an existing thread.

Send New Email

Immediately and irreversibly send an external email to a customer.

Set Customer Tenants

Replace a customer's complete tenant membership set in one call.

Transition Thread Status

Move a thread to Todo, Snoozed, or Done.

Unarchive Label Type

Restore an archived label type for use on new threads.

Unmark Customer As Spam

Idempotently clear a customer's spam flag and restore inbox visibility.

Update Autoresponder

Update an autoresponder that automatically sends outbound customer replies.

Update Company

Upserts (creates or updates) a company in Plain.

Update Company Tier

Assign one company to a tier or clear its tier membership.

Update Generated Reply

Record teammate feedback on a generated reply.

Update Label Type

Update mutable label-type properties using field wrappers.

Update Note

Replace an internal note's content and optional attachment list.

Update Tier SLA

Update a legacy tier SLA without changing its SLA type.

Update SLA Policy

Replace an SLA policy's targets; omitted target types and their trackers are deleted.

Update Snippet

Update selected snippet fields using Plain's field-level wrappers.

Update Task

Update selected fields on an existing task.

Update Tenant Tier

Assign one tenant to a tier or clear its tier membership.

Update Thread Title

Tool to update a thread's title.

Update Thread Attributes

Change one thread priority, customer, external ID, tenant, or tier attribute.

Update Thread Field Schema

Update mutable properties of a thread-field schema.

Apply or Clear Thread SLA Policy

Apply an SLA policy, or clear it so the thread falls back to its tier's SLAs.

Update Tier

Update selected fields on an existing support tier.

Upsert Customer

Tool to upsert (create or update) a customer.

Upsert Customer Group

Idempotently create or update a customer group using machine-user-safe calls.

Upsert Tenant

Create or update a tenant while synchronizing its stable external ID.

Upsert Tenant Field

Set one typed custom field value for a tenant.

Upsert Tenant Field Schemas

Create or update tenant-field schemas keyed by source and external field ID.

FAQ

Frequently asked questions

Start with Plain.It takes 30 seconds.

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

Start building