The Model Context Protocol (MCP) gives LLM agents a standard way to call your tools, read your resources, and reuse your prompts. The protocol is straightforward; the hard part is everything around it: authentication, transport, persistence, observability, and keeping the whole thing online.
By the end of this tutorial, you’ll have a production-shaped MCP server running on Render that any MCP-capable client (Claude Desktop, Cursor, the Codex CLI, or the MCP Inspector) can connect to.
You start from Render’s official mcp-server-typescript template - a one-click-deployable MCP server with Streamable HTTP transport, a hello tool, bearer-token auth, and a working render.yaml. The tutorial’s value-add is everything you bolt on top of it: proper OAuth, Postgres, rate limiting, structured logs, and the operational habits that keep it running.
Before you start
You’ll need:
- A Render account. The Starter plan covers everything in this tutorial.
- A Render API key from Account Settings. Used by the CLI and by the Blueprint deploy.
- Node.js 22+ and pnpm on your machine.
- A GitHub account you can push a new repo to. The Blueprint deploys from it.
- An MCP-capable client to test against in step 8: Claude Desktop, Cursor, the Codex CLI, or the MCP Inspector (
npx @modelcontextprotocol/inspector). - Familiarity with TypeScript and HTTP servers.
The MCP server docs, the Blueprint spec, and the Postgres docs are good companions when you wire each piece in.
What “full-featured and secure” means here
You’re not shipping a toy. The server you’ll build covers the pieces real MCP deployments need:
- Streamable HTTP transport so any modern MCP client can connect over the public internet, not just stdio on the same machine.
- OAuth-based auth so each caller is a real, revocable identity - no shared bearer tokens checked into config files.
- A Postgres-backed tool that does something genuinely useful (a small note-taking surface), wired over Render’s private network.
- Rate limiting and structured logging so one bad client can’t drown the others, and you can actually debug what happened.
- A health check and a Blueprint so deploys are reproducible and Render can keep the service healthy.
What you won’t need to figure out
Render handles the boring-but-load-bearing parts:
- Free, auto-renewing TLS on a public URL.
- Zero-downtime deploys when you push to
main. - A managed Postgres database reachable over the private network - no public exposure.
- Logs, metrics, and shell access from the dashboard.
That lets the tutorial stay focused on the MCP-specific decisions: transport choice, auth flow, tool design, and how to validate the server end-to-end.
Roadmap
Here’s the path from a templated starting point to a deployed, authenticated, persistent MCP server:
- Fork Render’s MCP template - clone it, run it locally, optionally deploy the unmodified version first so you’ve seen the green path.
- Add your first real tool and resource - a
notes.createtool and anotes://recentresource backed by an in-memory store, registered alongside the template’shello. - Add Postgres locally with Docker, then wire the same code to Render’s managed Postgres over the private network.
- Replace the template’s bearer token with OAuth 2.1 - the flow MCP clients expect for multi-user remote servers.
- Add rate limiting, structured logs, and a real health check so the service is observable and resilient.
- Extend the template’s
render.yamlto provision Postgres, wire OAuth env vars, and lock down the deploy. - Connect a real client - point Claude Desktop (or the MCP Inspector) at your live URL and watch your tool execute.
- Harden and iterate - durable OAuth storage, per-user data scoping, secrets rotation, and what to monitor in week one.
flowchart LR
client["MCP client<br/>(Claude / Cursor / Inspector)"]
edge["Render web service<br/>(public HTTPS + OAuth)"]
app["MCP server<br/>(TypeScript SDK)"]
db[("Render Postgres<br/>(private network)")]
client -->|"Streamable HTTP + Bearer"| edge
edge --> app
app -->|"private DNS"| db
The public surface is exactly one URL with TLS and OAuth in front of it. Everything else - Postgres, internal env vars, deploy artifacts - stays on Render’s private network.
What you learned
- MCP standardizes how agents call tools and read resources - the hard part is the production envelope around it
- You'll ship a TypeScript MCP server with OAuth, Postgres, rate limiting, and structured logs
- Render provides TLS, zero-downtime deploys, private networking, and managed Postgres so you can focus on MCP itself
- Public surface is one OAuth-protected HTTPS URL; everything else stays private