dynamitecircle 2.0.1__tar.gz → 2.0.4__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.
@@ -14,6 +14,56 @@ the public Python API surface (`dc.DC`, `dc.DCError`, `dc.Result`,
14
14
 
15
15
  ---
16
16
 
17
+ ## [Unreleased]
18
+
19
+ ## [2.0.4] – 2026-06-16
20
+
21
+ ### Added
22
+
23
+ - **MCP tool annotations** — every tool now advertises `readOnlyHint` /
24
+ `destructiveHint` / `openWorldHint`, so clients can auto-approve reads and
25
+ flag writes. The read/write split is derived from each command's actual HTTP
26
+ verb and guarded by a test that re-derives it from source (no silent drift).
27
+ Write tools also carry a "⚠️ Write operation" note in their description,
28
+ mirroring the hosted MCP server.
29
+ - **Structured tool output** — object results (the list envelope
30
+ `{items, count, cursor, has_more}` and single records) are also returned as
31
+ MCP `structuredContent`, so structure-aware clients don't have to re-parse
32
+ the text. No `outputSchema` is declared (it would strictly validate every
33
+ response shape); lists/scalars remain text-only.
34
+
35
+ ### Changed
36
+
37
+ - **BREAKING (MCP interface) — tool names and fields are now snake_case.**
38
+ MCP tool names switched from kebab-case to snake_case
39
+ (`mcp__dc__trip-create` → `mcp__dc__trip_create`), and tool argument fields
40
+ likewise (`event-id` → `event_id`, `start-date` → `start_date`). This aligns
41
+ the agent-facing MCP surface with the snake_case Python library
42
+ (`dc.trip_create()`) and the prevailing MCP tool-naming convention. The
43
+ **CLI is unchanged** (`dc trip-create`, `--start-date`). Wildcard allow-rules
44
+ like `mcp__dc__*` are unaffected; any config pinning a specific kebab tool
45
+ name must update it. `_call_tool` still accepts the legacy kebab form as a
46
+ transitional fallback.
47
+
48
+ ### Docs / packaging
49
+
50
+ - Document the hosted MCP (`https://api.dynamitecircle.com/mcp`) and an
51
+ auto-updating `uvx` PyPI config; fix the `.venv/bin/python3` configs to bare
52
+ `python3`; unify `py/SKILL.md` (local client + hosted MCP) and add
53
+ `DC/SKILL.md`; add `tests/test_version_sync.py` guarding manifest/config
54
+ drift from `DC_API_VERSION`.
55
+ - **One-click install** — "Add to Cursor" and "Install in VS Code" badges for
56
+ the hosted MCP, a native VS Code section (`.vscode/mcp.json` + `code --add-mcp`),
57
+ and a `workflows`-first note for the ~80-tool surface.
58
+ - **`.mcpb` bundle** — the release workflow now builds a `dc.mcpb` (one-click
59
+ Claude Desktop install) via `mcpb pack` and attaches it to the GitHub release;
60
+ `.mcpbignore` keeps the bundle lean.
61
+ - **Official MCP Registry** — add `server.json` (`io.github.dynamitecircle/dc`,
62
+ listing the hosted streamable-HTTP endpoint) and a separate, isolated
63
+ `publish-mcp.yml` workflow that publishes it via GitHub OIDC on `v*` tags.
64
+ The registry feeds VS Code, Smithery, PulseMCP, and others. `server.json`
65
+ version is guarded by `tests/test_version_sync.py`.
66
+
17
67
  ## [2.0.1] – 2026-06-16
18
68
 
19
69
  ### Changed
@@ -1071,7 +1121,8 @@ Tag exists but no PyPI release. Replaced by 1.6.3.
1071
1121
 
1072
1122
  ---
1073
1123
 
1074
- [Unreleased]: https://github.com/dynamitecircle/dc/compare/v2.0.1...HEAD
1124
+ [Unreleased]: https://github.com/dynamitecircle/dc/compare/v2.0.4...HEAD
1125
+ [2.0.4]: https://github.com/dynamitecircle/dc/compare/v2.0.1...v2.0.4
1075
1126
  [2.0.1]: https://github.com/dynamitecircle/dc/releases/tag/v2.0.1
1076
1127
  [1.23.3]: https://github.com/dynamitecircle/dc/releases/tag/v1.23.3
1077
1128
  [1.22.6]: https://github.com/dynamitecircle/dc/releases/tag/v1.22.6
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dynamitecircle
3
- Version: 2.0.1
3
+ Version: 2.0.4
4
4
  Summary: Official Python client for the public Dynamite Circle Member API. CLI, library, Agent Skill, and MCP server in one file.
5
5
  Project-URL: Homepage, https://github.com/dynamitecircle/dc
6
6
  Project-URL: Repository, https://github.com/dynamitecircle/dc
@@ -153,6 +153,37 @@ The same `dc.py` file is shipped as **four** integrations — pick whichever fit
153
153
 
154
154
  The `mcp` package is **lazy-imported** — Agent Skill / CLI / Python-library users never need it.
155
155
 
156
+ ### Don't want to install anything? Use the hosted MCP
157
+
158
+ The DC Member API is **also a hosted MCP server** — a remote endpoint you point your AI app at, with nothing to clone, install, or update:
159
+
160
+ ```
161
+ https://api.dynamitecircle.com/mcp (Streamable HTTP)
162
+ ```
163
+
164
+ [![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=dc&config=eyJ1cmwiOiJodHRwczovL2FwaS5keW5hbWl0ZWNpcmNsZS5jb20vbWNwIn0=)
165
+  
166
+ [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_DC-007ACC?logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=dc&config=%7B%22url%22%3A%22https%3A%2F%2Fapi.dynamitecircle.com%2Fmcp%22%7D)
167
+
168
+ - **Claude Code:** `claude mcp add --transport http dc https://api.dynamitecircle.com/mcp`
169
+ - **Claude (web / Desktop / mobile):** Settings → Connectors → **Add custom connector** → paste the URL → **Connect**
170
+ - **VS Code:** click the badge above, or `code --add-mcp '{"name":"dc","url":"https://api.dynamitecircle.com/mcp"}'`
171
+ - **Cursor:** click the badge above, or add a custom MCP connector pointing at the URL
172
+ - **ChatGPT / any other MCP app:** add a custom / remote MCP connector pointing at the URL above
173
+
174
+ When prompted, **sign in with your DC account** (one-click OAuth) — or send a `dk_` key as `Authorization: Bearer`. The server is always on the current API version, so there's nothing to keep up to date.
175
+
176
+ **Hosted MCP vs. this client — which do I want?**
177
+
178
+ | You want… | Use |
179
+ |---|---|
180
+ | Zero install, always-current, just MCP tools in a chat app | **Hosted MCP** (URL above) |
181
+ | To call DC from your own Python code (`from dynamitecircle import DC`) | **This client** (library) |
182
+ | A local CLI (`dc profile`) or shell scripting | **This client** (CLI) |
183
+ | A local stdio MCP server (offline-capable, pin a version, dev against localhost) | **This client** (`dc --mcp`) |
184
+
185
+ The rest of this README is about **this client**. For the hosted MCP, the URL above is all you need.
186
+
156
187
  ## Features
157
188
 
158
189
  - **Full Member API coverage** — read + write across every public endpoint (run `python3 py/dc.py help` for the live list)
@@ -214,7 +245,18 @@ Run `python3 py/dc.py help` for the full command list.
214
245
 
215
246
  ## Setup per AI tool
216
247
 
217
- ### Claude Code
248
+ ### Hosted MCP (no install, always current)
249
+
250
+ Nothing to clone or update — point your tool at the remote endpoint:
251
+
252
+ ```bash
253
+ # Claude Code
254
+ claude mcp add --transport http dc https://api.dynamitecircle.com/mcp
255
+ ```
256
+
257
+ For Claude web / Desktop, Cursor, ChatGPT, and other MCP apps, add a **custom / remote connector** pointing at `https://api.dynamitecircle.com/mcp` (Streamable HTTP). Sign in with your DC account (OAuth) or send a `dk_` key as a Bearer header. Discovery is published at [`/.well-known/mcp.json`](https://api.dynamitecircle.com/.well-known/mcp.json). This is the lowest-effort path; everything below runs the client **locally** instead.
258
+
259
+ ### Claude Code (local client)
218
260
 
219
261
  `.mcp.json` is already shipped with this repo. Open the repo with `claude`:
220
262
 
@@ -231,9 +273,30 @@ pip install -r py/requirements.txt
231
273
 
232
274
  Skill discovery (CLI + import) works automatically via `py/SKILL.md`.
233
275
 
276
+ #### Auto-updating local MCP via `uvx` (PyPI)
277
+
278
+ Prefer a local stdio server that **pulls the latest published client on every launch** with no clone to maintain? Use [`uv`](https://docs.astral.sh/uv/)'s `uvx` and pass your key in the `env` block:
279
+
280
+ ```json
281
+ {
282
+ "mcpServers": {
283
+ "dc": {
284
+ "type": "stdio",
285
+ "command": "uvx",
286
+ "args": ["--refresh", "--from", "dynamitecircle[mcp]", "dc", "--mcp"],
287
+ "env": { "DC_API_KEY": "dk_<api-key>" }
288
+ }
289
+ }
290
+ }
291
+ ```
292
+
293
+ Notes: the console script is `dc` (not `dynamitecircle`), so `--from dynamitecircle[mcp]` is required — it both names the package and pulls the optional `mcp` extra. `--refresh` is what makes it auto-update; drop it to let `uvx` cache. The `DC_API_KEY` in `env` wins over any `.env.dc` (an ephemeral `uvx` install has none).
294
+
234
295
  ### Claude Desktop
235
296
 
236
- Edit your config file:
297
+ **Easiest — one-click `.mcpb`:** download `dc.mcpb` from the [latest GitHub release](https://github.com/dynamitecircle/dc/releases/latest) and double-click it. Claude Desktop installs the `dc` server and prompts for your `DC_API_KEY` (no JSON editing). Requires Python 3.9+ with the `mcp` package available on your machine (`pip install 'dynamitecircle[mcp]'`). *(Or skip local install entirely and use the [hosted MCP connector](#hosted-mcp-no-install-always-current) above.)*
298
+
299
+ **Manual** — edit your config file:
237
300
 
238
301
  - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
239
302
  - Linux: `~/.config/Claude/claude_desktop_config.json`
@@ -330,9 +393,19 @@ Non-paginated extras (e.g. `totalUnread` on `inbox`) are passed through under an
330
393
 
331
394
  ## Use as a Python library
332
395
 
396
+ Installed from PyPI (`pip install dynamitecircle`) — import by the package name:
397
+
398
+ ```python
399
+ from dynamitecircle import DC
400
+
401
+ dc = DC()
402
+ ```
403
+
404
+ Working from a clone instead? Point `sys.path` at the `py/` folder and import as `dc`:
405
+
333
406
  ```python
334
407
  import sys
335
- sys.path.insert(0, "dc")
408
+ sys.path.insert(0, "py")
336
409
  from dc import DC
337
410
 
338
411
  dc = DC()
@@ -366,8 +439,21 @@ The full live reference for the DC Member API — every endpoint, parameter, and
366
439
 
367
440
  The DC Member API ships new endpoints and refinements regularly. This skill is the official client and we update it whenever the API changes. **Plan for updates** — the skill will warn you on stderr the first time a request returns a server `X-API-Version` newer than `DC_API_VERSION`, and major-version bumps may break older clients.
368
441
 
442
+ **The no-maintenance option:** if you use the [hosted MCP](#hosted-mcp-no-install-always-current) (`https://api.dynamitecircle.com/mcp`), there's nothing to update — the server always runs the current API version. The strategies below apply only to the **local client**.
443
+
369
444
  Pick whichever integration style fits your project. From simplest to most isolated:
370
445
 
446
+ ### 0. PyPI — `pip` / `uvx`
447
+
448
+ ```bash
449
+ pip install dynamitecircle # CLI + library
450
+ pip install 'dynamitecircle[mcp]' # + local MCP server (dc --mcp)
451
+ ```
452
+
453
+ Then `dc setup --api-key dk_<api-key>` and `dc self-test`. Update with `pip install --upgrade dynamitecircle`. For a local MCP that auto-updates on every launch, use the [`uvx` config above](#auto-updating-local-mcp-via-uvx-pypi).
454
+
455
+ Best for: the quickest local install, and the auto-updating `uvx` MCP setup.
456
+
371
457
  ### 1. Plain git clone — quick local use
372
458
 
373
459
  ```bash
@@ -506,6 +592,9 @@ dc/
506
592
 
507
593
  │ (Future: go/, node/, rs/ folders for sister clients in other languages)
508
594
 
595
+ ├── DC/
596
+ │ └── SKILL.md → ../py/SKILL.md # human-friendly skill path (symlink)
597
+
509
598
  ├── docs/ # ← REAL design docs (canonical)
510
599
  │ ├── skill-info.md # design rules / architecture
511
600
  │ └── mcp-info.md # MCP setup for every supported client
@@ -100,6 +100,37 @@ The same `dc.py` file is shipped as **four** integrations — pick whichever fit
100
100
 
101
101
  The `mcp` package is **lazy-imported** — Agent Skill / CLI / Python-library users never need it.
102
102
 
103
+ ### Don't want to install anything? Use the hosted MCP
104
+
105
+ The DC Member API is **also a hosted MCP server** — a remote endpoint you point your AI app at, with nothing to clone, install, or update:
106
+
107
+ ```
108
+ https://api.dynamitecircle.com/mcp (Streamable HTTP)
109
+ ```
110
+
111
+ [![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=dc&config=eyJ1cmwiOiJodHRwczovL2FwaS5keW5hbWl0ZWNpcmNsZS5jb20vbWNwIn0=)
112
+ &nbsp;
113
+ [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_DC-007ACC?logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=dc&config=%7B%22url%22%3A%22https%3A%2F%2Fapi.dynamitecircle.com%2Fmcp%22%7D)
114
+
115
+ - **Claude Code:** `claude mcp add --transport http dc https://api.dynamitecircle.com/mcp`
116
+ - **Claude (web / Desktop / mobile):** Settings → Connectors → **Add custom connector** → paste the URL → **Connect**
117
+ - **VS Code:** click the badge above, or `code --add-mcp '{"name":"dc","url":"https://api.dynamitecircle.com/mcp"}'`
118
+ - **Cursor:** click the badge above, or add a custom MCP connector pointing at the URL
119
+ - **ChatGPT / any other MCP app:** add a custom / remote MCP connector pointing at the URL above
120
+
121
+ When prompted, **sign in with your DC account** (one-click OAuth) — or send a `dk_` key as `Authorization: Bearer`. The server is always on the current API version, so there's nothing to keep up to date.
122
+
123
+ **Hosted MCP vs. this client — which do I want?**
124
+
125
+ | You want… | Use |
126
+ |---|---|
127
+ | Zero install, always-current, just MCP tools in a chat app | **Hosted MCP** (URL above) |
128
+ | To call DC from your own Python code (`from dynamitecircle import DC`) | **This client** (library) |
129
+ | A local CLI (`dc profile`) or shell scripting | **This client** (CLI) |
130
+ | A local stdio MCP server (offline-capable, pin a version, dev against localhost) | **This client** (`dc --mcp`) |
131
+
132
+ The rest of this README is about **this client**. For the hosted MCP, the URL above is all you need.
133
+
103
134
  ## Features
104
135
 
105
136
  - **Full Member API coverage** — read + write across every public endpoint (run `python3 py/dc.py help` for the live list)
@@ -161,7 +192,18 @@ Run `python3 py/dc.py help` for the full command list.
161
192
 
162
193
  ## Setup per AI tool
163
194
 
164
- ### Claude Code
195
+ ### Hosted MCP (no install, always current)
196
+
197
+ Nothing to clone or update — point your tool at the remote endpoint:
198
+
199
+ ```bash
200
+ # Claude Code
201
+ claude mcp add --transport http dc https://api.dynamitecircle.com/mcp
202
+ ```
203
+
204
+ For Claude web / Desktop, Cursor, ChatGPT, and other MCP apps, add a **custom / remote connector** pointing at `https://api.dynamitecircle.com/mcp` (Streamable HTTP). Sign in with your DC account (OAuth) or send a `dk_` key as a Bearer header. Discovery is published at [`/.well-known/mcp.json`](https://api.dynamitecircle.com/.well-known/mcp.json). This is the lowest-effort path; everything below runs the client **locally** instead.
205
+
206
+ ### Claude Code (local client)
165
207
 
166
208
  `.mcp.json` is already shipped with this repo. Open the repo with `claude`:
167
209
 
@@ -178,9 +220,30 @@ pip install -r py/requirements.txt
178
220
 
179
221
  Skill discovery (CLI + import) works automatically via `py/SKILL.md`.
180
222
 
223
+ #### Auto-updating local MCP via `uvx` (PyPI)
224
+
225
+ Prefer a local stdio server that **pulls the latest published client on every launch** with no clone to maintain? Use [`uv`](https://docs.astral.sh/uv/)'s `uvx` and pass your key in the `env` block:
226
+
227
+ ```json
228
+ {
229
+ "mcpServers": {
230
+ "dc": {
231
+ "type": "stdio",
232
+ "command": "uvx",
233
+ "args": ["--refresh", "--from", "dynamitecircle[mcp]", "dc", "--mcp"],
234
+ "env": { "DC_API_KEY": "dk_<api-key>" }
235
+ }
236
+ }
237
+ }
238
+ ```
239
+
240
+ Notes: the console script is `dc` (not `dynamitecircle`), so `--from dynamitecircle[mcp]` is required — it both names the package and pulls the optional `mcp` extra. `--refresh` is what makes it auto-update; drop it to let `uvx` cache. The `DC_API_KEY` in `env` wins over any `.env.dc` (an ephemeral `uvx` install has none).
241
+
181
242
  ### Claude Desktop
182
243
 
183
- Edit your config file:
244
+ **Easiest — one-click `.mcpb`:** download `dc.mcpb` from the [latest GitHub release](https://github.com/dynamitecircle/dc/releases/latest) and double-click it. Claude Desktop installs the `dc` server and prompts for your `DC_API_KEY` (no JSON editing). Requires Python 3.9+ with the `mcp` package available on your machine (`pip install 'dynamitecircle[mcp]'`). *(Or skip local install entirely and use the [hosted MCP connector](#hosted-mcp-no-install-always-current) above.)*
245
+
246
+ **Manual** — edit your config file:
184
247
 
185
248
  - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
186
249
  - Linux: `~/.config/Claude/claude_desktop_config.json`
@@ -277,9 +340,19 @@ Non-paginated extras (e.g. `totalUnread` on `inbox`) are passed through under an
277
340
 
278
341
  ## Use as a Python library
279
342
 
343
+ Installed from PyPI (`pip install dynamitecircle`) — import by the package name:
344
+
345
+ ```python
346
+ from dynamitecircle import DC
347
+
348
+ dc = DC()
349
+ ```
350
+
351
+ Working from a clone instead? Point `sys.path` at the `py/` folder and import as `dc`:
352
+
280
353
  ```python
281
354
  import sys
282
- sys.path.insert(0, "dc")
355
+ sys.path.insert(0, "py")
283
356
  from dc import DC
284
357
 
285
358
  dc = DC()
@@ -313,8 +386,21 @@ The full live reference for the DC Member API — every endpoint, parameter, and
313
386
 
314
387
  The DC Member API ships new endpoints and refinements regularly. This skill is the official client and we update it whenever the API changes. **Plan for updates** — the skill will warn you on stderr the first time a request returns a server `X-API-Version` newer than `DC_API_VERSION`, and major-version bumps may break older clients.
315
388
 
389
+ **The no-maintenance option:** if you use the [hosted MCP](#hosted-mcp-no-install-always-current) (`https://api.dynamitecircle.com/mcp`), there's nothing to update — the server always runs the current API version. The strategies below apply only to the **local client**.
390
+
316
391
  Pick whichever integration style fits your project. From simplest to most isolated:
317
392
 
393
+ ### 0. PyPI — `pip` / `uvx`
394
+
395
+ ```bash
396
+ pip install dynamitecircle # CLI + library
397
+ pip install 'dynamitecircle[mcp]' # + local MCP server (dc --mcp)
398
+ ```
399
+
400
+ Then `dc setup --api-key dk_<api-key>` and `dc self-test`. Update with `pip install --upgrade dynamitecircle`. For a local MCP that auto-updates on every launch, use the [`uvx` config above](#auto-updating-local-mcp-via-uvx-pypi).
401
+
402
+ Best for: the quickest local install, and the auto-updating `uvx` MCP setup.
403
+
318
404
  ### 1. Plain git clone — quick local use
319
405
 
320
406
  ```bash
@@ -453,6 +539,9 @@ dc/
453
539
 
454
540
  │ (Future: go/, node/, rs/ folders for sister clients in other languages)
455
541
 
542
+ ├── DC/
543
+ │ └── SKILL.md → ../py/SKILL.md # human-friendly skill path (symlink)
544
+
456
545
  ├── docs/ # ← REAL design docs (canonical)
457
546
  │ ├── skill-info.md # design rules / architecture
458
547
  │ └── mcp-info.md # MCP setup for every supported client
@@ -1,5 +1,31 @@
1
1
  # MCP — Setup for every supported client
2
2
 
3
+ There are **two ways** to get DC's tools into an MCP client:
4
+
5
+ 1. **Hosted MCP (no install)** — point your client at the remote endpoint `https://api.dynamitecircle.com/mcp`. Nothing to clone, install, or update; always on the current API version. Best for chat apps (Claude web/Desktop, Cursor, ChatGPT).
6
+ 2. **Local server (this client)** — run `dc.py --mcp` as a local stdio server with the `--mcp` flag. Best when you want offline capability, a pinned version, or to develop against a local API. Most of this doc covers this path.
7
+
8
+ ## Hosted MCP (no install)
9
+
10
+ `https://api.dynamitecircle.com/mcp` (Streamable HTTP). Authenticate with one-click OAuth (reuses your DC login) or a `dk_` API key as a Bearer header.
11
+
12
+ | Client | How |
13
+ |---|---|
14
+ | **Claude Code** | `claude mcp add --transport http dc https://api.dynamitecircle.com/mcp` |
15
+ | **Claude web / Desktop / mobile** | Settings → Connectors → **Add custom connector** → paste URL → **Connect** → sign in with DC (paid plan; Team/Enterprise admins may need to approve first) |
16
+ | **VS Code** | `code --add-mcp '{"name":"dc","url":"https://api.dynamitecircle.com/mcp"}'` (or the badge below) |
17
+ | **Cursor / ChatGPT / other MCP apps** | Add a custom / remote MCP connector pointing at the URL above |
18
+
19
+ One-click badges (hosted MCP):
20
+
21
+ [![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=dc&config=eyJ1cmwiOiJodHRwczovL2FwaS5keW5hbWl0ZWNpcmNsZS5jb20vbWNwIn0=)
22
+ &nbsp;
23
+ [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_DC-007ACC?logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=dc&config=%7B%22url%22%3A%22https%3A%2F%2Fapi.dynamitecircle.com%2Fmcp%22%7D)
24
+
25
+ Discovery metadata is published at [`/.well-known/mcp.json`](https://api.dynamitecircle.com/.well-known/mcp.json); the OAuth protected-resource descriptor is at `/.well-known/oauth-protected-resource`. The server is also listed in the [Official MCP Registry](https://registry.modelcontextprotocol.io) as `io.github.dynamitecircle/dc` (which feeds VS Code, Smithery, PulseMCP, and others).
26
+
27
+ ## Local server (this client)
28
+
3
29
  The `dc` skill exposes its commands as [Model Context Protocol](https://modelcontextprotocol.io) tools when run with the `--mcp` flag. The protocol is standard, so the same server works in every MCP-compatible client.
4
30
 
5
31
  ## One-time install
@@ -22,6 +48,31 @@ python3 py/dc.py --mcp
22
48
 
23
49
  The server speaks JSON-RPC over stdio. It's not meant to be used directly — wire it into a client below.
24
50
 
51
+ ## Auto-updating local server via `uvx` (PyPI)
52
+
53
+ To run a local stdio server **without a clone** that pulls the latest published client on every launch, use [`uv`](https://docs.astral.sh/uv/)'s `uvx`:
54
+
55
+ ```json
56
+ {
57
+ "mcpServers": {
58
+ "dc": {
59
+ "type": "stdio",
60
+ "command": "uvx",
61
+ "args": ["--refresh", "--from", "dynamitecircle[mcp]", "dc", "--mcp"],
62
+ "env": { "DC_API_KEY": "dk_<api-key>" }
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ Three things make this work:
69
+
70
+ - **`--from dynamitecircle[mcp]`** — the package is `dynamitecircle` but the console script is `dc`, so you can't write `uvx dynamitecircle`; `--from` names the package (and the `[mcp]` extra installs the MCP dependency) while `dc` is the command to run.
71
+ - **`--refresh`** — forces `uvx` to check PyPI for a newer version instead of reusing its cache. Drop it if you'd rather pin to whatever's cached.
72
+ - **`env.DC_API_KEY`** — an ephemeral `uvx` install has no `py/.env.dc`, so pass the key via the env block. It takes precedence over any `.env.dc` anyway (existing env vars always win).
73
+
74
+ Same idea with `pipx`: `pipx run --spec 'dynamitecircle[mcp]' dc --mcp` (add `--no-cache` to force the latest).
75
+
25
76
  ## Quick check (no client needed)
26
77
 
27
78
  ```bash
@@ -129,6 +180,24 @@ mcp_servers:
129
180
 
130
181
  `GEMINI.md` (symlinked to `CLAUDE.md`) provides additional context for the assistant.
131
182
 
183
+ ### VS Code
184
+
185
+ VS Code has native MCP support and reads `.vscode/mcp.json` (per-workspace) or your user `mcp.json`. For the **hosted** server, the one-click badge above or `code --add-mcp '{"name":"dc","url":"https://api.dynamitecircle.com/mcp"}'` is all you need. For a **local** stdio server, add to `.vscode/mcp.json`:
186
+
187
+ ```json
188
+ {
189
+ "servers": {
190
+ "dc": {
191
+ "type": "stdio",
192
+ "command": "python3",
193
+ "args": ["/absolute/path/to/dc/py/dc.py", "--mcp"]
194
+ }
195
+ }
196
+ }
197
+ ```
198
+
199
+ VS Code shows inline **Start / Stop / Restart** actions above each server in that file. It also renders the [MCP registry](https://code.visualstudio.com/docs/agent-customization/mcp-servers) in the Extensions view, so once `dc` is listed there it's installable from inside the editor.
200
+
132
201
  ### Cursor
133
202
 
134
203
  1. Open Settings → MCP
@@ -138,6 +207,8 @@ mcp_servers:
138
207
  - Command: `python3`
139
208
  - Args: `/absolute/path/to/dc/py/dc.py --mcp`
140
209
 
210
+ Or use the one-click **Add to Cursor** badge above (hosted MCP).
211
+
141
212
  ### Cline / Continue / Windsurf / Zed
142
213
 
143
214
  Same shape. Each tool has its own settings UI but the data is the same:
@@ -77,11 +77,25 @@ Fields:
77
77
 
78
78
  | Field | Required | Meaning |
79
79
  |---|---|---|
80
- | `name` | MUST | Public command name. Kebab-case. Used in CLI and as MCP tool name |
81
- | `help` | SHOULD | One-line description for `help` listing |
80
+ | `name` | MUST | Public command name. **Kebab-case** used verbatim as the CLI command (`dc trip-create`) and converted to snake_case for the MCP tool name (`mcp__dc__trip_create`) |
81
+ | `help` | SHOULD | One-line description for `help` listing (also the MCP tool description) |
82
82
  | `parser` | optional | Static method on `_DCCore` that converts raw CLI args → `(args, kwargs)`. Required if the command takes flags |
83
83
 
84
- There are **no** `mode="read"|"write"` labels, no permission gates, no extension hooks. The decorator is intentionally minimal.
84
+ The decorator carries **no** `mode="read"|"write"` label, no permission gates, no extension hooks — it stays minimal. Read/write classification for MCP annotations is derived centrally instead (see Tool naming below).
85
+
86
+ ## Tool naming and casing
87
+
88
+ Three surfaces, three casings — each idiomatic for its context. Don't "fix" them to match each other:
89
+
90
+ | Surface | Form | Example |
91
+ |---|---|---|
92
+ | CLI command | kebab-case | `dc trip-create`, `--start-date` |
93
+ | Python method / library | snake_case | `dc.trip_create()` |
94
+ | MCP tool + fields | snake_case | `mcp__dc__trip_create`, field `event_id` |
95
+
96
+ The MCP layer converts kebab → snake at the boundary (`run_mcp` for tool names, `_build_input_schema` for fields); the CLI parser still gets kebab. Tool names are **noun-first** (`trip_create`, not `create_trip`). This is deliberate: alphabetical tool lists then cluster all operations on the same resource together (`trip_create` / `trip_delete` / `trip_update` / `trips` sit adjacent), which is more discoverable for an agent scanning `tools/list` than a verb-first scheme that scatters them by action. A verb-first reorder was considered and **rejected** for that reason (and to avoid breaking renames across all three surfaces).
97
+
98
+ **Annotations:** each MCP tool advertises `readOnlyHint` / `destructiveHint` / `openWorldHint`. The read/write split lives in `_WRITE_COMMANDS` (in `dc.py`), derived from each command's actual HTTP verb and guarded by `tests/test_annotations.py`, which re-derives the set from source and fails on drift — so a new write command can't silently ship mislabelled as read-only.
85
99
 
86
100
  ## Argument parsing
87
101
 
@@ -221,8 +235,8 @@ How it works:
221
235
 
222
236
  1. `_MCP_AVAILABLE` flag set at module load via `try: from mcp.server import Server`
223
237
  2. `--mcp` in `sys.argv` → entry point calls `DC().run_mcp()`
224
- 3. `run_mcp()` builds an `mcp.server.Server`, registers `list_tools` (one per `@skill_command`) and `call_tool` (translates MCP args → CLI raw_args, then invokes via `self._invoke`)
225
- 4. Result serialized as `TextContent` (JSON for dicts/lists, str for scalars)
238
+ 3. `run_mcp()` builds an `mcp.server.Server`, registers `list_tools` (one per `@skill_command`, each with a snake_case name + `readOnlyHint`/`destructiveHint`/`openWorldHint` annotations) and `call_tool` (translates MCP args → CLI raw_args, then invokes via `self._invoke`)
239
+ 4. Result serialized as `TextContent` (JSON for dicts/lists, str for scalars); object results are *also* returned as `structuredContent` for structure-aware clients
226
240
 
227
241
  If `mcp` is not installed → clean install hint, no traceback. CLI and Python-import users have **zero** dependencies.
228
242
 
@@ -231,9 +245,10 @@ If `mcp` is not installed → clean install hint, no traceback. CLI and Python-i
231
245
  1. Add the business logic to `_DCCore` (returns plain Python data)
232
246
  2. If it takes flags → add a `_parse_*` static method to `_DCCore`
233
247
  3. Add a `@skill_command`-decorated wrapper to `DC` that delegates to `_core`
234
- 4. Update `SKILL.md` with usage examples
235
- 5. Verify the new endpoint is documented in the live API reference: https://www.dynamitecircle.com/developers/
236
- 6. Verify:
248
+ 4. **If it mutates (POST/PATCH/PUT/DELETE), add its name to `_WRITE_COMMANDS`** in `dc.py` `tests/test_annotations.py` re-derives writes from the HTTP verbs and fails if you forget, so a write can't ship mislabelled as a safe read
249
+ 5. Update `SKILL.md` with usage examples
250
+ 6. Verify the new endpoint is documented in the live API reference: https://www.dynamitecircle.com/developers/
251
+ 7. Verify:
237
252
  ```bash
238
253
  python3 py/dc.py help # appears in list
239
254
  python3 py/dc.py <new-command> --json # round-trips
@@ -246,7 +261,7 @@ These were design choices. **Do not add them without a discussion**:
246
261
 
247
262
  - ❌ Telemetry / usage tracking
248
263
  - ❌ Error logging to disk (`.errors.jsonl`)
249
- - ❌ Read/write mode labels on commands
264
+ - ❌ Read/write **gates** (permission enforcement) — note: read/write *annotations* for MCP clients DO exist (advisory hints derived from HTTP verbs), but nothing in the client blocks a write
250
265
  - ❌ Extension hooks (`@skill_extension`)
251
266
  - ❌ Owner-binding / runtime metadata
252
267
  - ❌ Lint rules / sweep / pulse
@@ -2,7 +2,7 @@
2
2
  "manifest_version": "0.3",
3
3
  "name": "dc",
4
4
  "display_name": "DC Member API",
5
- "version": "2.0.1",
5
+ "version": "2.0.4",
6
6
  "description": "Read and act on your own Dynamite Circle membership data via the public Member API.",
7
7
  "long_description": "Single-file Python **client** for the public DC Member API at https://api.dynamitecircle.com. Same file runs three ways: CLI, Python library, and MCP server (--mcp flag, opt-in mcp dependency). Compatible with Claude Code, Claude Desktop, Codex CLI, Gemini CLI, Cursor, GitHub Copilot, and any other Agent Skills / MCP-compatible tool. Covers profile (read+update), trips (CRUD), events + RSVP, virtual events + RSVP, tickets, invites, inbox, rooms, chapters, places lookup, locator digest, announcements, and event-day agendas (DCBKK / DCMEX / DC BLACK schedules, meetups, sponsors, session bookmarks).",
8
8
  "author": {