@sam2kb/m365-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/auth.js ADDED
@@ -0,0 +1,288 @@
1
+ /**
2
+ * OAuth 2.0 Device Code Flow + Multi-Account Token Management
3
+ *
4
+ * Uses delegated permissions — the app acts AS YOU, not as the tenant.
5
+ * Tokens stored in ~/.m365-mcp/auth/ by default with 0600 permissions.
6
+ */
7
+ import * as fs from "node:fs";
8
+ import * as path from "node:path";
9
+ import * as os from "node:os";
10
+ export function resolveAuthDir(env = process.env, homeDir = os.homedir()) {
11
+ const configuredDir = env.M365_MCP_AUTH_DIR?.trim();
12
+ return configuredDir ? path.resolve(configuredDir) : path.join(homeDir, ".m365-mcp", "auth");
13
+ }
14
+ const AUTH_DIR = resolveAuthDir();
15
+ const ACCOUNTS_PATH = path.join(AUTH_DIR, "accounts.json");
16
+ const TOKENS_DIR = path.join(AUTH_DIR, "tokens");
17
+ // Scopes for delegated access — reads/writes YOUR data only, not the whole tenant
18
+ const SCOPES = [
19
+ "offline_access",
20
+ "User.Read",
21
+ "Mail.Read",
22
+ "Mail.ReadWrite",
23
+ "Mail.Send",
24
+ "Calendars.Read",
25
+ "Calendars.ReadWrite",
26
+ "Contacts.Read",
27
+ "Contacts.ReadWrite",
28
+ "Files.Read.All",
29
+ "Tasks.ReadWrite",
30
+ "Chat.Read",
31
+ "Chat.ReadWrite",
32
+ ].join(" ");
33
+ // ─── helpers ────────────────────────────────────────────────────────
34
+ export function ensureDirs() {
35
+ fs.mkdirSync(AUTH_DIR, { recursive: true, mode: 0o700 });
36
+ fs.mkdirSync(TOKENS_DIR, { recursive: true, mode: 0o700 });
37
+ }
38
+ export function getTokenPath(accountName) {
39
+ return path.join(TOKENS_DIR, `${accountName}.json`);
40
+ }
41
+ // ─── account store ──────────────────────────────────────────────────
42
+ export function loadAccounts() {
43
+ ensureDirs();
44
+ if (!fs.existsSync(ACCOUNTS_PATH)) {
45
+ return { default: null, accounts: {} };
46
+ }
47
+ try {
48
+ return JSON.parse(fs.readFileSync(ACCOUNTS_PATH, "utf8"));
49
+ }
50
+ catch {
51
+ return { default: null, accounts: {} };
52
+ }
53
+ }
54
+ function saveAccounts(store) {
55
+ ensureDirs();
56
+ fs.writeFileSync(ACCOUNTS_PATH, JSON.stringify(store, null, 2), { mode: 0o600 });
57
+ }
58
+ export function addAccount(name, tenantId, clientId, email, description) {
59
+ const store = loadAccounts();
60
+ store.accounts[name] = {
61
+ tenantId,
62
+ clientId,
63
+ email,
64
+ description,
65
+ addedAt: store.accounts[name]?.addedAt ?? new Date().toISOString(),
66
+ };
67
+ if (!store.default)
68
+ store.default = name;
69
+ saveAccounts(store);
70
+ }
71
+ export function removeAccount(name) {
72
+ const store = loadAccounts();
73
+ delete store.accounts[name];
74
+ const tokenPath = getTokenPath(name);
75
+ if (fs.existsSync(tokenPath))
76
+ fs.unlinkSync(tokenPath);
77
+ if (store.default === name) {
78
+ store.default = Object.keys(store.accounts)[0] ?? null;
79
+ }
80
+ saveAccounts(store);
81
+ }
82
+ export function setDefaultAccount(name) {
83
+ const store = loadAccounts();
84
+ if (!store.accounts[name])
85
+ throw new Error(`Account "${name}" not found`);
86
+ store.default = name;
87
+ saveAccounts(store);
88
+ }
89
+ export function resolveAccount(accountName) {
90
+ const store = loadAccounts();
91
+ const name = accountName ?? store.default;
92
+ if (!name)
93
+ throw new Error("No account configured. Run: m365-mcp-auth add <name> <tenantId> <clientId>");
94
+ const acc = store.accounts[name];
95
+ if (!acc)
96
+ throw new Error(`Account "${name}" not found`);
97
+ return { name, ...acc, tokenPath: getTokenPath(name) };
98
+ }
99
+ export function listAccounts() {
100
+ const store = loadAccounts();
101
+ return {
102
+ default: store.default,
103
+ accounts: Object.entries(store.accounts).map(([name, acc]) => ({
104
+ ...acc,
105
+ name,
106
+ isDefault: name === store.default,
107
+ tokenPath: getTokenPath(name),
108
+ })),
109
+ };
110
+ }
111
+ // ─── token store ────────────────────────────────────────────────────
112
+ export function loadTokens(config) {
113
+ if (!fs.existsSync(config.tokenPath))
114
+ return null;
115
+ try {
116
+ const data = JSON.parse(fs.readFileSync(config.tokenPath, "utf8"));
117
+ if (!data.access_token || !data.refresh_token)
118
+ return null;
119
+ return data;
120
+ }
121
+ catch {
122
+ return null;
123
+ }
124
+ }
125
+ function saveTokens(config, tokens) {
126
+ ensureDirs();
127
+ // Preemptively rotate the refresh token — store, don't re-use the old one
128
+ const data = {
129
+ access_token: tokens.access_token,
130
+ refresh_token: tokens.refresh_token,
131
+ expires_at: tokens.expires_at,
132
+ scope: tokens.scope ?? SCOPES,
133
+ };
134
+ fs.writeFileSync(config.tokenPath, JSON.stringify(data, null, 2), { mode: 0o600 });
135
+ return data;
136
+ }
137
+ // ─── HTTPS helper ───────────────────────────────────────────────────
138
+ async function httpsPost(url, body) {
139
+ const resp = await fetch(url, {
140
+ method: "POST",
141
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
142
+ body: body.toString(),
143
+ });
144
+ const data = await resp.json();
145
+ if (!resp.ok) {
146
+ throw new Error(`Auth error: ${data.error_description ?? data.error ?? resp.statusText}`);
147
+ }
148
+ return data;
149
+ }
150
+ // ─── device code flow ───────────────────────────────────────────────
151
+ export async function requestDeviceCode(config) {
152
+ return httpsPost(`https://login.microsoftonline.com/${config.tenantId}/oauth2/v2.0/devicecode`, new URLSearchParams({ client_id: config.clientId, scope: SCOPES }));
153
+ }
154
+ async function pollForToken(deviceCode, config) {
155
+ const data = await httpsPost(`https://login.microsoftonline.com/${config.tenantId}/oauth2/v2.0/token`, new URLSearchParams({
156
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
157
+ client_id: config.clientId,
158
+ device_code: deviceCode,
159
+ }));
160
+ return {
161
+ access_token: data.access_token,
162
+ refresh_token: data.refresh_token,
163
+ expires_at: Date.now() + (data.expires_in - 60) * 1000,
164
+ scope: data.scope,
165
+ };
166
+ }
167
+ // ─── token refresh ──────────────────────────────────────────────────
168
+ async function refreshAccessToken(config, refreshToken) {
169
+ const data = await httpsPost(`https://login.microsoftonline.com/${config.tenantId}/oauth2/v2.0/token`, new URLSearchParams({
170
+ grant_type: "refresh_token",
171
+ client_id: config.clientId,
172
+ refresh_token: refreshToken,
173
+ scope: SCOPES,
174
+ }));
175
+ return {
176
+ access_token: data.access_token,
177
+ // OAuth servers may omit refresh_token when they do not rotate it.
178
+ refresh_token: data.refresh_token ?? refreshToken,
179
+ expires_at: Date.now() + (data.expires_in - 60) * 1000,
180
+ scope: data.scope,
181
+ };
182
+ }
183
+ // ─── public API ─────────────────────────────────────────────────────
184
+ /** Serialises refresh attempts per account without mixing tokens across accounts. */
185
+ const refreshLocks = new Map();
186
+ /**
187
+ * Get a valid access token (auto-refresh if expired or within 5 min of expiry).
188
+ * Uses a mutex to avoid concurrent refresh races.
189
+ */
190
+ export async function getAccessToken(accountName) {
191
+ const config = resolveAccount(accountName);
192
+ let tokens = loadTokens(config);
193
+ if (!tokens) {
194
+ throw new Error(`Not authenticated for account "${config.name}". Run: m365-mcp-auth login [--account=${config.name}]`);
195
+ }
196
+ // Still valid with 5-minute buffer
197
+ if (tokens.expires_at > Date.now() + 5 * 60 * 1000) {
198
+ return tokens.access_token;
199
+ }
200
+ // Serialise refresh — if another caller is already refreshing, wait for it
201
+ let refreshLock = refreshLocks.get(config.tokenPath);
202
+ if (!refreshLock) {
203
+ refreshLock = doRefresh(config, tokens.refresh_token).finally(() => {
204
+ refreshLocks.delete(config.tokenPath);
205
+ });
206
+ refreshLocks.set(config.tokenPath, refreshLock);
207
+ }
208
+ return refreshLock;
209
+ }
210
+ async function doRefresh(config, refreshToken) {
211
+ // Double-check tokens haven't been refreshed by a concurrent caller
212
+ const current = loadTokens(config);
213
+ if (current && current.expires_at > Date.now() + 5 * 60 * 1000) {
214
+ return current.access_token;
215
+ }
216
+ let attempt = 0;
217
+ let delay = 1000;
218
+ while (attempt < 3) {
219
+ try {
220
+ const fresh = await refreshAccessToken(config, current?.refresh_token ?? refreshToken);
221
+ saveTokens(config, fresh);
222
+ return fresh.access_token;
223
+ }
224
+ catch (err) {
225
+ attempt++;
226
+ if (err.message.includes("expired") || err.message.includes("invalid_grant")) {
227
+ throw new Error(`Refresh token expired for "${config.name}". Re-authenticate: m365-mcp-auth login`);
228
+ }
229
+ if (attempt >= 3)
230
+ throw err;
231
+ await new Promise((r) => setTimeout(r, delay));
232
+ delay *= 2;
233
+ }
234
+ }
235
+ throw new Error(`Failed to refresh token after 3 attempts`);
236
+ }
237
+ /**
238
+ * Full device-code authentication flow.
239
+ * Calls onPrompt(verificationUrl, userCode) so the caller can display instructions.
240
+ */
241
+ export async function authenticate(accountName, onPrompt) {
242
+ const config = resolveAccount(accountName);
243
+ // Check existing
244
+ const existing = loadTokens(config);
245
+ if (existing && existing.expires_at > Date.now() + 5 * 60 * 1000) {
246
+ return existing;
247
+ }
248
+ // Request device code
249
+ const dc = await requestDeviceCode(config);
250
+ onPrompt(dc.verification_uri, dc.user_code);
251
+ // Poll
252
+ const deadline = Date.now() + dc.expires_in * 1000;
253
+ while (Date.now() < deadline) {
254
+ await new Promise((r) => setTimeout(r, dc.interval * 1000));
255
+ try {
256
+ const tokens = await pollForToken(dc.device_code, config);
257
+ return saveTokens(config, tokens);
258
+ }
259
+ catch (err) {
260
+ if (err.message.includes("authorization_pending")) {
261
+ continue;
262
+ }
263
+ if (err.message.includes("authorization_declined") ||
264
+ err.message.includes("access_denied")) {
265
+ throw new Error("User declined authorization.");
266
+ }
267
+ if (err.message.includes("expired_token") ||
268
+ err.message.includes("AADSTS70019")) {
269
+ throw new Error("Device code expired. Please try again.");
270
+ }
271
+ throw err;
272
+ }
273
+ }
274
+ throw new Error("Authentication timed out.");
275
+ }
276
+ export async function getTokenStatus(accountName) {
277
+ const config = resolveAccount(accountName);
278
+ const tokens = loadTokens(config);
279
+ if (!tokens)
280
+ return { account: config.name, valid: false, expires: null };
281
+ const valid = tokens.expires_at > Date.now();
282
+ return {
283
+ account: config.name,
284
+ valid,
285
+ expires: new Date(tokens.expires_at).toISOString(),
286
+ };
287
+ }
288
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Typed Microsoft Graph API client with:
3
+ * - Automatic pagination (@odata.nextLink)
4
+ * - Rate-limit handling (429 → exponential backoff + retry)
5
+ * - Transient error retry (5xx)
6
+ * - Configurable timezone
7
+ */
8
+ export declare class GraphClient {
9
+ private accountName?;
10
+ timezone: string;
11
+ constructor(timezone?: string, accountName?: string | undefined);
12
+ private request;
13
+ /** Single-page GET */
14
+ get<T>(path: string): Promise<T>;
15
+ /** GET that follows @odata.nextLink pagination, collecting all results */
16
+ getAll<T>(path: string, maxPages?: number, maxItems?: number): Promise<T[]>;
17
+ /** Paginated GET — yields pages lazily */
18
+ paginate<T>(path: string, maxPages?: number): AsyncGenerator<T[]>;
19
+ /** POST */
20
+ post<T>(path: string, body: unknown): Promise<T>;
21
+ /** PATCH */
22
+ patch<T>(path: string, body: unknown): Promise<T>;
23
+ /** DELETE */
24
+ delete(path: string): Promise<void>;
25
+ /** Download raw file content with retry on transient failures */
26
+ download(url: string, maxBytes?: number): Promise<string>;
27
+ /** Format a dateTimeTimeZone object for Graph API */
28
+ dateTimeStr(iso: string): {
29
+ dateTime: string;
30
+ timeZone: string;
31
+ };
32
+ }
package/dist/graph.js ADDED
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Typed Microsoft Graph API client with:
3
+ * - Automatic pagination (@odata.nextLink)
4
+ * - Rate-limit handling (429 → exponential backoff + retry)
5
+ * - Transient error retry (5xx)
6
+ * - Configurable timezone
7
+ */
8
+ import { getAccessToken } from "./auth.js";
9
+ const GRAPH_BASE = "https://graph.microsoft.com/v1.0";
10
+ const MAX_RETRIES = 4;
11
+ const RETRY_BACKOFF_MS = 1000;
12
+ function retryDelayMs(retryAfter, fallbackMs) {
13
+ if (!retryAfter)
14
+ return fallbackMs;
15
+ const seconds = Number(retryAfter);
16
+ if (Number.isFinite(seconds) && seconds >= 0)
17
+ return seconds * 1000;
18
+ const retryAt = Date.parse(retryAfter);
19
+ return Number.isNaN(retryAt) ? fallbackMs : Math.max(0, retryAt - Date.now());
20
+ }
21
+ export class GraphClient {
22
+ accountName;
23
+ timezone;
24
+ constructor(timezone = "UTC", accountName) {
25
+ this.accountName = accountName;
26
+ this.timezone = timezone;
27
+ }
28
+ // ─── request ────────────────────────────────────────────────────
29
+ async request(path, method = "GET", body, raw = false) {
30
+ const token = await getAccessToken(this.accountName);
31
+ const url = path.startsWith("https://") ? path : `${GRAPH_BASE}${path}`;
32
+ if (new URL(url).origin !== new URL(GRAPH_BASE).origin) {
33
+ throw new Error("Graph API request URL must use graph.microsoft.com");
34
+ }
35
+ let attempt = 0;
36
+ let delay = RETRY_BACKOFF_MS;
37
+ while (true) {
38
+ const opts = {
39
+ method,
40
+ headers: {
41
+ Authorization: `Bearer ${token}`,
42
+ "Content-Type": "application/json",
43
+ Accept: "application/json",
44
+ Prefer: `outlook.timezone="${this.timezone}"`,
45
+ },
46
+ };
47
+ if (body && method !== "GET") {
48
+ opts.body = JSON.stringify(body);
49
+ }
50
+ let resp;
51
+ try {
52
+ resp = await fetch(url, opts);
53
+ }
54
+ catch (error) {
55
+ if (attempt >= MAX_RETRIES)
56
+ throw error;
57
+ await new Promise((r) => setTimeout(r, delay));
58
+ attempt++;
59
+ delay *= 2;
60
+ continue;
61
+ }
62
+ // 204 No Content
63
+ if (resp.status === 204)
64
+ return undefined;
65
+ // Rate-limited — retry with Retry-After header or exponential backoff
66
+ if (resp.status === 429) {
67
+ if (attempt >= MAX_RETRIES) {
68
+ throw new Error(`Rate limited after ${MAX_RETRIES} retries`);
69
+ }
70
+ const retryAfter = resp.headers.get("Retry-After");
71
+ const waitMs = retryDelayMs(retryAfter, delay);
72
+ await new Promise((r) => setTimeout(r, waitMs));
73
+ attempt++;
74
+ delay *= 2;
75
+ continue;
76
+ }
77
+ // Transient server error — retry
78
+ if (resp.status >= 500 && resp.status < 600) {
79
+ if (attempt >= MAX_RETRIES) {
80
+ throw new Error(`Graph API server error ${resp.status} after ${MAX_RETRIES} retries`);
81
+ }
82
+ await new Promise((r) => setTimeout(r, delay));
83
+ attempt++;
84
+ delay *= 2;
85
+ continue;
86
+ }
87
+ // Parse response
88
+ if (raw && resp.ok) {
89
+ return resp;
90
+ }
91
+ let parsed;
92
+ try {
93
+ parsed = await resp.json();
94
+ }
95
+ catch {
96
+ // Graph action endpoints such as sendMail commonly return 202 with
97
+ // an empty body. A successful empty response is not an error.
98
+ if (resp.ok)
99
+ return undefined;
100
+ throw new Error(`Graph API ${resp.status}: unable to parse response`);
101
+ }
102
+ if (!resp.ok) {
103
+ const err = parsed?.error;
104
+ throw new Error(`Graph API ${resp.status}: ${err?.message ?? resp.statusText}`);
105
+ }
106
+ return parsed;
107
+ }
108
+ }
109
+ // ─── public methods ──────────────────────────────────────────────
110
+ /** Single-page GET */
111
+ async get(path) {
112
+ return this.request(path);
113
+ }
114
+ /** GET that follows @odata.nextLink pagination, collecting all results */
115
+ async getAll(path, maxPages = 10, maxItems = Infinity) {
116
+ const results = [];
117
+ let url = path;
118
+ let pages = 0;
119
+ while (url && pages < maxPages && results.length < maxItems) {
120
+ // If url is a nextLink, it's already absolute
121
+ const data = await this.request(url);
122
+ if (data.value)
123
+ results.push(...data.value.slice(0, maxItems - results.length));
124
+ url = data["@odata.nextLink"];
125
+ pages++;
126
+ }
127
+ return results;
128
+ }
129
+ /** Paginated GET — yields pages lazily */
130
+ async *paginate(path, maxPages = 10) {
131
+ let url = path;
132
+ let pages = 0;
133
+ while (url && pages < maxPages) {
134
+ const data = await this.request(url);
135
+ if (data.value)
136
+ yield data.value;
137
+ url = data["@odata.nextLink"];
138
+ pages++;
139
+ }
140
+ }
141
+ /** POST */
142
+ async post(path, body) {
143
+ return this.request(path, "POST", body);
144
+ }
145
+ /** PATCH */
146
+ async patch(path, body) {
147
+ return this.request(path, "PATCH", body);
148
+ }
149
+ /** DELETE */
150
+ async delete(path) {
151
+ await this.request(path, "DELETE");
152
+ }
153
+ /** Download raw file content with retry on transient failures */
154
+ async download(url, maxBytes = 50_000) {
155
+ const token = await getAccessToken(this.accountName);
156
+ let attempt = 0;
157
+ let delay = RETRY_BACKOFF_MS;
158
+ while (true) {
159
+ const resp = await fetch(url, {
160
+ headers: { Authorization: `Bearer ${token}` },
161
+ });
162
+ if (resp.status === 429 && attempt < MAX_RETRIES) {
163
+ const retryAfter = resp.headers.get("Retry-After");
164
+ await new Promise((r) => setTimeout(r, retryDelayMs(retryAfter, delay)));
165
+ attempt++;
166
+ delay *= 2;
167
+ continue;
168
+ }
169
+ if (resp.status >= 500 && resp.status < 600 && attempt < MAX_RETRIES) {
170
+ await new Promise((r) => setTimeout(r, delay));
171
+ attempt++;
172
+ delay *= 2;
173
+ continue;
174
+ }
175
+ if (!resp.ok) {
176
+ throw new Error(`Download failed: ${resp.status} ${resp.statusText}`);
177
+ }
178
+ const buf = await resp.arrayBuffer();
179
+ // Truncate to text-safe size
180
+ const slice = buf.byteLength > maxBytes ? buf.slice(0, maxBytes) : buf;
181
+ return new TextDecoder().decode(slice);
182
+ }
183
+ }
184
+ /** Format a dateTimeTimeZone object for Graph API */
185
+ dateTimeStr(iso) {
186
+ return { dateTime: iso, timeZone: this.timezone };
187
+ }
188
+ }
189
+ //# sourceMappingURL=graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.js","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * m365-mcp — Production-grade Microsoft 365 MCP server
4
+ *
5
+ * Delegated OAuth device-code flow | Multi-account | Pagination | Rate-limit handling
6
+ *
7
+ * Covers: Email, Calendar, Contacts, OneDrive, Teams, Tasks, Users
8
+ */
9
+ export {};