nest-mcp-learning-server

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.

Category
Visit Server

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:

  1. Codex sends an MCP JSON-RPC request to POST /mcp.
  2. McpAccessGuard checks the bearer token and any supplied Origin.
  3. McpController creates a fresh server and Streamable HTTP transport.
  4. McpServerFactory registers the resource and tools.
  5. A selected tool calls JsonPlaceholderService.
  6. 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/health
  • http://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:

  1. Select Streamable HTTP.
  2. Enter http://localhost:3000/mcp.
  3. Add header Authorization: Bearer <your MCP_AUTH_TOKEN>.
  4. Connect and open the Resources and Tools tabs.
  5. 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.

  1. Push this repository to GitHub.
  2. Open Render's Blueprint page.
  3. Create a Blueprint from the GitHub repository.
  4. Choose the Free instance when prompted.
  5. Set MCP_AUTH_TOKEN to a random 32-byte hex token. Do not commit it.
  6. Deploy and copy the resulting https://<service>.onrender.com URL.

render.yaml configures:

  • npm ci && npm run build as the build command;
  • npm run start:prod as the start command;
  • /health as the health check;
  • Node.js 22;
  • MCP_AUTH_TOKEN as 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 /mcp request requires the shared bearer token.
  • Requests with an Origin are 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 to MCP_ALLOWED_ORIGINS.
  • Codex says the server timed out: wake /health, wait for 200, and keep startup_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

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.

Official
Featured
TypeScript
Magic Component Platform (MCP)

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.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

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.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

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.

Official
Featured
TypeScript
Kagi MCP Server

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.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

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.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured