Production-ready Rust library and MCP server for Visa's Trusted Agent Protocol (TAP), enabling AI agents to securely authenticate with merchants and execute payment transactions.
- Overview
- Quick Start β‘
- Installation
- Features
- Architecture
- Examples
- Documentation
- Development
- Security
- Testing
The TAP-MCP Bridge acts as a protocol adapter, translating between:
- MCP layer: Application-layer tool calls (JSON-RPC 2.0 over stdio/HTTP)
- TAP layer: Transport-layer cryptographic authentication (RFC 9421 HTTP Message Signatures)
β Authenticate with TAP-protected merchants using Ed25519 cryptographic signatures β Execute secure payment transactions with PCI-DSS compliant encryption β Browse merchant catalogs with verified agent identity β Maintain session state across multi-step interactions
| Metric | Status |
|---|---|
| TAP Compliance | 100% (18/18 requirements) |
| Test Coverage | 122 tests passing (104 unit + 18 binary) |
| Security | PCI-DSS compliant, RFC 7516 JWE encryption |
| Code Quality | 0 warnings, Microsoft Rust Guidelines compliant |
| Platform Support | macOS, Linux, Windows |
1. Install the MCP server:
cargo install --path tap-mcp-server2. Configure your MCP client (config file location varies by client):
{
"mcpServers": {
"tap": {
"command": "tap-mcp-server",
"env": {
"TAP_AGENT_ID": "your-agent-id",
"TAP_AGENT_DIRECTORY": "https://your-agent-directory.com",
"TAP_SIGNING_KEY": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
}
}
}3. Start your MCP client - The TAP server will be available with three tools:
checkout_with_tap- Execute payment with TAP authenticationbrowse_merchant- Browse merchant catalogverify_agent_identity- Health check and agent verification
Add to your Cargo.toml:
[dependencies]
tap-mcp-bridge = "0.1.0"
ed25519-dalek = "2.2"
tokio = { version = "1.48", features = ["rt", "macros"] }Execute a TAP checkout:
use ed25519_dalek::SigningKey;
use tap_mcp_bridge::{
mcp::{checkout_with_tap, CheckoutParams},
tap::TapSigner,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize TAP signer
let signing_key = SigningKey::from_bytes(&[0u8; 32]);
let signer = TapSigner::new(
signing_key,
"agent-123",
"https://agent.example.com"
);
// Execute checkout
let params = CheckoutParams {
merchant_url: "https://merchant.example.com".to_string(),
consumer_id: "user-456".to_string(),
intent: "payment".to_string(),
payment_method: None,
merchant_public_key: None,
};
let result = checkout_with_tap(&signer, params).await?;
println!("Transaction status: {}", result.status);
Ok(())
}Run examples:
cargo run --example basic_checkout
cargo run --example apc_generation
cargo run --example browse_catalogThis is a Cargo workspace with two crates:
tap-mcp-bridge/
βββ tap-mcp-bridge/ # π Library - TAP protocol implementation
βββ tap-mcp-server/ # π§ Binary - MCP server for AI agents
Add to your Cargo.toml:
[dependencies]
tap-mcp-bridge = "0.1.0"
ed25519-dalek = "2.2" # For signing key generationOption 1: Install from source
git clone https://github.com/bug-ops/tap-mcp-bridge.git
cd tap-mcp-bridge
cargo install --path tap-mcp-serverOption 2: Build locally
cargo build --release --bin tap-mcp-server
# Binary location: ./target/release/tap-mcp-server| Variable | Required | Format | Example |
|---|---|---|---|
TAP_AGENT_ID |
β Yes | Alphanumeric + -_ (1-64 chars) |
agent-123 |
TAP_AGENT_DIRECTORY |
β Yes | HTTPS URL | https://agent.example.com |
TAP_SIGNING_KEY |
β Yes | 64 hex characters (32 bytes) | 0123... |
RUST_LOG |
β No | Log level | info (default) |
Missing environment variable
Error: TAP_AGENT_ID environment variable is required
Solution: Set all required environment variables in your MCP client config
Invalid signing key
Error: TAP_SIGNING_KEY must be exactly 64 hex characters
Solution: Generate a valid Ed25519 key and encode as hex (64 chars)
# Generate key (example - use secure key management in production)
openssl genpkey -algorithm Ed25519 -out private.pem
# Convert to hex format (64 chars)Invalid URL
Error: TAP_AGENT_DIRECTORY must be an HTTPS URL
Solution: Use HTTPS URL (not HTTP) for agent directory
| Feature | RFC | Status |
|---|---|---|
| HTTP Message Signatures | RFC 9421 | β Ed25519 |
| JWK Thumbprint | RFC 7638 | β SHA-256 |
| Content-Digest | RFC 9530 | β SHA-256 |
| JWE Encryption | RFC 7516 | β A256GCM + RSA-OAEP-256 |
| Signature Expiration | TAP Spec | β 8-minute window |
| Replay Protection | TAP Spec | β UUID v4 nonce |
| Interaction Type Tags | TAP Spec | β
agent-browser-auth, agent-payer-auth |
| Public Key Directory | TAP Spec | β JWKS endpoint |
| ID Token (JWT) | TAP Spec | β Consumer authentication |
| ACRO | TAP Spec | β Consumer Recognition Object |
| APC | TAP Spec | β Payment Container with JWE |
π PCI-DSS Compliance - JWE encryption for payment data (A256GCM + RSA-OAEP-256) π Defense-in-Depth - Application-level JWE + transport-level TLS π Memory Safety - Automatic zeroization of sensitive data π Type Safety - Rust's ownership model prevents cryptographic errors π Input Validation - HTTPS-only URLs, consumer ID format validation π Secure Key Management - No plaintext secrets in logs or errors
β‘ Async/Await - Built on Tokio for concurrent operations
π Comprehensive Documentation - Rustdoc with examples for all public APIs
π― Type-Safe Errors - Context-rich errors via thiserror
π§ͺ Extensive Testing - 122 automated tests (104 unit + 18 binary)
β
Zero Warnings - Strict clippy lints enforced
π Cross-Platform - Works on macOS, Linux, Windows
π Structured Logging - JSON or pretty-printed logs with contextual fields π Request Correlation - Automatic span tracking for all operations β€οΈ Health Checks - Built-in health verification via MCP tool π Instrumentation - Distributed tracing-compatible spans π― Contextual Fields - merchant_url, consumer_id, nonce, interaction_type in all logs
graph TB
Agent[AI Agent / MCP Client]
Bridge[TAP-MCP Bridge<br/>this workspace]
Library[tap-mcp-bridge<br/>Library]
Binary[tap-mcp-server<br/>Binary]
Tools[MCP Tools<br/>checkout, browse]
TAPClient[TAP Signatures<br/>RFC 9421, Ed25519, JWE]
Merchant[TAP Merchant<br/>E-commerce]
Agent -->|"MCP Protocol<br/>(JSON-RPC 2.0)"| Binary
Binary --> Library
Library --> Tools
Library --> TAPClient
TAPClient -->|"HTTPS + TAP Signatures<br/>(Ed25519, JWE)"| Merchant
style Agent fill:#e1f5ff
style Bridge fill:#fff4e1
style Library fill:#e8f5e9
style Binary fill:#e8f5e9
style Tools fill:#f0f0f0
style TAPClient fill:#f0f0f0
style Merchant fill:#ffe1e1
- MCP Server (
tap-mcp-server) - Standalone binary exposing TAP as MCP tools - Protocol Adapter - Translates between MCP and TAP protocols
- TAP Client - Generates RFC 9421 HTTP Message Signatures with Ed25519
- Cryptographic Engine - JWE encryption, signature generation, key management
The project uses a clean separation between library and binary:
| Crate | Purpose | Dependencies |
|---|---|---|
tap-mcp-bridge |
Reusable TAP protocol library | Lean (TAP protocol only) |
tap-mcp-server |
MCP server for AI agents | Rich (logging, CLI, error handling) |
Benefits:
- Library consumers don't pull unnecessary binary dependencies
- Binary can use
anyhow,tracing-subscriber,clapindependently - Clear separation: reusable logic vs application
The examples/ directory contains complete working examples:
| Example | Description |
|---|---|
| basic_checkout.rs | Simple checkout flow with error handling |
| browse_catalog.rs | Browsing merchant catalogs |
| apc_generation.rs | Payment container with JWE encryption |
| acro_generation.rs | Consumer recognition object |
| signature_generation.rs | Low-level TAP signatures |
| jwks_generation.rs | Public key directory |
| id_token_generation.rs | JWT ID tokens |
use tap_mcp_bridge::tap::{
TapSigner,
apc::{CardData, PaymentMethod, RsaPublicKey},
};
// Create payment method
let card = CardData {
number: "4111111111111111".to_owned(),
exp_month: "12".to_owned(),
exp_year: "25".to_owned(),
cvv: "123".to_owned(),
cardholder_name: "John Doe".to_owned(),
};
// Load merchant's public key
let merchant_pem = std::fs::read("merchant_key.pem")?;
let merchant_key = RsaPublicKey::from_pem(&merchant_pem)?;
// Generate APC with JWE-encrypted payment data
let nonce = uuid::Uuid::new_v4().to_string();
let apc = signer.generate_apc(&nonce, &PaymentMethod::Card(card), &merchant_key)?;
println!("Payment data encrypted with JWE");# Basic checkout flow
cargo run --example basic_checkout
# Payment container with JWE
cargo run --example apc_generation
# Browse merchant catalog
cargo run --example browse_catalog
# All examples
cargo run --example signature_generation
cargo run --example acro_generation
cargo run --example jwks_generationGenerate and view the full API documentation:
cargo doc --no-deps --all-features --open| Guide | Description |
|---|---|
| TAP Specification | Detailed TAP protocol implementation guide |
| MCP Integration | MCP protocol and tool integration |
| Observability Guide | Logging, monitoring, and health checks |
| Library Overview | Architecture overview and integration guide |
| Error Types | All error variants with recovery strategies |
Comprehensive testing analysis available in .local/:
TESTING_INDEX.md- Navigation guidetesting-summary-phase6.md- Executive summarytesting-report-phase6.md- Detailed 10,000+ word analysis
- Rust 1.75+ (Edition 2024)
- Cargo
- Optional:
cargo-make,cargo-nextest,cargo-deny
# Clone the repository
git clone https://github.com/bug-ops/tap-mcp-bridge.git
cd tap-mcp-bridge
# Install development tools (recommended)
cargo install cargo-make cargo-nextest cargo-deny cargo-udepsEnable sccache for dramatically faster incremental builds:
# Install sccache
cargo install sccache
# Configure Cargo (~/.cargo/config.toml)
[build]
rustc-wrapper = "sccache"
[env]
SCCACHE_CACHE_SIZE = { value = "10G", force = true }Results:
- First build: ~22s
- Cached rebuild: ~2-3s (10x faster!)
Using cargo-make (recommended):
# Quick pre-commit checks (format, clippy, test, deny)
cargo make pre-commit
# Full verification suite
cargo make verify
# Individual tasks
cargo make format # Format with nightly rustfmt
cargo make clippy # Strict lint checks
cargo make test # Run tests with nextest
cargo make deny # Security and license checks
cargo make doc-open # Build and open docsDirect cargo commands:
# Build workspace
cargo build --workspace
# Run tests (109 passing)
cargo nextest run --workspace --all-features
cargo test --doc --workspace
# Code quality
cargo clippy --workspace --all-features -- -D warnings
cargo +nightly fmt --all
# Security checks
cargo deny check
cargo audit
cargo +nightly udeps --all-targets
# Build binary
cargo build --release --bin tap-mcp-serverThis project follows the Microsoft Rust Guidelines for soundness and idiomatic design.
Key principles:
β No unsafe code - 100% safe Rust β Strong types - Domain-specific types instead of primitives β Comprehensive error handling - Context-rich errors with recovery guidance β API Guidelines compliance - Following Rust API Guidelines β Zero warnings - Strict clippy lints enforced
| Security Layer | Implementation |
|---|---|
| Encryption Algorithm | A256GCM (content) + RSA-OAEP-256 (key) |
| Standard Compliance | PCI-DSS, RFC 7516 JWE |
| Defense Strategy | Application-level JWE + TLS transport |
| Memory Safety | Automatic zeroization (card numbers, CVV) |
| Logging | Payment data never logged or in errors |
| Feature | Implementation |
|---|---|
| Signature Expiration | 8-minute validity window (TAP requirement) |
| Replay Protection | Unique UUID v4 nonce per request |
| Timestamp Validation | Created/expires enforce time windows |
| URL Validation | HTTPS-only, no localhost |
| Input Sanitization | Consumer IDs, URLs, payment data validated |
| Nonce Correlation | Same nonce across signature, ID token, ACRO, APC |
π Private Keys: Store Ed25519 signing keys securely (HSM, KMS, encrypted vault) π Public Keys: Distribute via HTTPS-only JWKS endpoint π Key Rotation: Supported through JWKS updates π Key Identification: JWK Thumbprint (RFC 7638) for each key
- β
cargo deny check- No security advisories - β
cargo audit- No known vulnerabilities - β All dependencies from trusted sources (crates.io)
- β License compliance verified
# Run all tests (109 tests, 100% passing)
cargo nextest run --workspace --all-features
# Run doc tests
cargo test --doc --workspace
# Run with coverage
cargo llvm-cov nextest --workspace --all-features
# Test specific module
cargo nextest run --workspace tap::apcβ TAP Protocol Compliance - RFC 9421, RFC 7638, RFC 7516 validation β Cryptographic Operations - Signature generation, JWE encryption β Security Controls - Input validation, HTTPS enforcement, URL sanitization β Binary Configuration - Environment variable validation β Error Handling - All error paths covered
See .local/TESTING_INDEX.md for detailed testing documentation.
Contributions are welcome! Before contributing:
- Review the architecture in library documentation
- Check TAP Specification for protocol details
- Ensure all tests pass:
cargo nextest run --workspace - Run quality checks:
cargo make verify - Format code:
cargo +nightly fmt --all
- Create a feature branch
- Implement changes with tests
- Run
cargo make pre-commit - Open PR with description
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
- TAP Specification - Official Visa documentation
- Visa Reference Implementation - Python reference code
- TAP Implementation Guide - This project's comprehensive guide
- RFC 9421 - HTTP Message Signatures
- RFC 7638 - JWK Thumbprint
- RFC 7516 - JSON Web Encryption (JWE)
- RFC 9530 - Digest Fields
- RFC 8032 - Ed25519 Signature Algorithm
- MCP Documentation - Anthropic's Model Context Protocol
- MCP Integration Guide - This project's integration guide
- rmcp Crate - Rust MCP SDK
- Microsoft Rust Guidelines - Soundness and best practices
- Rust API Guidelines - API design patterns
- Edition 2024 Guide - Latest Rust edition
Built with β€οΈ using Rust Edition 2024