@rubytech/create-maxy 1.0.762 → 1.0.764
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/package.json +1 -1
- package/payload/platform/neo4j/schema.cypher +50 -0
- package/payload/platform/package-lock.json +56 -1
- package/payload/platform/package.json +1 -0
- package/payload/platform/plugins/docs/references/outlook-guide.md +69 -0
- package/payload/platform/plugins/outlook/PLUGIN.md +48 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js +94 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.js +31 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/log.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js +213 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.js +130 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.d.ts +65 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js +261 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/pkce-flow.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.d.ts +61 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.js +170 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/index.d.ts +18 -0
- package/payload/platform/plugins/outlook/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/index.js +152 -0
- package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts +60 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js +189 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/log.d.ts +23 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/log.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/log.js +53 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/log.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts +26 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js +50 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.d.ts +12 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.js +32 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-event.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.d.ts +59 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.js +54 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-list.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.d.ts +14 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.js +45 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/contacts-list.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts +15 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js +48 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.d.ts +8 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js +49 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts +19 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js +58 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/package.json +20 -0
- package/payload/platform/plugins/outlook/mcp/scripts/verify-doc-impl.sh +109 -0
- package/payload/platform/plugins/outlook/references/auth.md +118 -0
- package/payload/platform/plugins/outlook/references/graph-surfaces.md +114 -0
- package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +65 -0
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/chunk-EIQT6QDH.js +9562 -0
- package/payload/server/chunk-S3M2NZMA.js +3136 -0
- package/payload/server/chunk-SGBNY4NP.js +9540 -0
- package/payload/server/client-pool-5V5GX3UT.js +28 -0
- package/payload/server/maxy-edge.js +2 -2
- package/payload/server/public/assets/{admin-7vGwd7wu.js → admin-V6NDkEoR.js} +2 -2
- package/payload/server/public/index.html +1 -1
- package/payload/server/server.js +104 -6
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @maxy/outlook MCP server.
|
|
4
|
+
*
|
|
5
|
+
* Read-only Microsoft Graph access for Outlook.com / Microsoft 365 mailboxes.
|
|
6
|
+
* Admin agent only. Public-agent surface is explicitly excluded.
|
|
7
|
+
*
|
|
8
|
+
* Auth: per-account OAuth Auth Code + PKCE. Tokens persist encrypted to
|
|
9
|
+
* {ACCOUNTS_DIR}/{accountId}/secrets/outlook/{tokens.enc, .key}
|
|
10
|
+
*
|
|
11
|
+
* Required environment:
|
|
12
|
+
* ACCOUNT_ID — provided by getMcpServers when spawned per account.
|
|
13
|
+
* PLATFORM_ROOT — repo root; used to derive ACCOUNTS_DIR.
|
|
14
|
+
* OUTLOOK_CLIENT_ID — Azure AD app (client) ID. Operator-set per install.
|
|
15
|
+
* OUTLOOK_TENANT_ID — Azure tenant ("common" for multi-tenant). Default "common".
|
|
16
|
+
*/
|
|
17
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
18
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
19
|
+
import { resolve } from "node:path";
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
import { TokenStore } from "./auth/token-store.js";
|
|
22
|
+
import { runAccountRegister } from "./tools/account-register.js";
|
|
23
|
+
import { runMailList } from "./tools/mail-list.js";
|
|
24
|
+
import { runMailSearch } from "./tools/mail-search.js";
|
|
25
|
+
import { runCalendarList } from "./tools/calendar-list.js";
|
|
26
|
+
import { runCalendarEvent } from "./tools/calendar-event.js";
|
|
27
|
+
import { runContactsList } from "./tools/contacts-list.js";
|
|
28
|
+
import { runMailboxInfo } from "./tools/mailbox-info.js";
|
|
29
|
+
const ACCOUNT_ID = process.env.ACCOUNT_ID;
|
|
30
|
+
const PLATFORM_ROOT = process.env.PLATFORM_ROOT ?? process.cwd();
|
|
31
|
+
const ACCOUNTS_DIR = resolve(PLATFORM_ROOT, "..", "data/accounts");
|
|
32
|
+
const OUTLOOK_CLIENT_ID = process.env.OUTLOOK_CLIENT_ID ?? "";
|
|
33
|
+
const OUTLOOK_TENANT_ID = process.env.OUTLOOK_TENANT_ID ?? "common";
|
|
34
|
+
if (!ACCOUNT_ID) {
|
|
35
|
+
process.stderr.write("[outlook-mcp] FATAL: ACCOUNT_ID env var is required\n");
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
function getStore() {
|
|
39
|
+
return new TokenStore(ACCOUNT_ID, ACCOUNTS_DIR);
|
|
40
|
+
}
|
|
41
|
+
function getConfig() {
|
|
42
|
+
if (!OUTLOOK_CLIENT_ID) {
|
|
43
|
+
throw new Error("OUTLOOK_CLIENT_ID is not configured. Set it in the operator's Maxy install before using outlook tools.");
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
clientId: OUTLOOK_CLIENT_ID,
|
|
47
|
+
tenantId: OUTLOOK_TENANT_ID,
|
|
48
|
+
accountId: ACCOUNT_ID,
|
|
49
|
+
tokenStore: getStore(),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function ok(payload) {
|
|
53
|
+
return {
|
|
54
|
+
content: [
|
|
55
|
+
{ type: "text", text: typeof payload === "string" ? payload : JSON.stringify(payload, null, 2) },
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function fail(message) {
|
|
60
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
61
|
+
}
|
|
62
|
+
const server = new McpServer({
|
|
63
|
+
name: "outlook",
|
|
64
|
+
version: "0.1.0",
|
|
65
|
+
});
|
|
66
|
+
server.tool("outlook-account-register", "Initiate the OAuth Auth Code + PKCE flow for this account. Returns an authorization URL the operator opens in the VNC browser. Tool blocks until the operator completes consent (5-minute timeout) or fails loudly.", {}, async () => {
|
|
67
|
+
try {
|
|
68
|
+
const config = getConfig();
|
|
69
|
+
const result = await runAccountRegister({
|
|
70
|
+
accountId: ACCOUNT_ID,
|
|
71
|
+
clientId: config.clientId,
|
|
72
|
+
tenantId: config.tenantId,
|
|
73
|
+
tokenStore: config.tokenStore,
|
|
74
|
+
});
|
|
75
|
+
return ok(result);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
return fail(`outlook-account-register failed: ${err.message}`);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
// @ts-expect-error — MCP SDK server.tool() type inference exceeds depth limit with Zod v4
|
|
82
|
+
server.tool("outlook-mail-list", "List recent messages in the mailbox. Defaults to top=25 ordered by receivedDateTime DESC. Folder defaults to Inbox; pass a folder displayName or id for other folders. Returns id, subject, from, receivedDateTime, bodyPreview, isRead.", {
|
|
83
|
+
top: z.number().int().min(1).max(250).optional(),
|
|
84
|
+
folder: z.string().optional(),
|
|
85
|
+
}, async ({ top, folder }) => {
|
|
86
|
+
try {
|
|
87
|
+
const items = await runMailList(getConfig(), { top, folder });
|
|
88
|
+
return ok(items);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
return fail(`outlook-mail-list failed: ${err.message}`);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
server.tool("outlook-mail-search", "Search mail with Microsoft Graph $search. The query is enclosed in quotes and applied with ConsistencyLevel=eventual. Returns the same shape as outlook-mail-list.", {
|
|
95
|
+
query: z.string().min(1),
|
|
96
|
+
top: z.number().int().min(1).max(250).optional(),
|
|
97
|
+
}, async ({ query, top }) => {
|
|
98
|
+
try {
|
|
99
|
+
const items = await runMailSearch(getConfig(), { query, top });
|
|
100
|
+
return ok(items);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
return fail(`outlook-mail-search failed: ${err.message}`);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
server.tool("outlook-calendar-list", "List calendar events in the next rangeDays days (default 7, max 365). Returns id, subject, start, end, location, attendees, isAllDay, organizer.", {
|
|
107
|
+
rangeDays: z.number().int().min(1).max(365).optional(),
|
|
108
|
+
top: z.number().int().min(1).max(500).optional(),
|
|
109
|
+
}, async ({ rangeDays, top }) => {
|
|
110
|
+
try {
|
|
111
|
+
const events = await runCalendarList(getConfig(), { rangeDays, top });
|
|
112
|
+
return ok(events);
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
return fail(`outlook-calendar-list failed: ${err.message}`);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
server.tool("outlook-calendar-event", "Fetch a single calendar event by id, including the full body, attendees, and webLink.", {
|
|
119
|
+
eventId: z.string().min(1),
|
|
120
|
+
}, async ({ eventId }) => {
|
|
121
|
+
try {
|
|
122
|
+
const detail = await runCalendarEvent(getConfig(), { eventId });
|
|
123
|
+
return ok(detail);
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
return fail(`outlook-calendar-event failed: ${err.message}`);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
server.tool("outlook-contacts-list", "List contacts. Defaults to top=50, max 250. Returns id, displayName, emailAddresses, phones, jobTitle, companyName.", {
|
|
130
|
+
top: z.number().int().min(1).max(250).optional(),
|
|
131
|
+
}, async ({ top }) => {
|
|
132
|
+
try {
|
|
133
|
+
const items = await runContactsList(getConfig(), { top });
|
|
134
|
+
return ok(items);
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
return fail(`outlook-contacts-list failed: ${err.message}`);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
server.tool("outlook-mailbox-info", "Health probe — reports auth state, refresh-window status, and top-level folder count for this account. Useful for answering 'did account X auth?' without grepping logs.", {}, async () => {
|
|
141
|
+
try {
|
|
142
|
+
const info = await runMailboxInfo(getConfig());
|
|
143
|
+
return ok(info);
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
return fail(`outlook-mailbox-info failed: ${err.message}`);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
const transport = new StdioServerTransport();
|
|
150
|
+
await server.connect(transport);
|
|
151
|
+
process.stderr.write(`[outlook-mcp] ready account=${ACCOUNT_ID}\n`);
|
|
152
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Microsoft Graph client wrapper.
|
|
3
|
+
*
|
|
4
|
+
* Builds a `Client` from `@microsoft/microsoft-graph-client` whose auth provider
|
|
5
|
+
* pulls from the per-account TokenStore, with auto-refresh when the access
|
|
6
|
+
* token is within the refresh threshold of expiry.
|
|
7
|
+
*
|
|
8
|
+
* Centralises error classification — every Graph error decision keys off the
|
|
9
|
+
* status code and the structured `error.code` field, never the prose body.
|
|
10
|
+
* Aligns with `feedback_no_stdout_parsing_for_control_flow.md`.
|
|
11
|
+
*/
|
|
12
|
+
import { Client } from "@microsoft/microsoft-graph-client";
|
|
13
|
+
import type { TokenStore } from "../auth/token-store.js";
|
|
14
|
+
export interface GraphClientConfig {
|
|
15
|
+
clientId: string;
|
|
16
|
+
tenantId: string;
|
|
17
|
+
accountId: string;
|
|
18
|
+
tokenStore: TokenStore;
|
|
19
|
+
}
|
|
20
|
+
export type GraphErrorClass = {
|
|
21
|
+
kind: "auth";
|
|
22
|
+
statusCode: number;
|
|
23
|
+
code: string;
|
|
24
|
+
} | {
|
|
25
|
+
kind: "rate-limit-recoverable";
|
|
26
|
+
statusCode: number;
|
|
27
|
+
retryAfterMs: number;
|
|
28
|
+
} | {
|
|
29
|
+
kind: "rate-limit-terminal";
|
|
30
|
+
statusCode: number;
|
|
31
|
+
} | {
|
|
32
|
+
kind: "on-prem";
|
|
33
|
+
statusCode: number;
|
|
34
|
+
mailServer?: string;
|
|
35
|
+
} | {
|
|
36
|
+
kind: "5xx";
|
|
37
|
+
statusCode: number;
|
|
38
|
+
code: string;
|
|
39
|
+
} | {
|
|
40
|
+
kind: "other";
|
|
41
|
+
statusCode: number;
|
|
42
|
+
code: string;
|
|
43
|
+
};
|
|
44
|
+
export declare function classifyGraphError(err: unknown): GraphErrorClass;
|
|
45
|
+
/**
|
|
46
|
+
* Factory wrapping `Client.init`. Exported as a named function so tests can
|
|
47
|
+
* `vi.spyOn(graphClient, "createGraphClient")` reliably (avoids the
|
|
48
|
+
* destructured-ESM mock no-op pitfall).
|
|
49
|
+
*/
|
|
50
|
+
export declare function createGraphClient(config: GraphClientConfig): Client;
|
|
51
|
+
export declare function getOrRefreshAccessToken(config: GraphClientConfig): Promise<string>;
|
|
52
|
+
/**
|
|
53
|
+
* Run a Graph call with classification, single retry on `auth` (refresh +
|
|
54
|
+
* retry once), and respect for 429 Retry-After. All terminal classes throw
|
|
55
|
+
* with operator-readable messages.
|
|
56
|
+
*/
|
|
57
|
+
export declare function callGraph<T>(config: GraphClientConfig, invoke: (client: Client) => Promise<T>, opts?: {
|
|
58
|
+
tool: string;
|
|
59
|
+
}): Promise<T>;
|
|
60
|
+
//# sourceMappingURL=graph-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-client.d.ts","sourceRoot":"","sources":["../../src/lib/graph-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAE3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,UAAU,CAAC;CACxB;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,wBAAwB,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC5E;IAAE,IAAI,EAAE,qBAAqB,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAqDxD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,CA2BhE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAWnE;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAgDxF;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EACtC,IAAI,GAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAsB,GACzC,OAAO,CAAC,CAAC,CAAC,CAsDZ"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Microsoft Graph client wrapper.
|
|
3
|
+
*
|
|
4
|
+
* Builds a `Client` from `@microsoft/microsoft-graph-client` whose auth provider
|
|
5
|
+
* pulls from the per-account TokenStore, with auto-refresh when the access
|
|
6
|
+
* token is within the refresh threshold of expiry.
|
|
7
|
+
*
|
|
8
|
+
* Centralises error classification — every Graph error decision keys off the
|
|
9
|
+
* status code and the structured `error.code` field, never the prose body.
|
|
10
|
+
* Aligns with `feedback_no_stdout_parsing_for_control_flow.md`.
|
|
11
|
+
*/
|
|
12
|
+
import { Client } from "@microsoft/microsoft-graph-client";
|
|
13
|
+
import { refreshAccessToken } from "../auth/pkce-flow.js";
|
|
14
|
+
import { log } from "./log.js";
|
|
15
|
+
/** Pull (statusCode, code, headers) from whatever shape the SDK threw. */
|
|
16
|
+
function normalizeGraphError(err) {
|
|
17
|
+
const e = err;
|
|
18
|
+
const statusCode = e.statusCode ?? e.status ?? 0;
|
|
19
|
+
let code = e.code ?? "";
|
|
20
|
+
if (!code && typeof e.body === "string") {
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(e.body);
|
|
23
|
+
code = parsed.error?.code ?? "";
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// body wasn't JSON; leave code empty
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else if (!code && e.body && typeof e.body === "object") {
|
|
30
|
+
code = e.body.error?.code ?? "";
|
|
31
|
+
}
|
|
32
|
+
let retryAfterHeader = null;
|
|
33
|
+
let mailServerHeader = null;
|
|
34
|
+
const fetchHeaders = e.response?.headers;
|
|
35
|
+
if (fetchHeaders && typeof fetchHeaders.get === "function") {
|
|
36
|
+
retryAfterHeader = fetchHeaders.get("retry-after") ?? null;
|
|
37
|
+
mailServerHeader = fetchHeaders.get("x-mailbox-server") ?? null;
|
|
38
|
+
}
|
|
39
|
+
else if (e.headers instanceof Map) {
|
|
40
|
+
retryAfterHeader = e.headers.get("retry-after") ?? e.headers.get("Retry-After") ?? null;
|
|
41
|
+
mailServerHeader = e.headers.get("x-mailbox-server") ?? null;
|
|
42
|
+
}
|
|
43
|
+
else if (e.headers && typeof e.headers === "object") {
|
|
44
|
+
const h = e.headers;
|
|
45
|
+
retryAfterHeader =
|
|
46
|
+
h["retry-after"] ?? h["Retry-After"] ?? h["RETRY-AFTER"] ?? null;
|
|
47
|
+
mailServerHeader = h["x-mailbox-server"] ?? h["X-MailboxServer"] ?? null;
|
|
48
|
+
}
|
|
49
|
+
return { statusCode, code, retryAfterHeader, mailServerHeader };
|
|
50
|
+
}
|
|
51
|
+
export function classifyGraphError(err) {
|
|
52
|
+
const norm = normalizeGraphError(err);
|
|
53
|
+
const { statusCode, code, retryAfterHeader, mailServerHeader } = norm;
|
|
54
|
+
if (statusCode === 401) {
|
|
55
|
+
return { kind: "auth", statusCode, code: code || "Unauthenticated" };
|
|
56
|
+
}
|
|
57
|
+
if (statusCode === 429) {
|
|
58
|
+
if (retryAfterHeader) {
|
|
59
|
+
const seconds = Number(retryAfterHeader);
|
|
60
|
+
if (Number.isFinite(seconds) && seconds >= 0) {
|
|
61
|
+
return {
|
|
62
|
+
kind: "rate-limit-recoverable",
|
|
63
|
+
statusCode,
|
|
64
|
+
retryAfterMs: seconds * 1000,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { kind: "rate-limit-terminal", statusCode };
|
|
69
|
+
}
|
|
70
|
+
if (statusCode === 503 && code === "MailboxNotEnabledForRESTAPI") {
|
|
71
|
+
return { kind: "on-prem", statusCode, mailServer: mailServerHeader ?? undefined };
|
|
72
|
+
}
|
|
73
|
+
if (statusCode >= 500 && statusCode < 600) {
|
|
74
|
+
return { kind: "5xx", statusCode, code: code || "ServerError" };
|
|
75
|
+
}
|
|
76
|
+
return { kind: "other", statusCode, code: code || "Unknown" };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Factory wrapping `Client.init`. Exported as a named function so tests can
|
|
80
|
+
* `vi.spyOn(graphClient, "createGraphClient")` reliably (avoids the
|
|
81
|
+
* destructured-ESM mock no-op pitfall).
|
|
82
|
+
*/
|
|
83
|
+
export function createGraphClient(config) {
|
|
84
|
+
return Client.init({
|
|
85
|
+
authProvider: async (done) => {
|
|
86
|
+
try {
|
|
87
|
+
const token = await getOrRefreshAccessToken(config);
|
|
88
|
+
done(null, token);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
done(err, null);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
export async function getOrRefreshAccessToken(config) {
|
|
97
|
+
const { tokenStore, clientId, tenantId, accountId } = config;
|
|
98
|
+
const blob = tokenStore.read();
|
|
99
|
+
if (!blob) {
|
|
100
|
+
throw new Error(`auth-required: Outlook not connected for account=${accountId}; run outlook-account-register.`);
|
|
101
|
+
}
|
|
102
|
+
if (tokenStore.refreshTokenExpired() || !blob.refreshToken) {
|
|
103
|
+
throw new Error(`auth-required: Outlook refresh token expired for account=${accountId}; run outlook-account-register.`);
|
|
104
|
+
}
|
|
105
|
+
if (!tokenStore.needsRefresh()) {
|
|
106
|
+
return blob.accessToken;
|
|
107
|
+
}
|
|
108
|
+
const oldExpSec = Math.floor(blob.accessTokenExpiry / 1000);
|
|
109
|
+
try {
|
|
110
|
+
const refreshed = await refreshAccessToken({
|
|
111
|
+
clientId,
|
|
112
|
+
tenantId,
|
|
113
|
+
refreshToken: blob.refreshToken,
|
|
114
|
+
});
|
|
115
|
+
const updated = tokenStore.store(refreshed.access_token, refreshed.refresh_token ?? blob.refreshToken, refreshed.expires_in, { graphUserId: blob.graphUserId, scopes: blob.scopes });
|
|
116
|
+
log({
|
|
117
|
+
event: "token-refreshed",
|
|
118
|
+
account: accountId,
|
|
119
|
+
oldExpSec,
|
|
120
|
+
newExpSec: Math.floor(updated.accessTokenExpiry / 1000),
|
|
121
|
+
});
|
|
122
|
+
return updated.accessToken;
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
log({
|
|
126
|
+
event: "token-refresh-failed",
|
|
127
|
+
account: accountId,
|
|
128
|
+
reason: err.message,
|
|
129
|
+
});
|
|
130
|
+
tokenStore.clear();
|
|
131
|
+
throw new Error(`Outlook token refresh failed for account=${accountId}; re-auth required.`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Run a Graph call with classification, single retry on `auth` (refresh +
|
|
136
|
+
* retry once), and respect for 429 Retry-After. All terminal classes throw
|
|
137
|
+
* with operator-readable messages.
|
|
138
|
+
*/
|
|
139
|
+
export async function callGraph(config, invoke, opts = { tool: "graph" }) {
|
|
140
|
+
let attempts = 0;
|
|
141
|
+
// Up to 3 attempts: original, one for auth-refresh, one for 429-recoverable.
|
|
142
|
+
while (true) {
|
|
143
|
+
attempts += 1;
|
|
144
|
+
const client = createGraphClient(config);
|
|
145
|
+
try {
|
|
146
|
+
return await invoke(client);
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
const cls = classifyGraphError(err);
|
|
150
|
+
log({
|
|
151
|
+
event: "graph-error",
|
|
152
|
+
account: config.accountId,
|
|
153
|
+
status: cls.statusCode,
|
|
154
|
+
code: "code" in cls ? cls.code : "",
|
|
155
|
+
retryAfterMs: cls.kind === "rate-limit-recoverable" ? cls.retryAfterMs : null,
|
|
156
|
+
});
|
|
157
|
+
if (cls.kind === "on-prem") {
|
|
158
|
+
log({
|
|
159
|
+
event: "on-prem-rejected",
|
|
160
|
+
account: config.accountId,
|
|
161
|
+
mailServer: cls.mailServer ?? "unknown",
|
|
162
|
+
});
|
|
163
|
+
throw new Error("Microsoft Graph does not support on-premises Exchange. Use Task 769 (IMAP).");
|
|
164
|
+
}
|
|
165
|
+
if (cls.kind === "rate-limit-terminal") {
|
|
166
|
+
throw new Error(`Outlook rate-limited without Retry-After hint (account=${config.accountId}, tool=${opts.tool}).`);
|
|
167
|
+
}
|
|
168
|
+
if (cls.kind === "auth" && attempts === 1) {
|
|
169
|
+
// Force a refresh on the next iteration by clearing the local cache
|
|
170
|
+
// and explicitly refreshing, then retry.
|
|
171
|
+
await getOrRefreshAccessToken({ ...config });
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (cls.kind === "auth") {
|
|
175
|
+
throw new Error(`Outlook auth expired for account=${config.accountId}; run outlook-account-register.`);
|
|
176
|
+
}
|
|
177
|
+
if (cls.kind === "rate-limit-recoverable" && attempts === 1) {
|
|
178
|
+
await new Promise((resolve) => setTimeout(resolve, cls.retryAfterMs));
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (cls.kind === "5xx" && attempts === 1) {
|
|
182
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=graph-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-client.js","sourceRoot":"","sources":["../../src/lib/graph-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [outlook-mcp] structured logger.
|
|
3
|
+
*
|
|
4
|
+
* Every log line is prefixed `[outlook-mcp]` and rendered as `key=value` pairs
|
|
5
|
+
* (Pino-compatible shape). Bearer tokens and `access_token=` query strings are
|
|
6
|
+
* redacted before writing — defense in depth even though tools never log them.
|
|
7
|
+
*/
|
|
8
|
+
export declare function redact(value: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Log an event line. The first key in `fields` (typically `event`) becomes the
|
|
11
|
+
* top-level event tag; remaining keys are rendered as `k=v` pairs.
|
|
12
|
+
*
|
|
13
|
+
* log({ event: "mail-list", account: "abc", count: 25, elapsedMs: 320 })
|
|
14
|
+
* → [outlook-mcp] mail-list account=abc count=25 elapsedMs=320
|
|
15
|
+
*/
|
|
16
|
+
export declare function log(fields: {
|
|
17
|
+
event: string;
|
|
18
|
+
} & Record<string, unknown>): void;
|
|
19
|
+
/** Truncate a string to N chars, replacing the tail with `…` if it overflowed. */
|
|
20
|
+
export declare function trunc(value: string, max: number): string;
|
|
21
|
+
/** Hex-prefix of a SHA-256 challenge for log identification (8 chars). */
|
|
22
|
+
export declare function challengePrefix(challenge: string): string;
|
|
23
|
+
//# sourceMappingURL=log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/lib/log.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI5C;AAcD;;;;;;GAMG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAO7E;AAED,kFAAkF;AAClF,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzD"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [outlook-mcp] structured logger.
|
|
3
|
+
*
|
|
4
|
+
* Every log line is prefixed `[outlook-mcp]` and rendered as `key=value` pairs
|
|
5
|
+
* (Pino-compatible shape). Bearer tokens and `access_token=` query strings are
|
|
6
|
+
* redacted before writing — defense in depth even though tools never log them.
|
|
7
|
+
*/
|
|
8
|
+
const REDACT_BEARER = /Bearer\s+[A-Za-z0-9._\-+/=]+/g;
|
|
9
|
+
const REDACT_ACCESS_TOKEN = /access_token=[A-Za-z0-9._\-+/=]+/g;
|
|
10
|
+
export function redact(value) {
|
|
11
|
+
return value
|
|
12
|
+
.replace(REDACT_BEARER, "Bearer <REDACTED>")
|
|
13
|
+
.replace(REDACT_ACCESS_TOKEN, "access_token=<REDACTED>");
|
|
14
|
+
}
|
|
15
|
+
function fmt(value) {
|
|
16
|
+
if (value === null || value === undefined)
|
|
17
|
+
return "null";
|
|
18
|
+
if (typeof value === "string") {
|
|
19
|
+
if (/[\s"=]/.test(value)) {
|
|
20
|
+
return `"${redact(value).replace(/"/g, '\\"')}"`;
|
|
21
|
+
}
|
|
22
|
+
return redact(value);
|
|
23
|
+
}
|
|
24
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
25
|
+
return String(value);
|
|
26
|
+
return `"${redact(JSON.stringify(value)).replace(/"/g, '\\"')}"`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Log an event line. The first key in `fields` (typically `event`) becomes the
|
|
30
|
+
* top-level event tag; remaining keys are rendered as `k=v` pairs.
|
|
31
|
+
*
|
|
32
|
+
* log({ event: "mail-list", account: "abc", count: 25, elapsedMs: 320 })
|
|
33
|
+
* → [outlook-mcp] mail-list account=abc count=25 elapsedMs=320
|
|
34
|
+
*/
|
|
35
|
+
export function log(fields) {
|
|
36
|
+
const { event, ...rest } = fields;
|
|
37
|
+
const parts = [event];
|
|
38
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
39
|
+
parts.push(`${key}=${fmt(value)}`);
|
|
40
|
+
}
|
|
41
|
+
process.stderr.write(`[outlook-mcp] ${parts.join(" ")}\n`);
|
|
42
|
+
}
|
|
43
|
+
/** Truncate a string to N chars, replacing the tail with `…` if it overflowed. */
|
|
44
|
+
export function trunc(value, max) {
|
|
45
|
+
if (value.length <= max)
|
|
46
|
+
return value;
|
|
47
|
+
return value.slice(0, max - 1) + "…";
|
|
48
|
+
}
|
|
49
|
+
/** Hex-prefix of a SHA-256 challenge for log identification (8 chars). */
|
|
50
|
+
export function challengePrefix(challenge) {
|
|
51
|
+
return challenge.slice(0, 8);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/lib/log.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,aAAa,GAAG,+BAA+B,CAAC;AACtD,MAAM,mBAAmB,GAAG,mCAAmC,CAAC;AAEhE,MAAM,UAAU,MAAM,CAAC,KAAa;IAClC,OAAO,KAAK;SACT,OAAO,CAAC,aAAa,EAAE,mBAAmB,CAAC;SAC3C,OAAO,CAAC,mBAAmB,EAAE,yBAAyB,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,GAAG,CAAC,KAAc;IACzB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACzD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC;QACnD,CAAC;QACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IAClF,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC;AACnE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,GAAG,CAAC,MAAmD;IACrE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;IAClC,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC;IACtB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC7D,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,KAAK,CAAC,KAAa,EAAE,GAAW;IAC9C,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,KAAK,CAAC;IACtC,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;AACvC,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { TokenStore } from "../auth/token-store.js";
|
|
2
|
+
export interface AccountRegisterArgs {
|
|
3
|
+
accountId: string;
|
|
4
|
+
clientId: string;
|
|
5
|
+
tenantId: string;
|
|
6
|
+
tokenStore: TokenStore;
|
|
7
|
+
}
|
|
8
|
+
export interface AccountRegisterResult {
|
|
9
|
+
status: "registered";
|
|
10
|
+
authUrl: string;
|
|
11
|
+
graphUserId: string;
|
|
12
|
+
scopes: string[];
|
|
13
|
+
tokenExpSec: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Run the PKCE register flow end-to-end:
|
|
17
|
+
* 1. Bind a loopback callback server, build the auth URL, return URL to operator.
|
|
18
|
+
* 2. Wait for the callback (5-min timeout).
|
|
19
|
+
* 3. Exchange code → tokens; persist encrypted to per-account store.
|
|
20
|
+
* 4. Fetch /me to record graphUserId for log lines.
|
|
21
|
+
*
|
|
22
|
+
* The MCP tool layer prints the auth URL prominently in the tool response so
|
|
23
|
+
* the operator can paste it into the VNC browser.
|
|
24
|
+
*/
|
|
25
|
+
export declare function runAccountRegister(args: AccountRegisterArgs): Promise<AccountRegisterResult>;
|
|
26
|
+
//# sourceMappingURL=account-register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-register.d.ts","sourceRoot":"","sources":["../../src/tools/account-register.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAQD;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAiDlG"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { startPkceFlow } from "../auth/pkce-flow.js";
|
|
2
|
+
import { log } from "../lib/log.js";
|
|
3
|
+
/**
|
|
4
|
+
* Run the PKCE register flow end-to-end:
|
|
5
|
+
* 1. Bind a loopback callback server, build the auth URL, return URL to operator.
|
|
6
|
+
* 2. Wait for the callback (5-min timeout).
|
|
7
|
+
* 3. Exchange code → tokens; persist encrypted to per-account store.
|
|
8
|
+
* 4. Fetch /me to record graphUserId for log lines.
|
|
9
|
+
*
|
|
10
|
+
* The MCP tool layer prints the auth URL prominently in the tool response so
|
|
11
|
+
* the operator can paste it into the VNC browser.
|
|
12
|
+
*/
|
|
13
|
+
export async function runAccountRegister(args) {
|
|
14
|
+
const flow = await startPkceFlow({
|
|
15
|
+
clientId: args.clientId,
|
|
16
|
+
tenantId: args.tenantId,
|
|
17
|
+
accountId: args.accountId,
|
|
18
|
+
});
|
|
19
|
+
process.stderr.write(`[outlook-mcp] OPERATOR ACTION: open this URL in the VNC browser to authorize Outlook for account=${args.accountId}:\n${flow.authUrl}\n`);
|
|
20
|
+
const { tokenResponse, scopes } = await flow.result;
|
|
21
|
+
// Fetch /me with the raw access token directly — bypasses TokenStore so we
|
|
22
|
+
// can persist a single, complete blob (with graphUserId) in one write. If
|
|
23
|
+
// /me fails, no tokens hit disk and the operator simply re-runs register.
|
|
24
|
+
const meResp = await fetch("https://graph.microsoft.com/v1.0/me?$select=id,mail,userPrincipalName", {
|
|
25
|
+
headers: { Authorization: `Bearer ${tokenResponse.access_token}` },
|
|
26
|
+
});
|
|
27
|
+
if (!meResp.ok) {
|
|
28
|
+
const text = await meResp.text();
|
|
29
|
+
throw new Error(`Failed to fetch /me after token exchange (status=${meResp.status}): ${text}`);
|
|
30
|
+
}
|
|
31
|
+
const me = (await meResp.json());
|
|
32
|
+
const graphUserId = me.id ?? me.mail ?? me.userPrincipalName ?? "unknown";
|
|
33
|
+
args.tokenStore.store(tokenResponse.access_token, tokenResponse.refresh_token ?? null, tokenResponse.expires_in, { graphUserId, scopes });
|
|
34
|
+
const tokenExpSec = Math.floor((args.tokenStore.read()?.accessTokenExpiry ?? 0) / 1000);
|
|
35
|
+
log({
|
|
36
|
+
event: "auth-ok",
|
|
37
|
+
account: args.accountId,
|
|
38
|
+
graphUserId,
|
|
39
|
+
scopes: scopes.join(","),
|
|
40
|
+
tokenExpSec,
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
status: "registered",
|
|
44
|
+
authUrl: flow.authUrl,
|
|
45
|
+
graphUserId,
|
|
46
|
+
scopes,
|
|
47
|
+
tokenExpSec,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=account-register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-register.js","sourceRoot":"","sources":["../../src/tools/account-register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAuBpC;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAyB;IAChE,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC;QAC/B,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC,CAAC;IAEH,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,oGAAoG,IAAI,CAAC,SAAS,MAAM,IAAI,CAAC,OAAO,IAAI,CACzI,CAAC;IAEF,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC;IAEpD,2EAA2E;IAC3E,0EAA0E;IAC1E,0EAA0E;IAC1E,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,uEAAuE,EAAE;QAClG,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,aAAa,CAAC,YAAY,EAAE,EAAE;KACnE,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,oDAAoD,MAAM,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IACjG,CAAC;IACD,MAAM,EAAE,GAAG,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAoB,CAAC;IACpD,MAAM,WAAW,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,iBAAiB,IAAI,SAAS,CAAC;IAE1E,IAAI,CAAC,UAAU,CAAC,KAAK,CACnB,aAAa,CAAC,YAAY,EAC1B,aAAa,CAAC,aAAa,IAAI,IAAI,EACnC,aAAa,CAAC,UAAU,EACxB,EAAE,WAAW,EAAE,MAAM,EAAE,CACxB,CAAC;IAEF,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,iBAAiB,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACxF,GAAG,CAAC;QACF,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,IAAI,CAAC,SAAS;QACvB,WAAW;QACX,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QACxB,WAAW;KACZ,CAAC,CAAC;IAEH,OAAO;QACL,MAAM,EAAE,YAAY;QACpB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,WAAW;QACX,MAAM;QACN,WAAW;KACZ,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { GraphClientConfig } from "../lib/graph-client.js";
|
|
2
|
+
import { type CalendarEventItem } from "./calendar-list.js";
|
|
3
|
+
export interface CalendarEventArgs {
|
|
4
|
+
eventId: string;
|
|
5
|
+
}
|
|
6
|
+
export interface CalendarEventDetail extends CalendarEventItem {
|
|
7
|
+
bodyPreview: string;
|
|
8
|
+
body: string;
|
|
9
|
+
webLink: string | null;
|
|
10
|
+
}
|
|
11
|
+
export declare function runCalendarEvent(config: GraphClientConfig, args: CalendarEventArgs): Promise<CalendarEventDetail>;
|
|
12
|
+
//# sourceMappingURL=calendar-event.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar-event.d.ts","sourceRoot":"","sources":["../../src/tools/calendar-event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGhE,OAAO,EAAc,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAqBD,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,mBAAmB,CAAC,CAiC9B"}
|