Skip to main content
Glama
server.ts7.58 kB
import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, } from '@modelcontextprotocol/sdk/types.js'; import { logger, info, error as logError } from './utils/logger.js'; import { formatErrorResponse, ValidationError } from './utils/errors.js'; import { requireString, optionalString } from './utils/validation.js'; import { requireAuthentication } from './auth/identity.js'; import { copilotRetrieval } from './tools/retrieval.js'; import { copilotSearch } from './tools/search.js'; import { copilotChat } from './tools/chat.js'; /** * Create and configure the MCP server */ export function createServer(): Server { const server = new Server( { name: 'm365-copilot-mcp', version: '0.6.0', }, { capabilities: { tools: {}, }, } ); // Define available tools const tools: Tool[] = [ { name: 'm365copilotretrieval', description: `Retrieves relevant text extracts from user's SharePoint and OneDrive content to answer questions using RAG (Retrieval-Augmented Generation). Returns text snippets with relevance scores - ideal for grounding answers in M365 data. Use this when: - User asks questions that need answers from their M365 content (e.g., "What did the team decide about the project?") - You need text content to support your response with specific information - Grounding AI responses with actual document content is required Example queries: "project deadlines", "budget approval status", "team meeting notes about feature X" DO NOT use for: Finding document links (use m365copilotsearch instead) or interactive conversations (use m365copilotchat instead).`, inputSchema: { type: 'object', properties: { queryString: { type: 'string', description: 'Natural language query to search for relevant content in Microsoft 365', }, }, required: ['queryString'], }, }, { name: 'm365copilotsearch', description: `Searches across SharePoint, OneDrive, and other M365 content to find and locate specific documents. Returns document links with preview text - ideal for document discovery and navigation. Use this when: - User wants to find or locate specific files/documents (e.g., "Find the VPN setup guide") - User needs document links to open or share - Building a list of relevant documents Example queries: "quarterly budget spreadsheet", "network configuration document", "presentation about product launch" DO NOT use for: Extracting text to answer questions (use m365copilotretrieval instead) or asking Copilot questions (use m365copilotchat instead).`, inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Natural language search query to find relevant documents in Microsoft 365', }, }, required: ['query'], }, }, { name: 'm365copilotchat', description: `Enables conversational AI interactions with Microsoft 365 Copilot. Maintains conversation context for multi-turn dialogues - ideal for complex queries, follow-up questions, and time-aware requests. Use this when: - User wants to have a conversation with Copilot (e.g., "Ask Copilot about my schedule") - Questions involve time, calendar, or scheduling (e.g., "What meetings do I have tomorrow?") - Follow-up questions or clarifications are needed - Complex queries that benefit from Copilot's reasoning (e.g., "Summarize team discussions and action items") Example messages: "What's on my calendar tomorrow?", "Summarize recent emails about the project", "Who should I follow up with this week?" Requires timezone parameter (IANA format: "America/New_York", "Europe/London", "Asia/Shanghai"). DO NOT use for: Simple text retrieval (use m365copilotretrieval instead) or finding documents (use m365copilotsearch instead).`, inputSchema: { type: 'object', properties: { message: { type: 'string', description: 'The message or question to send to Copilot', }, conversationId: { type: 'string', description: 'Optional conversation ID to continue an existing conversation. If not provided, a conversation will be created automatically and reused within the session.', }, timeZone: { type: 'string', description: 'User timezone in IANA format (e.g., "America/New_York", "Asia/Shanghai", "Europe/London"). REQUIRED - Must be a valid IANA timezone identifier.', }, }, required: ['message', 'timeZone'], }, }, ]; // Handle list_tools request server.setRequestHandler(ListToolsRequestSchema, async () => { info('Received list_tools request'); return { tools, }; }); // Handle call_tool request server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; info(`Received call_tool request for: ${name}`, { args }); try { switch (name) { case 'm365copilotretrieval': { // Require authentication for this tool await requireAuthentication(); // Validate and extract parameters const queryString = requireString(args?.queryString, 'queryString'); // Call the Copilot Retrieval API const result = await copilotRetrieval(queryString); // Return the raw JSON response return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'm365copilotsearch': { // Require authentication for this tool await requireAuthentication(); // Validate and extract parameters const query = requireString(args?.query, 'query'); // Call the Copilot Search API const result = await copilotSearch(query); // Return the raw JSON response return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'm365copilotchat': { // Require authentication for this tool await requireAuthentication(); // Validate and extract parameters const message = requireString(args?.message, 'message'); const timeZone = requireString(args?.timeZone, 'timeZone'); const conversationId = optionalString(args?.conversationId, 'conversationId'); // Call the Copilot Chat API const result = await copilotChat(message, conversationId, timeZone); // Return the raw JSON response return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } default: throw new ValidationError(`Unknown tool: ${name}`, { toolName: name }); } } catch (error) { logError(`Error handling tool call: ${name}`, error); const errorResponse = formatErrorResponse(error); return { content: [ { type: 'text', text: JSON.stringify(errorResponse, null, 2), }, ], isError: true, }; } }); return server; }

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/chenxizhang/m365copilot-mcp'

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