Your customers are starting to work differently. Instead of opening your dashboard, they open Claude, ChatGPT or Cursor and type what they want done: "create an invoice for Acme," "show me last week's signups," "add this bug to the roadmap." If your SaaS can't be reached from inside those assistants, the assistant either fails or recommends a competitor that can.
The way to make your product reachable is an MCP server. This guide explains how to add an MCP server to an existing SaaS, step by step. It covers what to expose, how to design tools an AI model actually uses well, how to handle authentication with OAuth, where to host it, how to keep it secure, and how to get it in front of users.
I've built MCP connectors for my own products, including a notes app, a bookmark manager and a changelog/roadmap SaaS. Everything below comes from doing that, not from reading about it.
TL;DR
- An MCP server is a thin layer on top of your existing backend. It lets AI assistants discover and call your product's features as tools.
- For a SaaS, build a remote MCP server over Streamable HTTP. Local stdio servers are for developer tools that run on the user's machine.
- Tool design matters more than code. Expose outcomes ("create_invoice"), not raw endpoints ("POST /v2/invoices").
- Use OAuth 2.1 so every user connects their own account with scoped permissions.
- Cloudflare Workers is one of the fastest ways to host a remote MCP server, with OAuth handled by an official library.
- Test with MCP Inspector, then with real clients. Then publish a docs page and submit to connector directories.
What an MCP server means for a SaaS
MCP (Model Context Protocol) is an open standard that defines how AI applications connect to external tools and data. If you're new to it, start with my complete MCP guide and the breakdown of MCP vs API.
The short version for SaaS founders:
- Your API is built for developers. They read docs, write code and hard-code the calls.
- Your MCP server is built for AI models. The model reads a list of tools with names, descriptions and input schemas, decides which one fits the user's request, and calls it.
You don't replace your API. The MCP server usually sits on top of it and calls the same business logic. Think of it as a new front door to your product, one that AI assistants know how to open.
An MCP server can expose three kinds of things:
| Primitive | Who controls it | SaaS example |
|---|---|---|
| Tools | The model decides when to call them | create_invoice, search_customers, publish_changelog_post |
| Resources | The application loads them as context | A customer record, a project's settings, a document |
| Prompts | The user picks them, like templates | "Weekly revenue summary," "Draft release notes" |
For most SaaS products, tools carry 90% of the value. Start there.
Why add an MCP server to your SaaS
1. Distribution. AI assistants are becoming a place where people discover and use software. A listed, working connector puts your product in front of users at the exact moment they need it.
2. Retention. When your product is wired into the assistant a customer uses all day, switching away gets more expensive. Integrations have always reduced churn, and MCP is an integration that works across many AI clients at once.
3. One integration, many clients. Before MCP, you needed a separate plugin for every AI platform. With MCP you build one server, and any compatible client can use it: Claude, ChatGPT, Cursor, VS Code and a growing list of agents.
4. It's a selling point. "Works with Claude and ChatGPT" is a line that closes deals right now, especially in B2B.
Before you write code: decide what to expose
The biggest mistake I see is wrapping every API endpoint as a tool. A model handed 80 tools gets confused, picks the wrong ones and burns context. Start with a short list.
Ask three questions:
- What do users repeatedly do in my product that they'd rather say than click? Creating things, searching, summarizing and updating statuses are the usual winners.
- What questions do users ask about their own data? "How many…", "Which customers…", "What changed since…". Those become read tools.
- What would be dangerous if the model got it wrong? Deleting data, sending emails to customers, charging cards. Leave these out of version one, or put them behind confirmation (more on that below).
A good first release is 5–10 tools. For a changelog SaaS that might be: list_projects, list_posts, create_post, update_post, list_roadmap_items, list_feedback. Small and useful beats complete and confusing.
Step 1: Choose the transport (remote vs local)
MCP defines two standard transports:
- stdio (local). The server runs as a process on the user's computer, and the client talks to it through standard input and output. Credentials come from environment variables. This is right for developer tools like a local database or a file system.
- Streamable HTTP (remote). The server runs on the internet at a URL like
https://mcp.yourproduct.com/mcp. Clients connect over HTTPS. This is what a SaaS needs. Your users shouldn't have to install Node.js to talk to your product.
Two things worth knowing about the current spec:
- The older HTTP + SSE transport (separate SSE endpoint plus a POST endpoint) is deprecated. If a tutorial tells you to build an
/sseendpoint as your main transport, it's out of date. - The 2026-07-28 revision made the core protocol stateless. Protocol-level sessions are gone, and each request carries its own protocol version and client capabilities. For a SaaS this is good news: stateless servers scale horizontally and fit serverless platforms like Cloudflare Workers perfectly.
The MCP spec is versioned by date and moves fast. Always check the current revision and your SDK's changelog before you build.
Step 2: Design tools the model will actually use
This is the step that decides whether your MCP server feels magical or broken. The model only knows what you tell it in the tool name, description and schema.
Name tools after outcomes
- ✅
create_invoice,search_customers,get_monthly_revenue - ❌
postInvoicesV2,query,doAction
Use verb_noun in snake_case and stay consistent.
Write descriptions for a model, not a developer
The description is a prompt. Say what the tool does, when to use it and when not to use it.
Weak: "Creates a post."
Strong: "Create a new changelog post for a project. Use this when the user wants to announce a release, fix or new feature. Posts are created as drafts unless
publishis true. Calllist_projectsfirst if you don't know the project ID."
That last sentence matters. It teaches the model the order of operations.
Keep input schemas tight
- Use enums instead of free text where you can (
status: "draft" | "published"). - Mark required fields as required and give sensible defaults to the rest.
- Accept human-friendly inputs. Let the model pass a project name and resolve it to an ID on your side, instead of forcing an extra lookup.
- Add a short description to every parameter.
Return results the model can reason about
- Return compact, structured data: the fields that matter, not your entire database row with 40 columns.
- Paginate large lists and tell the model how to get the next page.
- Include IDs and URLs, so the model can link the user to the exact place in your app.
Make errors helpful
An error is also a prompt. "Error 400" teaches the model nothing. "Project 'Mobile App' not found. Available projects: Website, API, Dashboard." lets it fix itself and retry.
Bundle workflows into one tool
If users always do A → B → C, consider one tool that does all three. Fewer round trips means faster, cheaper and more reliable results.
Step 3: Authentication and authorization (OAuth 2.1)
A SaaS MCP server acts on behalf of a specific user, so it has to know who is calling and what they're allowed to do.
The standard way: OAuth 2.1
The MCP authorization spec is built on OAuth 2.1. The flow, from the user's side:
- The user adds your connector in their AI client by pasting your MCP URL or clicking it in a directory.
- The client discovers your authorization server through metadata your MCP server publishes.
- The user is sent to your login page, signs in and approves the requested permissions.
- The client receives an access token and sends it with every MCP request.
- Your server validates the token and runs each tool as that user, inside their workspace.
Key points:
- In current versions of the spec, your MCP server is a resource server, separate from the authorization server role. You can use your existing identity provider (Auth0, Clerk, WorkOS, Cognito, Cloudflare Access) as the authorization server, or run your own.
- Support Dynamic Client Registration if you want arbitrary MCP clients to connect without manual setup.
- Use PKCE, validate the
iss(issuer) in the authorization response, and issue short-lived tokens with refresh. - Define scopes (
invoices:read,invoices:write) so users can grant read-only access.
The shortcut: API keys
For personal tools, internal tools or early beta testers, a per-user API key in a header is simpler and works fine. Many of my own internal MCP connectors started this way. For a public SaaS connector listed in directories, plan for OAuth from the start, because that's what users and platforms expect.
Multi-tenancy is not optional
Every query your tools run must be scoped to the authenticated user's account or workspace. Never let a tool accept an account_id from the model and trust it. Derive it from the token.
Step 4: Build the server
Official MCP SDKs exist for TypeScript, Python, Go, Java, C#, Kotlin and more. Pick the one that matches your backend.
Here's a minimal TypeScript sketch of a single tool. Treat it as a shape rather than copy-paste code, since SDK APIs change between releases:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
const server = new McpServer({ name: "acme-invoices", version: "1.0.0" });
server.registerTool(
"create_invoice",
{
title: "Create invoice",
description:
"Create a draft invoice for an existing customer. Use search_customers first if you only know the customer's name.",
inputSchema: {
customerId: z.string().describe("Customer ID from search_customers"),
items: z.array(
z.object({
description: z.string(),
quantity: z.number().int().positive(),
unitPrice: z.number().positive().describe("Price in the account currency"),
})
),
dueInDays: z.number().int().default(14),
},
},
async ({ customerId, items, dueInDays }, ctx) => {
const user = ctx.auth; // resolved from the OAuth token, never from model input
const invoice = await invoices.create(user.accountId, { customerId, items, dueInDays });
return {
content: [
{
type: "text",
text: JSON.stringify({ id: invoice.id, total: invoice.total, url: invoice.url }),
},
],
};
}
);Notice that the tool calls your existing service layer (invoices.create). The MCP server shouldn't duplicate business logic. It translates between the model and your product.
Step 5: Host it (Cloudflare Workers and alternatives)
A remote MCP server is just an HTTPS service, so you can host it anywhere. I use Cloudflare Workers for almost everything (I wrote about my full Cloudflare, D1 and Paddle stack). For MCP it's a particularly good fit:
- McpAgent in the Cloudflare Agents SDK handles the remote transport for you.
- workers-oauth-provider is an OAuth 2.1 provider library that wraps your Worker and handles authorization, token and client registration endpoints.
- Starter templates exist for both an authless server and one with OAuth (for example, GitHub login).
- It runs at the edge, scales to zero, and the free tier covers a lot of early usage.
- If your SaaS already lives on Workers with D1 or KV, the MCP server can talk to the same bindings directly.
A simplified shape:
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
export class AcmeMCP extends McpAgent<Env> {
server = new McpServer({ name: "acme", version: "1.0.0" });
async init() {
// register your tools here, using this.env for D1/KV bindings
// and this.props for the authenticated user
}
}Your server ends up at a URL like https://acme-mcp.yourname.workers.dev/mcp, or behind your own domain at mcp.yourproduct.com.
Alternatives: Vercel and Netlify functions, AWS Lambda, Fly.io, Railway, or simply a new route in your existing Node, Python or Go backend. If you already have a backend with good auth, adding an /mcp route is often the least work.
Step 6: Security (don't skip this)
Giving an AI model the keys to a user's account deserves real care.
- Prompt injection. Content your tools return (customer notes, emails, ticket text) can contain instructions written by an attacker. Treat everything as data. Never let returned content trigger privileged actions on its own.
- Destructive actions. Mark tools that delete, send or charge as destructive using tool annotations, so clients can ask the user to confirm. For the riskiest actions, require an explicit
confirm: trueparameter, or return a preview first and execute on a second call. - Least privilege. Scope tokens tightly and offer read-only access.
- Rate limiting. Models can call tools in fast loops. Add per-user limits, just like on your public API.
- Tenant isolation. Covered above, but it's worth repeating: derive the account from the token, always.
- Audit logs. Log which tool was called, by which user and through which client. When something odd happens, you'll want this.
- No secrets in responses. Never return API keys, tokens or internal IDs the user shouldn't see.
Step 7: Test it
- MCP Inspector. The official debugging tool. Connect to your server, list tools, call them with test inputs and check the responses and errors.
- Real clients. Add your server as a custom connector in Claude, ChatGPT and Cursor. Then try real requests the way a user would phrase them: vague, incomplete, with typos.
- Watch tool selection. If the model picks the wrong tool or calls things in the wrong order, the fix is almost always in your names and descriptions, not in the code.
- Test the auth flow end to end, including token expiry and revoked access.
Step 8: Launch and get discovered
Building it is half the job. Users need to find it.
- Docs page. A page at
yourproduct.com/mcpor in your docs with the server URL, the list of tools and setup steps for each major client. Write it so it ranks for "[your product] MCP" and "[your product] Claude integration." - Connector directories. Submit to the connector directories of the major AI clients and to public MCP registries. Each has its own review requirements, usually around OAuth, privacy policy and tool annotations.
- llms.txt. Mention your MCP server in your site's
llms.txt, so AI assistants that read it know the integration exists. - Announce it. Changelog post, email to customers, a short demo video showing one real request going from prompt to result. MCP demos are very visual and get shared.
Common mistakes
- Wrapping every endpoint. Start with 5–10 outcome-based tools.
- Vague descriptions. The model can't read your mind or your docs.
- Returning huge payloads. They waste context and slow everything down.
- Building on the deprecated SSE transport. Use Streamable HTTP.
- Trusting IDs from the model. Scope everything from the token.
- No confirmation on destructive tools. One misunderstood prompt can delete real data.
- Shipping without a docs page. If nobody knows it exists, it doesn't exist.
How long does it take, and what does it cost?
It depends mostly on how clean your existing backend is.
- Your SaaS already has a solid API and auth. A focused MCP server with 5–10 well-designed tools, OAuth and hosting is usually a matter of days to two weeks of work.
- Business logic is tangled inside your frontend, or there's no proper API. Expect extra time to pull that logic into a service layer the MCP server can call. That work also improves the rest of your product.
- Hosting. On Cloudflare Workers, an MCP server for an early-stage SaaS often costs little or nothing until usage grows.
FAQ
Do I need an MCP server if I already have a public API? Yes, if you want AI assistants to use your product well. An API is designed for developers who write code. An MCP server gives models discoverable tools with descriptions and schemas they can reason about. The MCP server usually calls your API or service layer underneath.
Will my MCP server work with ChatGPT, Claude and Cursor? That's the point of the standard. A spec-compliant remote MCP server works with any client that supports remote MCP. Each platform may still have its own listing and review process.
Should I build a local (stdio) or remote MCP server for my SaaS? Remote, over Streamable HTTP. Local stdio servers make sense for developer tools that need access to the user's machine. Not for a web product.
Is OAuth required? Authorization is technically optional in the spec, but for a public SaaS connector, OAuth 2.1 is the expected and safest approach. API keys are fine for internal tools and betas.
Can an MCP server expose my users' data to the wrong people? Only if you build it wrong. Scope every tool to the authenticated user's account, use least-privilege scopes and never trust account IDs supplied by the model.
What language should I use? The one your backend already uses. Official SDKs cover TypeScript, Python, Go, Java, C# and more. TypeScript on Cloudflare Workers is a very fast path.
Can an MCP server help my SaaS get more customers? It can. Listings in AI connector directories, "works with Claude/ChatGPT" on your landing page, and lower churn from deeper integration all add up. It's a distribution channel, not just a feature.
Want an MCP server for your product?
I design and build MCP servers for SaaS products end to end: tool design, OAuth, hosting on Cloudflare Workers, security and the docs page that gets it discovered. If you want your product usable from Claude, ChatGPT and Cursor, write to me at collab@vhirty.com with a short description of your product and what your users do most often in it.