enwise

An MCP server for creating invoices and accepting private crypto payments via RAILGUN

enwise

Created At

Open Agents

Project Description

enwise is an open-source MCP server that turns Claude (or any MCP-compatible AI client like Cursor, Codex, Windsurf) into a complete invoicing platform. Every operation a small business needs to bill customers — creating clients, defining products, drafting invoices, attaching receipts, sending emails, tracking payments, scheduling recurring billing, generating PDFs, running revenue analytics — is exposed as an MCP tool. The user signs into a dashboard once to mint an API key, drops it into their AI client, and from then on everything happens through chat. There is no admin UI for the actual invoicing workflow; the dashboard exists only for sign-in, the API key, and a quick activity overview. 41 MCP tools cover the full surface. The standout feature is private USDC payments via RAILGUN. When a business enables private payments, enwise mints a fresh shielded wallet just for that business and prints its 0zk address on every USD invoice. Clients viewing the invoice share page see a "Pay privately" button that connects their wallet (MetaMask, Rabby, Coinbase, or WalletConnect for mobile), shields USDC directly into the recipient's 0zk address via the RAILGUN smart contract, and the funds land in a shielded balance only the recipient can decrypt. The on-chain Shield event is opaque to outside observers — they see USDC entering RAILGUN, but not who it was for. The recipient is fully non-custodial: the 12-word seed phrase is returned at setup and never persisted server-side; spending happens via Railway Wallet (RAILGUN's official client) by importing the same seed phrase. enwise itself is receive-only by design — we hold a viewing key (encrypted at rest with AES-256-GCM) so the server can verify incoming payments and mark invoices paid, but we cannot move funds even if breached. This combination — AI-native operation plus optional zkSNARK-private settlement — fits a real gap. Freelancers, consultants, and small businesses can run their billing without learning new software (just chat with the AI they already use), and clients who care about privacy (e.g. paying a lawyer, doctor, or competitor for sensitive services) get a one-click private settlement option that doesn't require either side to leave their existing wallet. Everything is open source (github.com/adipundir/enwise) and self-hostable.

How it's Made

Built on Next.js 16 (App Router, server components, server actions), Drizzle ORM on Neon Postgres for serverless data, the Model Context Protocol SDK for the AI tool surface, Auth.js v5 for OAuth (GitHub + Google), Resend for email delivery, React-PDF for invoice rendering, and Vercel Blob for attachment storage. The MCP endpoint at /api/mcp authenticates each request with a hashed bearer token (sha256, indexed lookup, constant-time compare, AES-encrypted at rest with TOKEN_ENC_KEY) and routes into per-entity tool handlers (whoami, businesses, clients, products, invoices, analytics, recurring, attachments, private_payments). Multi-business per user is first-class: clients and products are account-level, but each business has its own invoice numbering, branding, and 0zk address. Fuzzy client / product resolution uses pg_trgm trigram indexes on Postgres so "send to acme" resolves cleanly without exact-name matching.

The RAILGUN integration is where the interesting engineering lives. We use @railgun-community/wallet to mint a fresh shielded wallet per business, but configure the engine extremely conservatively: in-memory LevelDB (memdown) with no filesystem persistence so it boots cleanly on Vercel's read-only function bundles, a no-op ArtifactStore because we never generate Groth16 proofs server-side (the shield-only flag in the docs), and no provider load because wallet creation is pure cryptography that doesn't touch the chain. The engine starts in ~1ms cold; createRailgunWallet derives a Baby Jubjub
spending key + Ed25519 viewing key from a fresh BIP-39 mnemonic, encodes them into the 0zk bech32 address, and we immediately unloadWalletByID to drop the in-memory record. We extract the raw viewing private key (32 bytes hex) for our own verifier, encrypt it with AES-256-GCM, and persist that — plus
the shareable viewing key string Railway Wallet expects, returned to the user for parity. The mnemonic is returned exactly once and never written to disk. The persist step uses a conditional UPDATE (WHERE railgun_zk_address IS NULL) so concurrent setup attempts on the same business can't both win and orphan a mnemonic.

Shield-event verification runs fully stateless on the server. The browser-side PayButton (built with viem) handles wallet connect, chain switch to Sepolia, USDC balance + allowance check, asks the user to personal_sign("RAILGUN_SHIELD") so the server can derive the shieldPrivateKey via keccak256, calls a server action that builds the Shield calldata using ShieldNoteERC20 from @railgun-community/engine (recovers masterPublicKey + viewingPublicKey from the recipient's 0zk address, generates a 16-byte witness random, serializes the note, ABI-encodes the shield([request]) call), and submits via the user's wallet. After confirmation, the browser POSTs txHash + shieldRandom back; the verifier fetches the receipt with a small FallbackRpc wrapper (Alchemy primary + publicnode.com secondary, strict primary-then-secondary on error so we don't double-spend RPC quota), filters logs for the RAILGUN
proxy + Shield topic, recomputes the expected note public key from masterPublicKey + the witness random, and matches against the event commitment. No engine, no merkle tree state, no proof generation — just event decoding and one cryptographic equality check. The amount check sums (value + protocol fee) and matches against the invoice's outstanding balance in USDC's 6-decimal native units.

A few hacky-but-deliberate decisions worth calling out. (1) File uploads expose the Vercel Blob presigned-PUT mint as an MCP tool (request_attachment_upload) so the AI never has to wield the user's bearer token — the AI calls the tool, gets back a 30-min scoped client token signed by the server's BLOB_READ_WRITE_TOKEN, then PUTs bytes directly to Vercel Blob with no further auth. The user's API token never enters the AI's context. (2) The setup_private_payments tool description explicitly instructs Claude to format the mnemonic in a fenced code block with a stakes-framing warning sentence and to point users at railway.xyz for spending, because the mnemonic is shown exactly once and the cost of Claude burying or summarizing it is "the user loses their funds." (3) Engine init wraps in a try/catch that clears the cached promise on failure so a transient POI URL hiccup doesn't
brick the engine for the entire process lifetime. (4) Strict allow-list validation on RAILGUN_NETWORK env var (mainnet/sepolia only) — silent fallback to mainnet for typos like "polygon" was a fund-loss footgun that I removed. (5) End-to-end on-chain testing is wired up in scripts/railgun-onchain.mjs: generate test EOA (gitignored private key), run real Shield against Sepolia USDC, run our own verifier against the resulting on-chain event. Confirmed working with tx 0xfb096f93…b62d.

The Vercel Blob bit is genuinely useful infrastructure: the presigned-PUT pattern eliminates the "MCP server needs to know the user's bearer token" footgun that breaks on hosted MCP setups where the AI client never exposes the auth header. Worth calling out as a partner-tech win for any hackathon judge from Vercel.

background image mobile

Join the mailing list

Get the latest news and updates