SolKnife for agents
SolKnife is agent-native by construction. Every action a user can take through the UI is reachable as a JSON HTTP call. The server builds unsigned transactions; the caller (a user wallet, or an agent with key custody) signs; the server self-submits. The machine-readable manifest at /api/manifest is the source of truth.
The four kinds of tools
Each entry in the manifest carries one of four tags:
- read: pure GET, parameters in the query string, no signing. Used for the rug checker, portfolio, pool data, LP positions, current state of any account.
- build: POST with structured JSON in and out. Returns an unsigned base64 transaction the caller can verify and sign.
- submit: POST that takes a signed transaction (or array of signed transactions, for saga flows) and self-submits. Per-tool naming varies — most tools use
/execute, confidential transfer uses a single/submitwith anopdiscriminator. - worker-bound: the only flow that isn't pure HTTP. Confidential-transfer proof generation runs in a browser Web Worker because the ElGamal + AES keys are derived from a wallet signature and never leave the process that derived them. An agent that wants to drive this without a browser has to embed the worker logic in its own runtime, becoming the key-holder.
What an agent can do today
Without any extra infrastructure, an HTTP-only agent can already:
- Rug-check any Solana token and inspect mint authorities
- Read any wallet's priced holdings and DLMM positions
- Compare DLMM pools for a token side by side
- Quote and execute non-custodial swaps via Jupiter (with its own keypair or a user-authorized wallet)
- Create SPL or Token-2022 mints (including with the confidential-transfer extension), mint additional supply, revoke authorities, set Metaplex metadata
- Create token accounts, reclaim rent from empty ones, burn NFTs
- Read confidential-transfer state for any owner-and-mint pair, list any wallet's orphan ZK context-state accounts, and reclaim their rent
The only flow gated behind the in-browser worker is the generation of ZK proofs for confidential-transfer deposit/apply/ empty/withdraw/transfer. Driving those without a browser is possible (the WASM zk-sdk runs in Node), but it extends the trust boundary because the agent's runtime takes custody of the user's ElGamal + AES keys.
MCP server (reads + builds + submits)
Streamable-HTTP MCP endpoint at /api/mcp. Stateless transport, one request per response. Phases 1+2+3 are live: 13 read tools, 15 build tools, 9 submit/execute tools (37 total) — every action the agent-parity audit identified is callable. The confidential-transfer worker stays off MCP for the non-custodial reasons explained below.
Point any MCP client at:
{
"mcpServers": {
"solknife": {
"transport": "streamable-http",
"url": "https://solknife.xyz/api/mcp"
}
}
}Quickstart (raw HTTP)
Read the manifest:
curl -s https://solknife.xyz/api/manifest | jq .Rug-check a token:
curl -s "https://solknife.xyz/api/check?mint=<MINT_ADDRESS>" | jq .Build an unsigned create-mint tx (then sign + execute):
curl -s -X POST https://solknife.xyz/api/create-mint/build \
-H 'content-type: application/json' \
-d '{ "owner": "<PUBKEY>", "decimals": 6, "extensions": {} }' | jq .Every response uses the ApiResponse envelope: { ok: true, data } or { ok: false, error: { code, message } }. No bare 500s with HTML bodies.
Non-custodial guarantees
The server never holds a signing key. Every build endpoint returns bytes; every submit endpoint takes signed bytes and relays them. The client's verifier (and the matching server-side re-verifier) walks the assembled transaction against a strict program allowlist before the wallet ever sees it.
For confidential transfer, the ElGamal and AES keys derive from a wallet signature in the browser's Web Worker and are not posted to the server. An agent that drives the CT flows from outside a browser is choosing to take custody of those derived keys — that is an explicit trust extension, not something the architecture obscures.
Fees
The server is free to call by default. Each transaction-producing tool has an optional flat lamport fee, configured by the operator via environment variable. When set, the fee shows up as a single SystemProgram.transferinside the build output, pinned by the verifier — the fee can't be stripped or redirected without the verifier rejecting the transaction. The swap fee accrues to a wSOL token account; every other fee goes to SOLKNIFE_FEE_WALLET.
x402 (Phase 1, off by default)
The HTTP-native x402 Payment Required gate is shipped. It is OFF by default; operators flip SOLKNIFE_X402_ENABLED=true to enable per-call payment on the agent-callable surface.
When enabled, an agent request returns:
HTTP/1.1 402 Payment Required
Www-Authenticate: x402 scheme=solana, network=mainnet-beta, ...
Content-Type: application/json
{
"x402": {
"scheme": "solana",
"network": "mainnet-beta",
"lamports": "1000000",
"to": "<SOLKNIFE_FEE_WALLET>",
"note": "Pay the listed amount ... Retry with header X-Payment: <signature>"
}
}Agent flow:
- Receive the 402 challenge
- Build a Solana tx with one
SystemProgram.transferfrom your wallet totoforlamports - Sign + submit the tx to Solana
- Once confirmed, retry the original request with header
X-Payment: <base58 signature>
The server verifies the payment via Helius getTransaction: tx must be confirmed, contain a top-level transfer of ≥ price lamports to the configured fee wallet, and have landed within the last 10 minutes.
Phase 1 limitation: replay protection is in-process only. The server remembers signatures it has accepted this lifecycle and rejects repeats; restart loses the memory. Phase 2 will add memo-nonce binding (the challenge ships a one-time nonce; the client embeds it as an SPL Memo ix; the server verifies the memo matches), which makes replay impossible across processes too.
Same SOLKNIFE_FEE_WALLET receives both x402 payments and the existing per-op fees inside built transactions — operators have one revenue address.
Sign-in bypass (SIWS session)
Pay-per-call is the universal path, but agents that make many calls in a row can sign in once and skip the per-call fee for an hour. The bypass is a cookie issued only after a real Sign-In With Solana (SIWS) signature — there's no shared origin allow-list, no spoofable header.
Three-step handshake:
# 1. Ask for a challenge bound to your wallet
curl -s -c cookies.txt -X POST https://solknife.xyz/api/auth/challenge \
-H 'content-type: application/json' \
-d '{ "wallet": "<YOUR_PUBKEY>" }' | jq .
# Response:
# { "ok": true, "data": { "message": "...", "nonce": "...", "issuedAt": "..." } }
# 2. Sign the EXACT message bytes with your wallet's Ed25519 key
# (Node example using @noble/curves):
# const sig = ed25519.sign(new TextEncoder().encode(message), privKey);
# const signatureBase64 = Buffer.from(sig).toString("base64");
# 3. POST the signed message back to mint a session cookie
curl -s -b cookies.txt -c cookies.txt -X POST https://solknife.xyz/api/auth/verify \
-H 'content-type: application/json' \
-d '{ "wallet": "<YOUR_PUBKEY>", "message": "<MSG>", "nonce": "<NONCE>", "signatureBase64": "<SIG_B64>" }'
# Subsequent gated calls just need the cookie:
curl -s -b cookies.txt "https://solknife.xyz/api/check?mint=<MINT>"
# Sign out (clears the cookie):
curl -s -b cookies.txt -X POST https://solknife.xyz/api/auth/logoutThe cookie is HttpOnly; Secure; SameSite=Lax and HMAC-signed with a server-side secret over (wallet, expiresAt). It can't be forged client-side and survives only as long as its embedded expiry. Lifetime: 1 hour. Browser users get this automatically; the wallet adapter calls /api/auth/challenge the moment they connect.
Discover the endpoints + cookie name from the x402.sessionAuth block of /api/manifest — that's the contract, not this paragraph.
Reference
- /api/manifest — machine-readable tool inventory
- /api/mcp — Streamable HTTP MCP endpoint (Phase 1: reads)
- How confidential transfer works on Solana — explainer for the most involved tool surface
- How SolKnife checks tokens — what the rug-checker actually verifies
- x402.org — the payment protocol we plan to integrate