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.