Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 147b774e44 | |||
| a79db30ea9 |
+34
-3
@@ -5,7 +5,27 @@ import { createRequire } from "node:module";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { promisify } from "node:util";
|
||||
import type { Email, Folder, LoginConfig } from "./models.ts";
|
||||
import type { Email } from "./types/email.ts";
|
||||
import type { Folder } from "./types/folder.ts";
|
||||
import type { LoginConfig } from "./types/login_config.ts";
|
||||
|
||||
let inMemoryPassword: string | null = null;
|
||||
|
||||
export function setPassword(password: string): void {
|
||||
inMemoryPassword = password;
|
||||
}
|
||||
|
||||
export function getPassword(): string | null {
|
||||
return inMemoryPassword;
|
||||
}
|
||||
|
||||
export function hasPassword(): boolean {
|
||||
return inMemoryPassword !== null;
|
||||
}
|
||||
|
||||
export function clearPassword(): void {
|
||||
inMemoryPassword = null;
|
||||
}
|
||||
|
||||
const ntlm: any = createRequire(import.meta.url)("httpntlm");
|
||||
const postAsync = promisify(ntlm.post.bind(ntlm));
|
||||
@@ -76,27 +96,38 @@ export function clearConfig(): void {
|
||||
if (fs.existsSync(filePath)) {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
clearPassword();
|
||||
}
|
||||
|
||||
export class EwsClient {
|
||||
private config: LoginConfig;
|
||||
private ewsUrl: string;
|
||||
|
||||
constructor(config: LoginConfig) {
|
||||
constructor(config: LoginConfig, password?: string) {
|
||||
this.config = config;
|
||||
this.ewsUrl = `${config.serverUrl.replace(/\/+$/, "")}/EWS/Exchange.asmx`;
|
||||
|
||||
if (password) {
|
||||
setPassword(password);
|
||||
}
|
||||
}
|
||||
|
||||
private get domain(): string {
|
||||
return this.config.domain ?? "corp";
|
||||
}
|
||||
|
||||
private get password(): string {
|
||||
const pw = getPassword();
|
||||
if (!pw) throw new Error("Not logged in. Password not found in memory.");
|
||||
return pw;
|
||||
}
|
||||
|
||||
private async soapRequest(body: string, soapAction: string): Promise<any> {
|
||||
const res = await postAsync({
|
||||
url: this.ewsUrl,
|
||||
username: this.config.username,
|
||||
domain: this.domain,
|
||||
password: this.config.password,
|
||||
password: this.password,
|
||||
agent: AGENT,
|
||||
headers: {
|
||||
"Content-Type": "text/xml; charset=utf-8",
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
export interface LoginConfig {
|
||||
serverUrl: string;
|
||||
email: string;
|
||||
username: string;
|
||||
password: string;
|
||||
domain?: string;
|
||||
}
|
||||
|
||||
export interface Email {
|
||||
subject: string;
|
||||
from: string;
|
||||
fromName: string;
|
||||
date: string;
|
||||
isRead: boolean;
|
||||
hasAttachments: boolean;
|
||||
hasLinks: boolean;
|
||||
itemId: string;
|
||||
size: number;
|
||||
isMeeting: boolean;
|
||||
itemType: string;
|
||||
to: string[];
|
||||
cc: string[];
|
||||
body: string;
|
||||
bodyType: string;
|
||||
attachments: Attachment[];
|
||||
preview: string;
|
||||
location?: string;
|
||||
start?: string;
|
||||
end?: string;
|
||||
requiredAttendees?: string[];
|
||||
optionalAttendees?: string[];
|
||||
}
|
||||
|
||||
export interface Attachment {
|
||||
name: string;
|
||||
size: number;
|
||||
contentType: string;
|
||||
attachmentId: string;
|
||||
isInline: boolean;
|
||||
}
|
||||
|
||||
export interface Folder {
|
||||
name: string;
|
||||
id: string;
|
||||
totalCount: number;
|
||||
unreadCount: number;
|
||||
childFolderCount: number;
|
||||
}
|
||||
|
||||
export interface Person {
|
||||
name: string;
|
||||
email: string;
|
||||
mailboxType: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
jobTitle: string;
|
||||
department: string;
|
||||
company: string;
|
||||
office: string;
|
||||
alias: string;
|
||||
manager: string;
|
||||
managerEmail: string;
|
||||
phones: Record<string, string>;
|
||||
address: string;
|
||||
directReports: Array<{ name: string; email: string }>;
|
||||
}
|
||||
|
||||
export interface CalendarEvent {
|
||||
subject: string;
|
||||
start: string;
|
||||
end: string;
|
||||
location: string;
|
||||
isAllDay: boolean;
|
||||
isCancelled: boolean;
|
||||
isMeeting: boolean;
|
||||
isRecurring: boolean;
|
||||
organizer: string;
|
||||
organizerEmail: string;
|
||||
myResponse: string;
|
||||
itemId: string;
|
||||
body: string;
|
||||
requiredAttendees: string[];
|
||||
optionalAttendees: string[];
|
||||
}
|
||||
|
||||
export interface FreeSlot {
|
||||
date: string;
|
||||
start: string;
|
||||
end: string;
|
||||
durationMinutes: number;
|
||||
}
|
||||
|
||||
export interface MeetingResult {
|
||||
success: boolean;
|
||||
subject: string;
|
||||
date: string;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
location: string;
|
||||
requiredAttendees: string[];
|
||||
optionalAttendees: string[];
|
||||
error: string;
|
||||
}
|
||||
+14
-2
@@ -3,8 +3,10 @@ import { z } from "zod/v4";
|
||||
import {
|
||||
clearConfig,
|
||||
EwsClient,
|
||||
hasPassword,
|
||||
loadConfig,
|
||||
saveConfig,
|
||||
setPassword,
|
||||
} from "../ews_client.ts";
|
||||
|
||||
export function registerAuthTools(server: McpServer): void {
|
||||
@@ -28,11 +30,10 @@ export function registerAuthTools(server: McpServer): void {
|
||||
serverUrl: serverUrl.replace(/\/+$/, ""),
|
||||
email,
|
||||
username,
|
||||
password,
|
||||
domain: domain ?? "corp",
|
||||
};
|
||||
|
||||
const client = new EwsClient(config);
|
||||
const client = new EwsClient(config, password);
|
||||
const result = await client.verifyConnection();
|
||||
|
||||
if (!result.ok) {
|
||||
@@ -80,6 +81,17 @@ export function registerAuthTools(server: McpServer): void {
|
||||
},
|
||||
async () => {
|
||||
try {
|
||||
if (!hasPassword()) {
|
||||
return {
|
||||
content: [{
|
||||
type: "text" as const,
|
||||
text: JSON.stringify({
|
||||
authenticated: false,
|
||||
error: "Not logged in. Password not found in memory.",
|
||||
}),
|
||||
}],
|
||||
};
|
||||
}
|
||||
const config = loadConfig();
|
||||
const client = new EwsClient(config);
|
||||
const result = await client.verifyConnection();
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
export interface CalendarEvent {
|
||||
subject: string;
|
||||
start: string;
|
||||
end: string;
|
||||
location: string;
|
||||
isAllDay: boolean;
|
||||
isCancelled: boolean;
|
||||
isMeeting: boolean;
|
||||
isRecurring: boolean;
|
||||
organizer: string;
|
||||
organizerEmail: string;
|
||||
myResponse: string;
|
||||
itemId: string;
|
||||
body: string;
|
||||
requiredAttendees: string[];
|
||||
optionalAttendees: string[];
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
export interface Email {
|
||||
subject: string;
|
||||
from: string;
|
||||
fromName: string;
|
||||
date: string;
|
||||
isRead: boolean;
|
||||
hasAttachments: boolean;
|
||||
hasLinks: boolean;
|
||||
itemId: string;
|
||||
size: number;
|
||||
isMeeting: boolean;
|
||||
itemType: string;
|
||||
to: string[];
|
||||
cc: string[];
|
||||
body: string;
|
||||
bodyType: string;
|
||||
attachments: Attachment[];
|
||||
preview: string;
|
||||
location?: string;
|
||||
start?: string;
|
||||
end?: string;
|
||||
requiredAttendees?: string[];
|
||||
optionalAttendees?: string[];
|
||||
}
|
||||
|
||||
export interface Attachment {
|
||||
name: string;
|
||||
size: number;
|
||||
contentType: string;
|
||||
attachmentId: string;
|
||||
isInline: boolean;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface Folder {
|
||||
name: string;
|
||||
id: string;
|
||||
totalCount: number;
|
||||
unreadCount: number;
|
||||
childFolderCount: number;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface FreeSlot {
|
||||
date: string;
|
||||
start: string;
|
||||
end: string;
|
||||
durationMinutes: number;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface LoginConfig {
|
||||
serverUrl: string;
|
||||
email: string;
|
||||
username: string;
|
||||
domain?: string;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export interface MeetingResult {
|
||||
success: boolean;
|
||||
subject: string;
|
||||
date: string;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
location: string;
|
||||
requiredAttendees: string[];
|
||||
optionalAttendees: string[];
|
||||
error: string;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
export interface Person {
|
||||
name: string;
|
||||
email: string;
|
||||
mailboxType: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
jobTitle: string;
|
||||
department: string;
|
||||
company: string;
|
||||
office: string;
|
||||
alias: string;
|
||||
manager: string;
|
||||
managerEmail: string;
|
||||
phones: Record<string, string>;
|
||||
address: string;
|
||||
directReports: Array<{ name: string; email: string }>;
|
||||
}
|
||||
Reference in New Issue
Block a user