DevPulse PM Agent
by maheshg3
README.md
# DevPulse PM Agent β MCP Server
An MCP (Model Context Protocol) server that gives Claude four decision tools for a product manager:
rank the backlog, mine customer feedback, size sprint capacity, and trace dependency risk β all
grounded in DevPulse's own JSON data, computed fresh from whatever dataset is mounted.
Built for the "Product Nerve Center / PM Agent" challenge. Entry point is `server.py`; the four tools
live in `tools/` as standalone `*_impl` functions.
---
## The four tools
| Tool | Answers the PM question | Type |
|---|---|---|
| `prioritize_backlog` | *What should we work on next?* | judgment |
| `analyze_feedback` | *What are customers actually asking for?* | judgment |
| `assess_capacity` | *Can we fit this into the sprint / who has capacity?* | discovered rule π |
| `map_dependencies` | *What's blocking X?* | discovered rule π |
`assess_capacity` and `map_dependencies` are built on rules reverse-engineered from the **Nimbus Oracle**
(a discovery-only service). The server **never calls the oracle** β the discovered rules are implemented
as standalone logic (see *Discovered rules* below).
---
## Repo layout
```
server.py # entry point β registers the 4 tools, loads data from PM_AGENT_DATA
tools/
prioritize_backlog.py # prioritize_backlog_impl(method, filters, include_dependency_check, backlog, feedback, deps)
analyze_feedback.py # analyze_feedback_impl(time_range, customer_tier, source, group_by, feedback)
assess_capacity.py # assess_capacity_impl(sprint_id, squad, include_carry_over, check_skill_fit, roster, backlog, sprints)
map_dependencies.py # map_dependencies_impl(item_ids, include_external, max_depth, backlog, deps)
data/ # sample data for local dev (grading mounts a DIFFERENT dataset)
requirements.txt # pinned deps
agent_config.json # run contract (runtime_version, entry, env_file, required_env: ["MCP_DATA_URL"])
env_vars.json # {"PM_AGENT_DATA":"", "MCP_DATA_URL":"..."}
olympics.json # {entrypoint, language, data_env_var, tools:[...]}
program.md # build spec the coding-agent loop executes against
eval.py # self-test harness (writes results.md / results.json)
results.md # latest eval report (regenerated by eval.py)
```
---
## Quick start
```bash
python3.11 -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
pip install -r requirements.txt
python server.py # stdio transport (what Claude Desktop / Code use)
```
`requirements.txt` must pin exact versions (no `0.0.0`). Minimum is `mcp` (which pulls in `pydantic`); pin
whatever `pip freeze` reports for your Python (3.11 / 3.12 / 3.13) and set the matching `runtime_version`
in `agent_config.json`.
**Connect to Claude Desktop** β add to `claude_desktop_config.json`:
```json
{ "mcpServers": { "pm-agent": {
"command": "python", "args": ["/absolute/path/to/server.py"],
"env": { "PM_AGENT_DATA": "/absolute/path/to/data" } } } }
```
**Connect via Claude Code:** `claude mcp add pm-agent python server.py`
---
## Data sourcing (design decision)
The server reads **all five** JSON files from `DATA_DIR = PM_AGENT_DATA or ./data` and makes **no network
calls**:
```python
DATA_DIR = Path(os.environ.get("PM_AGENT_DATA", Path(__file__).parent / "data"))
```
The challenge brief states three times that the submitted server must not call the oracle, and that at
grading the oracle is gone and a *different* dataset is mounted. The only interpretation under which the
two discovery tools remain testable is that `team_roster.json` and `dependency_map.json` arrive as mounted
files alongside the three given files β so the server reads them from `DATA_DIR`, with a dev-only fallback
to `sample_roster.json` / `sample_dependencies.json` when running offline. Every tool tolerates an empty
`roster` / `deps` / `feedback` list without crashing.
Two dependency sources are merged: each backlog item's own `dependencies` field (always present, untyped β
`EXT-*` targets treated as external) is overlaid with the typed edges in `dependency_map.json`, so
`map_dependencies` works even when the map is empty.
---
## Discovered rules π
These are implemented as standalone logic. The exact constants are **confirmed against the Nimbus Oracle**
in Phase 1; the module-level `CONFIRMED` flag records whether that step is done.
**Capacity** (`tools/assess_capacity.py`), sprint = 10 working days, 21 pts at 100% allocation:
```
effective_capacity = total_capacity_points * (allocation/100) * ((10 - pto_days) / 10)
available_capacity = effective_capacity - carry_over_points
```
Roster field names are mapped defensively (`sprint_allocation_percent`β`allocation_percent`,
`carry_over_items[].points`β`carry_over_points`, `name`β`engineer_id`). A 0%-allocation engineer contributes
0 to squad totals and is flagged `zero_effective_capacity`; `available < 0` is flagged `overloaded`.
**Dependencies** (`tools/map_dependencies.py`): `blocks` and `external` block; `soft` is advisory (does not
block or extend the critical path). External deps with no ETA (`null`/`""`/`TBD`) are flagged HIGH risk;
cycles are detected and reported as full node lists and excluded from the critical path.
---
## Development loop (how this repo was built)
This repo is driven by a buildβtestβfix loop:
1. **`program.md`** β the full build spec (contracts, tool schemas, algorithms, edge cases, the resolved
data-sourcing decision, and the discovered-rule constants). The coding agent implements against it.
2. **`eval.py`** β a self-contained harness. It runs every tool against the given data **and** a synthetic
dataset with different ids/names/numbers and every trap baked in (cycle, churned+over-represented
customer, unestimated item, stale item, 0%-allocation engineer, external-no-ETA edge, skill mismatch).
Expectations are **derived from the data itself**, so the same checks pass on the blind grading set β
nothing is hardcoded. It writes `results.md` (human) and `results.json` (machine) and exits non-zero on
any FAIL or WARN.
3. **`results.md`** β the latest report; the loop fixes every FAIL, then every WARN.
Run it:
```bash
python eval.py # -> results.md, results.json ; exit 0 when green
```
To drive it with a coding agent (e.g. GitHub Copilot CLI): point the agent at `program.md`, let it edit
`tools/*.py` and `server.py`, run `python eval.py` each iteration, read `results.md`, and repeat until 0
FAIL / 0 WARN. The only checks that stay open are `[TODO-ORACLE]` β the exact capacity numbers, which a
human locks by pasting the Phase-1 oracle findings into `EXPECTED_CAPACITY` in `eval.py` and the DISCOVERY
BLOCK in `tools/assess_capacity.py`.
---
## Submission checklist
- [ ] All 4 tools implemented; each returns a dict (no `NotImplementedError`).
- [ ] `requirements.txt` pinned (no `0.0.0`); installs clean in a fresh venv on the declared Python.
- [ ] `agent_config.json` `runtime_version` matches the venv; `MCP_DATA_URL` in `required_env`.
- [ ] Tools read from `PM_AGENT_DATA`; no hardcoded ids / names / numbers.
- [ ] Deterministic output; graceful on bad input and empty roster/deps.
- [ ] `python eval.py` β 0 FAIL, 0 WARN (only `[TODO-ORACLE]` open).
- [ ] `.venv/`, `__pycache__/`, `.env` git-ignored.
The six Technical-Decision-Log answers (schema rationale, investigation & traps, description craft,
failure modes, custom insight, production scaling) are seeded in **program.md β Appendix B** β lift and
expand them into the β€1,500-word Approach Summary.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues