206 lines
5.8 KiB
TypeScript
206 lines
5.8 KiB
TypeScript
import { type McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
import { z } from "zod/v4";
|
|
import {
|
|
clearConfig,
|
|
EwsClient,
|
|
hasPassword,
|
|
loadConfig,
|
|
saveConfig,
|
|
setPassword,
|
|
} from "../client/ews_client.ts";
|
|
import { type LoginConfig } from "../utils/config.ts";
|
|
|
|
export function registerAuthTools(server: McpServer): void {
|
|
server.registerTool(
|
|
"login",
|
|
{
|
|
description: "Authenticate to Exchange EWS using NTLM credentials."
|
|
+ " On first use all fields except domain are required."
|
|
+ " On subsequent logins only password is needed — previously saved"
|
|
+ " serverUrl, email, username, and domain are reused"
|
|
+ " automatically from config.",
|
|
inputSchema: z.object({
|
|
serverUrl: z.string().optional().describe("EWS server URL"),
|
|
email: z.string().optional().describe("Email address"),
|
|
username: z.string().optional().describe("NTLM username"),
|
|
password: z.string().describe("NTLM password"),
|
|
domain: z.string().optional().describe("NTLM domain"),
|
|
}),
|
|
outputSchema: z.object({
|
|
success: z.boolean(),
|
|
message: z.string().optional(),
|
|
error: z.string().optional(),
|
|
}),
|
|
},
|
|
async ({ serverUrl, email, username, password, domain }) => {
|
|
try {
|
|
let savedConfig: Partial<LoginConfig> = {};
|
|
try {
|
|
savedConfig = loadConfig();
|
|
} catch {
|
|
// no saved config — all fields must be provided
|
|
}
|
|
|
|
const config: LoginConfig = {
|
|
serverUrl: (serverUrl ?? savedConfig.serverUrl)!,
|
|
email: (email ?? savedConfig.email)!,
|
|
username: (username ?? savedConfig.username)!,
|
|
domain: domain ?? savedConfig.domain ?? "corp",
|
|
};
|
|
|
|
if (!config.serverUrl || !config.email || !config.username) {
|
|
return {
|
|
content: [{
|
|
type: "text" as const,
|
|
text: JSON.stringify({
|
|
success: false,
|
|
error: "Missing required fields."
|
|
+ " Provide serverUrl, email, and username, or"
|
|
+ " login once with all fields first.",
|
|
}),
|
|
}],
|
|
};
|
|
}
|
|
|
|
config.serverUrl = config.serverUrl.replace(/\/+$/, "");
|
|
|
|
const client = new EwsClient(config, password);
|
|
const result = await client.verifyConnection();
|
|
|
|
if (!result.ok) {
|
|
return {
|
|
content: [{
|
|
type: "text" as const,
|
|
text: JSON.stringify({
|
|
success: false,
|
|
error: result.error ?? "Connection verification failed",
|
|
}),
|
|
}],
|
|
};
|
|
}
|
|
|
|
saveConfig(config);
|
|
|
|
return {
|
|
content: [{
|
|
type: "text" as const,
|
|
text: JSON.stringify({
|
|
success: true,
|
|
message: `Logged in as ${email} to ${serverUrl}`,
|
|
}),
|
|
}],
|
|
};
|
|
} catch (error: any) {
|
|
return {
|
|
content: [{
|
|
type: "text" as const,
|
|
text: JSON.stringify({
|
|
success: false,
|
|
error: error.message ?? String(error),
|
|
}),
|
|
}],
|
|
};
|
|
}
|
|
},
|
|
);
|
|
|
|
server.registerTool(
|
|
"check_session",
|
|
{
|
|
description:
|
|
"Check whether the current EWS session is authenticated. No parameters.",
|
|
inputSchema: z.object({}),
|
|
outputSchema: z.object({
|
|
authenticated: z.boolean(),
|
|
email: z.string().optional(),
|
|
serverUrl: z.string().optional(),
|
|
error: z.string().optional(),
|
|
}),
|
|
},
|
|
async () => {
|
|
try {
|
|
if (!hasPassword()) {
|
|
return {
|
|
content: [{
|
|
type: "text" as const,
|
|
text: JSON.stringify({
|
|
authenticated: false,
|
|
error: "Not logged in. Password not found in memory.",
|
|
}),
|
|
}],
|
|
structuredContent: {
|
|
authenticated: false,
|
|
error: "Not logged in. Password not found in memory.",
|
|
},
|
|
};
|
|
}
|
|
const config = loadConfig();
|
|
const client = new EwsClient(config);
|
|
const result = await client.verifyConnection();
|
|
|
|
return {
|
|
content: [{
|
|
type: "text" as const,
|
|
text: JSON.stringify({
|
|
authenticated: result.ok,
|
|
email: config.email,
|
|
serverUrl: config.serverUrl,
|
|
error: result.error,
|
|
}),
|
|
}],
|
|
structuredContent: {
|
|
authenticated: result.ok,
|
|
email: config.email,
|
|
serverUrl: config.serverUrl,
|
|
error: result.error,
|
|
},
|
|
};
|
|
} catch (error: any) {
|
|
return {
|
|
content: [{
|
|
type: "text" as const,
|
|
text: JSON.stringify({
|
|
authenticated: false,
|
|
error: error.message ?? String(error),
|
|
}),
|
|
}],
|
|
structuredContent: {
|
|
authenticated: false,
|
|
error: error.message ?? String(error),
|
|
},
|
|
};
|
|
}
|
|
},
|
|
);
|
|
|
|
server.registerTool(
|
|
"logout",
|
|
{
|
|
description:
|
|
"Clear stored credentials (serverUrl, email, username, domain, password)."
|
|
+ " No parameters.",
|
|
inputSchema: z.object({}),
|
|
outputSchema: z.object({
|
|
success: z.boolean(),
|
|
message: z.string().optional(),
|
|
}),
|
|
},
|
|
async () => {
|
|
clearConfig();
|
|
return {
|
|
content: [{
|
|
type: "text" as const,
|
|
text: JSON.stringify({
|
|
success: true,
|
|
message: "Logged out. Credentials cleared.",
|
|
}),
|
|
}],
|
|
structuredContent: {
|
|
success: true,
|
|
message: "Logged out. Credentials cleared.",
|
|
},
|
|
};
|
|
},
|
|
);
|
|
}
|