@chrischall/office-outlook-mcp 0.4.0 → 0.4.1
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +2 -2
- package/dist/bundle.js +621 -174
- package/dist/client.js +13 -8
- package/dist/timezone.js +32 -3
- package/dist/tools/_paging.js +9 -2
- package/dist/tools/calendar.js +17 -24
- package/dist/tools/directory.js +27 -31
- package/dist/tools/healthcheck.js +4 -3
- package/dist/tools/mail.js +16 -22
- package/dist/tools/writes.js +55 -12
- package/dist/version.js +1 -1
- package/dist/view.js +4 -0
- package/package.json +2 -2
- package/server.json +14 -2
- package/skills/outlook-fpx/references/outlook-env.sh +2 -2
package/dist/bundle.js
CHANGED
|
@@ -35334,6 +35334,30 @@ var init_dist = __esm({
|
|
|
35334
35334
|
});
|
|
35335
35335
|
|
|
35336
35336
|
// node_modules/@chrischall/mcp-utils/dist/errors/index.js
|
|
35337
|
+
function firstDeclared(err, read) {
|
|
35338
|
+
let cur = err;
|
|
35339
|
+
for (let depth = 0; depth < DECLARATION_CAUSE_DEPTH && cur !== null && typeof cur === "object"; depth++) {
|
|
35340
|
+
const found = read(cur);
|
|
35341
|
+
if (found !== void 0)
|
|
35342
|
+
return found;
|
|
35343
|
+
cur = cur.cause;
|
|
35344
|
+
}
|
|
35345
|
+
return void 0;
|
|
35346
|
+
}
|
|
35347
|
+
function errorStatusOf(err) {
|
|
35348
|
+
return firstDeclared(err, (link2) => {
|
|
35349
|
+
const e = link2;
|
|
35350
|
+
if (typeof e.status === "number")
|
|
35351
|
+
return e.status;
|
|
35352
|
+
return typeof e.statusCode === "number" ? e.statusCode : void 0;
|
|
35353
|
+
});
|
|
35354
|
+
}
|
|
35355
|
+
function errorKindOf(err) {
|
|
35356
|
+
return firstDeclared(err, (link2) => {
|
|
35357
|
+
const k = link2.kind;
|
|
35358
|
+
return typeof k === "string" && MCP_TOOL_ERROR_KINDS.has(k) ? k : void 0;
|
|
35359
|
+
});
|
|
35360
|
+
}
|
|
35337
35361
|
function redactJsonCookie(quote) {
|
|
35338
35362
|
const stringRe = quote === '"' ? DQ_STRING_RE : SQ_STRING_RE;
|
|
35339
35363
|
return (_m, prefix, key, value) => {
|
|
@@ -35361,21 +35385,82 @@ function messageOf(err) {
|
|
|
35361
35385
|
return err.message;
|
|
35362
35386
|
return String(err);
|
|
35363
35387
|
}
|
|
35364
|
-
var DEFAULT_ERROR_MESSAGE_MAX, McpToolError, HEADER_NAME_RUN, BEARER_RE, JWT_RE, BASIC_AUTH_RE, SET_COOKIE_RE, COOKIE_HEADER_RE, API_KEY_RE, GOOGLE_OAUTH_TOKEN_RE, QUERY_SECRET_RE, OAUTH_CODE_RE, FORM_PASSWORD_RE, HEADER_SECRET_RE, AWS_SIGV4_RE, JSON_SECRET_KEYS, JSON_SECRET_DQ_RE, JSON_SECRET_SQ_RE, JSON_SECRET_NUM_RE, JSON_COOKIE_DQ_RE, JSON_COOKIE_SQ_RE, DQ_STRING_RE, SQ_STRING_RE, ERROR_REDACTION_INPUT_MAX;
|
|
35388
|
+
var DEFAULT_ERROR_MESSAGE_MAX, MCP_TOOL_ERROR_KINDS, McpToolError, DECLARATION_CAUSE_DEPTH, UPSTREAM_BODY_PHRASE, UpstreamFormatError, HEADER_NAME_RUN, BEARER_RE, JWT_RE, BASIC_AUTH_RE, SET_COOKIE_RE, COOKIE_HEADER_RE, API_KEY_RE, GOOGLE_OAUTH_TOKEN_RE, QUERY_SECRET_RE, OAUTH_CODE_RE, FORM_PASSWORD_RE, HEADER_SECRET_RE, AWS_SIGV4_RE, JSON_SECRET_KEYS, JSON_SECRET_DQ_RE, JSON_SECRET_SQ_RE, JSON_SECRET_NUM_RE, JSON_COOKIE_DQ_RE, JSON_COOKIE_SQ_RE, DQ_STRING_RE, SQ_STRING_RE, ERROR_REDACTION_INPUT_MAX;
|
|
35365
35389
|
var init_errors3 = __esm({
|
|
35366
35390
|
"node_modules/@chrischall/mcp-utils/dist/errors/index.js"() {
|
|
35367
35391
|
DEFAULT_ERROR_MESSAGE_MAX = 500;
|
|
35392
|
+
MCP_TOOL_ERROR_KINDS = /* @__PURE__ */ new Set([
|
|
35393
|
+
"no_credential",
|
|
35394
|
+
"credential_rejected",
|
|
35395
|
+
"edge_blocked",
|
|
35396
|
+
"session_expired",
|
|
35397
|
+
"verification_pending",
|
|
35398
|
+
"timeout",
|
|
35399
|
+
"http",
|
|
35400
|
+
"transport",
|
|
35401
|
+
"too_large",
|
|
35402
|
+
"unknown"
|
|
35403
|
+
]);
|
|
35368
35404
|
McpToolError = class extends Error {
|
|
35369
35405
|
/** Actionable remediation text, when one applies. */
|
|
35370
35406
|
hint;
|
|
35407
|
+
/** The upstream HTTP status this failure came from, when known. */
|
|
35408
|
+
status;
|
|
35409
|
+
/** What kind of failure this is, when the thrower knows. */
|
|
35410
|
+
kind;
|
|
35371
35411
|
constructor(message, opts) {
|
|
35372
35412
|
super(message, opts?.cause !== void 0 ? { cause: opts.cause } : void 0);
|
|
35373
35413
|
this.name = "McpToolError";
|
|
35374
35414
|
if (opts?.hint !== void 0)
|
|
35375
35415
|
this.hint = opts.hint;
|
|
35416
|
+
if (opts?.status !== void 0)
|
|
35417
|
+
this.status = opts.status;
|
|
35418
|
+
if (opts?.kind !== void 0)
|
|
35419
|
+
this.kind = opts.kind;
|
|
35376
35420
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
35377
35421
|
}
|
|
35378
35422
|
};
|
|
35423
|
+
DECLARATION_CAUSE_DEPTH = 4;
|
|
35424
|
+
UPSTREAM_BODY_PHRASE = {
|
|
35425
|
+
"non-json": "a non-JSON response",
|
|
35426
|
+
empty: "an empty body",
|
|
35427
|
+
null: "null",
|
|
35428
|
+
array: "an array",
|
|
35429
|
+
object: "an object",
|
|
35430
|
+
string: "a string",
|
|
35431
|
+
number: "a number",
|
|
35432
|
+
boolean: "a boolean"
|
|
35433
|
+
};
|
|
35434
|
+
UpstreamFormatError = class extends McpToolError {
|
|
35435
|
+
/** What the body turned out to be. */
|
|
35436
|
+
received;
|
|
35437
|
+
/** The shape the caller required, when it required one. */
|
|
35438
|
+
expected;
|
|
35439
|
+
/** The response `Content-Type`, when known. (`status` is the base class's.) */
|
|
35440
|
+
contentType;
|
|
35441
|
+
constructor(details) {
|
|
35442
|
+
const service = details.service ?? "The upstream service";
|
|
35443
|
+
const request = details.method !== void 0 && details.path !== void 0 ? `${details.method.toUpperCase()} ${details.path}` : void 0;
|
|
35444
|
+
const meta3 = [
|
|
35445
|
+
details.status !== void 0 ? `HTTP ${details.status}` : void 0,
|
|
35446
|
+
details.contentType || void 0
|
|
35447
|
+
].filter((p) => p !== void 0);
|
|
35448
|
+
const what = details.expected !== void 0 ? `${UPSTREAM_BODY_PHRASE[details.received]} where a JSON ${details.expected} was expected` + (request ? ` for ${request}` : "") : `${UPSTREAM_BODY_PHRASE[details.received]}` + (request ? ` to ${request}` : "");
|
|
35449
|
+
const message = `${service} returned ${what}${meta3.length > 0 ? ` (${meta3.join(", ")})` : ""}.`;
|
|
35450
|
+
const hint = details.received === "non-json" ? "The service answered with something other than JSON, usually a sign-in page or interstitial, or its API has changed. Check the session, then retry." : "The service answered with a body of the wrong shape: the resource may be missing or its API may have changed.";
|
|
35451
|
+
super(truncateErrorMessage(message), {
|
|
35452
|
+
hint,
|
|
35453
|
+
cause: details.cause,
|
|
35454
|
+
...details.status !== void 0 ? { status: details.status } : {}
|
|
35455
|
+
});
|
|
35456
|
+
this.name = "UpstreamFormatError";
|
|
35457
|
+
this.received = details.received;
|
|
35458
|
+
if (details.expected !== void 0)
|
|
35459
|
+
this.expected = details.expected;
|
|
35460
|
+
if (details.contentType)
|
|
35461
|
+
this.contentType = details.contentType;
|
|
35462
|
+
}
|
|
35463
|
+
};
|
|
35379
35464
|
HEADER_NAME_RUN = "[a-z0-9_-]{0,40}";
|
|
35380
35465
|
BEARER_RE = /(bearer\s+)[A-Za-z0-9._~+/=-]{8,}/gi;
|
|
35381
35466
|
JWT_RE = /(?<![A-Za-z0-9_-])[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{8,}(?![A-Za-z0-9_-])/g;
|
|
@@ -35397,7 +35482,7 @@ var init_errors3 = __esm({
|
|
|
35397
35482
|
// webhook signing secret (Stripe-style)
|
|
35398
35483
|
].map((p) => `\\b${p}`).join("|"), "g");
|
|
35399
35484
|
GOOGLE_OAUTH_TOKEN_RE = /(?<![A-Za-z0-9+/])(?:ya29\.|1\/\/)[A-Za-z0-9._-]{8,}/g;
|
|
35400
|
-
QUERY_SECRET_RE = new RegExp(`([?&](?:(?:access|refresh|id|auth|session|csrf|xsrf)[_-]?token|client[_-]?secret|api[_-]?secret|api[_-]?key|password|passwd|signature|token|key|sig|(?:php|j)?sess(?:ion)?[_-]?id|sid|x[-_]${HEADER_NAME_RUN}?(?:api[-_]?key|token|secret|auth)${HEADER_NAME_RUN})=)[^&#\\s"'<>\`]+`, "gi");
|
|
35485
|
+
QUERY_SECRET_RE = new RegExp(`([?&](?:(?:access|refresh|id|auth|session|csrf|xsrf)[_-]?token|client[_-]?secret|api[_-]?secret|api[_-]?key|password|passwd|signature|token|key|sig|(?:code[_-]?)?verifier|(?:php|j)?sess(?:ion)?[_-]?id|sid|x[-_]${HEADER_NAME_RUN}?(?:api[-_]?key|token|secret|auth)${HEADER_NAME_RUN})=)[^&#\\s"'<>\`]+`, "gi");
|
|
35401
35486
|
OAUTH_CODE_RE = /([?&]code=)(?=[^&#\s"'<>`]{16,})[^&#\s"'<>`]+/gi;
|
|
35402
35487
|
FORM_PASSWORD_RE = /((?:^|[\s,;:"'(\[{])(?:password|passwd)=)[^&#\s"'<>`]+/gim;
|
|
35403
35488
|
HEADER_SECRET_RE = new RegExp(`(\\b(?:x[-_]${HEADER_NAME_RUN}?(?:api[-_]?key|token|secret|auth)${HEADER_NAME_RUN}|api[-_]?key)\\s*:\\s*)[^\\s,;"'<>\`]+`, "gi");
|
|
@@ -35408,6 +35493,8 @@ var init_errors3 = __esm({
|
|
|
35408
35493
|
"api[_-]?secret",
|
|
35409
35494
|
"(?:x[_-])?api[_-]?key",
|
|
35410
35495
|
"private[_-]?key",
|
|
35496
|
+
"code[_-]?verifier",
|
|
35497
|
+
// PKCE; a bare `verifier` key is too generic for JSON
|
|
35411
35498
|
`x[_-]${HEADER_NAME_RUN}?(?:api[_-]?key|token|secret|auth)${HEADER_NAME_RUN}`,
|
|
35412
35499
|
// bounded like HEADER_SECRET_RE
|
|
35413
35500
|
"(?:proxy-)?authorization",
|
|
@@ -35904,7 +35991,7 @@ function readEnvVar(key, opts = {}) {
|
|
|
35904
35991
|
if (typeof raw === "string") {
|
|
35905
35992
|
const trimmed = raw.trim();
|
|
35906
35993
|
if (trimmed.length > 0 && trimmed !== "undefined" && trimmed !== "null" && !PLACEHOLDER_RE.test(trimmed)) {
|
|
35907
|
-
return trimmed;
|
|
35994
|
+
return opts.trim === false ? raw : trimmed;
|
|
35908
35995
|
}
|
|
35909
35996
|
}
|
|
35910
35997
|
return opts.default;
|
|
@@ -36141,12 +36228,19 @@ function boundArgs(args) {
|
|
|
36141
36228
|
}
|
|
36142
36229
|
function confirmationFromEnv(options) {
|
|
36143
36230
|
const { tool, account, confirmToken, subject, instruction, args, env = process.env, spent: callerSpent, ...rest } = options;
|
|
36231
|
+
if (args === void 0 || args === null) {
|
|
36232
|
+
throw new TypeError(`confirmationFromEnv: ${tool} passed no args; pass the tool's validated arguments ({} for a tool with none).`);
|
|
36233
|
+
}
|
|
36144
36234
|
const spent = callerSpent ?? spentTokenStoreFromEnv(env);
|
|
36145
36235
|
const mode = readConfirmMode(env);
|
|
36146
36236
|
const ttlSeconds = readConfirmTtl(env);
|
|
36147
36237
|
const elicitationOff = readConfirmElicitation(env) === "off";
|
|
36148
|
-
const bound =
|
|
36149
|
-
const
|
|
36238
|
+
const bound = boundArgs(args);
|
|
36239
|
+
const elicitationBound = account === void 0 ? { args: bound } : { account, args: bound };
|
|
36240
|
+
const bindingAdded = rest.binding ? rest : {
|
|
36241
|
+
...rest,
|
|
36242
|
+
binding: { key: confirmKeyFromEnv(env), args: elicitationBound, ttlSeconds: ttlSeconds ?? DEFAULT_TTL_SECONDS2 }
|
|
36243
|
+
};
|
|
36150
36244
|
const confirmation = elicitationOff ? { ...bindingAdded, elicitation: false } : bindingAdded;
|
|
36151
36245
|
if (mode === "refuse" || ttlSeconds === void 0) {
|
|
36152
36246
|
const reason = mode === "refuse" ? REFUSE_HINT : BAD_TTL_HINT;
|
|
@@ -36156,7 +36250,7 @@ function confirmationFromEnv(options) {
|
|
|
36156
36250
|
unsupportedNote: confirmation.unsupportedNote ? `${confirmation.unsupportedNote} ${hint}` : hint
|
|
36157
36251
|
};
|
|
36158
36252
|
}
|
|
36159
|
-
const boundSubject =
|
|
36253
|
+
const boundSubject = async () => {
|
|
36160
36254
|
const read = await subject();
|
|
36161
36255
|
if ("content" in read)
|
|
36162
36256
|
return read;
|
|
@@ -36244,7 +36338,7 @@ async function confirmWrite(ctx, options) {
|
|
|
36244
36338
|
...options.unsupportedNote === void 0 ? {} : { unsupportedNote: options.unsupportedNote },
|
|
36245
36339
|
...options.confirmationLabel === void 0 ? {} : { confirmationLabel: options.confirmationLabel },
|
|
36246
36340
|
tool,
|
|
36247
|
-
|
|
36341
|
+
account,
|
|
36248
36342
|
confirmToken,
|
|
36249
36343
|
args: bound,
|
|
36250
36344
|
subject: () => ({ target, ...revision === void 0 ? {} : { revision }, payload: bound, preview }),
|
|
@@ -36272,12 +36366,14 @@ var init_merge_write = __esm({
|
|
|
36272
36366
|
|
|
36273
36367
|
// node_modules/@chrischall/mcp-utils/dist/server/index.js
|
|
36274
36368
|
function hintResultOrRethrow(err) {
|
|
36369
|
+
if (err instanceof ProtocolError && err.code === ProtocolErrorCode.UrlElicitationRequired)
|
|
36370
|
+
throw err;
|
|
36275
36371
|
if (err instanceof McpToolError && err.hint) {
|
|
36276
36372
|
return errorResult(`${err.message}
|
|
36277
36373
|
|
|
36278
36374
|
Hint: ${err.hint}`);
|
|
36279
36375
|
}
|
|
36280
|
-
|
|
36376
|
+
return errorResult(messageOf(err));
|
|
36281
36377
|
}
|
|
36282
36378
|
function mcpRequestFrom(args) {
|
|
36283
36379
|
const ctx = args.at(-1);
|
|
@@ -36496,6 +36592,7 @@ var init_write = __esm({
|
|
|
36496
36592
|
var init_output = __esm({
|
|
36497
36593
|
"node_modules/@chrischall/mcp-utils/dist/fs/output.js"() {
|
|
36498
36594
|
init_config();
|
|
36595
|
+
init_errors3();
|
|
36499
36596
|
init_confine();
|
|
36500
36597
|
init_write();
|
|
36501
36598
|
}
|
|
@@ -36576,6 +36673,7 @@ var init_scrape = __esm({
|
|
|
36576
36673
|
// node_modules/@chrischall/mcp-utils/dist/http/throttle.js
|
|
36577
36674
|
var init_throttle = __esm({
|
|
36578
36675
|
"node_modules/@chrischall/mcp-utils/dist/http/throttle.js"() {
|
|
36676
|
+
init_cancel();
|
|
36579
36677
|
}
|
|
36580
36678
|
});
|
|
36581
36679
|
|
|
@@ -36601,6 +36699,113 @@ var init_net_atoms = __esm({
|
|
|
36601
36699
|
}
|
|
36602
36700
|
});
|
|
36603
36701
|
|
|
36702
|
+
// node_modules/@chrischall/mcp-utils/dist/http/body-limit.js
|
|
36703
|
+
async function readBytesCapped(res, maxBytes, service, bounded) {
|
|
36704
|
+
if (maxBytes !== void 0) {
|
|
36705
|
+
const declared = Number(res.headers.get("content-length") ?? NaN);
|
|
36706
|
+
if (Number.isFinite(declared) && declared > maxBytes) {
|
|
36707
|
+
cancelStream(res.body);
|
|
36708
|
+
throw new ResponseTooLargeError(service, maxBytes);
|
|
36709
|
+
}
|
|
36710
|
+
}
|
|
36711
|
+
if (!res.body)
|
|
36712
|
+
return new Uint8Array(0);
|
|
36713
|
+
const reader = res.body.getReader();
|
|
36714
|
+
const chunks = [];
|
|
36715
|
+
let total = 0;
|
|
36716
|
+
try {
|
|
36717
|
+
for (; ; ) {
|
|
36718
|
+
const { done, value } = await bounded(reader.read());
|
|
36719
|
+
if (done)
|
|
36720
|
+
break;
|
|
36721
|
+
total += value.byteLength;
|
|
36722
|
+
if (maxBytes !== void 0 && total > maxBytes)
|
|
36723
|
+
throw new ResponseTooLargeError(service, maxBytes);
|
|
36724
|
+
chunks.push(value);
|
|
36725
|
+
}
|
|
36726
|
+
} catch (err) {
|
|
36727
|
+
reader.cancel().catch(() => {
|
|
36728
|
+
});
|
|
36729
|
+
throw err;
|
|
36730
|
+
}
|
|
36731
|
+
const out = new Uint8Array(total);
|
|
36732
|
+
let offset = 0;
|
|
36733
|
+
for (const c of chunks) {
|
|
36734
|
+
out.set(c, offset);
|
|
36735
|
+
offset += c.byteLength;
|
|
36736
|
+
}
|
|
36737
|
+
return out;
|
|
36738
|
+
}
|
|
36739
|
+
function cancelStream(body) {
|
|
36740
|
+
try {
|
|
36741
|
+
body?.cancel().catch(() => {
|
|
36742
|
+
});
|
|
36743
|
+
} catch {
|
|
36744
|
+
}
|
|
36745
|
+
}
|
|
36746
|
+
var ResponseTooLargeError;
|
|
36747
|
+
var init_body_limit = __esm({
|
|
36748
|
+
"node_modules/@chrischall/mcp-utils/dist/http/body-limit.js"() {
|
|
36749
|
+
init_errors3();
|
|
36750
|
+
ResponseTooLargeError = class extends McpToolError {
|
|
36751
|
+
maxBytes;
|
|
36752
|
+
constructor(service, maxBytes) {
|
|
36753
|
+
super(`Response from ${service} is larger than the ${maxBytes}-byte limit.`, {
|
|
36754
|
+
hint: "Raise maxBytes (or the client's maxResponseBytes) if a response this large is expected.",
|
|
36755
|
+
kind: "too_large"
|
|
36756
|
+
});
|
|
36757
|
+
this.name = "ResponseTooLargeError";
|
|
36758
|
+
this.maxBytes = maxBytes;
|
|
36759
|
+
}
|
|
36760
|
+
};
|
|
36761
|
+
}
|
|
36762
|
+
});
|
|
36763
|
+
|
|
36764
|
+
// node_modules/@chrischall/mcp-utils/dist/http/fetch-bounded.js
|
|
36765
|
+
var init_fetch_bounded = __esm({
|
|
36766
|
+
"node_modules/@chrischall/mcp-utils/dist/http/fetch-bounded.js"() {
|
|
36767
|
+
init_cancel();
|
|
36768
|
+
init_errors3();
|
|
36769
|
+
init_body_limit();
|
|
36770
|
+
init_http();
|
|
36771
|
+
}
|
|
36772
|
+
});
|
|
36773
|
+
|
|
36774
|
+
// node_modules/@chrischall/mcp-utils/dist/http/url-safety.js
|
|
36775
|
+
function isDotSegment(segment) {
|
|
36776
|
+
const s = segment.replace(/%2e/gi, ".");
|
|
36777
|
+
return s === "." || s === "..";
|
|
36778
|
+
}
|
|
36779
|
+
function pathPartOf(path) {
|
|
36780
|
+
const end = path.search(/[?#]/);
|
|
36781
|
+
return end === -1 ? path : path.slice(0, end);
|
|
36782
|
+
}
|
|
36783
|
+
function findPathHazard(path) {
|
|
36784
|
+
const pathPart = pathPartOf(path);
|
|
36785
|
+
if (/[\x00-\x1f\x7f]/.test(pathPart))
|
|
36786
|
+
return "control character";
|
|
36787
|
+
if (pathPart.endsWith(" "))
|
|
36788
|
+
return "trailing space";
|
|
36789
|
+
if (pathPart.includes("\\"))
|
|
36790
|
+
return "backslash";
|
|
36791
|
+
if (pathPart.split("/").some(isDotSegment))
|
|
36792
|
+
return "dot segment";
|
|
36793
|
+
return void 0;
|
|
36794
|
+
}
|
|
36795
|
+
var RedirectRefusedError;
|
|
36796
|
+
var init_url_safety = __esm({
|
|
36797
|
+
"node_modules/@chrischall/mcp-utils/dist/http/url-safety.js"() {
|
|
36798
|
+
init_errors3();
|
|
36799
|
+
init_config();
|
|
36800
|
+
RedirectRefusedError = class extends McpToolError {
|
|
36801
|
+
constructor(message) {
|
|
36802
|
+
super(message);
|
|
36803
|
+
this.name = "RedirectRefusedError";
|
|
36804
|
+
}
|
|
36805
|
+
};
|
|
36806
|
+
}
|
|
36807
|
+
});
|
|
36808
|
+
|
|
36604
36809
|
// node_modules/@chrischall/mcp-utils/dist/internal/retry-after.js
|
|
36605
36810
|
function retryAfterToMs(header, now = Date.now()) {
|
|
36606
36811
|
if (header == null)
|
|
@@ -36621,6 +36826,14 @@ var init_retry_after = __esm({
|
|
|
36621
36826
|
});
|
|
36622
36827
|
|
|
36623
36828
|
// node_modules/@chrischall/mcp-utils/dist/http/index.js
|
|
36829
|
+
function cancelBody(res) {
|
|
36830
|
+
try {
|
|
36831
|
+
const body = res.body;
|
|
36832
|
+
body?.cancel?.()?.catch(() => {
|
|
36833
|
+
});
|
|
36834
|
+
} catch {
|
|
36835
|
+
}
|
|
36836
|
+
}
|
|
36624
36837
|
function headerOf(headers, name) {
|
|
36625
36838
|
if (headers === void 0)
|
|
36626
36839
|
return void 0;
|
|
@@ -36646,6 +36859,49 @@ function detectEdgeBlock(input2) {
|
|
|
36646
36859
|
}
|
|
36647
36860
|
return null;
|
|
36648
36861
|
}
|
|
36862
|
+
function jsonKind(value) {
|
|
36863
|
+
if (value === null)
|
|
36864
|
+
return "null";
|
|
36865
|
+
if (Array.isArray(value))
|
|
36866
|
+
return "array";
|
|
36867
|
+
return typeof value;
|
|
36868
|
+
}
|
|
36869
|
+
function parseJsonBody(text, opts = {}) {
|
|
36870
|
+
const contentType = opts.contentType ?? headerOf(opts.headers, "content-type");
|
|
36871
|
+
const details = {
|
|
36872
|
+
service: opts.service,
|
|
36873
|
+
method: opts.method,
|
|
36874
|
+
path: opts.path,
|
|
36875
|
+
status: opts.status,
|
|
36876
|
+
contentType,
|
|
36877
|
+
expected: opts.expect
|
|
36878
|
+
};
|
|
36879
|
+
if (text.trim().length === 0) {
|
|
36880
|
+
if (opts.expect === void 0)
|
|
36881
|
+
return void 0;
|
|
36882
|
+
throw new UpstreamFormatError({ ...details, received: "empty" });
|
|
36883
|
+
}
|
|
36884
|
+
let value;
|
|
36885
|
+
try {
|
|
36886
|
+
value = JSON.parse(text);
|
|
36887
|
+
} catch (cause) {
|
|
36888
|
+
const edge = detectEdgeBlock({ body: text, headers: opts.headers, status: opts.status });
|
|
36889
|
+
if (edge) {
|
|
36890
|
+
throw new EdgeBlockedError(opts.status ?? 200, edge.vendor, {
|
|
36891
|
+
service: opts.service ?? "the upstream service",
|
|
36892
|
+
method: opts.method,
|
|
36893
|
+
path: opts.path
|
|
36894
|
+
});
|
|
36895
|
+
}
|
|
36896
|
+
throw new UpstreamFormatError({ ...details, expected: void 0, received: "non-json", cause });
|
|
36897
|
+
}
|
|
36898
|
+
if (opts.expect !== void 0) {
|
|
36899
|
+
const kind = jsonKind(value);
|
|
36900
|
+
if (kind !== opts.expect)
|
|
36901
|
+
throw new UpstreamFormatError({ ...details, received: kind });
|
|
36902
|
+
}
|
|
36903
|
+
return value;
|
|
36904
|
+
}
|
|
36649
36905
|
function httpError(res, text, method, path, service) {
|
|
36650
36906
|
const edge = detectEdgeBlock({ body: text, headers: res.headers, status: res.status });
|
|
36651
36907
|
if (edge)
|
|
@@ -36696,13 +36952,26 @@ function createApiClient(opts) {
|
|
|
36696
36952
|
const sleep3 = opts.sleep ?? defaultSleep;
|
|
36697
36953
|
const unauthorized = () => opts.onUnauthorized ? opts.onUnauthorized() : new UnauthorizedError(service);
|
|
36698
36954
|
const retryStatuses = retry.statuses ?? [429];
|
|
36699
|
-
const timeoutMs = opts.timeout;
|
|
36955
|
+
const timeoutMs = opts.timeout === false ? 0 : opts.timeout ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
36956
|
+
const redirectMode = opts.redirect;
|
|
36957
|
+
const maxRedirects = opts.maxRedirects ?? DEFAULT_MAX_REDIRECTS;
|
|
36958
|
+
const reportWriteOutcome = opts.writeOutcomeUnknown !== false;
|
|
36959
|
+
const defaultMaxBytes = checkMaxBytes("maxResponseBytes", opts.maxResponseBytes);
|
|
36960
|
+
if (!Number.isSafeInteger(maxRedirects) || maxRedirects < 0) {
|
|
36961
|
+
throw new TypeError(`createApiClient: maxRedirects must be a non-negative integer, got ${String(maxRedirects)}.`);
|
|
36962
|
+
}
|
|
36700
36963
|
const authHeader = (token) => !token ? {} : opts.tokenHeader ? { [opts.tokenHeader]: token } : { Authorization: `Bearer ${token}` };
|
|
36964
|
+
const transportFailures = /* @__PURE__ */ new WeakSet();
|
|
36965
|
+
const markTransportFailure = (err) => {
|
|
36966
|
+
if (typeof err === "object" && err !== null && err.name !== "AbortError") {
|
|
36967
|
+
transportFailures.add(err);
|
|
36968
|
+
}
|
|
36969
|
+
};
|
|
36701
36970
|
async function withTimeout(run) {
|
|
36702
|
-
if (timeoutMs
|
|
36971
|
+
if (timeoutMs <= 0) {
|
|
36703
36972
|
const res2 = await run(withAmbientCancellation(void 0));
|
|
36704
36973
|
return { res: res2, done: () => {
|
|
36705
|
-
}, expired: void 0 };
|
|
36974
|
+
}, expired: void 0, timedOut: () => false };
|
|
36706
36975
|
}
|
|
36707
36976
|
const controller = new AbortController();
|
|
36708
36977
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -36723,16 +36992,11 @@ function createApiClient(opts) {
|
|
|
36723
36992
|
});
|
|
36724
36993
|
expired.catch(() => {
|
|
36725
36994
|
});
|
|
36726
|
-
return { res, done: () => clearTimeout(timer), expired };
|
|
36995
|
+
return { res, done: () => clearTimeout(timer), expired, timedOut: () => controller.signal.aborted };
|
|
36727
36996
|
}
|
|
36728
36997
|
function discard(attempt) {
|
|
36729
36998
|
attempt.done();
|
|
36730
|
-
|
|
36731
|
-
const body = attempt.res.body;
|
|
36732
|
-
body?.cancel?.()?.catch(() => {
|
|
36733
|
-
});
|
|
36734
|
-
} catch {
|
|
36735
|
-
}
|
|
36999
|
+
cancelBody(attempt.res);
|
|
36736
37000
|
}
|
|
36737
37001
|
async function readBody(attempt, read) {
|
|
36738
37002
|
try {
|
|
@@ -36740,15 +37004,40 @@ function createApiClient(opts) {
|
|
|
36740
37004
|
return await read(attempt.res);
|
|
36741
37005
|
return await Promise.race([read(attempt.res), attempt.expired]);
|
|
36742
37006
|
} catch (err) {
|
|
36743
|
-
|
|
36744
|
-
|
|
36745
|
-
|
|
37007
|
+
const timeout = err instanceof RequestTimeoutError || err instanceof Error && err.name === "AbortError" && attempt.timedOut();
|
|
37008
|
+
if (timeout) {
|
|
37009
|
+
cancelBody(attempt.res);
|
|
37010
|
+
throw err instanceof RequestTimeoutError ? err : new RequestTimeoutError(service, timeoutMs);
|
|
36746
37011
|
}
|
|
37012
|
+
if (!(err instanceof ResponseTooLargeError))
|
|
37013
|
+
markTransportFailure(err);
|
|
36747
37014
|
throw err;
|
|
36748
37015
|
} finally {
|
|
36749
37016
|
attempt.done();
|
|
36750
37017
|
}
|
|
36751
37018
|
}
|
|
37019
|
+
function readBytes(attempt, maxBytes) {
|
|
37020
|
+
if (maxBytes === void 0)
|
|
37021
|
+
return readBody(attempt, async (r) => new Uint8Array(await r.arrayBuffer()));
|
|
37022
|
+
const race = (p) => attempt.expired ? Promise.race([p, attempt.expired]) : p;
|
|
37023
|
+
return readBody(attempt, (r) => readBytesCapped(r, maxBytes, service, race));
|
|
37024
|
+
}
|
|
37025
|
+
async function readText(attempt, maxBytes) {
|
|
37026
|
+
if (maxBytes === void 0)
|
|
37027
|
+
return readBody(attempt, (r) => r.text());
|
|
37028
|
+
return new TextDecoder().decode(await readBytes(attempt, maxBytes));
|
|
37029
|
+
}
|
|
37030
|
+
function readErrorText(attempt, maxBytes) {
|
|
37031
|
+
return readText(attempt, maxBytes).catch((err) => {
|
|
37032
|
+
if (err instanceof ResponseTooLargeError)
|
|
37033
|
+
return "";
|
|
37034
|
+
throw err;
|
|
37035
|
+
});
|
|
37036
|
+
}
|
|
37037
|
+
function scanText(attempt, maxBytes) {
|
|
37038
|
+
return readText(attempt, maxBytes).catch(() => "");
|
|
37039
|
+
}
|
|
37040
|
+
const capOf = (opt) => opt.maxBytes !== void 0 ? checkMaxBytes("maxBytes", opt.maxBytes) : defaultMaxBytes;
|
|
36752
37041
|
const baseParts = (() => {
|
|
36753
37042
|
try {
|
|
36754
37043
|
const u = new URL(base);
|
|
@@ -36759,6 +37048,14 @@ function createApiClient(opts) {
|
|
|
36759
37048
|
})();
|
|
36760
37049
|
const baseOrigin = baseParts?.origin;
|
|
36761
37050
|
function resolveUrl(path, query) {
|
|
37051
|
+
const hazard = findPathHazard(path);
|
|
37052
|
+
if (hazard !== void 0) {
|
|
37053
|
+
throw new Error(
|
|
37054
|
+
// Quote only the path part: this fires on hostile input, and the
|
|
37055
|
+
// query may carry values that do not belong in a log line.
|
|
37056
|
+
`Refusing request to ${service}: path ${JSON.stringify(path.split(/[?#]/, 1)[0])} contains a ${hazard}, which URL normalisation would rewrite. Build paths with apiPath\`...\` so each value is one encoded segment.`
|
|
37057
|
+
);
|
|
37058
|
+
}
|
|
36762
37059
|
const url2 = `${base}${path}${query}`;
|
|
36763
37060
|
if (baseOrigin === void 0)
|
|
36764
37061
|
return url2;
|
|
@@ -36773,23 +37070,66 @@ function createApiClient(opts) {
|
|
|
36773
37070
|
}
|
|
36774
37071
|
return url2;
|
|
36775
37072
|
}
|
|
36776
|
-
|
|
37073
|
+
function sameOriginHop(location, from) {
|
|
37074
|
+
let next;
|
|
37075
|
+
try {
|
|
37076
|
+
next = new URL(location, from);
|
|
37077
|
+
} catch {
|
|
37078
|
+
throw new RedirectRefusedError(`Refusing redirect from ${service}: the Location header is not a valid URL.`);
|
|
37079
|
+
}
|
|
37080
|
+
if (baseOrigin === void 0 || next.origin !== baseOrigin || next.username !== baseParts?.username || next.password !== baseParts?.password) {
|
|
37081
|
+
throw new RedirectRefusedError(`Refusing redirect from ${service} to ${next.origin}: it leaves the base origin ${baseOrigin ?? "(unparseable)"}, and following it would re-send the credential there.`);
|
|
37082
|
+
}
|
|
37083
|
+
return next.href;
|
|
37084
|
+
}
|
|
37085
|
+
async function send(method, path, opt, dispatch) {
|
|
36777
37086
|
const { body: reqBody, contentType } = encodeBody(opt);
|
|
36778
37087
|
const query = opt.query ? buildQueryString(opt.query) : "";
|
|
36779
37088
|
const url2 = resolveUrl(path, query);
|
|
36780
37089
|
const bodyInit = reqBody !== void 0 ? { body: reqBody } : {};
|
|
36781
|
-
const
|
|
36782
|
-
|
|
36783
|
-
|
|
36784
|
-
|
|
36785
|
-
|
|
36786
|
-
|
|
36787
|
-
|
|
36788
|
-
|
|
36789
|
-
|
|
36790
|
-
|
|
36791
|
-
|
|
36792
|
-
|
|
37090
|
+
const fetchHop = (hopUrl, hopMethod, withBody, token, signal) => {
|
|
37091
|
+
if (!signal?.aborted)
|
|
37092
|
+
dispatch.sent = true;
|
|
37093
|
+
return doFetch(hopUrl, {
|
|
37094
|
+
method: hopMethod,
|
|
37095
|
+
headers: {
|
|
37096
|
+
Accept: "application/json",
|
|
37097
|
+
...withBody && contentType !== void 0 ? { "Content-Type": contentType } : {},
|
|
37098
|
+
...opts.baseHeaders,
|
|
37099
|
+
...authHeader(token || void 0),
|
|
37100
|
+
...opt.headers
|
|
37101
|
+
},
|
|
37102
|
+
...signal ? { signal } : {},
|
|
37103
|
+
...redirectMode !== void 0 ? { redirect: redirectMode === "same-origin" ? "manual" : redirectMode } : {},
|
|
37104
|
+
...withBody ? bodyInit : {}
|
|
37105
|
+
}).catch((err) => {
|
|
37106
|
+
markTransportFailure(err);
|
|
37107
|
+
throw err;
|
|
37108
|
+
});
|
|
37109
|
+
};
|
|
37110
|
+
const fetchWith = async (token, signal) => {
|
|
37111
|
+
if (redirectMode !== "same-origin")
|
|
37112
|
+
return fetchHop(url2, method, true, token, signal);
|
|
37113
|
+
let hopUrl = url2;
|
|
37114
|
+
let hopMethod = method;
|
|
37115
|
+
let withBody = true;
|
|
37116
|
+
for (let hops = 0; ; hops += 1) {
|
|
37117
|
+
const res = await fetchHop(hopUrl, hopMethod, withBody, token, signal);
|
|
37118
|
+
const location = REDIRECT_STATUSES.has(res.status) ? responseHeader(res, "location") : void 0;
|
|
37119
|
+
if (location === void 0 || location === null)
|
|
37120
|
+
return res;
|
|
37121
|
+
cancelBody(res);
|
|
37122
|
+
if (hops >= maxRedirects) {
|
|
37123
|
+
throw new RedirectRefusedError(`Refusing request to ${service}: too many redirects (more than ${maxRedirects}).`);
|
|
37124
|
+
}
|
|
37125
|
+
hopUrl = sameOriginHop(location, hopUrl);
|
|
37126
|
+
if (res.status === 303 || (res.status === 301 || res.status === 302) && hopMethod === "POST") {
|
|
37127
|
+
if (hopMethod !== "HEAD")
|
|
37128
|
+
hopMethod = "GET";
|
|
37129
|
+
withBody = false;
|
|
37130
|
+
}
|
|
37131
|
+
}
|
|
37132
|
+
};
|
|
36793
37133
|
const once = () => withTimeout((signal) => opts.tokenManager ? opts.tokenManager.withAuth((token) => fetchWith(token, signal)) : (async () => fetchWith(await opts.getToken?.() || void 0, signal))());
|
|
36794
37134
|
let attempt = 0;
|
|
36795
37135
|
for (; ; ) {
|
|
@@ -36808,20 +37148,20 @@ function createApiClient(opts) {
|
|
|
36808
37148
|
return current;
|
|
36809
37149
|
}
|
|
36810
37150
|
}
|
|
36811
|
-
async function unauthorizedOrEdge(attempt, method, path) {
|
|
37151
|
+
async function unauthorizedOrEdge(attempt, method, path, maxBytes) {
|
|
36812
37152
|
const res = attempt.res;
|
|
36813
37153
|
let text = "";
|
|
36814
37154
|
if (/json/i.test(responseHeader(res, "content-type") ?? "")) {
|
|
36815
37155
|
discard(attempt);
|
|
36816
37156
|
} else {
|
|
36817
|
-
text = await
|
|
37157
|
+
text = await scanText(attempt, maxBytes);
|
|
36818
37158
|
}
|
|
36819
37159
|
const edge = detectEdgeBlock({ body: text, headers: res.headers, status: res.status });
|
|
36820
37160
|
if (edge)
|
|
36821
37161
|
return new EdgeBlockedError(res.status, edge.vendor, { service, method, path });
|
|
36822
37162
|
return unauthorized();
|
|
36823
37163
|
}
|
|
36824
|
-
async function rateLimitedError(attempt, method, path) {
|
|
37164
|
+
async function rateLimitedError(attempt, method, path, maxBytes) {
|
|
36825
37165
|
const res = attempt.res;
|
|
36826
37166
|
const retryAfter = responseHeader(res, "retry-after");
|
|
36827
37167
|
const retryAfterMs = retryAfterToMs(retryAfter);
|
|
@@ -36833,61 +37173,92 @@ function createApiClient(opts) {
|
|
|
36833
37173
|
if (/json/i.test(responseHeader(res, "content-type") ?? "")) {
|
|
36834
37174
|
discard(attempt);
|
|
36835
37175
|
} else {
|
|
36836
|
-
text = await
|
|
37176
|
+
text = await scanText(attempt, maxBytes);
|
|
36837
37177
|
}
|
|
36838
37178
|
const edgeBlock = detectEdgeBlock({ body: text, headers: res.headers, status: res.status });
|
|
36839
37179
|
return opts.onRateLimited({ status: res.status, retryAfter, retryAfterMs, edgeBlock, method, path });
|
|
36840
37180
|
}
|
|
36841
|
-
async function
|
|
36842
|
-
const
|
|
37181
|
+
async function guardWrite(method, opt, call) {
|
|
37182
|
+
const dispatch = { sent: false };
|
|
37183
|
+
try {
|
|
37184
|
+
return await call(dispatch);
|
|
37185
|
+
} catch (err) {
|
|
37186
|
+
const write = reportWriteOutcome && opt.idempotent !== true && !SAFE_METHODS.has(method.toUpperCase());
|
|
37187
|
+
if (!write || !dispatch.sent)
|
|
37188
|
+
throw err;
|
|
37189
|
+
if (err instanceof RequestTimeoutError) {
|
|
37190
|
+
throw new WriteOutcomeUnknownError(service, method, {
|
|
37191
|
+
timeoutMs: err.timeoutMs,
|
|
37192
|
+
cause: err,
|
|
37193
|
+
hint: opts.writeOutcomeHint
|
|
37194
|
+
});
|
|
37195
|
+
}
|
|
37196
|
+
if (typeof err === "object" && err !== null && transportFailures.has(err)) {
|
|
37197
|
+
throw new WriteOutcomeUnknownError(service, method, { cause: err, hint: opts.writeOutcomeHint });
|
|
37198
|
+
}
|
|
37199
|
+
throw err;
|
|
37200
|
+
}
|
|
37201
|
+
}
|
|
37202
|
+
const fetchJson = (method, path, opt = {}) => guardWrite(method, opt, (dispatch) => fetchJsonOnce(method, path, opt, dispatch));
|
|
37203
|
+
const fetchHtml = (method, path, opt = {}) => guardWrite(method, opt, (dispatch) => fetchHtmlOnce(method, path, opt, dispatch));
|
|
37204
|
+
const fetchRaw = (method, path, opt = {}) => guardWrite(method, opt, (dispatch) => fetchRawOnce(method, path, opt, dispatch));
|
|
37205
|
+
async function fetchJsonOnce(method, path, opt, dispatch) {
|
|
37206
|
+
const maxBytes = capOf(opt);
|
|
37207
|
+
const attempt = await send(method, path, opt, dispatch);
|
|
36843
37208
|
const res = attempt.res;
|
|
36844
37209
|
if (res.status === 401)
|
|
36845
|
-
throw await unauthorizedOrEdge(attempt, method, path);
|
|
37210
|
+
throw await unauthorizedOrEdge(attempt, method, path, maxBytes);
|
|
36846
37211
|
if (res.status === 429)
|
|
36847
|
-
throw await rateLimitedError(attempt, method, path);
|
|
37212
|
+
throw await rateLimitedError(attempt, method, path, maxBytes);
|
|
36848
37213
|
if (res.status === 204) {
|
|
36849
37214
|
discard(attempt);
|
|
36850
|
-
return
|
|
37215
|
+
return parseJsonBody("", { expect: opt.expect, service, method, path, status: 204 });
|
|
36851
37216
|
}
|
|
36852
|
-
const text = await readBody(attempt, (r) => r.text());
|
|
36853
37217
|
if (!res.ok) {
|
|
36854
|
-
throw httpError(res,
|
|
37218
|
+
throw httpError(res, await readErrorText(attempt, maxBytes), method, path, service);
|
|
36855
37219
|
}
|
|
36856
|
-
|
|
36857
|
-
|
|
36858
|
-
|
|
37220
|
+
const text = await readText(attempt, maxBytes);
|
|
37221
|
+
return parseJsonBody(text, {
|
|
37222
|
+
expect: opt.expect,
|
|
37223
|
+
service,
|
|
37224
|
+
method,
|
|
37225
|
+
path,
|
|
37226
|
+
status: res.status,
|
|
37227
|
+
headers: res.headers
|
|
37228
|
+
});
|
|
36859
37229
|
}
|
|
36860
|
-
async function
|
|
37230
|
+
async function fetchHtmlOnce(method, path, opt, dispatch) {
|
|
37231
|
+
const maxBytes = capOf(opt);
|
|
36861
37232
|
const headers = { Accept: "text/html,*/*", ...opt.headers };
|
|
36862
|
-
const attempt = await send(method, path, { ...opt, headers });
|
|
37233
|
+
const attempt = await send(method, path, { ...opt, headers }, dispatch);
|
|
36863
37234
|
const res = attempt.res;
|
|
36864
37235
|
if (res.status === 401)
|
|
36865
|
-
throw await unauthorizedOrEdge(attempt, method, path);
|
|
37236
|
+
throw await unauthorizedOrEdge(attempt, method, path, maxBytes);
|
|
36866
37237
|
if (res.status === 429)
|
|
36867
|
-
throw await rateLimitedError(attempt, method, path);
|
|
36868
|
-
const text = await readBody(attempt, (r) => r.text());
|
|
37238
|
+
throw await rateLimitedError(attempt, method, path, maxBytes);
|
|
36869
37239
|
if (!res.ok) {
|
|
36870
|
-
throw httpError(res,
|
|
37240
|
+
throw httpError(res, await readErrorText(attempt, maxBytes), method, path, service);
|
|
36871
37241
|
}
|
|
36872
|
-
return
|
|
37242
|
+
return readText(attempt, maxBytes);
|
|
36873
37243
|
}
|
|
36874
|
-
async function
|
|
37244
|
+
async function fetchRawOnce(method, path, opt, dispatch) {
|
|
37245
|
+
const maxBytes = capOf(opt);
|
|
36875
37246
|
const headers = { Accept: "*/*", ...opt.headers };
|
|
36876
|
-
const attempt = await send(method, path, { ...opt, headers });
|
|
37247
|
+
const attempt = await send(method, path, { ...opt, headers }, dispatch);
|
|
36877
37248
|
const res = attempt.res;
|
|
36878
37249
|
if (res.status === 401)
|
|
36879
|
-
throw await unauthorizedOrEdge(attempt, method, path);
|
|
37250
|
+
throw await unauthorizedOrEdge(attempt, method, path, maxBytes);
|
|
36880
37251
|
if (res.status === 429)
|
|
36881
|
-
throw await rateLimitedError(attempt, method, path);
|
|
37252
|
+
throw await rateLimitedError(attempt, method, path, maxBytes);
|
|
36882
37253
|
if (!res.ok) {
|
|
36883
|
-
const text = await
|
|
37254
|
+
const text = await readText(attempt, maxBytes).catch((err) => {
|
|
36884
37255
|
if (err instanceof RequestTimeoutError)
|
|
36885
37256
|
throw err;
|
|
36886
37257
|
return "";
|
|
36887
37258
|
});
|
|
36888
37259
|
throw httpError(res, text, method, path, service);
|
|
36889
37260
|
}
|
|
36890
|
-
const bytes =
|
|
37261
|
+
const bytes = await readBytes(attempt, maxBytes);
|
|
36891
37262
|
return {
|
|
36892
37263
|
status: res.status,
|
|
36893
37264
|
contentType: responseHeader(res, "content-type"),
|
|
@@ -36897,6 +37268,14 @@ function createApiClient(opts) {
|
|
|
36897
37268
|
}
|
|
36898
37269
|
return { fetchJson, fetchHtml, fetchRaw };
|
|
36899
37270
|
}
|
|
37271
|
+
function checkMaxBytes(name, value) {
|
|
37272
|
+
if (value === void 0)
|
|
37273
|
+
return void 0;
|
|
37274
|
+
if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
|
|
37275
|
+
throw new TypeError(`createApiClient: ${name} must be a non-negative number, got ${String(value)}.`);
|
|
37276
|
+
}
|
|
37277
|
+
return value === Infinity ? void 0 : value;
|
|
37278
|
+
}
|
|
36900
37279
|
function buildQueryString(params) {
|
|
36901
37280
|
const parts = [];
|
|
36902
37281
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -36947,7 +37326,7 @@ function decodeJwtExp(token) {
|
|
|
36947
37326
|
}
|
|
36948
37327
|
return exp;
|
|
36949
37328
|
}
|
|
36950
|
-
var DEFAULT_RETRY, defaultSleep, UnauthorizedError, RateLimitedError, RequestTimeoutError, ApiError, EdgeBlockedError, EDGE_BLOCK_SCAN_MAX, EDGE_BLOCK_MARKERS;
|
|
37329
|
+
var DEFAULT_RETRY, defaultSleep, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_MAX_REDIRECTS, REDIRECT_STATUSES, UnauthorizedError, RateLimitedError, RequestTimeoutError, DEFAULT_WRITE_OUTCOME_HINT, WriteOutcomeUnknownError, SAFE_METHODS, ApiError, EdgeBlockedError, EDGE_BLOCK_SCAN_MAX, EDGE_BLOCK_MARKERS;
|
|
36951
37330
|
var init_http = __esm({
|
|
36952
37331
|
"node_modules/@chrischall/mcp-utils/dist/http/index.js"() {
|
|
36953
37332
|
init_headers();
|
|
@@ -36957,12 +37336,21 @@ var init_http = __esm({
|
|
|
36957
37336
|
init_throttle();
|
|
36958
37337
|
init_response_cache();
|
|
36959
37338
|
init_net_atoms();
|
|
37339
|
+
init_fetch_bounded();
|
|
37340
|
+
init_url_safety();
|
|
37341
|
+
init_body_limit();
|
|
36960
37342
|
init_net_atoms();
|
|
37343
|
+
init_url_safety();
|
|
36961
37344
|
init_retry_after();
|
|
36962
37345
|
DEFAULT_RETRY = { count: 1, delayMs: 2e3 };
|
|
36963
37346
|
defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
37347
|
+
DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
37348
|
+
DEFAULT_MAX_REDIRECTS = 5;
|
|
37349
|
+
REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
|
|
36964
37350
|
UnauthorizedError = class extends Error {
|
|
36965
37351
|
status = 401;
|
|
37352
|
+
/** Always `'credential_rejected'` unless a subclass says otherwise. */
|
|
37353
|
+
kind = "credential_rejected";
|
|
36966
37354
|
constructor(service) {
|
|
36967
37355
|
super(`Unauthorized (401) from ${service} \u2014 the token is missing, invalid, or expired.`);
|
|
36968
37356
|
this.name = "UnauthorizedError";
|
|
@@ -36971,6 +37359,8 @@ var init_http = __esm({
|
|
|
36971
37359
|
};
|
|
36972
37360
|
RateLimitedError = class extends Error {
|
|
36973
37361
|
status = 429;
|
|
37362
|
+
/** `'http'`: the far side answered, with a 429 (see `status`). */
|
|
37363
|
+
kind = "http";
|
|
36974
37364
|
retryAfterMs;
|
|
36975
37365
|
constructor(service, retryAfterMs) {
|
|
36976
37366
|
super(`Rate limited (429) by ${service} after retries.`);
|
|
@@ -36981,6 +37371,8 @@ var init_http = __esm({
|
|
|
36981
37371
|
};
|
|
36982
37372
|
RequestTimeoutError = class extends Error {
|
|
36983
37373
|
timeoutMs;
|
|
37374
|
+
/** Always `'timeout'` unless a subclass says otherwise. */
|
|
37375
|
+
kind = "timeout";
|
|
36984
37376
|
constructor(service, timeoutMs) {
|
|
36985
37377
|
super(`Request to ${service} timed out after ${timeoutMs}ms.`);
|
|
36986
37378
|
this.name = "RequestTimeoutError";
|
|
@@ -36988,6 +37380,33 @@ var init_http = __esm({
|
|
|
36988
37380
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
36989
37381
|
}
|
|
36990
37382
|
};
|
|
37383
|
+
DEFAULT_WRITE_OUTCOME_HINT = "The write may have happened \u2014 check before retrying; do not resend blindly.";
|
|
37384
|
+
WriteOutcomeUnknownError = class extends McpToolError {
|
|
37385
|
+
/** Always `true`: whether the write happened is not known. */
|
|
37386
|
+
outcomeUnknown = true;
|
|
37387
|
+
/** Always `false`: never replay this request without checking first. */
|
|
37388
|
+
retrySafe = false;
|
|
37389
|
+
/** `true` when the request's timeout expired; `false` for a dropped connection or broken body. */
|
|
37390
|
+
timedOut;
|
|
37391
|
+
/** The timeout that expired, when {@link timedOut}. */
|
|
37392
|
+
timeoutMs;
|
|
37393
|
+
/** The request method, upper-cased. */
|
|
37394
|
+
method;
|
|
37395
|
+
constructor(service, method, opts = {}) {
|
|
37396
|
+
const verb = method.toUpperCase();
|
|
37397
|
+
const what = opts.timeoutMs !== void 0 ? `${verb} to ${service} timed out after ${opts.timeoutMs}ms` : `${verb} to ${service} failed: ${truncateErrorMessage(messageOf(opts.cause), 200)}`;
|
|
37398
|
+
super(`${what} \u2014 the write may already have been applied (outcome is unknown).`, {
|
|
37399
|
+
hint: opts.hint ?? DEFAULT_WRITE_OUTCOME_HINT,
|
|
37400
|
+
cause: opts.cause,
|
|
37401
|
+
kind: opts.timeoutMs !== void 0 ? "timeout" : "transport"
|
|
37402
|
+
});
|
|
37403
|
+
this.name = "WriteOutcomeUnknownError";
|
|
37404
|
+
this.timedOut = opts.timeoutMs !== void 0;
|
|
37405
|
+
this.timeoutMs = opts.timeoutMs;
|
|
37406
|
+
this.method = verb;
|
|
37407
|
+
}
|
|
37408
|
+
};
|
|
37409
|
+
SAFE_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS", "TRACE"]);
|
|
36991
37410
|
ApiError = class extends Error {
|
|
36992
37411
|
status;
|
|
36993
37412
|
constructor(status, message) {
|
|
@@ -37000,6 +37419,8 @@ var init_http = __esm({
|
|
|
37000
37419
|
EdgeBlockedError = class extends ApiError {
|
|
37001
37420
|
/** Which edge refused the request, e.g. `'CloudFront'` or `'Cloudflare'`. */
|
|
37002
37421
|
vendor;
|
|
37422
|
+
/** Always `'edge_blocked'`: the credential was never judged. */
|
|
37423
|
+
kind = "edge_blocked";
|
|
37003
37424
|
/**
|
|
37004
37425
|
* @param where The service the request was for, and — when the thrower knows
|
|
37005
37426
|
* it — the method and path, which the message names.
|
|
@@ -37063,6 +37484,7 @@ var init_concurrency = __esm({
|
|
|
37063
37484
|
// node_modules/@chrischall/mcp-utils/dist/dates/index.js
|
|
37064
37485
|
var init_dates = __esm({
|
|
37065
37486
|
"node_modules/@chrischall/mcp-utils/dist/dates/index.js"() {
|
|
37487
|
+
init_config();
|
|
37066
37488
|
}
|
|
37067
37489
|
});
|
|
37068
37490
|
|
|
@@ -37083,7 +37505,7 @@ var init_zod2 = __esm({
|
|
|
37083
37505
|
NonNegInt = external_exports.number().int().nonnegative();
|
|
37084
37506
|
NonEmptyString = external_exports.string().min(1);
|
|
37085
37507
|
IsoDate = external_exports.iso.date();
|
|
37086
|
-
IsoTime = external_exports.string().regex(/^([01]?\d|2[0-3]):[0-5]\d$/, "must be HH:MM (24h), e.g. 19:30");
|
|
37508
|
+
IsoTime = external_exports.string().regex(/^([01]?\d|2[0-3]):[0-5]\d$/, "must be HH:MM (24h), e.g. 19:30").overwrite((v) => v.padStart(5, "0"));
|
|
37087
37509
|
NumericIdString = external_exports.string().regex(/^\d+$/, "must be a numeric id (digits only)").describe("A numeric id string (digits only), safe to interpolate into a URL path.");
|
|
37088
37510
|
SafePathSegment = external_exports.string().min(1).regex(/^[^/?#\s]+$/, 'must not contain "/", "?", "#", or whitespace').refine((v) => !v.includes(".."), 'must not contain ".."').describe('A single URL path segment, safe to interpolate: no "/", "..", "?", "#", or whitespace.');
|
|
37089
37511
|
schemaOrigin = external_exports.string().optional().describe("Portal origin (e.g. https://<vendor>.example.co) selecting which active session to use. Optional when only one session is active.");
|
|
@@ -37118,6 +37540,7 @@ var init_auth = __esm({
|
|
|
37118
37540
|
init_config();
|
|
37119
37541
|
init_errors3();
|
|
37120
37542
|
init_http();
|
|
37543
|
+
init_cancel();
|
|
37121
37544
|
init_cached_token();
|
|
37122
37545
|
init_es256();
|
|
37123
37546
|
}
|
|
@@ -37148,7 +37571,7 @@ var init_version = __esm({
|
|
|
37148
37571
|
"src/version.ts"() {
|
|
37149
37572
|
"use strict";
|
|
37150
37573
|
PACKAGE_NAME = "office-outlook-mcp";
|
|
37151
|
-
VERSION = "0.4.
|
|
37574
|
+
VERSION = "0.4.1";
|
|
37152
37575
|
}
|
|
37153
37576
|
});
|
|
37154
37577
|
|
|
@@ -44875,7 +45298,7 @@ function assertUrlInDomains(field, url2, domains) {
|
|
|
44875
45298
|
const declared = domains.map((d) => JSON.stringify(d)).join(", ");
|
|
44876
45299
|
throw new Error(`FetchproxyServer: ${field} host "${host}" is outside declared domains [${declared}] \u2014 must be one of them or a subdomain`);
|
|
44877
45300
|
}
|
|
44878
|
-
function
|
|
45301
|
+
function parseJsonBody2(response) {
|
|
44879
45302
|
if (response.status === 204 || response.body.trim() === "")
|
|
44880
45303
|
return null;
|
|
44881
45304
|
return JSON.parse(response.body);
|
|
@@ -45944,7 +46367,7 @@ var init_ws_server = __esm({
|
|
|
45944
46367
|
*/
|
|
45945
46368
|
async getJson(path, opts = {}) {
|
|
45946
46369
|
const response = await this.get(path, this.applyJsonDefaults(opts));
|
|
45947
|
-
return
|
|
46370
|
+
return parseJsonBody2(response);
|
|
45948
46371
|
}
|
|
45949
46372
|
/**
|
|
45950
46373
|
* POST a JSON body and parse the response body as JSON. The body is
|
|
@@ -45963,7 +46386,7 @@ var init_ws_server = __esm({
|
|
|
45963
46386
|
headers,
|
|
45964
46387
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
45965
46388
|
});
|
|
45966
|
-
return
|
|
46389
|
+
return parseJsonBody2(response);
|
|
45967
46390
|
}
|
|
45968
46391
|
/**
|
|
45969
46392
|
* GET a path and return the response body as a string. Throws
|
|
@@ -46028,7 +46451,7 @@ var init_ws_server = __esm({
|
|
|
46028
46451
|
};
|
|
46029
46452
|
let data;
|
|
46030
46453
|
try {
|
|
46031
|
-
data =
|
|
46454
|
+
data = parseJsonBody2(response);
|
|
46032
46455
|
} catch (e) {
|
|
46033
46456
|
throw new Error(`fetchproxy ${method} ${path} \u2014 response was not JSON: ${e instanceof Error ? e.message : String(e)}`);
|
|
46034
46457
|
}
|
|
@@ -47164,15 +47587,12 @@ var init_dist4 = __esm({
|
|
|
47164
47587
|
});
|
|
47165
47588
|
|
|
47166
47589
|
// node_modules/@chrischall/mcp-utils/dist/healthcheck/index.js
|
|
47167
|
-
function statusOf2(err) {
|
|
47168
|
-
if (typeof err !== "object" || err === null)
|
|
47169
|
-
return void 0;
|
|
47170
|
-
const s = err.status ?? err.statusCode;
|
|
47171
|
-
return typeof s === "number" ? s : void 0;
|
|
47172
|
-
}
|
|
47173
47590
|
function isArm(kind) {
|
|
47174
47591
|
return kind !== void 0 && CREDENTIAL_ARMS.has(kind);
|
|
47175
47592
|
}
|
|
47593
|
+
function fromSource(source) {
|
|
47594
|
+
return source !== null ? ` from '${source}'` : "";
|
|
47595
|
+
}
|
|
47176
47596
|
function credentialHint(arm, prefix, hostLabel, source) {
|
|
47177
47597
|
switch (arm) {
|
|
47178
47598
|
case "ok":
|
|
@@ -47180,15 +47600,15 @@ function credentialHint(arm, prefix, hostLabel, source) {
|
|
|
47180
47600
|
case "no_credential":
|
|
47181
47601
|
return `No credential resolved. Nothing was available to authenticate with \u2014 sign in and reconnect the connector so ${prefix} receives a token, or set the documented environment variable.`;
|
|
47182
47602
|
case "credential_rejected":
|
|
47183
|
-
return `${hostLabel} rejected the credential
|
|
47603
|
+
return `${hostLabel} rejected the credential${fromSource(source)}. It is present but no longer valid \u2014 most often expired or revoked upstream. Re-authenticate and reconnect; retrying will not fix it.`;
|
|
47184
47604
|
case "edge_blocked":
|
|
47185
47605
|
return `${hostLabel} refused the request at its CDN/WAF before it reached the API, so the credential${source !== null ? ` from '${source}'` : ""} was never judged \u2014 re-signing in or rotating it will not help. This is usually a block on this host's IP address or request fingerprint: route through the ContextMint Bridge if this connector supports it, or retry later from a different network.`;
|
|
47186
47606
|
case "session_expired":
|
|
47187
|
-
return `The credential
|
|
47607
|
+
return `The credential${fromSource(source)} is configured, but no session is live \u2014 ${hostLabel} served a sign-in page rather than the data. Sign in again; a cookie-session portal expires these on its own, so this recurs between uses.`;
|
|
47188
47608
|
case "verification_pending":
|
|
47189
47609
|
return `${hostLabel} is holding the sign-in on a second factor rather than refusing it. Supply the verification code the ACCOUNT HOLDER received \u2014 the credential itself is not the problem, so changing it will not help.`;
|
|
47190
47610
|
case "timeout":
|
|
47191
|
-
return `The credential from '${source}' resolved, but ${hostLabel} did not answer in time. Usually transient \u2014 retry. If it persists, ${hostLabel} is slow or unreachable from here.`;
|
|
47611
|
+
return `${source !== null ? `The credential from '${source}' resolved, but ` : ""}${hostLabel} did not answer in time. Usually transient \u2014 retry. If it persists, ${hostLabel} is slow or unreachable from here.`;
|
|
47192
47612
|
case "http":
|
|
47193
47613
|
return `${hostLabel} answered with an error status that is not an auth rejection. That is USUALLY a ${hostLabel}-side problem rather than an auth one \u2014 but a 404 here more often means the probe path is wrong than that ${hostLabel} is broken, so check error.message and probe.url before concluding anything about the credential.`;
|
|
47194
47614
|
case "transport":
|
|
@@ -47209,7 +47629,8 @@ async function runCredentialHealthcheck(args) {
|
|
|
47209
47629
|
state = await resolveCredential();
|
|
47210
47630
|
} catch (e) {
|
|
47211
47631
|
const edge = edgeBlockOf(e);
|
|
47212
|
-
const
|
|
47632
|
+
const declared = errorKindOf(e);
|
|
47633
|
+
const classified = classifyThrown?.(e) ?? (edge !== null ? { kind: "edge_blocked", detail: { vendor: edge.vendor } } : declared !== void 0 ? { kind: declared } : void 0);
|
|
47213
47634
|
const result2 = {
|
|
47214
47635
|
ok: false,
|
|
47215
47636
|
// Still false, and still no source: a classification explains WHY
|
|
@@ -47256,10 +47677,11 @@ async function runCredentialHealthcheck(args) {
|
|
|
47256
47677
|
try {
|
|
47257
47678
|
await probeFn();
|
|
47258
47679
|
} catch (e) {
|
|
47259
|
-
status =
|
|
47680
|
+
status = errorStatusOf(e);
|
|
47260
47681
|
const aborted2 = e instanceof Error && e.name === "AbortError";
|
|
47261
47682
|
const edge = edgeBlockOf(e);
|
|
47262
|
-
|
|
47683
|
+
const declared = errorKindOf(e);
|
|
47684
|
+
arm = edge !== null ? "edge_blocked" : declared !== void 0 ? declared : status === 401 || status === 403 ? "credential_rejected" : status !== void 0 ? "http" : aborted2 || /timeout|timed out|ETIMEDOUT/i.test(messageOf(e)) ? "timeout" : /fetch failed|ENOTFOUND|ECONNREFUSED|ECONNRESET|network/i.test(messageOf(e)) ? "transport" : "unknown";
|
|
47263
47685
|
let kind = arm;
|
|
47264
47686
|
let detail = edge !== null ? { vendor: edge.vendor } : void 0;
|
|
47265
47687
|
const custom2 = classifyThrown?.(e);
|
|
@@ -47618,6 +48040,7 @@ init_dist2();
|
|
|
47618
48040
|
// node_modules/@chrischall/mcp-utils/dist/session/index.js
|
|
47619
48041
|
init_response();
|
|
47620
48042
|
init_config();
|
|
48043
|
+
init_errors3();
|
|
47621
48044
|
init_http();
|
|
47622
48045
|
init_edge_block();
|
|
47623
48046
|
import { existsSync as existsSync3, readFileSync as readFileSync3, mkdirSync as mkdirSync4, chmodSync as chmodSync2, renameSync as renameSync2, unlinkSync as unlinkSync4 } from "node:fs";
|
|
@@ -48099,7 +48522,9 @@ var TokenManager = class {
|
|
|
48099
48522
|
/** Spend `rt`, then install and persist what comes back. */
|
|
48100
48523
|
async exchange(rt) {
|
|
48101
48524
|
if (rt === void 0) {
|
|
48102
|
-
throw new
|
|
48525
|
+
throw new McpToolError("TokenManager: cannot refresh \u2014 no refresh token is available.", {
|
|
48526
|
+
kind: "no_credential"
|
|
48527
|
+
});
|
|
48103
48528
|
}
|
|
48104
48529
|
const tok = await this.refreshFn(rt);
|
|
48105
48530
|
this.tokens = {
|
|
@@ -48347,7 +48772,8 @@ var OutlookClient = class {
|
|
|
48347
48772
|
* — measured 9.4x smaller on a real message, and it also normalises a mixed
|
|
48348
48773
|
* HTML/Text collection to one type. `prefer` sets the `Prefer` header
|
|
48349
48774
|
* directly, for anything else the API takes there (notably
|
|
48350
|
-
* `outlook.timezone="…"`).
|
|
48775
|
+
* `outlook.timezone="…"`). `query` is serialised by mcp-utils'
|
|
48776
|
+
* `buildQueryString`, which drops `undefined`/empty values.
|
|
48351
48777
|
*/
|
|
48352
48778
|
async get(path, opts = {}) {
|
|
48353
48779
|
this.#requireConfigured();
|
|
@@ -48356,7 +48782,8 @@ var OutlookClient = class {
|
|
|
48356
48782
|
opts.prefer
|
|
48357
48783
|
].filter((p) => p !== void 0);
|
|
48358
48784
|
return this.#client().fetchJson("GET", path, {
|
|
48359
|
-
headers: prefer.length ? { Prefer: prefer.join(", ") } : void 0
|
|
48785
|
+
headers: prefer.length ? { Prefer: prefer.join(", ") } : void 0,
|
|
48786
|
+
...opts.query ? { query: opts.query } : {}
|
|
48360
48787
|
});
|
|
48361
48788
|
}
|
|
48362
48789
|
/**
|
|
@@ -48397,7 +48824,10 @@ var OutlookClient = class {
|
|
|
48397
48824
|
this.#requireConfigured();
|
|
48398
48825
|
return this.#client().fetchJson("POST", path, {
|
|
48399
48826
|
body,
|
|
48400
|
-
headers: opts.prefer ? { Prefer: opts.prefer } : void 0
|
|
48827
|
+
headers: opts.prefer ? { Prefer: opts.prefer } : void 0,
|
|
48828
|
+
// Read-only: a timeout or dropped connection is safe to retry, so keep
|
|
48829
|
+
// the plain error rather than mcp-utils' WriteOutcomeUnknownError.
|
|
48830
|
+
idempotent: true
|
|
48401
48831
|
});
|
|
48402
48832
|
}
|
|
48403
48833
|
/**
|
|
@@ -48442,6 +48872,7 @@ init_dist2();
|
|
|
48442
48872
|
|
|
48443
48873
|
// src/view.ts
|
|
48444
48874
|
init_dist2();
|
|
48875
|
+
var VIEWS2 = ["compact", "full", "raw"];
|
|
48445
48876
|
function addr(r) {
|
|
48446
48877
|
const e = r?.EmailAddress;
|
|
48447
48878
|
if (!e) return void 0;
|
|
@@ -48514,7 +48945,9 @@ function compactFolder(f) {
|
|
|
48514
48945
|
Id: f.Id,
|
|
48515
48946
|
Name: f.DisplayName,
|
|
48516
48947
|
Unread: f.UnreadItemCount,
|
|
48517
|
-
Total: f.TotalItemCount
|
|
48948
|
+
Total: f.TotalItemCount,
|
|
48949
|
+
// Listings are one level deep; a non-zero count says there is more below.
|
|
48950
|
+
Children: f.ChildFolderCount
|
|
48518
48951
|
});
|
|
48519
48952
|
}
|
|
48520
48953
|
function stripOData(o) {
|
|
@@ -48548,7 +48981,9 @@ var nextLinkParam = external_exports.string().min(1).optional().describe(
|
|
|
48548
48981
|
"Continue a previous listing: pass its `nextLink` value unchanged to fetch the next page. When set, the paging/filter arguments are taken from the link and ignored here."
|
|
48549
48982
|
);
|
|
48550
48983
|
function fetchPage(client2, nextLink, path, opts = {}) {
|
|
48551
|
-
|
|
48984
|
+
if (nextLink === void 0) return client2.get(path, opts);
|
|
48985
|
+
const { query: _ignored, ...headerOpts } = opts;
|
|
48986
|
+
return client2.getAbsolute(nextLink, headerOpts);
|
|
48552
48987
|
}
|
|
48553
48988
|
function plainCollection(data) {
|
|
48554
48989
|
const items = Array.isArray(data?.value) ? data.value : [];
|
|
@@ -48567,7 +49002,6 @@ function mailboxUntrusted(payload) {
|
|
|
48567
49002
|
}
|
|
48568
49003
|
|
|
48569
49004
|
// src/tools/mail.ts
|
|
48570
|
-
var VIEWS2 = ["compact", "full", "raw"];
|
|
48571
49005
|
var WELL_KNOWN = [
|
|
48572
49006
|
"inbox",
|
|
48573
49007
|
"drafts",
|
|
@@ -48579,14 +49013,6 @@ var WELL_KNOWN = [
|
|
|
48579
49013
|
"clutter"
|
|
48580
49014
|
];
|
|
48581
49015
|
var LIST_SELECT = "Id,Subject,From,ToRecipients,ReceivedDateTime,IsRead,HasAttachments,BodyPreview";
|
|
48582
|
-
function qs(params) {
|
|
48583
|
-
const parts = [];
|
|
48584
|
-
for (const [k, v] of Object.entries(params)) {
|
|
48585
|
-
if (v === void 0 || v === "") continue;
|
|
48586
|
-
parts.push(`${k}=${encodeURIComponent(String(v))}`);
|
|
48587
|
-
}
|
|
48588
|
-
return parts.length ? `?${parts.join("&")}` : "";
|
|
48589
|
-
}
|
|
48590
49016
|
function folderSegment(folder) {
|
|
48591
49017
|
return encodeURIComponent(folder);
|
|
48592
49018
|
}
|
|
@@ -48594,19 +49020,22 @@ function registerMailTools(server, client2) {
|
|
|
48594
49020
|
server.registerTool(
|
|
48595
49021
|
"outlook_list_folders",
|
|
48596
49022
|
{
|
|
48597
|
-
description: "List mail folders with unread and total counts. Use this to discover folder ids before listing messages; the well-known names (" + WELL_KNOWN.join(", ") + ") can be used directly without a lookup.",
|
|
48598
|
-
annotations: { readOnlyHint: true },
|
|
49023
|
+
description: "List mail folders with unread and total counts. Use this to discover folder ids before listing messages; the well-known names (" + WELL_KNOWN.join(", ") + ") can be used directly without a lookup. Lists ONE level: top-level folders by default, or the child folders of `parent`. A folder with `Children` > 0 has subfolders \u2014 list them with `parent` set to its Id.",
|
|
49024
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
48599
49025
|
inputSchema: external_exports.object({
|
|
48600
49026
|
view: viewParam(VIEWS2),
|
|
49027
|
+
parent: external_exports.string().min(1).optional().describe("List the child folders of this folder (id or well-known name). Omit for the top level."),
|
|
48601
49028
|
limit: external_exports.number().int().min(1).max(200).optional().describe("Max folders (default 50)"),
|
|
48602
49029
|
nextLink: nextLinkParam
|
|
48603
49030
|
})
|
|
48604
49031
|
},
|
|
48605
|
-
async ({ view, limit, nextLink }) => {
|
|
49032
|
+
async ({ view, parent, limit, nextLink }) => {
|
|
49033
|
+
const base = parent !== void 0 ? `/me/mailfolders/${folderSegment(parent)}/childfolders` : "/me/mailfolders";
|
|
48606
49034
|
const data = await fetchPage(
|
|
48607
49035
|
client2,
|
|
48608
49036
|
nextLink,
|
|
48609
|
-
|
|
49037
|
+
base,
|
|
49038
|
+
{ query: { $top: limit ?? 50 } }
|
|
48610
49039
|
);
|
|
48611
49040
|
if (resolveView(view, VIEWS2) === "raw") return minifiedResult(data);
|
|
48612
49041
|
return minifiedResult(projectCollection(data, compactFolder, "mail folder"));
|
|
@@ -48616,7 +49045,7 @@ function registerMailTools(server, client2) {
|
|
|
48616
49045
|
"outlook_list_messages",
|
|
48617
49046
|
{
|
|
48618
49047
|
description: "List messages in a folder, newest first. Defaults to the inbox. Use `unreadOnly` for a triage view. Bodies are NOT included \u2014 call outlook_get_message for one. Note `search` and `unreadOnly` cannot be combined on a first-page request (the API rejects $search with $filter); pass `search` alone to search the whole mailbox. When following a `nextLink`, both are ignored, so echoing them back is harmless. " + UNTRUSTED_DESCRIPTION_SUFFIX,
|
|
48619
|
-
annotations: { readOnlyHint: true },
|
|
49048
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
48620
49049
|
inputSchema: external_exports.object({
|
|
48621
49050
|
view: viewParam(VIEWS2),
|
|
48622
49051
|
folder: external_exports.string().optional().describe('Folder id or well-known name (default "inbox")'),
|
|
@@ -48648,7 +49077,8 @@ function registerMailTools(server, client2) {
|
|
|
48648
49077
|
const data = await fetchPage(
|
|
48649
49078
|
client2,
|
|
48650
49079
|
nextLink,
|
|
48651
|
-
|
|
49080
|
+
base,
|
|
49081
|
+
{ query: params }
|
|
48652
49082
|
);
|
|
48653
49083
|
const v = resolveView(view, VIEWS2);
|
|
48654
49084
|
if (v === "raw") return mailboxUntrusted(data);
|
|
@@ -48661,7 +49091,7 @@ function registerMailTools(server, client2) {
|
|
|
48661
49091
|
"outlook_get_message",
|
|
48662
49092
|
{
|
|
48663
49093
|
description: `Get one message including its body. The body is requested as plain text rather than HTML (measured ~9x smaller and far easier to read); pass view:"raw" to get Outlook's untouched record. ` + UNTRUSTED_DESCRIPTION_SUFFIX,
|
|
48664
|
-
annotations: { readOnlyHint: true },
|
|
49094
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
48665
49095
|
inputSchema: external_exports.object({
|
|
48666
49096
|
view: viewParam(VIEWS2),
|
|
48667
49097
|
id: external_exports.string().min(1).describe("Message Id from outlook_list_messages"),
|
|
@@ -48681,7 +49111,7 @@ function registerMailTools(server, client2) {
|
|
|
48681
49111
|
"outlook_list_attachments",
|
|
48682
49112
|
{
|
|
48683
49113
|
description: "List a message's attachments (name, size, content type). Metadata only \u2014 this deliberately does not download bytes.",
|
|
48684
|
-
annotations: { readOnlyHint: true },
|
|
49114
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
48685
49115
|
inputSchema: external_exports.object({
|
|
48686
49116
|
id: external_exports.string().min(1).describe("Message Id"),
|
|
48687
49117
|
nextLink: nextLinkParam
|
|
@@ -48691,9 +49121,8 @@ function registerMailTools(server, client2) {
|
|
|
48691
49121
|
const data = await fetchPage(
|
|
48692
49122
|
client2,
|
|
48693
49123
|
nextLink,
|
|
48694
|
-
`/me/messages/${encodeURIComponent(id)}/attachments
|
|
48695
|
-
|
|
48696
|
-
})}`
|
|
49124
|
+
`/me/messages/${encodeURIComponent(id)}/attachments`,
|
|
49125
|
+
{ query: { $select: "Id,Name,Size,ContentType" } }
|
|
48697
49126
|
);
|
|
48698
49127
|
return minifiedResult(plainCollection(data));
|
|
48699
49128
|
}
|
|
@@ -48705,8 +49134,9 @@ init_zod();
|
|
|
48705
49134
|
init_dist2();
|
|
48706
49135
|
|
|
48707
49136
|
// src/timezone.ts
|
|
49137
|
+
init_dist2();
|
|
48708
49138
|
var zoneByClient = /* @__PURE__ */ new WeakMap();
|
|
48709
|
-
async function mailboxTimeZone(client2) {
|
|
49139
|
+
async function mailboxTimeZone(client2, opts = {}) {
|
|
48710
49140
|
const cached2 = zoneByClient.get(client2);
|
|
48711
49141
|
if (cached2) return cached2;
|
|
48712
49142
|
try {
|
|
@@ -48714,21 +49144,16 @@ async function mailboxTimeZone(client2) {
|
|
|
48714
49144
|
const zone = settings?.TimeZone?.trim() || void 0;
|
|
48715
49145
|
if (zone) zoneByClient.set(client2, zone);
|
|
48716
49146
|
return zone;
|
|
48717
|
-
} catch {
|
|
48718
|
-
return void 0;
|
|
49147
|
+
} catch (e) {
|
|
49148
|
+
if (!opts.required) return void 0;
|
|
49149
|
+
throw new McpToolError("Could not read the mailbox's time zone, so the event time is ambiguous.", {
|
|
49150
|
+
hint: `Retry, or pass \`timeZone\` explicitly (a Windows zone name such as "Eastern Standard Time"). Cause: ${e instanceof Error ? e.message : String(e)}`
|
|
49151
|
+
});
|
|
48719
49152
|
}
|
|
48720
49153
|
}
|
|
48721
49154
|
|
|
48722
49155
|
// src/tools/calendar.ts
|
|
48723
49156
|
var EVENT_SELECT = "Id,Subject,Start,End,Location,Organizer,IsAllDay,IsCancelled,ShowAs,OnlineMeetingUrl,OnlineMeeting,BodyPreview";
|
|
48724
|
-
function qs2(params) {
|
|
48725
|
-
const parts = [];
|
|
48726
|
-
for (const [k, v] of Object.entries(params)) {
|
|
48727
|
-
if (v === void 0 || v === "") continue;
|
|
48728
|
-
parts.push(`${k}=${encodeURIComponent(String(v))}`);
|
|
48729
|
-
}
|
|
48730
|
-
return parts.length ? `?${parts.join("&")}` : "";
|
|
48731
|
-
}
|
|
48732
49157
|
function dateTimeTimeZone(value, zone) {
|
|
48733
49158
|
if (/(Z|[+-]\d{2}:?\d{2})$/i.test(value)) {
|
|
48734
49159
|
const d = new Date(value);
|
|
@@ -48756,7 +49181,7 @@ function registerCalendarTools(server, client2) {
|
|
|
48756
49181
|
"outlook_list_events",
|
|
48757
49182
|
{
|
|
48758
49183
|
description: 'List calendar events in a date window. This uses the calendar VIEW, which expands recurring series into their individual occurrences \u2014 the right tool for "what is on my schedule". Times are returned in `timeZone` when given. ' + UNTRUSTED_DESCRIPTION_SUFFIX,
|
|
48759
|
-
annotations: { readOnlyHint: true },
|
|
49184
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
48760
49185
|
inputSchema: external_exports.object({
|
|
48761
49186
|
view: viewParam(VIEWS2),
|
|
48762
49187
|
start: external_exports.string().min(1).describe("Window start, ISO 8601 (e.g. 2026-09-20T00:00:00Z)"),
|
|
@@ -48773,14 +49198,17 @@ function registerCalendarTools(server, client2) {
|
|
|
48773
49198
|
const data = await fetchPage(
|
|
48774
49199
|
client2,
|
|
48775
49200
|
nextLink,
|
|
48776
|
-
|
|
48777
|
-
|
|
48778
|
-
|
|
48779
|
-
|
|
48780
|
-
|
|
48781
|
-
|
|
48782
|
-
|
|
48783
|
-
|
|
49201
|
+
"/me/calendarview",
|
|
49202
|
+
{
|
|
49203
|
+
query: {
|
|
49204
|
+
startDateTime: start,
|
|
49205
|
+
endDateTime: end,
|
|
49206
|
+
$select: EVENT_SELECT,
|
|
49207
|
+
$orderby: "Start/DateTime",
|
|
49208
|
+
$top: limit ?? 50
|
|
49209
|
+
},
|
|
49210
|
+
...zone ? { prefer: `outlook.timezone="${zone}"` } : {}
|
|
49211
|
+
}
|
|
48784
49212
|
);
|
|
48785
49213
|
const v = resolveView(view, VIEWS2);
|
|
48786
49214
|
if (v === "raw") return mailboxUntrusted(data);
|
|
@@ -48793,7 +49221,7 @@ function registerCalendarTools(server, client2) {
|
|
|
48793
49221
|
"outlook_get_event",
|
|
48794
49222
|
{
|
|
48795
49223
|
description: "Get one calendar event in full, including attendees and their responses. " + UNTRUSTED_DESCRIPTION_SUFFIX,
|
|
48796
|
-
annotations: { readOnlyHint: true },
|
|
49224
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
48797
49225
|
inputSchema: external_exports.object({
|
|
48798
49226
|
view: viewParam(VIEWS2),
|
|
48799
49227
|
id: external_exports.string().min(1).describe("Event Id from outlook_list_events"),
|
|
@@ -48815,14 +49243,14 @@ function registerCalendarTools(server, client2) {
|
|
|
48815
49243
|
"outlook_list_calendars",
|
|
48816
49244
|
{
|
|
48817
49245
|
description: "List the calendars in the mailbox, for picking one to read or write against.",
|
|
48818
|
-
annotations: { readOnlyHint: true },
|
|
49246
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
48819
49247
|
inputSchema: external_exports.object({
|
|
48820
49248
|
view: viewParam(VIEWS2),
|
|
48821
49249
|
nextLink: nextLinkParam
|
|
48822
49250
|
})
|
|
48823
49251
|
},
|
|
48824
49252
|
async ({ view, nextLink }) => {
|
|
48825
|
-
const data = await fetchPage(client2, nextLink,
|
|
49253
|
+
const data = await fetchPage(client2, nextLink, "/me/calendars", { query: { $select: "Id,Name,Color,CanEdit,Owner" } });
|
|
48826
49254
|
if (resolveView(view, VIEWS2) === "raw") return minifiedResult(data);
|
|
48827
49255
|
return minifiedResult(plainCollection(data));
|
|
48828
49256
|
}
|
|
@@ -48831,7 +49259,7 @@ function registerCalendarTools(server, client2) {
|
|
|
48831
49259
|
"outlook_find_meeting_times",
|
|
48832
49260
|
{
|
|
48833
49261
|
description: "Find times when people can meet \u2014 Outlook's Scheduling Assistant. Give the attendees, a search window and a duration; Outlook checks everyone's free/busy (and the signed-in user's, as organizer) and returns ranked candidate slots with each attendee's availability. By default only working hours are searched. Use this to answer \"when can X and Y meet?\"; then book with outlook_create_event. `start`/`end` are local wall-clock times in `timeZone` (default: the mailbox zone), and returned slots are in that zone too. " + UNTRUSTED_DESCRIPTION_SUFFIX,
|
|
48834
|
-
annotations: { readOnlyHint: true },
|
|
49262
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
48835
49263
|
inputSchema: external_exports.object({
|
|
48836
49264
|
view: viewParam(VIEWS2),
|
|
48837
49265
|
attendees: attendeeList("required attendees").min(1),
|
|
@@ -48898,7 +49326,7 @@ function registerCalendarTools(server, client2) {
|
|
|
48898
49326
|
"outlook_get_schedule",
|
|
48899
49327
|
{
|
|
48900
49328
|
description: "Get people's free/busy blocks for a time window \u2014 the grid view of Outlook's Scheduling Assistant. Returns each person's busy, tentative and out-of-office items (with subject and location where their calendar shares them). Use outlook_find_meeting_times to have Outlook pick slots instead. `start`/`end` are local wall-clock times in `timeZone` (default: the mailbox zone). " + UNTRUSTED_DESCRIPTION_SUFFIX,
|
|
48901
|
-
annotations: { readOnlyHint: true },
|
|
49329
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
48902
49330
|
inputSchema: external_exports.object({
|
|
48903
49331
|
view: viewParam(VIEWS2),
|
|
48904
49332
|
people: attendeeList("people (or rooms) to look up").min(1).max(100),
|
|
@@ -48945,14 +49373,6 @@ function registerCalendarTools(server, client2) {
|
|
|
48945
49373
|
// src/tools/directory.ts
|
|
48946
49374
|
init_zod();
|
|
48947
49375
|
init_dist2();
|
|
48948
|
-
function qs3(params) {
|
|
48949
|
-
const parts = [];
|
|
48950
|
-
for (const [k, v] of Object.entries(params)) {
|
|
48951
|
-
if (v === void 0 || v === "") continue;
|
|
48952
|
-
parts.push(`${k}=${encodeURIComponent(String(v))}`);
|
|
48953
|
-
}
|
|
48954
|
-
return parts.length ? `?${parts.join("&")}` : "";
|
|
48955
|
-
}
|
|
48956
49376
|
function collection(data, raw) {
|
|
48957
49377
|
if (raw) return data;
|
|
48958
49378
|
return plainCollection(data);
|
|
@@ -48962,7 +49382,7 @@ function registerDirectoryTools(server, client2) {
|
|
|
48962
49382
|
"outlook_get_profile",
|
|
48963
49383
|
{
|
|
48964
49384
|
description: "Get the signed-in mailbox's identity (email address, display name, alias). The cheapest call that proves the credential works.",
|
|
48965
|
-
annotations: { readOnlyHint: true },
|
|
49385
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
48966
49386
|
inputSchema: external_exports.object({})
|
|
48967
49387
|
},
|
|
48968
49388
|
async () => minifiedResult(stripOData(await client2.get("/me")))
|
|
@@ -48971,7 +49391,7 @@ function registerDirectoryTools(server, client2) {
|
|
|
48971
49391
|
"outlook_get_mailbox_settings",
|
|
48972
49392
|
{
|
|
48973
49393
|
description: "Get mailbox settings: time zone, working hours, language, and automatic-replies (out-of-office) configuration.",
|
|
48974
|
-
annotations: { readOnlyHint: true },
|
|
49394
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
48975
49395
|
inputSchema: external_exports.object({})
|
|
48976
49396
|
},
|
|
48977
49397
|
async () => minifiedResult(stripOData(await client2.get("/me/MailboxSettings")))
|
|
@@ -48980,7 +49400,7 @@ function registerDirectoryTools(server, client2) {
|
|
|
48980
49400
|
"outlook_list_contacts",
|
|
48981
49401
|
{
|
|
48982
49402
|
description: "List saved contacts from the mailbox address book.",
|
|
48983
|
-
annotations: { readOnlyHint: true },
|
|
49403
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
48984
49404
|
inputSchema: external_exports.object({
|
|
48985
49405
|
view: viewParam(VIEWS2),
|
|
48986
49406
|
limit: external_exports.number().int().min(1).max(200).optional().describe("Max contacts (default 50)"),
|
|
@@ -48992,13 +49412,16 @@ function registerDirectoryTools(server, client2) {
|
|
|
48992
49412
|
const data = await fetchPage(
|
|
48993
49413
|
client2,
|
|
48994
49414
|
nextLink,
|
|
48995
|
-
|
|
48996
|
-
|
|
48997
|
-
|
|
48998
|
-
|
|
48999
|
-
|
|
49000
|
-
|
|
49001
|
-
|
|
49415
|
+
"/me/contacts",
|
|
49416
|
+
{
|
|
49417
|
+
query: {
|
|
49418
|
+
$top: limit ?? 50,
|
|
49419
|
+
$skip: skip,
|
|
49420
|
+
// `MobilePhone1`, not `MobilePhone`: the latter is the Graph name and
|
|
49421
|
+
// the v2.0 Contact type rejects it outright with a 400.
|
|
49422
|
+
$select: "Id,DisplayName,EmailAddresses,CompanyName,JobTitle,MobilePhone1"
|
|
49423
|
+
}
|
|
49424
|
+
}
|
|
49002
49425
|
);
|
|
49003
49426
|
return minifiedResult(collection(data, resolveView(view, VIEWS2) === "raw"));
|
|
49004
49427
|
}
|
|
@@ -49007,7 +49430,7 @@ function registerDirectoryTools(server, client2) {
|
|
|
49007
49430
|
"outlook_list_people",
|
|
49008
49431
|
{
|
|
49009
49432
|
description: 'List people ranked by relevance to the user \u2014 colleagues and frequent correspondents, drawn from mail traffic rather than the saved address book. Better than outlook_list_contacts for resolving "who is X" in a work mailbox.',
|
|
49010
|
-
annotations: { readOnlyHint: true },
|
|
49433
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
49011
49434
|
inputSchema: external_exports.object({
|
|
49012
49435
|
view: viewParam(VIEWS2),
|
|
49013
49436
|
limit: external_exports.number().int().min(1).max(100).optional().describe("Max people (default 25)"),
|
|
@@ -49018,10 +49441,13 @@ function registerDirectoryTools(server, client2) {
|
|
|
49018
49441
|
const data = await fetchPage(
|
|
49019
49442
|
client2,
|
|
49020
49443
|
nextLink,
|
|
49021
|
-
|
|
49022
|
-
|
|
49023
|
-
|
|
49024
|
-
|
|
49444
|
+
"/me/people",
|
|
49445
|
+
{
|
|
49446
|
+
query: {
|
|
49447
|
+
$top: limit ?? 25,
|
|
49448
|
+
$select: "Id,DisplayName,ScoredEmailAddresses,JobTitle,CompanyName"
|
|
49449
|
+
}
|
|
49450
|
+
}
|
|
49025
49451
|
);
|
|
49026
49452
|
return minifiedResult(collection(data, resolveView(view, VIEWS2) === "raw"));
|
|
49027
49453
|
}
|
|
@@ -49030,7 +49456,7 @@ function registerDirectoryTools(server, client2) {
|
|
|
49030
49456
|
"outlook_list_tasks",
|
|
49031
49457
|
{
|
|
49032
49458
|
description: "List Outlook tasks with status and due dates.",
|
|
49033
|
-
annotations: { readOnlyHint: true },
|
|
49459
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
49034
49460
|
inputSchema: external_exports.object({
|
|
49035
49461
|
view: viewParam(VIEWS2),
|
|
49036
49462
|
limit: external_exports.number().int().min(1).max(200).optional().describe("Max tasks (default 50)"),
|
|
@@ -49041,10 +49467,13 @@ function registerDirectoryTools(server, client2) {
|
|
|
49041
49467
|
const data = await fetchPage(
|
|
49042
49468
|
client2,
|
|
49043
49469
|
nextLink,
|
|
49044
|
-
|
|
49045
|
-
|
|
49046
|
-
|
|
49047
|
-
|
|
49470
|
+
"/me/tasks",
|
|
49471
|
+
{
|
|
49472
|
+
query: {
|
|
49473
|
+
$top: limit ?? 50,
|
|
49474
|
+
$select: "Id,Subject,Status,Importance,DueDateTime,CompletedDateTime"
|
|
49475
|
+
}
|
|
49476
|
+
}
|
|
49048
49477
|
);
|
|
49049
49478
|
return minifiedResult(collection(data, resolveView(view, VIEWS2) === "raw"));
|
|
49050
49479
|
}
|
|
@@ -49086,6 +49515,21 @@ function dateTimeTimeZone2(value, zone) {
|
|
|
49086
49515
|
}
|
|
49087
49516
|
return { DateTime: value, TimeZone: zone };
|
|
49088
49517
|
}
|
|
49518
|
+
var TIMEOUT_SENTENCE = 'If Outlook does not answer in time (or the connection drops) the result has status "unknown": the write may well have gone through, so check before retrying \u2014 never resend blindly.';
|
|
49519
|
+
async function writeOrUnknown(run, checkWhere) {
|
|
49520
|
+
try {
|
|
49521
|
+
return { ok: true, value: await run() };
|
|
49522
|
+
} catch (e) {
|
|
49523
|
+
if (!(e instanceof WriteOutcomeUnknownError)) throw e;
|
|
49524
|
+
return {
|
|
49525
|
+
ok: false,
|
|
49526
|
+
result: minifiedResult({
|
|
49527
|
+
status: "unknown",
|
|
49528
|
+
warning: `Outlook did not confirm this write (${e.timedOut ? "it did not answer in time" : "the connection dropped"}), so it may already have happened. Check ${checkWhere} before retrying; do not resend blindly.`
|
|
49529
|
+
})
|
|
49530
|
+
};
|
|
49531
|
+
}
|
|
49532
|
+
}
|
|
49089
49533
|
var naiveMs = (dt) => Date.parse(`${dt.replace(/\.\d+$/, "")}Z`);
|
|
49090
49534
|
var fromNaiveMs = (ms) => new Date(ms).toISOString().replace(/\.\d{3}Z$/, "");
|
|
49091
49535
|
var trimTime2 = (dt) => dt?.replace(/\.\d+$/, "");
|
|
@@ -49093,8 +49537,8 @@ function registerWriteTools(server, client2) {
|
|
|
49093
49537
|
server.registerTool(
|
|
49094
49538
|
"outlook_send_mail",
|
|
49095
49539
|
{
|
|
49096
|
-
description: "Send an email from the signed-in mailbox. " + CONFIRM_FLOW_SENTENCE + " The preview shows exactly what would be sent. " + CONFIRM_INJECTION_RULE,
|
|
49097
|
-
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
49540
|
+
description: "Send an email from the signed-in mailbox. " + CONFIRM_FLOW_SENTENCE + " The preview shows exactly what would be sent. " + TIMEOUT_SENTENCE + " " + CONFIRM_INJECTION_RULE,
|
|
49541
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
49098
49542
|
inputSchema: external_exports.object({
|
|
49099
49543
|
to: recipientList,
|
|
49100
49544
|
cc: recipientList,
|
|
@@ -49128,7 +49572,8 @@ function registerWriteTools(server, client2) {
|
|
|
49128
49572
|
confirmToken
|
|
49129
49573
|
});
|
|
49130
49574
|
if (gate) return gate;
|
|
49131
|
-
await client2.write("POST", "/me/sendmail", payload);
|
|
49575
|
+
const sent = await writeOrUnknown(() => client2.write("POST", "/me/sendmail", payload), "Sent Items");
|
|
49576
|
+
if (!sent.ok) return sent.result;
|
|
49132
49577
|
return minifiedResult({ sent: true, subject, recipients: recipients || null });
|
|
49133
49578
|
}
|
|
49134
49579
|
);
|
|
@@ -49136,7 +49581,7 @@ function registerWriteTools(server, client2) {
|
|
|
49136
49581
|
"outlook_create_draft",
|
|
49137
49582
|
{
|
|
49138
49583
|
description: "Create a draft message in the Drafts folder without sending it. Returns the created draft, which can be reviewed and sent from Outlook. " + CONFIRM_FLOW_SENTENCE,
|
|
49139
|
-
annotations: { readOnlyHint: false, destructiveHint:
|
|
49584
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
49140
49585
|
inputSchema: external_exports.object({
|
|
49141
49586
|
to: recipientList,
|
|
49142
49587
|
cc: recipientList,
|
|
@@ -49175,7 +49620,7 @@ function registerWriteTools(server, client2) {
|
|
|
49175
49620
|
"outlook_mark_read",
|
|
49176
49621
|
{
|
|
49177
49622
|
description: "Mark a message read or unread. The result is verified by re-reading the message \u2014 a 2xx alone is not proof it persisted. " + CONFIRM_FLOW_SENTENCE,
|
|
49178
|
-
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },
|
|
49623
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
49179
49624
|
inputSchema: external_exports.object({
|
|
49180
49625
|
id: external_exports.string().min(1).describe("Message Id"),
|
|
49181
49626
|
isRead: external_exports.boolean().describe("true to mark read, false to mark unread"),
|
|
@@ -49210,7 +49655,7 @@ function registerWriteTools(server, client2) {
|
|
|
49210
49655
|
"outlook_move_message",
|
|
49211
49656
|
{
|
|
49212
49657
|
description: 'Move a message to another folder (e.g. "archive", "deleteditems", or a folder id from outlook_list_folders). Moving assigns a NEW message id, which is returned. ' + CONFIRM_FLOW_SENTENCE,
|
|
49213
|
-
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
49658
|
+
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
49214
49659
|
inputSchema: external_exports.object({
|
|
49215
49660
|
id: external_exports.string().min(1).describe("Message Id"),
|
|
49216
49661
|
destination: external_exports.string().min(1).describe('Destination folder id or well-known name (e.g. "archive")'),
|
|
@@ -49248,8 +49693,8 @@ function registerWriteTools(server, client2) {
|
|
|
49248
49693
|
server.registerTool(
|
|
49249
49694
|
"outlook_create_event",
|
|
49250
49695
|
{
|
|
49251
|
-
description: 'Create a calendar event or meeting. A Microsoft Teams meeting is attached by default (`teamsMeeting: false` to skip) and its join link is returned. `timeZone` takes a WINDOWS zone name such as "Eastern Standard Time", not an IANA name. Attendees are emailed an invitation. To pick a time first, use outlook_find_meeting_times. ' + CONFIRM_FLOW_SENTENCE + " " + CONFIRM_INJECTION_RULE,
|
|
49252
|
-
annotations: { readOnlyHint: false, destructiveHint:
|
|
49696
|
+
description: 'Create a calendar event or meeting. A Microsoft Teams meeting is attached by default (`teamsMeeting: false` to skip) and its join link is returned. `timeZone` takes a WINDOWS zone name such as "Eastern Standard Time", not an IANA name. Attendees are emailed an invitation. To pick a time first, use outlook_find_meeting_times. ' + CONFIRM_FLOW_SENTENCE + " " + TIMEOUT_SENTENCE + " " + CONFIRM_INJECTION_RULE,
|
|
49697
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
49253
49698
|
inputSchema: external_exports.object({
|
|
49254
49699
|
subject: external_exports.string().describe("Event title"),
|
|
49255
49700
|
start: external_exports.string().min(1).describe("Start, ISO 8601 local time e.g. 2026-09-22T15:00:00"),
|
|
@@ -49269,7 +49714,7 @@ function registerWriteTools(server, client2) {
|
|
|
49269
49714
|
...(attendees ?? []).map((a) => attendee(a, "Required")),
|
|
49270
49715
|
...(optionalAttendees ?? []).map((a) => attendee(a, "Optional"))
|
|
49271
49716
|
];
|
|
49272
|
-
const tz = timeZone ?? await mailboxTimeZone(client2) ?? "UTC";
|
|
49717
|
+
const tz = timeZone ?? await mailboxTimeZone(client2, { required: true }) ?? "UTC";
|
|
49273
49718
|
const payload = {
|
|
49274
49719
|
Subject: subject,
|
|
49275
49720
|
Start: { DateTime: start, TimeZone: tz },
|
|
@@ -49290,11 +49735,12 @@ function registerWriteTools(server, client2) {
|
|
|
49290
49735
|
confirmToken
|
|
49291
49736
|
});
|
|
49292
49737
|
if (gate) return gate;
|
|
49293
|
-
const
|
|
49294
|
-
"POST",
|
|
49295
|
-
"
|
|
49296
|
-
payload
|
|
49738
|
+
const write = await writeOrUnknown(
|
|
49739
|
+
() => client2.write("POST", "/me/events", payload),
|
|
49740
|
+
"the calendar (outlook_list_events)"
|
|
49297
49741
|
);
|
|
49742
|
+
if (!write.ok) return write.result;
|
|
49743
|
+
const created = write.value;
|
|
49298
49744
|
let joinUrl = joinUrlOf(created);
|
|
49299
49745
|
if (wantTeams && !joinUrl && created?.Id) {
|
|
49300
49746
|
const after = await client2.get(
|
|
@@ -49306,6 +49752,7 @@ function registerWriteTools(server, client2) {
|
|
|
49306
49752
|
created: true,
|
|
49307
49753
|
Id: created?.Id,
|
|
49308
49754
|
WebLink: created?.WebLink,
|
|
49755
|
+
TimeZone: tz,
|
|
49309
49756
|
...joinUrl ? { JoinUrl: joinUrl } : {},
|
|
49310
49757
|
...wantTeams && !joinUrl ? { warning: "The event was created, but Outlook returned no Teams join link." } : {}
|
|
49311
49758
|
});
|
|
@@ -49315,7 +49762,7 @@ function registerWriteTools(server, client2) {
|
|
|
49315
49762
|
"outlook_update_event",
|
|
49316
49763
|
{
|
|
49317
49764
|
description: "Update a meeting you organize: retitle, move, relocate, edit the description, or add/remove attendees. Moving only `start` keeps the meeting's length. A Microsoft Teams meeting is added if the event lacks one (`teamsMeeting: false` to skip). Attendees are sent an updated invitation. Only the organizer can update a meeting \u2014 an attendee's edit would change their own copy alone, so it is refused. The result is verified by re-reading the event. " + CONFIRM_FLOW_SENTENCE + " " + CONFIRM_INJECTION_RULE,
|
|
49318
|
-
annotations: { readOnlyHint: false, destructiveHint:
|
|
49765
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
|
|
49319
49766
|
inputSchema: external_exports.object({
|
|
49320
49767
|
id: external_exports.string().min(1).describe("Event Id from outlook_list_events or outlook_get_event"),
|
|
49321
49768
|
subject: external_exports.string().optional().describe("New title"),
|
|
@@ -49332,7 +49779,7 @@ function registerWriteTools(server, client2) {
|
|
|
49332
49779
|
})
|
|
49333
49780
|
},
|
|
49334
49781
|
async (args, ctx) => {
|
|
49335
|
-
const tz = args.timeZone ?? await mailboxTimeZone(client2) ?? "UTC";
|
|
49782
|
+
const tz = args.timeZone ?? await mailboxTimeZone(client2, { required: true }) ?? "UTC";
|
|
49336
49783
|
const path = `/me/events/${encodeURIComponent(args.id)}`;
|
|
49337
49784
|
const readEvent = (zone) => client2.get(`${path}?$select=${EVENT_STATE_SELECT}`, {
|
|
49338
49785
|
prefer: `outlook.timezone="${zone}"`
|