PasskeyKit Integration
TypeScript MCP servers include full PasskeyKit integration for smart wallet authentication. This enables users to sign transactions using passkeys (WebAuthn) instead of secret keys.
passkey-kit v0.12.0 — Launchtube has been replaced by the OpenZeppelin Relayer . New env vars: RELAYER_URL + RELAYER_API_KEY. See Environment Variables below.
[!NOTE] PasskeyKit is only available in TypeScript servers. Python servers don’t support passkey signing.
What is PasskeyKit?
PasskeyKit is a Stellar smart wallet SDK that enables:
- Passkey-based authentication (biometric, security keys)
- Multi-signer support
- Policy enforcement
- Gasless transactions (with OZ Relayer fee sponsorship)
Generated Files
TypeScript servers include:
my-token-mcp/
├── src/lib/passkey.ts # PasskeyKit utilities
├── deploy-wallet.ts # Wallet deployment script
└── .env.example # Wallet configurationEnvironment Variables
# PasskeyKit Configuration
WALLET_WASM_HASH=b3eba29c256a2458faff84fc872226d29df37ad05a6a1c0efe2a95817d6765ec
WALLET_CONTRACT_ID=<wallet-contract-address>
WALLET_SIGNER_SECRET=S... # Signs auth entries
DEPLOYER_SECRET=S... # For wallet deployment
# OpenZeppelin Relayer (replaces Launchtube)
RELAYER_URL=https://channels.openzeppelin.com/testnet
RELAYER_API_KEY=<your-api-key>Get a free testnet API key at https://channels.openzeppelin.com/testnet/gen .
Deploying a Smart Wallet
Use the included deployment script:
Upload the Wallet WASM
The testnet WASM hash b3eba29c256a2458faff84fc872226d29df37ad05a6a1c0efe2a95817d6765ec is already deployed — skip this step if you’re using testnet.
To deploy your own:
stellar contract upload \
--wasm wallet.wasm \
--source your-key \
--network testnetSave the returned WASM hash (64-character hex string).
Run the Deployment Script
# Set your deployer secret
export DEPLOYER_SECRET=SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# Deploy the wallet
pnpm deploy-passkey <wasm-hash>Save the Output
The script outputs:
Wallet deployed successfully!
Add to your .env:
WALLET_WASM_HASH=b3eba29c256a2458faff84fc872226d29df37ad05a6a1c0efe2a95817d6765ec
WALLET_CONTRACT_ID=CDXYZ...
WALLET_SIGNER_SECRET=SDEF...How Passkey Signing Works
PasskeyKit uses a two-phase signing approach:
Phase 1: Auth Entry Signing
- Uses WALLET_SIGNER_SECRET
- Signs the invocation authorization
- Sequence-independent
Phase 2: Transaction Submission
- OZ Relayer pays transaction fees
- Submits via ChannelsTransactionResponsesignAndSendWithPasskey()
Located in src/lib/passkey.ts. Initialises PasskeyKit with the OZ Relayer and uses server.send() which handles fee sponsorship and submission:
export async function signAndSendWithPasskey(
xdr: string,
walletContractId: string
): Promise<{ hash: string; status: string }> {
// Initialize PasskeyKit with OZ Relayer
const passkey = getPasskeyWallet(walletContractId);
// Sign auth entries with wallet signer
const signerSecret = process.env.WALLET_SIGNER_SECRET;
const signerKeypair = Keypair.fromSecret(signerSecret);
const assembled = await passkey.sign(
xdr, // XDR string accepted directly (avoids stellar-sdk type collision)
{ keyId: signerKeypair }
);
// Submit via OZ Relayer — handles fee payment and polling
const response = await passkey.server.send(assembled);
// response: ChannelsTransactionResponse { hash, transactionId, status }
return {
hash: response.hash ?? response.transactionId,
status: response.status,
};
}getPasskeyWallet() — v0.12 API
import { PasskeyKit, PasskeyServer } from 'passkey-kit';
function getPasskeyWallet(walletContractId: string) {
const server = new PasskeyServer({
rpcUrl: process.env.RPC_URL!,
walletWasmHash: process.env.WALLET_WASM_HASH!,
relayerUrl: process.env.RELAYER_URL!, // OZ Relayer URL
relayerApiKey: process.env.RELAYER_API_KEY!, // OZ Relayer API key
});
return new PasskeyKit({
rpcUrl: process.env.RPC_URL!,
networkPassphrase: process.env.NETWORK_PASSPHRASE!,
walletWasmHash: process.env.WALLET_WASM_HASH!,
server,
});
}Using with MCP Tools
The sign-and-submit tool supports passkey signing:
// With passkey (walletContractId provided)
server.tool('sign-and-submit', schema, async (params) => {
if (params.walletContractId) {
return await signAndSendWithPasskey(
params.xdr,
params.walletContractId
);
}
// Regular signing...
});Example Conversation
User: Deploy a Capped token using my smart wallet
Claude: I'll deploy a Capped token using your smart wallet.
1. First, let me build the transaction...
[Calls deploy-token tool]
2. Now I'll sign and submit using your wallet...
[Calls sign-and-submit with walletContractId]
The token has been deployed:
- Address: CDXYZ...
- Type: Capped
- Transaction: abc123...Claude Desktop Configuration
Add wallet and relayer configuration to your MCP server:
{
"mcpServers": {
"my-token": {
"command": "node",
"args": ["/path/to/my-token-mcp/dist/index.js"],
"env": {
"CONTRACT_ID": "CBX7HKQ6WIOYAHS6SLL7Y3MRPTYOSGJTDL6XXYYOTEWEN3PRGK3DSVZG",
"RPC_URL": "https://soroban-testnet.stellar.org",
"NETWORK_PASSPHRASE": "Test SDF Network ; September 2015",
"WALLET_WASM_HASH": "b3eba29c256a2458faff84fc872226d29df37ad05a6a1c0efe2a95817d6765ec",
"WALLET_CONTRACT_ID": "CDXYZ...",
"WALLET_SIGNER_SECRET": "SDEF...",
"RELAYER_URL": "https://channels.openzeppelin.com/testnet",
"RELAYER_API_KEY": "your-api-key-here"
}
}
}
}Signer vs Fee Payer
| Role | Environment Variable | Purpose |
|---|---|---|
| Wallet Signer | WALLET_SIGNER_SECRET | Signs auth entries (authorizes operations) |
| Fee Sponsor | OZ Relayer (RELAYER_API_KEY) | Pays transaction fees via the relayer |
The OZ Relayer handles fee payment, so users need no XLM in their wallet.
Troubleshooting
”WALLET_WASM_HASH not set”
Set the environment variable. Use the pre-deployed testnet hash:
export WALLET_WASM_HASH=b3eba29c256a2458faff84fc872226d29df37ad05a6a1c0efe2a95817d6765ec“Wallet contract not found”
The wallet contract may not be deployed. Run:
pnpm deploy-passkey <wasm-hash>“Auth entry signing failed”
Check that WALLET_SIGNER_SECRET is a valid Stellar secret key (starts with ‘S’).
”Relayer API key expired or invalid”
Get a new free testnet API key at https://channels.openzeppelin.com/testnet/gen and update RELAYER_API_KEY.
”Fee payer account not found”
With the OZ Relayer, users do not need a funded account — the relayer sponsors fees. If you see this error, ensure RELAYER_URL and RELAYER_API_KEY are set correctly.