diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..b261f16
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,8 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..e51fdcc
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+/.pnp.* binary linguist-generated
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c46c3e7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+.cache
+.data
+.DS_Store
+.env
+.git
+.idea
+.pnp.*
+.target
+.tmp
+.vscode
+.yarn
+node_modules
\ No newline at end of file
diff --git a/dprint.json b/dprint.json
new file mode 100644
index 0000000..b9bf997
--- /dev/null
+++ b/dprint.json
@@ -0,0 +1,18 @@
+{
+ "lineWidth": 80,
+ "indentWidth": 2,
+ "typescript": {
+ "module.sortImportDeclarations": "caseInsensitive",
+ "module.sortExportDeclarations": "caseInsensitive"
+ },
+ "excludes": [
+ "**/.git",
+ "**/.target"
+ ],
+ "plugins": [
+ "https://plugins.dprint.dev/typescript-0.91.1.wasm",
+ "https://plugins.dprint.dev/json-0.17.4.wasm",
+ "https://plugins.dprint.dev/markdown-0.15.3.wasm",
+ "https://plugins.dprint.dev/dockerfile-0.3.0.wasm"
+ ]
+}
diff --git a/main.ts b/main.ts
new file mode 100644
index 0000000..e4704d1
--- /dev/null
+++ b/main.ts
@@ -0,0 +1,148 @@
+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";
+
+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 `
+
+
+${body}
+
+`;
+}
+
+async function ewsSoap(options: { body: string; soapAction: string }): Promise {
+ 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,
+ });
+
+ if (typeof res.body !== "string") {
+ throw new Error(`NTLM request failed, status=${res.statusCode}`);
+ }
+ return PARSER.parse(res.body);
+}
+
+async function getFolder() {
+ const soap = buildSoapEnvelope(`\
+
+
+ IdOnly
+
+
+
+
+
+
+
+
+
+ `);
+
+ 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(`\
+
+
+ AllProperties
+
+
+
+
+
+
+
+
+
+
+ `);
+
+ 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();
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..2bf61c1
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,238 @@
+{
+ "name": "exchange-mcp",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "exchange-mcp",
+ "version": "0.0.0",
+ "dependencies": {
+ "fast-xml-parser": "^5.2.0",
+ "httpntlm": "^1.8.13"
+ },
+ "devDependencies": {
+ "@types/node": "^22.0.0",
+ "typescript": "^5.9.3"
+ }
+ },
+ "node_modules/@nodable/entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodable"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "22.20.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz",
+ "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/anynum": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz",
+ "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/des.js": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz",
+ "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "node_modules/fast-xml-builder": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.1.tgz",
+ "integrity": "sha512-tPb5TTWfgfVx5BNSi2xV0eLr89POeXXn0dXIsCJ9m1narrWxeIyx6je9d7Rce/3NyXLbvuQmLkxq+RuxMWejvw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "path-expression-matcher": "^1.5.0",
+ "xml-naming": "^0.1.0"
+ }
+ },
+ "node_modules/fast-xml-parser": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.9.3.tgz",
+ "integrity": "sha512-brCNCeScma/kqa54J4PIDriSSSLssRkuYaUCpvHJulGc3HGI/xxKUCTDcYkAdqJsyb//ydpbxecjC3hB9+tb/g==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@nodable/entities": "^2.2.0",
+ "fast-xml-builder": "^1.2.0",
+ "is-unsafe": "^1.0.1",
+ "path-expression-matcher": "^1.5.0",
+ "strnum": "^2.4.1",
+ "xml-naming": "^0.1.0"
+ },
+ "bin": {
+ "fxparser": "src/cli/cli.js"
+ }
+ },
+ "node_modules/httpntlm": {
+ "version": "1.8.13",
+ "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.8.13.tgz",
+ "integrity": "sha512-2F2FDPiWT4rewPzNMg3uPhNkP3NExENlUGADRUDPQvuftuUTGW98nLZtGemCIW3G40VhWZYgkIDcQFAwZ3mf2Q==",
+ "funding": [
+ {
+ "type": "paypal",
+ "url": "https://www.paypal.com/donate/?hosted_button_id=2CKNJLZJBW8ZC"
+ },
+ {
+ "type": "buymeacoffee",
+ "url": "https://www.buymeacoffee.com/samdecrock"
+ }
+ ],
+ "dependencies": {
+ "des.js": "^1.0.1",
+ "httpreq": ">=0.4.22",
+ "js-md4": "^0.3.2",
+ "underscore": "~1.12.1"
+ },
+ "engines": {
+ "node": ">=10.4.0"
+ }
+ },
+ "node_modules/httpreq": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-1.1.1.tgz",
+ "integrity": "sha512-uhSZLPPD2VXXOSN8Cni3kIsoFHaU2pT/nySEU/fHr/ePbqHYr0jeiQRmUKLEirC09SFPsdMoA7LU7UXMd/w0Kw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.15.1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/is-unsafe": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-1.0.1.tgz",
+ "integrity": "sha512-CLK2+VdgERgD96EYm5lUQssZYlRg2tkZnbsxZoacmSiRxiFJ4Nk4SzjCl+Ur+v3kXIY9dTIdb3IH22y1mZ56LA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/js-md4": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz",
+ "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==",
+ "license": "MIT"
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "license": "ISC"
+ },
+ "node_modules/path-expression-matcher": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz",
+ "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/strnum": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz",
+ "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "anynum": "^1.0.1"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/underscore": {
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz",
+ "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==",
+ "license": "MIT"
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/xml-naming": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz",
+ "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..1f68d12
--- /dev/null
+++ b/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "exchange-mcp",
+ "version": "0.0.0",
+ "type": "module",
+ "dependencies": {
+ "fast-xml-parser": "^5.2.0",
+ "httpntlm": "^1.8.13"
+ },
+ "devDependencies": {
+ "@types/node": "^22.0.0",
+ "typescript": "^5.9.3"
+ }
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..424bf37
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,42 @@
+{
+ "compilerOptions": {
+ "allowImportingTsExtensions": true,
+ "allowUnreachableCode": false,
+ "allowUnusedLabels": false,
+ "checkJs": false,
+ "erasableSyntaxOnly": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "keyofStringsOnly": false,
+ "lib": [
+ "esnext"
+ ],
+ "module": "nodenext",
+ "moduleResolution": "nodenext",
+ "noEmit": true,
+ "noErrorTruncation": false,
+ "noFallthroughCasesInSwitch": false,
+ "noImplicitAny": true,
+ "noImplicitOverride": true,
+ "noImplicitReturns": false,
+ "noImplicitThis": true,
+ "noStrictGenericChecks": false,
+ "noUncheckedIndexedAccess": false,
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "skipLibCheck": true,
+ "strict": true,
+ "strictBindCallApply": true,
+ "strictFunctionTypes": true,
+ "strictNullChecks": true,
+ "strictPropertyInitialization": true,
+ "suppressExcessPropertyErrors": false,
+ "suppressImplicitAnyIndexErrors": false,
+ "target": "esnext",
+ "types": [
+ "node"
+ ],
+ "useUnknownInCatchVariables": true,
+ "verbatimModuleSyntax": true
+ }
+}
\ No newline at end of file