Skip to main content
Glama
KirillSerchenko

products-mcp-server

README.md
# products-mcp-server

A custom [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server exposing **27 product tools** backed by [fakestoreapi.com](https://fakestoreapi.com).

It runs three ways from the same code:

- **Local (stdio)** — for Cursor / Claude Desktop, via `src/index.ts` → `dist/index.js`.
- **Render (persistent HTTP)** — a long-running Streamable HTTP server, via `src/http.ts` → `dist/http.js`.
- **Vercel (serverless HTTP)** — a stateless Streamable HTTP function, via `api/mcp.ts`.

## Architecture

```
src/
  products-api.ts   # Fake Store API client (endpoints + 60s TTL cache)
  tools.ts          # registerTools(server): all 27 tools (shared)
  server.ts         # createServer(): builds a configured server (shared)
  index.ts          # stdio entry point (local)
  http.ts           # persistent HTTP entry point (Render) — listens on $PORT
api/
  mcp.ts            # Vercel serverless entry point (stateless Streamable HTTP)
render.yaml         # Render blueprint (web service)
vercel.json         # Vercel function config
```

All entry points call `createServer()`, so the tool set stays identical across transports.

## Tools

| # | Tool | Description |
| --- | --- | --- |
| 1 | `getProducts` | All products (optional `limit`, `sort`). |
| 2 | `getProductById` | One product by `id`. |
| 3 | `getCategories` | List of categories. |
| 4 | `getProductsByCategory` | Products in a `category` (optional `limit`). |
| 5 | `searchProducts` | Keyword search over title + description. |
| 6 | `getProductsByPriceRange` | Products within `[min, max]`. |
| 7 | `getProductsAbovePrice` | Products costing more than `price`. |
| 8 | `getProductsBelowPrice` | Products costing less than `price`. |
| 9 | `getCheapestProduct` | Single cheapest product. |
| 10 | `getMostExpensiveProduct` | Single most expensive product. |
| 11 | `getTopRatedProducts` | Sorted by rating (optional `limit`). |
| 12 | `getMostReviewedProducts` | Sorted by review count (optional `limit`). |
| 13 | `getProductsByMinRating` | Products with rating >= `minRating`. |
| 14 | `getPriceStats` | count / min / max / average / median / total. |
| 15 | `getCategoryStats` | Per-category price stats. |
| 16 | `countProducts` | Total count (optional `category`). |
| 17 | `listProductTitles` | Lightweight id + title list. |
| 18 | `getRandomProduct` | A random product. |
| 19 | `compareProducts` | Compare 2–10 products by `ids`. |
| 20 | `getProductRecommendations` | Same-category picks for an `id`. |
| 21 | `getDiscountedPrice` | Price after `discountPercent`. |
| 22 | `summarizeCatalog` | High-level catalog summary. |
| 23 | `getCheapestInCategory` | Cheapest within a `category`. |
| 24 | `getHighestRatedInCategory` | Highest-rated within a `category`. |
| 25 | `createProduct` | Create a product (simulated by the API). |
| 26 | `updateProduct` | Update a product (simulated). |
| 27 | `deleteProduct` | Delete a product (simulated). |

> Note: fakestoreapi.com simulates write operations (create/update/delete) and does not persist them.

## Local development

```bash
npm install
npm run build       # tsc: src -> dist
npm run typecheck   # type-checks src + api
npm start           # runs the stdio server
```

### Use locally in Cursor (`~/.cursor/mcp.json`)

```json
{
  "mcpServers": {
    "products": {
      "command": "node",
      "args": ["c:/Users/serch/OneDrive/Desktop/MCP-Servers/products-mcp-server/dist/index.js"]
    }
  }
}
```

Re-run `npm run build` after changing the TypeScript, since Cursor runs the compiled `dist/index.js`.

To run the HTTP server locally (same one Render uses):

```bash
npm run build
npm run start:http          # listens on http://localhost:3000/mcp
# health check: curl http://localhost:3000/healthz
```

## Deploy to Render

Render runs a **persistent Node process** (via `src/http.ts`), which suits MCP well and avoids serverless cold starts / timeouts.

### Option A — Blueprint (recommended)

The repo includes `render.yaml`:

```yaml
services:
  - type: web
    name: products-mcp-server
    runtime: node
    plan: free
    buildCommand: npm install && npm run build
    startCommand: npm run start:http
    healthCheckPath: /healthz
```

1. Push this folder to a Git repo.
2. In Render: **New → Blueprint**, select the repo. Render reads `render.yaml` and creates the web service.
3. Deploy. Your endpoint will be:

```
https://<your-service>.onrender.com/mcp
```

### Option B — Manual web service

**New → Web Service** → connect the repo, then set:

- **Runtime**: Node
- **Build Command**: `npm install && npm run build`
- **Start Command**: `npm run start:http`
- **Health Check Path**: `/healthz`

### Connect a client to the Render server

```json
{
  "mcpServers": {
    "products": { "url": "https://<your-service>.onrender.com/mcp" }
  }
}
```

For stdio-only clients, bridge with [`mcp-remote`](https://www.npmjs.com/package/mcp-remote):

```json
{
  "mcpServers": {
    "products": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://<your-service>.onrender.com/mcp"]
    }
  }
}
```

### Notes

- The server binds to `process.env.PORT` (Render sets this automatically). The MCP path defaults to `/mcp` and can be overridden with the `MCP_PATH` env var.
- Render's **free** web services sleep after inactivity; the first request after idle may be slow to wake.
- The deployed URL is public — add authentication before exposing sensitive tools.

## Deploy to Vercel

The `api/mcp.ts` function serves the MCP server over **Streamable HTTP** in **stateless** mode (a fresh server per request — no Redis required).

1. Push this folder to a Git repo and import it in Vercel (or run `npx vercel`).
2. Vercel compiles `api/mcp.ts` automatically; no build step is required for the function.
3. After deploy, your endpoint is:

```
https://<your-project>.vercel.app/api/mcp
```

### Connect a client to the deployed server

If the client supports Streamable HTTP (e.g. recent Cursor):

```json
{
  "mcpServers": {
    "products": { "url": "https://<your-project>.vercel.app/api/mcp" }
  }
}
```

For stdio-only clients, bridge with [`mcp-remote`](https://www.npmjs.com/package/mcp-remote):

```json
{
  "mcpServers": {
    "products": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://<your-project>.vercel.app/api/mcp"]
    }
  }
}
```

### Notes & limits

- **Stateless**: each request is self-contained. If you later need SSE streaming / persistent sessions across instances, add Upstash Redis and enable Vercel Fluid Compute.
- **Timeout**: `vercel.json` sets `maxDuration: 60`; each tool also has a 10s upstream-fetch abort guard.
- **Auth**: the deployed URL is public. Add authentication before exposing sensitive tools.

TDQS

B3.2/5.0

Scored across 27 tools

Disambiguation4/5

Most tools have distinct purposes, but some overlap exists, e.g., getTopRatedProducts vs getMostReviewedProducts vs getProductsByMinRating, and multiple price-filtering tools could cause confusion.

Naming Consistency5/5

All tool names follow a consistent camelCase verb_noun pattern (e.g., getProductById, createProduct, countProducts), with no mixing of conventions.

Tool Count3/5

27 tools is on the high side; many are specialized queries that could be combined with optional parameters, but each tool serves a clear purpose.

Completeness4/5

Covers CRUD and a wide range of queries, though lacks a generic sort/filter endpoint and bulk operations; write operations are noted as simulated.

Maintenance

ActivityInactive
ResponsivenessNo issues