worm-mcp 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,16 @@
1
+ .venv/
2
+ venv/
3
+ __pycache__/
4
+ *.pyc
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .pypirc
9
+ .idea/
10
+ .vscode/
11
+ .pytest_cache/
12
+ .ruff_cache/
13
+ .mypy_cache/
14
+ .DS_Store
15
+ .env
16
+ .env.local
worm_mcp-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Worm
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,245 @@
1
+ Metadata-Version: 2.4
2
+ Name: worm-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server for Worm prediction markets on Solana
5
+ Project-URL: Homepage, https://worm.wtf
6
+ Project-URL: Documentation, https://docs.worm.wtf
7
+ Project-URL: Repository, https://github.com/wormwtf/worm-mcp
8
+ Project-URL: Issues, https://github.com/wormwtf/worm-mcp/issues
9
+ Author-email: Worm <support@worm.wtf>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: mcp,prediction-markets,solana,trading,worm
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Office/Business :: Financial
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.10
26
+ Requires-Dist: httpx>=0.25
27
+ Requires-Dist: mcp>=1.2.0
28
+ Requires-Dist: worm-sdk[signing]>=0.9.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: mypy>=1.0; extra == 'dev'
31
+ Requires-Dist: ruff>=0.1; extra == 'dev'
32
+ Description-Content-Type: text/markdown
33
+
34
+ <div align="center">
35
+ <img src="https://www.worm.wtf/images/logo.png" alt="Worm" width="110px" />
36
+ <h1>Worm MCP</h1>
37
+ <p>Browse, trade, and manage prediction markets from any AI agent.</p>
38
+ <p>
39
+ ๐ŸŒ <a href="https://worm.wtf">Worm</a> &nbsp;ยท&nbsp;
40
+ ๐Ÿ“– <a href="https://docs.worm.wtf">Docs</a> &nbsp;ยท&nbsp;
41
+ ๐Ÿ“ฆ <a href="https://github.com/wormwtf/worm-sdk">worm-sdk</a> &nbsp;ยท&nbsp;
42
+ ๐Ÿ› <a href="https://github.com/wormwtf/worm-mcp/issues">Issues</a> &nbsp;ยท&nbsp;
43
+ โš–๏ธ <a href="LICENSE">MIT</a>
44
+ </p>
45
+ <p>
46
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10+-blue.svg" alt="Python 3.10+" /></a>
47
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT" /></a>
48
+ </p>
49
+ </div>
50
+
51
+ ## Installation
52
+
53
+ worm-mcp runs over stdio; your MCP client launches it on demand. Set `WALLET_PRIVATE_KEY` to enable authenticated and trading tools, or omit it to use only the public, read-only tools.
54
+
55
+ Pick the flow that fits you:
56
+
57
+ - **[For users](#for-users)**: you just want to use the tool in your MCP client. Your client launches it with `uvx`; no manual install.
58
+ - **[For developers](#for-developers)**: you want to modify the code. Clone the repo and run it from a local editable install.
59
+
60
+ ### For users
61
+
62
+ Your client launches worm-mcp on demand; `uvx` fetches and runs it for you.
63
+
64
+ **Claude Code**: register it once from the CLI; `--scope user` makes it available in every project:
65
+
66
+ ```bash
67
+ claude mcp add --scope user worm \
68
+ -e WALLET_PRIVATE_KEY=<base58-solana-private-key> \
69
+ -- uvx worm-mcp
70
+ ```
71
+
72
+ Confirm with `claude mcp list` (shows `worm โ€ฆ โœ“ Connected`).
73
+
74
+ **Cursor**: add to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (per project), then enable it under **Settings โ†’ MCP**:
75
+
76
+ ```json
77
+ {
78
+ "mcpServers": {
79
+ "worm": {
80
+ "command": "uvx",
81
+ "args": ["worm-mcp"],
82
+ "env": { "WALLET_PRIVATE_KEY": "<base58-solana-private-key>" }
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ **Claude Desktop**: **Settings โ†’ Developer โ†’ Edit Config** opens `claude_desktop_config.json`; add the same `mcpServers` block and restart the app.
89
+
90
+ **Smithery**: [Smithery](https://smithery.ai) installs it into a supported client with one command (it runs locally over stdio, so your key stays on your machine):
91
+
92
+ ```bash
93
+ npx -y @smithery/cli install worm-mcp --client claude
94
+ ```
95
+
96
+ **Other clients** (Windsurf, Cline, Zed, VS Code, โ€ฆ): same launch: command `uvx`, args `["worm-mcp"]`, plus the `WALLET_PRIVATE_KEY` env. Only the config file location differs.
97
+
98
+ > No `uv`? `pipx run worm-mcp` or `pip install worm-mcp` work too.
99
+
100
+ ### For developers
101
+
102
+ Run worm-mcp from a local clone, for hacking on the code or running an unpublished build. Clone and set up an editable environment:
103
+
104
+ ```bash
105
+ git clone https://github.com/wormwtf/worm-mcp.git
106
+ cd worm-mcp
107
+ uv venv
108
+ uv pip install -e ".[dev]"
109
+ uv run ruff check src
110
+ WALLET_PRIVATE_KEY=... worm-mcp # run the stdio server directly
111
+ ```
112
+
113
+ Point your client at the editable checkout so code changes take effect on the next client restart. Either add `--with-editable` to the uvx launch:
114
+
115
+ ```bash
116
+ claude mcp add --scope user worm \
117
+ -e WALLET_PRIVATE_KEY=<base58-solana-private-key> \
118
+ -- uvx --from /path/to/worm-mcp --with-editable /path/to/worm-mcp worm-mcp
119
+ ```
120
+
121
+ โ€ฆor point the client's `command` straight at the venv binary the editable install creates (`args` can be omitted):
122
+
123
+ ```json
124
+ {
125
+ "mcpServers": {
126
+ "worm": {
127
+ "command": "/path/to/worm-mcp/.venv/bin/worm-mcp",
128
+ "env": { "WALLET_PRIVATE_KEY": "<base58-solana-private-key>" }
129
+ }
130
+ }
131
+ }
132
+ ```
133
+
134
+ ## Verify
135
+
136
+ Ask your client *"List trending markets on Worm"* (public) or *"What's my Worm account summary?"* (confirms auth): a sensible answer means it's wired up.
137
+
138
+ ## Requirements
139
+
140
+ - An MCP client (Claude Code, Cursor, Claude Desktop, โ€ฆ)
141
+ - [`uv`](https://docs.astral.sh/uv/) (or `pipx` / `pip`)
142
+ - A funded Solana wallet (only for trading and other authenticated tools)
143
+
144
+ ## Environment variables
145
+
146
+ | Variable | Required | Purpose |
147
+ |---|---|---|
148
+ | `WALLET_PRIVATE_KEY` | for writes | Base58 Solana private key (the export from Phantom/Solflare; a 64-byte hex keypair also works). Signs transactions locally and bootstraps HMAC credentials on first run, cached in `~/.worm/config.json`. |
149
+ | `WORM_API_KEY` / `WORM_API_SECRET` | no | Use existing HMAC credentials instead of bootstrapping |
150
+ | `WORM_API_BASE` | no | API base URL (default `https://api.worm.wtf`) |
151
+
152
+ ## Tools
153
+
154
+ ### Public (no credentials)
155
+
156
+ | Tool | Description |
157
+ |---|---|
158
+ | `search` | Search markets and events by text and filters |
159
+ | `list_markets` | Browse markets by category, state, or sort |
160
+ | `get_market` | Full market detail (outcomes, fees, config, state) |
161
+ | `get_market_stats` | Volume, market cap, and trade count |
162
+ | `get_market_price` | Mid-price snapshot for an outcome |
163
+ | `get_orderbook` | Spot orderbook bids and asks |
164
+ | `get_market_candles` | OHLCV candles (5m / 30m) |
165
+ | `get_market_trades` | Recent public trades |
166
+ | `get_market_margin_activity` | Public margin activity feed |
167
+ | `list_events` | Browse events (groups of related markets) |
168
+ | `get_event` | Full event detail with all markets |
169
+ | `list_sports` | Sports and leagues catalog |
170
+ | `estimate_margin_position` | Preview a leveraged position |
171
+ | `read_worm_docs` | Fetch Worm documentation pages |
172
+
173
+ ### Authenticated read (HMAC)
174
+
175
+ | Tool | Description |
176
+ |---|---|
177
+ | `get_account_summary` | Your profile |
178
+ | `get_account_assets` | Portfolio share balances |
179
+ | `get_account_pnl` | PnL breakdown |
180
+ | `list_orders` / `get_order` | Your spot orders |
181
+ | `list_trades` | Your trade fills |
182
+ | `list_margin_positions` / `get_margin_position` | Your margin positions |
183
+ | `list_margin_requests` / `get_margin_request` | Your margin position requests |
184
+ | `list_margin_settlements` | Margin settlements |
185
+ | `list_redeems` / `get_redeem` | Your redeem records |
186
+ | `list_api_keys` | Your API keys |
187
+
188
+ ### Authenticated write (HMAC)
189
+
190
+ | Tool | Description |
191
+ |---|---|
192
+ | `cancel_margin_request` | Cancel a pending margin request |
193
+ | `close_margin_position` | Close a margin position |
194
+ | `set_tp_sl` / `remove_tp_sl` | Set / clear take-profit and stop-loss |
195
+ | `claim_margin_settlement` | Claim a resolved settlement |
196
+ | `revoke_api_key` | Revoke an API key |
197
+
198
+ ### Authenticated write: local signing (needs `WALLET_PRIVATE_KEY`)
199
+
200
+ | Tool | Description |
201
+ |---|---|
202
+ | `place_order` / `cancel_order` | Place / cancel a spot order |
203
+ | `open_margin_position` | Open a leveraged position |
204
+ | `redeem` | Redeem winnings from a resolved market |
205
+
206
+ ## Notes for agents
207
+
208
+ - **Spot vs margin**: `place_order` is for non-margin (orderbook) markets; use `open_margin_position` when `margin_enabled=true`. Margin markets quote against an AMM, so `get_orderbook` may be empty; size with `estimate_margin_position`.
209
+ - **Margin lifecycle**: `open_margin_position` returns a *position-request* pubkey; the actual position appears via `list_margin_positions` (match `position_request_pubkey`) once funding completes. TP/SL is unsupported on Hyperliquid-backed markets.
210
+ - **Leverage**: always call `estimate_margin_position` and confirm with the user before opening; leverage carries liquidation risk.
211
+ - **Docs on demand**: call `read_worm_docs` with no arguments for the index, then pass a path for exact API schemas and examples.
212
+
213
+ ## Features
214
+
215
+ - 38 tools across markets, events, search, sports, account, orders, trades, margin, redeems, key management, and docs
216
+ - Local Solana keypair signing, so your private key never leaves the MCP process
217
+ - Auto-bootstrapped HMAC credentials: set one env var and the rest is automatic
218
+ - Public market data works with no credentials at all
219
+ - Built on the official [`worm-sdk`](https://pypi.org/project/worm-sdk/), with full type hints
220
+
221
+ ## Architecture
222
+
223
+ ```
224
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” stdio โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” HTTPS + HMAC โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
225
+ โ”‚ MCP client โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ โ”‚ worm-mcp โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ โ”‚ Worm API โ”‚
226
+ โ”‚ (Claude etc.)โ”‚ โ”‚ (Python) โ”‚ โ”‚ api.worm.wtf โ”‚
227
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
228
+ โ”‚ ed25519 signing (orders, margin, redeems)
229
+ โ–ผ
230
+ worm-sdk + solders (local keypair)
231
+ ```
232
+
233
+ See [rate limits](https://docs.worm.wtf/api-reference/rate-limits) in the docs. Markets are viewable at `https://worm.wtf/market/{condition_id}`.
234
+
235
+ ## Security
236
+
237
+ - Your private key is used only to sign transactions locally; it is never sent to the Worm API or any third party.
238
+ - Bootstrapped HMAC credentials are cached at `~/.worm/config.json` with `0600` permissions, keyed by API base URL and wallet, so multiple wallets or environments (e.g. `WORM_API_BASE` overrides) each keep their own credentials instead of clobbering one another. Writes are atomic and locked, so concurrent clients can't corrupt or lose cached credentials.
239
+ - Use a dedicated wallet funded with only what you intend to trade, and keep the key out of version control and shared configs.
240
+
241
+ ## Troubleshooting
242
+
243
+ - **`uvx: command not found`**: install [`uv`](https://docs.astral.sh/uv/), or use `pipx run worm-mcp` / `pip install worm-mcp`.
244
+ - **Tools don't appear**: fully restart the client; it spawns the server once at startup. In Claude Code, check `claude mcp list`.
245
+ - **Authentication errors**: confirm `WALLET_PRIVATE_KEY` is a valid base58 Solana key; delete `~/.worm/config.json` to force a fresh credential bootstrap.
@@ -0,0 +1,212 @@
1
+ <div align="center">
2
+ <img src="https://www.worm.wtf/images/logo.png" alt="Worm" width="110px" />
3
+ <h1>Worm MCP</h1>
4
+ <p>Browse, trade, and manage prediction markets from any AI agent.</p>
5
+ <p>
6
+ ๐ŸŒ <a href="https://worm.wtf">Worm</a> &nbsp;ยท&nbsp;
7
+ ๐Ÿ“– <a href="https://docs.worm.wtf">Docs</a> &nbsp;ยท&nbsp;
8
+ ๐Ÿ“ฆ <a href="https://github.com/wormwtf/worm-sdk">worm-sdk</a> &nbsp;ยท&nbsp;
9
+ ๐Ÿ› <a href="https://github.com/wormwtf/worm-mcp/issues">Issues</a> &nbsp;ยท&nbsp;
10
+ โš–๏ธ <a href="LICENSE">MIT</a>
11
+ </p>
12
+ <p>
13
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10+-blue.svg" alt="Python 3.10+" /></a>
14
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT" /></a>
15
+ </p>
16
+ </div>
17
+
18
+ ## Installation
19
+
20
+ worm-mcp runs over stdio; your MCP client launches it on demand. Set `WALLET_PRIVATE_KEY` to enable authenticated and trading tools, or omit it to use only the public, read-only tools.
21
+
22
+ Pick the flow that fits you:
23
+
24
+ - **[For users](#for-users)**: you just want to use the tool in your MCP client. Your client launches it with `uvx`; no manual install.
25
+ - **[For developers](#for-developers)**: you want to modify the code. Clone the repo and run it from a local editable install.
26
+
27
+ ### For users
28
+
29
+ Your client launches worm-mcp on demand; `uvx` fetches and runs it for you.
30
+
31
+ **Claude Code**: register it once from the CLI; `--scope user` makes it available in every project:
32
+
33
+ ```bash
34
+ claude mcp add --scope user worm \
35
+ -e WALLET_PRIVATE_KEY=<base58-solana-private-key> \
36
+ -- uvx worm-mcp
37
+ ```
38
+
39
+ Confirm with `claude mcp list` (shows `worm โ€ฆ โœ“ Connected`).
40
+
41
+ **Cursor**: add to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (per project), then enable it under **Settings โ†’ MCP**:
42
+
43
+ ```json
44
+ {
45
+ "mcpServers": {
46
+ "worm": {
47
+ "command": "uvx",
48
+ "args": ["worm-mcp"],
49
+ "env": { "WALLET_PRIVATE_KEY": "<base58-solana-private-key>" }
50
+ }
51
+ }
52
+ }
53
+ ```
54
+
55
+ **Claude Desktop**: **Settings โ†’ Developer โ†’ Edit Config** opens `claude_desktop_config.json`; add the same `mcpServers` block and restart the app.
56
+
57
+ **Smithery**: [Smithery](https://smithery.ai) installs it into a supported client with one command (it runs locally over stdio, so your key stays on your machine):
58
+
59
+ ```bash
60
+ npx -y @smithery/cli install worm-mcp --client claude
61
+ ```
62
+
63
+ **Other clients** (Windsurf, Cline, Zed, VS Code, โ€ฆ): same launch: command `uvx`, args `["worm-mcp"]`, plus the `WALLET_PRIVATE_KEY` env. Only the config file location differs.
64
+
65
+ > No `uv`? `pipx run worm-mcp` or `pip install worm-mcp` work too.
66
+
67
+ ### For developers
68
+
69
+ Run worm-mcp from a local clone, for hacking on the code or running an unpublished build. Clone and set up an editable environment:
70
+
71
+ ```bash
72
+ git clone https://github.com/wormwtf/worm-mcp.git
73
+ cd worm-mcp
74
+ uv venv
75
+ uv pip install -e ".[dev]"
76
+ uv run ruff check src
77
+ WALLET_PRIVATE_KEY=... worm-mcp # run the stdio server directly
78
+ ```
79
+
80
+ Point your client at the editable checkout so code changes take effect on the next client restart. Either add `--with-editable` to the uvx launch:
81
+
82
+ ```bash
83
+ claude mcp add --scope user worm \
84
+ -e WALLET_PRIVATE_KEY=<base58-solana-private-key> \
85
+ -- uvx --from /path/to/worm-mcp --with-editable /path/to/worm-mcp worm-mcp
86
+ ```
87
+
88
+ โ€ฆor point the client's `command` straight at the venv binary the editable install creates (`args` can be omitted):
89
+
90
+ ```json
91
+ {
92
+ "mcpServers": {
93
+ "worm": {
94
+ "command": "/path/to/worm-mcp/.venv/bin/worm-mcp",
95
+ "env": { "WALLET_PRIVATE_KEY": "<base58-solana-private-key>" }
96
+ }
97
+ }
98
+ }
99
+ ```
100
+
101
+ ## Verify
102
+
103
+ Ask your client *"List trending markets on Worm"* (public) or *"What's my Worm account summary?"* (confirms auth): a sensible answer means it's wired up.
104
+
105
+ ## Requirements
106
+
107
+ - An MCP client (Claude Code, Cursor, Claude Desktop, โ€ฆ)
108
+ - [`uv`](https://docs.astral.sh/uv/) (or `pipx` / `pip`)
109
+ - A funded Solana wallet (only for trading and other authenticated tools)
110
+
111
+ ## Environment variables
112
+
113
+ | Variable | Required | Purpose |
114
+ |---|---|---|
115
+ | `WALLET_PRIVATE_KEY` | for writes | Base58 Solana private key (the export from Phantom/Solflare; a 64-byte hex keypair also works). Signs transactions locally and bootstraps HMAC credentials on first run, cached in `~/.worm/config.json`. |
116
+ | `WORM_API_KEY` / `WORM_API_SECRET` | no | Use existing HMAC credentials instead of bootstrapping |
117
+ | `WORM_API_BASE` | no | API base URL (default `https://api.worm.wtf`) |
118
+
119
+ ## Tools
120
+
121
+ ### Public (no credentials)
122
+
123
+ | Tool | Description |
124
+ |---|---|
125
+ | `search` | Search markets and events by text and filters |
126
+ | `list_markets` | Browse markets by category, state, or sort |
127
+ | `get_market` | Full market detail (outcomes, fees, config, state) |
128
+ | `get_market_stats` | Volume, market cap, and trade count |
129
+ | `get_market_price` | Mid-price snapshot for an outcome |
130
+ | `get_orderbook` | Spot orderbook bids and asks |
131
+ | `get_market_candles` | OHLCV candles (5m / 30m) |
132
+ | `get_market_trades` | Recent public trades |
133
+ | `get_market_margin_activity` | Public margin activity feed |
134
+ | `list_events` | Browse events (groups of related markets) |
135
+ | `get_event` | Full event detail with all markets |
136
+ | `list_sports` | Sports and leagues catalog |
137
+ | `estimate_margin_position` | Preview a leveraged position |
138
+ | `read_worm_docs` | Fetch Worm documentation pages |
139
+
140
+ ### Authenticated read (HMAC)
141
+
142
+ | Tool | Description |
143
+ |---|---|
144
+ | `get_account_summary` | Your profile |
145
+ | `get_account_assets` | Portfolio share balances |
146
+ | `get_account_pnl` | PnL breakdown |
147
+ | `list_orders` / `get_order` | Your spot orders |
148
+ | `list_trades` | Your trade fills |
149
+ | `list_margin_positions` / `get_margin_position` | Your margin positions |
150
+ | `list_margin_requests` / `get_margin_request` | Your margin position requests |
151
+ | `list_margin_settlements` | Margin settlements |
152
+ | `list_redeems` / `get_redeem` | Your redeem records |
153
+ | `list_api_keys` | Your API keys |
154
+
155
+ ### Authenticated write (HMAC)
156
+
157
+ | Tool | Description |
158
+ |---|---|
159
+ | `cancel_margin_request` | Cancel a pending margin request |
160
+ | `close_margin_position` | Close a margin position |
161
+ | `set_tp_sl` / `remove_tp_sl` | Set / clear take-profit and stop-loss |
162
+ | `claim_margin_settlement` | Claim a resolved settlement |
163
+ | `revoke_api_key` | Revoke an API key |
164
+
165
+ ### Authenticated write: local signing (needs `WALLET_PRIVATE_KEY`)
166
+
167
+ | Tool | Description |
168
+ |---|---|
169
+ | `place_order` / `cancel_order` | Place / cancel a spot order |
170
+ | `open_margin_position` | Open a leveraged position |
171
+ | `redeem` | Redeem winnings from a resolved market |
172
+
173
+ ## Notes for agents
174
+
175
+ - **Spot vs margin**: `place_order` is for non-margin (orderbook) markets; use `open_margin_position` when `margin_enabled=true`. Margin markets quote against an AMM, so `get_orderbook` may be empty; size with `estimate_margin_position`.
176
+ - **Margin lifecycle**: `open_margin_position` returns a *position-request* pubkey; the actual position appears via `list_margin_positions` (match `position_request_pubkey`) once funding completes. TP/SL is unsupported on Hyperliquid-backed markets.
177
+ - **Leverage**: always call `estimate_margin_position` and confirm with the user before opening; leverage carries liquidation risk.
178
+ - **Docs on demand**: call `read_worm_docs` with no arguments for the index, then pass a path for exact API schemas and examples.
179
+
180
+ ## Features
181
+
182
+ - 38 tools across markets, events, search, sports, account, orders, trades, margin, redeems, key management, and docs
183
+ - Local Solana keypair signing, so your private key never leaves the MCP process
184
+ - Auto-bootstrapped HMAC credentials: set one env var and the rest is automatic
185
+ - Public market data works with no credentials at all
186
+ - Built on the official [`worm-sdk`](https://pypi.org/project/worm-sdk/), with full type hints
187
+
188
+ ## Architecture
189
+
190
+ ```
191
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” stdio โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” HTTPS + HMAC โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
192
+ โ”‚ MCP client โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ โ”‚ worm-mcp โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ โ”‚ Worm API โ”‚
193
+ โ”‚ (Claude etc.)โ”‚ โ”‚ (Python) โ”‚ โ”‚ api.worm.wtf โ”‚
194
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
195
+ โ”‚ ed25519 signing (orders, margin, redeems)
196
+ โ–ผ
197
+ worm-sdk + solders (local keypair)
198
+ ```
199
+
200
+ See [rate limits](https://docs.worm.wtf/api-reference/rate-limits) in the docs. Markets are viewable at `https://worm.wtf/market/{condition_id}`.
201
+
202
+ ## Security
203
+
204
+ - Your private key is used only to sign transactions locally; it is never sent to the Worm API or any third party.
205
+ - Bootstrapped HMAC credentials are cached at `~/.worm/config.json` with `0600` permissions, keyed by API base URL and wallet, so multiple wallets or environments (e.g. `WORM_API_BASE` overrides) each keep their own credentials instead of clobbering one another. Writes are atomic and locked, so concurrent clients can't corrupt or lose cached credentials.
206
+ - Use a dedicated wallet funded with only what you intend to trade, and keep the key out of version control and shared configs.
207
+
208
+ ## Troubleshooting
209
+
210
+ - **`uvx: command not found`**: install [`uv`](https://docs.astral.sh/uv/), or use `pipx run worm-mcp` / `pip install worm-mcp`.
211
+ - **Tools don't appear**: fully restart the client; it spawns the server once at startup. In Claude Code, check `claude mcp list`.
212
+ - **Authentication errors**: confirm `WALLET_PRIVATE_KEY` is a valid base58 Solana key; delete `~/.worm/config.json` to force a fresh credential bootstrap.
@@ -0,0 +1,63 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "worm-mcp"
7
+ dynamic = ["version"]
8
+ description = "MCP server for Worm prediction markets on Solana"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "Worm", email = "support@worm.wtf" }]
13
+ keywords = ["mcp", "worm", "prediction-markets", "solana", "trading"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Topic :: Office/Business :: Financial",
25
+ "Topic :: Software Development :: Libraries :: Python Modules",
26
+ "Typing :: Typed",
27
+ ]
28
+ dependencies = [
29
+ "mcp>=1.2.0",
30
+ "worm-sdk[signing]>=0.9.0",
31
+ "httpx>=0.25",
32
+ ]
33
+
34
+ [project.urls]
35
+ Homepage = "https://worm.wtf"
36
+ Documentation = "https://docs.worm.wtf"
37
+ Repository = "https://github.com/wormwtf/worm-mcp"
38
+ Issues = "https://github.com/wormwtf/worm-mcp/issues"
39
+
40
+ [project.optional-dependencies]
41
+ dev = [
42
+ "ruff>=0.1",
43
+ "mypy>=1.0",
44
+ ]
45
+
46
+ [project.scripts]
47
+ worm-mcp = "worm_mcp.__main__:main"
48
+
49
+ [tool.hatch.version]
50
+ path = "src/worm_mcp/_version.py"
51
+
52
+ [tool.hatch.build.targets.wheel]
53
+ packages = ["src/worm_mcp"]
54
+
55
+ [tool.hatch.build.targets.sdist]
56
+ include = ["src/worm_mcp", "README.md", "LICENSE", "pyproject.toml"]
57
+
58
+ [tool.ruff]
59
+ line-length = 120
60
+ target-version = "py310"
61
+
62
+ [tool.mypy]
63
+ python_version = "3.10"
@@ -0,0 +1,5 @@
1
+ """Worm MCP server."""
2
+
3
+ from worm_mcp._version import __version__
4
+
5
+ __all__ = ["__version__"]
@@ -0,0 +1,14 @@
1
+ """Entry point launched by Claude Code/Cursor/Claude Desktop as subprocess."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .server import build_server
6
+
7
+
8
+ def main() -> None:
9
+ server = build_server()
10
+ server.run()
11
+
12
+
13
+ if __name__ == "__main__":
14
+ main()
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,15 @@
1
+ """Configuration constants for the Worm MCP server."""
2
+
3
+ from pathlib import Path
4
+
5
+ # Bootstrapped HMAC credentials are cached here, keyed by (base_url, wallet).
6
+ # This path is documented in the README and MCP server instructions โ€” keep it.
7
+ CONFIG_FILE = Path.home() / ".worm" / "config.json"
8
+
9
+ DOCS_HOST = "docs.worm.wtf"
10
+ DOCS_BASE = f"https://{DOCS_HOST}"
11
+ DOCS_INDEX_PATH = "llms.txt"
12
+ DOCS_FULL_PATH = "llms-full.txt"
13
+
14
+ # Cap on docs content returned by a single read_worm_docs call.
15
+ DOCS_MAX_CHARS = 100_000
@@ -0,0 +1,58 @@
1
+ """read_worm_docs tool โ€” fetches Worm documentation from docs.worm.wtf via httpx."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from urllib.parse import urlparse
6
+
7
+ import httpx
8
+
9
+ from worm_mcp.constants import DOCS_BASE, DOCS_FULL_PATH, DOCS_HOST, DOCS_INDEX_PATH, DOCS_MAX_CHARS
10
+
11
+
12
+ def _resolve_url(path: str | None, full: bool) -> str:
13
+ """Map (path, full) to a docs.worm.wtf URL, rejecting any other host."""
14
+ if full:
15
+ if path:
16
+ raise RuntimeError("Pass either a path or full=True, not both.")
17
+ return f"{DOCS_BASE}/{DOCS_FULL_PATH}"
18
+
19
+ # urlparse drops any query/fragment so they can't leak into the .md suffix below.
20
+ parsed = urlparse((path or "").strip())
21
+ if parsed.scheme and parsed.hostname != DOCS_HOST:
22
+ raise RuntimeError(f"read_worm_docs only fetches {DOCS_HOST}; got host {parsed.hostname!r}.")
23
+
24
+ rel = parsed.path.lstrip("/")
25
+ if not rel: # empty path, bare "/", or a bare docs URL โ†’ index
26
+ return f"{DOCS_BASE}/{DOCS_INDEX_PATH}"
27
+ if ".." in rel.split("/"):
28
+ raise RuntimeError(f"Invalid docs path {path!r}.")
29
+ if not rel.endswith((".md", ".txt")):
30
+ rel = f"{rel}.md"
31
+ return f"{DOCS_BASE}/{rel}"
32
+
33
+
34
+ def read_worm_docs(path: str | None = None, full: bool = False) -> dict:
35
+ """Fetch the docs index (no args), a doc page by path, or the full docs (full=True)."""
36
+ url = _resolve_url(path, full)
37
+ try:
38
+ resp = httpx.get(url, timeout=30.0, follow_redirects=True)
39
+ except httpx.HTTPError as e:
40
+ raise RuntimeError(f"Failed to fetch {url}: {e}") from e
41
+
42
+ # docs.worm.wtf uses same-host 307s (e.g. section -> intro page); follow them, but
43
+ # refuse the content if ANY hop (intermediate or final) left the allowlist.
44
+ off = next((r.url.host for r in [*resp.history, resp] if r.url.host != DOCS_HOST), None)
45
+ if off is not None:
46
+ raise RuntimeError(f"Refusing content from off-allowlist host {off!r} after redirect.")
47
+
48
+ if resp.status_code != 200:
49
+ raise RuntimeError(
50
+ f"{url} returned HTTP {resp.status_code}. "
51
+ "Call read_worm_docs() with no arguments to get the index of valid paths."
52
+ )
53
+
54
+ content = resp.text
55
+ truncated = len(content) > DOCS_MAX_CHARS
56
+ if truncated:
57
+ content = content[:DOCS_MAX_CHARS] + "\n\n[truncated โ€” fetch a more specific path]"
58
+ return {"url": str(resp.url), "truncated": truncated, "content": content}
File without changes