index.ts•43.6 kB
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ErrorCode,
  ListToolsRequestSchema,
  McpError,
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';
type AxiosError = {
  isAxiosError: boolean;
  response?: {
    data?: {
      message?: string;
    };
  };
  message: string;
};
interface PayPalResponse {
  data: unknown;
}
interface PayPalTokenResponse extends PayPalResponse {
  access_token: string;
}
function isAxiosError(error: unknown): error is AxiosError {
  return error !== null && typeof error === 'object' && 'isAxiosError' in error;
}
const PAYPAL_CLIENT_ID = process.env.PAYPAL_CLIENT_ID;
const PAYPAL_CLIENT_SECRET = process.env.PAYPAL_CLIENT_SECRET;
if (!PAYPAL_CLIENT_ID || !PAYPAL_CLIENT_SECRET) {
  throw new Error('PayPal credentials are required');
}
interface PayPalPaymentToken {
  id?: string;
  customer: {
    id: string;
    email_address?: string;
    phone?: {
      phone_type: string;
      phone_number: {
        national_number: string;
      };
    };
  };
  payment_source: {
    card?: {
      name: string;
      number: string;
      expiry: string;
      security_code: string;
    };
    paypal?: {
      email_address: string;
      account_id?: string;
    };
  };
}
interface PayPalPayment {
  id?: string;
  intent: string;
  payer: {
    payment_method: string;
    funding_instruments?: Array<{
      credit_card?: {
        number: string;
        type: string;
        expire_month: number;
        expire_year: number;
        cvv2: string;
        first_name: string;
        last_name: string;
      };
    }>;
  };
  transactions: Array<{
    amount: {
      total: string;
      currency: string;
    };
    description?: string;
  }>;
}
interface PayPalPayout {
  sender_batch_header: {
    sender_batch_id: string;
    email_subject?: string;
    recipient_type?: string;
  };
  items: Array<{
    recipient_type: string;
    amount: {
      value: string;
      currency: string;
    };
    receiver: string;
    note?: string;
    sender_item_id?: string;
  }>;
}
interface PayPalReferencedPayout {
  referenced_payouts: Array<{
    item_id: string;
    processing_state: {
      status: string;
      reason?: string;
    };
    reference_id: string;
    reference_type: string;
    payout_amount: {
      currency_code: string;
      value: string;
    };
    payout_destination: string;
  }>;
}
interface PayPalOrder {
  id?: string;
  intent: 'CAPTURE' | 'AUTHORIZE';
  purchase_units: Array<{
    amount: {
      currency_code: string;
      value: string;
    };
    description?: string;
    reference_id?: string;
  }>;
}
interface PayPalPartnerReferral {
  individual_owners: Array<{
    names: Array<{
      prefix?: string;
      given_name: string;
      surname: string;
      middle_name?: string;
      suffix?: string;
    }>;
    citizenship?: string;
    addresses?: Array<{
      address_line_1: string;
      address_line_2?: string;
      admin_area_2: string;
      admin_area_1: string;
      postal_code: string;
      country_code: string;
    }>;
  }>;
  business_entity: {
    business_type: {
      type: string;
      subtype?: string;
    };
    business_name: string;
    business_phone?: {
      country_code: string;
      national_number: string;
    };
  };
  email: string;
}
interface PayPalWebProfile {
  id?: string;
  name: string;
  presentation: {
    brand_name?: string;
    logo_image?: string;
    locale_code?: string;
  };
  input_fields: {
    no_shipping?: number;
    address_override?: number;
  };
  flow_config: {
    landing_page_type?: string;
    bank_txn_pending_url?: string;
  };
}
interface PayPalProduct {
  id?: string;
  name: string;
  description: string;
  type: 'PHYSICAL' | 'DIGITAL' | 'SERVICE';
  category: string;
  image_url?: string;
  home_url?: string;
}
interface PayPalDispute {
  id: string;
  reason: string;
  status: string;
  disputed_transactions: Array<{
    id: string;
    amount: {
      currency_code: string;
      value: string;
    };
  }>;
}
interface PayPalInvoice {
  id?: string;
  detail: {
    invoice_number: string;
    reference: string;
    currency_code: string;
  };
  primary_recipients: Array<{
    billing_info: {
      email_address: string;
    };
  }>;
  items: Array<{
    name: string;
    quantity: string;
    unit_amount: {
      currency_code: string;
      value: string;
    };
  }>;
}
interface PayPalIdentityTokenInfo {
  client_id: string;
  user_id: string;
  scopes: string[];
}
class PayPalServer {
  private server: Server;
  private accessToken: string | null = null;
  constructor() {
    this.server = new Server(
      {
        name: 'paypal-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );
    this.setupToolHandlers();
    this.server.onerror = (error) => console.error('[MCP Error]', error);
    process.on('SIGINT', async () => {
      await this.server.close();
      process.exit(0);
    });
  }
  private async getAccessToken(): Promise<string> {
    if (this.accessToken) return this.accessToken;
    const auth = Buffer.from(`${PAYPAL_CLIENT_ID}:${PAYPAL_CLIENT_SECRET}`).toString('base64');
    const response = await axios.post<PayPalTokenResponse>(
      'https://api-m.sandbox.paypal.com/v1/oauth2/token',
      'grant_type=client_credentials',
      {
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
          Authorization: `Basic ${auth}`,
        },
      }
    );
    this.accessToken = response.data.access_token;
    if (!this.accessToken) {
      throw new Error('Failed to obtain access token');
    }
    return this.accessToken;
  }
  private validatePaymentToken(args: unknown): PayPalPaymentToken {
    if (typeof args !== 'object' || !args) {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid payment token data');
    }
    const token = args as Record<string, unknown>;
    
    if (!token.customer || typeof token.customer !== 'object' ||
        !token.payment_source || typeof token.payment_source !== 'object') {
      throw new McpError(ErrorCode.InvalidParams, 'Missing required payment token fields');
    }
    const customer = token.customer as Record<string, unknown>;
    if (typeof customer.id !== 'string') {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid customer ID');
    }
    const validatedToken: PayPalPaymentToken = {
      customer: {
        id: customer.id
      },
      payment_source: {}
    };
    if (typeof customer.email_address === 'string') {
      validatedToken.customer.email_address = customer.email_address;
    }
    const source = token.payment_source as Record<string, unknown>;
    if (source.card && typeof source.card === 'object') {
      const card = source.card as Record<string, unknown>;
      if (typeof card.name === 'string' &&
          typeof card.number === 'string' &&
          typeof card.expiry === 'string' &&
          typeof card.security_code === 'string') {
        validatedToken.payment_source.card = {
          name: card.name,
          number: card.number,
          expiry: card.expiry,
          security_code: card.security_code
        };
      }
    }
    if (source.paypal && typeof source.paypal === 'object') {
      const paypal = source.paypal as Record<string, unknown>;
      if (typeof paypal.email_address === 'string') {
        validatedToken.payment_source.paypal = {
          email_address: paypal.email_address
        };
        if (typeof paypal.account_id === 'string') {
          validatedToken.payment_source.paypal.account_id = paypal.account_id;
        }
      }
    }
    return validatedToken;
  }
  private validatePayment(args: unknown): PayPalPayment {
    if (typeof args !== 'object' || !args) {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid payment data');
    }
    const payment = args as Record<string, unknown>;
    
    if (typeof payment.intent !== 'string' ||
        !payment.payer || typeof payment.payer !== 'object' ||
        !Array.isArray(payment.transactions) ||
        payment.transactions.length === 0) {
      throw new McpError(ErrorCode.InvalidParams, 'Missing required payment fields');
    }
    const payer = payment.payer as Record<string, unknown>;
    if (typeof payer.payment_method !== 'string') {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid payment method');
    }
    const transactions = payment.transactions.map(transaction => {
      const trans = transaction as Record<string, unknown>;
      if (!trans.amount || typeof trans.amount !== 'object') {
        throw new McpError(ErrorCode.InvalidParams, 'Invalid transaction amount');
      }
      const amount = trans.amount as Record<string, unknown>;
      if (typeof amount.total !== 'string' || typeof amount.currency !== 'string') {
        throw new McpError(ErrorCode.InvalidParams, 'Invalid amount fields');
      }
      const validatedTransaction = {
        amount: {
          total: amount.total,
          currency: amount.currency
        }
      };
      if (typeof trans.description === 'string') {
        (validatedTransaction as any).description = trans.description;
      }
      return validatedTransaction;
    });
    return {
      intent: payment.intent,
      payer: {
        payment_method: payer.payment_method,
        funding_instruments: payer.funding_instruments as PayPalPayment['payer']['funding_instruments']
      },
      transactions
    };
  }
  private validatePayout(args: unknown): PayPalPayout {
    if (typeof args !== 'object' || !args) {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid payout data');
    }
    const payout = args as Record<string, unknown>;
    
    if (!payout.sender_batch_header || typeof payout.sender_batch_header !== 'object' ||
        !Array.isArray(payout.items) || payout.items.length === 0) {
      throw new McpError(ErrorCode.InvalidParams, 'Missing required payout fields');
    }
    const header = payout.sender_batch_header as Record<string, unknown>;
    if (typeof header.sender_batch_id !== 'string') {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid sender batch ID');
    }
    const items = payout.items.map(item => {
      const payoutItem = item as Record<string, unknown>;
      if (typeof payoutItem.recipient_type !== 'string' ||
          !payoutItem.amount || typeof payoutItem.amount !== 'object' ||
          typeof payoutItem.receiver !== 'string') {
        throw new McpError(ErrorCode.InvalidParams, 'Invalid payout item');
      }
      const amount = payoutItem.amount as Record<string, unknown>;
      if (typeof amount.value !== 'string' || typeof amount.currency !== 'string') {
        throw new McpError(ErrorCode.InvalidParams, 'Invalid amount fields');
      }
      const validatedItem: PayPalPayout['items'][0] = {
        recipient_type: payoutItem.recipient_type,
        amount: {
          value: amount.value,
          currency: amount.currency
        },
        receiver: payoutItem.receiver
      };
      if (typeof payoutItem.note === 'string') {
        validatedItem.note = payoutItem.note;
      }
      if (typeof payoutItem.sender_item_id === 'string') {
        validatedItem.sender_item_id = payoutItem.sender_item_id;
      }
      return validatedItem;
    });
    return {
      sender_batch_header: {
        sender_batch_id: header.sender_batch_id,
        email_subject: typeof header.email_subject === 'string' ? header.email_subject : undefined,
        recipient_type: typeof header.recipient_type === 'string' ? header.recipient_type : undefined
      },
      items
    };
  }
  private validateReferencedPayout(args: unknown): PayPalReferencedPayout {
    if (typeof args !== 'object' || !args) {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid referenced payout data');
    }
    const payout = args as Record<string, unknown>;
    
    if (!Array.isArray(payout.referenced_payouts) || payout.referenced_payouts.length === 0) {
      throw new McpError(ErrorCode.InvalidParams, 'Missing referenced payouts');
    }
    const referenced_payouts = payout.referenced_payouts.map(ref => {
      const refPayout = ref as Record<string, unknown>;
      if (typeof refPayout.reference_id !== 'string' ||
          typeof refPayout.reference_type !== 'string' ||
          !refPayout.payout_amount || typeof refPayout.payout_amount !== 'object' ||
          typeof refPayout.payout_destination !== 'string') {
        throw new McpError(ErrorCode.InvalidParams, 'Invalid referenced payout');
      }
      const amount = refPayout.payout_amount as Record<string, unknown>;
      if (typeof amount.currency_code !== 'string' || typeof amount.value !== 'string') {
        throw new McpError(ErrorCode.InvalidParams, 'Invalid amount fields');
      }
      return {
        item_id: typeof refPayout.item_id === 'string' ? refPayout.item_id : '',
        processing_state: {
          status: typeof refPayout.processing_state === 'object' ? 
            (refPayout.processing_state as any).status || '' : '',
          reason: typeof refPayout.processing_state === 'object' ? 
            (refPayout.processing_state as any).reason : undefined
        },
        reference_id: refPayout.reference_id,
        reference_type: refPayout.reference_type,
        payout_amount: {
          currency_code: amount.currency_code,
          value: amount.value
        },
        payout_destination: refPayout.payout_destination
      };
    });
    return { referenced_payouts };
  }
  private validatePayPalOrder(args: unknown): PayPalOrder {
    if (typeof args !== 'object' || !args) {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid order data');
    }
    const order = args as Record<string, unknown>;
    
    if (!['CAPTURE', 'AUTHORIZE'].includes(order.intent as string) ||
        !Array.isArray(order.purchase_units) ||
        order.purchase_units.length === 0) {
      throw new McpError(ErrorCode.InvalidParams, 'Missing required order fields');
    }
    const purchase_units = order.purchase_units.map(unit => {
      const unitObj = unit as Record<string, unknown>;
      if (!unitObj.amount || typeof unitObj.amount !== 'object') {
        throw new McpError(ErrorCode.InvalidParams, 'Invalid purchase unit amount');
      }
      const amount = unitObj.amount as Record<string, unknown>;
      if (typeof amount.currency_code !== 'string' || typeof amount.value !== 'string') {
        throw new McpError(ErrorCode.InvalidParams, 'Invalid amount fields');
      }
      const validatedUnit: PayPalOrder['purchase_units'][0] = {
        amount: {
          currency_code: amount.currency_code,
          value: amount.value
        }
      };
      if (typeof unitObj.description === 'string') {
        validatedUnit.description = unitObj.description;
      }
      if (typeof unitObj.reference_id === 'string') {
        validatedUnit.reference_id = unitObj.reference_id;
      }
      return validatedUnit;
    });
    return {
      intent: order.intent as 'CAPTURE' | 'AUTHORIZE',
      purchase_units
    };
  }
  private validatePartnerReferral(args: unknown): PayPalPartnerReferral {
    if (typeof args !== 'object' || !args) {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid partner referral data');
    }
    const referral = args as Record<string, unknown>;
    
    if (!Array.isArray(referral.individual_owners) ||
        !referral.business_entity ||
        typeof referral.email !== 'string') {
      throw new McpError(ErrorCode.InvalidParams, 'Missing required referral fields');
    }
    const individual_owners = referral.individual_owners.map(owner => {
      const ownerObj = owner as Record<string, unknown>;
      if (!Array.isArray(ownerObj.names) || ownerObj.names.length === 0) {
        throw new McpError(ErrorCode.InvalidParams, 'Invalid owner names');
      }
      const names = ownerObj.names.map(name => {
        const nameObj = name as Record<string, unknown>;
        if (typeof nameObj.given_name !== 'string' || typeof nameObj.surname !== 'string') {
          throw new McpError(ErrorCode.InvalidParams, 'Invalid name fields');
        }
        return {
          given_name: nameObj.given_name,
          surname: nameObj.surname
        };
      });
      return { names };
    });
    const business = referral.business_entity as Record<string, unknown>;
    if (!business.business_type || typeof business.business_name !== 'string') {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid business entity');
    }
    const business_type = business.business_type as Record<string, unknown>;
    if (typeof business_type.type !== 'string') {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid business type');
    }
    return {
      individual_owners,
      business_entity: {
        business_type: {
          type: business_type.type
        },
        business_name: business.business_name
      },
      email: referral.email
    };
  }
  private validateWebProfile(args: unknown): PayPalWebProfile {
    if (typeof args !== 'object' || !args) {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid web profile data');
    }
    const profile = args as Record<string, unknown>;
    
    if (typeof profile.name !== 'string') {
      throw new McpError(ErrorCode.InvalidParams, 'Missing required profile name');
    }
    const webProfile: PayPalWebProfile = {
      name: profile.name,
      presentation: {},
      input_fields: {},
      flow_config: {}
    };
    if (profile.presentation && typeof profile.presentation === 'object') {
      const pres = profile.presentation as Record<string, unknown>;
      if (typeof pres.brand_name === 'string') webProfile.presentation.brand_name = pres.brand_name;
      if (typeof pres.logo_image === 'string') webProfile.presentation.logo_image = pres.logo_image;
      if (typeof pres.locale_code === 'string') webProfile.presentation.locale_code = pres.locale_code;
    }
    if (profile.input_fields && typeof profile.input_fields === 'object') {
      const fields = profile.input_fields as Record<string, unknown>;
      if (typeof fields.no_shipping === 'number') webProfile.input_fields.no_shipping = fields.no_shipping;
      if (typeof fields.address_override === 'number') webProfile.input_fields.address_override = fields.address_override;
    }
    if (profile.flow_config && typeof profile.flow_config === 'object') {
      const flow = profile.flow_config as Record<string, unknown>;
      if (typeof flow.landing_page_type === 'string') webProfile.flow_config.landing_page_type = flow.landing_page_type;
      if (typeof flow.bank_txn_pending_url === 'string') webProfile.flow_config.bank_txn_pending_url = flow.bank_txn_pending_url;
    }
    return webProfile;
  }
  private validatePayPalProduct(args: unknown): PayPalProduct {
    if (typeof args !== 'object' || !args) {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid product data');
    }
    const product = args as Record<string, unknown>;
    
    if (typeof product.name !== 'string' ||
        typeof product.description !== 'string' ||
        !['PHYSICAL', 'DIGITAL', 'SERVICE'].includes(product.type as string) ||
        typeof product.category !== 'string') {
      throw new McpError(ErrorCode.InvalidParams, 'Missing required product fields');
    }
    const validatedProduct: PayPalProduct = {
      name: product.name,
      description: product.description,
      type: product.type as 'PHYSICAL' | 'DIGITAL' | 'SERVICE',
      category: product.category,
    };
    if (typeof product.image_url === 'string') {
      validatedProduct.image_url = product.image_url;
    }
    if (typeof product.home_url === 'string') {
      validatedProduct.home_url = product.home_url;
    }
    return validatedProduct;
  }
  private validatePaginationParams(args: unknown): { page_size?: number; page?: number } {
    if (typeof args !== 'object' || !args) {
      return {};
    }
    const params = args as Record<string, unknown>;
    const validated: { page_size?: number; page?: number } = {};
    if (typeof params.page_size === 'number' && params.page_size >= 1 && params.page_size <= 100) {
      validated.page_size = params.page_size;
    }
    if (typeof params.page === 'number' && params.page >= 1) {
      validated.page = params.page;
    }
    return validated;
  }
  private validateDisputeParams(args: unknown): { dispute_id: string } {
    if (typeof args !== 'object' || !args || typeof (args as any).dispute_id !== 'string') {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid dispute ID');
    }
    return { dispute_id: (args as any).dispute_id };
  }
  private validateTokenParams(args: unknown): { access_token: string } {
    if (typeof args !== 'object' || !args || typeof (args as any).access_token !== 'string') {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid access token');
    }
    return { access_token: (args as any).access_token };
  }
  private validatePayPalInvoice(args: unknown): PayPalInvoice {
    if (typeof args !== 'object' || !args) {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid invoice data');
    }
    const invoice = args as Record<string, unknown>;
    
    if (!invoice.detail || typeof invoice.detail !== 'object' ||
        !Array.isArray(invoice.primary_recipients) || invoice.primary_recipients.length === 0 ||
        !Array.isArray(invoice.items) || invoice.items.length === 0) {
      throw new McpError(ErrorCode.InvalidParams, 'Missing required invoice fields');
    }
    const detail = invoice.detail as Record<string, unknown>;
    if (typeof detail.invoice_number !== 'string' ||
        typeof detail.reference !== 'string' ||
        typeof detail.currency_code !== 'string') {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid invoice detail fields');
    }
    const recipient = invoice.primary_recipients[0] as Record<string, unknown>;
    if (!recipient.billing_info || typeof recipient.billing_info !== 'object' ||
        typeof (recipient.billing_info as any).email_address !== 'string') {
      throw new McpError(ErrorCode.InvalidParams, 'Invalid recipient information');
    }
    const items = invoice.items as Array<Record<string, unknown>>;
    const validatedItems = items.map(item => {
      if (typeof item.name !== 'string' ||
          typeof item.quantity !== 'string' ||
          !item.unit_amount || typeof item.unit_amount !== 'object') {
        throw new McpError(ErrorCode.InvalidParams, 'Invalid invoice item');
      }
      const amount = item.unit_amount as Record<string, unknown>;
      if (typeof amount.currency_code !== 'string' || typeof amount.value !== 'string') {
        throw new McpError(ErrorCode.InvalidParams, 'Invalid item amount');
      }
      return {
        name: item.name,
        quantity: item.quantity,
        unit_amount: {
          currency_code: amount.currency_code,
          value: amount.value
        }
      };
    });
    return {
      detail: {
        invoice_number: detail.invoice_number,
        reference: detail.reference,
        currency_code: detail.currency_code
      },
      primary_recipients: [{
        billing_info: {
          email_address: (recipient.billing_info as any).email_address
        }
      }],
      items: validatedItems
    };
  }
  private setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'create_payment_token',
          description: 'Create a payment token',
          inputSchema: {
            type: 'object',
            properties: {
              customer: {
                type: 'object',
                properties: {
                  id: { type: 'string' },
                  email_address: { type: 'string' }
                },
                required: ['id']
              },
              payment_source: {
                type: 'object',
                properties: {
                  card: {
                    type: 'object',
                    properties: {
                      name: { type: 'string' },
                      number: { type: 'string' },
                      expiry: { type: 'string' },
                      security_code: { type: 'string' }
                    }
                  },
                  paypal: {
                    type: 'object',
                    properties: {
                      email_address: { type: 'string' }
                    }
                  }
                }
              }
            },
            required: ['customer', 'payment_source']
          }
        },
        {
          name: 'create_payment',
          description: 'Create a payment',
          inputSchema: {
            type: 'object',
            properties: {
              intent: { type: 'string' },
              payer: {
                type: 'object',
                properties: {
                  payment_method: { type: 'string' },
                  funding_instruments: {
                    type: 'array',
                    items: {
                      type: 'object',
                      properties: {
                        credit_card: {
                          type: 'object',
                          properties: {
                            number: { type: 'string' },
                            type: { type: 'string' },
                            expire_month: { type: 'number' },
                            expire_year: { type: 'number' },
                            cvv2: { type: 'string' },
                            first_name: { type: 'string' },
                            last_name: { type: 'string' }
                          }
                        }
                      }
                    }
                  }
                },
                required: ['payment_method']
              },
              transactions: {
                type: 'array',
                items: {
                  type: 'object',
                  properties: {
                    amount: {
                      type: 'object',
                      properties: {
                        total: { type: 'string' },
                        currency: { type: 'string' }
                      },
                      required: ['total', 'currency']
                    },
                    description: { type: 'string' }
                  },
                  required: ['amount']
                }
              }
            },
            required: ['intent', 'payer', 'transactions']
          }
        },
        {
          name: 'create_payout',
          description: 'Create a batch payout',
          inputSchema: {
            type: 'object',
            properties: {
              sender_batch_header: {
                type: 'object',
                properties: {
                  sender_batch_id: { type: 'string' },
                  email_subject: { type: 'string' },
                  recipient_type: { type: 'string' }
                },
                required: ['sender_batch_id']
              },
              items: {
                type: 'array',
                items: {
                  type: 'object',
                  properties: {
                    recipient_type: { type: 'string' },
                    amount: {
                      type: 'object',
                      properties: {
                        value: { type: 'string' },
                        currency: { type: 'string' }
                      },
                      required: ['value', 'currency']
                    },
                    receiver: { type: 'string' },
                    note: { type: 'string' },
                    sender_item_id: { type: 'string' }
                  },
                  required: ['recipient_type', 'amount', 'receiver']
                }
              }
            },
            required: ['sender_batch_header', 'items']
          }
        },
        {
          name: 'create_referenced_payout',
          description: 'Create a referenced payout',
          inputSchema: {
            type: 'object',
            properties: {
              referenced_payouts: {
                type: 'array',
                items: {
                  type: 'object',
                  properties: {
                    reference_id: { type: 'string' },
                    reference_type: { type: 'string' },
                    payout_amount: {
                      type: 'object',
                      properties: {
                        currency_code: { type: 'string' },
                        value: { type: 'string' }
                      },
                      required: ['currency_code', 'value']
                    },
                    payout_destination: { type: 'string' }
                  },
                  required: ['reference_id', 'reference_type', 'payout_amount', 'payout_destination']
                }
              }
            },
            required: ['referenced_payouts']
          }
        },
        {
          name: 'create_order',
          description: 'Create a new order in PayPal',
          inputSchema: {
            type: 'object',
            properties: {
              intent: { 
                type: 'string',
                enum: ['CAPTURE', 'AUTHORIZE']
              },
              purchase_units: {
                type: 'array',
                items: {
                  type: 'object',
                  properties: {
                    amount: {
                      type: 'object',
                      properties: {
                        currency_code: { type: 'string' },
                        value: { type: 'string' }
                      },
                      required: ['currency_code', 'value']
                    },
                    description: { type: 'string' },
                    reference_id: { type: 'string' }
                  },
                  required: ['amount']
                }
              }
            },
            required: ['intent', 'purchase_units']
          }
        },
        {
          name: 'create_partner_referral',
          description: 'Create a partner referral',
          inputSchema: {
            type: 'object',
            properties: {
              individual_owners: {
                type: 'array',
                items: {
                  type: 'object',
                  properties: {
                    names: {
                      type: 'array',
                      items: {
                        type: 'object',
                        properties: {
                          given_name: { type: 'string' },
                          surname: { type: 'string' }
                        },
                        required: ['given_name', 'surname']
                      }
                    }
                  },
                  required: ['names']
                }
              },
              business_entity: {
                type: 'object',
                properties: {
                  business_type: {
                    type: 'object',
                    properties: {
                      type: { type: 'string' }
                    },
                    required: ['type']
                  },
                  business_name: { type: 'string' }
                },
                required: ['business_type', 'business_name']
              },
              email: { type: 'string' }
            },
            required: ['individual_owners', 'business_entity', 'email']
          }
        },
        {
          name: 'create_web_profile',
          description: 'Create a web experience profile',
          inputSchema: {
            type: 'object',
            properties: {
              name: { type: 'string' },
              presentation: {
                type: 'object',
                properties: {
                  brand_name: { type: 'string' },
                  logo_image: { type: 'string' },
                  locale_code: { type: 'string' }
                }
              },
              input_fields: {
                type: 'object',
                properties: {
                  no_shipping: { type: 'number' },
                  address_override: { type: 'number' }
                }
              },
              flow_config: {
                type: 'object',
                properties: {
                  landing_page_type: { type: 'string' },
                  bank_txn_pending_url: { type: 'string' }
                }
              }
            },
            required: ['name']
          }
        },
        {
          name: 'create_product',
          description: 'Create a new product in PayPal',
          inputSchema: {
            type: 'object',
            properties: {
              name: { type: 'string' },
              description: { type: 'string' },
              type: { 
                type: 'string',
                enum: ['PHYSICAL', 'DIGITAL', 'SERVICE']
              },
              category: { type: 'string' },
              image_url: { type: 'string' },
              home_url: { type: 'string' }
            },
            required: ['name', 'description', 'type', 'category']
          }
        },
        {
          name: 'list_products',
          description: 'List all products',
          inputSchema: {
            type: 'object',
            properties: {
              page_size: { type: 'number', minimum: 1, maximum: 100 },
              page: { type: 'number', minimum: 1 }
            }
          }
        },
        {
          name: 'get_dispute',
          description: 'Get details of a dispute',
          inputSchema: {
            type: 'object',
            properties: {
              dispute_id: { type: 'string' }
            },
            required: ['dispute_id']
          }
        },
        {
          name: 'get_userinfo',
          description: 'Get user info from identity token',
          inputSchema: {
            type: 'object',
            properties: {
              access_token: { type: 'string' }
            },
            required: ['access_token']
          }
        },
        {
          name: 'create_invoice',
          description: 'Create a new invoice',
          inputSchema: {
            type: 'object',
            properties: {
              invoice_number: { type: 'string' },
              reference: { type: 'string' },
              currency_code: { type: 'string' },
              recipient_email: { type: 'string' },
              items: {
                type: 'array',
                items: {
                  type: 'object',
                  properties: {
                    name: { type: 'string' },
                    quantity: { type: 'string' },
                    unit_amount: {
                      type: 'object',
                      properties: {
                        currency_code: { type: 'string' },
                        value: { type: 'string' }
                      }
                    }
                  }
                }
              }
            },
            required: ['invoice_number', 'reference', 'currency_code', 'recipient_email', 'items']
          }
        }
      ]
    }));
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (!request.params.arguments) {
        throw new McpError(ErrorCode.InvalidParams, 'Arguments are required');
      }
      const accessToken = await this.getAccessToken();
      const headers = {
        Authorization: `Bearer ${accessToken}`,
        'Content-Type': 'application/json'
      };
      try {
        switch (request.params.name) {
          case 'create_payment_token': {
            const args = this.validatePaymentToken(request.params.arguments);
            const response = await axios.post<PayPalPaymentToken>(
              'https://api-m.sandbox.paypal.com/v3/payment-tokens',
              args,
              { headers }
            );
            return {
              content: [{
                type: 'text',
                text: JSON.stringify(response.data, null, 2)
              }]
            };
          }
          case 'create_payment': {
            const args = this.validatePayment(request.params.arguments);
            const response = await axios.post<PayPalPayment>(
              'https://api-m.sandbox.paypal.com/v2/payments/payment',
              args,
              { headers }
            );
            return {
              content: [{
                type: 'text',
                text: JSON.stringify(response.data, null, 2)
              }]
            };
          }
          case 'create_payout': {
            const args = this.validatePayout(request.params.arguments);
            const response = await axios.post<PayPalPayout>(
              'https://api-m.sandbox.paypal.com/v1/payments/payouts',
              args,
              { headers }
            );
            return {
              content: [{
                type: 'text',
                text: JSON.stringify(response.data, null, 2)
              }]
            };
          }
          case 'create_referenced_payout': {
            const args = this.validateReferencedPayout(request.params.arguments);
            const response = await axios.post<PayPalReferencedPayout>(
              'https://api-m.sandbox.paypal.com/v1/payments/referenced-payouts',
              args,
              { headers }
            );
            return {
              content: [{
                type: 'text',
                text: JSON.stringify(response.data, null, 2)
              }]
            };
          }
          case 'create_order': {
            const args = this.validatePayPalOrder(request.params.arguments);
            const response = await axios.post<PayPalOrder>(
              'https://api-m.sandbox.paypal.com/v2/checkout/orders',
              args,
              { headers }
            );
            return {
              content: [{
                type: 'text',
                text: JSON.stringify(response.data, null, 2)
              }]
            };
          }
          case 'create_partner_referral': {
            const args = this.validatePartnerReferral(request.params.arguments);
            const response = await axios.post<PayPalPartnerReferral>(
              'https://api-m.sandbox.paypal.com/v2/customer/partner-referrals',
              args,
              { headers }
            );
            return {
              content: [{
                type: 'text',
                text: JSON.stringify(response.data, null, 2)
              }]
            };
          }
          case 'create_web_profile': {
            const args = this.validateWebProfile(request.params.arguments);
            const response = await axios.post<PayPalWebProfile>(
              'https://api-m.sandbox.paypal.com/v1/payment-experience/web-profiles',
              args,
              { headers }
            );
            return {
              content: [{
                type: 'text',
                text: JSON.stringify(response.data, null, 2)
              }]
            };
          }
          case 'create_product': {
            const args = this.validatePayPalProduct(request.params.arguments);
            const response = await axios.post<PayPalResponse>(
              'https://api-m.sandbox.paypal.com/v1/catalogs/products',
              args,
              { headers }
            );
            return {
              content: [{
                type: 'text',
                text: JSON.stringify(response.data, null, 2)
              }]
            };
          }
          case 'list_products': {
            const args = this.validatePaginationParams(request.params.arguments);
            const params = new URLSearchParams({
              page_size: args.page_size?.toString() || '10',
              page: args.page?.toString() || '1'
            });
            const response = await axios.get<PayPalResponse>(
              `https://api-m.sandbox.paypal.com/v1/catalogs/products?${params}`,
              { headers }
            );
            return {
              content: [{
                type: 'text',
                text: JSON.stringify(response.data, null, 2)
              }]
            };
          }
          case 'get_dispute': {
            const args = this.validateDisputeParams(request.params.arguments);
            const response = await axios.get<PayPalDispute>(
              `https://api-m.sandbox.paypal.com/v1/customer/disputes/${args.dispute_id}`,
              { headers }
            );
            return {
              content: [{
                type: 'text',
                text: JSON.stringify(response.data, null, 2)
              }]
            };
          }
          case 'get_userinfo': {
            const args = this.validateTokenParams(request.params.arguments);
            const response = await axios.get<PayPalIdentityTokenInfo>(
              'https://api-m.sandbox.paypal.com/v1/identity/oauth2/userinfo',
              {
                headers: {
                  Authorization: `Bearer ${args.access_token}`,
                  'Content-Type': 'application/json'
                }
              }
            );
            return {
              content: [{
                type: 'text',
                text: JSON.stringify(response.data, null, 2)
              }]
            };
          }
          case 'create_invoice': {
            const args = this.validatePayPalInvoice(request.params.arguments);
            const invoiceData: PayPalInvoice = {
              detail: {
                invoice_number: args.detail.invoice_number,
                reference: args.detail.reference,
                currency_code: args.detail.currency_code
              },
              primary_recipients: [{
                billing_info: {
                  email_address: args.primary_recipients[0].billing_info.email_address
                }
              }],
              items: args.items
            };
            const response = await axios.post<PayPalInvoice>(
              'https://api-m.sandbox.paypal.com/v2/invoicing/invoices',
              invoiceData,
              { headers }
            );
            return {
              content: [{
                type: 'text',
                text: JSON.stringify(response.data, null, 2)
              }]
            };
          }
          default:
            throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
        }
      } catch (error) {
        if (isAxiosError(error)) {
          return {
            content: [{
              type: 'text',
              text: `PayPal API error: ${error.response?.data?.message || error.message}`
            }],
            isError: true
          };
        }
        const err = error as Error;
        throw new McpError(ErrorCode.InternalError, err.message || 'An unexpected error occurred');
      }
    });
  }
  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('PayPal MCP server running on stdio');
  }
}
const server = new PayPalServer();
server.run().catch(console.error);