tappy-mcp 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.11", "3.12", "3.13"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: ${{ matrix.python-version }}
19
+ - name: Install
20
+ run: pip install -e ".[dev]"
21
+ - name: Lint
22
+ run: ruff check .
23
+ - name: Test
24
+ run: pytest -q
@@ -0,0 +1,25 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+
9
+ # Virtual envs
10
+ .venv/
11
+ venv/
12
+ env/
13
+
14
+ # Tooling caches
15
+ .pytest_cache/
16
+ .mypy_cache/
17
+ .ruff_cache/
18
+
19
+ # Editor / OS
20
+ .DS_Store
21
+ .idea/
22
+ .vscode/
23
+
24
+ # Local agent config
25
+ .claude/
@@ -0,0 +1,38 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/), and the project aims to follow
5
+ [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Added
10
+ - Inspector primitives `tappy read <server> <uri>` (resource contents) and
11
+ `tappy prompt <server> <name>` (render a prompt) — all three MCP primitives are now
12
+ exercisable from the CLI.
13
+ - `tappy restore <client>` rolls a client config back to its latest backup (itself backed
14
+ up first); `tappy restore --list` shows available backups.
15
+ - `tappy add --dry-run` previews the change as a unified diff without writing.
16
+ - `--version` / `-V`.
17
+ - `--path` selector to disambiguate servers that exist in several configs of the same
18
+ client (e.g. Claude Code's project `./.mcp.json` vs. user `~/.claude.json`).
19
+ - `TAPPY_CONFIG` environment variable: point Tappy at extra config files (standard
20
+ `mcpServers` shape) it doesn't know natively.
21
+
22
+ ### Changed
23
+ - Renamed the monitoring commands: `ps` → `status`, `stats` → `watch`. Output and framing
24
+ no longer lean on Docker.
25
+ - `registry --init --from-client` now templates secret `env`/`header` values as `${KEY}`
26
+ placeholders instead of writing real secrets into the (git-committed) registry file.
27
+
28
+ ### Fixed
29
+ - A config file that fails to parse is now flagged (`parse error`) and writes to it are
30
+ refused, instead of silently appearing empty and having a sentinel written back into it.
31
+ - `add` no longer mangles arguments that contain spaces (args are kept as a list).
32
+ - `disable` warns when the target client may not honor a `disabled` flag.
33
+ - Inspector/tool errors surface the real MCP cause instead of a TaskGroup wrapper message.
34
+ - Fingerprint store writes are now atomic.
35
+
36
+ ## [0.1.0]
37
+ - Initial release: discovery, safe edits, MCP inspection, monitoring, tool-definition
38
+ pinning, and the team registry.
@@ -0,0 +1,380 @@
1
+ # Tappy — User Guide
2
+
3
+ A practical, task-oriented guide to using Tappy: the CLI that discovers, configures,
4
+ inspects, monitors, and standardizes **MCP (Model Context Protocol)** servers across your
5
+ AI clients (Claude Desktop, Claude Code, Cursor, and more).
6
+
7
+ If you just want the elevator pitch and install steps, see the [README](README.md). This
8
+ document is the hands-on manual.
9
+
10
+ ---
11
+
12
+ ## Contents
13
+
14
+ 1. [Install](#install)
15
+ 2. [Core concepts](#core-concepts)
16
+ 3. [Quick start](#quick-start)
17
+ 4. [How you name a server (targeting)](#how-you-name-a-server-targeting)
18
+ 5. [Command reference](#command-reference)
19
+ - [Manage](#manage)
20
+ - [Monitor](#monitor)
21
+ - [Inspect](#inspect)
22
+ - [Secure](#secure)
23
+ - [Team](#team)
24
+ 6. [Common workflows](#common-workflows)
25
+ 7. [Scripting & CI (JSON + exit codes)](#scripting--ci-json--exit-codes)
26
+ 8. [Files, environment & configuration](#files-environment--configuration)
27
+ 9. [Safety guarantees](#safety-guarantees)
28
+ 10. [Troubleshooting](#troubleshooting)
29
+ 11. [Security notes](#security-notes)
30
+
31
+ ---
32
+
33
+ ## Install
34
+
35
+ Requires Python 3.11+. The PyPI package is **`tappy-mcp`**; it installs the **`tappy`**
36
+ command.
37
+
38
+ ```bash
39
+ pip install tappy-mcp
40
+ # or, isolated (recommended for CLI tools):
41
+ pipx install tappy-mcp
42
+ # or run without installing (the npx equivalent):
43
+ uvx tappy-mcp status
44
+ ```
45
+
46
+ Verify:
47
+
48
+ ```bash
49
+ tappy --version
50
+ tappy help # grouped overview of every command
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Core concepts
56
+
57
+ | Term | What it means in Tappy |
58
+ |------|------------------------|
59
+ | **Client** | An AI app that runs MCP servers (Claude Desktop, Claude Code, Cursor). Each stores servers in its own JSON config file. |
60
+ | **Server** | A single MCP server entry — either a local **stdio** process (`command` + `args`) or a remote **HTTP/SSE** endpoint (`url`). |
61
+ | **Transport** | How Tappy talks to a server: `stdio`, `http` (streamable), or `sse`. |
62
+ | **Health** | A server's *live* status, obtained by actually connecting and running the MCP handshake — never guessed from config. `running` / `error` / `disabled`. |
63
+ | **Pin / fingerprint** | A recorded hash of a server's tool definitions. Lets Tappy detect when a server changes its tools *after* you trusted it (a "rug-pull"). |
64
+ | **Registry** | A shared `tappy.team.json` listing the servers a team has approved, so everyone can converge on the same set. |
65
+
66
+ Tappy reads every client's config into **one normalized view**, so the same commands work
67
+ no matter which client a server lives in.
68
+
69
+ ---
70
+
71
+ ## Quick start
72
+
73
+ ```bash
74
+ tappy list # every server across every client
75
+ tappy status # ...with live health, latency, and tool counts
76
+ tappy inspect <name> # full report for one server: status + tools + resources + prompts
77
+ ```
78
+
79
+ Every data command supports `--json` and returns exit code **0** on success / **1** on
80
+ error, so it drops straight into scripts and CI.
81
+
82
+ ---
83
+
84
+ ## How you name a server (targeting)
85
+
86
+ Most commands take a **target**. There are two ways to specify one:
87
+
88
+ **1. By name** — a server already in some client's config:
89
+ ```bash
90
+ tappy inspect github
91
+ ```
92
+
93
+ **2. Ad-hoc** — a server that isn't installed anywhere yet, described inline:
94
+ ```bash
95
+ tappy inspect --command npx --args "-y @modelcontextprotocol/server-everything"
96
+ tappy inspect --url https://example.com/mcp
97
+ ```
98
+
99
+ Narrowing helpers (for named targets):
100
+
101
+ - `--client <id>` — restrict to one client (`claude_desktop`, `claude_code`, `cursor`, …).
102
+ - `--path <substring>` — restrict to configs whose file path contains a string. Use this
103
+ when the **same** client has the server in two places (e.g. Claude Code's project
104
+ `./.mcp.json` vs. user `~/.claude.json`):
105
+ ```bash
106
+ tappy disable github --path .mcp.json # the project one, not the user one
107
+ ```
108
+
109
+ **Auto-pick:** the read-only inspector commands (`inspect`, `tools`, `resources`,
110
+ `prompts`, `read`, `prompt`, `probe`, `pin`, `verify`, `call`) will use the sole configured
111
+ server automatically if you don't name one. Destructive commands (`remove`, `disable`,
112
+ `sync`) never auto-pick.
113
+
114
+ ---
115
+
116
+ ## Command reference
117
+
118
+ Run `tappy <command> -h` for the exact flags of any command.
119
+
120
+ ### Manage
121
+
122
+ ```bash
123
+ tappy list # all servers across every client (alias: ls)
124
+ tappy list --client cursor # only one client
125
+ tappy clients # the discovered config files themselves
126
+
127
+ # add a stdio server (put the command after `--` so args like -y aren't seen as flags)
128
+ tappy add fs -- npx -y @modelcontextprotocol/server-filesystem .
129
+
130
+ # add a remote server
131
+ tappy add api --url https://example.com/mcp
132
+ tappy add api --url https://example.com/sse --transport sse --header "Authorization=Bearer XYZ"
133
+
134
+ # preview a change as a diff without writing anything
135
+ tappy add fs --dry-run -- npx -y @scope/server .
136
+
137
+ tappy enable fs # / tappy disable fs
138
+ tappy remove fs # (alias: rm)
139
+
140
+ # roll a client's config back to its most recent backup
141
+ tappy restore --list # show available backups
142
+ tappy restore claude_desktop # restore the latest backup for that client
143
+ ```
144
+
145
+ Notes:
146
+ - `add` writes to the **first discovered client** if you don't pass `--client`; it prints
147
+ which one it chose.
148
+ - Secret `env`/`header` values are **masked** in all output.
149
+ - A config file that fails to parse is flagged as `parse error`, and Tappy **refuses to
150
+ write to it** until you fix the JSON by hand.
151
+
152
+ ### Monitor
153
+
154
+ ```bash
155
+ tappy status # health snapshot of every server
156
+ tappy status -a # include disabled servers
157
+ tappy status --client cursor # restrict to one client
158
+ tappy status --fail-on-down # exit non-zero if any enabled server is unreachable (CI)
159
+ tappy status --json # machine-readable
160
+
161
+ tappy watch # live, auto-refreshing monitor (Ctrl-C to quit)
162
+ tappy watch --interval 2 # refresh faster
163
+ tappy watch --no-stream # print one snapshot and exit
164
+
165
+ tappy probe github # one-line health + latency for a single server
166
+ ```
167
+
168
+ `status`/`watch`/`probe` connect over the **real MCP protocol** — `STATUS` reflects an
169
+ actual handshake, not just whether the config exists. `status` also prints a footer
170
+ warning if any pinned server's tools have drifted.
171
+
172
+ ### Inspect
173
+
174
+ ```bash
175
+ tappy inspect github # status + tools + resources + prompts + pin status
176
+ tappy tools github # just the tools, with input schemas
177
+ tappy tools github --json
178
+ tappy resources github # list resource URIs
179
+ tappy prompts github # list prompt names
180
+
181
+ # exercise each of the three MCP primitives:
182
+ tappy call github create_issue -a title="Bug" -a body="..." # invoke a tool (key=value args)
183
+ tappy call github create_issue --input '{"title":"Bug"}' # ...or JSON args
184
+ tappy read github "file:///etc/hosts" # read a resource's contents
185
+ tappy prompt github review -a lang=python # render a prompt's messages
186
+ ```
187
+
188
+ All of these work against installed servers **and** ad-hoc `--command`/`--url` targets.
189
+
190
+ ### Secure
191
+
192
+ ```bash
193
+ tappy pin github # trust the current tools (record a fingerprint)
194
+ tappy verify github # re-check; exit 1 if the tools changed (CI gate)
195
+ ```
196
+
197
+ The first time you `pin`, Tappy stores a hash of the server's tool definitions. `verify`
198
+ compares against it later. A change means the server altered the tools it advertises after
199
+ you trusted it — a potential rug-pull. `inspect` shows pin status inline; `status` warns
200
+ about drift automatically.
201
+
202
+ ### Team
203
+
204
+ ```bash
205
+ # create a shared registry, optionally seeded from a client you already have set up
206
+ tappy registry --init --from-client claude_desktop
207
+ tappy registry # show the approved server set (alias: team)
208
+
209
+ tappy apply --dry-run # preview provisioning into local clients
210
+ tappy apply # write them (each change backed up)
211
+ tappy apply --client cursor # only one client
212
+
213
+ tappy lint # report drift; exit 1 on unapproved servers (CI gate)
214
+
215
+ tappy sync github --from claude_desktop --to cursor # copy a server between clients
216
+ ```
217
+
218
+ Seeding from a client **never writes real secrets** into the registry: `env`/`header`
219
+ values are templated as `${KEY}` placeholders that each developer fills in locally after
220
+ `apply`. Commit `tappy.team.json`; teammates run `tappy apply`.
221
+
222
+ ---
223
+
224
+ ## Common workflows
225
+
226
+ **"Is everything I rely on actually working right now?"**
227
+ ```bash
228
+ tappy status
229
+ ```
230
+
231
+ **"A server is failing — what's wrong?"**
232
+ ```bash
233
+ tappy probe <name> # confirm it's down and see the error
234
+ tappy inspect <name> # more detail if it connects at all
235
+ ```
236
+
237
+ **"I want to try a server before installing it anywhere."**
238
+ ```bash
239
+ tappy inspect --command npx --args "-y @some/mcp-server"
240
+ ```
241
+
242
+ **"Copy a working server from one client to another."**
243
+ ```bash
244
+ tappy sync <name> --from claude_desktop --to cursor
245
+ ```
246
+
247
+ **"Lock down a server so I get warned if it changes its tools."**
248
+ ```bash
249
+ tappy pin <name>
250
+ # later, or in CI:
251
+ tappy verify <name> # exits 1 if the tools changed
252
+ ```
253
+
254
+ **"Undo a change I just made."**
255
+ ```bash
256
+ tappy restore <client> # rolls back to the latest backup
257
+ ```
258
+
259
+ **"Standardize a team on the same servers."**
260
+ ```bash
261
+ tappy registry --init --from-client claude_desktop # once, by a maintainer
262
+ git add tappy.team.json && git commit -m "Approved MCP servers"
263
+ # each teammate:
264
+ tappy apply
265
+ ```
266
+
267
+ ---
268
+
269
+ ## Scripting & CI (JSON + exit codes)
270
+
271
+ Every data command accepts `--json` and emits pretty JSON to stdout. Secret values are
272
+ always masked.
273
+
274
+ **Exit codes:**
275
+
276
+ | Code | Meaning |
277
+ |------|---------|
278
+ | `0` | success |
279
+ | `1` | error — not found, unreachable, drift detected, or invalid input |
280
+ | `2` | bad command-line usage (argparse) |
281
+ | `130` | interrupted with Ctrl-C |
282
+
283
+ **CI gate examples:**
284
+ ```bash
285
+ tappy status --fail-on-down # fail the build if any server is down
286
+ tappy verify weather # fail if a pinned server's tools changed
287
+ tappy lint # fail if any client has an unapproved server
288
+ ```
289
+
290
+ **Parse output with `jq`:**
291
+ ```bash
292
+ tappy status --json | jq '.[] | select(.health != "running") | .name'
293
+ ```
294
+
295
+ ---
296
+
297
+ ## Files, environment & configuration
298
+
299
+ **Files Tappy owns** (all under `~/.tappy/`):
300
+
301
+ | Path | Purpose |
302
+ |------|---------|
303
+ | `~/.tappy/backups/` | Timestamped backups written before every config change. |
304
+ | `~/.tappy/fingerprints.json` | Pinned tool fingerprints for rug-pull detection. |
305
+ | `~/.tappy/tappy.team.json` | Default location for the team registry (user scope). |
306
+
307
+ **Client config files Tappy reads/writes** (discovered automatically):
308
+
309
+ - Claude Desktop — `~/Library/Application Support/Claude/claude_desktop_config.json`
310
+ (macOS), `%APPDATA%\Claude\…` (Windows), `~/.config/Claude/…` (Linux)
311
+ - Claude Code — `./.mcp.json` (project) and `~/.claude.json` (user)
312
+ - Cursor — `./.cursor/mcp.json` (project) and `~/.cursor/mcp.json` (user)
313
+
314
+ **Environment variables:**
315
+
316
+ | Variable | Effect |
317
+ |----------|--------|
318
+ | `TAPPY_CONFIG` | OS-path-separated list of extra config files (standard `mcpServers` shape) to manage — point Tappy at a client it doesn't know natively. |
319
+ | `TAPPY_REGISTRY` | Path to the team registry, overriding the default lookup. |
320
+
321
+ Registry path resolves from `--registry` → `$TAPPY_REGISTRY` → `./tappy.team.json` →
322
+ `~/.tappy/tappy.team.json`.
323
+
324
+ ---
325
+
326
+ ## Safety guarantees
327
+
328
+ When Tappy edits a config file, it guarantees:
329
+
330
+ - **Non-destructive** — only the `mcpServers` section is touched; every other key in the
331
+ file is preserved exactly.
332
+ - **Backup-first** — a timestamped copy is written to `~/.tappy/backups/` before any
333
+ change, so every edit is reversible with `tappy restore`.
334
+ - **Atomic** — writes go to a temp file and are then atomically renamed into place, so a
335
+ crash can never leave a half-written config.
336
+ - **Refuses broken files** — if a config can't be parsed, Tappy won't write to it (it would
337
+ otherwise clobber content it couldn't read).
338
+
339
+ ---
340
+
341
+ ## Troubleshooting
342
+
343
+ **`error: unknown command: X`** — run `tappy help` for the command list. Note the
344
+ monitoring commands are `status` and `watch` (not `ps`/`stats`).
345
+
346
+ **A server shows `error` in `status`/`probe`.** Tappy actually tried to connect. Common
347
+ causes: the `command` isn't on your `PATH`, a required runtime (`node`/`npx`/`uvx`) is
348
+ missing, a file path in `args` is wrong, or a remote `url` is unreachable. Run
349
+ `tappy probe <name>` to see the underlying error message.
350
+
351
+ **`... could not be parsed (…); fix its JSON by hand first`.** The client's config file has
352
+ invalid JSON. Fix the JSON, then retry. Tappy deliberately refuses to overwrite a file it
353
+ couldn't read.
354
+
355
+ **`server '<name>' exists in multiple configs`.** The name is defined in more than one
356
+ config. Disambiguate with `--client` and/or `--path` (the error lists each location).
357
+
358
+ **`no team registry found`.** Create one with `tappy registry --init`, or point at an
359
+ existing one with `--registry` / `$TAPPY_REGISTRY`.
360
+
361
+ **A remote server needs auth.** Pass headers when adding it:
362
+ `tappy add api --url … --header "Authorization=Bearer <token>"`. Header values are masked
363
+ in output.
364
+
365
+ ---
366
+
367
+ ## Security notes
368
+
369
+ - **Secrets in output are masked.** `env` and `header` values show as `***` in tables and
370
+ `--json`. The real values remain only in the client config files themselves.
371
+ - **The registry never carries real secrets.** Seeding writes `${KEY}` placeholders, not
372
+ your actual tokens — safe to commit to git.
373
+ - **Rug-pull detection.** Pin the servers you trust (`tappy pin`), then `tappy verify` (or
374
+ watch the `status` drift footer) to catch a server that changes its advertised tools
375
+ after approval. Re-pin intentionally when a change is expected.
376
+
377
+ ---
378
+
379
+ *For the project overview, architecture, and contribution notes, see the
380
+ [README](README.md) and [CHANGELOG](CHANGELOG.md).*
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 aadhil96
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.