Jake Shore d25ea2031b Gold standard upgrade: greenhouse, lever, loom
- Greenhouse: 29 tools (was 18), added interviews, scorecards, organization
- Lever: 26 tools (was 13), added tags, sources, expanded opportunities/postings
- Loom: 25 tools (was 14), added analytics, privacy, search, workspace members

All servers now have:
- main.ts with env validation & graceful shutdown
- server.ts with lazy-loaded tool modules
- Zod validation on all inputs
- Rich tool descriptions (when/why to use)
- Pagination support on all list_* tools
- Updated package.json (bin field, updated deps)
- Updated README with coverage manifests
- Old index.ts renamed to index.ts.bak
- Zero TypeScript errors (npx tsc --noEmit verified)
2026-02-14 05:52:42 -05:00

382 lines
11 KiB
JavaScript

#!/usr/bin/env node
/**
* Chargebee MCP Server
* Provides tools for interacting with the Chargebee subscription billing platform
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ErrorCode,
McpError,
} from '@modelcontextprotocol/sdk/types.js';
import { ChargebeeClient } from './client/chargebee-client.js';
// Import all tool definitions
import {
listSubscriptionsTool,
getSubscriptionTool,
createSubscriptionTool,
updateSubscriptionTool,
cancelSubscriptionTool,
reactivateSubscriptionTool,
} from './tools/subscriptions.js';
import {
listCustomersTool,
getCustomerTool,
createCustomerTool,
updateCustomerTool,
deleteCustomerTool,
} from './tools/customers.js';
import {
listInvoicesTool,
getInvoiceTool,
createInvoiceTool,
} from './tools/invoices.js';
import {
listPlansTool,
getPlanTool,
listAddonsTool,
getAddonTool,
} from './tools/plans.js';
import {
listCouponsTool,
getCouponTool,
createCouponTool,
} from './tools/coupons.js';
import {
listCreditNotesTool,
getCreditNoteTool,
createCreditNoteTool,
} from './tools/credit_notes.js';
// Collect all tools
const ALL_TOOLS = [
// Subscriptions (6 tools)
listSubscriptionsTool,
getSubscriptionTool,
createSubscriptionTool,
updateSubscriptionTool,
cancelSubscriptionTool,
reactivateSubscriptionTool,
// Customers (5 tools)
listCustomersTool,
getCustomerTool,
createCustomerTool,
updateCustomerTool,
deleteCustomerTool,
// Invoices (3 tools)
listInvoicesTool,
getInvoiceTool,
createInvoiceTool,
// Plans & Addons (4 tools)
listPlansTool,
getPlanTool,
listAddonsTool,
getAddonTool,
// Coupons (3 tools)
listCouponsTool,
getCouponTool,
createCouponTool,
// Credit Notes (3 tools)
listCreditNotesTool,
getCreditNoteTool,
createCreditNoteTool,
];
// Initialize Chargebee client
const siteName = process.env.CHARGEBEE_SITE_NAME;
const apiKey = process.env.CHARGEBEE_API_KEY;
if (!siteName || !apiKey) {
throw new Error('CHARGEBEE_SITE_NAME and CHARGEBEE_API_KEY environment variables are required');
}
const chargebeeClient = new ChargebeeClient({ siteName, apiKey });
// Create MCP server
const server = new Server(
{
name: 'chargebee-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// Register tool list handler
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: ALL_TOOLS,
};
});
// Register tool call handler
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
// Subscription tools
case 'list_subscriptions':
return await handleListSubscriptions(args);
case 'get_subscription':
return await handleGetSubscription(args);
case 'create_subscription':
return await handleCreateSubscription(args);
case 'update_subscription':
return await handleUpdateSubscription(args);
case 'cancel_subscription':
return await handleCancelSubscription(args);
case 'reactivate_subscription':
return await handleReactivateSubscription(args);
// Customer tools
case 'list_customers':
return await handleListCustomers(args);
case 'get_customer':
return await handleGetCustomer(args);
case 'create_customer':
return await handleCreateCustomer(args);
case 'update_customer':
return await handleUpdateCustomer(args);
case 'delete_customer':
return await handleDeleteCustomer(args);
// Invoice tools
case 'list_invoices':
return await handleListInvoices(args);
case 'get_invoice':
return await handleGetInvoice(args);
case 'create_invoice':
return await handleCreateInvoice(args);
// Plan & Addon tools
case 'list_plans':
return await handleListPlans(args);
case 'get_plan':
return await handleGetPlan(args);
case 'list_addons':
return await handleListAddons(args);
case 'get_addon':
return await handleGetAddon(args);
// Coupon tools
case 'list_coupons':
return await handleListCoupons(args);
case 'get_coupon':
return await handleGetCoupon(args);
case 'create_coupon':
return await handleCreateCoupon(args);
// Credit note tools
case 'list_credit_notes':
return await handleListCreditNotes(args);
case 'get_credit_note':
return await handleGetCreditNote(args);
case 'create_credit_note':
return await handleCreateCreditNote(args);
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
}
} catch (error) {
if (error instanceof McpError) throw error;
throw new McpError(
ErrorCode.InternalError,
`Tool execution failed: ${error instanceof Error ? error.message : String(error)}`
);
}
});
// Tool implementation functions
async function handleListSubscriptions(args: any) {
const result = await chargebeeClient.getPaginated('/subscriptions', args, args.limit);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleGetSubscription(args: any) {
const result = await chargebeeClient.get(`/subscriptions/${args.id}`);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleCreateSubscription(args: any) {
const result = await chargebeeClient.postFormEncoded('/subscriptions', args);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleUpdateSubscription(args: any) {
const { id, ...updateData } = args;
const result = await chargebeeClient.postFormEncoded(`/subscriptions/${id}`, updateData);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleCancelSubscription(args: any) {
const { id, ...cancelData } = args;
const result = await chargebeeClient.postFormEncoded(`/subscriptions/${id}/cancel`, cancelData);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleReactivateSubscription(args: any) {
const { id, ...reactivateData } = args;
const result = await chargebeeClient.postFormEncoded(`/subscriptions/${id}/reactivate`, reactivateData);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleListCustomers(args: any) {
const result = await chargebeeClient.getPaginated('/customers', args, args.limit);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleGetCustomer(args: any) {
const result = await chargebeeClient.get(`/customers/${args.id}`);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleCreateCustomer(args: any) {
const result = await chargebeeClient.postFormEncoded('/customers', args);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleUpdateCustomer(args: any) {
const { id, ...updateData } = args;
const result = await chargebeeClient.postFormEncoded(`/customers/${id}`, updateData);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleDeleteCustomer(args: any) {
const result = await chargebeeClient.postFormEncoded(`/customers/${args.id}/delete`, {});
return {
content: [{ type: 'text', text: JSON.stringify({ success: true, id: args.id }, null, 2) }],
};
}
async function handleListInvoices(args: any) {
const result = await chargebeeClient.getPaginated('/invoices', args, args.limit);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleGetInvoice(args: any) {
const result = await chargebeeClient.get(`/invoices/${args.id}`);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleCreateInvoice(args: any) {
const result = await chargebeeClient.postFormEncoded('/invoices', args);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleListPlans(args: any) {
const result = await chargebeeClient.getPaginated('/plans', args, args.limit);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleGetPlan(args: any) {
const result = await chargebeeClient.get(`/plans/${args.id}`);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleListAddons(args: any) {
const result = await chargebeeClient.getPaginated('/addons', args, args.limit);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleGetAddon(args: any) {
const result = await chargebeeClient.get(`/addons/${args.id}`);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleListCoupons(args: any) {
const result = await chargebeeClient.getPaginated('/coupons', args, args.limit);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleGetCoupon(args: any) {
const result = await chargebeeClient.get(`/coupons/${args.id}`);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleCreateCoupon(args: any) {
const result = await chargebeeClient.postFormEncoded('/coupons', args);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleListCreditNotes(args: any) {
const result = await chargebeeClient.getPaginated('/credit_notes', args, args.limit);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleGetCreditNote(args: any) {
const result = await chargebeeClient.get(`/credit_notes/${args.id}`);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
async function handleCreateCreditNote(args: any) {
const result = await chargebeeClient.postFormEncoded('/credit_notes', args);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Chargebee MCP Server running on stdio');
}
main().catch((error) => {
console.error('Fatal error in main():', error);
process.exit(1);
});