Project Manager MCP Server
by cjafet
README.md
# Project Manager MCP Server
A small MCP (Model Context Protocol) server for a project-management
domain — `create_task`, `list_tasks`, `update_task` — backed by SQLite,
integrated with Claude Code, and deployable to Railway or Render over SSE.
## How this maps to the assignment
| Objective | Where |
|---|---|
| MCP server exposing tools for an external system | `src/` — SQLite-backed task tracker |
| Configure Claude Code to use the server | `.mcp.json`, `.claude/settings.json` |
| CLAUDE.md + commands leveraging the tools | `CLAUDE.md`, `.claude/commands/*.md` |
| End-to-end workflow demo | `WALKTHROUGH.md` |
| Learning goal | Where it shows up |
|---|---|
| MCP architecture (host, client, server, transport) | Two transports implemented: `src/index.ts` (stdio) and `src/sse.ts` (SSE). Claude Code is the host/client in both cases. |
| Tool definitions with JSON Schema | `src/tools.ts` — `TOOL_DEFINITIONS` |
| Integrate via `.mcp.json` | `.mcp.json` (stdio) and `.mcp.sse.json.example` (SSE, for the deployed server) |
| Deploy with SSE transport | `Dockerfile`, `railway.json`, `render.yaml`, `src/sse.ts` |
## Project layout
```
src/
types.ts Task/Priority/Status types
db.ts SQLite persistence (better-sqlite3)
tools.ts Tool JSON Schemas + tools/call handler
server.ts Shared MCP Server factory (tools + resources)
index.ts stdio entry point (local Claude Code use)
sse.ts SSE/HTTP entry point (cloud deployment)
.claude/
settings.json auto-approves this project's MCP tools
commands/
create-task.md
list-tasks.md
update-task.md
task-report.md bonus: reads back a status summary
CLAUDE.md project context Claude Code reads automatically
.mcp.json local stdio server registration
.mcp.sse.json.example swap-in config for the deployed SSE server
Dockerfile build for Railway/Render
railway.json Railway build/deploy config
render.yaml Render Blueprint config
WALKTHROUGH.md scripted demo + how to record your own
```
## The three tools
| Tool | Required args | Optional args | Behavior |
|---|---|---|---|
| `create_task` | `title` | `description`, `priority` (low/medium/high) | Inserts a row, `status` starts at `pending` |
| `list_tasks` | — | `status`, `priority` | Filters, newest first |
| `update_task` | `task_id`, `status` | — | Errors if `task_id` doesn't exist |
There's also a read-only resource, `tasks://summary`, returning counts of
tasks by status — used by the `/task-report` command.
## Run it locally
```bash
npm install
npm run build
npm start # stdio server, for manual testing outside Claude Code
```
Or skip the build step during development:
```bash
npm run dev # runs src/index.ts directly via tsx
```
Data is stored in `tasks.db` in the project root by default; override with
`DB_PATH=/some/path/tasks.db`.
## Wire it up to Claude Code
1. Open this project directory in a terminal: `cd project-manager-mcp`.
2. Make sure it's built: `npm run build` (the committed `.mcp.json` points
at `dist/index.js`).
3. Start `claude` from this directory. Claude Code reads `.mcp.json`
automatically and will ask you to approve the project-scoped
`project-manager` server the first time — approve it.
4. `.claude/settings.json` pre-allows the three tools so you won't get a
permission prompt per-call; `CLAUDE.md` gives Claude the context on when
to use each one.
5. Try it:
- "Create a task called 'Set up CI' with medium priority" → calls `create_task`
- `/list-tasks` → calls `list_tasks`
- `/update-task task_xxx in_progress` → calls `update_task`
- `/task-report` → summarizes status counts
If you'd rather not build first, you can point `.mcp.json` at the dev
command instead:
```json
{
"mcpServers": {
"project-manager": {
"command": "npx",
"args": ["tsx", "src/index.ts"]
}
}
}
```
## Deploy to Railway
```bash
npm install -g @railway/cli # if you don't have it
railway login
railway init
railway up
```
Railway detects `railway.json` and builds the `Dockerfile`. Once deployed:
1. In the Railway dashboard, add a **volume** mounted at `/app/data` so
`tasks.db` (at `DB_PATH=/app/data/tasks.db`) survives redeploys.
2. Grab the public URL Railway assigns (Settings → Networking → Generate
Domain if it's not already public).
3. Verify it's up: `curl https://<your-app>.up.railway.app/` should return
a small JSON status blob.
## Deploy to Render
1. Push this repo to GitHub.
2. In Render, "New" → "Blueprint", point it at the repo — it picks up
`render.yaml` automatically (Docker build, persistent disk at
`/app/data`, `PORT`/`DB_PATH` env vars already set).
3. Once live, verify with `curl https://<your-app>.onrender.com/`.
## Point Claude Code at the deployed server
Replace (or add an entry to) `.mcp.json` with the SSE config — see
`.mcp.sse.json.example` for the exact shape:
```json
{
"mcpServers": {
"project-manager": {
"type": "sse",
"url": "https://<your-deployed-app>/sse"
}
}
}
```
Restart Claude Code and re-run the same natural-language requests and
slash commands — same tools, same data, now over the network instead of a
local subprocess.
## Design notes
- **Two transports, one server**: `src/server.ts` exports a `createServer()`
factory used by both `index.ts` (stdio) and `sse.ts` (SSE), so the tool
logic in `tools.ts`/`db.ts` is written once. The SSE entry point creates
one `Server` instance per connection (matching the official MCP SDK
examples) so concurrent clients don't share request state; they all read
and write the same underlying SQLite file, though.
- **SQLite over in-memory**: the original example (`src/index.ts` you may
have seen elsewhere) kept tasks in a `Map`, which resets on every
restart. This version persists to SQLite via `better-sqlite3` so state
survives restarts and redeploys (given a persistent volume).
- **Validation**: `tools.ts` checks required fields and enum values before
touching the database and returns `isError: true` with a plain-language
message on bad input, rather than throwing.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues