legwork-mcp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- legwork_mcp-0.1.0/.github/workflows/ci.yml +16 -0
- legwork_mcp-0.1.0/.github/workflows/publish.yml +49 -0
- legwork_mcp-0.1.0/.gitignore +7 -0
- legwork_mcp-0.1.0/CLAUDE.md +20 -0
- legwork_mcp-0.1.0/LICENSE +21 -0
- legwork_mcp-0.1.0/PKG-INFO +189 -0
- legwork_mcp-0.1.0/README.md +170 -0
- legwork_mcp-0.1.0/docs/designs/legwork-jit-ai-runtime.md +1079 -0
- legwork_mcp-0.1.0/docs/designs/legwork-live-runs-2026-09-25.md +89 -0
- legwork_mcp-0.1.0/docs/designs/legwork-trending-trial-2026-09-26.md +83 -0
- legwork_mcp-0.1.0/docs/designs/legwork-validation-spike-2026-09-22.md +147 -0
- legwork_mcp-0.1.0/legwork/__init__.py +1 -0
- legwork_mcp-0.1.0/legwork/cache_writer.py +357 -0
- legwork_mcp-0.1.0/legwork/cli.py +251 -0
- legwork_mcp-0.1.0/legwork/codegen.py +238 -0
- legwork_mcp-0.1.0/legwork/llm_client.py +160 -0
- legwork_mcp-0.1.0/legwork/local_store.py +88 -0
- legwork_mcp-0.1.0/legwork/obfuscation_scanner.py +255 -0
- legwork_mcp-0.1.0/legwork/readme_parser.py +272 -0
- legwork_mcp-0.1.0/legwork/repo_fetcher.py +188 -0
- legwork_mcp-0.1.0/legwork/retry_loop.py +261 -0
- legwork_mcp-0.1.0/legwork/sandbox_runner.py +347 -0
- legwork_mcp-0.1.0/pyproject.toml +37 -0
- legwork_mcp-0.1.0/tests/__init__.py +0 -0
- legwork_mcp-0.1.0/tests/fixtures/README.md +25 -0
- legwork_mcp-0.1.0/tests/fixtures/ai_data_extractor_payload.py +45 -0
- legwork_mcp-0.1.0/tests/fixtures/clean_plugin_loader.py +16 -0
- legwork_mcp-0.1.0/tests/mcp_stdio_client.py +77 -0
- legwork_mcp-0.1.0/tests/test_cache_writer.py +350 -0
- legwork_mcp-0.1.0/tests/test_cli.py +180 -0
- legwork_mcp-0.1.0/tests/test_codegen.py +211 -0
- legwork_mcp-0.1.0/tests/test_llm_client.py +177 -0
- legwork_mcp-0.1.0/tests/test_local_store.py +47 -0
- legwork_mcp-0.1.0/tests/test_obfuscation_scanner.py +201 -0
- legwork_mcp-0.1.0/tests/test_pipeline_integration.py +56 -0
- legwork_mcp-0.1.0/tests/test_readme_parser.py +206 -0
- legwork_mcp-0.1.0/tests/test_repo_fetcher.py +160 -0
- legwork_mcp-0.1.0/tests/test_retry_loop.py +298 -0
- legwork_mcp-0.1.0/tests/test_retry_loop_integration.py +86 -0
- legwork_mcp-0.1.0/tests/test_sandbox_runner.py +297 -0
- legwork_mcp-0.1.0/tests/test_serve_integration.py +84 -0
- legwork_mcp-0.1.0/uv.lock +156 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
# The sandbox is macOS sandbox-exec, so the real tests need macOS.
|
|
11
|
+
runs-on: macos-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v6
|
|
15
|
+
- run: uv sync
|
|
16
|
+
- run: uv run pytest -q
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
# Push a tag like v0.1.0 to publish that version to PyPI. Uses PyPI trusted
|
|
4
|
+
# publishing: no API token exists anywhere. The PyPI project must list this
|
|
5
|
+
# repo, this workflow file and the `pypi` environment as a trusted publisher.
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
tags: ["v*"]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
runs-on: macos-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: astral-sh/setup-uv@v6
|
|
16
|
+
- run: uv sync
|
|
17
|
+
- run: uv run pytest -q
|
|
18
|
+
|
|
19
|
+
build:
|
|
20
|
+
needs: test
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- uses: astral-sh/setup-uv@v6
|
|
25
|
+
- name: Tag must match the package version
|
|
26
|
+
run: |
|
|
27
|
+
version=$(uv version --short)
|
|
28
|
+
if [ "v$version" != "$GITHUB_REF_NAME" ]; then
|
|
29
|
+
echo "Tag $GITHUB_REF_NAME doesn't match pyproject version $version" >&2
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
- run: uv build
|
|
33
|
+
- uses: actions/upload-artifact@v4
|
|
34
|
+
with:
|
|
35
|
+
name: dist
|
|
36
|
+
path: dist/
|
|
37
|
+
|
|
38
|
+
pypi:
|
|
39
|
+
needs: build
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
environment: pypi
|
|
42
|
+
permissions:
|
|
43
|
+
id-token: write
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/download-artifact@v4
|
|
46
|
+
with:
|
|
47
|
+
name: dist
|
|
48
|
+
path: dist/
|
|
49
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# AwesomeAI
|
|
2
|
+
|
|
3
|
+
## Skill routing
|
|
4
|
+
|
|
5
|
+
When the user's request matches an available skill, invoke it via the Skill tool. When in doubt, invoke the skill.
|
|
6
|
+
|
|
7
|
+
Key routing rules:
|
|
8
|
+
- Product ideas/brainstorming → invoke /office-hours
|
|
9
|
+
- Strategy/scope → invoke /plan-ceo-review
|
|
10
|
+
- Architecture → invoke /plan-eng-review
|
|
11
|
+
- Design system/plan review → invoke /design-consultation or /plan-design-review
|
|
12
|
+
- Full review pipeline → invoke /autoplan
|
|
13
|
+
- Bugs/errors → invoke /investigate
|
|
14
|
+
- QA/testing site behavior → invoke /qa or /qa-only
|
|
15
|
+
- Code review/diff check → invoke /review
|
|
16
|
+
- Visual polish → invoke /design-review
|
|
17
|
+
- Ship/deploy/PR → invoke /ship or /land-and-deploy
|
|
18
|
+
- Save progress → invoke /context-save
|
|
19
|
+
- Resume context → invoke /context-restore
|
|
20
|
+
- Author a backlog-ready spec/issue → invoke /spec
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kumar Ganduri and Legwork contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: legwork-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Point it at a GitHub repo, get a working MCP tool back.
|
|
5
|
+
Project-URL: Homepage, https://github.com/kumarganduri/legwork
|
|
6
|
+
Project-URL: Repository, https://github.com/kumarganduri/legwork
|
|
7
|
+
Project-URL: Issues, https://github.com/kumarganduri/legwork/issues
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: agents,codegen,llm,mcp,model-context-protocol,sandbox
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: MacOS
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# Legwork
|
|
21
|
+
|
|
22
|
+
**Point it at a GitHub repo, get a working MCP tool back.**
|
|
23
|
+
|
|
24
|
+
Legwork reads a repo's README, has your LLM write an
|
|
25
|
+
[MCP](https://modelcontextprotocol.io) wrapper for it, installs it in a
|
|
26
|
+
sandbox, and proves it runs before handing it to Claude Code, Cursor or any
|
|
27
|
+
other MCP client. If a repo can't be wrapped, it says why instead of
|
|
28
|
+
producing something broken.
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
uvx legwork-mcp owner/repo
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Proof, not a pitch
|
|
35
|
+
|
|
36
|
+
On 2026-09-26 we ran Legwork against **this week's 10 most-starred new AI
|
|
37
|
+
repos on GitHub**, taken exactly as search ranked them, with nothing skipped
|
|
38
|
+
for being hard:
|
|
39
|
+
|
|
40
|
+
| Outcome | Repos |
|
|
41
|
+
|---|---|
|
|
42
|
+
| ✅ Built a working MCP server | **3** — an npm CLI, a Python library, a Go binary |
|
|
43
|
+
| ↩️ Refused, with the right reason | **5** — two Android/macOS apps, a desktop app, a repo with no usage docs, a tool that needs its own API keys |
|
|
44
|
+
| 🛑 Blocked as malware | **2** |
|
|
45
|
+
|
|
46
|
+
Two of the week's top-10 "AI tools", about 700 stars each and three days
|
|
47
|
+
old, carried the same byte-identical obfuscated dropper under different
|
|
48
|
+
file names. Legwork's pre-install scan refused both in about a second.
|
|
49
|
+
Nothing from them was installed or run. Stars are not a trust signal.
|
|
50
|
+
|
|
51
|
+
Full write-up, including what failed along the way and what we fixed:
|
|
52
|
+
[docs/designs/legwork-trending-trial-2026-09-26.md](docs/designs/legwork-trending-trial-2026-09-26.md).
|
|
53
|
+
|
|
54
|
+
## Quick start
|
|
55
|
+
|
|
56
|
+
You need **macOS**, [uv](https://docs.astral.sh/uv/), and any
|
|
57
|
+
OpenAI-compatible chat-completions endpoint. Legwork itself is free; the
|
|
58
|
+
only cost is your own model usage, which is 1–3 calls per build.
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
export LEGWORK_LLM_ENDPOINT=https://api.openai.com/v1
|
|
62
|
+
export LEGWORK_LLM_API_KEY=... # your own key; never a CLI flag
|
|
63
|
+
export LEGWORK_LLM_MODEL=gpt-5
|
|
64
|
+
|
|
65
|
+
uvx legwork-mcp 2akouwu/reverify
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
A build takes 1–3 minutes and ends with the line to connect it:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
Built an MCP wrapper for 2akouwu/reverify on attempt 2 of 3.
|
|
72
|
+
What it wraps: Verify claims about binaries using Reverify's deterministic tools (wraps `reverify verify --json`)
|
|
73
|
+
|
|
74
|
+
Connect it to Claude Code:
|
|
75
|
+
claude mcp add reverify -- ~/.local/bin/uvx legwork-mcp serve 2akouwu/reverify
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
It also prints an `mcpServers` block for Claude Desktop, Cursor and other
|
|
79
|
+
clients. For a permanent `legwork` command: `uv tool install legwork-mcp`.
|
|
80
|
+
|
|
81
|
+
## How it works
|
|
82
|
+
|
|
83
|
+
1. **Fetch.** Checks the repo is public and reachable, then shallow-clones it.
|
|
84
|
+
2. **Scan.** Statically scans every Python file for obfuscated payloads
|
|
85
|
+
(XOR-decoded byte arrays, computed imports and attribute lookups,
|
|
86
|
+
unreadable identifiers). A hit stops everything before install.
|
|
87
|
+
3. **Read.** The README, plus setup docs it links to (`install.md`,
|
|
88
|
+
`docs/getting-started.md`, …), capped at 50KB with install and usage
|
|
89
|
+
sections kept first.
|
|
90
|
+
4. **Write.** Your model writes the install command and a Python MCP
|
|
91
|
+
wrapper with a self-test, or refuses with a specific reason: no
|
|
92
|
+
programmatic entrypoint, needs hardware, needs its own credentials,
|
|
93
|
+
needs a toolchain the sandbox doesn't have, and so on.
|
|
94
|
+
5. **Install**, sandboxed, network on.
|
|
95
|
+
6. **Self-test**, sandboxed, network off. A failure goes back to the model
|
|
96
|
+
and it tries again, up to 3 attempts and 10 minutes.
|
|
97
|
+
7. **Serve.** `legwork serve owner/repo` runs the wrapper as an MCP server
|
|
98
|
+
over stdio, sandboxed, network off.
|
|
99
|
+
|
|
100
|
+
## What "built" means
|
|
101
|
+
|
|
102
|
+
The self-test calls the **simplest** documented command and checks the
|
|
103
|
+
shape of its result. So "built" means the wrapper starts and its simplest
|
|
104
|
+
tool works. It does not mean every tool works. In the trial:
|
|
105
|
+
|
|
106
|
+
- golive-skill's wrapper exposes its read-only commands, not its deploy
|
|
107
|
+
flow, which needs accounts.
|
|
108
|
+
- AnyJev's decision tool needs a running vLLM server, which the self-test
|
|
109
|
+
didn't have.
|
|
110
|
+
|
|
111
|
+
Tools that must reach the network at run time won't work under `serve`,
|
|
112
|
+
because the network is off.
|
|
113
|
+
|
|
114
|
+
## Risks, stated plainly
|
|
115
|
+
|
|
116
|
+
Legwork runs code you didn't write: the target repo's install steps and a
|
|
117
|
+
wrapper an LLM wrote from a README a stranger wrote. What contains it:
|
|
118
|
+
|
|
119
|
+
- **Install time is the biggest risk.** Package installs and `install.sh`
|
|
120
|
+
scripts run with network on, because they have to. The sandbox limits
|
|
121
|
+
them to their own working folder: no access to your home directory (SSH
|
|
122
|
+
keys, cloud credentials, dotfiles), no writes outside the folder, and an
|
|
123
|
+
environment with none of your variables (including your model key). A
|
|
124
|
+
malicious package can still misbehave inside that folder and reach the
|
|
125
|
+
network during install.
|
|
126
|
+
- **The scan covers the repo's source, not what installers download.**
|
|
127
|
+
magpie, for example, installs with the vendor's `curl … | sh`. For
|
|
128
|
+
downloaded code, the sandbox is the protection.
|
|
129
|
+
- **Prompt injection is not mitigated.** A README can steer the model into
|
|
130
|
+
writing a wrapper that does something other than what you asked for. The
|
|
131
|
+
wrapper still runs sandboxed with network off, which bounds the damage;
|
|
132
|
+
it doesn't prevent a wrong or misleading tool.
|
|
133
|
+
- **The scanner is heuristic.** It catches the obfuscation patterns seen in
|
|
134
|
+
real payloads so far, and a determined author can get past it.
|
|
135
|
+
|
|
136
|
+
The sandbox is macOS `sandbox-exec`. If it isn't available, Legwork refuses
|
|
137
|
+
to run. It never falls back to running unsandboxed.
|
|
138
|
+
|
|
139
|
+
## Commands
|
|
140
|
+
|
|
141
|
+
| Command | What it does |
|
|
142
|
+
|---|---|
|
|
143
|
+
| `legwork owner/repo` | Build a wrapper (also `legwork build`). Accepts `owner/repo` or a github.com URL |
|
|
144
|
+
| `legwork serve owner/repo` | Run the built wrapper as an MCP server over stdio. Needs no model key |
|
|
145
|
+
| `legwork contribute owner/repo [--out DIR]` | Write the build as a public-cache entry, ready for a PR (below) |
|
|
146
|
+
|
|
147
|
+
Builds live in `~/.legwork` (override with `LEGWORK_HOME`). A failed build
|
|
148
|
+
saves its full error output to `attempts.log` in its build folder.
|
|
149
|
+
|
|
150
|
+
## Contributing a wrapper
|
|
151
|
+
|
|
152
|
+
`legwork contribute owner/repo` writes `cache/<owner>__<repo>/` containing
|
|
153
|
+
`wrapper.py` and `manifest.json`, and prints a PR description. Before
|
|
154
|
+
writing anything, it:
|
|
155
|
+
|
|
156
|
+
- blocks the entry if the wrapper copies 50+ words in a row from the source repo
|
|
157
|
+
- re-runs the self-test in the sandbox
|
|
158
|
+
- blocks the write if any output contains something shaped like an API key,
|
|
159
|
+
including your own configured key
|
|
160
|
+
|
|
161
|
+
A GPL, AGPL, missing or unrecognized source license is flagged in the
|
|
162
|
+
manifest and the PR description, but not blocked. If the repo has commits
|
|
163
|
+
newer than the build or the existing entry, you get a warning. Open the PR
|
|
164
|
+
yourself; a bad entry is removed with a plain `git revert`.
|
|
165
|
+
|
|
166
|
+
## Status
|
|
167
|
+
|
|
168
|
+
Early. What's next:
|
|
169
|
+
|
|
170
|
+
- **Linux sandbox.** Legwork is macOS-only today.
|
|
171
|
+
- **Reuse from the cache**, so a repo someone already wrapped doesn't cost
|
|
172
|
+
you a build.
|
|
173
|
+
- Better multi-tool verification than a single simplest-command self-test.
|
|
174
|
+
|
|
175
|
+
## Development
|
|
176
|
+
|
|
177
|
+
```sh
|
|
178
|
+
uv sync
|
|
179
|
+
uv run pytest # the sandbox and integration tests need macOS
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`tests/fixtures/ai_data_extractor_payload.py` is a real malicious file with
|
|
183
|
+
its payload destroyed (every encoded byte randomized, code shape kept), so
|
|
184
|
+
the scanner is tested against a real technique. Tests only parse it with
|
|
185
|
+
`ast`; see `tests/fixtures/README.md`.
|
|
186
|
+
|
|
187
|
+
## License
|
|
188
|
+
|
|
189
|
+
MIT
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Legwork
|
|
2
|
+
|
|
3
|
+
**Point it at a GitHub repo, get a working MCP tool back.**
|
|
4
|
+
|
|
5
|
+
Legwork reads a repo's README, has your LLM write an
|
|
6
|
+
[MCP](https://modelcontextprotocol.io) wrapper for it, installs it in a
|
|
7
|
+
sandbox, and proves it runs before handing it to Claude Code, Cursor or any
|
|
8
|
+
other MCP client. If a repo can't be wrapped, it says why instead of
|
|
9
|
+
producing something broken.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
uvx legwork-mcp owner/repo
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Proof, not a pitch
|
|
16
|
+
|
|
17
|
+
On 2026-09-26 we ran Legwork against **this week's 10 most-starred new AI
|
|
18
|
+
repos on GitHub**, taken exactly as search ranked them, with nothing skipped
|
|
19
|
+
for being hard:
|
|
20
|
+
|
|
21
|
+
| Outcome | Repos |
|
|
22
|
+
|---|---|
|
|
23
|
+
| ✅ Built a working MCP server | **3** — an npm CLI, a Python library, a Go binary |
|
|
24
|
+
| ↩️ Refused, with the right reason | **5** — two Android/macOS apps, a desktop app, a repo with no usage docs, a tool that needs its own API keys |
|
|
25
|
+
| 🛑 Blocked as malware | **2** |
|
|
26
|
+
|
|
27
|
+
Two of the week's top-10 "AI tools", about 700 stars each and three days
|
|
28
|
+
old, carried the same byte-identical obfuscated dropper under different
|
|
29
|
+
file names. Legwork's pre-install scan refused both in about a second.
|
|
30
|
+
Nothing from them was installed or run. Stars are not a trust signal.
|
|
31
|
+
|
|
32
|
+
Full write-up, including what failed along the way and what we fixed:
|
|
33
|
+
[docs/designs/legwork-trending-trial-2026-09-26.md](docs/designs/legwork-trending-trial-2026-09-26.md).
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
You need **macOS**, [uv](https://docs.astral.sh/uv/), and any
|
|
38
|
+
OpenAI-compatible chat-completions endpoint. Legwork itself is free; the
|
|
39
|
+
only cost is your own model usage, which is 1–3 calls per build.
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
export LEGWORK_LLM_ENDPOINT=https://api.openai.com/v1
|
|
43
|
+
export LEGWORK_LLM_API_KEY=... # your own key; never a CLI flag
|
|
44
|
+
export LEGWORK_LLM_MODEL=gpt-5
|
|
45
|
+
|
|
46
|
+
uvx legwork-mcp 2akouwu/reverify
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
A build takes 1–3 minutes and ends with the line to connect it:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
Built an MCP wrapper for 2akouwu/reverify on attempt 2 of 3.
|
|
53
|
+
What it wraps: Verify claims about binaries using Reverify's deterministic tools (wraps `reverify verify --json`)
|
|
54
|
+
|
|
55
|
+
Connect it to Claude Code:
|
|
56
|
+
claude mcp add reverify -- ~/.local/bin/uvx legwork-mcp serve 2akouwu/reverify
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
It also prints an `mcpServers` block for Claude Desktop, Cursor and other
|
|
60
|
+
clients. For a permanent `legwork` command: `uv tool install legwork-mcp`.
|
|
61
|
+
|
|
62
|
+
## How it works
|
|
63
|
+
|
|
64
|
+
1. **Fetch.** Checks the repo is public and reachable, then shallow-clones it.
|
|
65
|
+
2. **Scan.** Statically scans every Python file for obfuscated payloads
|
|
66
|
+
(XOR-decoded byte arrays, computed imports and attribute lookups,
|
|
67
|
+
unreadable identifiers). A hit stops everything before install.
|
|
68
|
+
3. **Read.** The README, plus setup docs it links to (`install.md`,
|
|
69
|
+
`docs/getting-started.md`, …), capped at 50KB with install and usage
|
|
70
|
+
sections kept first.
|
|
71
|
+
4. **Write.** Your model writes the install command and a Python MCP
|
|
72
|
+
wrapper with a self-test, or refuses with a specific reason: no
|
|
73
|
+
programmatic entrypoint, needs hardware, needs its own credentials,
|
|
74
|
+
needs a toolchain the sandbox doesn't have, and so on.
|
|
75
|
+
5. **Install**, sandboxed, network on.
|
|
76
|
+
6. **Self-test**, sandboxed, network off. A failure goes back to the model
|
|
77
|
+
and it tries again, up to 3 attempts and 10 minutes.
|
|
78
|
+
7. **Serve.** `legwork serve owner/repo` runs the wrapper as an MCP server
|
|
79
|
+
over stdio, sandboxed, network off.
|
|
80
|
+
|
|
81
|
+
## What "built" means
|
|
82
|
+
|
|
83
|
+
The self-test calls the **simplest** documented command and checks the
|
|
84
|
+
shape of its result. So "built" means the wrapper starts and its simplest
|
|
85
|
+
tool works. It does not mean every tool works. In the trial:
|
|
86
|
+
|
|
87
|
+
- golive-skill's wrapper exposes its read-only commands, not its deploy
|
|
88
|
+
flow, which needs accounts.
|
|
89
|
+
- AnyJev's decision tool needs a running vLLM server, which the self-test
|
|
90
|
+
didn't have.
|
|
91
|
+
|
|
92
|
+
Tools that must reach the network at run time won't work under `serve`,
|
|
93
|
+
because the network is off.
|
|
94
|
+
|
|
95
|
+
## Risks, stated plainly
|
|
96
|
+
|
|
97
|
+
Legwork runs code you didn't write: the target repo's install steps and a
|
|
98
|
+
wrapper an LLM wrote from a README a stranger wrote. What contains it:
|
|
99
|
+
|
|
100
|
+
- **Install time is the biggest risk.** Package installs and `install.sh`
|
|
101
|
+
scripts run with network on, because they have to. The sandbox limits
|
|
102
|
+
them to their own working folder: no access to your home directory (SSH
|
|
103
|
+
keys, cloud credentials, dotfiles), no writes outside the folder, and an
|
|
104
|
+
environment with none of your variables (including your model key). A
|
|
105
|
+
malicious package can still misbehave inside that folder and reach the
|
|
106
|
+
network during install.
|
|
107
|
+
- **The scan covers the repo's source, not what installers download.**
|
|
108
|
+
magpie, for example, installs with the vendor's `curl … | sh`. For
|
|
109
|
+
downloaded code, the sandbox is the protection.
|
|
110
|
+
- **Prompt injection is not mitigated.** A README can steer the model into
|
|
111
|
+
writing a wrapper that does something other than what you asked for. The
|
|
112
|
+
wrapper still runs sandboxed with network off, which bounds the damage;
|
|
113
|
+
it doesn't prevent a wrong or misleading tool.
|
|
114
|
+
- **The scanner is heuristic.** It catches the obfuscation patterns seen in
|
|
115
|
+
real payloads so far, and a determined author can get past it.
|
|
116
|
+
|
|
117
|
+
The sandbox is macOS `sandbox-exec`. If it isn't available, Legwork refuses
|
|
118
|
+
to run. It never falls back to running unsandboxed.
|
|
119
|
+
|
|
120
|
+
## Commands
|
|
121
|
+
|
|
122
|
+
| Command | What it does |
|
|
123
|
+
|---|---|
|
|
124
|
+
| `legwork owner/repo` | Build a wrapper (also `legwork build`). Accepts `owner/repo` or a github.com URL |
|
|
125
|
+
| `legwork serve owner/repo` | Run the built wrapper as an MCP server over stdio. Needs no model key |
|
|
126
|
+
| `legwork contribute owner/repo [--out DIR]` | Write the build as a public-cache entry, ready for a PR (below) |
|
|
127
|
+
|
|
128
|
+
Builds live in `~/.legwork` (override with `LEGWORK_HOME`). A failed build
|
|
129
|
+
saves its full error output to `attempts.log` in its build folder.
|
|
130
|
+
|
|
131
|
+
## Contributing a wrapper
|
|
132
|
+
|
|
133
|
+
`legwork contribute owner/repo` writes `cache/<owner>__<repo>/` containing
|
|
134
|
+
`wrapper.py` and `manifest.json`, and prints a PR description. Before
|
|
135
|
+
writing anything, it:
|
|
136
|
+
|
|
137
|
+
- blocks the entry if the wrapper copies 50+ words in a row from the source repo
|
|
138
|
+
- re-runs the self-test in the sandbox
|
|
139
|
+
- blocks the write if any output contains something shaped like an API key,
|
|
140
|
+
including your own configured key
|
|
141
|
+
|
|
142
|
+
A GPL, AGPL, missing or unrecognized source license is flagged in the
|
|
143
|
+
manifest and the PR description, but not blocked. If the repo has commits
|
|
144
|
+
newer than the build or the existing entry, you get a warning. Open the PR
|
|
145
|
+
yourself; a bad entry is removed with a plain `git revert`.
|
|
146
|
+
|
|
147
|
+
## Status
|
|
148
|
+
|
|
149
|
+
Early. What's next:
|
|
150
|
+
|
|
151
|
+
- **Linux sandbox.** Legwork is macOS-only today.
|
|
152
|
+
- **Reuse from the cache**, so a repo someone already wrapped doesn't cost
|
|
153
|
+
you a build.
|
|
154
|
+
- Better multi-tool verification than a single simplest-command self-test.
|
|
155
|
+
|
|
156
|
+
## Development
|
|
157
|
+
|
|
158
|
+
```sh
|
|
159
|
+
uv sync
|
|
160
|
+
uv run pytest # the sandbox and integration tests need macOS
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`tests/fixtures/ai_data_extractor_payload.py` is a real malicious file with
|
|
164
|
+
its payload destroyed (every encoded byte randomized, code shape kept), so
|
|
165
|
+
the scanner is tested against a real technique. Tests only parse it with
|
|
166
|
+
`ast`; see `tests/fixtures/README.md`.
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT
|