Configuration
Configure wallets, capabilities, tokens, protocols, and custom tools
Server Setup
Create a server with a name and version:
import { WdkMcpServer } from '@tetherto/wdk-mcp-toolkit'
const server = new WdkMcpServer('my-server', '1.0.0')The WdkMcpServer extends McpServer from the official @modelcontextprotocol/sdk with WDK-specific capabilities. All standard MCP server features are available.
Confirmation and Agent Access
The server enables MCP elicitation by default. sendTransaction, transfer, sign, swap, bridge, and lending execution tools request approval through the MCP client before continuing. The client must support elicitation and present the request to the user. Fiat buy and sell return provider URLs without this confirmation step; the user completes the purchase or sale with the provider.
For a programmatic server, the setting is options.capabilities.elicitation:
const server = new WdkMcpServer('my-server', '1.0.0', {
capabilities: { elicitation: true }
})The serve command uses WDK_MCP_ELICITATION; the exact string "false" disables elicitation. A custom server script must set the constructor option itself.
Disabling elicitation automatically accepts confirmation requests. It removes the human approval step; it does not add policy checks or spending limits. If your client cannot support confirmation, expose only read-only tools or provide authorization outside the agent's control before enabling writes.
These controls apply to the exposed MCP tools. They do not isolate signing keys from code that can access the server's seed, configuration, or runtime. The toolkit does not register transaction policies automatically. The policy engine is experimental and not production ready, and its local method checks are not a substitute for that isolation.
MCP tool annotations and agent instructions describe expected behavior; they do not enforce authorization. For the separate WDK CLI daemon, see its security model.
Wallet Configuration
Enable WDK
Pass a BIP-39 mnemonic resolved by your application to useWdk(). See the custom server example for a command-based loader:
server.useWdk({ seed })The mnemonic is used for key derivation across all registered blockchains. If config.seed is omitted or empty, useWdk() falls back to WDK_SEED. The API does not resolve WDK_SEED_COMMAND or WDK_SEED_FILE; those sources are implemented by the built-in serve command.
Never hardcode seed phrases in source code or client configuration. Prefer a trusted secret-manager command. Keep credential files out of version control and restrict access; gitignore does not prevent local processes from reading them.
Seed Sources for serve
The built-in serve command selects the first environment variable with a nonempty value in this order:
WDK_SEED: the mnemonic supplied directly.WDK_SEED_COMMAND: a trusted shell command whose stdout contains the mnemonic.WDK_SEED_FILE: a UTF-8 file containing the mnemonic.
Prefer WDK_SEED_COMMAND to putting the mnemonic in client configuration. Replace /absolute/path/to/trusted-seed-loader below with your own secret-manager executable; WDK does not supply it. Authenticate the secret manager before starting the MCP client. The command must work without interactive input and print only the mnemonic to stdout.
WDK_SEED_COMMAND="/absolute/path/to/trusted-seed-loader" node bin/index.js serveUnset WDK_SEED when selecting the command source, and unset both preceding variables when selecting a file. Use an absolute file path: relative paths resolve against the server process's working directory.
serve trims the selected value or output. An empty environment value is skipped, but a whitespace-only value or empty command/file output does not trigger fallback to another source; it starts pricing-only mode. A command failure or unreadable file aborts startup. A nonempty invalid mnemonic also aborts startup.
The command runs through a shell and blocks startup until it finishes. serve does not configure a timeout, so a hung command prevents the MCP server from connecting. Use only commands and configuration controlled by your application, never agent-generated command text. Do not include the mnemonic in diagnostics or errors: command failures can reach client logs.
After resolving a nonempty seed, serve removes all three source variables from its own environment as it initializes WDK. This does not delete a seed file, change the parent environment, or isolate the mnemonic from code running in the server process.
Register Wallets
Register a wallet module for each blockchain you want to support:
import WalletManagerEvm from '@tetherto/wdk-wallet-evm'
import WalletManagerBtc from '@tetherto/wdk-wallet-btc'
import WalletManagerSolana from '@tetherto/wdk-wallet-solana'
// EVM chains - one module handles all EVM networks
server.registerWallet('ethereum', WalletManagerEvm, {
provider: 'https://eth-mainnet.g.alchemy.com/v2/KEY'
})
server.registerWallet('polygon', WalletManagerEvm, {
provider: 'https://polygon-rpc.com'
})
// Bitcoin
server.registerWallet('bitcoin', WalletManagerBtc, {
network: 'bitcoin',
host: 'electrum.blockstream.info',
port: 50001
})
// Solana
server.registerWallet('solana', WalletManagerSolana, {
provider: 'https://api.mainnet-beta.solana.com'
})Each registerWallet() call registers the chain name and makes it available to all wallet tools. For configuration details of each wallet module, see the Wallet Modules documentation.
Capabilities
Enable optional capabilities before registering their tools:
| Capability | Method | Requirement | Unlocks |
|---|---|---|---|
| Pricing | server.usePricing() | None | PRICING_TOOLS (2 tools) |
| Indexer | server.useIndexer({ apiKey }) | WDK API key | INDEXER_TOOLS (2 tools) |
| Swap | server.registerProtocol(chain, label, SwapProtocol) | Swap module installed | SWAP_TOOLS (2 tools) |
| Bridge | server.registerProtocol(chain, label, BridgeProtocol) | Bridge module installed | BRIDGE_TOOLS (2 tools) |
| Lending | server.registerProtocol(chain, label, LendingProtocol) | Lending module installed | LENDING_TOOLS (8 tools) |
| Fiat | server.registerProtocol(chain, label, FiatProtocol, config) | Fiat module installed | FIAT_TOOLS (8 tools) |
Pricing
Fetches live prices from Bitfinex. No API key needed.
server.usePricing()Indexer
Enables querying token balances and transfer history for any address. Requires an API key.
server.useIndexer({ apiKey: process.env.WDK_INDEXER_API_KEY })Protocols
Protocol modules are registered per-chain. This example uses MoonPay:
Do not register Velora beta.8 and beta.9, USD₮0 bridge beta.10, or Aave beta.7 with EVM wallet beta.19. These adapters reject the provider on manager-derived accounts, so the example below omits them. Verify the installed versions before enabling their tools; see EVM provider compatibility. A separate direct account does not inherit the server's registered account controls.
import MoonPayProtocol from '@tetherto/wdk-protocol-fiat-moonpay'
server.registerProtocol('ethereum', 'moonpay', MoonPayProtocol, {
apiKey: process.env.MOONPAY_API_KEY
})Token Management
Default Tokens
DEFAULT_TOKENS registers Tether-family tokens for configured chains, including USD₮ and XAU₮ on Ethereum. Query the registry for the available machine symbols:
server.getRegisteredTokens('ethereum') // ['USDT', 'XAUT']Custom Tokens
Register additional tokens with registerToken():
server.registerToken('ethereum', 'WETH', {
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
decimals: 18
})Registered tokens are available to all tools that accept a token parameter (getTokenBalance, transfer, quoteTransfer, swap, etc.).
Tool Registration
Built-in Tool Arrays
Each category exports three arrays for fine-grained control:
| Export | Contents |
|---|---|
WALLET_TOOLS | All 11 wallet tools |
WALLET_READ_TOOLS | 7 read-only wallet tools |
WALLET_WRITE_TOOLS | 4 wallet tools that modify state |
PRICING_TOOLS | All 2 pricing tools |
INDEXER_TOOLS | All 2 indexer tools |
SWAP_TOOLS | All 2 swap tools |
SWAP_READ_TOOLS | 1 read-only swap tool |
SWAP_WRITE_TOOLS | 1 swap tool that modifies state |
BRIDGE_TOOLS | All 2 bridge tools |
BRIDGE_READ_TOOLS | 1 read-only bridge tool |
BRIDGE_WRITE_TOOLS | 1 bridge tool that modifies state |
LENDING_TOOLS | All 8 lending tools |
LENDING_READ_TOOLS | 4 read-only lending tools |
LENDING_WRITE_TOOLS | 4 lending tools that modify state |
FIAT_TOOLS | All 8 fiat tools |
FIAT_READ_TOOLS | 6 read-only fiat tools |
FIAT_WRITE_TOOLS | 2 fiat tools that modify state |
Read-Only Mode
To expose only read-only MCP tools to an AI agent:
import {
WALLET_READ_TOOLS,
PRICING_TOOLS,
INDEXER_TOOLS,
SWAP_READ_TOOLS
} from '@tetherto/wdk-mcp-toolkit'
server.registerTools([
...WALLET_READ_TOOLS,
...PRICING_TOOLS,
...INDEXER_TOOLS,
...SWAP_READ_TOOLS
])Individual Tool Registration
You can also import and register tools individually:
import { getAddress, getBalance, getCurrentPrice } from '@tetherto/wdk-mcp-toolkit'
server.registerTools([getAddress, getBalance, getCurrentPrice])Custom Tools
Add your own MCP tools alongside the built-in ones using the standard registerTool() method (inherited from McpServer). See the MCP SDK tools documentation for full details.
server.registerTool(
'myCustomTool',
{
title: 'My Custom Tool',
description: 'Description of what this tool does',
inputSchema: z.object({
param: z.string().describe('A required parameter')
}),
outputSchema: z.object({
result: z.string()
}),
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false
}
},
async ({ param }) => {
return {
content: [{ type: 'text', text: `Result: ${param}` }],
structuredContent: { result: param }
}
}
)Environment Variables
For serve, seed variables are optional individually. Wallet tools require a nonempty resolved seed; see seed sources and precedence. Custom servers must resolve command or file sources in application code before calling useWdk().
| Variable | Required | Description |
|---|---|---|
WDK_SEED | No | Direct mnemonic; first source checked by serve, and fallback for useWdk({}) |
WDK_SEED_COMMAND | No | Trusted command whose stdout supplies the mnemonic to serve |
WDK_SEED_FILE | No | UTF-8 mnemonic file read by serve when both preceding variables are unset or empty |
WDK_INDEXER_API_KEY | No | API key for WDK Indexer |
MOONPAY_API_KEY | No | API key for MoonPay fiat on/off-ramp |
Security Checklist
Self-custodial wallets require careful key management. Follow these guidelines to protect user funds.
- Use a dedicated development wallet - Never use production wallets with real funds for testing
- Use a secret-manager command - Keep the mnemonic out of source code and client configuration; configure only a trusted loader command and authenticate it before startup
- Limit the exposed tools - Use read-only tool arrays when writes are unnecessary. Before enabling writes, review confirmation and agent access.
- Await
server.close()on shutdown - WDK disposal runs before the server clears its WDK reference and closes the transport. A disposal error rejects before those later steps. Successful cleanup does not guarantee erasure of every seed or key copy in process memory. - Use
stdiotransport - The default transport communicates only with the local AI client process - Review MCP annotations - Tools declare
readOnlyHintanddestructiveHintso clients can warn users appropriately - Keep client configuration gitignored - Exclude files containing wallet credentials and restrict which processes can read them