---
title: "Connect an AI assistant with MCP - Workflow Transactional Email"
description: "Connect Claude, Cursor, VS Code or Gemini CLI to Workflow Transactional Email over MCP. Tools are gated by your API key's access level and no tool can read a secret."
canonical: "https://docs.workflow-transactional-email.app/developer-mcp"
---

# Connect an AI assistant (MCP)

The app runs an **MCP server**, so an AI assistant can inspect and edit your actions directly instead of you copying configuration back and forth.

Useful things it can do: audit which layouts reference a secret that is about to rotate, explain why yesterday's sends failed, or build a new HTTP request from a third-party API's documentation.

## Endpoint

```text
POST https://shopify.workflow-transactional-email.app/api/mcp
```

Transport is Streamable HTTP. Authentication is the same bearer key as the REST API - see [Authentication and API keys](https://docs.workflow-transactional-email.app/developer-authentication.md).

## Connecting

The **MCP** tab on the app's **Developer** page generates a copy-ready command with your URL and key already filled in, for Claude, Claude Desktop, Cursor, VS Code and Gemini CLI. Use that rather than typing it by hand.

For Claude Code the command looks like this:

```bash
claude mcp add --transport http flow-transactional-email \
  https://shopify.workflow-transactional-email.app/api/mcp \
  --header "Authorization: Bearer fak_your_key_here"
```

For editors that use a JSON config, the shape is:

```json
{
  "mcpServers": {
    "flow-transactional-email": {
      "url": "https://shopify.workflow-transactional-email.app/api/mcp",
      "headers": {
        "Authorization": "Bearer fak_your_key_here"
      }
    }
  }
}
```

> [!WARNING]
> **This config file holds a live credential**
> The key sits in plain text in your MCP configuration. Keep that file out of version control, and use a **read-only** key unless the assistant genuinely needs to change or run things.
>
> If you are not sure which level a key has, run the `get_stats` tool or call `GET /api/v1/me`.

## Available tools

The tool set reflects your key's level. A read-only key does not merely get refused when it calls a write tool - it never sees that tool in the list at all, so an assistant cannot be talked into attempting it.

### Read (10 tools)

`list_http_requests`, `get_http_request`, `list_smtp_configs`, `list_email_templates`, `get_email_template`, `list_secret_keys`, `list_senders`, `list_history`, `get_history_entry`, `get_stats`

### Read & write (adds 3)

`create_http_request`, `update_http_request`, `delete_http_request`

### Read, write & execute (adds 1)

`test_http_request` - runs a configured HTTP request against the real target.

## What the assistant can never see

There is deliberately **no tool that returns a secret value, an SMTP password or an OAuth token**. `list_secret_keys` returns names only.

That is the point of the design: you can ask an assistant to write an HTTP request that authenticates with your payment provider, and it will reference `{{ secrets.stripeApiKey }}` correctly without the key itself ever entering the model's context.

History returned through MCP is PII-masked exactly as it is over REST. The same caveat applies - masking is heuristic, and free-text fields can still carry personal data into the conversation. Think about that before pointing an assistant at a large history range.

> [!NOTE]
> **Newly added tools need a fresh session**
> Assistants cache the tool list when they connect. If you change a key's level, or we ship new tools, restart the assistant session to pick them up - otherwise it keeps offering the old set.
