Skip to main content
Glama
README.md
# Todo MCP Demo

A demo project showing how an AI Agent (**MCP Host**) uses the **Model
Context Protocol (MCP)** to communicate with Tools, Resources, and Prompts
exposed by an MCP Server, over both **stdio** and **HTTP** transports.

**Live public MCP endpoint:** `https://todo-mcp-demo.onrender.com/mcp`
(Streamable HTTP — no API key required; connect with MCP Inspector by
choosing the "Streamable HTTP" transport. First request after idle may take
~30-60s to wake up the free Render instance.)

Full specification: [`docs/00_GLOBAL_RULES.md`](docs/00_GLOBAL_RULES.md),
[`docs/01_REQUIREMENTS.md`](docs/01_REQUIREMENTS.md),
[`docs/02_ARCHITECTURE.md`](docs/02_ARCHITECTURE.md),
[`docs/03_IMPLEMENTATION_PLAN.md`](docs/03_IMPLEMENTATION_PLAN.md).

## Overview

```
User → Agent (MCP Host) → MCP Tool → Todo Store → Result → Agent → User
```

- **Todo Store** (`src/server/todoStore.ts`) — in-memory business logic:
  `addTask`, `listTasks`, `completeTask`. No database, no transport
  dependency.
- **MCP Server** (`src/server/server.ts`) — exposes the Todo Store as:
  - 3 Tools: `add_task`, `list_tasks`, `complete_task`
  - 1 Resource: `todo://list`
  - 1 Prompt: `plan_my_day`
- **Transports** (`src/transports/`) — `stdio.ts` and `http.ts` both boot the
  *same* MCP Server Core; no business logic is duplicated between them.
- **Agent / MCP Host** (`src/agent/`) — connects to an MCP Server (stdio or
  HTTP), discovers Tools dynamically, and dispatches Tool calls.
- **Skill** (`src/skills/plan-my-day/SKILL.md`) — a reusable workflow: read
  the todo list via the `list_tasks` Tool, filter unfinished tasks, and
  generate a prioritized plan for the day.

## Architecture

```
                         User
                           │
                           ▼
                    ┌──────────────┐
                    │    Agent     │
                    │  (MCP Host)  │
                    └──────┬───────┘
                           │
                  MCP Client Layer
                           │
        ┌──────────────────┴──────────────────┐
        │                                     │
        ▼                                     ▼
  stdio Transport                      HTTP Transport
        │                                     │
        └──────────────────┬──────────────────┘
                           ▼
                  MCP Server Core
                           │
      ┌──────────────┬──────────────┬──────────────┐
      ▼              ▼              ▼
    Tools        Resources       Prompts
      │
      ▼
   Todo Store
```

## Prerequisites

- Node.js >= 22.9
- npm

## Installation

```bash
npm install
npm run build
```

## Configuration (optional)

No environment variable is required — every value has a working default.
To override one locally, copy the example file:

```bash
cp .env.example .env
```

| Variable        | Used by                | Default                         |
| ---------------- | ----------------------- | -------------------------------- |
| `PORT`            | `npm run server:http` / `npm start` | `3000`                      |
| `MCP_TRANSPORT`   | `npm run agent`          | `stdio`                          |
| `MCP_HTTP_URL`    | `npm run agent` (when `MCP_TRANSPORT=http`) | `http://localhost:3000/mcp` |

All npm scripts load `.env` automatically via Node's built-in
`--env-file-if-exists` flag (no `dotenv` dependency needed). `.env` is
git-ignored; only `.env.example` is committed. On hosting platforms
(Render/Railway) `PORT` is injected by the platform itself, so `.env` is
not used in production.

## Available npm Scripts

| Script                | Description                                              |
| ---------------------- | --------------------------------------------------------- |
| `npm run build`        | Compile TypeScript to `dist/`                              |
| `npm run clean`        | Remove `dist/`                                             |
| `npm run server:stdio` | Build, then start the MCP Server over stdio                |
| `npm run server:http`  | Build, then start the MCP Server over HTTP (port `3000`)   |
| `npm run agent`        | Build, then run the Agent CLI with a natural-language command |
| `npm start`            | Start the HTTP MCP Server from an already-built `dist/` (used by hosting platforms) |

## Run the stdio Server

```bash
npm run server:stdio
```

This starts the MCP Server on stdio. Connect with the
[MCP Inspector](https://github.com/modelcontextprotocol/inspector):

```bash
npx @modelcontextprotocol/inspector node dist/transports/stdio.js
```

## Run the HTTP Server

```bash
npm run server:http
```

Starts an HTTP MCP endpoint at `http://localhost:3000/mcp` (override the
port with the `PORT` environment variable). Connect with MCP Inspector by
choosing the "Streamable HTTP" transport and pointing it at that URL.

## Run the Agent

The Agent connects to a running MCP Server, discovers its Tools, and
dispatches a single natural-language command.

Against the **stdio** server (default — the Agent spawns the server itself,
no need to start it separately):

```bash
npm run agent -- "add task Buy milk"
npm run agent -- "list tasks"
npm run agent -- "complete task <task-id>"
```

Against a running **HTTP** server (start it first with
`npm run server:http` in another terminal):

```bash
MCP_TRANSPORT=http npm run agent -- "add task Buy milk"
MCP_TRANSPORT=http MCP_HTTP_URL=http://localhost:3000/mcp npm run agent -- "list tasks"
```

## Run the Skill: plan-my-day

See [`src/skills/plan-my-day/SKILL.md`](src/skills/plan-my-day/SKILL.md) for
the full workflow definition. The Agent recognizes the trigger phrase and
runs the workflow using the existing `list_tasks` Tool (no direct access to
the Todo Store):

```bash
npm run agent -- "plan my day"
# or, against the HTTP server:
MCP_TRANSPORT=http npm run agent -- "plan my day"
```

Example output:

```
Today's plan:
1. Buy milk
2. Write report
```

## Demo Walkthrough

```bash
npm run agent -- "add task Buy milk"
npm run agent -- "add task Write report"
npm run agent -- "plan my day"
```

> Note: each `npm run agent` call against the **stdio** transport spawns a
> fresh server process, so tasks only persist for the lifetime of a single
> command. To see tasks persist across multiple Agent calls, start the HTTP
> server once (`npm run server:http`) and pass `MCP_TRANSPORT=http` to every
> Agent call, as shown above.

## Deployment

The HTTP MCP Server (`src/transports/http.ts`) is deployment-ready:

- It reads the port from the `PORT` environment variable.
- `npm start` runs the compiled server directly (`node dist/transports/http.js`).

Deployed on **Render** (Free instance type):

- Repository: https://github.com/thienbanho/todo-mcp-demo
- Build command: `npm install && npm run build`
- Start command: `npm start`
- No environment variables required — Render sets `PORT` automatically.
- Live endpoint: `https://todo-mcp-demo.onrender.com/mcp`

To deploy on **Railway** instead, the steps are equivalent: connect the
repository, set the same build/start commands, and Railway will assign a
public URL and `PORT` automatically.

Verified with a raw MCP `initialize` + `tools/list` handshake against the
live endpoint (see `mcp-session-id` flow in `src/transports/http.ts`); you
can also verify interactively with MCP Inspector, using the public `/mcp`
URL as a Streamable HTTP endpoint.

## Out of Scope

Per the project spec: no database, authentication, authorization, user
management, frontend, Docker, CI/CD, or multi-user support. All Todo data is
in-memory and is lost on server restart — this is intentional for a demo
project.