# ai-payments

A TypeScript library that integrates payment functionality with MCP (Model Context Protocol) servers, enabling developers to easily create paywall-protected tools.

## Installation

```bash
npm install ai-payments
# or
yarn add ai-payments
```

## Quick Start

```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { createPaywallTool } from 'ai-payments';

// Create your MCP server
const server = new McpServer({
  name: 'my-server',
  version: '1.0.0',
});

// Create a simple paywall-protected tool
await createPaywallTool(
  server,
  '/secret',
  0.01,
  process.env.PAY_TO_ADDRESS!,
  'get_secret'
);

// Create a tool with API key authentication
await createPaywallTool(
  server,
  '/auth/query',
  0.01,
  process.env.PAY_TO_ADDRESS!,
  'get_url_query_secret',
  {
    queryParam: 'x-api-key',
    apiKey: 'demo-api-key-12345',
  }
);
```

## Prerequisites

### Environment Variables

You need to set the following environment variable:

- `BUYER_PRIVATE_KEY`: The private key of the wallet that will verify payments (format: `0x...`)

## API Reference

### `createPaywallTool`

Creates a paywall-protected MCP tool that requires payment before revealing content.

```typescript
async function createPaywallTool(
  server: McpServer,
  endpoint: string,
  priceUSD: number,
  payToAddress: string,
  toolName: string,
  authOptions?: PaywallAuthOptions,
  config?: PaywallConfig
): Promise<void>;
```

#### Parameters

- `server`: MCP server instance to register the tool with
- `endpoint`: The protected endpoint to access (e.g., '/secret')
- `priceUSD`: Price in USD (e.g., 0.01 for $0.01)
- `payToAddress`: Wallet address to receive payment
- `toolName`: Tool name for the MCP server
- `authOptions` (optional): Authentication options for protected endpoints
- `config` (optional): Configuration overrides

#### Authentication Options

```typescript
interface PaywallAuthOptions {
  // Query parameter authentication
  queryParam?: string; // e.g., 'x-api-key'
  apiKey?: string; // The API key value

  // Header-based authentication
  header?: string; // e.g., 'Authorization'

  // Custom headers
  headers?: Record<string, string>;
}
```

#### Configuration Options

```typescript
interface PaywallConfig {
  baseURL?: string; // Default: 'http://localhost:3001'
  network?: any; // Default: baseSepolia
  usdcAddress?: string; // Default: '0x036CbD53842c5426634e7929541eC2318f3dCF7e'
}
```

## Usage Examples

### Basic Paywall Tool

```typescript
const server = new McpServer({
  name: 'weather-service',
  version: '1.0.0',
});

// Simple endpoint without authentication
await createPaywallTool(
  server,
  '/weather/premium',
  0.05,
  '0xYourPaymentAddress',
  'get_premium_weather'
);
```

### With Query Parameter Authentication

```typescript
// Endpoint that requires an API key in query parameters
await createPaywallTool(
  server,
  '/api/data',
  0.01,
  '0xYourPaymentAddress',
  'get_api_data',
  {
    queryParam: 'apikey',
    apiKey: 'your-secret-api-key',
  }
);
```

### With Header Authentication

```typescript
// Endpoint that requires authentication header
await createPaywallTool(
  server,
  '/protected/resource',
  0.02,
  '0xYourPaymentAddress',
  'get_protected_resource',
  {
    header: 'Authorization',
    apiKey: 'Bearer your-token-here',
  }
);
```

### With Custom Headers

```typescript
// Endpoint with multiple custom headers
await createPaywallTool(
  server,
  '/custom/endpoint',
  0.03,
  '0xYourPaymentAddress',
  'get_custom_data',
  {
    headers: {
      'X-Custom-Header': 'custom-value',
      'X-API-Version': 'v2',
    },
  }
);
```

## How It Works

1. **Initial Request**: When a user calls the tool without payment parameters, they receive payment instructions including:

   - The exact USDC amount to send
   - The recipient address
   - Network information (Base Sepolia)

2. **Payment**: The user sends the specified USDC amount to the provided address.

3. **Verification**: After payment, the user calls the tool again with:

   - `txHash`: The transaction hash of their payment

4. **Content Delivery**: The tool verifies the payment on-chain and returns the protected content.

### Important Payment Verification Behavior

- **Payment verification is stateless**: The library validates payments based solely on blockchain data, not on stored session information. This means:
  - Users can verify payments even after server restarts
  - Previous payments with valid transaction hashes will always be honored
  - There's no risk of losing payments due to expired sessions

- **When `txHash` is provided**: The tool will always attempt to verify the payment and deliver content, never generate a new payment request.

- **New payment requests are only generated when**: No `txHash` is provided in the tool call.

### AI Assistant Integration Notes

When integrating with AI assistants (like Claude), ensure the AI correctly parses user input containing transaction details. The library supports case-insensitive parameter names (`txHash`, `txhash`, `TxHash`, etc.).

**Common user input patterns the AI should handle:**
- `txhash = 0x...`
- `transaction hash: 0x...`
- `I paid with tx 0x...`

The AI should extract this value and pass it as the `txHash` parameter to the MCP tool.

## Complete Example

```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { createPaywallTool } from 'ai-payments';

async function main() {
  // Create MCP server
  const server = new McpServer({
    name: 'premium-content-server',
    version: '1.0.0',
  });

  // Register multiple paywall tools
  await createPaywallTool(
    server,
    '/secrets/recipe',
    0.1,
    process.env.PAY_TO_ADDRESS!,
    'get_secret_recipe'
  );

  await createPaywallTool(
    server,
    '/api/premium-data',
    0.05,
    process.env.PAY_TO_ADDRESS!,
    'get_premium_data',
    {
      queryParam: 'api_key',
      apiKey: process.env.API_KEY!,
    }
  );

  // Connect to transport
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main().catch(console.error);
```

## Error Handling

The library throws `McpError` with appropriate error codes:

- `InvalidRequest`: When payment verification fails or parameters are missing
- `InternalError`: When configuration is missing or system errors occur

## License

MIT
