MCP Setup Guide
Configure TextForge as an MCP (Model Context Protocol) server for your AI tools. This enables your AI agents to draft and manage emails through TextForge.
Authentication
TextForge supports two ways to authenticate MCP connections: OAuth 2.0 and API keys. Which one you use depends on your tool.
Connect with OAuth (Recommended)
Tools that support MCP OAuth handle the entire auth flow for you — no tokens to copy, no environment variables to set. Run a single command or click a button, approve access in your browser, and you’re connected. If you don’t have a TextForge account yet, the OAuth flow walks you through signup automatically.
OAuth grants both mcp:drafts and mcp:threads scopes by default,
which covers all 23 MCP tools.
Connect with an API Key
For tools that don’t support OAuth yet, or when you need fine-grained control over which permissions a connection has, create an API token in Settings → API Tokens and pass it as a Bearer token. See the Tokens Guide for details on creating and managing tokens.
mcp:drafts and
mcp:threads by default — that covers everything most people need.
Use an API key if you want to limit a connection to specific permissions
(e.g., read-only access) or if your tool doesn’t support OAuth.
Tool Compatibility
| Tool | OAuth | API Key |
|---|---|---|
| Claude Code CLI | Yes | Yes |
| Claude Desktop | Yes | Yes |
| VS Code | No | Yes |
| Cursor | Yes | Yes |
| Windsurf | Yes | Yes |
| Cline | No | Yes |
| Zed | No | Yes |
| GitHub Copilot CLI | No | Yes |
| OpenCode | Yes | Yes |
| Amp | Yes | Yes |
| OpenClaw | No | Yes (via MCPorter) |
OpenClaw
OpenClaw connects to MCP servers through MCPorter. Install MCPorter and add the TextForge server:
npm install -g mcporter
mcporter config add textforge https://textforge.net/mcp \
--header "Authorization: Bearer $TEXTFORGE_TOKEN"
Set your token as an environment variable: export TEXTFORGE_TOKEN="your_api_token"
Test the connection: mcporter call textforge.list_threads limit:5
Once configured, your OpenClaw agent can use TextForge tools to draft emails, read threads, and manage conversations — all with human approval before anything sends. See OpenClaw Email Safety for why this matters.
Claude Code CLI
Claude Code supports both OAuth and API key authentication. Official documentation.
Connect with OAuth (Recommended)
One command, no tokens to manage. Claude Code handles the OAuth flow automatically — it opens your browser, you approve access, and you’re connected. If you don’t have a TextForge account yet, the flow walks you through signup too.
claude mcp add textforge --transport http https://textforge.net/mcp
That’s it. No --header flag, no environment variables, no config files to edit.
Connect with an API Key
If you need custom permissions or prefer manual token management, you can connect with an API key instead.
Install via Plugin
The official plugin bundles MCP configuration, skills, and prompt templates: textforge-app/textforge-claude-plugin.
/plugin marketplace add textforge-app/textforge-claude-plugin
/plugin install textforge@textforge
Add via CLI
claude mcp add textforge \
--transport http \
https://textforge.net/mcp \
--header "Authorization: Bearer YOUR_API_TOKEN"
--header flag may have bugs in some versions.
If the CLI approach fails, edit the config file directly (see below).
Manual Configuration
Edit ~/.claude.json (user-level) or .mcp.json (project-level):
{
"mcpServers": {
"textforge": {
"type": "http",
"url": "https://textforge.net/mcp",
"headers": {
"Authorization": "Bearer ${TEXTFORGE_TOKEN}"
}
}
}
}
Set your token as an environment variable: export TEXTFORGE_TOKEN="your_api_token"
Useful commands for managing MCP servers:
claude mcp list– List configured serversclaude mcp get textforge– Test server connectionclaude mcp remove textforge– Remove server
Claude Desktop
Claude Desktop supports both OAuth (via the Settings UI) and API keys (via
mcp-remote).
Official documentation.
Option 1: Settings → Connectors (Recommended)
Go to Settings → Connectors in Claude Desktop and add TextForge
as a remote server using the URL https://textforge.net/mcp.
Claude Desktop handles the OAuth flow automatically — no token needed.
Option 2: API Key with mcp-remote
If you need custom permissions, use the mcp-remote npm package to bridge
remote servers. This runs a local process that proxies requests to TextForge.
Config file locations:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"textforge": {
"command": "npx",
"args": [
"mcp-remote",
"https://textforge.net/mcp",
"--header",
"Authorization:${AUTH_TOKEN}"
],
"env": {
"AUTH_TOKEN": "Bearer YOUR_API_TOKEN"
}
}
}
}
Restart Claude Desktop after making changes.
VS Code (Native MCP)
VS Code has native MCP support since version 1.102. This works with GitHub Copilot and other VS Code AI features. Official documentation.
Configuration
Create or edit mcp.json in your VS Code user settings directory, or use
.vscode/mcp.json for project-level configuration.
{
"inputs": [
{
"type": "promptString",
"id": "textforge-token",
"description": "TextForge API Token",
"password": true
}
],
"servers": {
"textforge": {
"type": "http",
"url": "https://textforge.net/mcp",
"headers": {
"Authorization": "Bearer ${input:textforge-token}"
}
}
}
}
VS Code will securely prompt for your API token when the server is first used. Reload the window after adding the configuration.
GitHub Copilot CLI
Configure TextForge in your Copilot CLI MCP settings file at ~/.copilot/mcp-config.json.
Official documentation.
Configuration
{
"mcpServers": {
"textforge": {
"type": "http",
"url": "https://textforge.net/mcp",
"tools": ["*"],
"headers": {
"Authorization": "Bearer $COPILOT_MCP_TEXTFORGE_TOKEN"
}
}
}
}
Set the environment variable: export COPILOT_MCP_TEXTFORGE_TOKEN="your_api_token"
You can also use project-level configuration by placing the file at
.copilot/mcp-config.json in your project root.
Useful CLI commands:
/mcp add– Add a new MCP server interactively/mcp show– Show configured servers
OpenCode
OpenCode supports both OAuth and API key authentication. Official documentation.
Connect with OAuth (Recommended)
opencode mcp add
Select Remote when prompted and enter the URL
https://textforge.net/mcp. OpenCode handles the OAuth flow automatically
— no token needed.
Connect with an API Key
Add to ~/.config/opencode/opencode.json (global) or
opencode.json in your project root:
{
"mcp": {
"textforge": {
"type": "remote",
"url": "https://textforge.net/mcp",
"headers": {
"Authorization": "Bearer {env:TEXTFORGE_TOKEN}"
},
"enabled": true
}
}
}
Note: OpenCode uses {env:VAR} syntax (without the $ prefix) for environment variables.
Cursor
Cursor supports both OAuth and API key authentication.
Edit the MCP settings file at ~/.cursor/mcp.json (global) or
.cursor/mcp.json (project-level).
Official documentation.
Configuration
{
"mcpServers": {
"textforge": {
"type": "streamableHttp",
"url": "https://textforge.net/mcp",
"headers": {
"Authorization": "Bearer ${env:TEXTFORGE_TOKEN}"
}
}
}
}
Set your token as an environment variable: export TEXTFORGE_TOKEN="your_api_token"
Windsurf
Windsurf supports both OAuth and API key authentication.
Add TextForge to your Windsurf MCP configuration at ~/.codeium/windsurf/mcp_config.json.
Official documentation.
Configuration
{
"mcpServers": {
"textforge": {
"serverUrl": "https://textforge.net/mcp",
"headers": {
"Authorization": "Bearer ${env:TEXTFORGE_TOKEN}"
}
}
}
}
Note: Windsurf uses serverUrl instead of url.
Set your token as an environment variable: export TEXTFORGE_TOKEN="your_api_token"
Cline (VS Code)
Cline stores MCP configuration in VS Code's globalStorage directory. Click the MCP Servers icon in Cline and select "Configure MCP Servers" to open the file. Official documentation.
Configuration
The config file is located at: ~/.vscode/extensions/saoudrizwan.claude-dev-*/settings/cline_mcp_settings.json
(the exact path varies by extension version).
{
"mcpServers": {
"textforge": {
"type": "streamableHttp",
"url": "https://textforge.net/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_TOKEN"
},
"disabled": false
}
}
}
Restart VS Code after saving changes. A green indicator shows successful connection.
Zed
Add TextForge to your Zed settings under context_servers.
Open Settings (Cmd/Ctrl + ,) and add the configuration.
Official documentation.
Configuration
Add the following to your Zed settings file. Note that context_servers is a
top-level key in the settings:
{
"context_servers": {
"textforge": {
"url": "https://textforge.net/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_TOKEN"
}
}
}
}
Check the Agent Panel settings – a green indicator next to the server name means it's connected successfully.
Amp
Amp supports both OAuth and API key authentication. Official documentation.
Connect with OAuth (Recommended)
amp mcp add textforge https://textforge.net/mcp
Amp handles the OAuth flow automatically — it opens your browser on startup to complete authentication.
Connect with an API Key
Add to ~/.config/amp/settings.json under the amp.mcpServers key:
{
"amp.mcpServers": {
"textforge": {
"url": "https://textforge.net/mcp",
"headers": {
"Authorization": "Bearer ${TEXTFORGE_TOKEN}"
}
}
}
}
Set your token as an environment variable: export TEXTFORGE_TOKEN="your_api_token"
Or add via CLI: amp mcp add textforge --header "Authorization=Bearer YOUR_API_TOKEN" https://textforge.net/mcp
Other MCP-Compatible Tools
TextForge works with any MCP-compatible tool. The general configuration pattern is:
- Server URL:
https://textforge.net/mcp - OAuth Discovery:
https://textforge.net/.well-known/oauth-authorization-server - Authentication: OAuth 2.0 with PKCE (recommended) or Bearer token in Authorization header
- OAuth Scopes:
mcp:drafts(draft management),mcp:threads(inbox threads) - Transport: HTTP (stateless)
Use the MCP Setup Generator to create a client-specific configuration for any tool listed above.
Available MCP Tools
TextForge exposes 23 MCP tools across 3 categories.
MCP-compatible AI tools discover these tools and their parameters automatically when they connect.
The descriptions below are generated from the same [Description] attributes that
MCP clients see, so they always match what your agent receives.
Draft Management (8 tools)
create_draft
Create a new email draft and submit it for human approval. This is the primary way for LLM agents to initiate the email sending workflow. The draft will be created and automatically submitted for approval. To create a reply to an existing thread, provide either threadId or externalThreadId.
| Parameter | Type | Required | Description |
|---|---|---|---|
subject |
string | No | Email subject line |
body |
string | No | Email body content |
toRecipients |
string | No | To recipients (comma-separated email addresses) |
bodyFormat |
string | No | Body format: Html (default) or Markdown. Markdown will be converted to HTML. |
ccRecipients |
string | No | CC recipients (comma-separated email addresses) |
bccRecipients |
string | No | BCC recipients (comma-separated email addresses) |
scheduledFor |
string | No | Scheduled send time in ISO 8601 format (e.g., 2024-12-25T10:00:00Z). If not provided, email will be sent immediately upon approval. |
threadId |
string | No | Thread ID (GUID) to reply to. Creates a reply draft linked to this thread with proper email threading headers. |
externalThreadId |
string | No | External thread ID to reply to (alternative to threadId). Looks up the thread by provider thread identifier. |
delete_draft
Permanently delete a draft. Only drafts that will not be sent can be deleted: Draft (not yet submitted), Rejected, Expired, or SendFailed status. Cannot delete drafts that are PendingApproval, Approved, or Sent.
| Parameter | Type | Required | Description |
|---|---|---|---|
draftId |
string | No | Draft ID to delete (GUID format) |
get_draft
Get detailed information about a specific draft including subject, body, recipients, status, and approval history.
| Parameter | Type | Required | Description |
|---|---|---|---|
draftId |
string | No | Draft ID (GUID format) |
get_draft_activity
Get the complete activity history for a draft, including creation, updates, approval/rejection, and sending events.
| Parameter | Type | Required | Description |
|---|---|---|---|
draftId |
string | No | Draft ID (GUID format) |
list_drafts
List email drafts with optional filtering by status or recipient email. Returns paginated results with draft metadata including subject, status, recipients, and creation info.
| Parameter | Type | Required | Description |
|---|---|---|---|
status |
string | No | Filter by draft status: Draft, PendingApproval, Approved, Rejected, Sent, or SendFailed |
recipientEmail |
string | No | Filter by recipient email address (searches To, CC, and BCC fields) |
page |
integer | No | Page number (1-based, default: 1) |
pageSize |
integer | No | Number of items per page (default: 25, max: 100) |
reject_draft
Reject a draft that is pending approval. Only drafts in 'PendingApproval' status can be rejected. Use this to withdraw a draft that should not be sent.
| Parameter | Type | Required | Description |
|---|---|---|---|
draftId |
string | No | Draft ID to reject (GUID format) |
rejectionReason |
string | No | Optional reason for rejection |
submit_draft
Submit a draft for human approval. Note: create_draft automatically submits, so this is only needed for drafts that were not auto-submitted.
| Parameter | Type | Required | Description |
|---|---|---|---|
draftId |
string | No | Draft ID to submit (GUID format) |
update_draft
Update an existing draft's content. Only drafts in 'Draft' or 'PendingApproval' status can be updated.
| Parameter | Type | Required | Description |
|---|---|---|---|
draftId |
string | Yes | Draft ID to update (GUID format) |
subject |
string | No | New email subject line |
body |
string | No | New email body content |
toRecipients |
string | No | New To recipients (comma-separated email addresses) |
bodyFormat |
string | No | Body format: Html (default) or Markdown. Markdown will be converted to HTML. |
ccRecipients |
string | No | New CC recipients (comma-separated email addresses). Set to empty string to clear. |
bccRecipients |
string | No | New BCC recipients (comma-separated email addresses). Set to empty string to clear. |
scheduledFor |
string | No | New scheduled send time in ISO 8601 format. Set to empty string to clear and send immediately upon approval. |
Thread Management (10 tools)
get_thread
Get full details of a specific email thread including messages. Requires an internal TextForge thread ID (GUID format) — use the 'id' field from list_threads, list_engaged_threads, search_threads_by_contact, or matched search_messages results. Do NOT pass provider/Gmail thread IDs here; use get_thread_by_external_id for those. By default returns the latest 5 messages with quoted reply content stripped. Set maxMessages=0 for all messages.
| Parameter | Type | Required | Description |
|---|---|---|---|
threadId |
string | No | Internal TextForge thread ID (GUID format, e.g., '3fa85f64-5717-4562-b3fc-2c963f66afa6'). Use the 'id' field from thread listing or search results. |
maxMessages |
integer | No | Max messages to return (latest N). Default: 5. Set to 0 for all. |
stripQuotedReplies |
boolean | No | Strip quoted reply content from bodyText. Default: true. |
get_thread_by_external_id
Look up a thread by the email provider's thread ID (e.g., Gmail thread ID from search_messages results or email headers). Use this when you have a provider thread ID instead of an internal TextForge GUID. If not found, use import_thread to import it first. By default returns the latest 5 messages with quoted reply content stripped.
| Parameter | Type | Required | Description |
|---|---|---|---|
externalThreadId |
string | No | Provider thread ID (e.g., Gmail thread ID like '19d2243fcda1917c' from search_messages results) |
maxMessages |
integer | No | Max messages to return (latest N). Default: 5. Set to 0 for all. |
stripQuotedReplies |
boolean | No | Strip quoted reply content from bodyText. Default: true. |
import_thread
Import a thread from the email provider by its external thread ID (e.g., from the 'unmatchedProviderThreads' array in search_messages results). Returns the thread with its internal 'id' (GUID) for use with get_thread, create_draft, etc. Idempotent: if already imported, syncs instead. Requires 'threads:sync' permission.
| Parameter | Type | Required | Description |
|---|---|---|---|
externalThreadId |
string | No | Provider thread ID (e.g., Gmail thread ID from search_messages results) |
list_engaged_threads
List threads where you have previously sent at least one message, ordered by most recent activity. Includes 'hasUnreadReply' (true when the latest message is inbound — the ball is in your court) plus 'lastInboundAt' and 'lastOutboundAt' timestamps. Use this to find conversations that need a response.
| Parameter | Type | Required | Description |
|---|---|---|---|
page |
integer | No | Page number (1-based, default: 1) |
pageSize |
integer | No | Number of items per page (default: 25, max: 100) |
list_recent_messages
List recent email messages across all threads, filtered by date and optionally by direction. Returns individual messages (not threads) with thread context (threadId, threadSubject) so you can navigate to the full thread via get_thread. Use this to quickly find recent inbound emails without paging through threads. Requires 'threads:read' permission.
| Parameter | Type | Required | Description |
|---|---|---|---|
since |
string | No | Return messages after this date/time (ISO 8601, e.g., '2026-03-25T00:00:00Z'). Required. |
direction |
string | No | Filter by direction: 'inbound', 'outbound', or omit for all messages |
limit |
integer | No | Max results to return (default: 50, max: 100) |
newestFirst |
boolean | No | Sort order: true for newest first (default), false for oldest first |
list_threads
List email threads (conversations) for the authenticated user's organization. Threads are created when emails are sent and track both outbound and inbound messages. Use this to find threads for context when composing replies.
| Parameter | Type | Required | Description |
|---|---|---|---|
participantEmail |
string | No | Filter by participant email address. Returns threads where this email appears in From, To, or CC. |
page |
integer | No | Page number (1-based, default: 1) |
pageSize |
integer | No | Number of items per page (default: 25, max: 100) |
search_messages
Search emails using Gmail-style query syntax (e.g., from:, subject:, has:attachment). Returns two result groups: 'threads' contains matches already in TextForge with full metadata and an internal 'id' (GUID) for use with get_thread; 'unmatchedProviderThreads' contains provider-only results not yet imported — use import_thread with their 'externalThreadId' first, then get_thread with the returned id. Requires 'threads:read' permission.
| Parameter | Type | Required | Description |
|---|---|---|---|
query |
string | No | Search query using Gmail syntax (e.g., from:[email protected], subject:"invoice", has:attachment, after:2026/01/01) |
maxResults |
integer | No | Maximum number of results (default: 100, max: 500) |
pageToken |
string | No | Page token for pagination (from previous response) |
search_threads_by_contact
Search for email threads involving a specific contact (email address). Searches across From, To, and CC fields of all messages in threads. Useful for finding all email history with a particular person or company.
| Parameter | Type | Required | Description |
|---|---|---|---|
email |
string | No | Email address to search for (e.g., '[email protected]' or '@example.com' for domain) |
page |
integer | No | Page number (1-based, default: 1) |
pageSize |
integer | No | Number of items per page (default: 25, max: 100) |
sync_inbox
Trigger a full inbox sync to discover new threads and update existing ones from the email provider. Like hitting refresh in an email client. Use this when you need to find recently arrived emails that aren't yet in TextForge. Requires 'threads:sync' permission.
sync_thread
Sync a thread from the email provider to fetch any new messages. Use this before composing a reply to ensure you have the latest conversation context. Requires an internal TextForge thread ID (GUID). Requires 'threads:sync' permission.
| Parameter | Type | Required | Description |
|---|---|---|---|
threadId |
string | No | Internal TextForge thread ID (GUID format). Use the 'id' field from thread listing or search results. |
Attachment Management (5 tools)
get_attachment_download_url
Get a presigned URL to download an attachment. The URL expires in 10 minutes. Works for both message attachments and draft attachments.
| Parameter | Type | Required | Description |
|---|---|---|---|
parentId |
string | No | The message ID (for message attachments) or draft ID (for draft attachments) |
attachmentId |
string | No | The attachment ID to download |
sourceType |
string | No | Source type: 'message' for received emails or 'draft' for draft attachments |
get_draft_attachment_upload_url
Get a presigned URL to upload an attachment to a draft. The URL is single-use and expires in 10 minutes. Use HTTP PUT to upload raw file bytes to the returned URL.
| Parameter | Type | Required | Description |
|---|---|---|---|
draftId |
string | No | The draft ID to attach the file to (GUID format) |
filename |
string | No | The filename for the attachment |
contentType |
string | No | The MIME type of the file (e.g., 'application/pdf', 'image/png') |
sizeBytes |
integer | No | The expected file size in bytes |
list_draft_attachments
List attachments currently attached to a draft email. Returns attachment metadata including filename, MIME type, size, and upload time.
| Parameter | Type | Required | Description |
|---|---|---|---|
draftId |
string | No | The draft ID to list attachments for (GUID format) |
list_message_attachments
List attachments for a received email message. Returns attachment metadata including filename, MIME type, and size. Use get_attachment_download_url to download content.
| Parameter | Type | Required | Description |
|---|---|---|---|
messageId |
string | No | The message ID (GUID format) |
remove_draft_attachment
Remove an attachment from a draft email. The attachment will be permanently deleted.
| Parameter | Type | Required | Description |
|---|---|---|---|
draftId |
string | No | The draft ID containing the attachment (GUID format) |
attachmentId |
string | No | The attachment ID to remove (GUID format) |
Quick start workflow
Here is a typical agent flow using TextForge MCP tools:
search_threads_by_contact("[email protected]")— find existing conversation historyget_thread(threadId)— read the full thread for contextcreate_draft(subject, body, toRecipients, threadId)— draft a reply- Human reviews and approves in the TextForge UI or via Slack notification
list_drafts(status: "Sent")— confirm delivery
For the REST API equivalent, see the API Reference or the Interactive API Reference.
Agent Skills & Plugins
Pre-built skills give your AI tool the full TextForge workflow out of the box — not just MCP tool access, but email writing best practices, draft lifecycle patterns, and a built-in audit for common AI writing tells. Both packs include the same two skills. See the AI Email Writing Skill page for the full breakdown.
- TextForge skill — Complete reference for all 23 MCP tools, organized by category. Documents every parameter, covers the attachment upload workflow, and explains draft lifecycle (create → review → update/reject → approve).
- Email writing skill — Composition guide purpose-built for AI-drafted email. Covers six writing principles (be direct, use specific language, frame questions with binary structure), HTML formatting rules, a pre-send checklist, and a mandatory audit for 12 common AI writing patterns — em dashes, synonym cycling, sycophantic openers, filler phrases, and more — derived from Wikipedia’s AI writing signal research. The goal: drafts that read like a person wrote them.
OpenClaw
textforge-app/openclaw-textforge — Install via mcporter. Includes the TextForge skill and the email writing skill. See the OpenClaw setup section above for installation commands.
Claude Code
textforge-app/textforge-claude-plugin — Install via the Claude Code plugin marketplace. Includes the TextForge skill and the email writing skill.
/plugin marketplace add textforge-app/textforge-claude-plugin
/plugin install textforge@textforge
.mcp.json inside these plugins uses the same format as VS Code, Copilot,
and other MCP-compatible tools. The connection config is portable across clients — only
the skill files are tool-specific.