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

@mcp-wallet/sdk

v0.1.0

Published

Extend the MCP SDK with on-chain transaction signing capabilities

Readme

mcp-wallet

Extend the MCP SDK with on-chain transaction signing. Give your AI agents a wallet.

mcp-wallet adds server.transaction() alongside the standard server.tool(). Transaction tools go through a full safety pipeline: build -> introspect -> policy -> simulate -> sign -> submit — with audit logging, spending tracking, and webhook notifications at every step.

Install

npm install @mcp-wallet/sdk @modelcontextprotocol/sdk

For the Solana adapter:

npm install @solana/web3.js

For SQLite persistence:

npm install better-sqlite3

Quick Start

import { SigningMcpServer, maxPerTransaction, maxPerDay } from '@mcp-wallet/sdk';
import { SolanaKeypairAdapter } from '@mcp-wallet/sdk/adapters/solana';
import { Keypair } from '@solana/web3.js';
import { z } from 'zod';

const server = new SigningMcpServer(
  { name: 'My Agent', version: '1.0.0' },
  {
    wallet: new SolanaKeypairAdapter({
      keypair: Keypair.generate(),
      rpcUrl: 'https://api.devnet.solana.com',
    }),
    policies: [
      maxPerTransaction(500_000_000),  // 0.5 SOL
      maxPerDay(2_000_000_000),        // 2 SOL
    ],
  },
);

// Regular MCP tool — no signing
server.tool('get_price', 'Get token price', { token: z.string() }, async (params) => ({
  content: [{ type: 'text', text: `Price: $1.00` }],
}));

// Transaction tool — full signing pipeline
server.transaction(
  'transfer_sol',
  'Transfer SOL to a recipient',
  {
    recipient: z.string().describe('Recipient address'),
    amount: z.number().describe('Amount in lamports'),
  },
  async (params, extra) => {
    const tx = buildTransferTx(params.recipient, params.amount, extra.signerPublicKey);
    return {
      transaction: tx.serialize({ requireAllSignatures: false }),
      amount: params.amount as number,
      recipient: params.recipient as string,
    };
  },
);

Features

Policy Engine

Guard every transaction with composable rules:

import {
  maxPerTransaction,
  maxPerDay,
  allowedRecipients,
  allowedPrograms,
  rateLimit,
  replayProtection,
  customPolicy,
} from '@mcp-wallet/sdk';

const server = new SigningMcpServer(info, {
  wallet: adapter,
  policies: [
    maxPerTransaction(100_000_000),           // max per tx
    maxPerDay(1_000_000_000),                 // max per session
    allowedRecipients(['addr1', 'addr2']),     // whitelist
    allowedPrograms(['TokenkegQ...']),         // program whitelist
    rateLimit(10, 60),                        // 10 calls per 60s
    replayProtection(30),                     // block duplicate tx within 30s
    customPolicy('my-rule', (ctx) => {        // your own logic
      return { allowed: true, policyName: 'my-rule' };
    }),
  ],
});

Policies read structured metadata from the handler's return value (amount, recipient, programs) — not from raw params.

Transaction Introspection

Verify that handler-declared metadata matches the actual transaction bytes:

import type { TransactionDecoder } from '@mcp-wallet/sdk';

const decoder: TransactionDecoder = {
  chain: 'solana',
  decode(tx) {
    // Parse the raw transaction and extract facts
    return { totalAmount: 100, recipients: ['addr'], programs: ['prog'] };
  },
};

const server = new SigningMcpServer(info, {
  wallet: adapter,
  decoders: [decoder],  // mismatches reject the transaction
});

Dry-Run Mode

Preview what a transaction would do without signing:

const result = await server.dryRun('transfer_sol', {
  recipient: 'addr',
  amount: 500_000_000,
});
// result: { allowed, policyResults, simulationResult, estimatedFee }

Multi-Wallet (Multi-Chain)

One server, multiple chains:

const server = new SigningMcpServer(info, {
  wallets: [solanaAdapter, evmAdapter],
});

server.transaction('send_sol', 'Send SOL', schema, handler, { chain: 'solana' });
server.transaction('send_eth', 'Send ETH', schema, handler, { chain: 'evm' });

Key Rotation

Hot-swap wallet adapters at runtime:

server.rotateWallet('solana', newSolanaAdapter);
// Existing tools pick up the new adapter on next call

Transaction Batching

Multiple operations in one tool call:

server.batch('multi_send', 'Send to multiple recipients', schema,
  async (params) => [
    { transaction: tx1, amount: 100, recipient: 'addr-1' },
    { transaction: tx2, amount: 200, recipient: 'addr-2' },
  ],
);

Each sub-transaction goes through the full pipeline independently.

Persistence

Durable spending tracking and audit history:

import { SqlitePersistenceAdapter } from '@mcp-wallet/sdk/persistence/sqlite';

const server = new SigningMcpServer(info, {
  wallet: adapter,
  persistence: new SqlitePersistenceAdapter('./mcp-wallet.db'),
});

Spending limits survive restarts. Audit history is queryable:

const history = await server.getAuditLog().queryHistory({
  chain: 'solana',
  since: new Date('2026-03-01'),
  limit: 50,
});

Implement PersistenceAdapter for your own storage (Postgres, Redis, etc.).

Chain Query Tools

Auto-register read-only tools from a query provider:

const server = new SigningMcpServer(info, {
  wallet: adapter,
  queryProviders: [{
    chain: 'solana',
    getBalance: async (addr) => ({ balance: 5_000_000_000, decimals: 9 }),
    getTokenAccounts: async (addr) => [{ mint: 'USDC...', balance: 1000000 }],
    getTransactionHistory: async (addr, limit) => [{ txHash: '...', timestamp: '...', status: 'confirmed' }],
  }],
});
// Auto-creates: get_balance_solana, get_token_accounts_solana, get_transaction_history_solana

Webhook Notifications

Push signing events to external systems:

const server = new SigningMcpServer(info, {
  wallet: adapter,
  webhooks: [
    {
      url: 'https://hooks.slack.com/...',
      events: ['signed', 'denied'],  // or 'failed', 'all'
      secret: 'hmac-key',            // adds X-Signature-256 header
    },
  ],
});

Audit Logging

Every signing decision is recorded:

const server = new SigningMcpServer(info, {
  wallet: adapter,
  auditSink: (entry) => {
    console.log(`[${entry.signed ? 'SIGNED' : 'DENIED'}] ${entry.toolName} ${entry.txHash ?? ''}`);
  },
});

Transport

SigningMcpServer extends McpServer — it works with any MCP transport:

import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';

const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => crypto.randomUUID() });
await server.connect(transport);

See examples/solana-signing-server for a complete Express setup.

Writing a Wallet Adapter

Implement the WalletAdapter interface for any chain:

import type { WalletAdapter, SimulationResult } from '@mcp-wallet/sdk';

class MyChainAdapter implements WalletAdapter {
  readonly chain = 'my-chain';
  readonly publicKey = '0x...';

  async sign(transaction: Uint8Array): Promise<Uint8Array> { /* ... */ }
  async simulate?(transaction: Uint8Array): Promise<SimulationResult> { /* ... */ }
  async submit?(signedTransaction: Uint8Array): Promise<string> { /* ... */ }
}

The SDK ships SolanaKeypairAdapter at @mcp-wallet/sdk/adapters/solana.

API Reference

SigningMcpServer

| Method | Description | |---|---| | transaction(name, desc, schema, handler, opts?) | Register a signing tool | | batch(name, desc, schema, handler, opts?) | Register a batch signing tool | | dryRun(toolName, params) | Preview a transaction without signing | | rotateWallet(chain, newAdapter) | Hot-swap a wallet adapter | | getAuditLog() | Access audit log and history | | getSpendingTracker() | Access spending data | | getWallet(chain) | Get a wallet adapter by chain | | getSigningCapability() | Get advertised capabilities | | tool(), resource(), prompt() | Standard MCP methods (inherited) |

Built-in Policies

| Policy | Description | |---|---| | maxPerTransaction(amount) | Max spend per transaction | | maxPerDay(amount) | Max cumulative spend per session | | allowedRecipients(addrs) | Recipient whitelist | | allowedPrograms(addrs) | Program/contract whitelist | | rateLimit(maxCalls, windowSec) | Call frequency limit | | replayProtection(windowSec) | Block duplicate transactions | | customPolicy(name, fn) | Your own policy logic |

License

MIT