@marlinjai/email-mcp 1.7.0 → 1.7.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/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @marlinjai/email-mcp
2
2
 
3
+ <p align="center"><img src="assets/icon-512.png" width="120" height="120" alt="email-mcp logo"></p>
4
+
3
5
  A unified MCP server for email access across Gmail, Outlook, iCloud, and generic IMAP providers.
4
6
 
5
7
  ## Features
package/dist/index.js CHANGED
@@ -7710,15 +7710,17 @@ var init_auth = __esm({
7710
7710
  getAuthUrl(redirectUri, scopeMode) {
7711
7711
  const codeVerifier = crypto2.randomBytes(32).toString("base64url");
7712
7712
  const codeChallenge = crypto2.createHash("sha256").update(codeVerifier).digest("base64url");
7713
+ const state = crypto2.randomBytes(16).toString("base64url");
7713
7714
  this.oauth2Client = new OAuth2Client2(this.clientId, this.clientSecret, redirectUri);
7714
7715
  const url2 = this.oauth2Client.generateAuthUrl({
7715
7716
  access_type: "offline",
7716
7717
  scope: GMAIL_SCOPES[resolveScopeMode(scopeMode)],
7717
7718
  prompt: "consent",
7718
7719
  code_challenge: codeChallenge,
7719
- code_challenge_method: "S256"
7720
+ code_challenge_method: "S256",
7721
+ state
7720
7722
  });
7721
- return { url: url2, codeVerifier };
7723
+ return { url: url2, codeVerifier, state };
7722
7724
  }
7723
7725
  async exchangeCode(code, codeVerifier) {
7724
7726
  const { tokens } = await this.oauth2Client.getToken({ code, codeVerifier });
@@ -8369,6 +8371,7 @@ var init_adapter2 = __esm({
8369
8371
  });
8370
8372
 
8371
8373
  // src/providers/outlook/auth.ts
8374
+ import crypto3 from "node:crypto";
8372
8375
  import fs2 from "node:fs";
8373
8376
  import path2 from "node:path";
8374
8377
  import os2 from "node:os";
@@ -8423,13 +8426,15 @@ var init_auth2 = __esm({
8423
8426
  }
8424
8427
  async getAuthUrl(redirectUri) {
8425
8428
  const { verifier, challenge } = await this.cryptoProvider.generatePkceCodes();
8429
+ const state = crypto3.randomBytes(16).toString("base64url");
8426
8430
  const url2 = await this.pca.getAuthCodeUrl({
8427
8431
  scopes: _OutlookAuth.SCOPES,
8428
8432
  redirectUri,
8429
8433
  codeChallenge: challenge,
8430
- codeChallengeMethod: "S256"
8434
+ codeChallengeMethod: "S256",
8435
+ state
8431
8436
  });
8432
- return { url: url2, codeVerifier: verifier };
8437
+ return { url: url2, codeVerifier: verifier, state };
8433
8438
  }
8434
8439
  async exchangeCode(code, codeVerifier, redirectUri) {
8435
8440
  const result = await this.pca.acquireTokenByCode({
@@ -9660,6 +9665,7 @@ var init_oauth_server = __esm({
9660
9665
  rejectCode = null;
9661
9666
  timeoutMs;
9662
9667
  timeoutHandle = null;
9668
+ expectedState = null;
9663
9669
  constructor(timeoutMs = 12e4) {
9664
9670
  this.timeoutMs = timeoutMs;
9665
9671
  }
@@ -9670,7 +9676,12 @@ var init_oauth_server = __esm({
9670
9676
  if (url2.pathname === "/callback" || url2.pathname === "/") {
9671
9677
  const code = url2.searchParams.get("code");
9672
9678
  const error48 = url2.searchParams.get("error");
9673
- if (code) {
9679
+ const state = url2.searchParams.get("state");
9680
+ if (code && this.expectedState !== null && state !== this.expectedState) {
9681
+ res.writeHead(400, { "Content-Type": "text/html" });
9682
+ res.end(ERROR_HTML);
9683
+ this.rejectCode?.(new Error("OAuth error: state mismatch, callback did not come from the authorization request we started"));
9684
+ } else if (code) {
9674
9685
  res.writeHead(200, { "Content-Type": "text/html" });
9675
9686
  res.end(SUCCESS_HTML);
9676
9687
  this.resolveCode?.(code);
@@ -9695,7 +9706,13 @@ var init_oauth_server = __esm({
9695
9706
  this.server.on("error", reject);
9696
9707
  });
9697
9708
  }
9698
- waitForCode() {
9709
+ /**
9710
+ * Resolve with the authorization code once the browser is redirected back.
9711
+ * Pass the `state` that was put into the authorization URL: callbacks whose
9712
+ * state does not match are rejected instead of resolved (CSRF protection).
9713
+ */
9714
+ waitForCode(expectedState) {
9715
+ this.expectedState = expectedState ?? null;
9699
9716
  return new Promise((resolve, reject) => {
9700
9717
  this.resolveCode = (code) => {
9701
9718
  if (this.timeoutHandle) clearTimeout(this.timeoutHandle);
@@ -9727,7 +9744,7 @@ var init_oauth_server = __esm({
9727
9744
 
9728
9745
  // src/setup/wizard.ts
9729
9746
  var wizard_exports = {};
9730
- import crypto4 from "node:crypto";
9747
+ import crypto5 from "node:crypto";
9731
9748
  import { exec } from "node:child_process";
9732
9749
  function openBrowser(url2) {
9733
9750
  const platform = process.platform;
@@ -9794,7 +9811,7 @@ async function setupGmail(scopeModeFlag) {
9794
9811
  const port = await server.start();
9795
9812
  const redirectUri = `http://localhost:${port}/callback`;
9796
9813
  const gmailAuth = new GmailAuth(GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET, redirectUri);
9797
- const { url: url2, codeVerifier } = gmailAuth.getAuthUrl(redirectUri, scopeMode);
9814
+ const { url: url2, codeVerifier, state } = gmailAuth.getAuthUrl(redirectUri, scopeMode);
9798
9815
  console.log("\nOpening browser for Google authorization...");
9799
9816
  console.log(`If the browser does not open, visit:
9800
9817
  ${url2}
@@ -9802,7 +9819,7 @@ ${url2}
9802
9819
  openBrowser(url2);
9803
9820
  let code;
9804
9821
  try {
9805
- code = await server.waitForCode();
9822
+ code = await server.waitForCode(state);
9806
9823
  } catch (err) {
9807
9824
  server.shutdown();
9808
9825
  throw new Error(`OAuth flow failed: ${err.message}`);
@@ -9818,7 +9835,7 @@ ${url2}
9818
9835
  if (!email3) throw new Error("Could not retrieve Gmail address from profile");
9819
9836
  console.log(`Authenticated as ${email3}`);
9820
9837
  const name = await promptAccountName("Gmail");
9821
- const id = crypto4.randomUUID();
9838
+ const id = crypto5.randomUUID();
9822
9839
  const creds = {
9823
9840
  id,
9824
9841
  name,
@@ -9835,7 +9852,7 @@ async function setupOutlook() {
9835
9852
  const port = await server.start();
9836
9853
  const redirectUri = `http://localhost:${port}`;
9837
9854
  const outlookAuth = new OutlookAuth(OUTLOOK_CLIENT_ID);
9838
- const { url: url2, codeVerifier } = await outlookAuth.getAuthUrl(redirectUri);
9855
+ const { url: url2, codeVerifier, state } = await outlookAuth.getAuthUrl(redirectUri);
9839
9856
  console.log("\nOpening browser for Microsoft authorization...");
9840
9857
  console.log(`If the browser does not open, visit:
9841
9858
  ${url2}
@@ -9843,7 +9860,7 @@ ${url2}
9843
9860
  openBrowser(url2);
9844
9861
  let code;
9845
9862
  try {
9846
- code = await server.waitForCode();
9863
+ code = await server.waitForCode(state);
9847
9864
  } catch (err) {
9848
9865
  server.shutdown();
9849
9866
  throw new Error(`OAuth flow failed: ${err.message}`);
@@ -9856,7 +9873,7 @@ ${url2}
9856
9873
  if (!email3) throw new Error("Could not retrieve email from Microsoft account");
9857
9874
  console.log(`Authenticated as ${email3}`);
9858
9875
  const name = await promptAccountName("Outlook");
9859
- const id = crypto4.randomUUID();
9876
+ const id = crypto5.randomUUID();
9860
9877
  const creds = {
9861
9878
  id,
9862
9879
  name,
@@ -9888,7 +9905,7 @@ async function setupICloud() {
9888
9905
  password = await askPassword("App-specific password:");
9889
9906
  }
9890
9907
  const name = await promptAccountName("iCloud");
9891
- const id = crypto4.randomUUID();
9908
+ const id = crypto5.randomUUID();
9892
9909
  const creds = {
9893
9910
  id,
9894
9911
  name,
@@ -9929,7 +9946,7 @@ async function setupIMAP() {
9929
9946
  smtpPort = parseInt(smtpPortStr, 10);
9930
9947
  }
9931
9948
  const name = await promptAccountName("IMAP");
9932
- const id = crypto4.randomUUID();
9949
+ const id = crypto5.randomUUID();
9933
9950
  const creds = {
9934
9951
  id,
9935
9952
  name,
@@ -33215,7 +33232,7 @@ var StdioServerTransport = class {
33215
33232
  init_account_manager();
33216
33233
 
33217
33234
  // src/tools/accounts.ts
33218
- import crypto3 from "node:crypto";
33235
+ import crypto4 from "node:crypto";
33219
33236
  function jsonResult(data) {
33220
33237
  return { content: [{ type: "text", text: JSON.stringify(data) }] };
33221
33238
  }
@@ -33270,7 +33287,7 @@ function registerAccountTools(server, accountManager) {
33270
33287
  error: "IMAP host and port are required for non-iCloud providers"
33271
33288
  });
33272
33289
  }
33273
- const accountId = crypto3.randomUUID();
33290
+ const accountId = crypto4.randomUUID();
33274
33291
  const creds = {
33275
33292
  id: accountId,
33276
33293
  name: args.name,