nest-mcp-learning-server
A read-only MCP server that teaches how to expose documentation as a resource and safe operations as tools, using JSONPlaceholder as the data source.
README
NestJS MCP Learning Server
A deliberately small Model Context Protocol (MCP) server built with NestJS. It teaches how a remote MCP server exposes documentation as a resource and safe operations as tools.
The server documents and reads from JSONPlaceholder. Codex is the AI/MCP client, so this project needs no model SDK, API key, frontend, or database.
Architecture
flowchart LR
Codex["Codex MCP client"] -->|"Streamable HTTP + bearer token"| Nest["NestJS /mcp"]
Nest --> Resource["API documentation resource"]
Nest --> Tools["Three read-only tools"]
Tools --> API["JSONPlaceholder REST API"]
One MCP request follows this path:
- Codex sends an MCP JSON-RPC request to
POST /mcp. McpAccessGuardchecks the bearer token and any suppliedOrigin.McpControllercreates a fresh server and Streamable HTTP transport.McpServerFactoryregisters the resource and tools.- A selected tool calls
JsonPlaceholderService. - The controller returns the MCP response and closes the server and transport.
Creating a fresh transport per request makes the server stateless. It does not issue session IDs or require sticky sessions.
What the server exposes
Resource
jsonplaceholder://api-docs is a short Markdown reference for the supported JSONPlaceholder endpoints. Resources provide context that a client can list and read without pretending the documentation is an action.
Tools
| Tool | Input | Purpose |
|---|---|---|
list_posts |
userId?, limit = 3 |
Lists at most 10 posts, optionally filtered by user. |
get_post |
id from 1–100 |
Gets one post. |
get_user |
id from 1–10 |
Gets one user. |
Every tool has a Zod input schema, structured output, a five-second upstream timeout, and MCP annotations marking it read-only, non-destructive, and idempotent. There is intentionally no generic URL caller and no write tool.
Code map
src/
├── main.ts # Starts Nest on PORT
├── app.module.ts # Wires configuration and providers
├── health.controller.ts # Render/local health check
├── json-placeholder/
│ └── json-placeholder.service.ts # Typed REST calls and timeout
└── mcp/
├── mcp-access.guard.ts # Token and Origin checks
├── mcp.controller.ts # Streamable HTTP transport
└── mcp-server.factory.ts # Resource and tool definitions
The official SDK is pinned to @modelcontextprotocol/sdk@1.29.0. Version 1 remains the stable production line while the SDK's version 2 is pre-release.
Run locally
Requirements: Node.js 22 or newer and npm.
npm install
cp .env.example .env
openssl rand -hex 32
Copy the generated token into MCP_AUTH_TOKEN in .env, then start Nest:
npm run start:dev
The endpoints are:
http://localhost:3000/healthhttp://localhost:3000/mcp
Check health:
curl http://localhost:3000/health
Opening /mcp in a browser is not a valid test: MCP uses JSON-RPC over HTTP and this stateless server intentionally rejects GET. Use an MCP client.
Test with MCP Inspector
Run the official Inspector without installing it globally:
npx @modelcontextprotocol/inspector
In the Inspector UI:
- Select Streamable HTTP.
- Enter
http://localhost:3000/mcp. - Add header
Authorization: Bearer <your MCP_AUTH_TOKEN>. - Connect and open the Resources and Tools tabs.
- Read
jsonplaceholder://api-docs, then call each tool.
If the Inspector sends an Origin header, add that exact origin to the comma-separated MCP_ALLOWED_ORIGINS value. The example allows http://localhost:6274.
Automated verification
npm run lint
npm test
npm run build
The end-to-end test starts Nest on a random port and connects with the official Client and StreamableHTTPClientTransport. It verifies real MCP initialization, resources, tools, structured responses, input validation, authentication, and independent stateless clients. JSONPlaceholder is mocked only in automated tests so tests stay deterministic.
Deploy free on Render
This repository includes a Render Blueprint. Render runs the normal Nest HTTP server; MCP does not require a special hosting product.
- Push this repository to GitHub.
- Open Render's Blueprint page.
- Create a Blueprint from the GitHub repository.
- Choose the Free instance when prompted.
- Set
MCP_AUTH_TOKENto a random 32-byte hex token. Do not commit it. - Deploy and copy the resulting
https://<service>.onrender.comURL.
render.yaml configures:
npm ci && npm run buildas the build command;npm run start:prodas the start command;/healthas the health check;- Node.js 22;
MCP_AUTH_TOKENas a dashboard-supplied secret.
Render Free services sleep after 15 idle minutes. Wake the service before an MCP test:
until curl -fsS https://<service>.onrender.com/health; do sleep 5; done
Then repeat the Inspector steps with https://<service>.onrender.com/mcp.
Connect the deployed server to Codex
Keep the token out of config.toml. Codex can read it from an environment variable:
read -s NEST_MCP_DEMO_TOKEN
export NEST_MCP_DEMO_TOKEN
launchctl setenv NEST_MCP_DEMO_TOKEN "$NEST_MCP_DEMO_TOKEN"
codex mcp add jsonplaceholder-learning \
--url https://<service>.onrender.com/mcp \
--bearer-token-env-var NEST_MCP_DEMO_TOKEN
The launchctl step makes the variable available to GUI applications started on macOS. Fully quit and reopen Codex after setting it.
Because Render can cold start slowly, add these non-secret settings to the generated server table in ~/.codex/config.toml:
[mcp_servers.jsonplaceholder-learning]
url = "https://<service>.onrender.com/mcp"
bearer_token_env_var = "NEST_MCP_DEMO_TOKEN"
startup_timeout_sec = 90
tool_timeout_sec = 30
After restarting Codex, check Settings → MCP servers or type /mcp. Try:
- “Read the API documentation exposed by the JSONPlaceholder learning server.”
- “List the first three posts and summarize them.”
- “Get post 1, then find its author.”
- “Show only two posts belonging to user 3.”
- “Create a new post.” The correct response is that no write tool exists.
Security boundaries
This is a temporary read-only learning server:
- Every
/mcprequest requires the shared bearer token. - Requests with an
Originare accepted only when explicitly allowed. - Tool inputs cannot supply arbitrary URLs.
- Result sizes and IDs are bounded.
- Upstream errors are replaced by a safe tool error.
- Secrets are excluded from Git and from MCP results.
A shared token is intentionally simpler than production authorization. For user-specific or sensitive data, implement the MCP OAuth 2.1 authorization model, per-user authorization, auditing, rate limiting, and secret rotation.
Troubleshooting
401 Unauthorized: Codex/Inspector is missing the token or it differs from Render.403 Forbidden: add the exact supplied Origin toMCP_ALLOWED_ORIGINS.- Codex says the server timed out: wake
/health, wait for200, and keepstartup_timeout_sec = 90. - Browser shows 405 on
/mcp: expected; use Inspector or Codex instead of browser navigation. - Tool returns a retry message: JSONPlaceholder timed out or returned an error.
- Server does not appear in Codex: fully restart Codex after adding the server and environment variable.
Remove the experiment
codex mcp remove jsonplaceholder-learning
launchctl unsetenv NEST_MCP_DEMO_TOKEN
unset NEST_MCP_DEMO_TOKEN
Then suspend or delete the Render service. Rotating or deleting MCP_AUTH_TOKEN immediately prevents further MCP access.
Primary references
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.