#!/usr/bin/env node
/**
* Outlook MCP Server - Main entry point
*
* A Model Context Protocol server that provides access to
* Microsoft Outlook through the Microsoft Graph API.
*/
const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
const config = require('./config');
// Import module tools
const { authTools } = require('./auth');
const { calendarTools } = require('./calendar');
const { emailTools } = require('./email');
const { folderTools } = require('./folder');
const { rulesTools } = require('./rules');
// Log startup information
console.error(`STARTING ${config.SERVER_NAME.toUpperCase()} MCP SERVER`);
// Combine all tools
const TOOLS = [
...authTools,
...calendarTools,
...emailTools,
...folderTools,
...rulesTools
// Future modules: contactsTools, etc.
];
// Create server with tools capabilities
const server = new Server(
{ name: config.SERVER_NAME, version: config.SERVER_VERSION },
{
capabilities: {
tools: TOOLS.reduce((acc, tool) => {
acc[tool.name] = {};
return acc;
}, {})
}
}
);
// Handle all requests
server.fallbackRequestHandler = async (request) => {
try {
const { method, params, id } = request;
console.error(`REQUEST: ${method} [${id}]`);
// Initialize handler
if (method === "initialize") {
console.error(`INITIALIZE REQUEST: ID [${id}]`);
return {
protocolVersion: "2024-11-05",
capabilities: {
tools: TOOLS.reduce((acc, tool) => {
acc[tool.name] = {};
return acc;
}, {})
},
serverInfo: { name: config.SERVER_NAME, version: config.SERVER_VERSION },
instructions: "Outlook data in tool responses is provided in TOON (Token-Oriented Object Notation) format, a compact encoding optimized for LLM token efficiency. Parse TOON responses as structured data. When sending data back (e.g. creating emails or events), use standard JSON arguments as defined in each tool's input schema."
};
}
// Tools list handler
if (method === "tools/list") {
console.error(`TOOLS LIST REQUEST: ID [${id}]`);
console.error(`TOOLS COUNT: ${TOOLS.length}`);
console.error(`TOOLS NAMES: ${TOOLS.map(t => t.name).join(', ')}`);
return {
tools: TOOLS.map(tool => ({
name: tool.name,
description: tool.description,
inputSchema: tool.inputSchema
}))
};
}
// Required empty responses for other capabilities
if (method === "resources/list") return { resources: [] };
if (method === "prompts/list") return { prompts: [] };
// Tool call handler
if (method === "tools/call") {
try {
const { name, arguments: args = {} } = params || {};
console.error(`TOOL CALL: ${name}`);
// Find the tool handler
const tool = TOOLS.find(t => t.name === name);
if (tool && tool.handler) {
return await tool.handler(args);
}
// Tool not found
return {
error: {
code: -32601,
message: `Tool not found: ${name}`
}
};
} catch (error) {
console.error(`Error in tools/call:`, error);
return {
error: {
code: -32603,
message: `Error processing tool call: ${error.message}`
}
};
}
}
// For any other method, return method not found
return {
error: {
code: -32601,
message: `Method not found: ${method}`
}
};
} catch (error) {
console.error(`Error in fallbackRequestHandler:`, error);
return {
error: {
code: -32603,
message: `Error processing request: ${error.message}`
}
};
}
};
// Make the script executable
process.on('SIGTERM', () => {
console.error('SIGTERM received but staying alive');
});
// Start the server
const transport = new StdioServerTransport();
server.connect(transport)
.then(() => console.error(`${config.SERVER_NAME} connected and listening`))
.catch(error => {
console.error(`Connection error: ${error.message}`);
process.exit(1);
});