U4 of the Claude Desktop .mcpb bundle plan.
- mcp/manifest.json hand-authored to match PP's emitted shape (see
~/printing-press/library/bugbounty-goat/manifest.json for the
canonical reference). 13 user_config slots, all sensitive=true and
required=false so the engine's graceful degradation to web-only
mode keeps the install non-blocking on credential entry.
- Covered API keys: OpenAI, xAI, Brave, Exa, Serper, Google,
Gemini (and the Google_genai alias), Apify, Bluesky app password,
Parallel, ScrapeCreators, OpenRouter. Cookie / session flows
(Truth Social, Xiaohongshu, ChatGPT account ID, Codex auth)
deferred per plan Scope Boundaries - they need a richer UX than
plain user_config strings.
- internal/manifest/manifest_test.go enforces the structural
invariants Claude Desktop install correctness depends on:
required MCPB fields, lowercased-env-name -> user_config-key
cross-reference both directions, sensitive=true + required=false +
description present on every slot, and platform list coverage.
- Local smoke: `printing-press bundle --skip-build --binary <built>`
produces last30days-pp-mcp-darwin-arm64.mcpb (4.7MB compressed,
manifest.json + bin/last30days-pp-mcp).
U3 of the Claude Desktop .mcpb bundle plan.
- internal/engine/run.go invokes python3 with the cached last30days.py,
forwards os.Environ() so MCPB user_config env-injection reaches the
engine, sets PYTHONPATH so the lib/ imports resolve, and surfaces
three distinct error shapes (missing interpreter with install URL,
non-zero exit with stderr, timeout). RunOptions.PythonPath lets tests
inject a stub without manipulating PATH.
- internal/engine/run_test.go drives a shell-script stub interpreter
through happy path, env forwarding, PYTHONPATH, non-zero exit with
stderr surfacing, timeout, missing python3 (empty PATH), missing
last30days.py, empty CacheDir, and timeout-env-override parsing.
- internal/tools/research.go registers a single research tool whose
schema mirrors /last30days <topic> (required topic, optional emit
enum, optional save bool). Validation failures surface as MCP
tool errors so Claude sees structured failures instead of transport
faults; engine extract or run errors fold engine stderr into the
message so users can diagnose without leaving Desktop.
- internal/tools/research_test.go covers requireString, emitArgument,
boolArgument, handler-level validation routing, and formatRunError.
- cmd/last30days-pp-mcp/main.go wires NewMCPServer + tools.Register +
ServeStdio. main.Version is ldflags-stamped at build time and
namespaces the per-user cache. mcp-go pinned at v0.54.0.
Smoke check: ./build/last30days-pp-mcp answers tools/list with the
research tool plus full schema and read-only/open-world annotations.
go test ./... passes across engine + tools (32 cases).
U2 of the Claude Desktop .mcpb bundle plan.
- internal/engine/embed.go embeds the vendored Python tree at build time
via //go:embed all:vendored. The engine package owns the embed because
Go's directive cannot reach outside its own package directory; sync and
gitignore paths are updated to match (internal/engine/vendored/ in
place of mcp/vendored/).
- internal/engine/extract.go materializes the embed into
<cache>/last30days-pp-mcp/<version>/ with a .version sentinel that
short-circuits re-extraction. Atomic rename from a .tmp sibling means
a partial extraction can never be mistaken for complete. Concurrent
first-call extractions serialize behind a per-cache-dir sync.Once.
- EnsureUserCache honors a LAST30DAYS_CACHE_DIR env override for
locked-down filesystems; the override is named in extract errors.
- internal/engine/extract_test.go covers happy path, sentinel skip,
version bump, 10-goroutine race, empty-version rejection, unwritable
cache parent, and the env override (7 tests, all passing).
- A tracked vendored/.gitkeep anchors the embed path so the directive
matches even before scripts/sync-engine.sh runs.