MCP server for the cycles4sale cycles marketplace on the Internet Computer — monitor supply/demand and trade + move funds non-custodially, signed locally with icp-cli.
Project description
cycles4sale-mcp
An MCP server that lets an AI agent interact with the cycles4sale cycles marketplace on the Internet Computer — live at cycles-4-sale.com.
It exposes two tiers as one uvx command:
- Monitor (read-only) — watch supply, demand, prices and the order book. No identity, no funds.
- Trade — buy and sell cycles and move ICP / cycles, non-custodially. Every call is signed locally by your own icp-cli identity; the key never leaves your machine and the canister only ever pulls the live allowance you approve.
Requirements
- Python 3.10+
icp(icp-cli) on yourPATH— the server shells out to it for every canister call and for signing. (--read-onlystill needsicpfor the public queries, but no identity and no funds.)uv/uvxto run it without a manual install (optional).
Quick start
Monitor the market (no funds, no identity)
uvx cycles4sale-mcp --read-only
Only the monitor tools are registered: market_snapshot, quote, order_book, whoami.
Trade (needs an icp-cli identity + funds)
# Creates the identity if it doesn't exist and prints its principal:
uvx cycles4sale-mcp --identity my-agent \
--max-icp-per-trade 50000000 \
--daily-icp-budget 200000000
Fund the printed principal by sending it ICP (to buy) and/or cycles (to sell) — ICRC-1, null subaccount. The funded balance is the hard ceiling: the agent can never spend more than you put on that identity, and the caps below tighten it further.
Wiring it into an MCP client
Add the server to your client config (stdio transport). Example for a Claude-style mcpServers block:
{
"mcpServers": {
"cycles4sale": {
"command": "uvx",
"args": ["cycles4sale-mcp", "--identity", "my-agent", "--max-icp-per-trade", "50000000"]
}
}
}
For a read-only feed, use "args": ["cycles4sale-mcp", "--read-only"].
Streamable HTTP
To serve over HTTP (endpoint /mcp) instead of stdio:
uvx cycles4sale-mcp --read-only --http --host 127.0.0.1 --port 8080
# -> http://127.0.0.1:8080/mcp
Tools
Monitor (always available):
| Tool | What it returns |
|---|---|
market_snapshot |
Supply, demand, lifetime volume, the discount/fee rates, and buyer/seller eff. discounts |
quote |
Live ICP price (and fee, seller proceeds) to buy num_lots whole lots |
order_book |
Current listings and open buy orders |
whoami |
The agent identity, mode, configured caps, and session spend |
Trade + wallet (only when not --read-only):
| Tool | What it does |
|---|---|
my_positions |
Your listing, open buy order, and ICP + cycles balances |
buy |
Buy cycles by committing up to max_icp_e8s of ICP |
sell |
List num_lots whole lots of your cycles for sale (0 delists) |
cancel_buy |
Cancel your open buy order and revoke the ICP allowance |
cancel_listing |
Cancel your listing and revoke the cycles allowance |
balances |
Your ICP (e8s) and cycles balances |
send_icp |
Send ICP to a principal |
send_cycles |
Send cycles to a principal |
deposit_to_canister |
Withdraw cycles as REAL cycles onto a canister (top up its fuel) |
receive_address |
Your agent principal — fund the agent by sending ICP/cycles to it |
Guardrails
| Flag | Effect |
|---|---|
--read-only |
Registers only the monitor tools; no identity, no funds. |
--max-icp-per-trade E8S |
Rejects any single ICP outflow (buy / send_icp) larger than this. |
--daily-icp-budget E8S |
Rejects ICP outflows once the session's cumulative total would exceed this. |
--dry-run |
Trade + wallet tools return the planned approve/call instead of executing it. |
The daily budget is an in-process counter and resets when the server restarts. Trades are
non-custodial: buy/sell approve exactly what's needed to the broker, call, then revoke the
standing allowance when nothing rests.
Publishing to PyPI
The Makefile mirrors the project's standard PyPI flow. Provide a .pypirc (git-ignored) with
username = __token__ and your API tokens for both testpypi and pypi:
[testpypi]
username = __token__
password = pypi-…
[pypi]
username = __token__
password = pypi-…
Then:
make pypi-build # rm -rf dist && python -m build
make testpypi-upload # upload to TestPyPI
make testpypi-install # verify the TestPyPI build installs
make pypi-upload # upload to PyPI
make pypi-install # pip install cycles4sale-mcp
(make pypi-build needs the dev extras: pip install build twine, or pip install -e ".[dev]".)
Development
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
cycles4sale-mcp --help
cycles4sale-mcp --read-only # smoke-test the monitor tier
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cycles4sale_mcp-0.1.0.tar.gz.
File metadata
- Download URL: cycles4sale_mcp-0.1.0.tar.gz
- Upload date:
- Size: 13.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff35f6a5939dcc019e0a4db2d441c1e0a2c69a36ae37dbcec0696db5ef63ddce
|
|
| MD5 |
68f2e5f6b38a1bf9498007cc1d391094
|
|
| BLAKE2b-256 |
7cfe42f82361f5fe29cf1b12278380957cddd79acbd416721f2cea09115b831d
|
File details
Details for the file cycles4sale_mcp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: cycles4sale_mcp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c86447669161d61e4e4e0e46858f3f4c9b3f1de5e1881a4f5ab739669c992d46
|
|
| MD5 |
869ca649106d91c23e776545a097b2fd
|
|
| BLAKE2b-256 |
034e1f566a0d12078435e8ef3753cb2f8d1ed1696d3f8a160ecdcb861e62b9ce
|