{"id":51224814,"url":"https://github.com/saagpatel/portfolio-health","last_synced_at":"2026-06-28T10:03:13.558Z","repository":{"id":363758703,"uuid":"1242272224","full_name":"saagpatel/portfolio-health","owner":"saagpatel","description":"MCP server for project portfolio health monitoring — SQLite FTS5 index of memory files + bridge-db activity","archived":false,"fork":false,"pushed_at":"2026-06-19T05:37:04.000Z","size":145,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-19T07:18:21.534Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/saagpatel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-18T09:22:19.000Z","updated_at":"2026-06-19T05:36:53.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/saagpatel/portfolio-health","commit_stats":null,"previous_names":["saagpatel/portfolio-health"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/saagpatel/portfolio-health","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saagpatel%2Fportfolio-health","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saagpatel%2Fportfolio-health/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saagpatel%2Fportfolio-health/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saagpatel%2Fportfolio-health/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saagpatel","download_url":"https://codeload.github.com/saagpatel/portfolio-health/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saagpatel%2Fportfolio-health/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34884278,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-28T02:00:05.809Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-28T10:03:13.046Z","updated_at":"2026-06-28T10:03:13.551Z","avatar_url":"https://github.com/saagpatel.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# portfolio-health\n\n[![CI](https://github.com/saagpatel/portfolio-health/actions/workflows/ci.yml/badge.svg)](https://github.com/saagpatel/portfolio-health/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\nMCP server for project portfolio health monitoring. Indexes `project_*.md` memory files via SQLite FTS5 and joins against bridge-db activity to surface active, stale, and unshipped projects.\n\n`portfolio-health` is a read-oriented local MCP helper and cache/query surface. It is not the portfolio source of truth. Treat bridge-db, memory files, and generated portfolio truth as upstream evidence; use this repo to inspect and cross-check those sources.\n\n## Prerequisites\n\n- **Python 3.12+** and **[uv](https://docs.astral.sh/uv/)** (`pip install uv` or `brew install uv`)\n- **Claude Code** installed locally — this server reads two data sources that Claude Code writes:\n  - **Memory files**: `~/.claude/projects/\u003cencoded-home\u003e/memory/project_*.md`\n  - **Bridge-db**: `~/.local/share/bridge-db/bridge.db` (written by the bridge-db MCP server)\n\n\u003e **What is `\u003cencoded-home\u003e`?** Claude Code stores per-project memory under `~/.claude/projects/` and encodes your home directory path into the folder name by replacing every `/` with `-`. For example, if your home is `/Users/alice`, the folder becomes `-Users-alice`. The server detects this automatically at startup — you do not need to configure it by hand. If auto-detection fails (no `project_*.md` files found), set the `PORTFOLIO_HEALTH_MEMORY_DIR` environment variable to the full path of your memory directory.\n\n## Install\n\n```bash\ngit clone https://github.com/saagpatel/portfolio-health.git\ncd portfolio-health\nuv sync\n```\n\n## Run (stdio transport for MCP)\n\n```bash\nuv run python -m portfolio_health\n```\n\nThe installed script exposes the same MCP server:\n\n```bash\nuv run portfolio-health\n```\n\n## Tools\n\n| Tool | Description |\n|---|---|\n| `portfolio_list_active(window_days=14)` | Projects with bridge-db activity in last N days, most-recent first |\n| `portfolio_get_project(name)` | Full detail for one project: frontmatter, first section, file path |\n| `portfolio_search(query, limit=10)` | FTS5 full-text search across name + description + body |\n| `portfolio_stale_candidates(days=90)` | Projects with no activity in N days, excluding abandoned/archived |\n| `portfolio_unshipped()` | Ship-ready projects with no `SHIPPED` bridge-db tag in 30 days |\n\n### `portfolio_unshipped` — what it matches\n\n`portfolio_unshipped` scans project descriptions for phrases that signal the project is done but not yet logged as shipped. It matches descriptions containing any of:\n\n- `v1.0 complete`, `v1.0 done`, `v1.0 ready`\n- `deploy-ready`\n- `launch-ready`\n- `all phases done`, `all phases complete`\n\nProjects are excluded from the result if a bridge-db `activity_log` row with `SHIPPED` in its `tags` field exists within the last 30 days.\n\n## Health report\n\nUse the health command to check cache/source alignment before audits or MCP registration work:\n\n```bash\nuv run portfolio-health health\n```\n\nFor a live-safe smoke that does not mutate the default cache, point the command at a temp index:\n\n```bash\ntmp_index=\"$(mktemp -t portfolio-health.XXXXXX.db)\"\nuv run portfolio-health health \\\n  --index-path \"$tmp_index\" \\\n  --memory-dir \"$HOME/.claude/projects/$(python3 -c \"import os; h=os.path.expanduser('~').lstrip('/'); print('-' + h.replace('/', '-'))\")/memory\" \\\n  --bridge-path \"$HOME/.local/share/bridge-db/bridge.db\" \\\n  --full-rebuild \\\n  --json\nrm -f \"$tmp_index\"\n```\n\nThe report includes memory file count, cache row count, FTS row count, duplicate file paths/slugs, stale or missing cache paths, bridge-db activity row count, and latest bridge-db activity timestamp.\n\n## Data sources (read-only)\n\n- **Memory files**: `~/.claude/projects/\u003cencoded-home\u003e/memory/project_*.md` — auto-detected at startup\n- **Bridge-db**: `~/.local/share/bridge-db/bridge.db`\n\n## Index location\n\n`~/.local/share/portfolio-health/index.db` — auto-created, incrementally refreshed on every tool call. Disposable cache; rebuild or delete it when it drifts.\n\n## MCP registration\n\nAdd to your Claude Code `mcp.json` or `~/.claude/claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"portfolio-health\": {\n      \"command\": \"uv\",\n      \"args\": [\"run\", \"--directory\", \"/path/to/portfolio-health\", \"python\", \"-m\", \"portfolio_health\"],\n      \"env\": {}\n    }\n  }\n}\n```\n\nReplace `/path/to/portfolio-health` with the absolute path to your clone. If the memory directory is not auto-detected, or bridge-db lives outside the default `~/.local/share` location, set the matching env vars:\n\n```json\n{\n  \"mcpServers\": {\n    \"portfolio-health\": {\n      \"command\": \"uv\",\n      \"args\": [\"run\", \"--directory\", \"/path/to/portfolio-health\", \"python\", \"-m\", \"portfolio_health\"],\n      \"env\": {\n        \"PORTFOLIO_HEALTH_MEMORY_DIR\": \"/path/to/.claude/projects/\u003cencoded-home\u003e/memory\",\n        \"PORTFOLIO_HEALTH_BRIDGE_DB\": \"/path/to/.local/share/bridge-db/bridge.db\"\n      }\n    }\n  }\n}\n```\n\n`PORTFOLIO_HEALTH_BRIDGE_DB` overrides the bridge-db path (default `~/.local/share/bridge-db/bridge.db`); `PORTFOLIO_HEALTH_MEMORY_DIR` overrides the memory directory.\n\n## Dev\n\n```bash\nuv run pytest -q\nuv run ruff check src/ tests/\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaagpatel%2Fportfolio-health","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaagpatel%2Fportfolio-health","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaagpatel%2Fportfolio-health/lists"}