Skip to main content
Glama
fluentcrm-mcp-server.ts36 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import axios, { AxiosInstance } from 'axios'; import * as dotenv from 'dotenv'; dotenv.config(); const FLUENTCRM_API_URL = process.env.FLUENTCRM_API_URL || 'https://your-domain.com/wp-json/fluent-crm/v2'; const FLUENTCRM_API_USERNAME = process.env.FLUENTCRM_API_USERNAME || ''; const FLUENTCRM_API_PASSWORD = process.env.FLUENTCRM_API_PASSWORD || ''; /** * FluentCRM API Client * Based on: https://rest-api.fluentcrm.com/#introduction */ class FluentCRMClient { private apiClient: AxiosInstance; private baseURL: string; constructor(baseURL: string, username: string, password: string) { this.baseURL = baseURL; // Basic Auth dla FluentCRM API const credentials = Buffer.from(`${username}:${password}`).toString('base64'); this.apiClient = axios.create({ baseURL, headers: { 'Authorization': `Basic ${credentials}`, 'Content-Type': 'application/json', 'Accept': 'application/json', }, timeout: 30000, }); // Error interceptor this.apiClient.interceptors.response.use( response => response, error => { const message = error.response?.data?.message || error.message; throw new Error(`FluentCRM API Error: ${message}`); } ); } // ===== SUBSCRIBERS / KONTAKTY ===== async listContacts(params: any = {}) { const response = await this.apiClient.get('/subscribers', { params }); return response.data; } async getContact(subscriberId: number) { const response = await this.apiClient.get(`/subscribers/${subscriberId}`); return response.data; } async findContactByEmail(email: string) { const response = await this.apiClient.get('/subscribers', { params: { search: email }, }); return response.data.data?.[0] || null; } async createContact(data: { email: string; first_name?: string; last_name?: string; phone?: string; address_line_1?: string; city?: string; state?: string; country?: string; postal_code?: string; [key: string]: any; }) { const response = await this.apiClient.post('/subscribers', data); return response.data; } async updateContact(subscriberId: number, data: any) { const response = await this.apiClient.put(`/subscribers/${subscriberId}`, data); return response.data; } async deleteContact(subscriberId: number) { const response = await this.apiClient.delete(`/subscribers/${subscriberId}`); return response.data; } // ===== TAGI ===== async listTags(params: any = {}) { const response = await this.apiClient.get('/tags', { params }); return response.data; } async getTag(tagId: number) { const response = await this.apiClient.get(`/tags/${tagId}`); return response.data; } async createTag(data: { title: string; slug?: string; description?: string; }) { const response = await this.apiClient.post('/tags', data); return response.data; } async updateTag(tagId: number, data: any) { const response = await this.apiClient.put(`/tags/${tagId}`, data); return response.data; } async deleteTag(tagId: number) { const response = await this.apiClient.delete(`/tags/${tagId}`); return response.data; } async attachTagToContact(subscriberId: number, tagIds: number[]) { const response = await this.apiClient.post( `/subscribers/${subscriberId}/tags`, { tags: tagIds } ); return response.data; } async detachTagFromContact(subscriberId: number, tagIds: number[]) { const response = await this.apiClient.post( `/subscribers/${subscriberId}/tags/detach`, { tags: tagIds } ); return response.data; } // ===== LISTY ===== async listLists(params: any = {}) { const response = await this.apiClient.get('/lists', { params }); return response.data; } async getList(listId: number) { const response = await this.apiClient.get(`/lists/${listId}`); return response.data; } async createList(data: { title: string; slug?: string; description?: string; }) { const response = await this.apiClient.post('/lists', data); return response.data; } async updateList(listId: number, data: any) { const response = await this.apiClient.put(`/lists/${listId}`, data); return response.data; } async deleteList(listId: number) { const response = await this.apiClient.delete(`/lists/${listId}`); return response.data; } async attachContactToList(subscriberId: number, listIds: number[]) { const response = await this.apiClient.post( `/subscribers/${subscriberId}/lists`, { lists: listIds } ); return response.data; } async detachContactFromList(subscriberId: number, listIds: number[]) { const response = await this.apiClient.post( `/subscribers/${subscriberId}/lists/detach`, { lists: listIds } ); return response.data; } // ===== KAMPANIE ===== async listCampaigns(params: any = {}) { const response = await this.apiClient.get('/campaigns', { params }); return response.data; } async getCampaign(campaignId: number) { const response = await this.apiClient.get(`/campaigns/${campaignId}`); return response.data; } async createCampaign(data: any) { const response = await this.apiClient.post('/campaigns', data); return response.data; } async updateCampaign(campaignId: number, data: any) { const response = await this.apiClient.put(`/campaigns/${campaignId}`, data); return response.data; } async pauseCampaign(campaignId: number) { const response = await this.apiClient.post(`/campaigns/${campaignId}/pause`); return response.data; } async resumeCampaign(campaignId: number) { const response = await this.apiClient.post(`/campaigns/${campaignId}/resume`); return response.data; } async deleteCampaign(campaignId: number) { const response = await this.apiClient.delete(`/campaigns/${campaignId}`); return response.data; } // ===== EMAIL TEMPLATES ===== async listEmailTemplates(params: any = {}) { const response = await this.apiClient.get('/email-templates', { params }); return response.data; } async getEmailTemplate(templateId: number) { const response = await this.apiClient.get(`/email-templates/${templateId}`); return response.data; } async createEmailTemplate(data: any) { const response = await this.apiClient.post('/email-templates', data); return response.data; } async updateEmailTemplate(templateId: number, data: any) { const response = await this.apiClient.put(`/email-templates/${templateId}`, data); return response.data; } async deleteEmailTemplate(templateId: number) { const response = await this.apiClient.delete(`/email-templates/${templateId}`); return response.data; } // ===== AUTOMATION FUNNELS ===== async listAutomations(params: any = {}) { const response = await this.apiClient.get('/funnels', { params }); return response.data; } async getAutomation(funnelId: number) { const response = await this.apiClient.get(`/funnels/${funnelId}`); return response.data; } async createAutomation(data: any) { const response = await this.apiClient.post('/funnels', data); return response.data; } async updateAutomation(funnelId: number, data: any) { const response = await this.apiClient.put(`/funnels/${funnelId}`, data); return response.data; } async deleteAutomation(funnelId: number) { const response = await this.apiClient.delete(`/funnels/${funnelId}`); return response.data; } // ===== WEBHOOKS ===== async listWebhooks(params: any = {}) { const response = await this.apiClient.get('/webhooks', { params }); return response.data; } async createWebhook(data: { name: string; status: 'pending' | 'subscribed'; url: string; tags?: number[]; lists?: number[]; }) { const response = await this.apiClient.post('/webhook', data); return response.data; } async updateWebhook(webhookId: number, data: any) { const response = await this.apiClient.put(`/webhook/${webhookId}`, data); return response.data; } async deleteWebhook(webhookId: number) { const response = await this.apiClient.delete(`/webhook/${webhookId}`); return response.data; } // ===== CUSTOM FIELDS ===== async listCustomFields() { const response = await this.apiClient.get('/custom-fields'); return response.data; } // ===== SMART LINKS ===== // Note: FluentCRM doesn't have native REST API for Smart Links yet // These methods prepare for future API or work with existing endpoints async listSmartLinks(params: any = {}) { // Try to get smart links - this might not work until FluentCRM adds the endpoint try { const response = await this.apiClient.get('/smart-links', { params }); return response.data; } catch (error: any) { // If endpoint doesn't exist, return helpful message if (error.response?.status === 404) { return { success: false, message: "Smart Links API endpoint not available yet in FluentCRM", suggestion: "Use FluentCRM admin panel to manage Smart Links manually", available_endpoints: [ "FluentCRM → Smart Links (admin panel)", "Custom WordPress hooks for Smart Links" ] }; } throw error; } } async getSmartLink(smartLinkId: number) { try { const response = await this.apiClient.get(`/smart-links/${smartLinkId}`); return response.data; } catch (error: any) { if (error.response?.status === 404) { return { success: false, message: "Smart Links API endpoint not available yet in FluentCRM", suggestion: "Use FluentCRM admin panel to view Smart Link details" }; } throw error; } } async createSmartLink(data: { title: string; slug?: string; target_url: string; apply_tags?: number[]; apply_lists?: number[]; remove_tags?: number[]; remove_lists?: number[]; auto_login?: boolean; }) { try { const response = await this.apiClient.post('/smart-links', data); return response.data; } catch (error: any) { if (error.response?.status === 404) { return { success: false, message: "Smart Links API endpoint not available yet in FluentCRM", suggestion: "Create Smart Link manually in FluentCRM admin panel", recommended_data: data }; } throw error; } } async updateSmartLink(smartLinkId: number, data: any) { try { const response = await this.apiClient.put(`/smart-links/${smartLinkId}`, data); return response.data; } catch (error: any) { if (error.response?.status === 404) { return { success: false, message: "Smart Links API endpoint not available yet in FluentCRM", suggestion: "Update Smart Link manually in FluentCRM admin panel" }; } throw error; } } async deleteSmartLink(smartLinkId: number) { try { const response = await this.apiClient.delete(`/smart-links/${smartLinkId}`); return response.data; } catch (error: any) { if (error.response?.status === 404) { return { success: false, message: "Smart Links API endpoint not available yet in FluentCRM", suggestion: "Delete Smart Link manually in FluentCRM admin panel" }; } throw error; } } // Helper method to generate Smart Link shortcode generateSmartLinkShortcode(slug: string, linkText?: string): string { if (linkText) { return `<a href="{{fc_smart_link slug='${slug}'}}">${linkText}</a>`; } return `{{fc_smart_link slug='${slug}'}}`; } // Helper method to validate Smart Link data validateSmartLinkData(data: any): { valid: boolean; errors: string[] } { const errors: string[] = []; if (!data.title || typeof data.title !== 'string') { errors.push('Title is required and must be a string'); } if (!data.target_url || typeof data.target_url !== 'string') { errors.push('Target URL is required and must be a string'); } if (data.target_url && !data.target_url.startsWith('http')) { errors.push('Target URL must start with http:// or https://'); } if (data.slug && !/^[a-z0-9-]+$/.test(data.slug)) { errors.push('Slug must contain only lowercase letters, numbers, and hyphens'); } return { valid: errors.length === 0, errors }; } // ===== REPORTS ===== async getDashboardStats() { const response = await this.apiClient.get('/reports/dashboard-stats'); return response.data; } async getSubscribersGrowthRate(params: any = {}) { const response = await this.apiClient.get('/reports/subscribers-growth-rate', { params }); return response.data; } } // ===== MCP SERVER SETUP ===== const server = new Server( { name: 'fluentcrm-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); const client = new FluentCRMClient( FLUENTCRM_API_URL, FLUENTCRM_API_USERNAME, FLUENTCRM_API_PASSWORD ); server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ // ===== KONTAKTY ===== { name: 'fluentcrm_list_contacts', description: 'Pobiera listę wszystkich kontaktów z FluentCRM', inputSchema: { type: 'object', properties: { page: { type: 'number', description: 'Numer strony (default: 1)' }, per_page: { type: 'number', description: 'Ilość rekordów na stronę (default: 10)' }, search: { type: 'string', description: 'Szukaj po emailu/imieniu' }, }, }, }, { name: 'fluentcrm_get_contact', description: 'Pobiera szczegóły konkretnego kontaktu', inputSchema: { type: 'object', properties: { subscriberId: { type: 'number', description: 'ID kontaktu' }, }, required: ['subscriberId'], }, }, { name: 'fluentcrm_find_contact_by_email', description: 'Wyszukuje kontakt po adresie email', inputSchema: { type: 'object', properties: { email: { type: 'string', description: 'Adres email' }, }, required: ['email'], }, }, { name: 'fluentcrm_create_contact', description: 'Tworzy nowy kontakt w FluentCRM', inputSchema: { type: 'object', properties: { email: { type: 'string', description: 'Email kontaktu' }, first_name: { type: 'string', description: 'Imię' }, last_name: { type: 'string', description: 'Nazwisko' }, phone: { type: 'string', description: 'Numer telefonu' }, address_line_1: { type: 'string', description: 'Adres' }, city: { type: 'string', description: 'Miasto' }, country: { type: 'string', description: 'Kraj' }, }, required: ['email'], }, }, { name: 'fluentcrm_update_contact', description: 'Aktualizuje dane kontaktu', inputSchema: { type: 'object', properties: { subscriberId: { type: 'number', description: 'ID kontaktu' }, first_name: { type: 'string' }, last_name: { type: 'string' }, phone: { type: 'string' }, }, required: ['subscriberId'], }, }, { name: 'fluentcrm_delete_contact', description: 'Usuwa kontakt z FluentCRM', inputSchema: { type: 'object', properties: { subscriberId: { type: 'number', description: 'ID kontaktu do usunięcia' }, }, required: ['subscriberId'], }, }, // ===== TAGI ===== { name: 'fluentcrm_list_tags', description: 'Pobiera wszystkie tagi z FluentCRM', inputSchema: { type: 'object', properties: { page: { type: 'number', description: 'Numer strony' }, search: { type: 'string', description: 'Szukaj tagu' }, }, }, }, { name: 'fluentcrm_create_tag', description: 'Tworzy nowy tag w FluentCRM', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Nazwa tagu (np. "AW-progress-75")' }, slug: { type: 'string', description: 'Slug tagu (np. "aw-progress-75")' }, description: { type: 'string', description: 'Opis tagu' }, }, required: ['title'], }, }, { name: 'fluentcrm_delete_tag', description: 'Usuwa tag z FluentCRM', inputSchema: { type: 'object', properties: { tagId: { type: 'number', description: 'ID tagu' }, }, required: ['tagId'], }, }, { name: 'fluentcrm_attach_tag_to_contact', description: 'Przypisuje tag do kontaktu', inputSchema: { type: 'object', properties: { subscriberId: { type: 'number', description: 'ID kontaktu' }, tagIds: { type: 'array', items: { type: 'number' }, description: 'Lista ID tagów' }, }, required: ['subscriberId', 'tagIds'], }, }, { name: 'fluentcrm_detach_tag_from_contact', description: 'Usuwa tag z kontaktu', inputSchema: { type: 'object', properties: { subscriberId: { type: 'number', description: 'ID kontaktu' }, tagIds: { type: 'array', items: { type: 'number' }, description: 'Lista ID tagów' }, }, required: ['subscriberId', 'tagIds'], }, }, // ===== LISTY ===== { name: 'fluentcrm_list_lists', description: 'Pobiera wszystkie listy z FluentCRM', inputSchema: { type: 'object', properties: {}, }, }, { name: 'fluentcrm_create_list', description: 'Tworzy nową listę w FluentCRM', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Nazwa listy' }, slug: { type: 'string', description: 'Slug listy' }, description: { type: 'string', description: 'Opis listy' }, }, required: ['title'], }, }, { name: 'fluentcrm_delete_list', description: 'Usuwa listę z FluentCRM', inputSchema: { type: 'object', properties: { listId: { type: 'number', description: 'ID listy' }, }, required: ['listId'], }, }, { name: 'fluentcrm_attach_contact_to_list', description: 'Przypisuje kontakt do listy', inputSchema: { type: 'object', properties: { subscriberId: { type: 'number', description: 'ID kontaktu' }, listIds: { type: 'array', items: { type: 'number' }, description: 'Lista ID list' }, }, required: ['subscriberId', 'listIds'], }, }, { name: 'fluentcrm_detach_contact_from_list', description: 'Usuwa kontakt z listy', inputSchema: { type: 'object', properties: { subscriberId: { type: 'number', description: 'ID kontaktu' }, listIds: { type: 'array', items: { type: 'number' }, description: 'Lista ID list' }, }, required: ['subscriberId', 'listIds'], }, }, // ===== KAMPANIE ===== { name: 'fluentcrm_list_campaigns', description: 'Pobiera listę kampanii email', inputSchema: { type: 'object', properties: { page: { type: 'number' }, search: { type: 'string' }, }, }, }, { name: 'fluentcrm_create_campaign', description: 'Tworzy nową kampanię email', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Tytuł kampanii' }, subject: { type: 'string', description: 'Temat emaila' }, template_id: { type: 'number', description: 'ID szablonu' }, recipient_list: { type: 'array', items: { type: 'number' }, description: 'ID list' }, }, required: ['title', 'subject'], }, }, { name: 'fluentcrm_pause_campaign', description: 'Wstrzymuje kampanię', inputSchema: { type: 'object', properties: { campaignId: { type: 'number', description: 'ID kampanii' }, }, required: ['campaignId'], }, }, { name: 'fluentcrm_resume_campaign', description: 'Wznawia kampanię', inputSchema: { type: 'object', properties: { campaignId: { type: 'number', description: 'ID kampanii' }, }, required: ['campaignId'], }, }, { name: 'fluentcrm_delete_campaign', description: 'Usuwa kampanię', inputSchema: { type: 'object', properties: { campaignId: { type: 'number', description: 'ID kampanii' }, }, required: ['campaignId'], }, }, // ===== EMAIL TEMPLATES ===== { name: 'fluentcrm_list_email_templates', description: 'Pobiera szablony email', inputSchema: { type: 'object', properties: {}, }, }, { name: 'fluentcrm_create_email_template', description: 'Tworzy nowy szablon email', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Nazwa szablonu' }, subject: { type: 'string', description: 'Temat' }, body: { type: 'string', description: 'Treść HTML' }, }, required: ['title', 'subject', 'body'], }, }, // ===== AUTOMATYZACJE ===== { name: 'fluentcrm_list_automations', description: 'Pobiera automatyzacje (funnels)', inputSchema: { type: 'object', properties: { page: { type: 'number' }, search: { type: 'string' }, }, }, }, { name: 'fluentcrm_create_automation', description: 'Tworzy nową automatyzację', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Nazwa automatyzacji' }, description: { type: 'string' }, trigger: { type: 'string', description: 'Typ triggera' }, }, required: ['title', 'trigger'], }, }, // ===== WEBHOOKS ===== { name: 'fluentcrm_list_webhooks', description: 'Pobiera webhooks', inputSchema: { type: 'object', properties: {}, }, }, { name: 'fluentcrm_create_webhook', description: 'Tworzy nowy webhook', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Nazwa webhook' }, url: { type: 'string', description: 'URL webhook' }, status: { type: 'string', enum: ['pending', 'subscribed'] }, tags: { type: 'array', items: { type: 'number' } }, lists: { type: 'array', items: { type: 'number' } }, }, required: ['name', 'url', 'status'], }, }, // ===== SMART LINKS ===== { name: 'fluentcrm_list_smart_links', description: 'Pobiera listę Smart Links z FluentCRM (może nie być dostępne w obecnej wersji)', inputSchema: { type: 'object', properties: { page: { type: 'number', description: 'Numer strony' }, search: { type: 'string', description: 'Szukaj Smart Link' }, }, }, }, { name: 'fluentcrm_get_smart_link', description: 'Pobiera szczegóły konkretnego Smart Link', inputSchema: { type: 'object', properties: { smartLinkId: { type: 'number', description: 'ID Smart Link' }, }, required: ['smartLinkId'], }, }, { name: 'fluentcrm_create_smart_link', description: 'Tworzy nowy Smart Link (może nie być dostępne w obecnej wersji)', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Nazwa Smart Link (np. "AW-Link-Webinar-Mail")' }, slug: { type: 'string', description: 'Slug (np. "aw-link-webinar-mail")' }, target_url: { type: 'string', description: 'Docelowy URL' }, apply_tags: { type: 'array', items: { type: 'number' }, description: 'ID tagów do dodania po kliknięciu' }, apply_lists: { type: 'array', items: { type: 'number' }, description: 'ID list do dodania po kliknięciu' }, remove_tags: { type: 'array', items: { type: 'number' }, description: 'ID tagów do usunięcia po kliknięciu' }, remove_lists: { type: 'array', items: { type: 'number' }, description: 'ID list do usunięcia po kliknięciu' }, auto_login: { type: 'boolean', description: 'Czy automatycznie logować użytkownika' }, }, required: ['title', 'target_url'], }, }, { name: 'fluentcrm_update_smart_link', description: 'Aktualizuje Smart Link (może nie być dostępne w obecnej wersji)', inputSchema: { type: 'object', properties: { smartLinkId: { type: 'number', description: 'ID Smart Link' }, title: { type: 'string' }, target_url: { type: 'string' }, apply_tags: { type: 'array', items: { type: 'number' } }, apply_lists: { type: 'array', items: { type: 'number' } }, remove_tags: { type: 'array', items: { type: 'number' } }, remove_lists: { type: 'array', items: { type: 'number' } }, auto_login: { type: 'boolean' }, }, required: ['smartLinkId'], }, }, { name: 'fluentcrm_delete_smart_link', description: 'Usuwa Smart Link (może nie być dostępne w obecnej wersji)', inputSchema: { type: 'object', properties: { smartLinkId: { type: 'number', description: 'ID Smart Link do usunięcia' }, }, required: ['smartLinkId'], }, }, { name: 'fluentcrm_generate_smart_link_shortcode', description: 'Generuje shortcode dla Smart Link', inputSchema: { type: 'object', properties: { slug: { type: 'string', description: 'Slug Smart Link' }, linkText: { type: 'string', description: 'Tekst linku (opcjonalny)' }, }, required: ['slug'], }, }, { name: 'fluentcrm_validate_smart_link_data', description: 'Waliduje dane Smart Link przed utworzeniem', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Nazwa Smart Link' }, slug: { type: 'string', description: 'Slug' }, target_url: { type: 'string', description: 'Docelowy URL' }, apply_tags: { type: 'array', items: { type: 'number' } }, apply_lists: { type: 'array', items: { type: 'number' } }, remove_tags: { type: 'array', items: { type: 'number' } }, remove_lists: { type: 'array', items: { type: 'number' } }, auto_login: { type: 'boolean' }, }, required: ['title', 'target_url'], }, }, // ===== RAPORTY ===== { name: 'fluentcrm_dashboard_stats', description: 'Pobiera statystyki dashboarda', inputSchema: { type: 'object', properties: {}, }, }, { name: 'fluentcrm_custom_fields', description: 'Pobiera pola niestandardowe', inputSchema: { type: 'object', properties: {}, }, }, ], }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'fluentcrm_list_contacts': return { content: [{ type: 'text', text: JSON.stringify(await client.listContacts(args || {}), null, 2) }] }; case 'fluentcrm_get_contact': return { content: [{ type: 'text', text: JSON.stringify(await client.getContact((args as any)?.subscriberId), null, 2) }] }; case 'fluentcrm_find_contact_by_email': return { content: [{ type: 'text', text: JSON.stringify(await client.findContactByEmail((args as any)?.email), null, 2) }] }; case 'fluentcrm_create_contact': return { content: [{ type: 'text', text: JSON.stringify(await client.createContact(args as any), null, 2) }] }; case 'fluentcrm_update_contact': return { content: [{ type: 'text', text: JSON.stringify(await client.updateContact((args as any)?.subscriberId, args as any), null, 2) }] }; case 'fluentcrm_delete_contact': return { content: [{ type: 'text', text: JSON.stringify(await client.deleteContact((args as any)?.subscriberId), null, 2) }] }; case 'fluentcrm_list_tags': return { content: [{ type: 'text', text: JSON.stringify(await client.listTags(args || {}), null, 2) }] }; case 'fluentcrm_create_tag': return { content: [{ type: 'text', text: JSON.stringify(await client.createTag(args as any), null, 2) }] }; case 'fluentcrm_delete_tag': return { content: [{ type: 'text', text: JSON.stringify(await client.deleteTag((args as any)?.tagId), null, 2) }] }; case 'fluentcrm_attach_tag_to_contact': return { content: [{ type: 'text', text: JSON.stringify(await client.attachTagToContact((args as any)?.subscriberId, (args as any)?.tagIds), null, 2) }] }; case 'fluentcrm_detach_tag_from_contact': return { content: [{ type: 'text', text: JSON.stringify(await client.detachTagFromContact((args as any)?.subscriberId, (args as any)?.tagIds), null, 2) }] }; case 'fluentcrm_list_lists': return { content: [{ type: 'text', text: JSON.stringify(await client.listLists(), null, 2) }] }; case 'fluentcrm_create_list': return { content: [{ type: 'text', text: JSON.stringify(await client.createList(args as any), null, 2) }] }; case 'fluentcrm_delete_list': return { content: [{ type: 'text', text: JSON.stringify(await client.deleteList((args as any)?.listId), null, 2) }] }; case 'fluentcrm_attach_contact_to_list': return { content: [{ type: 'text', text: JSON.stringify(await client.attachContactToList((args as any)?.subscriberId, (args as any)?.listIds), null, 2) }] }; case 'fluentcrm_detach_contact_from_list': return { content: [{ type: 'text', text: JSON.stringify(await client.detachContactFromList((args as any)?.subscriberId, (args as any)?.listIds), null, 2) }] }; case 'fluentcrm_list_campaigns': return { content: [{ type: 'text', text: JSON.stringify(await client.listCampaigns(args || {}), null, 2) }] }; case 'fluentcrm_create_campaign': return { content: [{ type: 'text', text: JSON.stringify(await client.createCampaign(args as any), null, 2) }] }; case 'fluentcrm_pause_campaign': return { content: [{ type: 'text', text: JSON.stringify(await client.pauseCampaign((args as any)?.campaignId), null, 2) }] }; case 'fluentcrm_resume_campaign': return { content: [{ type: 'text', text: JSON.stringify(await client.resumeCampaign((args as any)?.campaignId), null, 2) }] }; case 'fluentcrm_delete_campaign': return { content: [{ type: 'text', text: JSON.stringify(await client.deleteCampaign((args as any)?.campaignId), null, 2) }] }; case 'fluentcrm_list_email_templates': return { content: [{ type: 'text', text: JSON.stringify(await client.listEmailTemplates(), null, 2) }] }; case 'fluentcrm_create_email_template': return { content: [{ type: 'text', text: JSON.stringify(await client.createEmailTemplate(args as any), null, 2) }] }; case 'fluentcrm_list_automations': return { content: [{ type: 'text', text: JSON.stringify(await client.listAutomations(args || {}), null, 2) }] }; case 'fluentcrm_create_automation': return { content: [{ type: 'text', text: JSON.stringify(await client.createAutomation(args as any), null, 2) }] }; case 'fluentcrm_list_webhooks': return { content: [{ type: 'text', text: JSON.stringify(await client.listWebhooks(), null, 2) }] }; case 'fluentcrm_create_webhook': return { content: [{ type: 'text', text: JSON.stringify(await client.createWebhook(args as any), null, 2) }] }; case 'fluentcrm_dashboard_stats': return { content: [{ type: 'text', text: JSON.stringify(await client.getDashboardStats(), null, 2) }] }; case 'fluentcrm_custom_fields': return { content: [{ type: 'text', text: JSON.stringify(await client.listCustomFields(), null, 2) }] }; // ===== SMART LINKS ===== case 'fluentcrm_list_smart_links': return { content: [{ type: 'text', text: JSON.stringify(await client.listSmartLinks(args || {}), null, 2) }] }; case 'fluentcrm_get_smart_link': return { content: [{ type: 'text', text: JSON.stringify(await client.getSmartLink((args as any)?.smartLinkId), null, 2) }] }; case 'fluentcrm_create_smart_link': return { content: [{ type: 'text', text: JSON.stringify(await client.createSmartLink(args as any), null, 2) }] }; case 'fluentcrm_update_smart_link': return { content: [{ type: 'text', text: JSON.stringify(await client.updateSmartLink((args as any)?.smartLinkId, args as any), null, 2) }] }; case 'fluentcrm_delete_smart_link': return { content: [{ type: 'text', text: JSON.stringify(await client.deleteSmartLink((args as any)?.smartLinkId), null, 2) }] }; case 'fluentcrm_generate_smart_link_shortcode': return { content: [{ type: 'text', text: JSON.stringify({ shortcode: client.generateSmartLinkShortcode((args as any)?.slug, (args as any)?.linkText) }, null, 2) }] }; case 'fluentcrm_validate_smart_link_data': return { content: [{ type: 'text', text: JSON.stringify(client.validateSmartLinkData(args as any), null, 2) }] }; default: throw new Error(`Unknown tool: ${name}`); } } catch (error: any) { return { content: [{ type: 'text', text: `❌ Error: ${error.message}` }], isError: true, }; } }); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error('🚀 FluentCRM MCP Server running on stdio'); console.error(`📡 API URL: ${FLUENTCRM_API_URL}`); console.error(`👤 Username: ${FLUENTCRM_API_USERNAME}`); } main().catch((error) => { console.error('❌ Server error:', error); process.exit(1); });

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/netflyapp/fluentcrm-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server