Skip to content
Dev ResourcesUpdated October 1, 2026

Ship an MCP Server With Your Product, and Get It Found

You launched the product. Launch two is the MCP server that lets Claude Code, Cursor, Codex and ChatGPT use it without a browser. Transport, clean stdout, OAuth the way clients expect it, install in every client, and the registry, card and skill that get it found.

MCP · Launch 2
Your product, usable from inside the agent.
MCPModel Context ProtocolClaude CodeCursorCodexOAuthAI AgentsDeveloper ToolsDistributionTutorial
Cost
$0 to $5/mo
Time
~25 min
Steps
7
Stack
Streamable HTTP · OAuth 2.1 · Registry
What you'll get
  • ✓A remote MCP server that Claude Code, Cursor, Codex and ChatGPT can all install
  • ✓The three rules that stop a working server from silently failing in a client
  • ✓A registry listing, a server card and a skill served from the same endpoint
  • ✓Your own score on the nine public checks before anyone else grades it
Share

You launched your product. That is launch one. Launch two is the MCP server you ship with it, so an agent in Claude Code, Cursor, Codex or ChatGPT can use your product without opening a browser, and your product shows up where buyers now spend their working day.

The numbers say this is not a side quest. Sentry's MCP server handles about 50 million requests a month. dbt's docs tool went from 338 calls in March to 6,774 in the first two weeks of May. Context7, a documentation server from Upstash, pulls close to 890,000 npm downloads a week. And one founder who shipped MCP plus OAuth and got into Claude's connector directory reported 109 visitors, 41 signups and 13 paid trials in a weekend, from a listing.

The other number matters too. The official registry had 31,309 servers in mid-September, and 22.6% of them had no source repository. One account had published 1,505. A server that exists is not a server that gets installed. Most servers I grade fail on things that take ten minutes to fix: one stray log line on stdout, a scope that makes the server vanish in the next project, no paste-able config, nothing for an agent to discover.

This is the order I would do it in today.

Step 1 of 7
3 min

Pick the transport

At a glance
Product
Streamable HTTP on your own domain
Local CLI
stdio, and only then
Spec
2026-07-28

MCP gives you two transports. stdio runs the server as a subprocess of the client on the user's machine. Streamable HTTP runs it on your domain and the client connects over HTTPS. For a product, pick HTTP. Three reasons:

  • ChatGPT only connects to remote HTTPS servers, and it requires two tools named search and fetch to treat you as a connector at all.
  • Your users' data already lives on your side. A remote server reads it with their session, so you never ship credentials into a config file on a laptop.
  • You can update the server without asking anyone to reinstall. A stdio server is a package version on someone else's disk.

The current specification is dated July 28, 2026. Start from the TypeScript SDK and the Streamable HTTP transport. A minimal server is under forty lines:

src/mcp.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { z } from "zod";

export const server = new McpServer({ name: "yourproduct", version: "1.0.0" });

server.registerTool(
  "search_launches",
  {
    title: "Search launches",
    description: "Find products launched on Nick Launches by keyword. Returns name, tagline, URL and upvotes.",
    inputSchema: { query: z.string().min(2), limit: z.number().int().max(25).default(10) },
  },
  async ({ query, limit }) => {
    const rows = await db.searchProducts(query, limit);
    return { content: [{ type: "text", text: JSON.stringify(rows) }] };
  },
);

// One transport per request; stateless is fine for most SaaS tools.
export async function handleMcp(req: Request): Promise<Response> {
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
  await server.connect(transport);
  return transport.handleRequest(req);
}

Mount that at https://mcp.yourproduct.com/mcp or /api/mcp on your main domain. Keep it on a domain you own: a server on your own hostname is one of the things the registry can verify, and it is what a reader trusts when the install line shows up in a README.

When stdio is still right
You ship a CLI that already runs on the user's machine and reads local files. Then the agent should run it locally too. Everything else in this guide still applies, and step 2 applies twice.
Step 2 of 7
2 min

Keep stdout clean

At a glance
Rule
Nothing on stdout but MCP messages
Logs
stderr, or a file
Symptom
JSON-RPC -32700 parse errors

The specification is blunt about it: the server MUST NOT write anything to its stdout that is not a valid MCP message. stderr may be used for logging. In a stdio server, stdout is the wire. One console.log("connected") and the client reads a line that is not JSON-RPC, throws a -32700 parse error, and your server "works in the terminal" and breaks in every client.

Route every log to stderr on day one, including the ones from libraries you import.

src/log.ts
// stdout belongs to the protocol. Everything human goes to stderr.
export const log = (...args: unknown[]) => console.error(new Date().toISOString(), ...args);

// Belt and braces for a stdio build: make console.log unusable.
if (process.env.MCP_TRANSPORT === "stdio") {
  console.log = (...args) => console.error("[redirected from stdout]", ...args);
}

Remote servers do not have this failure, which is another point for HTTP. They have a cousin: anything you print into a tool result is what the model reads, so a stack trace in a result is a stack trace in the agent's context. Return a short error string and log the rest.

Test with the Inspector before any client
npx @modelcontextprotocol/inspector speaks the protocol and shows you the raw frames. If the Inspector lists your tools, the client will. If it does not, no amount of config on the client side fixes it.
Step 3 of 7
5 min

Write tools an agent will actually pick

At a glance
Description
Point first, under 2,048 chars
Result size
Under 10k tokens
Count
Few tools, verbs, not your whole API

The agent never reads your docs. It reads the tool list, picks one, and sends arguments. So the description is the product page now, and the client constrains it. In Claude Code, server instructions and tool descriptions are truncated at 2,048 characters. Tool results over about 10,000 tokens get a warning, and results over 50,000 characters are written to disk and the model gets a pointer unless the server raises _meta["anthropic/maxResultSizeChars"]. Lead with the point. Say what comes back.

  • Name tools as verbs on your nouns. search_launches, get_product, submit_product. Not api_v2_products_list.
  • Return what the agent needs, not the row. Spec0, a launch here in September, serves Cursor and Claude Code one API operation at a time over MCP: about 300 tokens instead of a whole OpenAPI spec pasted into the chat. That is the shape to copy.
  • Five tools beat fifty. Claude Code's Tool Search only loads a tool into context when it matches; a tool marked anthropic/alwaysLoad is exempt. Mark the one or two that define your product and let the rest be found.
  • Ship prompts and resources if they are real. They are worth 16 of the 100 points in step 7, but a prompt nobody would use costs you trust with the one person who tries it.
Write the description for the model, then read it as a human
If the first sentence does not tell a stranger when to call this tool and what they get back, the model will not call it either. "Returns the product's DR, traffic estimate and dofollow flag" beats "Fetches product metadata".
Step 4 of 7
6 min

OAuth 2.1 the way clients expect it

At a glance
Discovery
/.well-known/oauth-protected-resource
Clients
Client ID Metadata Documents, not DCR
Identity
Read sub, not client_id

A remote server for a product means someone's account is behind it, so the client needs to log the user in. The flow every client implements now is OAuth 2.1 with an external authorization server, discovered from a 401:

  1. The client calls your MCP endpoint with no token. You answer 401 with a WWW-Authenticate header pointing at your protected resource metadata (RFC 9728).
  2. The client fetches /.well-known/oauth-protected-resource, finds your authorization server, and starts the authorization code flow with PKCE.
  3. The user signs in on your domain, consents, and the client comes back with a token scoped to your server as the resource.
src/oauth.ts
// 1. The 401 that starts discovery
return new Response("Unauthorized", {
  status: 401,
  headers: {
    "WWW-Authenticate":
      'Bearer resource_metadata="https://mcp.yourproduct.com/.well-known/oauth-protected-resource"',
  },
});

// 2. The metadata the client fetches next
// GET /.well-known/oauth-protected-resource
{
  "resource": "https://mcp.yourproduct.com/mcp",
  "authorization_servers": ["https://auth.yourproduct.com"],
  "scopes_supported": ["launches:read", "launches:write"],
  "bearer_methods_supported": ["header"]
}

Three things trip up servers that otherwise work:

  • Use Client ID Metadata Documents, not Dynamic Client Registration. Claude, ChatGPT and the WorkOS write-up all point the same way: the client identifies itself with a hosted JSON document, and you do not keep a registration table that fills with abandoned clients.
  • client_id is the app, sub is the user. Every Claude Code user arrives with the same client_id. Read sub for who they are, and reject any session token that has no client_id at all.
  • Redirect URIs match exactly. localhost and 127.0.0.1 are different strings. Register both.
Claude Code and environment variables in headers
When Claude Code expands variables in a remote server's headers, anything named ANTHROPIC_* reads as empty. Name your own variables after your product.

If you run Next.js with Supabase or Clerk, Makerkit's August write-up walks the whole flow on Next.js 16 and is the closest thing to a copy-paste reference. If you would rather not run an authorization server, WorkOS and Clerk both sell one that speaks this exact dance.

Step 5 of 7
4 min

Get installed in every client

At a glance
Claude Code
--scope user, or .mcp.json
Cursor
Deeplink + ~/.cursor/mcp.json
Codex
codex mcp add

Your README needs one paste-able mcpServers block per client, not a sentence that says "add it to your MCP config". A bare npx line leaves the reader to work out where it goes. The JSON block is what the client consumes, and it is one of the nine checks in step 7.

Claude Code. The default scope is local, which means the server exists in the current project folder only. That is why a server you added on Monday has vanished by Wednesday in another repo. Tell users to pick a scope:

Terminal · Claude Code
# For the user, every project (stored in ~/.claude.json)
claude mcp add --transport http --scope user yourproduct https://mcp.yourproduct.com/mcp

# For a team, checked in with the repo (stored in .mcp.json)
claude mcp add --transport http --scope project yourproduct https://mcp.yourproduct.com/mcp
.mcp.json
{
  "mcpServers": {
    "yourproduct": {
      "type": "http",
      "url": "https://mcp.yourproduct.com/mcp"
    }
  }
}

Cursor. Global config lives in ~/.cursor/mcp.json, project config in .cursor/mcp.json, same mcpServers shape. Cursor also takes an install link you can put behind a button, with the config base64-encoded:

Install link
cursor://anysphere.cursor-deeplink/mcp/install?name=yourproduct&config=eyJ1cmwiOiJodHRwczovL21jcC55b3VycHJvZHVjdC5jb20vbWNwIn0=

Codex. Servers live in ~/.codex/config.toml under [mcp_servers.yourproduct], or run codex mcp add.

~/.codex/config.toml
[mcp_servers.yourproduct]
url = "https://mcp.yourproduct.com/mcp"

ChatGPT. Connectors must be remote, over HTTPS, with search and fetch tools present, and OAuth is strongly recommended. If your server has those two tools it can be a connector; if it does not, ChatGPT will not list it, however good the rest is.

Four blocks, one page
Put all four install blocks on one page at /mcp on your own site, link it from the README and the registry listing, and keep the hostname identical everywhere. The score check in step 7 for "deployment" looks for exactly that: a server on a domain that is yours.
Step 6 of 7
4 min

Registry, server card, skill

At a glance
Registry
registry.modelcontextprotocol.io
Server card
Draft SEP, say so
Skills
Extension final 2026-09-13

The official registry is where clients and directories pull from. Publish with a namespace you can verify (your GitHub org or your domain), a real description, and the source repo. The registry solves listing, not vouching: with thousands of near-duplicate entries from a handful of accounts, a verified domain namespace and a repo are what separate you from the noise. Check the registry's spam policy before you automate anything; rate limits were still "under consideration" in October.

A server card is a small JSON document that tells an agent "there is an MCP server for this site, here is how to connect". It is useful, and it is still a draft. SEP-2127 was approved by the community maintainers on August 13 and has not been merged into the specification; the proposed well-known path has moved from /.well-known/mcp/server-card.json to /.well-known/mcp/server-cards.json, and the experimental extension recommends GET <mcp-url>/server-card instead. Serve one, keep tool lists out of it (cards describe connectivity, not tools), and label it experimental in your docs so nobody files a bug when the path changes again.

GET https://mcp.yourproduct.com/mcp/server-card
{
  "name": "yourproduct",
  "version": "1.0.0",
  "description": "Search, read and submit launches on Nick Launches from your agent.",
  "endpoint": "https://mcp.yourproduct.com/mcp",
  "transport": "streamable-http",
  "authentication": { "type": "oauth2", "resource_metadata": "https://mcp.yourproduct.com/.well-known/oauth-protected-resource" },
  "documentation": "https://yourproduct.com/mcp"
}

Skills over MCP went final on September 13 as the io.modelcontextprotocol/skills extension: your server answers skills/list and skills/get, and the skill's files come through resources/read. In September about 270 providers were shipping a server and a skill as two separate installs. Serve both from the one server: the MCP tools give the agent access, the skill tells it the workflow. Client support is still rolling out, so keep the standalone skill folder too.

Then put a line in llms.txt pointing at your /mcp page. It is the one discovery file agents already read today.

Step 7 of 7
1 min

Pre-grade yourself on the nine checks

At a glance
Scale
100 points, nine checks
Required
Deployment, tools, README
Cap
B unless the server can be run

Every MCP server on the AI agents board gets the same public score, whether the owner claimed the listing or not. The score never moves a listing up the rankings and nobody can pay to change it, so it is a straight reading of the server. The nine checks and what each is worth:

  • Validated, 20. The server answered a real handshake: tools/list over HTTP, or a sandboxed stdio run of the npm package.
  • Deployment, 15, required. A reachable endpoint or a runnable package on a domain or namespace that is yours.
  • Tools, 15, required. A verified tool table, not a list in the README.
  • Easy install, 12. A paste-able mcpServers config block. A bare npx line does not count.
  • README, 10, required. Present, and it describes this server.
  • License, 8. A license file. Anthropic's own skills repo shipped without one for a while; do not copy that.
  • Prompts, 8. prompts/list returns something real.
  • Resources, 8. resources/list returns something real.
  • Claimed, 4. The owner claimed the listing.

The four introspection checks, 51 points between them, only count when the board can actually run the server: an npm package or a public HTTP endpoint. A Docker-only or build-it-yourself server leaves them out of the denominator and the grade caps at B. Before that rule existed, 274 of 291 listings were an F, and 127 of them could never have been anything else. The cap is what keeps it honest: no server reaches A without a verified tool table.

The one fix that jumps the score most
If you are on stdio with no public endpoint, standing up the Streamable HTTP build from step 1 on your own domain turns four "not checkable" items into points and lifts the cap. It is usually a one-afternoon change.

What it actually costs

  • Hosting: $0 on the free tier of a Worker or a small Node host, up to $5 a month with traffic.
  • Authorization server: $0 if you run your own; hosted options start free and scale with users.
  • Registry listing, server card, skill: $0.

The cost is engineering time, and most of it is the OAuth step. Budget an afternoon for the server and a day for auth if you have never implemented a resource server before.

Troubleshooting

  • Works in the Inspector, fails in Claude Code with a parse error. Something wrote to stdout. Grep for console.log and for libraries that print banners.
  • Server disappears when you open another repo. It was added with the default local scope. Re-add with --scope user.
  • OAuth loop that never completes. Nine times out of ten the redirect URI differs by localhost versus 127.0.0.1, or the token's audience is not your MCP endpoint.
  • ChatGPT will not add the connector. It needs search and fetch tools with those exact names, over HTTPS.
  • Tool never gets called. Read the description out loud. If it does not say when to call it and what comes back in the first sentence, rewrite it.

You're set

A product with an MCP server is a product an agent can recommend and then use in the same breath. List it on the AI agents board once it passes the three required checks, and launch it here the same week; a launch that arrives with an install block in the description gets a different kind of reader.

Want to know how the rest of your site reads to an agent? The AI readiness test runs fourteen checks on any URL, free and without an account, and it will tell you whether your llms.txt actually points at the server you just shipped.

Disclaimer: I run the AI agents board that computes the score in step 7. Nothing in it can be bought, and the checks and weights are public on every listing. Specification details are current to the 2026-07-28 revision and the skills extension of September 13, 2026; the server card is a draft and may change.

Share

Get the weekly recap

New dev-resource guides, top launches, and what's worth a look. One email a week.