w
This commit is contained in:
@@ -1,148 +1,20 @@
|
||||
import { XMLParser } from "fast-xml-parser";
|
||||
import { promisify } from "node:util";
|
||||
import { env } from "node:process";
|
||||
import { createRequire } from "node:module";
|
||||
import https from "node:https";
|
||||
import { McpServer } from "@modelcontextprotocol/server";
|
||||
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
||||
import { registerAuthTools } from "./tools/auth.ts";
|
||||
import { registerEmailTools } from "./tools/email.ts";
|
||||
import { registerFolderTools } from "./tools/folders.ts";
|
||||
|
||||
const ntlm: any = createRequire(import.meta.url)("httpntlm");
|
||||
const postAsync = promisify(ntlm.post.bind(ntlm));
|
||||
|
||||
const SSL_OP_LEGACY_SERVER_CONNECT = 0x00000004;
|
||||
|
||||
const AGENT = new https.Agent({
|
||||
keepAlive: true,
|
||||
rejectUnauthorized: false,
|
||||
secureOptions: SSL_OP_LEGACY_SERVER_CONNECT,
|
||||
});
|
||||
|
||||
const EWS_URL = "https://mail.b1.ru/EWS/Exchange.asmx";
|
||||
|
||||
const PARSER = new XMLParser({
|
||||
ignoreAttributes: false,
|
||||
attributeNamePrefix: "@_",
|
||||
removeNSPrefix: true,
|
||||
textNodeName: "#text",
|
||||
});
|
||||
|
||||
function buildSoapEnvelope(body: string): string {
|
||||
return `<?xml version="1.0" encoding="utf-8"?>
|
||||
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
|
||||
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
|
||||
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
|
||||
<s:Body>
|
||||
${body}
|
||||
</s:Body>
|
||||
</s:Envelope>`;
|
||||
}
|
||||
|
||||
async function ewsSoap(options: { body: string; soapAction: string }): Promise<any> {
|
||||
const res = await postAsync({
|
||||
url: EWS_URL,
|
||||
username: env.EWS_USERNAME ?? "polina.litvinova",
|
||||
domain: "corp",
|
||||
password: env.EWS_PASSWORD,
|
||||
agent: AGENT,
|
||||
headers: {
|
||||
"Content-Type": "text/xml; charset=utf-8",
|
||||
SOAPAction: options.soapAction,
|
||||
},
|
||||
body: options.body,
|
||||
function buildServer(): McpServer {
|
||||
const server = new McpServer({
|
||||
name: "exchange-mcp",
|
||||
version: "1.0.0",
|
||||
});
|
||||
|
||||
if (typeof res.body !== "string") {
|
||||
throw new Error(`NTLM request failed, status=${res.statusCode}`);
|
||||
}
|
||||
return PARSER.parse(res.body);
|
||||
registerAuthTools(server);
|
||||
registerEmailTools(server);
|
||||
registerFolderTools(server);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
async function getFolder() {
|
||||
const soap = buildSoapEnvelope(`\
|
||||
<m:GetFolder>
|
||||
<m:FolderShape>
|
||||
<t:BaseShape>IdOnly</t:BaseShape>
|
||||
<t:AdditionalProperties>
|
||||
<t:FieldURI FieldURI="folder:DisplayName"/>
|
||||
<t:FieldURI FieldURI="folder:TotalCount"/>
|
||||
<t:FieldURI FieldURI="folder:UnreadCount"/>
|
||||
</t:AdditionalProperties>
|
||||
</m:FolderShape>
|
||||
<m:FolderIds>
|
||||
<t:DistinguishedFolderId Id="inbox"/>
|
||||
</m:FolderIds>
|
||||
</m:GetFolder>`);
|
||||
|
||||
const data = await ewsSoap({
|
||||
body: soap,
|
||||
soapAction: "http://schemas.microsoft.com/exchange/services/2006/messages/GetFolder",
|
||||
});
|
||||
|
||||
const rm = data.Envelope?.Body?.GetFolderResponse?.ResponseMessages;
|
||||
const msg = rm?.GetFolderResponseMessage;
|
||||
const folder = msg?.Folders?.Folder;
|
||||
|
||||
console.log(`DisplayName: ${folder?.DisplayName}`);
|
||||
console.log(`FolderId: ${folder?.FolderId?.["@_Id"]}`);
|
||||
console.log(`Total items: ${folder?.TotalCount}`);
|
||||
console.log(`Unread: ${folder?.UnreadCount}`);
|
||||
}
|
||||
|
||||
async function getLastMessages() {
|
||||
const soap = buildSoapEnvelope(`\
|
||||
<m:FindItem Traversal="Shallow">
|
||||
<m:ItemShape>
|
||||
<t:BaseShape>AllProperties</t:BaseShape>
|
||||
</m:ItemShape>
|
||||
<m:IndexedPageItemView MaxEntriesReturned="3" Offset="0" BasePoint="Beginning"/>
|
||||
<m:ParentFolderIds>
|
||||
<t:DistinguishedFolderId Id="inbox"/>
|
||||
</m:ParentFolderIds>
|
||||
<m:SortOrder>
|
||||
<t:FieldOrder Order="Descending">
|
||||
<t:FieldURI FieldURI="item:DateTimeReceived"/>
|
||||
</t:FieldOrder>
|
||||
</m:SortOrder>
|
||||
</m:FindItem>`);
|
||||
|
||||
const data = await ewsSoap({
|
||||
body: soap,
|
||||
soapAction: "http://schemas.microsoft.com/exchange/services/2006/messages/FindItem",
|
||||
});
|
||||
|
||||
const rm = data.Envelope?.Body?.FindItemResponse?.ResponseMessages;
|
||||
const msg = rm?.FindItemResponseMessage;
|
||||
const rootFolder = msg?.RootFolder;
|
||||
const items = rootFolder?.Items;
|
||||
const messagesList: any[] = items?.Message ? (Array.isArray(items.Message) ? items.Message : [items.Message]) : [];
|
||||
|
||||
if (!messagesList.length) {
|
||||
console.log("\n--- No messages found ---");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`\n--- ${messagesList.length} message(s) ---`);
|
||||
for (const m of messagesList) {
|
||||
const mbox = m.From?.Mailbox;
|
||||
const author = mbox ? `${mbox.Name ?? "(no name)"} <${mbox.EmailAddress ?? "(no email)"}>` : "(unknown)";
|
||||
console.log(`\nSubject: ${m.Subject ?? "(no subject)"}`);
|
||||
console.log(`From: ${author}`);
|
||||
console.log(`Date: ${m.DateTimeReceived ?? "(unknown)"}`);
|
||||
console.log(`Preview: ${(m.Preview ?? "(no preview)").slice(0, 200)}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (!env.EWS_PASSWORD) {
|
||||
console.error("Set EWS_PASSWORD environment variable");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
await getFolder();
|
||||
await getLastMessages();
|
||||
} catch (err) {
|
||||
console.error("Error:", err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
serveStdio(buildServer);
|
||||
Reference in New Issue
Block a user