- 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)
382 lines
11 KiB
JavaScript
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);
|
|
});
|