MCP Language Server
by isaacphi
#!/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 Microsoft Defender Policy IDs and Requirements
const DEFENDER_POLICIES = {
PRESET_SECURITY: {
id: 'MS.DEFENDER.1.1v1',
title: 'Standard and Strict Preset Security Policies',
requirement: 'The standard and strict preset security policies SHALL be enabled.',
dateAdded: '2024-12-17',
dueDate: '2025-06-20'
},
EOP_STANDARD: {
id: 'MS.DEFENDER.1.2v1',
title: 'Exchange Online Protection Standard',
requirement: 'All users SHALL be added to Exchange Online Protection in either the standard or strict preset security policy.',
dateAdded: '2024-12-17',
dueDate: '2025-06-20'
},
DEFENDER_O365: {
id: 'MS.DEFENDER.1.3v1',
title: 'Defender for Office 365 Protection',
requirement: 'All users SHALL be added to Defender for Office 365 Protection in either the standard or strict preset security policy.',
dateAdded: '2024-12-17',
dueDate: '2025-06-20'
},
EOP_STRICT: {
id: 'MS.DEFENDER.1.4v1',
title: 'Exchange Online Protection Strict',
requirement: 'Sensitive accounts SHALL be added to Exchange Online Protection in the strict preset security policy.',
dateAdded: '2024-12-17',
dueDate: '2025-06-20'
},
DEFENDER_O365_STRICT: {
id: 'MS.DEFENDER.1.5v1',
title: 'Defender for Office 365 Strict Protection',
requirement: 'Sensitive accounts SHALL be added to Defender for Office 365 Protection in the strict preset security policy.',
dateAdded: '2024-12-17',
dueDate: '2025-06-20'
},
PII_PROTECTION: {
id: 'MS.DEFENDER.4.1v1',
title: 'PII and Sensitive Information Protection',
requirement: 'A custom policy SHALL be configured to protect PII and sensitive information, blocking credit card numbers, TINs, and SSNs.',
dateAdded: '2024-12-17',
dueDate: '2025-06-20'
},
ALERTS: {
id: 'MS.DEFENDER.5.1v1',
title: 'Required Security Alerts',
requirement: 'At a minimum, the alerts required by the CISA M365 Security Configuration Baseline for Exchange Online SHALL be enabled.',
dateAdded: '2024-12-17',
dueDate: '2025-06-20'
},
AUDIT_STANDARD: {
id: 'MS.DEFENDER.6.1v1',
title: 'Microsoft Purview Audit Standard',
requirement: 'Microsoft Purview Audit (Standard) logging SHALL be enabled.',
dateAdded: '2024-12-17',
dueDate: '2025-06-20'
},
AUDIT_PREMIUM: {
id: 'MS.DEFENDER.6.2v1',
title: 'Microsoft Purview Audit Premium',
requirement: 'Microsoft Purview Audit (Premium) logging SHALL be enabled for ALL users.',
dateAdded: '2024-12-17',
dueDate: '2025-06-20'
}
};
interface SecurityPolicyArgs {
standardPolicy: boolean;
strictPolicy: boolean;
sensitiveAccounts?: string[];
}
interface PiiProtectionArgs {
blockCreditCards: boolean;
blockTIN: boolean;
blockSSN: boolean;
customPatterns?: string[];
}
interface AuditConfigArgs {
enableStandard: boolean;
enablePremium: boolean;
userScope?: 'all' | 'selected';
selectedUsers?: string[];
}
function validateSecurityPolicyArgs(args: unknown): SecurityPolicyArgs {
if (typeof args !== 'object' || args === null) {
throw new McpError(ErrorCode.InvalidParams, 'Invalid security policy arguments');
}
const typedArgs = args as Record<string, unknown>;
if (typeof typedArgs.standardPolicy !== 'boolean' || typeof typedArgs.strictPolicy !== 'boolean') {
throw new McpError(ErrorCode.InvalidParams, 'Missing required boolean parameters');
}
if (typedArgs.sensitiveAccounts !== undefined && !Array.isArray(typedArgs.sensitiveAccounts)) {
throw new McpError(ErrorCode.InvalidParams, 'sensitiveAccounts must be an array of strings');
}
return args as SecurityPolicyArgs;
}
function validatePiiProtectionArgs(args: unknown): PiiProtectionArgs {
if (typeof args !== 'object' || args === null) {
throw new McpError(ErrorCode.InvalidParams, 'Invalid PII protection arguments');
}
const typedArgs = args as Record<string, unknown>;
if (typeof typedArgs.blockCreditCards !== 'boolean' ||
typeof typedArgs.blockTIN !== 'boolean' ||
typeof typedArgs.blockSSN !== 'boolean') {
throw new McpError(ErrorCode.InvalidParams, 'Missing required boolean parameters');
}
if (typedArgs.customPatterns !== undefined && !Array.isArray(typedArgs.customPatterns)) {
throw new McpError(ErrorCode.InvalidParams, 'customPatterns must be an array of strings');
}
return args as PiiProtectionArgs;
}
function validateAuditConfigArgs(args: unknown): AuditConfigArgs {
if (typeof args !== 'object' || args === null) {
throw new McpError(ErrorCode.InvalidParams, 'Invalid audit config arguments');
}
const typedArgs = args as Record<string, unknown>;
if (typeof typedArgs.enableStandard !== 'boolean' ||
typeof typedArgs.enablePremium !== 'boolean') {
throw new McpError(ErrorCode.InvalidParams, 'Missing required boolean parameters');
}
if (typedArgs.userScope !== undefined &&
typedArgs.userScope !== 'all' &&
typedArgs.userScope !== 'selected') {
throw new McpError(ErrorCode.InvalidParams, 'userScope must be "all" or "selected"');
}
if (typedArgs.selectedUsers !== undefined && !Array.isArray(typedArgs.selectedUsers)) {
throw new McpError(ErrorCode.InvalidParams, 'selectedUsers must be an array of strings');
}
return args as AuditConfigArgs;
}
class CisaDefenderServer {
private server: Server = new Server(
{
name: 'cisa-defender',
version: '0.1.0',
},
{
capabilities: {
tools: {},
},
}
);
private graphClient: Client | null = null;
constructor() {
const tenantId = process.env.MS_TENANT_ID;
const clientId = process.env.MS_CLIENT_ID;
const clientSecret = process.env.MS_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 Defender policies',
inputSchema: {
type: 'object',
properties: {}
}
},
{
name: 'configure_security_policies',
description: 'Configure standard and strict security policies',
inputSchema: {
type: 'object',
properties: {
standardPolicy: {
type: 'boolean',
description: 'Enable standard preset security policy'
},
strictPolicy: {
type: 'boolean',
description: 'Enable strict preset security policy'
},
sensitiveAccounts: {
type: 'array',
items: {
type: 'string'
},
description: 'List of sensitive account UPNs'
}
},
required: ['standardPolicy', 'strictPolicy']
}
},
{
name: 'configure_pii_protection',
description: 'Configure PII and sensitive information protection',
inputSchema: {
type: 'object',
properties: {
blockCreditCards: {
type: 'boolean',
description: 'Block credit card numbers'
},
blockTIN: {
type: 'boolean',
description: 'Block Taxpayer Identification Numbers'
},
blockSSN: {
type: 'boolean',
description: 'Block Social Security Numbers'
},
customPatterns: {
type: 'array',
items: {
type: 'string'
},
description: 'Additional patterns to block'
}
},
required: ['blockCreditCards', 'blockTIN', 'blockSSN']
}
},
{
name: 'configure_audit_logging',
description: 'Configure Microsoft Purview Audit settings',
inputSchema: {
type: 'object',
properties: {
enableStandard: {
type: 'boolean',
description: 'Enable standard audit logging'
},
enablePremium: {
type: 'boolean',
description: 'Enable premium audit logging'
},
userScope: {
type: 'string',
enum: ['all', 'selected'],
description: 'Scope of premium audit logging'
},
selectedUsers: {
type: 'array',
items: {
type: 'string'
},
description: 'List of users for premium audit if not all'
}
},
required: ['enableStandard', 'enablePremium']
}
}
]
}));
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 'configure_security_policies':
return await this.configureSecurityPolicies(validateSecurityPolicyArgs(request.params.arguments));
case 'configure_pii_protection':
return await this.configurePiiProtection(validatePiiProtectionArgs(request.params.arguments));
case 'configure_audit_logging':
return await this.configureAuditLogging(validateAuditConfigArgs(request.params.arguments));
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: DEFENDER_POLICIES,
currentStatus: {
securityPolicies: await this.graphClient.api('/security/securityPresetPolicies').get(),
piiProtection: await this.graphClient.api('/security/sensitiveTypes').get(),
auditConfig: await this.graphClient.api('/security/auditLogs/config').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 configureSecurityPolicies(args: SecurityPolicyArgs) {
try {
if (!this.graphClient) {
throw new Error('Graph client not initialized');
}
const results = {
standardPolicy: null as any,
strictPolicy: null as any,
sensitiveAccounts: null as any,
appliedPolicies: [
DEFENDER_POLICIES.PRESET_SECURITY,
DEFENDER_POLICIES.EOP_STANDARD,
DEFENDER_POLICIES.DEFENDER_O365
]
};
if (args.standardPolicy) {
results.standardPolicy = await this.graphClient.api('/security/securityPresetPolicies/standard')
.patch({
isEnabled: true
});
}
if (args.strictPolicy) {
results.strictPolicy = await this.graphClient.api('/security/securityPresetPolicies/strict')
.patch({
isEnabled: true
});
if (args.sensitiveAccounts?.length) {
results.sensitiveAccounts = await this.graphClient.api('/security/sensitiveAccounts')
.post({
accounts: args.sensitiveAccounts
});
results.appliedPolicies.push(
DEFENDER_POLICIES.EOP_STRICT,
DEFENDER_POLICIES.DEFENDER_O365_STRICT
);
}
}
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 security policies: ${errorMessage}`
);
}
}
private async configurePiiProtection(args: PiiProtectionArgs) {
try {
if (!this.graphClient) {
throw new Error('Graph client not initialized');
}
const policy = {
displayName: 'CISA Required - PII Protection',
mode: 'enforce',
sensitiveTypes: [
...(args.blockCreditCards ? [{
name: 'Credit Card Numbers',
pattern: '\\b(?:\\d[ -]*?){13,16}\\b'
}] : []),
...(args.blockTIN ? [{
name: 'Taxpayer Identification Numbers',
pattern: '\\b[0-9]{2}-[0-9]{7}\\b'
}] : []),
...(args.blockSSN ? [{
name: 'Social Security Numbers',
pattern: '\\b[0-9]{3}-[0-9]{2}-[0-9]{4}\\b'
}] : []),
...(args.customPatterns?.map(pattern => ({
name: `Custom Pattern - ${pattern}`,
pattern
})) || [])
]
};
const result = await this.graphClient.api('/security/sensitiveTypes')
.post(policy);
return {
content: [
{
type: 'text',
text: JSON.stringify({
result,
appliedPolicy: DEFENDER_POLICIES.PII_PROTECTION
}, null, 2)
}
]
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
throw new McpError(
ErrorCode.InternalError,
`Failed to configure PII protection: ${errorMessage}`
);
}
}
private async configureAuditLogging(args: AuditConfigArgs) {
try {
if (!this.graphClient) {
throw new Error('Graph client not initialized');
}
const results = {
standardAudit: null as any,
premiumAudit: null as any,
appliedPolicies: [] as any[]
};
if (args.enableStandard) {
results.standardAudit = await this.graphClient.api('/security/auditLogs/config')
.patch({
isEnabled: true,
retentionDays: 180
});
results.appliedPolicies.push(DEFENDER_POLICIES.AUDIT_STANDARD);
}
if (args.enablePremium) {
const premiumConfig = {
isEnabled: true,
scope: args.userScope === 'all' ? 'all' : 'selected',
...(args.userScope === 'selected' && {
users: args.selectedUsers
})
};
results.premiumAudit = await this.graphClient.api('/security/auditLogs/premiumConfig')
.patch(premiumConfig);
results.appliedPolicies.push(DEFENDER_POLICIES.AUDIT_PREMIUM);
}
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 audit logging: ${errorMessage}`
);
}
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('CISA Defender MCP server running on stdio');
}
}
const server = new CisaDefenderServer();
server.run().catch(console.error);