<feed xmlns='http://www.w3.org/2005/Atom'>
<title>rulesets/mcp/install.py, branch main</title>
<subtitle>Claude Code skills, rules, and language bundles
</subtitle>
<id>https://git.cjennings.net/rulesets/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/rulesets/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/'/>
<updated>2026-05-28T14:20:08+00:00</updated>
<entry>
<title>feat(mcp): add uninstall + --check + README section for MCP pipeline</title>
<updated>2026-05-28T14:20:08+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-28T14:20:08+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=814695eae81dd1c63d75cae87375e703bb388243'/>
<id>urn:sha1:814695eae81dd1c63d75cae87375e703bb388243</id>
<content type='text'>
Three coupled additions close the MCP pipeline thread.

mcp/install.py grew --uninstall and --check modes via argparse. The
default install behavior is unchanged.

--uninstall iterates over servers.json and runs `claude mcp remove
&lt;name&gt; -s user` for each, skipping anything not registered. Idempotent.

--check is the dry-run drift report. For each server, classify as ok
(in both servers.json and `claude mcp list`), MISSING (configured but
not registered), or EXTRA (registered but not in servers.json). Exit
non-zero only on MISSING since EXTRA entries are often deliberate (the
claude.ai web servers register out-of-band). Smoke test against the
live config: 9 ok, 0 missing, 3 EXTRA, exit 0.

Two new Makefile targets:
- make uninstall-mcp invokes the --uninstall mode.
- make check-mcp invokes the --check mode.

README.org gained an MCP section under Two install modes covering all
three targets, the OAuth-token-on-disk story, and a pointer to
mcp/README.org for the full pipeline.

Closes TODO #7 (uninstall + --check) and TODO #8 (README MCP section).
</content>
</entry>
<entry>
<title>chore(mcp): mark install.py executable</title>
<updated>2026-05-07T04:39:25+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-07T04:39:25+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=e26264ac85ac07af8716770063e636abccf2d9fe'/>
<id>urn:sha1:e26264ac85ac07af8716770063e636abccf2d9fe</id>
<content type='text'>
The shebang was already there but the file mode was 644. The Makefile invokes via "python3 mcp/install.py" so it worked anyway, but the mode now matches the shebang.
</content>
</entry>
<entry>
<title>feat(mcp): add user-scope MCP install pipeline</title>
<updated>2026-05-07T04:11:15+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-07T04:11:15+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=07c2c5ccf288e6ecc25808784ea407821df3d433'/>
<id>urn:sha1:07c2c5ccf288e6ecc25808784ea407821df3d433</id>
<content type='text'>
I needed a single source of truth for MCP server registration so a fresh machine boots with the full set instead of being rebuilt by hand. install.py decrypts mcp/secrets.env.gpg, expands ${VAR} placeholders in mcp/servers.json, and runs claude mcp add --scope user for anything not already registered. Idempotent.

The encrypted bundle carries six values: the Google client id and secret, the Figma API key, the GCP OAuth keys JSON (base64), and the two @a-bonus/google-docs-mcp token caches (personal and work, base64). install.py writes the keys file and the two token files to the paths each package reads at startup, all mode 600.

Bundling the Google Docs tokens lets a new machine connect google-docs-personal and google-docs-work without the interactive OAuth flow. Without the cached token, the package falls back to a browser-redirect flow that Claude Code's stdio MCP loader can't drive, so it shows "Failed to connect" until the user runs the npx command manually.

Make target: install-mcp. Plaintext secrets and the decrypted keys file are gitignored.
</content>
</entry>
</feed>
