Skip to main content
Glama
Thethirdone3

merchant-catalog-mcp

by Thethirdone3
README.md
# merchant-catalog-mcp

A small, production-quality **MCP (Model Context Protocol) server** in TypeScript.
It exposes a mock merchant catalog to any MCP-compatible client (Claude Desktop,
the MCP Inspector, IDE agents, etc.) through three tools — search, availability,
and ordering. The data is intentionally in-memory: this project is a clear,
readable demonstration of *the protocol*, not a data layer.

---

## What it does

The server exposes three **tools** over the MCP `stdio` transport:

| Tool | Input | What it returns |
|------|-------|-----------------|
| `search_products` | `query` (string, required), `category` (enum, optional) | Products whose name/description match the query, optionally filtered by category. |
| `check_availability` | `productId` (string, required) | Stock status (`in_stock` / `low_stock` / `out_of_stock`) and on-hand quantity. |
| `place_order` | `productId` (string), `quantity` (positive integer) | Validates against stock, decrements it, and returns a mock order confirmation. Errors (bad id, not enough stock) come back as tool results flagged `isError`. |

Every tool also declares an **`outputSchema`** and returns **`structuredContent`**,
so clients receive typed objects, not just text.

It also exposes the other two MCP primitives:

| Primitive | Name | What it is |
|-----------|------|-----------|
| Resource | `catalog://products` | The full catalog as a read-only JSON resource the host can pull into context. |
| Prompt | `gift_finder` | A user-invoked template (`occasion`, `budget`) that composes a catalog gift-recommendation request. |

---

## How the MCP pieces fit (the 60-second tour)

- **Host / client / server.** A *host* app (Claude Desktop, the Inspector) runs an
  MCP *client* that connects to one or more *servers*. This repo is one server.
- **A server exposes three kinds of things.** **Tools** (model-callable actions),
  **resources** (read-only data the app pulls by URI), and **prompts**
  (user-invoked templates). This server demonstrates **all three**: three tools,
  a `catalog://products` resource, and a `gift_finder` prompt.
- **Transport = the pipe.** Messages are **JSON-RPC 2.0**. With the **stdio**
  transport, the client launches this server as a subprocess and exchanges
  messages over stdin/stdout. (Because stdout *is* the protocol channel, all
  diagnostics in this server go to **stderr**.)
- **Input schema = the contract.** Each tool declares its arguments with a `zod`
  schema. The SDK converts it to JSON Schema, advertises it during discovery, and
  validates every incoming call against it *before* the handler runs. The schema
  is what tells the model exactly how it's allowed to call the tool.
- **Output schema = the response contract.** Each tool also declares an
  `outputSchema` and returns `structuredContent` — a typed object the client
  gets directly, validated by the SDK — instead of only stringified JSON in text.
- **Lifecycle.** `initialize` (capability handshake) → `tools/list` (discovery) →
  `tools/call` (invocation) → structured result. State (stock levels) persists for
  the life of the process, so an order visibly reduces availability.

---

## Project structure

```
src/
  catalog.ts   Domain layer: Product type, in-memory data, pure lookup/search/mutate helpers.
               No MCP imports — this is the "swap-in-a-database-here" seam.
  index.ts     Protocol layer: creates the McpServer, registers the three tools
               (input + output schema + handler), the catalog resource, and the
               gift_finder prompt, then connects the stdio transport.
```

The split is deliberate: business logic in `catalog.ts`, protocol wiring in
`index.ts`. The tools are thin adapters over logic that could just as easily sit
behind a real API or database.

---

## Requirements

- Node.js 18+ (developed on Node 24 LTS)

## Install

```bash
npm install
```

## Run

```bash
# Dev: run the TypeScript directly, no build step
npm run dev

# Production: compile to dist/ then run the compiled server
npm run build
npm start
```

On startup the server prints `merchant-catalog-mcp running on stdio` to **stderr**
and then waits for JSON-RPC messages on stdin. (Running it in a bare terminal and
seeing it "hang" is correct — it's waiting for a client to speak to it.)

---

## Verify it works

### Option A — MCP Inspector, UI mode

```bash
npm run inspect
```

This launches the official [MCP Inspector](https://github.com/modelcontextprotocol/inspector)
and opens a browser panel. Steps:

1. It auto-connects to this server over stdio.
2. Open the **Tools** tab and click **List Tools** — you'll see all three, each
   with a form generated *from its input schema*.
3. Select `search_products`, enter a `query` (e.g. `speaker`), and **Run Tool**.
4. Try `place_order` with `productId=sku-003`, `quantity=2`, then run
   `check_availability` on the same id to watch stock drop.

### Option B — MCP Inspector, CLI mode (scriptable)

```bash
# Discover tools
npx @modelcontextprotocol/inspector --cli node dist/index.js --method tools/list

# Call a tool
npx @modelcontextprotocol/inspector --cli node dist/index.js \
  --method tools/call --tool-name search_products --tool-arg query=speaker

# Place an order
npx @modelcontextprotocol/inspector --cli node dist/index.js \
  --method tools/call --tool-name place_order \
  --tool-arg productId=sku-002 --tool-arg quantity=1
```

### Option C — raw JSON-RPC (what a client does under the hood)

```bash
{
  printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"raw","version":"1.0.0"}}}'
  printf '%s\n' '{"jsonrpc":"2.0","method":"notifications/initialized"}'
  printf '%s\n' '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
  printf '%s\n' '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"search_products","arguments":{"query":"headphones"}}}'
  sleep 0.5
} | node dist/index.js
```

---

## Use it from Claude Desktop

Build first (`npm run build`), then add this server to your Claude Desktop MCP
config. The file lives at:

- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "merchant-catalog": {
      "command": "/absolute/path/to/node",
      "args": ["/absolute/path/to/merchant-catalog-mcp/dist/index.js"]
    }
  }
}
```

Then **fully quit and reopen** Claude Desktop (it loads MCP servers only at
startup). The three tools will appear in the tools menu.

> **Gotcha — use absolute paths for both `command` and `args`.** Claude Desktop
> is a GUI app, so it launches this server with the system environment, *not*
> your shell's — it does not read `~/.zshrc`/`~/.bashrc` and therefore may not
> find `node` on its `PATH`. Pointing `command` at the absolute path of your
> Node binary (find it with `which node`) avoids a "spawn node ENOENT" failure.

---

## Catalog

Eight products across four categories (`audio`, `wearables`, `home`,
`accessories`), including deliberately low-stock (`sku-003`, `sku-008`) and
out-of-stock (`sku-004`) items so you can exercise the availability and
validation paths. See [`src/catalog.ts`](src/catalog.ts).

---

## Possible next steps

- Swap `catalog.ts` for a real database — the protocol layer wouldn't change.
- Add an **HTTP/SSE transport** for remote hosting (only the transport lines in
  `index.ts` change).
- Add **unit tests** over `catalog.ts` and a CI workflow that runs `tsc` + tests.

## License

MIT

TDQS

A4/5.0

Scored across 3 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: searching products, checking stock, and placing orders. There is no overlap or ambiguity.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern (search_products, check_availability, place_order), making them predictable.

Tool Count5/5

Three tools is well-scoped for a simple merchant catalog server, covering the core search, stock check, and order operations without excess.

Completeness4/5

The tool set covers search, stock inquiry, and ordering. A minor gap is the lack of a dedicated get_product_details tool, but search can compensate.

Maintenance

ActivityInactive
ResponsivenessNo issues