PayPal MCP

#!/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);