Building an MCP Server: A Practical Guide
By CorpusIQ
Building a Model Context Protocol server is easier than it looks, and harder than it reads. Easier because the protocol is well-documented and the SDKs are solid. Harder because your tool descriptions, not your code, determine whether ChatGPT, Claude, and Perplexity actually use your server. CorpusIQ runs a production MCP server exposing 22+ business tools to all three LLMs; this guide is what we wish we had known before writing it.
Step 1, decide on transport
MCP supports two transports: stdio for local servers and HTTP+SSE for remote servers. Pick stdio when the tool runs on the user machine and the LLM client (typically Claude Desktop or Claude Code) can spawn it as a subprocess. Pick HTTP+SSE when the tool has to serve many users, persist state, or hold OAuth tokens centrally.
Stdio is simpler to build and easier to ship as an open-source tool. HTTP+SSE is heavier (you need hosting, TLS, authentication) but is the only option for a managed service. CorpusIQ is HTTP+SSE on Microsoft Azure because multi-tenant OAuth to 22+ SaaS vendors is easier to operate centrally than to distribute as a local binary.
Step 2, define tools with descriptions the model can act on
A tool definition has a name, a parameter schema, and a description. The description is the single most important piece. It is the text the LLM reads when deciding whether to call your tool for a given prompt. If the description is vague, the LLM will pick another tool; if it is overly specific, the LLM will never match a prompt to it.
Good descriptions name the action, the scope, the key filters, and what the result contains. Write descriptions for a reader who has never seen your tool before and has a few seconds to pick one of fifty options. Generic verbs like query almost never route well; specific verbs like list overdue invoices with explicit parameter names do.
Step 3, authenticate users, not models
MCP itself does not specify authentication. Your server handles it. For consumer-facing MCP servers like CorpusIQ, that means OAuth per downstream connector. The user authorizes Gmail once; your server stores the token, refreshes it on expiry, and presents Gmail tools in the MCP tool list only when the user has an active connection.
The LLM never sees the token. The MCP session is authenticated per-user (typically via a session cookie or signed request from the LLM client); the downstream SaaS token stays on the server. This is where plugin-style approaches, where the LLM sees the token, fail for multi-tenant business data.
Step 4, design for rate limits and errors
Downstream APIs throttle. When Gmail rate-limits, your MCP tool call has to degrade gracefully. Return a structured error that the LLM can explain to the user, not a raw HTTP 429. A plain-English message about the downstream rate limit is a better answer than a crash. Structured error content also keeps the LLM from retrying the same tool immediately.
On your side, implement per-tenant token buckets and per-connector ceilings so one busy user does not exhaust your shared API quota.
Step 5, observe, do not trust
Instrument tool calls from day one. Which tools get picked. Which prompts miss. Which tools error and why. CorpusIQ logs every tool call as an audit event, including the LLM client, the tool name, argument shape, and outcome category. That data drives the description-tuning loop (step 2) and the connector capability planning process.
For production business data, logging also matters for compliance. CorpusIQ is CASA Tier 2 certified, hosted on Microsoft Azure, and stores zero customer data; the audit log records metadata about calls, not payloads, so retention is bounded and redaction is unnecessary.
Step 6, ship it, then iterate on descriptions
The gap between a working MCP server and an MCP server that Claude actually uses for the right prompts is a month or two of description tuning. Ship the first version. Register it in all three LLM clients (ChatGPT, Claude Desktop, Perplexity). Run real prompts. Watch tool-selection behavior. Revise descriptions. Repeat.
The CorpusIQ experience: we thought the hard part would be the OAuth plumbing. It was not. OAuth is boring and solved. The hard part was rewriting tool descriptions until Claude consistently called the right tool for a given prompt. That work never ends, but it compounds: better descriptions mean better tool selection mean better answers mean better trust.
Common mistakes to avoid
- Writing tool descriptions for developers instead of for the model
- Exposing too many tools and overwhelming the LLM selection process
- Returning raw HTTP status codes from downstream APIs instead of structured, explanation-friendly errors
- Skipping rate limits and discovering them in production at 3 a.m.
- Letting OAuth scopes creep beyond read-only because we might need it later
If you do not want to build one
Not every business needs its own MCP server. CorpusIQ already connects 22+ business tools (QuickBooks, Shopify, HubSpot, Gmail, Google Drive, GA4, Google Ads, Meta Ads, Slack, PostgreSQL, SQL Server) to ChatGPT, Claude, and Perplexity through a single OAuth. Solo plan starts at $29.95 per month. If your use case fits, connecting a live server beats building one.
Related reading
- What is the Model Context Protocol?
- MCP vs API: What Is Actually Different
- MCP Security: Protecting Your Data in the Context Window
- See all 22+ live CorpusIQ connectors
- Pricing, starting at $29.95 per month
Frequently asked questions
Any language that can handle JSON-RPC over stdio or HTTP+SSE works. Anthropic publishes reference SDKs in TypeScript and Python. Community SDKs exist in Go, Rust, and Java. The CorpusIQ production server is TypeScript on Node.js, deployed on Microsoft Azure Container Apps.
No. Local MCP servers running over stdio are common for developer-centric tools, since they spawn as subprocesses of the client. Remote HTTP+SSE servers make sense when the tool has to serve many users or needs to persist OAuth tokens centrally. CorpusIQ is remote because multi-tenant OAuth to 22+ SaaS vendors is easier to operate centrally.
OAuth belongs inside your server, not in the MCP protocol itself. Your server stores each user connector tokens, refreshes them on expiry, and uses them when it executes tool calls against the downstream API. The LLM never sees the tokens. CorpusIQ uses read-only OAuth scopes per connector, short token lifetimes, and rotates refresh tokens on use.
Writing tool descriptions for developers instead of models. The description is what the LLM reads to decide whether to call your tool. Generic descriptions produce bad tool selection. Specific descriptions that name the action, the scope, the arguments, and the shape of the result produce good routing.
Register the server in each LLM client MCP configuration and run representative prompts. Watch tool-selection behavior: the LLM should pick your tool over alternatives when the prompt matches your description. If it does not, your description or parameter schema likely needs clarification. Anthropic publishes an MCP Inspector that replays traffic for debugging.