mcpay
Version:
SDK and CLI for MCPay functionality - MCP servers with payment capabilities
724 lines • 33.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.validatePaymentWithMcpay = validatePaymentWithMcpay;
exports.createPaymentVerifier = createPaymentVerifier;
exports.createPaidMcpHandler = createPaidMcpHandler;
const mcp_handler_1 = require("mcp-handler");
const zod_1 = require("zod");
const platform_detection_1 = require("./platform-detection");
// Simple debug logging without external dependency
const debug = (namespace) => {
return (...args) => {
if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV !== 'production') {
console.log(`[${namespace}]`, ...args);
}
};
};
/**
* Validates a payment header with the mcpay API.
*/
async function validatePaymentWithMcpay(paymentHeader, config) {
const log = debug('payment-validation');
try {
log('Validating payment with mcpay API:', config.mcpayApiUrl);
log('Payment header:', paymentHeader);
log('Config:', JSON.stringify({
apiKey: config.apiKey,
mcpayApiUrl: config.mcpayApiUrl,
mcpayApiValidationPath: config.mcpayApiValidationPath
}, null, 2));
const controller = new AbortController();
const timeoutId = setTimeout(() => {
log('Payment validation request timing out...');
controller.abort();
}, config.validationTimeout || 15000);
const headers = {
'Content-Type': 'application/json',
};
if (config.apiKey) {
headers['Authorization'] = `Bearer ${config.apiKey}`;
}
// Construct URL properly, using /validate as default if path is undefined
const validationPath = config.mcpayApiValidationPath || '/validate';
const baseUrl = config.mcpayApiUrl?.replace(/\/$/, '') || ''; // Remove trailing slash
const url = baseUrl + validationPath;
log('Making request to:', url);
// Validate URL before making request
try {
new URL(url);
}
catch (urlError) {
throw new Error(`Invalid URL constructed: ${url}. Check mcpayApiUrl (${config.mcpayApiUrl}) and mcpayApiValidationPath (${config.mcpayApiValidationPath})`);
}
log('Request headers:', JSON.stringify(headers, null, 2));
const requestBody = {
payment: paymentHeader,
timestamp: new Date().toISOString(),
};
log('Request body:', JSON.stringify(requestBody, null, 2));
const response = await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify(requestBody),
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) {
log('Mcpay API responded with error:', response.status, response.statusText);
const errorText = await response.text();
return {
isValid: false,
errorReason: `API error: ${response.status} - ${errorText}`,
};
}
const validationResult = await response.json();
log('Payment validation result:', validationResult);
return validationResult;
}
catch (error) {
log('Payment validation failed:', error);
return {
isValid: false,
errorReason: error instanceof Error ? error.message : 'Unknown validation error',
};
}
}
/**
* Creates payment requirements using the MCPay API.
*/
async function createPaymentRequirementsWithMcpay(tool, paymentOptions, config) {
const log = debug('payment-requirements');
try {
log('Creating payment requirements with mcpay API:', config.mcpayApiUrl);
const controller = new AbortController();
const timeoutId = setTimeout(() => {
log('Payment requirements request timing out...');
controller.abort();
}, config.validationTimeout || 15000);
const headers = {
'Content-Type': 'application/json',
};
if (config.apiKey) {
headers['Authorization'] = `Bearer ${config.apiKey}`;
}
const requirementsPath = config.mcpayApiRequirementsPath || '/requirements';
const baseUrl = config.mcpayApiUrl?.replace(/\/$/, '') || ''; // Remove trailing slash
const url = baseUrl + requirementsPath;
log('Making requirements request to:', url);
// Validate URL before making request
try {
new URL(url);
}
catch (urlError) {
throw new Error(`Invalid URL constructed: ${url}. Check mcpayApiUrl (${config.mcpayApiUrl}) and mcpayApiRequirementsPath (${config.mcpayApiRequirementsPath})`);
}
const response = await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify({
tool: tool,
paymentOptions: paymentOptions,
timestamp: new Date().toISOString(),
}),
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) {
log('Mcpay API requirements responded with error:', response.status, response.statusText);
const errorText = await response.text();
throw new Error(`Requirements API error: ${response.status} - ${errorText}`);
}
const requirementsResult = await response.json();
log('Payment requirements result:', requirementsResult);
return requirementsResult;
}
catch (error) {
log('Payment requirements creation failed:', error);
throw new Error(error instanceof Error ? error.message : 'Unknown requirements creation error');
}
}
/**
* Creates a payment verifier function that validates X-Payment headers with MCPay.
*/
function createPaymentVerifier(config) {
return async (req, paymentHeader) => {
if (!paymentHeader) {
// Extract X-Payment header from request
paymentHeader = req.headers.get('X-PAYMENT') || undefined;
}
if (!paymentHeader) {
return undefined;
}
// Validate the payment with MCPay API
const validationResult = await validatePaymentWithMcpay(paymentHeader, config);
if (!validationResult.isValid) {
console.warn('[mcpay] Payment validation failed:', validationResult.errorReason);
return undefined;
}
// Create AuthInfo from validated payment
return {
token: paymentHeader,
scopes: ['payment:verified'], // Default scope for validated payments
clientId: validationResult.userId || validationResult.paymentId || 'anonymous',
extra: {
paymentId: validationResult.paymentId,
userId: validationResult.userId,
amount: validationResult.amount,
currency: validationResult.currency,
paymentMetadata: validationResult.metadata,
validatedAt: new Date().toISOString(),
},
};
};
}
class PaymentProcessor {
log;
options;
pingAttempts = 0;
maxPingAttempts = 3;
pingCircuitBreakerUntil = null;
circuitBreakerDuration = 5 * 60 * 1000; // 5 minutes
constructor(options) {
this.log = debug('payflow-sdk');
// Define default ping options
const defaultPingOptions = {
enabled: true, // Disabled by default to prevent hanging when no server is available
pingOnCreate: true,
timeout: 3000, // Reduced timeout for faster failures
retryAttempts: 2,
retryDelay: 1000, // 1 second base delay
};
// Define default mcpay options
const defaultMcpayOptions = {
mcpayApiUrl: process.env.MCPAY_API_URL || 'https://mcpay.fun',
apiKey: process.env.MCPAY_API_KEY || '',
mcpayApiValidationPath: process.env.MCPAY_API_VALIDATION_PATH || '/validate',
mcpayApiPingPath: process.env.MCPAY_API_PING_PATH || '/ping',
mcpayApiRequirementsPath: process.env.MCPAY_API_REQUIREMENTS_PATH || '/requirements',
validationTimeout: 15000,
required: true,
resourceMetadataPath: '/.well-known/oauth-protected-resource',
};
// Merge user options with defaults (preserving defaults for undefined values)
this.options = {
ping: {
...defaultPingOptions,
...(options?.ping || {}),
},
mcpay: {
...defaultMcpayOptions,
...(options?.mcpay || {}),
// Ensure critical paths have defaults if undefined
mcpayApiValidationPath: options?.mcpay?.mcpayApiValidationPath ?? defaultMcpayOptions.mcpayApiValidationPath,
mcpayApiRequirementsPath: options?.mcpay?.mcpayApiRequirementsPath ?? defaultMcpayOptions.mcpayApiRequirementsPath,
mcpayApiPingPath: options?.mcpay?.mcpayApiPingPath ?? defaultMcpayOptions.mcpayApiPingPath,
},
};
// If pingOnCreate is enabled, ping immediately (good for serverless)
if (this.options.ping?.pingOnCreate) {
this.log('Pinging on create enabled, triggering immediate ping...');
this.pingServerWithRetry().catch((error) => {
console.warn('[mcpay] Immediate ping failed (non-blocking):', error);
});
}
}
/**
* Creates a timeout promise that rejects after the specified duration
*/
createTimeoutPromise(timeoutMs) {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`Operation timed out after ${timeoutMs}ms`));
}, timeoutMs);
});
}
/**
* Attempts to ping the server with retry logic and circuit breaker
*/
async pingServerWithRetry() {
// Check circuit breaker
if (this.pingCircuitBreakerUntil && new Date() < this.pingCircuitBreakerUntil) {
this.log('Ping circuit breaker active, skipping ping until', this.pingCircuitBreakerUntil.toISOString());
return;
}
const maxRetries = this.options.ping?.retryAttempts || 2;
const baseDelay = this.options.ping?.retryDelay || 1000;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
await this.pingServer();
this.log('Ping successful on attempt', attempt + 1);
this.pingAttempts = 0; // Reset failure counter on success
this.pingCircuitBreakerUntil = null; // Clear circuit breaker
return;
}
catch (error) {
this.pingAttempts++;
const isLastAttempt = attempt === maxRetries;
this.log(`Ping attempt ${attempt + 1} failed:`, error instanceof Error ? error.message : String(error));
if (isLastAttempt) {
// Activate circuit breaker if we've failed too many times
if (this.pingAttempts >= this.maxPingAttempts) {
this.pingCircuitBreakerUntil = new Date(Date.now() + this.circuitBreakerDuration);
this.log('Ping circuit breaker activated until', this.pingCircuitBreakerUntil.toISOString());
}
// Log the final failure but don't throw - let server continue running normally
this.log('All ping attempts failed, continuing server operation normally:', error instanceof Error ? error.message : String(error));
return;
}
// Exponential backoff with jitter
const delay = baseDelay * Math.pow(2, attempt) + Math.random() * 1000;
this.log(`Retrying ping in ${Math.round(delay)}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
/**
* Pings the configured server to notify it that the MCP handler has been created
*/
async pingServer() {
this.log('Starting ping process...');
if (!this.options.ping?.enabled) {
this.log('Ping disabled, skipping server ping');
return;
}
const mcpayApiUrl = this.options.mcpay?.mcpayApiUrl;
const mcpayApiPingPath = this.options.mcpay?.mcpayApiPingPath;
if (!mcpayApiUrl || !mcpayApiPingPath) {
this.log('No ping server URL configured, skipping ping');
return;
}
const baseUrl = mcpayApiUrl?.replace(/\/$/, '') || ''; // Remove trailing slash
const pingPath = mcpayApiPingPath || '/ping';
const pingUrl = baseUrl + pingPath;
const timeout = this.options.ping?.timeout || 3000;
// Validate URL before making request
try {
new URL(pingUrl);
}
catch (urlError) {
throw new Error(`Invalid ping URL constructed: ${pingUrl}. Check mcpayApiUrl (${mcpayApiUrl}) and mcpayApiPingPath (${mcpayApiPingPath})`);
}
this.log('Attempting to ping server at:', pingUrl, 'with timeout:', timeout);
// Quick connectivity hint for localhost
if (pingUrl.includes('localhost') || pingUrl.includes('127.0.0.1')) {
this.log('Pinging localhost - ensure local MCPay server is running or disable ping');
}
// Gather platform info with timeout (non-blocking)
let platformInfo;
try {
const platformInfoPromise = (0, platform_detection_1.gatherPlatformInfo)(process.env);
const platformTimeout = Math.min(timeout / 2, 2000); // Use half the ping timeout, max 2s
platformInfo = await Promise.race([
platformInfoPromise,
this.createTimeoutPromise(platformTimeout)
]);
this.log('Detected platform info:', platformInfo);
}
catch (error) {
this.log('Platform detection failed or timed out, using fallback:', error instanceof Error ? error.message : String(error));
platformInfo = {
platform: 'Unknown',
urls: [`http://localhost:${process.env.PORT || '3000'}`],
signatureEnv: {}
};
}
// Ensure all values in the payload are serializable and safe
let payload;
try {
payload = {
event: 'mcp_handler_created',
timestamp: new Date().toISOString(),
platform: platformInfo?.platform || 'Unknown',
detectedUrls: Array.isArray(platformInfo?.urls) ? platformInfo.urls.filter(url => typeof url === 'string') : [],
platformEnv: (platformInfo?.signatureEnv && typeof platformInfo.signatureEnv === 'object')
? Object.fromEntries(Object.entries(platformInfo.signatureEnv)
.filter(([key, value]) => typeof key === 'string' && typeof value === 'string'))
: {},
};
}
catch (payloadError) {
// If payload construction fails, use minimal safe payload
this.log('Failed to construct full payload, using minimal payload:', payloadError instanceof Error ? payloadError.message : String(payloadError));
payload = {
event: 'mcp_handler_created',
timestamp: new Date().toISOString(),
platform: 'Unknown',
detectedUrls: [],
platformEnv: {},
};
}
this.log('Ping payload:', payload);
// Validate JSON serialization before sending
let jsonBody;
try {
jsonBody = JSON.stringify(payload);
this.log('Serialized JSON body length:', jsonBody.length);
}
catch (jsonError) {
throw new Error(`Failed to serialize ping payload: ${jsonError instanceof Error ? jsonError.message : String(jsonError)}`);
}
try {
// Use AbortController for proper timeout handling instead of Promise.race
const controller = new AbortController();
const timeoutId = setTimeout(() => {
this.log('Ping request timeout, aborting...');
controller.abort();
}, timeout);
const response = await fetch(pingUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.options.mcpay?.apiKey}`,
},
body: jsonBody,
signal: controller.signal,
});
clearTimeout(timeoutId);
if (response.ok) {
this.log('Successfully pinged server:', response.status);
try {
const responseText = await response.text();
if (responseText) {
this.log('Ping response:', responseText);
}
}
catch (error) {
this.log('Error reading ping response (non-critical):', error instanceof Error ? error.message : String(error));
}
}
else {
this.log('Ping server responded with error:', response.status, response.statusText);
try {
const errorText = await response.text();
if (errorText) {
this.log('Error response body:', errorText);
}
}
catch (error) {
this.log('Error reading error response (non-critical):', error instanceof Error ? error.message : String(error));
}
throw new Error(`Ping failed with status ${response.status}: ${response.statusText}`);
}
}
catch (error) {
// Categorize and handle different types of errors
if (error instanceof Error) {
if (error.name === 'AbortError' || error.message.includes('aborted')) {
throw new Error(`Ping timed out after ${timeout}ms`);
}
else if (error.message.includes('timed out')) {
throw new Error(`Ping timed out after ${timeout}ms`);
}
else if ('code' in error && error.code === 'ECONNREFUSED') {
throw new Error(`Connection refused - is the ping server running at ${pingUrl}?`);
}
else if ('code' in error && error.code === 'ENOTFOUND') {
throw new Error(`DNS resolution failed for ${pingUrl}`);
}
else if ('code' in error && error.code === 'ECONNRESET') {
throw new Error(`Connection reset by server at ${pingUrl}`);
}
else {
throw new Error(`Ping failed: ${error.message}`);
}
}
else {
throw new Error(`Ping failed: ${String(error)}`);
}
}
}
/**
* Determines if payment options are simple or advanced based on their properties.
*/
isSimplePayment(options) {
return 'price' in options && 'currency' in options && !('rawAmount' in options);
}
/**
* Creates an error response in the standard CallToolResult format
*/
createErrorResponse(message) {
this.log('Error:', message);
return {
content: [{
type: 'text',
text: message,
isError: true
}],
};
}
/**
* Creates a payment requirements response for unpaid requests
*/
createPaymentRequirementsResponse(requirements) {
return {
content: [{
type: 'text',
text: `Payment required. Please provide payment via X-PAYMENT header.\n\nPayment Requirements:\n- Amount: ${requirements.maxAmountRequired} ${requirements.asset}\n- Recipient: ${requirements.payTo}\n- Network: ${requirements.network}\n- Description: ${requirements.description}`
}],
isError: false,
};
}
/**
* Executes the tool callback with proper type safety
*/
async executeToolCallback(cb, toolArgs, context) {
const authInfo = context.authInfo;
if (!authInfo || !authInfo.scopes.includes('payment:verified')) {
return this.createErrorResponse('Payment required. Please provide payment via X-PAYMENT header.');
}
return await cb(toolArgs, context);
}
/**
* Generates payment requirements for a tool when no valid payment is provided
*/
async generatePaymentRequirements(toolName, paymentConfig) {
if (!this.options.mcpay) {
throw new Error('MCPay configuration is required');
}
const paymentOptionsArray = this.normalizePaymentConfig(paymentConfig);
const selectedPaymentOption = paymentOptionsArray[0]; // Use first option for requirements
return await createPaymentRequirementsWithMcpay(toolName, selectedPaymentOption, this.options.mcpay);
}
createPaidCallback(name, paymentConfig, schema, cb) {
// Return a function that matches the expected signature for server.tool
return async (args, context) => {
this.log('Executing paid tool:', name);
try {
// Extract payment header and tool arguments
const { ...toolArgs } = args;
// Validate tool arguments against schema
const validatedArgs = schema.parse(toolArgs);
// Check if payment header is provided
if (!context.authInfo?.scopes.includes('payment:verified')) {
// No payment provided - generate and return payment requirements
try {
const requirements = await this.generatePaymentRequirements(name, paymentConfig);
return this.createPaymentRequirementsResponse(requirements);
}
catch (error) {
return this.createErrorResponse(`Failed to generate payment requirements: ${error instanceof Error ? error.message : String(error)}`);
}
}
try {
const result = await this.executeToolCallback(cb, validatedArgs, context);
return result;
}
catch (error) {
return this.createErrorResponse(`Tool execution failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
catch (error) {
// Handle schema validation errors and any other unexpected errors
if (error instanceof zod_1.z.ZodError) {
return this.createErrorResponse(`Invalid arguments: ${error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`);
}
return this.createErrorResponse(`Unexpected error: ${error instanceof Error ? error.message : String(error)}`);
}
};
}
// Helper function to check if an object is a Zod schema
isZodRawShape(obj) {
if (typeof obj !== 'object' || obj === null)
return false;
const isEmptyObject = Object.keys(obj).length === 0;
// Check if object is empty or at least one property is a ZodType instance
return isEmptyObject || Object.values(obj).some(this.isZodTypeLike);
}
isZodTypeLike(value) {
if (value === null || typeof value !== 'object')
return false;
try {
const obj = value;
// Most reliable check: Zod types have a _def property
if ('_def' in obj && typeof obj._def === 'object') {
return true;
}
// Check for parse and safeParse methods
if ('parse' in obj && typeof obj.parse === 'function' &&
'safeParse' in obj && typeof obj.safeParse === 'function') {
return true;
}
// Check constructor name (covers more Zod types)
if (obj.constructor?.name?.includes('Zod')) {
return true;
}
return false;
}
catch {
return false;
}
}
/**
* Normalizes PaymentConfig to always return an array of PaymentOptions.
*/
normalizePaymentConfig(config) {
return Array.isArray(config) ? config : [config];
}
/**
* Determines if payment config contains simple or advanced payment types.
*/
isPaymentConfig(obj) {
if (Array.isArray(obj)) {
return obj.length > 0 && obj.every(item => (typeof item === 'object' && item !== null) &&
(
// SimplePaymentOptions: price + currency (recipient optional)
('price' in item && 'currency' in item) ||
// AdvancedPaymentOptions: recipient + rawAmount + tokenDecimals
('recipient' in item && 'rawAmount' in item && 'tokenDecimals' in item)));
}
return (typeof obj === 'object' && obj !== null) &&
(
// SimplePaymentOptions: price + currency (recipient optional)
('price' in obj && 'currency' in obj) ||
// AdvancedPaymentOptions: recipient + rawAmount + tokenDecimals
('recipient' in obj && 'rawAmount' in obj && 'tokenDecimals' in obj));
}
createPaidToolMethod(server) {
return (name, ...rest) => {
let description;
let paymentConfig;
let inputSchema;
let annotations;
// Check for description as first argument
if (rest.length >= 1 && typeof rest[0] === 'string') {
description = rest.shift();
}
// Check for PaymentConfig object or array
if (rest.length >= 1 && this.isPaymentConfig(rest[0])) {
paymentConfig = rest.shift();
}
// Check for input schema or annotations
if (rest.length >= 1) {
if (this.isZodRawShape(rest[0])) {
inputSchema = rest.shift();
}
else if (typeof rest[0] === 'object' && rest[0] !== null && 'title' in rest[0]) {
annotations = rest.shift();
}
}
// Check for annotations if not found yet
if (rest.length >= 1 && typeof rest[0] === 'object' && rest[0] !== null && 'title' in rest[0]) {
annotations = rest.shift();
}
if (rest.length > 1) {
throw new Error('Too many arguments to paidTool()');
}
if (!paymentConfig) {
throw new Error('PaymentConfig are required for paidTool()');
}
// Normalize paymentConfig to an array of PaymentOptions
const paymentOptionsArray = this.normalizePaymentConfig(paymentConfig);
// Create annotations with all payment options information
const allPaymentDetails = paymentOptionsArray.map(option => {
return this.isSimplePayment(option)
? {
type: 'simple',
price: option.price,
currency: option.currency,
recipient: option.recipient,
}
: {
type: 'advanced',
rawAmount: option.rawAmount,
tokenDecimals: option.tokenDecimals,
recipient: option.recipient,
network: option.network,
...(option.tokenAddress && { tokenAddress: option.tokenAddress }),
...(option.tokenSymbol && { tokenSymbol: option.tokenSymbol }),
};
});
const paymentAnnotations = {
payment: paymentOptionsArray.length === 1 ? allPaymentDetails[0] : allPaymentDetails,
...annotations,
};
const cb = rest[0];
let finalSchema;
if (inputSchema) {
// Merge the input schema with payment schema
finalSchema = {
...inputSchema,
};
}
else {
finalSchema = {};
}
// Create the schema object for createPaidCallback
const schemaForCallback = inputSchema ? zod_1.z.object(inputSchema) : zod_1.z.object({});
// Create the paid callback with proper typing
const paidCb = this.createPaidCallback(name, paymentConfig, schemaForCallback, cb);
// Generate description based on payment options
const toolDescription = description || `Paid tool ${name}`;
let paymentInfo;
if (paymentOptionsArray.length === 1) {
const option = paymentOptionsArray[0];
paymentInfo = this.isSimplePayment(option)
? `Price: ${option.price} ${option.currency} - Recipient: ${option.recipient}`
: `Raw Amount: ${option.rawAmount} (${option.tokenSymbol || 'tokens'}) - Recipient: ${option.recipient}`;
}
else {
paymentInfo = `Multiple payment options available (${paymentOptionsArray.length} options):\n` +
paymentOptionsArray.map((option, index) => {
const details = this.isSimplePayment(option)
? `${option.price} ${option.currency}`
: `${option.rawAmount} ${option.tokenSymbol || 'tokens'}`;
return ` ${index + 1}. ${details} - ${option.recipient}`;
}).join('\n');
}
const fullDescription = `${toolDescription}\nIMPORTANT: Payment details:\n${paymentInfo}`;
// Register the tool with payment annotations
return server.tool(name, fullDescription, finalSchema, paymentAnnotations, paidCb);
};
}
}
function _createPaidMcpHandler(initializeServer, serverOptions, config) {
console.log('[mcpay] Creating paid MCP handler...', { serverOptions });
const processor = new PaymentProcessor(serverOptions);
processor.pingServerWithRetry();
return (0, mcp_handler_1.createMcpHandler)(
// Wrap the initialization to use ExtendedMcpServer
async (server) => {
console.log('[mcpay] Initializing MCP server with payment support...');
// Create a proxy that adds the paidTool method while delegating everything else to the original server
const extendedServer = new Proxy(server, {
get(target, prop, receiver) {
if (prop === 'paidTool') {
return processor.createPaidToolMethod(target);
}
return Reflect.get(target, prop, receiver);
}
});
// Call the user's initialization function with the extended server
await initializeServer(extendedServer);
console.log('[mcpay] Server initialized, triggering ping...');
// Ping the server asynchronously after initialization (don't await to avoid blocking)
// Only ping here if pingOnCreate is disabled
if (!serverOptions?.ping?.pingOnCreate) {
processor.pingServerWithRetry().catch((error) => {
// Already handled in pingServer method, but adding extra safety
console.warn('Ping server error (non-blocking):', error);
});
}
else {
console.log('[mcpay] Skipping server initialization ping (already pinged on create)');
}
}, serverOptions, config);
}
/**
* Creates a payment-based authenticated MCP handler that validates X-Payment headers with mcpay API
* @param initializeServer - A function that initializes the MCP server with paid tools
* @param serverOptions - Options for the MCP server including MCPay configuration
* @param config - Configuration for the MCP handler
* @returns A function that can be used to handle MCP requests with payment-based authentication
*/
function createPaidMcpHandler(initializeServer, serverOptions, config) {
// Create the payment verifier
const paymentVerifier = createPaymentVerifier(serverOptions?.mcpay);
// Create the base paid handler
const paidHandler = _createPaidMcpHandler(initializeServer, serverOptions, config);
// Wrap the paid handler with payment-based authentication
const authHandler = (0, mcp_handler_1.withMcpAuth)(paidHandler, paymentVerifier, {
requiredScopes: ['payment:verified'],
});
return authHandler;
}
//# sourceMappingURL=paid-mcp-server.js.map