Announcing CIMD support for MCP Client registration
Learn more

SendGrid MCP vs SendGrid API for AI Agents (2026)

Nityashree Yadunath
Product Marketing Manager

TL;DR

  • SendGrid ships no MCP server that executes API calls. Twilio's hosted MCP server indexes SendGrid docs, but it is read-only.
  • Every path uses one credential type: a SendGrid API key sent as a Bearer token. SendGrid documents no user-delegated OAuth flow.
  • Community SendGrid MCP servers usually read one key from an environment variable and run unreviewed code with it.
  • Multi-tenant agents need one key per customer on every path, and the on-behalf-of header does not work with Mail Send.
  • Scalekit's SendGrid connector vaults each user's key, ships 391 prebuilt tools, and serves any subset over a Virtual MCP server.

Three ways to connect SendGrid to an agent

Your agent needs to send email through SendGrid: an order confirmation, an incident notice, a follow-up drafted from a support ticket. Then it needs to answer whether that email bounced. SendGrid's v3 API has covered all of this for years, but SendGrid has no MCP server of its own that can execute a call. That leaves three real options: a community MCP server, the direct API, or Scalekit's connector. Each one puts the same long-lived API key in a different place.

What each SendGrid path actually is

All three paths end at the same SendGrid v3 endpoints. What differs is who writes the tool schemas, who holds the API key, and what code runs between the model and SendGrid.

Community SendGrid MCP servers

Public SendGrid MCP servers on GitHub and npm range from a single send-email tool to a couple dozen Marketing Campaigns tools. Most run as local processes started by the MCP host and read one key from a SENDGRID_API_KEY environment variable. At least one HTTP-based server guards its endpoint with a single shared secret. None is published or supported by Twilio.

Twilio's hosted MCP server

Twilio's official MCP server is a Public Beta documentation service. It indexes Twilio SendGrid docs and support articles, requires no authentication, and is read-only: it does not execute API calls. It helps a coding agent write SendGrid integration code. It cannot send an email at runtime. Twilio lists execute-ready, OAuth-authenticated MCP tools as a planned addition, with no date given.

The SendGrid v3 API

The v3 API is a REST surface covering Mail Send, templates, Marketing Campaigns contacts and lists, suppressions, stats, Email Activity and Email Logs, sender and domain authentication, subusers, and webhooks. Authentication is an API key in an Authorization: Bearer header. SendGrid ended username and password Basic Authentication in Q4 2020; SMTP and some services accept Basic Authentication with the username apikey and the key as the password. SendGrid documents all of it in its v3 API reference and maintains SDKs in seven languages.

Scalekit's SendGrid connector

The Scalekit SendGrid connector docs cover 391 prebuilt SendGrid tools, each authenticated with the calling user's own SendGrid key. Your agent calls them through execute_tool or a framework adapter. Scalekit injects the key at request time, so it never enters agent code or LLM context. The same tools can be served over a Virtual MCP server scoped to what one agent role needs. The SendGrid connector overview summarizes the model.

Comparing them where it matters for agents

The three paths expose the same SendGrid account through very different surfaces. Capability is the smallest difference between them. Credential placement is the largest.

What your agent can actually do

Community coverage reflects public servers today; any single server covers only a slice.

Capability
Community MCP servers
SendGrid v3 API
Scalekit connector
Send email
Most
Yes
Yes, sendgrid_send_mail
Dynamic templates
Some
Yes
Yes
Contacts and lists
Some
Yes
Yes
Suppressions and bounces
Rare
Yes
Yes
Per-message delivery lookup
Rare
Yes, add-on for Email Activity
Yes, sendgrid_list_messages_by_filter
Stats
Some
Yes
Yes
Address validation
Rare
Yes, separate key permission
Yes, sendgrid_validate_email
Sender and domain authentication
Rare
Yes
Yes
Subuser administration
Rare
Yes, on-behalf-of
Yes, on_behalf_of
Webhook and Inbound Parse setup
Rare
Yes
Yes
Receiving webhook events
No
Your endpoint
No, your endpoint

Where the capability gap bites

The gap that matters is not sending. It is everything after the send. Mail Send returns 202 Accepted with an empty body, which means SendGrid queued the message, not that it arrived. Addresses on the bounce or unsubscribe lists are dropped after acceptance. An agent answering "did the password reset arrive?" needs per-message lookup, and the Email Activity API requires a paid history add-on, a key with Email Activity permission, and is limited to 6 requests per minute. Community servers rarely expose any of this.

Webhooks sit outside every agent path

SendGrid's push surfaces, the Event Webhook and Inbound Parse, POST to an HTTPS endpoint you host. No MCP server or connector replaces that endpoint. An agent can configure a webhook; your service still receives the events. The OAuth that does appear in SendGrid's docs runs in the other direction: the Event Webhook can use the Client Credentials grant to authenticate to your endpoint.

The auth path each one puts you on

Every path, including Scalekit's, ends with a SendGrid API key in an Authorization: Bearer header. SendGrid's Authentication docs describe no user-delegated OAuth consent flow for third-party apps. There is no consent screen, no refresh token, and no scope negotiation at connect time. The key-creation call takes a name and optional scopes, nothing else. There is no expiry field, so a key stays valid until someone deletes it.

What a SendGrid key lets an agent do

Full Access covers every endpoint except billing and Email Address Validation. Restricted (Custom) Access sets No, Read, or Full Access per permission. A key cannot exceed its creator's permissions, and each account caps out at 100 keys. Two details matter for agents. A key created through the API without a scopes field defaults to Full Access. A Full Access key can also call the API Keys endpoints, so a leaked key can mint replacement keys that survive its own deletion.

Per-tenant credentials in a multi-tenant agent

If each customer brings their own SendGrid account, your agent holds one key per customer. If you run customers as subusers under one Pro or Premier account, a parent key with the on-behalf-of header can administer every subuser, but that header does not work with Mail Send. Sending as a subuser, with its own reputation, stats, and suppressions, requires that subuser's key.

Both paths require per-tenant credential isolation. A community server holds one key per process. The direct API holds one key per tenant in your storage. Neither path stores, rotates, or revokes those keys for you. This is a core challenge covered in depth in the article on how tool calling auth changes when you move from single-tenant to multi-tenant.

What you own in production

Ownership splits three ways: the server code, the tool schemas, and the key lifecycle. Each path assigns them differently.

On a community MCP server

You own the key, the host config file that stores it, and a dependency you did not write. Tool schemas change when the maintainer publishes, and an npx -y launch resolves the latest published version unless you pin one. SendGrid surface changes, such as the newer Email Logs API or the split between legacy and current Marketing Campaigns APIs, reach the server when its maintainer gets to them. There is no SLA and no support path.

On the direct v3 API

You own everything: tool schemas written for an LLM rather than a human reader, pagination, 429 handling against per-endpoint rate limits, and Mail Send limits of 30MB, 1,000 recipients, and 10,000 bytes of custom arguments per request. You also own the key store. Writing the schema is the hard part, not the API call. In exchange you get a stable v3 contract and immediate access to any new endpoint.

On Scalekit's connector

Scalekit owns the 391 tool schemas, key storage, and per-user key injection. You own the connection configuration, which tools each agent role sees, and the SendGrid-side lifecycle. Revoking a SendGrid key happens in SendGrid: deleting the connected account stops your agent from using it, and the customer should still delete the key in their SendGrid account.

The concrete risks of community SendGrid MCP servers

Community servers earn their place in local experiments, and some maintainers deliberately keep write tools out of auto-approve lists. The failure modes are still specific, and all of them trace back to one long-lived key held by code outside your review process.

Credential exposure

The key sits in plaintext in an MCP host config file or shell profile, readable by any process running as that user and copied into every environment the config travels to. Public setup guides commonly tell readers to pick Full Access, including Twilio's own tutorial for building a SendGrid MCP server. A Full Access key on a laptop can send as your authenticated domain from anywhere. Disabling the owner's SSO account when they leave does not delete it.

Recommended reading: When an employee leaves, who revokes their AI agent's access?

Unreviewed code holding account access

An MCP server can call any endpoint its key allows, not only the tools it advertises. A server that lists four read tools, run with a Full Access key, still holds a key that can delete suppressions or create API keys. Twilio Labs, in the docs for its own MCP server, advises users not to run community MCP servers alongside official Twilio ones, to guard against injection attacks that could expose account data.

Prompt injection becomes outbound email

Email is an exfiltration channel. An agent that reads untrusted text, such as an inbound ticket, a scraped page, or a parsed reply, and also holds a send tool can be steered into mailing data out from your authenticated domain, with your domain's reputation behind it. One public server's sample config sets autoApprove to "*", which removes the human check on exactly that call.

Recommended reading: MCP security risks for AI agents

Maintenance and breakage

Single-maintainer projects stall, get abandoned, or change tool names between releases. When one breaks on a SendGrid change, the failure shows up inside your agent. Your team then debugs someone else's code, often during an incident when the agent was supposed to be sending notices.

No audit trail tied to a user

SendGrid's Email Logs record the api_key_id that authenticated each send. With one shared key behind a community server, every message traces to the same key ID, not to the user, the agent run, or the prompt that caused it. When a customer asks who sent an email in their name, that log cannot answer.

Recommended reading: Audit trails for agent auth in B2B SaaS

Policy liability

The Twilio Email Policy, part of the Twilio Acceptable Use Policy, requires affirmative consent before any non-transactional email and rules out blanket or third-party consent. Every such message needs a physical mailing address, a working unsubscribe link, and a privacy policy link, with opt-outs honored within 10 days. An agent that emails a list it assembled does not shift that obligation. The account owner carries the violation, and SendGrid's support docs warn that ignoring consent jeopardizes account status.

When to use each SendGrid path

The right path depends on who owns the SendGrid account and who is in the loop when the agent acts.

Use a community MCP server when

  • You are experimenting in Claude Desktop or Cursor against your own test account or subuser, with a Restricted key limited to the permissions the server's tools need.
  • You are prototyping template editing, where a human reviews every change and nothing sends to real recipients.
  • You have read the server's source, pinned its version, and the server runs only on your machine.

For coding agents writing SendGrid integration code, Twilio's read-only documentation MCP server is the safer companion: it answers API questions without holding a key.

Use the SendGrid v3 API directly when

  • The send is deterministic: an order event triggers a templated receipt, and the LLM only drafts copy that your code sends.
  • You batch high-volume sends with up to 1,000 recipients per request, scheduled with send_at up to 72 hours ahead and grouped under a batch_id you can pause or cancel.
  • You process Event Webhook or Inbound Parse traffic, which needs your own HTTPS endpoint regardless of path.
  • Your product is single-tenant, and one server-side key kept out of LLM context is the whole credential story.

Use Scalekit's SendGrid connector when

  • Each customer connects their own SendGrid account, and the agent must act with that customer's key and nothing broader.
  • The agent needs post-send tools, such as message lookup, bounce lists, and stats, without your team writing and maintaining schemas.
  • You want MCP access from Claude, Cursor, or an agent framework without deploying a server.
  • SendGrid is one connector among several, as in an incident response agent or a support ticket automation agent.

The credential problem that exists on every path

Swap MCP for the API, or the API for a connector, and the credential stays the same: a long-lived SendGrid key per tenant.

What SendGrid gives you

SendGrid gives you scoped keys and a revocation lever: delete the key and every call made with it fails. It does not give you expiry, per-user identity on a shared key, or a record of which user caused a call.

What you still have to build

Take a B2B support platform with 40 customers, each on its own SendGrid account. That is 40 keys to store encrypted and isolated per tenant, 40 rotations you schedule yourself since no key expires, a reconnect flow for each customer who replaces a key, and 40 clean-ups as customers churn. Add a Restricted key per agent role and the count multiplies. The key type never changes, and the infrastructure required is identical on the MCP path and the API path.

Where Scalekit fits

Scalekit's SendGrid connector stores each user's key in its token vault, injects it on every execute_tool call, and collects replacement keys through the same hosted form, so the MCP vs API decision does not change your credential infrastructure.

Recommended reading: Why a token vault is critical for AI agent workflows

How to connect SendGrid to an agent with Scalekit

The model has two objects: a connection, created once per environment, and a connected account per user that holds that user's SendGrid key.

Configure the connection

Create the SendGrid connection once in the Scalekit dashboard under AgentKit > Connections, following the SendGrid connector docs. Each user then supplies their own key through a Scalekit-hosted form. The connection name in your code must match the dashboard name exactly; a mismatch is the most common integration error.

Ask users for a Restricted key with only the permissions the agent needs. What the user's key can't do, the agent can't do.

Install the SDK

pip install scalekit-sdk-python python-dotenv anthropic

Set SCALEKIT_ENVIRONMENT_URL, SCALEKIT_CLIENT_ID, and SCALEKIT_CLIENT_SECRET in .env, and ANTHROPIC_API_KEY for the Claude client.

Connect a user's SendGrid account

For API-key connectors, get_authorization_link opens a hosted form that collects the user's SendGrid key and stores it on their connected account. See Authorize a user for production handling.

import os from dotenv import load_dotenv from scalekit import ScalekitClient load_dotenv() scalekit_client = ScalekitClient( env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"), client_id=os.getenv("SCALEKIT_CLIENT_ID"), client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), ) actions = scalekit_client.actions CONNECTION_NAME = "sendgrid" # must match AgentKit > Connections exactly USER_ID = "user_123" # your app's opaque user ID response = actions.get_or_create_connected_account( connection_name=CONNECTION_NAME, identifier=USER_ID ) if response.connected_account.status != "ACTIVE": link = actions.get_authorization_link( connection_name=CONNECTION_NAME, identifier=USER_ID ) print("Connect SendGrid:", link.link) input("Press Enter after submitting your SendGrid key...") response = actions.get_or_create_connected_account( connection_name=CONNECTION_NAME, identifier=USER_ID ) if response.connected_account.status != "ACTIVE": raise RuntimeError(f"SendGrid is {response.connected_account.status}, not ACTIVE")

Why the tool surface has to be small

Loading all 391 SendGrid tools at roughly 200 tokens each would spend about 78,000 tokens before the agent does any work, and SendGrid's tool descriptions run longer than that estimate. Tool selection also degrades as the catalog grows. The fix is not better prompting. It is surface reduction.

Retrieve the tools this user is authorized to call

The agent does not load a connector catalog. list_scoped_tools returns the tools this user's connected account is authorized to call, filtered here to four tools for a delivery assistant.

from google.protobuf.json_format import MessageToDict SENDGRID_TOOLS = [ "sendgrid_send_mail", "sendgrid_list_template_templates", "sendgrid_list_suppression_bounces", "sendgrid_list_messages_by_filter", ] scoped_response, _ = actions.tools.list_scoped_tools( identifier=USER_ID, filter={"connection_names": [CONNECTION_NAME], "tool_names": SENDGRID_TOOLS}, page_size=100, ) llm_tools = [] for scoped_tool in scoped_response.tools: definition = MessageToDict(scoped_tool.tool).get("definition", {}) llm_tools.append({ "name": definition.get("name"), "description": definition.get("description", ""), "input_schema": definition.get("input_schema", {}), })

Run the Claude tool-use loop

Scalekit returns schemas with input_schema, the format Claude's tool use API expects. The loop runs until Claude stops requesting tools. See the Anthropic example for the Node.js version.

import anthropic from scalekit.common.exceptions import ScalekitToolException client = anthropic.Anthropic() messages = [{ "role": "user", "content": "Did the password reset email to dana@example.com bounce? " "If it did, explain why. Do not resend anything.", }] while True: response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, tools=llm_tools, messages=messages, ) if response.stop_reason != "tool_use": print("".join(b.text for b in response.content if b.type == "text")) break

Execute tool calls as the connected user

The rest of the same loop body runs each requested tool with execute_tool, returns results to Claude, and appends both turns to the history. SendGrid errors, such as a 403 from a key missing a permission, go back to Claude as error results.

tool_results = [] for block in response.content: if block.type != "tool_use": continue try: result = actions.execute_tool( tool_input=block.input, tool_name=block.name, connection_name=CONNECTION_NAME, identifier=USER_ID, ) content, is_error = str(result.data), False except ScalekitToolException as e: content, is_error = f"{e.tool_error_code}: {e.tool_error_message}", True tool_results.append({ "type": "tool_result", "tool_use_id": block.id, "content": content, "is_error": is_error, }) messages.append({"role": "assistant", "content": response.content}) messages.append({"role": "user", "content": tool_results})

Serve SendGrid over a Virtual MCP server

A Virtual MCP server gives each agent role one static MCP endpoint that exposes only the tools you allow. Each run authenticates with a short-lived session token bound to one user's connected accounts.

Why a Virtual MCP server fits SendGrid

A delivery-troubleshooting agent needs to read logs, bounces, and stats. It never needs to send. Leave sendgrid_send_mail off the server and a prompt injection has no send tool to reach, whatever the user's key allows. One server definition serves every user, with no MCP server to deploy, host, or maintain. Multi-tool agents add mappings for other connections, such as Zendesk or Slack, to the same server.

Create the server once per agent role

Run this once, not per user. The response carries a static mcp_server_url you reuse for every session. The Virtual MCP setup guide covers updates and deletion.

from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping vmcp = actions.mcp.create_config( name="sendgrid-delivery-assistant", description="Read-only SendGrid tools for delivery troubleshooting", connection_tool_mappings=[ McpConfigConnectionToolMapping( connection_name=CONNECTION_NAME, tools=[ "sendgrid_list_messages_by_filter", "sendgrid_get_message_by_id", "sendgrid_list_suppression_bounces", "sendgrid_list_stat_stats", ], ), ], ) config_id = vmcp.config.id mcp_server_url = vmcp.config.mcp_server_url

Mint a session token before each run

Check that the user's connections are active, then mint a token. Tokens default to about one hour. There is no refresh endpoint; call create_session_token again to remint.

from datetime import timedelta state = actions.mcp.list_mcp_connected_accounts( config_id=config_id, identifier=USER_ID, include_auth_link=True ) for account in state.connected_accounts: if (account.connected_account_status or "").upper() != "ACTIVE": raise RuntimeError( f"{account.connection_name} needs auth: {account.authentication_link}" ) session_token = actions.mcp.create_session_token( mcp_config_id=config_id, identifier=USER_ID, expiry=timedelta(minutes=30), ).token

Connect a LangChain agent over MCP

Install the adapters with pip install "langchain-mcp-adapters>=0.3,<1" langchain-anthropic, then pass the URL and token as bearer auth. The LangChain example shows the same pattern.

import asyncio from langchain_anthropic import ChatAnthropic from langchain_core.messages import HumanMessage, ToolMessage from langchain_mcp_adapters.client import MultiServerMCPClient async def run(question: str) -> None: mcp_client = MultiServerMCPClient({ "sendgrid": { "transport": "streamable_http", "url": mcp_server_url, "headers": {"Authorization": f"Bearer {session_token}"}, } }) tools = await mcp_client.get_tools() tool_map = {t.name: t for t in tools} llm = ChatAnthropic(model="claude-sonnet-4-6").bind_tools(tools) messages = [HumanMessage(question)] while True: response = await llm.ainvoke(messages) messages.append(response) if not response.tool_calls: print(response.content) return for tc in response.tool_calls: result = await tool_map[tc["name"]].ainvoke(tc["args"]) messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"])) asyncio.run(run("Find recent messages to dana@example.com and explain any bounce."))

TypeScript and Mastra agents

TypeScript agents, including Mastra, connect to the same URL with the token as a Bearer header. Mint the token on a backend with the Python SDK, since the Node.js SDK does not create MCP session tokens yet.

Observe every SendGrid call your agent makes

SendGrid's logs identify the key. An agent in production also needs to identify the user and the tool call behind each request. Understanding agent tool observability is essential for production deployments.

Tool-calling auth logs per connected account

Every execute_tool call returns an execution_id. The Scalekit dashboard shows status and tool execution logs per connected account under AgentKit > Connected Accounts. For token and session events across your app, see Scalekit auth logs. That closes the gap SendGrid's Email Logs leave: SendGrid records which api_key_id sent a message, and Scalekit records which user identifier and which tool made the call.

Failures carry the same identifiers

Upstream SendGrid errors raise ScalekitToolException with tool_error_code, tool_error_message, and execution_id. A 403 from a key without Email Activity permission traces to one user and one call. Subscribe to the connected_account.status_updated webhook to act when an account leaves ACTIVE, and see Manage connected accounts for reconnect flows.

Which one to build against

If your agent is a local experiment against your own test account, a community MCP server is fast, provided you read its source, pin its version, and give it a Restricted key. If your agent sends deterministic, single-tenant transactional email, call the v3 API from your own code and keep the key out of model context. If your agent acts for many customers with their own SendGrid accounts, or needs a scoped MCP endpoint without hosting one, use a connector that vaults each key and scopes each tool surface. Every path runs on the same long-lived key. The decision that matters is where that key lives and how narrow the surface around it is.

The patterns involved here parallel the broader challenge of credential ownership across agent tool-calling patterns — a structural question that every multi-tenant AI product must answer.

Build your SendGrid agent with Scalekit

Browse the Scalekit SendGrid connector, start from the SendGrid connector docs, or compare plans on Scalekit pricing.

Building a SendGrid agent now? Talk to us for immediate help with key scoping, Virtual MCP setup, or multi-tenant rollout.

No items found.
Agent
Auth Quickstart
On this page
Share this article
Agent
Auth Quickstart

Acquire enterprise customers with
‍zero upfront cost.

Every feature unlocked. No hidden fees.