MCP Language Server

#!/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 { Client } from '@microsoft/microsoft-graph-client'; import { ClientSecretCredential } from '@azure/identity'; // CISA BOD 25-01 Exchange Online Policy IDs and Requirements const EXO_POLICIES = { EXTERNAL_FORWARDING: { id: 'MS.EXO.1.1v1', title: 'Disable External Forwarding', requirement: 'Automatic forwarding to external domains SHALL be disabled.', dateAdded: '2024-12-17', dueDate: '2025-06-20' }, SPF_POLICY: { id: 'MS.EXO.2.2v2', title: 'SPF Policy', requirement: 'An SPF policy SHALL be published for each domain that fails all non-approved senders.', dateAdded: '2024-12-17', dueDate: '2025-06-20' }, DMARC_POLICY: { id: 'MS.EXO.4.1v1', title: 'DMARC Policy', requirement: 'A DMARC policy SHALL be published for every second-level domain.', dateAdded: '2024-12-17', dueDate: '2025-06-20' }, DMARC_REJECT: { id: 'MS.EXO.4.2v1', title: 'DMARC Reject', requirement: 'The DMARC message rejection option SHALL be p=reject.', dateAdded: '2024-12-17', dueDate: '2025-06-20' }, DMARC_REPORTS: { id: 'MS.EXO.4.3v1', title: 'DMARC Reports', requirement: 'The DMARC point of contact for aggregate reports SHALL include reports@dmarc.cyber.dhs.gov.', dateAdded: '2024-12-17', dueDate: '2025-06-20' }, SMTP_AUTH: { id: 'MS.EXO.5.1v1', title: 'SMTP AUTH', requirement: 'SMTP AUTH SHALL be disabled.', dateAdded: '2024-12-17', dueDate: '2025-06-20' }, CONTACT_SHARING: { id: 'MS.EXO.6.1v1', title: 'Contact Sharing', requirement: 'Contact folders SHALL NOT be shared with all domains.', dateAdded: '2024-12-17', dueDate: '2025-06-20' }, CALENDAR_SHARING: { id: 'MS.EXO.6.2v1', title: 'Calendar Sharing', requirement: 'Calendar details SHALL NOT be shared with all domains.', dateAdded: '2024-12-17', dueDate: '2025-06-20' }, EXTERNAL_SENDER: { id: 'MS.EXO.7.1v1', title: 'External Sender Warnings', requirement: 'External sender warnings SHALL be implemented.', dateAdded: '2024-12-17', dueDate: '2025-06-20' }, MAILBOX_AUDIT: { id: 'MS.EXO.13.1v1', title: 'Mailbox Auditing', requirement: 'Mailbox auditing SHALL be enabled.', dateAdded: '2024-12-17', dueDate: '2025-06-20' } }; interface DmarcPolicyArgs { domain: string; rejectPolicy: boolean; includeReports: boolean; } interface SharingPolicyArgs { disableContactSharing: boolean; disableCalendarSharing: boolean; } class CisaExchangeServer { private server: Server; private graphClient: Client | null = null; constructor() { this.server = new Server( { name: 'cisa-exchange', version: '0.1.0', }, { capabilities: { tools: {}, }, } ); const tenantId = process.env.TENANT_ID; const clientId = process.env.CLIENT_ID; const clientSecret = process.env.CLIENT_SECRET; if (tenantId && clientId && clientSecret) { const credential = new ClientSecretCredential(tenantId, clientId, clientSecret); this.graphClient = Client.initWithMiddleware({ authProvider: { getAccessToken: async () => { const token = await credential.getToken('https://graph.microsoft.com/.default'); return token.token; } } }); } this.setupToolHandlers(); this.server.onerror = (error) => console.error('[MCP Error]', error); process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } private setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'get_policy_status', description: 'Get current status of all CISA Exchange Online policies', inputSchema: { type: 'object', properties: {} } }, { name: 'disable_external_forwarding', description: 'Disable automatic forwarding to external domains', inputSchema: { type: 'object', properties: {} } }, { name: 'configure_spf_policy', description: 'Configure SPF policy for domains', inputSchema: { type: 'object', properties: { domain: { type: 'string', description: 'Domain to configure SPF for' } }, required: ['domain'] } }, { name: 'configure_dmarc_policy', description: 'Configure DMARC policy settings', inputSchema: { type: 'object', properties: { domain: { type: 'string', description: 'Domain to configure DMARC for' }, rejectPolicy: { type: 'boolean', description: 'Enable p=reject policy' }, includeReports: { type: 'boolean', description: 'Include CISA DMARC reporting address' } }, required: ['domain', 'rejectPolicy', 'includeReports'] } }, { name: 'disable_smtp_auth', description: 'Disable SMTP AUTH', inputSchema: { type: 'object', properties: {} } }, { name: 'configure_sharing_policies', description: 'Configure contact and calendar sharing policies', inputSchema: { type: 'object', properties: { disableContactSharing: { type: 'boolean', description: 'Disable contact folder sharing' }, disableCalendarSharing: { type: 'boolean', description: 'Disable calendar detail sharing' } }, required: ['disableContactSharing', 'disableCalendarSharing'] } }, { name: 'enable_external_sender_warning', description: 'Enable external sender warnings', inputSchema: { type: 'object', properties: {} } }, { name: 'enable_mailbox_audit', description: 'Enable mailbox auditing', inputSchema: { type: 'object', properties: {} } } ] })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { if (!this.graphClient) { throw new McpError( ErrorCode.InvalidRequest, 'Microsoft Graph client not configured. Please provide TENANT_ID, CLIENT_ID, and CLIENT_SECRET.' ); } switch (request.params.name) { case 'get_policy_status': return await this.getPolicyStatus(); case 'disable_external_forwarding': return await this.disableExternalForwarding(); case 'configure_spf_policy': return await this.configureSpfPolicy(request.params.arguments.domain); case 'configure_dmarc_policy': return await this.configureDmarcPolicy(request.params.arguments as DmarcPolicyArgs); case 'disable_smtp_auth': return await this.disableSmtpAuth(); case 'configure_sharing_policies': return await this.configureSharingPolicies(request.params.arguments as SharingPolicyArgs); case 'enable_external_sender_warning': return await this.enableExternalSenderWarning(); case 'enable_mailbox_audit': return await this.enableMailboxAudit(); default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}` ); } }); } private async getPolicyStatus() { try { if (!this.graphClient) { throw new Error('Graph client not initialized'); } const results = { policies: EXO_POLICIES, currentStatus: { externalForwarding: await this.graphClient.api('/admin/exchangeSettings/externalForwarding').get(), spfPolicies: await this.graphClient.api('/admin/domains/spfRecords').get(), dmarcPolicies: await this.graphClient.api('/admin/domains/dmarcRecords').get(), smtpAuth: await this.graphClient.api('/admin/exchangeSettings/smtpAuth').get(), sharingPolicies: await this.graphClient.api('/admin/exchangeSettings/sharingPolicies').get(), externalSenderWarning: await this.graphClient.api('/admin/exchangeSettings/externalSenderWarning').get(), mailboxAudit: await this.graphClient.api('/admin/exchangeSettings/mailboxAudit').get() } }; return { content: [ { type: 'text', text: JSON.stringify(results, null, 2) } ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; throw new McpError( ErrorCode.InternalError, `Failed to get policy status: ${errorMessage}` ); } } private async disableExternalForwarding() { try { if (!this.graphClient) { throw new Error('Graph client not initialized'); } const result = await this.graphClient.api('/admin/exchangeSettings/externalForwarding') .patch({ enabled: false }); return { content: [ { type: 'text', text: JSON.stringify({ result, appliedPolicy: EXO_POLICIES.EXTERNAL_FORWARDING }, null, 2) } ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; throw new McpError( ErrorCode.InternalError, `Failed to disable external forwarding: ${errorMessage}` ); } } private async configureSpfPolicy(domain: string) { try { if (!this.graphClient) { throw new Error('Graph client not initialized'); } const result = await this.graphClient.api(`/admin/domains/${domain}/spfRecord`) .patch({ record: 'v=spf1 include:spf.protection.outlook.com -all' }); return { content: [ { type: 'text', text: JSON.stringify({ result, appliedPolicy: EXO_POLICIES.SPF_POLICY }, null, 2) } ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; throw new McpError( ErrorCode.InternalError, `Failed to configure SPF policy: ${errorMessage}` ); } } private async configureDmarcPolicy(args: DmarcPolicyArgs) { try { if (!this.graphClient) { throw new Error('Graph client not initialized'); } const dmarcRecord = { version: 'DMARC1', policy: args.rejectPolicy ? 'reject' : 'quarantine', rua: args.includeReports ? 'mailto:reports@dmarc.cyber.dhs.gov' : undefined }; const result = await this.graphClient.api(`/admin/domains/${args.domain}/dmarcRecord`) .patch({ record: `v=${dmarcRecord.version}; p=${dmarcRecord.policy}${dmarcRecord.rua ? `; rua=${dmarcRecord.rua}` : ''}` }); return { content: [ { type: 'text', text: JSON.stringify({ result, appliedPolicies: [ EXO_POLICIES.DMARC_POLICY, args.rejectPolicy && EXO_POLICIES.DMARC_REJECT, args.includeReports && EXO_POLICIES.DMARC_REPORTS ].filter(Boolean) }, null, 2) } ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; throw new McpError( ErrorCode.InternalError, `Failed to configure DMARC policy: ${errorMessage}` ); } } private async disableSmtpAuth() { try { if (!this.graphClient) { throw new Error('Graph client not initialized'); } const result = await this.graphClient.api('/admin/exchangeSettings/smtpAuth') .patch({ enabled: false }); return { content: [ { type: 'text', text: JSON.stringify({ result, appliedPolicy: EXO_POLICIES.SMTP_AUTH }, null, 2) } ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; throw new McpError( ErrorCode.InternalError, `Failed to disable SMTP AUTH: ${errorMessage}` ); } } private async configureSharingPolicies(args: SharingPolicyArgs) { try { if (!this.graphClient) { throw new Error('Graph client not initialized'); } const results = { contactSharing: null as any, calendarSharing: null as any, appliedPolicies: [] as any[] }; if (args.disableContactSharing) { results.contactSharing = await this.graphClient.api('/admin/sharingPolicies/contacts') .patch({ allowExternalSharing: false }); results.appliedPolicies.push(EXO_POLICIES.CONTACT_SHARING); } if (args.disableCalendarSharing) { results.calendarSharing = await this.graphClient.api('/admin/sharingPolicies/calendar') .patch({ allowExternalSharing: false }); results.appliedPolicies.push(EXO_POLICIES.CALENDAR_SHARING); } return { content: [ { type: 'text', text: JSON.stringify(results, null, 2) } ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; throw new McpError( ErrorCode.InternalError, `Failed to configure sharing policies: ${errorMessage}` ); } } private async enableExternalSenderWarning() { try { if (!this.graphClient) { throw new Error('Graph client not initialized'); } const result = await this.graphClient.api('/admin/exchangeSettings/externalSenderWarning') .patch({ enabled: true }); return { content: [ { type: 'text', text: JSON.stringify({ result, appliedPolicy: EXO_POLICIES.EXTERNAL_SENDER }, null, 2) } ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; throw new McpError( ErrorCode.InternalError, `Failed to enable external sender warning: ${errorMessage}` ); } } private async enableMailboxAudit() { try { if (!this.graphClient) { throw new Error('Graph client not initialized'); } const result = await this.graphClient.api('/admin/exchangeSettings/mailboxAudit') .patch({ enabled: true, auditAdmin: true, auditDelegate: true, auditOwner: true }); return { content: [ { type: 'text', text: JSON.stringify({ result, appliedPolicy: EXO_POLICIES.MAILBOX_AUDIT }, null, 2) } ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; throw new McpError( ErrorCode.InternalError, `Failed to enable mailbox audit: ${errorMessage}` ); } } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('CISA Exchange Online MCP server running on stdio'); } } const server = new CisaExchangeServer(); server.run().catch(console.error);