Coderift MCP Server
README.md
# Coderift MCP Project
## Overview
Coderift Technologies is a B2B software company. Customers submit feature
requests continuously, and internal employees need to review, prioritize,
and approve them without touching the production database directly. This
project is an MCP server sitting in front of that database, plus a client
that talks to it through the protocol instead of raw SQL.
## The Problem
Before this system, support agents queried the database directly (or asked
an engineer to) whenever a customer asked about a request's status, and a
product manager approved requests from a spreadsheet with no audit trail
and no check on how many customers a change actually affected. Two things
made this risky enough to justify a real protocol-aware server instead of
a thin CRUD wrapper:
1. **Approvals are not all equal.** Approving a request that affects 800
customers is a different decision than approving one that affects 40.
The naive version either lets any agent approve anything, or hard-codes
a blanket rule that ignores context. We wanted a manager to have to
explicitly sign off on high-impact approvals, live, not just trust a
role field the caller sends us.
2. **Roles change mid-session.** A support agent gets promoted to product
manager during their shift (or a manager is filling in). The tool set
available to them should change the moment that happens, not after they
reconnect.
Those two facts are what justify elicitation and notifications in this
system, not a rule bolted on for the assignment. Report generation over
the full pending-request backlog is also genuinely slow-ish and benefits
from progress feedback instead of a blocking call.
## Database & ERD
Engine: **SQLite** (`db/coderift.db`, built from `db/schema.sql` +
`db/seed.sql` via `db/create_db.py`).

Tables: `customers`, `employees`, `feature_requests` (FK to `customers`),
`approvals` (FK to `feature_requests` and `employees`, for a future audit
trail). Seed data covers both roles, all three statuses (`Pending`,
`Approved`, `Rejected`), and both sides of the 500-customer elicitation
threshold (a 40-customer request, a 500-customer boundary case, and an
800-customer request).
## Project Structure
```text
coderift-mcp/
├── db/
│ ├── schema.sql
│ ├── seed.sql
│ ├── create_db.py
│ ├── ERD.png
│ └── coderift.db (generated)
├── mcp_server/
│ ├── server.py
│ └── models.py
├── agent/
│ └── client.py
├── requirements.txt
└── README.md
```
## Installation
```bash
python -m venv .venv
source .venv/bin/activate # .venv\Scripts\activate on Windows
pip install -r requirements.txt
python db/create_db.py
```
## Running
```bash
# server, local development
cd mcp_server
python server.py # stdio (default)
python server.py --transport streamable-http --port 8000 # remote
# agent, in a second terminal
cd agent
python client.py # stdio, spawns server.py itself
python client.py --http http://127.0.0.1:8000/mcp # against a running HTTP server
```
Set `ANTHROPIC_API_KEY` before running the agent to have the sampling
callback use a real model; otherwise it uses a labeled stub response so
the demo still runs end to end.
## Where each protocol concern lives
| Concern | File / function |
|---|---|
| Capability negotiation | `server.py`: `CoderiftMCP._init_options` declares `tools.listChanged=True`. `client.py`: `run_demo` reads `init.capabilities.tools.listChanged` before assuming notifications will arrive. |
| Notifications | `server.py`: `promote_to_manager` calls `ctx.session.send_tool_list_changed()` after verifying the employee against the `employees` table. `client.py`: `make_notification_handler` reacts to `ToolListChangedNotification`. |
| Elicitation | `server.py`: `approve_feature_request` calls `ctx.elicit(...)` when `affected_customers > 500`, gated behind `has_capability(ctx, elicitation=True)`. `client.py`: `elicitation_callback` prompts a human and returns accept/decline. |
| Sampling | `server.py`: `generate_feature_analysis` calls `ctx.session.create_message(...)` for the written risk recommendation, gated behind `has_capability(ctx, sampling=True)`. `client.py`: `sampling_callback` answers using the client's own model. |
| Resources | `server.py`: `policy://feature-prioritization`, `policy://approval-guidelines`, `policy://release-guidelines`, fetched via `resources/read`, never wrapped in a tool. |
| Prompts | `server.py`: `draft_feature_summary`, `draft_rejection_email`, `customer_feature_update`. |
| Transport | `server.py` `__main__` accepts `--transport stdio|streamable-http`. |
| Progress tracking | `server.py`: `generate_impact_report` calls `ctx.report_progress(...)` at five real milestones while it scans pending requests. `client.py` passes a `progress_callback` to `call_tool`. |
| Defensive tool design | `models.py`: every write tool takes a single Pydantic model with `extra="forbid"`, `Field(gt=0)`, and `Literal` enums. `server.py`: `change_priority` and `approve_feature_request` re-validate server-side and check `get_role(ctx)` — a role read from server-held session state, never a client-supplied parameter. |
## Comparison note
| Tool | Read/Write | Requires elicitation | Notes |
|---|---|---|---|
| `get_feature_request` | Read | No | |
| `list_customer_requests` | Read | No | |
| `search_knowledge_base` | Read | No | BM25 over titles/descriptions |
| `approve_feature_request` | Write | Conditional — only when `affected_customers > 500` | Falls back to an explanatory error if the client lacks elicitation support, instead of silently approving or hanging |
| `change_priority` | Write | No | Role-gated only |
| `generate_impact_report` | Read (long-running) | No | Progress-tracked |
| `generate_feature_analysis` | Read | No | Uses sampling if available; deterministic rule-based fallback otherwise |
| `promote_to_manager` | Write (session state) | No | Verified against `employees` table server-side |
If a client connects without elicitation support, `approve_feature_request`
refuses to silently approve a high-impact request — it returns a structured
`elicitation_unsupported` error explaining that a manager needs to approve
it out of band. If a client connects without sampling support,
`generate_feature_analysis` still returns a usable (if less nuanced)
rule-based risk rating instead of failing.
## Transport choice, justified
Development happens over **stdio** — no server process to manage, easy to
run from the agent script directly, matches how one developer iterates
locally. `server.py` also runs over **Streamable HTTP** (`--transport
streamable-http`), which is the right choice once Coderift's own product
managers across multiple offices need to hit the same server concurrently
instead of each spawning their own local process — a single deployed HTTP
endpoint sits behind normal auth/network controls, which stdio can't do.
Both paths share the same tool/resource/prompt code; only the transport
layer changes.
## Demonstration Workflow
`agent/client.py` runs, in order: initialize + capability check, list
tools as a `support_agent`, an unauthorized `change_priority` attempt,
`promote_to_manager` (real employee lookup + real notification), tool list
re-fetch showing the unlocked write tools, `generate_impact_report` with
live progress, `approve_feature_request` on an 800-customer request
(elicitation fires) and a 40-customer request (it doesn't), sampling-backed
`generate_feature_analysis`, `change_priority`, a resource read, and a
prompt fetch.
## Where it stands now
Session role state lives in a process-local dict keyed by session id,
which is fine for the stdio demo and a single-process HTTP deployment but
wouldn't survive a restart or a multi-instance deployment — a production
version would move that to a signed session token or a real auth layer
instead. The `approvals` table exists in the schema for an audit trail but
isn't written to yet; that'd be the next addition, along with a director
review tier for requests over 1000 affected customers as described in the
approval-guidelines resource.
## Technologies Used
Python, MCP Python SDK (`mcp`), SQLite, Pydantic, rank_bm25.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues