career-watch, mcp, Gemini, Antigravity

00 — Rebuilding this after the first version never really connected

00 — Rebuilding this after the first version never really connected

The first version of this server worked fine when called directly in Python, but never got a clean connection to Antigravity over the real MCP protocol — it kept hitting "unknown tool" errors even after a full app restart, and the only way to get a session going was bypassing MCP entirely and calling the Python functions in-process. That's not a working integration, so this is a rebuild with a few real changes, not just a copy-paste.

The likely actual bug: a cwd key that was never real

The old mcp_config.json entry had a cwd field pointing at the server's own folder. Reading Antigravity's actual documented schema (not a guess, not a third-party blog with a different field list), the real schema for a local stdio server is just command, args, and env — there is no cwd key at all. Antigravity's own JSON schema likely rejected or silently dropped that key, which lines up with the symptom observed at the time: the mcp_servers component just didn't load, instead of failing loudly.

The actual fix isn't a config trick — it's not depending on an external cwd in the first place. server.py now resolves its own .env file relative to __file__:

SERVER_DIR = Path(__file__).resolve().parent
load_dotenv(SERVER_DIR / ".env")

This works no matter what directory the MCP client happens to launch the process from, so there's nothing left for a missing cwd to break.

Functions instead of a class

The first version wrapped all six tools in a CareerWatchTools class and registered them in __init__. That's also what caused the "Tool already exists" warnings during the earlier debugging session — re-importing server.py for a quick manual test re-ran that registration against the same mcp object. Plain top-level functions with @mcp.tool() directly on them don't have that problem, and there was no real reason for the class in the first place — none of the tools share state that needs an instance.

Why record_job_findings replaces instead of merges

The first build's actual bug (documented in the old notes/02-*.md, since deleted along with the rest of that project) was a tool that consumed a one-time placeholder string on its first call, so every call after that silently did nothing while still reporting success. The fix chosen then — and kept here — is: every call to record_job_findings replaces everything after a stable ## Step 2 - findings heading with exactly the findings passed in that call. The tool stays dumb and predictable; the calling agent is responsible for including earlier findings again if it wants them kept. Verified with a real two-call test against this rebuild: the second call's data fully replaced the first's, confirmed by reading the file back, not assumed from the code.

The new piece: finish_watch_session

The first build needed the calling agent to remember to separately call update_watch_list with the entire watch-list file content by hand — error-prone, and the kind of step an agent (or me) forgets under token pressure. finish_watch_session closes that loop itself: it re-reads the session file's own findings table (not a separate in-memory state that could drift from what's actually on disk) and updates just the Last checked/Notes columns for the companies it actually found a row for, leaving everything else in the watch list untouched. Verified end-to-end: ran a real session with two fake findings (TD, Cohere), called finish_watch_session, and confirmed both rows updated correctly in watch-list.md — then reverted that test data back to the real 2026-10-19 values before committing anything for real.