206 lines
6.1 KiB
TypeScript

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ListResourcesRequestSchema,
ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { BrevoApiClient } from './types/api-client.js';
import { createContactsTools } from './tools/contacts-tools.js';
import { createCampaignsTools } from './tools/campaigns-tools.js';
import { createTransactionalTools } from './tools/transactional-tools.js';
import { createListsTools } from './tools/lists-tools.js';
import { createSendersTools } from './tools/senders-tools.js';
import { createTemplatesTools } from './tools/templates-tools.js';
import { createAutomationsTools } from './tools/automations-tools.js';
import { createSmsTools } from './tools/sms-tools.js';
import { createDealsTools } from './tools/deals-tools.js';
import { createWebhooksTools } from './tools/webhooks-tools.js';
import { contactDashboardApp } from './apps/contact-dashboard.js';
import { contactDetailApp } from './apps/contact-detail.js';
import { contactGridApp } from './apps/contact-grid.js';
import { campaignDashboardApp } from './apps/campaign-dashboard.js';
import { campaignBuilderApp } from './apps/campaign-builder.js';
import { automationDashboardApp } from './apps/automation-dashboard.js';
import { dealPipelineApp } from './apps/deal-pipeline.js';
import { transactionalMonitorApp } from './apps/transactional-monitor.js';
import { templateGalleryApp } from './apps/template-gallery.js';
import { smsDashboardApp } from './apps/sms-dashboard.js';
import { listManagerApp } from './apps/list-manager.js';
import { reportDashboardApp } from './apps/report-dashboard.js';
import { webhookManagerApp } from './apps/webhook-manager.js';
import { importWizardApp } from './apps/import-wizard.js';
export class BrevoServer {
private server: Server;
private client: BrevoApiClient;
private tools: any[];
private apps: any[];
constructor(apiKey: string) {
this.server = new Server(
{
name: 'brevo-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
resources: {},
},
}
);
this.client = new BrevoApiClient({ apiKey });
// Initialize all tools
this.tools = [
...createContactsTools(this.client),
...createCampaignsTools(this.client),
...createTransactionalTools(this.client),
...createListsTools(this.client),
...createSendersTools(this.client),
...createTemplatesTools(this.client),
...createAutomationsTools(this.client),
...createSmsTools(this.client),
...createDealsTools(this.client),
...createWebhooksTools(this.client),
];
// Initialize all apps
this.apps = [
contactDashboardApp(),
contactDetailApp(),
contactGridApp(),
campaignDashboardApp(),
campaignBuilderApp(),
automationDashboardApp(),
dealPipelineApp(),
transactionalMonitorApp(),
templateGalleryApp(),
smsDashboardApp(),
listManagerApp(),
reportDashboardApp(),
webhookManagerApp(),
importWizardApp(),
];
this.setupHandlers();
}
private setupHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: this.tools.map(tool => ({
name: tool.name,
description: tool.description,
inputSchema: {
type: 'object',
properties: tool.inputSchema.shape,
required: Object.keys(tool.inputSchema.shape).filter(
key => !tool.inputSchema.shape[key].isOptional()
),
},
})),
};
});
// Execute tool
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const tool = this.tools.find(t => t.name === request.params.name);
if (!tool) {
throw new Error(`Tool not found: ${request.params.name}`);
}
try {
const result = await tool.execute(request.params.arguments || {});
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error: any) {
return {
content: [
{
type: 'text',
text: `Error: ${error.message}`,
},
],
isError: true,
};
}
});
// List resources (MCP apps)
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: this.apps.map(app => ({
uri: `brevo://app/${app.name}`,
name: app.name,
description: app.description,
mimeType: 'text/html',
})),
};
});
// Read resource (render app UI)
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const appName = request.params.uri.replace('brevo://app/', '');
const app = this.apps.find(a => a.name === appName);
if (!app) {
throw new Error(`App not found: ${appName}`);
}
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${app.ui.title}</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 1400px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
h1, h2, h3, h4 { color: #2c3e50; }
${app.ui.style}
</style>
</head>
<body>
${app.ui.content}
</body>
</html>
`;
return {
contents: [
{
uri: request.params.uri,
mimeType: 'text/html',
text: html,
},
],
};
});
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('Brevo MCP Server running on stdio');
}
}