Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default {
GENERAL_ENDPOINT: "https://mailtrap.io",
USER_AGENT:
"mailtrap-nodejs (https://github.com/railsware/mailtrap-nodejs)",
MCP_USER_AGENT: "mailtrap-mcp (https://github.com/railsware/mailtrap-mcp)",
MAX_REDIRECTS: 0,
TIMEOUT: 10000,
},
Expand Down
6 changes: 3 additions & 3 deletions src/lib/MailtrapClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import axios, { AxiosInstance } from "axios";
import encodeMailBuffers from "./mail-buffer-encoder";
import handleSendingError from "./axios-logger";
import MailtrapError from "./MailtrapError";
import getDynamicUserAgent from "./get-agent";

import GeneralAPI from "./api/General";
import TestingAPI from "./api/Testing";
import ContactsBaseAPI from "./api/Contacts";
import ContactListsBaseAPI from "./api/ContactLists";
import TemplatesBaseAPI from "./api/Templates";
import SuppressionsBaseAPI from "./api/Suppressions";

import CONFIG from "../config";

Expand All @@ -22,13 +24,11 @@ import {
BatchSendResponse,
BatchSendRequest,
} from "../types/mailtrap";
import SuppressionsBaseAPI from "./api/Suppressions";

const { CLIENT_SETTINGS, ERRORS } = CONFIG;
const {
SENDING_ENDPOINT,
MAX_REDIRECTS,
USER_AGENT,
TIMEOUT,
TESTING_ENDPOINT,
BULK_ENDPOINT,
Expand Down Expand Up @@ -66,7 +66,7 @@ export default class MailtrapClient {
headers: {
Authorization: `Bearer ${token}`,
Connection: "keep-alive",
"User-Agent": USER_AGENT,
"User-Agent": getDynamicUserAgent(),
},
maxRedirects: MAX_REDIRECTS,
timeout: TIMEOUT,
Expand Down
63 changes: 63 additions & 0 deletions src/lib/get-agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import CONFIG from "../config";

const { USER_AGENT, MCP_USER_AGENT } = CONFIG.CLIENT_SETTINGS;

/**
* Checks if the main module filename indicates MCP context.
* @returns true if main module contains "mailtrap-mcp" and is not in node_modules
*/
function isMainModuleMCP(): boolean {
const mainFile = require?.main?.filename;

return !!(
mainFile &&
mainFile.includes("mailtrap-mcp") &&
!mainFile.includes("node_modules")
);
}
Comment on lines +9 to +17
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix ESM ReferenceError risk from using require directly.

require?.main still throws in pure ESM because require is an unbound identifier. Guard with typeof require !== 'undefined' and fall back to process.argv[1].

Apply this diff:

 function isMainModuleMCP(): boolean {
-  const mainFile = require?.main?.filename;
-
-  return !!(
-    mainFile &&
-    mainFile.includes("mailtrap-mcp") &&
-    !mainFile.includes("node_modules")
-  );
+  const mainFile =
+    (typeof require !== "undefined" ? require.main?.filename : undefined) ??
+    process.argv?.[1];
+  if (!mainFile) return false;
+  const lower = mainFile.toLowerCase();
+  return lower.includes("mailtrap-mcp") && !lower.includes("node_modules");
 }
🤖 Prompt for AI Agents
In src/lib/get-agent.ts around lines 9 to 17, avoid referencing the unbound
require in pure ESM by first checking typeof require !== 'undefined' and using
require.main?.filename only when defined, otherwise fall back to
process.argv[1]; replace const mainFile = require?.main?.filename with a safe
resolution like: if typeof require !== 'undefined' and require.main use
require.main.filename else use process.argv[1], then keep the subsequent
includes checks the same to determine mailtrap-mcp presence and node_modules
exclusion.


/**
* Checks if the current working directory indicates MCP context.
* @returns true if cwd contains "mailtrap-mcp" and is not in node_modules
*/
function isWorkingDirectoryMCP(): boolean {
try {
const cwd = process.cwd();
return cwd.includes("mailtrap-mcp") && !cwd.includes("node_modules");
} catch {
return false;
}
}

/**
* Checks if the call stack indicates MCP context.
* @returns true if stack contains "mailtrap-mcp" and is not from node_modules/mailtrap
*/
function isCallStackMCP(): boolean {
const { stack } = new Error();

return !!(
stack &&
stack.includes("mailtrap-mcp") &&
!stack.includes("node_modules/mailtrap")
);
}

/**
* Determines if the code is running in a Mailtrap MCP context.
* Uses multiple detection methods to ensure accurate context identification.
* @returns true if running in MCP context, false otherwise
*/
function isMailtrapMCPContext(): boolean {
return isMainModuleMCP() || isWorkingDirectoryMCP() || isCallStackMCP();
}

/**
* Gets the appropriate User-Agent string based on the current context.
* @returns The User-Agent string for the current context
*/
function getDynamicUserAgent(): string {
return isMailtrapMCPContext() ? MCP_USER_AGENT : USER_AGENT;
}

export default getDynamicUserAgent;