mcpsmith 0.2.2__tar.gz → 0.4.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.
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/.claude-plugin/plugin.json +18 -2
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/AGENTS.md +2 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/PKG-INFO +24 -2
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/README.md +22 -1
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/docs/design.md +3 -0
- mcpsmith-0.4.0/docs/superpowers/plans/2026-10-21-app-path-task1.md +70 -0
- mcpsmith-0.4.0/docs/superpowers/plans/2026-10-21-app-path-task2.md +51 -0
- mcpsmith-0.4.0/docs/superpowers/plans/2026-10-21-app-path-task3.md +53 -0
- mcpsmith-0.4.0/docs/superpowers/plans/2026-10-21-app-path-task4.md +48 -0
- mcpsmith-0.4.0/docs/superpowers/plans/2026-10-21-setup-bundle.md +75 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/pyproject.toml +5 -1
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/server.json +2 -2
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/skills/build-mcp-server/SKILL.md +2 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/skills/extract-api/SKILL.md +16 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/__init__.py +1 -1
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/cli.py +210 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/design/_digest.py +2 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/design/_draft.py +1 -1
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/design/_validate.py +7 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/importers/__init__.py +1 -1
- mcpsmith-0.4.0/src/mcpsmith/importers/android.py +432 -0
- mcpsmith-0.4.0/src/mcpsmith/importers/electron.py +475 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/importers/har.py +15 -0
- mcpsmith-0.4.0/src/mcpsmith/importers/jadx.py +131 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/model.py +2 -1
- mcpsmith-0.4.0/src/mcpsmith/probe.py +132 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/runtime/http.py +4 -3
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/runtime/interpret.py +4 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/server.py +124 -0
- mcpsmith-0.4.0/src/mcpsmith/setup/__init__.py +0 -0
- mcpsmith-0.4.0/src/mcpsmith/setup/agents.py +158 -0
- mcpsmith-0.4.0/src/mcpsmith/setup/run.py +234 -0
- mcpsmith-0.4.0/src/mcpsmith/skills_path.py +8 -0
- mcpsmith-0.4.0/tests/fixtures/android/builder.java +61 -0
- mcpsmith-0.4.0/tests/fixtures/android/interface.java +67 -0
- mcpsmith-0.4.0/tests/fixtures/electron/broken.js +3 -0
- mcpsmith-0.4.0/tests/fixtures/electron/broken.js.map +1 -0
- mcpsmith-0.4.0/tests/fixtures/electron/plain.js +23 -0
- mcpsmith-0.4.0/tests/fixtures/electron/renderer.js +2 -0
- mcpsmith-0.4.0/tests/fixtures/electron/renderer.js.map +1 -0
- mcpsmith-0.4.0/tests/test_android.py +324 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/test_cli.py +4 -0
- mcpsmith-0.4.0/tests/test_desktop.py +112 -0
- mcpsmith-0.4.0/tests/test_electron.py +352 -0
- mcpsmith-0.4.0/tests/test_probe.py +132 -0
- mcpsmith-0.4.0/tests/test_setup.py +375 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/uv.lock +12 -1
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/.claude-plugin/marketplace.json +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/.github/workflows/ci.yml +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/.github/workflows/publish.yml +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/.gitignore +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/LICENSE +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/docs/superpowers/plans/2026-10-21-hub-core.md +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/docs/superpowers/specs/2026-10-20-mcp-everything-design.md +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/skills/design-mcp-tools/SKILL.md +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/browser.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/capture.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/check.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/codegen.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/design/__init__.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/design/guide.md +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/design/llm.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/hub.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/importers/openapi.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/importers/rea.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/record.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/runtime/__init__.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/runtime/server.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/runtime/shape.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/runtime/transforms.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/secrets.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/templates/ci.yml +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/templates/conftest.py.tmpl +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/templates/gitignore +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/src/mcpsmith/templates/publish.yml +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/conftest.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/fake_cdp.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/fixtures/har/shop.har +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/fixtures/openapi/petstore2.yaml +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/fixtures/openapi/petstore3.json +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/fixtures/openapi/tasks31.json +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/fixtures/rea/scenario.json +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/test_browser.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/test_codegen.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/test_design.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/test_hub.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/test_importers.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/test_interpret.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/test_record.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/test_runtime.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/test_secrets.py +0 -0
- {mcpsmith-0.2.2 → mcpsmith-0.4.0}/tests/test_transforms.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcpsmith",
|
|
3
3
|
"displayName": "mcpsmith",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"description": "Turn an OpenAPI file or a recorded website session into a curated, tested MCP server. Skills and an MCP server for your coding agent.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Sepehr Radmard",
|
|
@@ -23,7 +23,23 @@
|
|
|
23
23
|
"mcpsmith": {
|
|
24
24
|
"command": "uvx",
|
|
25
25
|
"args": [
|
|
26
|
-
"mcpsmith@0.
|
|
26
|
+
"mcpsmith@0.4.0",
|
|
27
|
+
"mcp"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"mcpsmith-chrome": {
|
|
31
|
+
"command": "npx",
|
|
32
|
+
"args": [
|
|
33
|
+
"-y",
|
|
34
|
+
"chrome-devtools-mcp@1.11.0",
|
|
35
|
+
"--browser-url=http://127.0.0.1:9222"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"rea": {
|
|
39
|
+
"command": "npx",
|
|
40
|
+
"args": [
|
|
41
|
+
"-y",
|
|
42
|
+
"rea-agents@6.7.0",
|
|
27
43
|
"mcp"
|
|
28
44
|
]
|
|
29
45
|
}
|
|
@@ -4,6 +4,8 @@ Python framework that turns an OpenAPI/Swagger file or a recorded website sessio
|
|
|
4
4
|
|
|
5
5
|
## Workflow
|
|
6
6
|
|
|
7
|
+
Install for your agents with `uvx mcpsmith setup` (`--dry-run` first).
|
|
8
|
+
|
|
7
9
|
Ask the user which tasks the server should support, then:
|
|
8
10
|
|
|
9
11
|
1. Website only: `mcpsmith capture <url> -o site.har --host <host>` (user browses, closes the browser), or use a DevTools HAR export.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: mcpsmith
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Turn an OpenAPI file or a recorded website session into a curated, tested MCP server, with an LLM designing the tools.
|
|
5
5
|
Project-URL: Homepage, https://github.com/sepehr071/mcpsmith
|
|
6
6
|
Project-URL: Issues, https://github.com/sepehr071/mcpsmith/issues
|
|
@@ -22,6 +22,7 @@ Requires-Dist: httpx>=0.27
|
|
|
22
22
|
Requires-Dist: keyring>=25
|
|
23
23
|
Requires-Dist: mcp<3,>=2.2
|
|
24
24
|
Requires-Dist: pyyaml>=6
|
|
25
|
+
Requires-Dist: tomlkit>=0.12
|
|
25
26
|
Requires-Dist: truststore>=0.9
|
|
26
27
|
Requires-Dist: tzdata>=2024.1; sys_platform == 'win32'
|
|
27
28
|
Requires-Dist: websockets>=12
|
|
@@ -40,6 +41,23 @@ Turn an OpenAPI/Swagger file, or a website session you recorded in the browser,
|
|
|
40
41
|
|
|
41
42
|
Most generators mirror every endpoint as a tool and hand the model raw JSON, or stop at an OpenAPI spec or API client. mcpsmith designs a few workflow-level tools with plain argument names, descriptions that say when to call what next, and compact output. The input is an OpenAPI file or a HAR recording, so sites with no public API work too. The output is a server project with tests, CI, a PyPI + MCP Registry publish workflow and a Claude Code plugin.
|
|
42
43
|
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uvx mcpsmith setup
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Registers the `mcpsmith` and `mcpsmith-chrome` (Chrome DevTools) MCP servers and copies the skills into each coding agent it finds (Claude Code, Codex, Cursor, Gemini CLI), and installs REA (`rea-agents`) for them. Options:
|
|
51
|
+
|
|
52
|
+
- `--dry-run` prints the plan and writes nothing.
|
|
53
|
+
- `--agents claude,codex` limits it to those agents (default: the ones found).
|
|
54
|
+
- `--no-rea` skips REA; `--no-skills` skips the skills; `--yes` skips the confirmation.
|
|
55
|
+
- `--remove` undoes what setup did. Entries and skill folders you made yourself are left alone. REA stays installed (you may have had it before); the command prints the line that removes it.
|
|
56
|
+
|
|
57
|
+
Before setup first changes a config file, it copies the file to `*.mcpsmith.bak`. The backup is made once and not refreshed by later runs. Restart your agents afterwards. Run `mcpsmith browser` before using Chrome DevTools: it starts the shared Chrome the server connects to.
|
|
58
|
+
|
|
59
|
+
Claude Code plugin users already get all three servers from the plugin and do not need `setup`.
|
|
60
|
+
|
|
43
61
|
## Quickstart: OpenAPI
|
|
44
62
|
|
|
45
63
|
```bash
|
|
@@ -67,6 +85,10 @@ mcpsmith draft --name example-mcp
|
|
|
67
85
|
|
|
68
86
|
While recording, do each thing you want as a tool: search, open a detail page, paginate, apply a filter. Anything you do not click is not in the capture. `--host` (repeatable) keeps only the hosts you list. A DevTools "Save all as HAR" export works in place of `capture`. The recording browser uses `HTTPS_PROXY` when it is set.
|
|
69
87
|
|
|
88
|
+
A desktop program you own or are authorized to analyze: `mcpsmith app-desktop` prints the steps to record its traffic with your own mitmproxy (`mitmdump --set hardump=capture.har`); `mcpsmith app-desktop capture.har --host api.example.com` imports the result. mcpsmith runs nothing for you, and certificate-pinned programs are out of scope.
|
|
89
|
+
|
|
90
|
+
An Android app or an Electron/JavaScript app you own or are authorized to analyze: `mcpsmith app-android app.apk --base-url https://api.example.com` reads the app's Retrofit interface (needs REA's JADX jar, `REA_JADX_MCP_JAR`), and `mcpsmith app-js <dir|app.asar|file> --base-url ...` reads the calls in its shipped code. Neither runs the app. The endpoints are unverified (no example response) until `mcpsmith probe` calls the GET ones live with your credentials (`--writes` for the rest, only after you agree); a method that cannot be read stays empty and `validate` refuses it.
|
|
91
|
+
|
|
70
92
|
A HAR holds everything the browser sent and received, including cookies and your personal data: keep it out of git and delete it after `inspect`. `inspect` scrubs what it keeps: an API key or token becomes an `auth` env var with its value dropped, other secret-looking headers and query params are dropped, and emails, phone numbers, JWTs and values under secret or personal field names are masked in examples and test fixtures. This is pattern-based, so read `tests/fixtures/` before you publish a generated server. If a key is a public one the site sends to every visitor (a search or maps key), you may put it in the tool's `fixed` values.
|
|
71
93
|
|
|
72
94
|
## Use it from your agent
|
|
@@ -78,7 +100,7 @@ No API key needed: your coding agent is the designer.
|
|
|
78
100
|
```json
|
|
79
101
|
{"mcpServers": {"mcpsmith": {"command": "uvx", "args": ["mcpsmith", "mcp"]}}}
|
|
80
102
|
```
|
|
81
|
-
Tools: `smith_guide`, `smith_inspect`, `smith_draft`, `smith_validate`, `smith_generate`, `smith_check` (paths absolute), and for the hub `smith_record_start`, `smith_record_stop`, `smith_connect`, `smith_disconnect`, `smith_connections`, `smith_tools`, `smith_call`.
|
|
103
|
+
Tools: `smith_guide`, `smith_inspect`, `smith_app_android`, `smith_app_js`, `smith_app_desktop`, `smith_probe`, `smith_draft`, `smith_validate`, `smith_generate`, `smith_check` (paths absolute), and for the hub `smith_record_start`, `smith_record_stop`, `smith_connect`, `smith_disconnect`, `smith_connections`, `smith_tools`, `smith_call`.
|
|
82
104
|
MCP clients start servers without your shell's proxy variables; behind a proxy, add `"env": {"HTTPS_PROXY": "http://127.0.0.1:8080"}` so `smith_inspect` can fetch specs.
|
|
83
105
|
- Skills: `skills/build-mcp-server` (the whole workflow), `skills/extract-api` (logged-in sites into live tools) and `skills/design-mcp-tools` (design checklist). `AGENTS.md` points other agents (Codex, Cursor, Gemini CLI) at them.
|
|
84
106
|
|
|
@@ -6,6 +6,23 @@ Turn an OpenAPI/Swagger file, or a website session you recorded in the browser,
|
|
|
6
6
|
|
|
7
7
|
Most generators mirror every endpoint as a tool and hand the model raw JSON, or stop at an OpenAPI spec or API client. mcpsmith designs a few workflow-level tools with plain argument names, descriptions that say when to call what next, and compact output. The input is an OpenAPI file or a HAR recording, so sites with no public API work too. The output is a server project with tests, CI, a PyPI + MCP Registry publish workflow and a Claude Code plugin.
|
|
8
8
|
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
uvx mcpsmith setup
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Registers the `mcpsmith` and `mcpsmith-chrome` (Chrome DevTools) MCP servers and copies the skills into each coding agent it finds (Claude Code, Codex, Cursor, Gemini CLI), and installs REA (`rea-agents`) for them. Options:
|
|
16
|
+
|
|
17
|
+
- `--dry-run` prints the plan and writes nothing.
|
|
18
|
+
- `--agents claude,codex` limits it to those agents (default: the ones found).
|
|
19
|
+
- `--no-rea` skips REA; `--no-skills` skips the skills; `--yes` skips the confirmation.
|
|
20
|
+
- `--remove` undoes what setup did. Entries and skill folders you made yourself are left alone. REA stays installed (you may have had it before); the command prints the line that removes it.
|
|
21
|
+
|
|
22
|
+
Before setup first changes a config file, it copies the file to `*.mcpsmith.bak`. The backup is made once and not refreshed by later runs. Restart your agents afterwards. Run `mcpsmith browser` before using Chrome DevTools: it starts the shared Chrome the server connects to.
|
|
23
|
+
|
|
24
|
+
Claude Code plugin users already get all three servers from the plugin and do not need `setup`.
|
|
25
|
+
|
|
9
26
|
## Quickstart: OpenAPI
|
|
10
27
|
|
|
11
28
|
```bash
|
|
@@ -33,6 +50,10 @@ mcpsmith draft --name example-mcp
|
|
|
33
50
|
|
|
34
51
|
While recording, do each thing you want as a tool: search, open a detail page, paginate, apply a filter. Anything you do not click is not in the capture. `--host` (repeatable) keeps only the hosts you list. A DevTools "Save all as HAR" export works in place of `capture`. The recording browser uses `HTTPS_PROXY` when it is set.
|
|
35
52
|
|
|
53
|
+
A desktop program you own or are authorized to analyze: `mcpsmith app-desktop` prints the steps to record its traffic with your own mitmproxy (`mitmdump --set hardump=capture.har`); `mcpsmith app-desktop capture.har --host api.example.com` imports the result. mcpsmith runs nothing for you, and certificate-pinned programs are out of scope.
|
|
54
|
+
|
|
55
|
+
An Android app or an Electron/JavaScript app you own or are authorized to analyze: `mcpsmith app-android app.apk --base-url https://api.example.com` reads the app's Retrofit interface (needs REA's JADX jar, `REA_JADX_MCP_JAR`), and `mcpsmith app-js <dir|app.asar|file> --base-url ...` reads the calls in its shipped code. Neither runs the app. The endpoints are unverified (no example response) until `mcpsmith probe` calls the GET ones live with your credentials (`--writes` for the rest, only after you agree); a method that cannot be read stays empty and `validate` refuses it.
|
|
56
|
+
|
|
36
57
|
A HAR holds everything the browser sent and received, including cookies and your personal data: keep it out of git and delete it after `inspect`. `inspect` scrubs what it keeps: an API key or token becomes an `auth` env var with its value dropped, other secret-looking headers and query params are dropped, and emails, phone numbers, JWTs and values under secret or personal field names are masked in examples and test fixtures. This is pattern-based, so read `tests/fixtures/` before you publish a generated server. If a key is a public one the site sends to every visitor (a search or maps key), you may put it in the tool's `fixed` values.
|
|
37
58
|
|
|
38
59
|
## Use it from your agent
|
|
@@ -44,7 +65,7 @@ No API key needed: your coding agent is the designer.
|
|
|
44
65
|
```json
|
|
45
66
|
{"mcpServers": {"mcpsmith": {"command": "uvx", "args": ["mcpsmith", "mcp"]}}}
|
|
46
67
|
```
|
|
47
|
-
Tools: `smith_guide`, `smith_inspect`, `smith_draft`, `smith_validate`, `smith_generate`, `smith_check` (paths absolute), and for the hub `smith_record_start`, `smith_record_stop`, `smith_connect`, `smith_disconnect`, `smith_connections`, `smith_tools`, `smith_call`.
|
|
68
|
+
Tools: `smith_guide`, `smith_inspect`, `smith_app_android`, `smith_app_js`, `smith_app_desktop`, `smith_probe`, `smith_draft`, `smith_validate`, `smith_generate`, `smith_check` (paths absolute), and for the hub `smith_record_start`, `smith_record_stop`, `smith_connect`, `smith_disconnect`, `smith_connections`, `smith_tools`, `smith_call`.
|
|
48
69
|
MCP clients start servers without your shell's proxy variables; behind a proxy, add `"env": {"HTTPS_PROXY": "http://127.0.0.1:8080"}` so `smith_inspect` can fetch specs.
|
|
49
70
|
- Skills: `skills/build-mcp-server` (the whole workflow), `skills/extract-api` (logged-in sites into live tools) and `skills/design-mcp-tools` (design checklist). `AGENTS.md` points other agents (Codex, Cursor, Gemini CLI) at them.
|
|
50
71
|
|
|
@@ -60,6 +60,9 @@ openapi.yaml | site.har ──inspect──▶ .mcpsmith/source.json + digest.md
|
|
|
60
60
|
|
|
61
61
|
`mcpsmith capture <url> -o site.har` opens a browser (Playwright, optional extra) and saves the session.
|
|
62
62
|
A HAR exported from browser DevTools works the same.
|
|
63
|
+
`mcpsmith app-desktop` is the same path for a desktop program: it prints the steps for the developer's own
|
|
64
|
+
mitmproxy (`mitmdump --set hardump=<file>`), then imports the HAR through `inspect_source`. It never installs,
|
|
65
|
+
starts or configures a proxy or certificate; certificate-pinned programs are out of scope.
|
|
63
66
|
|
|
64
67
|
## Data model
|
|
65
68
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# App Path, Task 1: unverified endpoints and live probing (plan 3, part 1)
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans.
|
|
4
|
+
|
|
5
|
+
**Goal:** Let a `Source` hold endpoints that were read from an app's own code (method, path, params, no response yet), and verify them by calling the read-only ones live, so later platform importers (Android, Electron, desktop capture) all share one verification step.
|
|
6
|
+
|
|
7
|
+
**Architecture:** One new `Endpoint.verified` flag, and a `probe` module that calls GET candidates through the existing `mcpsmith.runtime.Api`, stores scrubbed example responses, and marks them verified. No new transport, no new auth path.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Existing: pydantic model, `runtime.Api`, `importers.har._scrub`, `mcp` server tools. No new dependency.
|
|
10
|
+
|
|
11
|
+
**Spec:** `docs/superpowers/specs/2026-10-20-mcp-everything-design.md` (G9; decisions 6, 7, 10). Scope confirmed by the user on 2026-10-21: apps the developer owns or is authorized to analyze; nothing that defeats a protection (no certificate-pinning bypass, no DRM or licensing circumvention).
|
|
12
|
+
|
|
13
|
+
## Global Constraints
|
|
14
|
+
|
|
15
|
+
- `verified: bool = True` on `Endpoint`, so every existing importer and saved `source.json` keeps its current meaning.
|
|
16
|
+
- Probing calls only `GET` endpoints by default; any other method requires an explicit opt-in argument and is reported as such. A probe that would send a write is skipped with a reason, never sent silently.
|
|
17
|
+
- Probe responses go through `importers.har._scrub` before being stored, like recordings do (spec anti-goal: no secrets or personal data on disk).
|
|
18
|
+
- Probing uses one `Api` built from the source's base URL plus the developer's own credentials (env var, keychain, or the shared Chrome session), exactly as hub tools do. No credential is created, guessed or brute-forced.
|
|
19
|
+
- Rate limit: at most 2 concurrent requests and a short delay between probes (reuse `Api`'s concurrency cap and jitter); a 429 or 403 stops probing that host with a clear message.
|
|
20
|
+
- Existing behaviour stays; full suite green; ruff clean; commits on `feat/app-path` without AI attribution.
|
|
21
|
+
|
|
22
|
+
## Review Focus
|
|
23
|
+
|
|
24
|
+
1. A probe target whose path still has unfilled `{placeholders}` → skipped with a reason, never requested with a literal brace. (Task tests.)
|
|
25
|
+
2. A 403/429 → probing stops for that host and says so, instead of hammering it. (Task tests.)
|
|
26
|
+
3. A probe response containing a token or an email → scrubbed before it reaches `source.json`. (Task tests.)
|
|
27
|
+
4. An endpoint with `verified: false` and no response example → `draft` still produces a usable plan, and `validate` warns rather than crashing. (Task tests.)
|
|
28
|
+
5. Non-GET candidates → never probed without the opt-in. (Task tests.)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
### Task 1: `Endpoint.verified` + `smith_probe`
|
|
33
|
+
|
|
34
|
+
**Files:**
|
|
35
|
+
- Modify: `src/mcpsmith/model.py`, `src/mcpsmith/design/_digest.py` (mark unverified endpoints in the digest), `src/mcpsmith/design/_draft.py` (prefer verified endpoints; keep unverified ones usable), `src/mcpsmith/server.py` (new tool), `src/mcpsmith/cli.py` (new command)
|
|
36
|
+
- Create: `src/mcpsmith/probe.py`
|
|
37
|
+
- Test: `tests/test_probe.py`
|
|
38
|
+
|
|
39
|
+
**Interfaces:**
|
|
40
|
+
- Produces:
|
|
41
|
+
- `Endpoint.verified: bool = True` — False means "read from the app's code, not yet seen answering".
|
|
42
|
+
- `probe.run(source: Source, *, writes: bool = False, limit: int = 20, env: dict[str, str] | None = None) -> list[ProbeResult]`; `ProbeResult` dataclass: `endpoint` (id), `ok`, `status`, `error`, `chars`, `skipped_reason`.
|
|
43
|
+
- Each successful probe sets that endpoint's `response_example` (scrubbed, trimmed like the HAR importer does) and `verified = True`; the caller saves the source.
|
|
44
|
+
- CLI `mcpsmith probe [-w DIR] [--limit N] [--writes]` → one line per endpoint; exit 1 if every probe failed.
|
|
45
|
+
- MCP tool `smith_probe(workdir, limit=20, writes=False)` → `{"verified": n, "failed": n, "skipped": n, "results": [...]}`, `read_only=False` (it calls a live API), docstring saying writes need the user's confirmation.
|
|
46
|
+
|
|
47
|
+
- [ ] **Step 1: Write the failing tests** in `tests/test_probe.py`, with a `Source` built in code (4 endpoints: a GET with a filled path, a GET whose path has an unfilled `{id}`, a POST, and a GET on a host that answers 429) and `Api.set_transport(httpx.MockTransport(...))`:
|
|
48
|
+
```python
|
|
49
|
+
async def test_probe_verifies_get_and_scrubs():
|
|
50
|
+
# GET /items -> {"items":[{"id":1,"email":"a@b.co","token":"secret123"}]}
|
|
51
|
+
results = probe.run(src)
|
|
52
|
+
ep = src.endpoint("GET /items")
|
|
53
|
+
assert ep.verified and "secret123" not in json.dumps(ep.response_example)
|
|
54
|
+
assert "a@b.co" not in json.dumps(ep.response_example)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_probe_skips_unfillable_path_and_writes():
|
|
58
|
+
r = {x.endpoint: x for x in probe.run(src)}
|
|
59
|
+
assert "placeholder" in r["GET /items/{id}"].skipped_reason
|
|
60
|
+
assert "write" in r["POST /items"].skipped_reason and src.endpoint("POST /items").verified is False
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_probe_stops_host_on_429():
|
|
64
|
+
# first GET -> 429; the second GET on the same host is skipped, not sent
|
|
65
|
+
assert sent == 1 and "rate" in results[1].skipped_reason
|
|
66
|
+
```
|
|
67
|
+
- [ ] **Step 2: Run** `uv run pytest tests/test_probe.py -q` → FAIL (module missing).
|
|
68
|
+
- [ ] **Step 3: Implement** `Endpoint.verified`, `probe.run`, the digest/draft touches (digest marks `unverified`; draft keeps such endpoints but their `Output.fields` stay empty when there is no example), the CLI command and the MCP tool.
|
|
69
|
+
- [ ] **Step 4: Run** `uv run pytest -q` → PASS; ruff clean.
|
|
70
|
+
- [ ] **Step 5: Commit** `feat: verify code-read endpoints by probing them live`.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# App Path, Task 2: Android endpoint extractor (plan 3, part 2)
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans.
|
|
4
|
+
|
|
5
|
+
**Goal:** Turn the HTTP interface declarations in a decompiled Android app into a `Source` of unverified endpoints, so a developer can give their agent tools for their own account in their own (or an authorized) app.
|
|
6
|
+
|
|
7
|
+
**Architecture:** REA (already installed by `mcpsmith setup`) decompiles the APK and returns Java source for a class; mcpsmith parses the Retrofit annotations in that text into `Endpoint`s with `verified: false`. Task 1's `probe` then verifies the read-only ones. No new transport and no bypass of any protection: this reads declarations the app itself ships.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Python stdlib `re`; the REA CLI (`npx -y rea-agents@6.7.0 …`) invoked through `subprocess` with injected runner for tests. No new dependency.
|
|
10
|
+
|
|
11
|
+
**Spec:** `docs/superpowers/specs/2026-10-20-mcp-everything-design.md` (G9 Android bullet; "Spike results" table: 113/114 endpoints with method, path and parameter kinds correct on Tusky 29.0). User scope (2026-10-21): the developer's own or authorized app; nothing that defeats a protection.
|
|
12
|
+
|
|
13
|
+
## Global Constraints
|
|
14
|
+
|
|
15
|
+
- Input is a decompiled-class text (Java) plus the app's base URL, supplied by the caller. mcpsmith never downloads an app, and never ships or installs a decompiler itself; REA is the provider and must already be configured (its own `rea doctor` reports that).
|
|
16
|
+
- Parser supports Retrofit's declarations only: `@GET @POST @PUT @PATCH @DELETE @HEAD @OPTIONS @HTTP(method=…, path=…, hasBody=…)` for the request line, and `@Path @Query @QueryMap @Field @FieldMap @Body @Part @PartMap @Header @HeaderMap @Url` for parameters, plus `@FormUrlEncoded`/`@Multipart` as body-encoding markers.
|
|
17
|
+
- Minified annotation names (release builds rename `retrofit2.http.*`): resolve them by their retained **structure** (which annotation carries a `String value`, which marks a body, the `@HTTP`-shaped one with `method`/`path`/`hasBody`) and by cross-checking against a path-like string; when a name cannot be resolved with confidence, emit the endpoint with `method: ""` and a note rather than guessing. Never assume alphabetical ordering (the spike found that assumption held for one app and is unconfirmed elsewhere).
|
|
18
|
+
- Every produced `Endpoint` has `verified: false`, `count: 0`, `response_example: None`, and `summary` set to the interface method's name (e.g. `homeTimeline`) so the digest is readable.
|
|
19
|
+
- Base URL comes from the caller (apps often build it per account); if the caller gives none, `Source.base_url` is `""` and `validate` already errors until a plan sets one.
|
|
20
|
+
- No secret from the app (hardcoded keys, tokens) is ever copied into the `Source`: run every string literal that becomes a param example or header value through `importers.har._scrub`.
|
|
21
|
+
- Existing behaviour stays; full suite green; ruff clean; commits on `feat/app-path` without AI attribution.
|
|
22
|
+
|
|
23
|
+
## Review Focus
|
|
24
|
+
|
|
25
|
+
1. A method whose path is built by concatenation (`"users/" + id`) → emitted with the literal part and a note, or skipped with a reason — never a path containing `+` or a Java expression. (Tests.)
|
|
26
|
+
2. A hardcoded credential in an `@Header("Authorization") String` default or a literal → never stored. (Tests.)
|
|
27
|
+
3. An interface with two methods on the same path and different parameters → two endpoints, distinct ids. (Tests.)
|
|
28
|
+
4. A class text that is not a Retrofit interface (no annotations) → empty endpoint list and a clear message, not a crash. (Tests.)
|
|
29
|
+
5. `@HTTP(method="DELETE", hasBody=true)` → method DELETE, body marked, and it stays unverified and unprobed (write). (Tests.)
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
### Task 2: Retrofit parser + `smith_app_android`
|
|
34
|
+
|
|
35
|
+
**Files:**
|
|
36
|
+
- Create: `src/mcpsmith/importers/android.py`, `tests/test_android.py`, `tests/fixtures/android/interface.java` (hand-written, no real app code: a small interface covering every supported annotation, one concatenated path, one minified-name case, one `@HTTP`)
|
|
37
|
+
- Modify: `src/mcpsmith/importers/__init__.py` (not auto-detected — exposed as its own function), `src/mcpsmith/server.py` (tool), `src/mcpsmith/cli.py` (command)
|
|
38
|
+
|
|
39
|
+
**Interfaces:**
|
|
40
|
+
- Produces:
|
|
41
|
+
- `android.parse_interface(java: str, base_url: str = "", title: str = "") -> Source` — endpoints from one decompiled interface.
|
|
42
|
+
- `android.rea_class_source(apk: Path, class_name: str, runner=subprocess.run) -> str` — thin wrapper that asks REA for a class's source; raises `RuntimeError` with REA's install/config hint when REA or its decompiler provider is unavailable.
|
|
43
|
+
- `android.find_api_classes(apk: Path, runner=subprocess.run) -> list[str]` — class names whose code contains a path-like string (`"api/`, `"/v1/`, …), via REA's code search.
|
|
44
|
+
- CLI `mcpsmith app-android <apk> [--class NAME] [--base-url URL] [-w DIR]`: with no `--class`, lists candidate classes and exits 0; with one, writes `source.json` + `digest.md` into the workdir and prints the next step (`mcpsmith probe`).
|
|
45
|
+
- MCP tool `smith_app_android(apk, class_name=None, base_url="", workdir=...)` → candidates, or `{"workdir", "endpoints", "unverified", "digest"}`; `read_only=False` (it runs REA); docstring: for the user's own or authorized app, and the endpoints are unverified until `smith_probe`.
|
|
46
|
+
|
|
47
|
+
- [ ] **Step 1: Write the failing tests** in `tests/test_android.py` against the fixture: every annotation kind maps to the right `Param.location` (`path`/`query`/`body`/`header`) and `required`; `@FormUrlEncoded` + `@Field` → body params; the concatenated path is skipped with a reason or emitted with its literal prefix and a note (assert no `+` in any `path`); a literal `Authorization` value is absent from the Source JSON; two same-path methods give two ids; `@HTTP(method="DELETE", hasBody=true)` → `method == "DELETE"`; a class with no annotations → `endpoints == []`; every endpoint has `verified is False`.
|
|
48
|
+
- [ ] **Step 2: Run** `uv run pytest tests/test_android.py -q` → FAIL.
|
|
49
|
+
- [ ] **Step 3: Implement** the parser, the two REA wrappers (runner injected; never execute anything from the APK's own directory), the CLI command and the MCP tool.
|
|
50
|
+
- [ ] **Step 4: Run** `uv run pytest -q` → PASS; ruff clean.
|
|
51
|
+
- [ ] **Step 5: Commit** `feat(importers): read Android HTTP interfaces into a Source`.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# App Path, Task 3: Electron / JavaScript app extractor (plan 3, part 3)
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans.
|
|
4
|
+
|
|
5
|
+
**Goal:** Read the HTTP calls an Electron (or any bundled JavaScript) app makes out of the app's own shipped code, as unverified endpoints, so a developer can give their agent tools for their own account in their own or an authorized app.
|
|
6
|
+
|
|
7
|
+
**Architecture:** The app ships its JavaScript, and usually its original sources inside `.map` files (`sourcesContent`). mcpsmith reads those files, prefers the original sources when present, and extracts call sites of the app's own request helper (`request(method, path, …)`, `fetch(url, {method})`, `axios.get(url)`) into `Endpoint`s with `verified: false`. Task 1's `probe` verifies the read-only ones. Pure Python, no Node, no new dependency.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** stdlib `json`, `re`, `struct` (asar index), `pathlib`. No parser dependency: extraction is regex over readable source; accuracy comes from Task 1's probe plus the agent's own review of the digest.
|
|
10
|
+
|
|
11
|
+
**Spec:** `docs/superpowers/specs/2026-10-20-mcp-everything-design.md` (G9 Electron bullet; "Spike results": REA's own findings 0/45 on GitHub Desktop, a small AST pass 42/45 with every method correct, and the app's `.map` files carrying the original TypeScript). User scope (2026-10-21): the developer's own or authorized app; nothing that defeats a protection.
|
|
12
|
+
|
|
13
|
+
## Global Constraints
|
|
14
|
+
|
|
15
|
+
- Input is a path the caller gives: an app directory, an `app.asar`, or a single `.js`/`.ts`/`.map` file. mcpsmith never downloads an app and never unpacks an installer; an `.asar` is read (header + JSON index + offsets), not executed.
|
|
16
|
+
- Treat every input file as untrusted data: read it, never import or run it, never resolve `require`/`import` targets, and keep output under the caller's workdir.
|
|
17
|
+
- Prefer original sources: for every `.map` with `sourcesContent`, extract from those texts and record the original file name (e.g. `app/src/lib/api.ts`) in `Endpoint.summary`; fall back to the bundle text only for files with no map.
|
|
18
|
+
- Minified identifiers stay as they are: an extracted param keeps its code name (`e`, `t`) and the digest marks the endpoint `UNVERIFIED`; the plan's author (the agent) renames them. Never invent a friendly name.
|
|
19
|
+
- An endpoint is emitted only when its path is a literal or a template whose non-expression part is a literal (`repos/${owner}/${name}` → `repos/{owner}/{name}`); a path built by an unresolvable expression is skipped with a note. No `Endpoint.path` may contain `$`, `{` without a matching `}`, `+`, backticks or quotes.
|
|
20
|
+
- Method comes from the call site only (the helper's first argument, `{method: …}`, or `axios.<verb>`); when it cannot be read, the endpoint keeps `method: ""` and a note — never guessed.
|
|
21
|
+
- Any string literal that becomes a header value, query value or base URL goes through `importers.har._scrub`; userinfo is stripped from any absolute URL.
|
|
22
|
+
- Every produced `Endpoint` has `verified: false`, `count: 0`, `response_example: None`; `Source.kind` is `"app"`.
|
|
23
|
+
- Caps so a 200 MB app cannot hang the tool: at most 400 files, 8 MB per file read for extraction, 2000 endpoints; whatever is dropped is counted in a note.
|
|
24
|
+
- Existing behaviour stays; full suite green; ruff clean; commits on `feat/app-path` without AI attribution.
|
|
25
|
+
|
|
26
|
+
## Review Focus
|
|
27
|
+
|
|
28
|
+
1. A `.map` whose `sourcesContent` is null or missing → fall back to the bundle, no crash. (Tests.)
|
|
29
|
+
2. A path from a template literal with an expression in the middle → `{param}` placeholders, never `${…}` in the path. (Tests.)
|
|
30
|
+
3. A hardcoded token in a header literal → scrubbed; an absolute URL with `user:pass@` → userinfo stripped. (Tests.)
|
|
31
|
+
4. A malformed `.asar` (truncated, bad index) → clear error naming the file, nothing written. (Tests.)
|
|
32
|
+
5. Two call sites with the same method and path in different files → two endpoints with distinct ids. (Tests.)
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
### Task 3: `importers/electron.py` + `smith_app_js`
|
|
37
|
+
|
|
38
|
+
**Files:**
|
|
39
|
+
- Create: `src/mcpsmith/importers/electron.py`, `tests/test_electron.py`, `tests/fixtures/electron/` (hand-written: `renderer.js` with a request-helper class, a `${}` template path, an unresolvable path, a header token, an absolute URL with userinfo; `renderer.js.map` with `sourcesContent` for one of them; a tiny valid `app.asar` built in the test, not committed as a binary)
|
|
40
|
+
- Modify: `src/mcpsmith/importers/__init__.py` (exported as its own function, not auto-detected), `src/mcpsmith/cli.py`, `src/mcpsmith/server.py`
|
|
41
|
+
|
|
42
|
+
**Interfaces:**
|
|
43
|
+
- Produces:
|
|
44
|
+
- `electron.read_app(path: Path) -> dict[str, str]` — `{logical file name: text}` for `.js`/`.mjs`/`.cjs`/`.ts` plus the `sourcesContent` of their `.map` files, from a directory, an `.asar` or one file; raises `ValueError` naming the file on a malformed `.asar`.
|
|
45
|
+
- `electron.parse_sources(files: dict[str, str], base_url: str = "", title: str = "") -> Source`.
|
|
46
|
+
- CLI `mcpsmith app-js <path> [--base-url URL] [-w DIR]` → writes `source.json` + `digest.md`, prints `N endpoints, all unverified` and the `mcpsmith probe` next step.
|
|
47
|
+
- MCP tool `smith_app_js(path, base_url="", workdir=...)` → `{"workdir", "endpoints", "unverified", "digest"}`; `read_only=False`; docstring: reads the app's own shipped code, for the user's own or an authorized app, endpoints unverified until `smith_probe`.
|
|
48
|
+
|
|
49
|
+
- [ ] **Step 1: Write the failing tests** in `tests/test_electron.py` against the fixtures: helper call sites give `(method, path)` pairs with templates turned into `{param}` placeholders; a `.map` with `sourcesContent` yields `Endpoint.summary` naming the original file; a null `sourcesContent` falls back to the bundle; the unresolvable path is absent and named in a note; no endpoint path contains `$`, backtick, quote or `+`; a header token literal and `user:pass@` are absent from `Source.model_dump_json()`; two same-path call sites in different files get distinct ids; a truncated `.asar` raises `ValueError` naming it; every endpoint has `verified is False` and `Source.kind == "app"`.
|
|
50
|
+
- [ ] **Step 2: Run** `uv run pytest tests/test_electron.py -q` → FAIL.
|
|
51
|
+
- [ ] **Step 3: Implement** the reader (asar index parse: 8-byte pickle header, then JSON index, then offsets), the extractor and the CLI/tool wiring.
|
|
52
|
+
- [ ] **Step 4: Run** `uv run pytest -q` → PASS; ruff clean.
|
|
53
|
+
- [ ] **Step 5: Commit** `feat(importers): read a JavaScript app's own HTTP calls into a Source`.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# App Path, Task 4: desktop apps via a capture proxy (plan 3, part 4)
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans.
|
|
4
|
+
|
|
5
|
+
**Goal:** Let a developer turn their own (or an authorized) desktop program's API into agent tools by recording the program's own traffic through a local proxy they run, then feeding it into the existing pipeline.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Almost nothing new. mitmproxy (free, OSS, not bundled) writes a HAR with its built-in `hardump` option; mcpsmith's HAR importer already reads HAR. So this task is a guided command that prints the exact steps for the developer's platform, then imports the resulting HAR with the existing `inspect` path, plus the skill text that keeps an agent from doing anything the developer did not ask for.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Existing `importers.load` (HAR), `cli`, `server`. No new dependency; mitmproxy is the developer's own tool, invoked by the developer.
|
|
10
|
+
|
|
11
|
+
**Spec:** `docs/superpowers/specs/2026-10-20-mcp-everything-design.md` (G9 native bullet; decision 13: native desktop apps use proxy capture because static extraction recovered ~4% of REST paths from a Go binary). User scope (2026-10-21): the developer's own or authorized software; nothing that defeats a protection.
|
|
12
|
+
|
|
13
|
+
## Global Constraints
|
|
14
|
+
|
|
15
|
+
- mcpsmith never installs, starts or configures a proxy, never installs a CA certificate, and never changes system or OS proxy settings. It prints the commands and the developer runs them. The guidance must say plainly that intercepting a program's traffic requires that the developer owns the program or is authorized to analyze it, and that certificate-pinned programs are out of scope (no bypass is offered or described).
|
|
16
|
+
- `hardump` is mitmproxy's documented option ("Save a HAR file with all flows on exit"), used as `mitmdump --set hardump=<file>` and available in mitmproxy/mitmdump/mitmweb; the printed instructions use it, plus `--set save_stream_filter=` guidance for narrowing hosts.
|
|
17
|
+
- The import path is the existing HAR importer: no second parser. `app-desktop` with a HAR delegates to the same `inspect_source` the CLI already uses, so scrubbing of secrets, cookies and personal data applies unchanged.
|
|
18
|
+
- Endpoints from a capture keep the HAR importer's existing semantics (`verified: True`, real response examples) — unlike the static importers, a capture is evidence of a real answer.
|
|
19
|
+
- Nothing is executed from the capture's directory; the HAR is read as untrusted data.
|
|
20
|
+
- Existing behaviour stays; full suite green; ruff clean; commits on `feat/app-path` without AI attribution.
|
|
21
|
+
|
|
22
|
+
## Review Focus
|
|
23
|
+
|
|
24
|
+
1. The printed steps must be correct per platform (Windows/macOS/Linux) for proxy address, the certificate step and the dump flag — a wrong flag wastes the developer's session. (Test asserts the exact `--set hardump=` form and that no step tells the user to disable certificate validation.)
|
|
25
|
+
2. A HAR that contains only the proxy's own CONNECT entries or no JSON → clear "nothing to import" message, not an empty `Source` written silently. (Test.)
|
|
26
|
+
3. A capture of a program that is not the target (other apps' traffic on the same proxy) → `--host` filtering is offered and applied. (Test.)
|
|
27
|
+
4. The guidance must not describe defeating pinning or any protection; it must say such programs are out of scope. (Test greps the skill text for the sentence.)
|
|
28
|
+
5. Secrets in a desktop capture (an app token in a header) are scrubbed by the existing importer before anything is written. (Test.)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
### Task 4: `app-desktop` command + skill
|
|
33
|
+
|
|
34
|
+
**Files:**
|
|
35
|
+
- Modify: `src/mcpsmith/cli.py`, `src/mcpsmith/server.py`, `skills/extract-api/SKILL.md` (a "desktop app" section), `README.md` (one line in the app-path list), `docs/design.md`
|
|
36
|
+
- Create: `tests/test_desktop.py`
|
|
37
|
+
|
|
38
|
+
**Interfaces:**
|
|
39
|
+
- Produces:
|
|
40
|
+
- CLI `mcpsmith app-desktop [HAR] [--port 8080] [--host H]... [-w DIR]`: with no HAR, prints the numbered steps (run `mitmdump --set hardump=<workdir>/capture.har -p <port>`, point the program at `http://127.0.0.1:<port>` or the OS proxy for that program only, install mitmproxy's CA for the program's trust store as mitmproxy's own docs describe, use the features you want as tools, stop mitmdump) and exits 0; with a HAR, imports it through the existing `inspect_source` and prints the endpoint count and the `mcpsmith draft` next step. Refuses to overwrite an existing `source.json` without `--force`.
|
|
41
|
+
- MCP tool `smith_app_desktop(har=None, hosts=None, workdir=..., port=8080)` → either `{"steps": [...]}` or `{"workdir", "endpoints", "digest"}`; `read_only=False`; docstring states the developer runs the proxy themselves and that this is for their own or authorized software.
|
|
42
|
+
- Consumes: `cli.inspect_source`, `importers.load`.
|
|
43
|
+
|
|
44
|
+
- [ ] **Step 1: Write the failing tests** in `tests/test_desktop.py`: the steps text contains `--set hardump=` and the workdir path, names the port, and contains no instruction to disable TLS verification (assert the absence of "verify" / "--ssl-insecure" / "pinning bypass"); a hand-written HAR with one JSON API call and one CONNECT entry imports to one endpoint; a HAR with no JSON calls prints "nothing to import" and writes no `source.json`; `--host` keeps only the named host; an `Authorization` header value in the HAR is absent from the written `source.json`; the skill text contains the out-of-scope sentence about certificate-pinned programs.
|
|
45
|
+
- [ ] **Step 2: Run** `uv run pytest tests/test_desktop.py -q` → FAIL.
|
|
46
|
+
- [ ] **Step 3: Implement** the command, the tool, and the skill/README/design wording.
|
|
47
|
+
- [ ] **Step 4: Run** `uv run pytest -q` → PASS; ruff clean; `claude plugin validate .` passes.
|
|
48
|
+
- [ ] **Step 5: Commit** `feat: import a desktop app's own traffic from a developer-run proxy`.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Setup Bundle Implementation Plan (plan 2 of 3)
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** `mcpsmith setup` registers the mcpsmith hub, Chrome DevTools MCP (attached to the shared Chrome) and REA with Claude Code, Codex, Cursor and Gemini CLI, and installs mcpsmith's skills — one command, previewed, backed up, reversible.
|
|
6
|
+
|
|
7
|
+
**Architecture:** A per-agent target module edits each agent's user-level MCP config (JSON for Claude Code/Cursor/Gemini, TOML via `tomlkit` for Codex) with read → merge → backup → write → verify, never touching unrelated entries. Skills ship inside the wheel and are copied to `~/.claude/skills` (Claude Code) and `~/.agents/skills` (Codex, Cursor, Gemini). REA is registered by delegating to REA's own non-interactive setup.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Python ≥3.10 stdlib (json, shutil, importlib.resources, subprocess), `tomlkit` (new dependency, comment-preserving TOML). Tests: pytest with a fake HOME.
|
|
10
|
+
|
|
11
|
+
**Spec:** `docs/superpowers/specs/2026-10-20-mcp-everything-design.md` (G6; decisions 8, 9). Research facts (2026-10-21) are recorded in Global Constraints below.
|
|
12
|
+
|
|
13
|
+
## Global Constraints
|
|
14
|
+
|
|
15
|
+
- Config locations (user level): Claude Code `~/.claude.json` (`$CLAUDE_CONFIG_DIR/.claude.json` if set), key `mcpServers.<name>` = `{"type":"stdio","command","args","env"}`; Codex `~/.codex/config.toml` (`$CODEX_HOME` if set), table `[mcp_servers.<name>]` with `command`, `args`, `startup_timeout_sec = 30`, sub-table `env`; Cursor `~/.cursor/mcp.json`, `mcpServers.<name>` = `{"type":"stdio","command","args","env"}`; Gemini `~/.gemini/settings.json` (`$GEMINI_CLI_HOME/.gemini/settings.json` if set), `mcpServers.<name>` = `{"command","args","env","timeout":30000}`.
|
|
16
|
+
- Skills: Claude Code `~/.claude/skills/<name>/`; Codex, Cursor, Gemini `~/.agents/skills/<name>/`. Skill folder name equals its frontmatter `name`.
|
|
17
|
+
- Server entries (names exactly): `mcpsmith` → `uvx mcpsmith@<package version> mcp`; `chrome-devtools` → `npx -y chrome-devtools-mcp@1.11.0 --browser-url=http://127.0.0.1:9222`; REA → delegated: `npx -y rea-agents@6.7.0 setup --client <id> --yes` with ids `claude_code`, `codex`, `cursor`, `gemini_cli`.
|
|
18
|
+
- On Windows, `npx` commands are written as `command: "cmd"`, `args: ["/d","/c","npx",...]`; `uvx` stays as is.
|
|
19
|
+
- `env` written for the `mcpsmith` entry: `HTTPS_PROXY`/`HTTP_PROXY`/`NO_PROXY` from setup's environment when set, with userinfo (`user:pass@`) stripped (reuse `browser.clean_proxy`); never write any other secret.
|
|
20
|
+
- Never modify or remove entries mcpsmith did not create; preserve TOML comments and unrelated tables; JSON written with 2-space indent and UTF-8, original key order kept.
|
|
21
|
+
- Before the first write to a file: copy it to `<file>.mcpsmith.bak` (do not overwrite an existing backup). After writing, re-read and verify the entry; on failure restore the backup and report.
|
|
22
|
+
- Node.js missing (`npx` not on PATH) → skip `chrome-devtools` and REA with one clear line each; still do the rest.
|
|
23
|
+
- Default asks for confirmation after printing the plan; `--yes` skips it; `--dry-run` prints the plan and writes nothing; non-interactive stdin without `--yes` → error, exit 2.
|
|
24
|
+
- New dependency only: `tomlkit>=0.12`. Existing behaviour stays; full suite green; ruff clean; commits on branch `feat/setup` without AI attribution.
|
|
25
|
+
|
|
26
|
+
## Review Focus
|
|
27
|
+
|
|
28
|
+
1. Existing user configs with unrelated servers, comments (TOML) and other top-level keys → untouched after setup and after `--remove`. (Task 2 tests.)
|
|
29
|
+
2. Running setup twice → idempotent: no duplicate entries, no second backup overwrite. (Task 3 test.)
|
|
30
|
+
3. A config file that is malformed JSON/TOML → that agent is skipped with a clear message; nothing written to it. (Task 2 test.)
|
|
31
|
+
4. Proxy env with credentials → credentials never written. (Task 2 test.)
|
|
32
|
+
5. Agent not installed → not touched unless named with `--agents`. (Task 3 test.)
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
### Task 1: Ship skills in the package
|
|
37
|
+
|
|
38
|
+
**Files:** Modify `pyproject.toml`; Create `src/mcpsmith/skills_path.py`; Test `tests/test_setup.py`
|
|
39
|
+
|
|
40
|
+
**Interfaces:**
|
|
41
|
+
- Produces: `skills_path.skills_dir() -> Path` — the packaged skills directory (`importlib.resources.files("mcpsmith") / "skills"` when present, else the repo's `skills/` for editable installs); contains `build-mcp-server`, `design-mcp-tools`, `extract-api`.
|
|
42
|
+
|
|
43
|
+
- [ ] **Step 1:** Test `test_skills_dir_has_all_skills`: the three folders exist, each with a `SKILL.md` whose frontmatter `name` equals the folder name. Test `test_wheel_contains_skills`: `uv build --wheel` into `tmp_path`, the wheel's namelist contains `mcpsmith/skills/extract-api/SKILL.md` (mark the test `slow` only if it exceeds 20 s).
|
|
44
|
+
- [ ] **Step 2:** Run → FAIL. **Step 3:** Add `[tool.hatch.build.targets.wheel.force-include] "skills" = "mcpsmith/skills"`; implement `skills_dir`. **Step 4:** Run full suite → PASS. **Step 5:** Commit `build: ship skills inside the package`.
|
|
45
|
+
|
|
46
|
+
### Task 2: Agent targets
|
|
47
|
+
|
|
48
|
+
**Files:** Create `src/mcpsmith/setup/__init__.py`, `src/mcpsmith/setup/agents.py`; Modify `pyproject.toml` (`tomlkit`); Test `tests/test_setup.py`
|
|
49
|
+
|
|
50
|
+
**Interfaces:**
|
|
51
|
+
- Produces:
|
|
52
|
+
- `SERVERS: dict[str, list[str]]` — base commands per server name (from Global Constraints), and `server_entry(name: str, windows: bool, env: dict[str,str]) -> dict` (command/args, Windows `cmd /d /c` wrapping for npx).
|
|
53
|
+
- `class Agent` (one per agent: `claude`, `codex`, `cursor`, `gemini`) with `id`, `rea_client`, `config_path(home: Path, environ) -> Path`, `skills_root(home) -> Path`, `detected(home, environ, which) -> bool` (config dir exists or CLI on PATH), `add(doc, name, entry) -> doc`, `remove(doc, name) -> doc`, `has(doc, name) -> bool`, `load(path) -> doc` (raises `ValueError` on malformed), `dump(doc) -> str`.
|
|
54
|
+
- `AGENTS: dict[str, Agent]`.
|
|
55
|
+
- [ ] **Step 1:** Tests on in-memory docs: each agent adds `mcpsmith` and `chrome-devtools` with exact shapes from Global Constraints; unrelated servers/keys preserved; Codex TOML comment and an unrelated `[mcp_servers.other]` survive add+remove; `remove` deletes only our names; malformed JSON/TOML → `ValueError`; `server_entry("chrome-devtools", windows=True, …)` → `command == "cmd"`, args start `["/d","/c","npx","-y","chrome-devtools-mcp@1.11.0"]`; mcpsmith env from `HTTPS_PROXY=http://u:p@h:1` → `"http://h:1"`; `CLAUDE_CONFIG_DIR`/`CODEX_HOME`/`GEMINI_CLI_HOME` honored.
|
|
56
|
+
- [ ] **Step 2:** Run → FAIL. **Step 3:** Implement. **Step 4:** Full suite → PASS. **Step 5:** Commit `feat(setup): agent config targets`.
|
|
57
|
+
|
|
58
|
+
### Task 3: `mcpsmith setup` command
|
|
59
|
+
|
|
60
|
+
**Files:** Create `src/mcpsmith/setup/run.py`; Modify `src/mcpsmith/cli.py`; Test `tests/test_setup.py`
|
|
61
|
+
|
|
62
|
+
**Interfaces:**
|
|
63
|
+
- Consumes: `AGENTS`, `server_entry`, `skills_dir`.
|
|
64
|
+
- Produces: CLI `mcpsmith setup [--agents claude,codex,cursor,gemini] [--no-rea] [--no-skills] [--dry-run] [--yes] [--remove]`; `plan(home, environ, agents, rea, skills, remove) -> list[Change]` (`Change`: agent, kind `server|skill|rea`, target path or command, action `add|update|remove|skip`, reason); `apply(changes, runner=subprocess.run) -> list[str]` (result lines).
|
|
65
|
+
- Behaviour: detected agents by default (or `--agents`); prints the plan (paths, entries, skill folders, REA command), asks `Apply these changes? [y/N]` unless `--yes`/`--dry-run`; per file backup→write→verify (restore on verify failure); skills copied with `shutil.copytree(dirs_exist_ok=True)` (only mcpsmith's three folders; `--remove` deletes only those); REA via `runner([npx..., "setup", "--client", id, "--yes"])` (Windows-wrapped), `--remove` → `rea-agents@6.7.0 uninstall --client id --yes`; final line tells the user to restart their agents and to run `mcpsmith browser` before using Chrome DevTools.
|
|
66
|
+
- [ ] **Step 1:** Tests with fake HOME and stub `which`/runner: `--dry-run` writes nothing and lists every change; a real run writes entries + skills + backups and calls REA once per agent with the right client id; second run → no changes reported as `add`, backups unchanged; `--remove` restores a config equal to the original except formatting (JSON) / byte-identical comments (TOML), skills gone; undetected agent untouched; malformed config → that agent skipped, others done; no `npx` → chrome-devtools and REA skipped with messages; stdin not a TTY and no `--yes` → exit 2.
|
|
67
|
+
- [ ] **Step 2:** Run → FAIL. **Step 3:** Implement. **Step 4:** Full suite → PASS. **Step 5:** Commit `feat: mcpsmith setup registers the bundle with coding agents`.
|
|
68
|
+
|
|
69
|
+
### Task 4: Plugin, docs, release 0.3.0
|
|
70
|
+
|
|
71
|
+
**Files:** Modify `.claude-plugin/plugin.json` (add `chrome-devtools` and `rea` servers, pinned, same args as setup on non-Windows), `README.md` (Install section: `uvx mcpsmith setup`; plugin users don't also run setup), `AGENTS.md`, `skills/build-mcp-server/SKILL.md` (setup line), version 0.3.0 in all version spots; `uv lock`.
|
|
72
|
+
|
|
73
|
+
- [ ] **Step 1:** `claude plugin validate .` → "Validation passed"; `uv run pytest -q`; ruff.
|
|
74
|
+
- [ ] **Step 2: Acceptance** — run `mcpsmith setup --dry-run` on the real machine and show the plan; run the real setup with `HOME`/`USERPROFILE` pointed at a scratch dir seeded with copies of the four real config files (no real config touched) and check: entries added, comments/unrelated servers preserved, `--remove` reverts. Real-machine install only with the user's yes.
|
|
75
|
+
- [ ] **Step 3:** Commit `chore: release 0.3.0`; PR `feat/setup` → main; CI green; merge; tag `v0.3.0`; publish workflow green.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mcpsmith"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Turn an OpenAPI file or a recorded website session into a curated, tested MCP server, with an LLM designing the tools."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -27,6 +27,7 @@ dependencies = [
|
|
|
27
27
|
"websockets>=12",
|
|
28
28
|
"keyring>=25",
|
|
29
29
|
"tzdata>=2024.1; sys_platform == 'win32'",
|
|
30
|
+
"tomlkit>=0.12",
|
|
30
31
|
]
|
|
31
32
|
|
|
32
33
|
[project.optional-dependencies]
|
|
@@ -49,6 +50,9 @@ dev = ["pytest>=8", "anyio>=4", "ruff>=0.6"]
|
|
|
49
50
|
requires = ["hatchling"]
|
|
50
51
|
build-backend = "hatchling.build"
|
|
51
52
|
|
|
53
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
54
|
+
"skills" = "mcpsmith/skills"
|
|
55
|
+
|
|
52
56
|
[tool.pytest.ini_options]
|
|
53
57
|
testpaths = ["tests"]
|
|
54
58
|
addopts = "-m 'not live'"
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
10
|
"websiteUrl": "https://github.com/sepehr071/mcpsmith",
|
|
11
|
-
"version": "0.
|
|
11
|
+
"version": "0.4.0",
|
|
12
12
|
"packages": [
|
|
13
13
|
{
|
|
14
14
|
"registryType": "pypi",
|
|
15
15
|
"registryBaseUrl": "https://pypi.org",
|
|
16
16
|
"identifier": "mcpsmith",
|
|
17
|
-
"version": "0.
|
|
17
|
+
"version": "0.4.0",
|
|
18
18
|
"runtimeHint": "uvx",
|
|
19
19
|
"transport": {
|
|
20
20
|
"type": "stdio"
|
|
@@ -5,6 +5,8 @@ description: Build a curated, tested MCP server (agent tools) with mcpsmith. Use
|
|
|
5
5
|
|
|
6
6
|
# Build an MCP server with mcpsmith
|
|
7
7
|
|
|
8
|
+
Not set up yet? `uvx mcpsmith setup` registers mcpsmith, Chrome DevTools MCP and REA with the user's agents.
|
|
9
|
+
|
|
8
10
|
mcpsmith turns an OpenAPI file or a recorded website session (HAR) into a Python MCP server project with tests, CI, a publish workflow and a Claude Code plugin. You design the tools; mcpsmith generates and checks the code.
|
|
9
11
|
|
|
10
12
|
Run commands with `uvx mcpsmith <cmd>` (or `mcpsmith <cmd>` if installed). No shell? Use the mcpsmith MCP tools instead: `smith_guide`, `smith_inspect`, `smith_draft`, `smith_validate`, `smith_generate`, `smith_check`, and the hub tools `smith_record_start`, `smith_record_stop`, `smith_connect`, `smith_disconnect`, `smith_connections`, `smith_tools`, `smith_call` (paths absolute).
|
|
@@ -65,6 +65,22 @@ Call each connected tool once with a real input. Open the same view in Chrome (`
|
|
|
65
65
|
|
|
66
66
|
If a call says the session expired, ask the user to log in again in the shared Chrome window, then retry.
|
|
67
67
|
|
|
68
|
+
## Desktop program (own or authorized)
|
|
69
|
+
|
|
70
|
+
For a desktop program's API, the developer records the program's own traffic with their own proxy; mcpsmith imports the HAR. Use this only for software the user owns or is authorized to analyze; certificate-pinned programs are out of scope, and you offer no way around that.
|
|
71
|
+
|
|
72
|
+
1. Call `smith_app_desktop(workdir=<dir>)` and show the user the steps. The user installs and runs mitmproxy (`mitmdump --set hardump=<file>`), points that one program at it (the program must trust mitmproxy's CA for HTTPS, per mitmproxy's certificate docs), uses the features they want as tools, and stops mitmdump with Ctrl-C (mitmweb: quit from the UI). Killing the process writes no HAR. Safer: record with `mitmdump -p <port> -w flows.mitm`, then convert with `mitmdump -nr flows.mitm --set hardump=<file>`. You never install, start or configure the proxy, install a certificate or change system proxy settings yourself.
|
|
73
|
+
2. Call `smith_app_desktop(har=<file>, hosts=[<the program's API host>], workdir=<dir>)`. Other programs' traffic on the same proxy is dropped by `hosts`. If it says nothing to import, the capture holds no JSON API calls: ask the user to record again.
|
|
74
|
+
3. Continue at step 4 (`smith_draft`). The endpoints came from real responses, so they are verified. Delete the HAR afterwards (step 6).
|
|
75
|
+
|
|
76
|
+
## Android app (own or authorized)
|
|
77
|
+
|
|
78
|
+
Only for an app the user owns or is authorized to analyze; nothing is run or bypassed. Call `smith_app_android(apk=<abs path>, base_url=<API base URL>, workdir=<dir>)` (needs REA's JADX jar; `REA_JADX_MCP_JAR`). Without `class_name` it lists candidate Retrofit classes: pick one and call again. Endpoints are read from code, so they are unverified until `smith_probe(workdir)` calls the GET ones with the user's credentials (`writes=true` only after the user confirms). A method or parameter kind that could not be read is listed in `source.json` `description`: an endpoint with no method fails `smith_validate` until you set it in the plan. Then continue at step 4.
|
|
79
|
+
|
|
80
|
+
## Electron / JavaScript app (own or authorized)
|
|
81
|
+
|
|
82
|
+
Same rules. Call `smith_app_js(path=<app dir, app.asar or one file>, base_url=<API base URL>, workdir=<dir>)`. Calls whose path is built at run time are skipped and noted, never guessed. Endpoints are unverified until `smith_probe(workdir)`; set `base_url` first or probe refuses. Then continue at step 4.
|
|
83
|
+
|
|
68
84
|
## 6. Privacy and publishing
|
|
69
85
|
|
|
70
86
|
- Tool results go to the model like any conversation text. Tell the user which personal fields the tools return and trim `fields` they do not need.
|