← Home

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 /submit with an op discriminator.
  • 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:

  1. Receive the 402 challenge
  2. Build a Solana tx with one SystemProgram.transfer from your wallet to to for lamports
  3. Sign + submit the tx to Solana
  4. 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/logout

The 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