Skip to main content
Glama
maheshg3

DevPulse PM Agent

by maheshg3

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).


Related MCP server: pm-copilot

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

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:

{ "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:

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:

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.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers