npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tokemak/autopools-mcp

v0.1.2

Published

Tokemak Autopools MCP server (internal): read pool/user data, prepare transactions, and execute them via an agent-owned Privy wallet or the user's own wallet signing in the browser.

Downloads

286

Readme

@tokemak/autopools-mcp

Internal MCP server for Tokemak Autopools: Claude (or any MCP client) reads pool/user data and prepares deposits, withdrawals, staking, and claims through @tokemak/autopoolsself-custody only. The server holds no keys and can never sign: autopools_request_signature mints a URL to the agentic console's /sign page, which rebuilds the plan in the browser with live quotes, shows drift vs. what the user confirmed with the agent, and signs with the user's own Privy-connected wallet (embedded or external).

Chains: mainnet (1), Base (8453), Arbitrum (42161). 17 tools; stdio transport; built on @modelcontextprotocol/server v2 (URL elicitation with plain-URL fallback). Agent instructions ship in SKILL.md.

Quickstart (Claude Code)

pnpm --filter @tokemak/autopools-mcp build
claude mcp add tokemak -- node <repo>/packages/autopools-mcp/dist/cli.js

Read-only works with zero configuration. For the full write flow, two env vars:

TOKEMAK_MCP_MODE=write                           # narrow-by-default: read unless set
TOKEMAK_DASHBOARD_URL=https://<agentic-deploy>   # signing/connect pages

The user's wallet is discovered via the wallet_connect tool (they connect in the browser and share only their address), or pinned with TOKEMAK_USER_ADDRESS.

Optional: TOKEMAK_RPC_<chainId> (defaults are publicnode — the canonical public RPCs gas-cap the lens reads), TOKEMAK_MCP_CHAINS=1,8453,42161, TOKEMAK_MCP_ALLOW_FORCE=1 (exposes deposit-gating bypass), TOKEMAK_MCP_STATE_DIR (default ~/.tokemak-mcp).

Install in the Claude Desktop app (regular chat, no Claude Code)

Works with the normal Claude Desktop app — you chat, Claude uses the Tokemak tools. One-time setup, ~5 minutes (also on the console's /setup page):

  1. Get the server built (needs Node ≥ 20 and pnpm once):

    git clone https://github.com/Tokemak/v2-apps && cd v2-apps
    pnpm install && pnpm --filter @tokemak/autopools-mcp build
  2. Open the Desktop config: Claude Desktop → Settings → Developer → Edit Config. That opens claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\).

  3. Add the server (use the absolute path from step 1):

    {
      "mcpServers": {
        "tokemak": {
          "command": "node",
          "args": [
            "/ABSOLUTE/PATH/TO/v2-apps/packages/autopools-mcp/dist/cli.js"
          ],
          "env": {
            "TOKEMAK_MCP_MODE": "write",
            "TOKEMAK_DASHBOARD_URL": "https://<agentic-deploy>"
          }
        }
      }
    }

    Read-only browsing (no transactions at all): delete the env block.

  4. Fully quit and reopen Claude Desktop (⌘Q / quit from the tray — the window close button isn't enough). The Tokemak tools appear under the tools icon in the chat input.

  5. First chat: say "Check my Tokemak wallet status" — Claude calls wallet_status. To link your wallet, ask it to run wallet_connect, click the URL it returns, and connect in the browser. From then on: ask for a deposit/withdraw → Claude shows you the plan → it hands you a signing link → you review the live numbers and sign in your own wallet. Approve each tool call when Desktop prompts you.

Notes for Desktop: signing/connect links arrive as plain URLs in the chat (Desktop doesn't render MCP link dialogs — the server detects this and falls back automatically), and nothing ever moves without your signature in the browser.

Hosted endpoint — /api/mcp (claude.ai web, zero install)

The agentic app also mounts a hosted, stateless flavor of this server at /api/mcp (Streamable HTTP via the SDK's web-standard transport, one instance per request — serverless-safe). Connect from claude.ai (Settings → Connectors → Add custom connector, paste https://<agentic-deploy>/api/mcp), from Claude Desktop's connectors, or from Claude Code (claude mcp add --transport http tokemak <url>/api/mcp).

How it differs from the local stdio server:

  • Same reads, same prepare tools — but prepare takes the user's wallet address as an explicit owner input (there's no per-user memory), and returns the signing URL inline with the envelope. The /sign page pins the signature to owner and rebuilds with live quotes as always.

  • Signing links are short when the deployment has a store: /sign#p=pln_… (~47 chars) instead of /sign#i=<~700 chars of base64>. The intent is parked under its planId and /sign fetches it from /api/sign/intent; the page re-validates it with the same codec checks rather than trusting what it gets back. Parking costs nothing in exposure — the hosted server built that plan, so it has already seen every field, and a link-holder could always read the same bytes out of the fragment. Without a store (and always on the local stdio server, where "the intent never touches a server" is a real property) the link stays self-contained. A store failure falls back to the long link rather than failing the prepare: a signable plan beats a tidy URL.

  • No plan store, no callback listener, no plan_status / autopools_request_signature — hosted lambdas share no disk and hold no state. Confirmation flows the other way: with a store, autopools_await_signature { planId, chainId, owner } blocks until the user is done and returns the outcome; without one the page shows the hashes and the user gives them to Claude. Either way tx_status can verify any hash on-chain.

    • autopools_await_signature re-checks every reported hash against the chain before calling anything sent: the record is written by whoever holds the signing link, so a hash the chain does not attribute to the plan's owner comes back as unverified — never as success. That is strictly stronger than trusting a hash a human pasted into chat.
    • Outcomes are sent (with per-tx fromOwner and receipt status), rejected (user declined, nothing sent), failed, unverified, or pending — call again, signing takes as long as the user takes.
  • URL-mode elicitation is wired but dormant on this transport. The consent dialog (the client renders the destination and returns an accept/decline) needs the 2026-07-28 multi-round-trip path, where the client fulfils the input_required itself and echoes back a requestState — the only shape a server with no memory can serve. The plumbing is here: an HMAC requestState (TOKEMAK_REQUEST_STATE_KEY, ≥32 bytes) seals the planId/nonce so a re-entry lands on the plan the user is actually looking at rather than rebuilding under a new id, and re-entry is checked before any rebuild for that reason.

    It does not activate today: @modelcontextprotocol/[email protected] negotiates at most 2025-11-25, and on that era the per-request capability envelope isn't surfaced, so the gate never opens. Fulfilling a 2025-era input_required needs the SDK's legacy shim, which re-enters in process over a held connection — impossible when every request is a fresh lambda, which is why legacyShim: false is set rather than left to hang. Every hosted client therefore gets the pre-rendered markdown link, verified by scripts/smoke-http-elicit.mjs.

    The stdio server does hold a connection, so it can elicit — but only for a client that declares elicitation: { url: {} }. A bare elicitation: {} is form mode per spec, and current Claude Code declares exactly that; sending it a URL dialog makes the SDK refuse the leg and fail the whole tool call, so those clients get the markdown link instead. scripts/smoke-elicit.mjs covers all three shapes (none / url+form / bare form-only).

  • wallet_connect ships here too, and wallet_status returns the same link inline as connectUrl whenever no address is known — so a client that never thinks to call wallet_connect still gets a link instead of a dead end. No URL elicitation on this transport: a dialog's reply arrives as a separate HTTP request that a stateless instance cannot match to anything.

  • The handoff completes on its own when the deployment has a connect store (BLOB_READ_WRITE_TOKEN): wallet_connect also returns a nonce, the intent is marked claimable, the /connect page POSTs the address to its own origin's /api/connect/claim (never a URL from the intent — that fragment is attacker-suppliable, and a post target inside it would be a zero-click address exfiltrator wearing the console's domain), and wallet_await_connection { nonce } blocks up to ~45s and returns it. pending just means the user hasn't clicked yet — call again. Without a store none of that is mounted and the page shows the address with a copy button for the user to paste, which is all a stateless deployment can honestly offer.

    • The nonce is a bearer secret (16 random bytes): first claim wins and is never overwritten, records expire after 10 minutes, claiming is same-origin POST only, and expired records are swept on write. A stolen link buys plans built for the thief's address, which /sign then refuses to sign with the user's wallet.
    • The store is injected (buildHostedServer(config, { connectStore })), so the SDK's runtime closure stays viem + fetch — the Blob dependency lives in the app. The address is the blob's pathname, so a poll is one list against the API and never a content read through the CDN (whose minimum cache is 60s and would serve stale misses for most of the wait).
  • Zero config on Vercel: the route defaults the signing-page origin to its own deployment (TOKEMAK_DASHBOARD_URL overrides). The endpoint is unauthenticated — everything it serves is public on-chain data and unsigned calldata the user must sign themselves; there is nothing to steal and no signer to abuse.

Verify a deployment with node scripts/smoke-http.mjs https://<deploy>/api/mcp.

Design notes (the parts that bite)

  • Prepare-only by construction: write tools return a Morpho-style envelope (planId, per-tx descriptions, expectations, simulation, warnings[] with halt semantics) of unsigned transactions. The only execution path is the human signing in their browser — there is no server-side signer to compromise.
  • Plan store is disk-persisted (~/.tokemak-mcp) with validated state transitions (prepared → awaiting_signature → sent → confirmed/failed); parallel MCP processes share it.
  • Simulation: single txs via eth_call from the owner; [approval, tx] sequences via eth_simulateV1 (simulateCalls), degrading to a loud "skipped" warning — never a silent pass.
  • The signing page trusts nothing: intents carry raw SDK params and the page rebuilds the plan client-side (fresh quotes — zap quotes live ~60s, so cached calldata is never signed), refuses non-registry autopools and expired links (2h), renders warnings and custom receivers loudly, and pins the sending account to the plan's owner.
  • Callback loop: signing results return over a loopback listener (127.0.0.1, unguessable planId/nonce auth, Host + content-type enforced, reported hashes verified on-chain against the plan before a plan may confirm); if unreachable, the page shows the hashes for paste-back and plan_status/tx_status still resolve on-chain.
  • msg.sender discipline: owner is always the connected user wallet, never a tool input (staking credits the sender; withdrawals burn the sender's shares). receiver on withdraw is the one free recipient field and is loudly flagged.

Verification

pnpm --filter @tokemak/autopools-mcp test    # unit: codec, serializers, store, locks
pnpm --filter @tokemak/autopools-mcp smoke   # live stdio: reads (3 chains), prepare
                                             # envelopes + gating on Base, signing
                                             # flow incl. callback, elicitation paths

Still pending before rollout: one end-to-end browser signing against the deployed console with a real wallet.

The console (apps/agentic)

/ — your positions, rewards, browser-signed tx log with live receipts, and onchain history. /sign — the signing surface described above. /connect — hands your address to your agent, serving all three flavors from one page: a claimable intent (hosted + store) posts as soon as a wallet connects, a callbackUrl intent (local MCP) keeps its explicit share button, and an intent with neither shows the address with a copy button for you to paste. /api/connect/claim — where the first of those posts. /setup — the install walkthrough. Env: NEXT_PUBLIC_PRIVY_APP_ID, optional BLOB_READ_WRITE_TOKEN (enables the automatic handoff), optional NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID and NEXT_PUBLIC_RPC_<chainId>.