Skip to content

Conversation

narekhovhannisyan
Copy link
Collaborator

@narekhovhannisyan narekhovhannisyan commented Sep 2, 2025

Motivation

The Mailtrap Node.js SDK needed to automatically detect when it's running within a Mailtrap MCP (Model Context Protocol) environment and use an appropriate User-Agent string. This improves environment detection, integration tracking, and provides better analytics for MCP-specific usage patterns.

Changes

  • Added dynamic User-Agent detection that automatically switches between mailtrap-nodejs and mailtrap-mcp based on runtime context
  • Implemented comprehensive MCP context detection using multiple methods: main module filename, working directory, call stack analysis, and MCP runtime detection
  • Added MCP_USER_AGENT configuration option to support MCP-specific User-Agent string
  • Created comprehensive unit test suite with 12 test cases covering all detection scenarios and edge cases
  • Updated MailtrapClient to use dynamic User-Agent selection instead of static configuration

How to test

  • Run npm test -- src/__tests__/lib/get-agent.test.ts to verify all 12 unit tests pass
  • Test in regular Node.js environment - should use mailtrap-nodejs User-Agent
  • Test in MCP environment with working directory containing "mailtrap-mcp" - should use mailtrap-mcp User-Agent
  • Test in MCP runtime context (Claude Desktop) - should detect and use MCP User-Agent
  • Verify User-Agent header in HTTP requests matches expected value for each environment
  • Test error handling when process.cwd() throws exceptions
  • Verify node_modules exclusion logic works correctly

Summary by CodeRabbit

  • New Features

    • Automatically sets a context-aware User-Agent when running within Mailtrap MCP to improve environment detection and integration.
    • Added a configurable MCP-specific User-Agent in client settings.
  • Refactor

    • Switched to dynamic User-Agent selection while preserving existing behavior outside MCP contexts.
  • Tests

    • Added comprehensive tests covering dynamic User-Agent detection and fallback scenarios.

Copy link

coderabbitai bot commented Sep 2, 2025

Walkthrough

Adds CLIENT_SETTINGS.MCP_USER_AGENT to config, implements getDynamicUserAgent to choose between MCP_USER_AGENT and USER_AGENT based on runtime context, updates MailtrapClient to call getDynamicUserAgent for the User-Agent header, and adds tests for get-agent logic.

Changes

Cohort / File(s) Summary
Config: add MCP user agent
src/config/index.ts
Adds CLIENT_SETTINGS.MCP_USER_AGENT set to "mailtrap-mcp (https://github.com/railsware/mailtrap-mcp)".
Dynamic User-Agent selection module
src/lib/get-agent.ts
New module exporting getDynamicUserAgent(); detects MCP context via main module, CWD, and call stack checks and returns MCP_USER_AGENT or fallback USER_AGENT.
Client uses dynamic User-Agent + import tidy
src/lib/MailtrapClient.ts
Replaces static User-Agent header with getDynamicUserAgent() call and removes USER_AGENT destructuring; consolidates SuppressionsBaseAPI import.
Tests: get-agent behavior
src/__tests__/lib/get-agent.test.ts
Adds tests covering cwd and call-stack MCP detection, process.cwd() errors, stack undefined/null, and integration with MailtrapClient instantiation.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Client as MailtrapClient
    participant Agent as getDynamicUserAgent
    participant Config as CONFIG.CLIENT_SETTINGS
    participant HTTP as Axios

    Caller->>Client: perform operation (e.g., instantiate/request)
    Client->>Agent: getDynamicUserAgent()
    Agent->>Config: read USER_AGENT and MCP_USER_AGENT
    Note over Agent,Config: detect MCP via main module, cwd, call stack
    alt MCP context detected
        Agent-->>Client: return MCP_USER_AGENT
    else Not MCP
        Agent-->>Client: return USER_AGENT
    end
    Client->>HTTP: send request (headers["User-Agent"] = chosen value)
    HTTP-->>Client: response
    Client-->>Caller: return response/result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I twitch my ears at headers’ fate,
A nimble hop to choose which trait,
MCP or plain — I check the gate,
I stamp the call, both sly and straight.
Hopping bytes with tiny feet, I sign each packet sweet. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title “Dynamic user agent” concisely describes the core feature introduced by this changeset, which is dynamic User-Agent detection. It is specific to the primary update and does not include unnecessary details or vague language. This makes it clear to any reviewer or collaborator what the main purpose of the pull request is.
Description Check ✅ Passed The pull request description includes all required template sections: Motivation, Changes, and How to test. Each section is populated with specific and detailed information that clearly explains the context, list of updates, and verification steps. The checklist in the How to test section covers a range of scenarios and edge cases. Although the Images and GIFs section from the template is not present, this is non-essential for the type of change presented.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dynamic-user-agent

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@narekhovhannisyan narekhovhannisyan marked this pull request as draft September 2, 2025 12:53
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
src/config/index.ts (1)

21-21: Option: include package version in UA for better telemetry.

Consider appending the library version (e.g., "mailtrap-mcp/1.2.3 (...)"). Implement in get-agent.ts to keep config static.

src/lib/get-agent.ts (4)

23-30: Make cwd-based detection case-insensitive.

Paths can differ in case across platforms; normalize before matching.

Apply this diff:

 function isWorkingDirectoryMCP(): boolean {
   try {
     const cwd = process.cwd();
-    return cwd.includes("mailtrap-mcp") && !cwd.includes("node_modules");
+    const lower = cwd.toLowerCase();
+    return lower.includes("mailtrap-mcp") && !lower.includes("node_modules");
   } catch {
     return false;
   }
 }

36-44: Broaden self-exclusion and normalize case in stack-based detection.

Also exclude node_modules/mailtrap-nodejs to avoid false positives when this lib is on the stack.

Apply this diff:

 function isCallStackMCP(): boolean {
-  const { stack } = new Error();
-
-  return !!(
-    stack &&
-    stack.includes("mailtrap-mcp") &&
-    !stack.includes("node_modules/mailtrap")
-  );
+  const { stack } = new Error();
+  const lower = stack?.toLowerCase();
+  return !!(
+    lower &&
+    lower.includes("mailtrap-mcp") &&
+    !/node_modules\/mailtrap(-nodejs)?/.test(lower)
+  );
 }

59-61: Allow env override and ensure robust fallback.

Let users force UA selection and guard against unexpected undefineds.

Apply this diff:

-function getDynamicUserAgent(): string {
-  return isMailtrapMCPContext() ? MCP_USER_AGENT : USER_AGENT;
-}
+function getDynamicUserAgent(): string {
+  const override = process.env.MAILTRAP_UA?.toLowerCase();
+  if (override === "mcp") return MCP_USER_AGENT;
+  if (override === "default") return USER_AGENT;
+  const ua = isMailtrapMCPContext() ? MCP_USER_AGENT : USER_AGENT;
+  return ua || "mailtrap-nodejs";
+}

59-61: Optional: add version to UA string.

If resolveJsonModule is enabled, append package version for traceability (example implementation):

// at top-level (outside selected range):
// import { version } from "../../package.json";

// inside getDynamicUserAgent():
// const base = isMailtrapMCPContext() ? MCP_USER_AGENT : USER_AGENT;
// return `${base}/${version}`;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ffeca33 and 9da5776.

📒 Files selected for processing (3)
  • src/config/index.ts (1 hunks)
  • src/lib/MailtrapClient.ts (2 hunks)
  • src/lib/get-agent.ts (1 hunks)
🔇 Additional comments (4)
src/config/index.ts (1)

21-21: LGTM: adds MCP-specific User-Agent.

Field name and value look consistent with existing config keys.

src/lib/MailtrapClient.ts (2)

9-9: LGTM: importing dynamic UA helper.


69-70: LGTM: apply dynamic User-Agent header.

Header is set once per client instance, which is appropriate.

src/lib/get-agent.ts (1)

1-63: CommonJS-only packaging detected; no ESM support found.
package.json “type” is absent (defaults to CJS), tsconfig module=commonjs, and no ESM exports fields—no changes needed.

Comment on lines +9 to +17
function isMainModuleMCP(): boolean {
const mainFile = require?.main?.filename;

return !!(
mainFile &&
mainFile.includes("mailtrap-mcp") &&
!mainFile.includes("node_modules")
);
}
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.

@narekhovhannisyan narekhovhannisyan marked this pull request as ready for review September 26, 2025 13:29
@narekhovhannisyan narekhovhannisyan self-assigned this Sep 26, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (12)
src/__tests__/lib/get-agent.test.ts (12)

41-41: Use jest.spyOn instead of reassigning process.cwd

Reassigning globals is brittle and hard to restore. Spy on it instead.

Apply:

-process.cwd = jest.fn().mockReturnValue("/path/to/mailtrap-mcp");
+jest.spyOn(process, "cwd").mockReturnValue("/path/to/mailtrap-mcp");

54-56: Use jest.spyOn for process.cwd here as well

Repeat the spy pattern.

Apply:

-process.cwd = jest
-  .fn()
-  .mockReturnValue("/path/to/node_modules/mailtrap-mcp");
+jest.spyOn(process, "cwd").mockReturnValue(
+  "/path/to/node_modules/mailtrap-mcp"
+);

68-70: Mock cwd error via spy, not reassignment

This keeps the original method intact.

Apply:

-process.cwd = jest.fn().mockImplementation(() => {
-  throw new Error("Permission denied");
-});
+jest.spyOn(process, "cwd").mockImplementation(() => {
+  throw new Error("Permission denied");
+});

91-91: Same here: prefer a spy for process.cwd

Apply:

-process.cwd = jest.fn().mockReturnValue("/path/to/regular-app");
+jest.spyOn(process, "cwd").mockReturnValue("/path/to/regular-app");

112-112: Prefer spy over direct assignment

Apply:

-process.cwd = jest.fn().mockReturnValue("/path/to/regular-app");
+jest.spyOn(process, "cwd").mockReturnValue("/path/to/regular-app");

133-133: Prefer spy over direct assignment

Apply:

-process.cwd = jest.fn().mockReturnValue("/path/to/regular-app");
+jest.spyOn(process, "cwd").mockReturnValue("/path/to/regular-app");

149-149: Prefer spy over direct assignment

Apply:

-process.cwd = jest.fn().mockReturnValue("/path/to/regular-app");
+jest.spyOn(process, "cwd").mockReturnValue("/path/to/regular-app");

162-165: Combine both mocks using spies and real Error

Also prefer returning a real Error with overridden stack to preserve instanceof and other semantics.

Apply:

-process.cwd = jest.fn().mockImplementation(() => {
-  throw new Error("Permission denied");
-});
-global.Error = jest.fn().mockImplementation(() => ({
-  stack: undefined,
-})) as any;
+jest.spyOn(process, "cwd").mockImplementation(() => {
+  throw new Error("Permission denied");
+});
+global.Error = jest.fn().mockImplementation(() => {
+  const e = new originalError("Test error");
+  Object.defineProperty(e, "stack", { value: undefined });
+  return e;
+}) as any;

Tip: apply the “real Error with overridden stack” pattern to other stack-based tests, too (Lines 82-89, 102-109, 124-131, 145-147).


181-183: Prefer spy over direct assignment

Apply:

-process.cwd = jest
-  .fn()
-  .mockReturnValue("/Users/user/projects/mailtrap-mcp");
+jest.spyOn(process, "cwd").mockReturnValue(
+  "/Users/user/projects/mailtrap-mcp"
+);

195-195: Prefer spy over direct assignment

Apply:

-process.cwd = jest.fn().mockReturnValue("/Users/user/projects/my-app");
+jest.spyOn(process, "cwd").mockReturnValue("/Users/user/projects/my-app");

82-89: Return a real Error instance with a mocked stack

Returning a plain object breaks Error semantics. Use the original constructor and override stack.

Apply:

-global.Error = jest.fn().mockImplementation(() => ({
-  stack: `
-Error: Test error
-    at Object.<anonymous> (/path/to/mailtrap-mcp/index.js:10:1)
-    at Module._compile (internal/modules/cjs/loader.js:1063:30)
-        `,
-})) as any;
+global.Error = jest.fn().mockImplementation(() => {
+  const e = new originalError("Test error");
+  Object.defineProperty(e, "stack", {
+    value: `
+Error: Test error
+    at Object.<anonymous> (/path/to/mailtrap-mcp/index.js:10:1)
+    at Module._compile (internal/modules/cjs/loader.js:1063:30)
+        `,
+  });
+  return e;
+}) as any;

206-256: Strengthen the integration test: assert getDynamicUserAgent is used

Right now it only checks client is created. Mock get-agent and assert it’s called during client construction. Also align the config mock to default-export shape.

Apply:

-      jest.doMock("../../config", () => ({
-        CLIENT_SETTINGS: {
+      jest.doMock("../../config", () => ({
+        __esModule: true,
+        default: {
+          CLIENT_SETTINGS: {
             SENDING_ENDPOINT: "https://send.api.mailtrap.io",
             BULK_ENDPOINT: "https://bulk.api.mailtrap.io",
             TESTING_ENDPOINT: "https://sandbox.api.mailtrap.io",
             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,
-        },
-        ERRORS: {
+          },
+          ERRORS: {
             FILENAME_REQUIRED: "Filename is required.",
             CONTENT_REQUIRED: "Content is required.",
             SUBJECT_REQUIRED: "Subject is required.",
             FROM_REQUIRED: "From is required.",
             SENDING_FAILED: "Sending failed.",
             NO_DATA_ERROR: "No Data.",
             TEST_INBOX_ID_MISSING:
               "testInboxId is missing, testing API will not work.",
             ACCOUNT_ID_MISSING:
               "accountId is missing, some features of testing API may not work properly.",
             BULK_SANDBOX_INCOMPATIBLE:
               "Bulk mode is not applicable for sandbox API.",
-        },
-        TRANSPORT_SETTINGS: {
-          NAME: "MailtrapTransport",
-        },
+          },
+          TRANSPORT_SETTINGS: {
+            NAME: "MailtrapTransport",
+          },
+        },
       }));
 
+      const getAgentMock = jest.fn(() => "ua-from-test");
+      jest.doMock("../../lib/get-agent", () => ({
+        __esModule: true,
+        default: getAgentMock,
+      }));
+
       // Clear module cache and re-import
       jest.resetModules();
       const { default: MailtrapClient } = jest.requireActual(
         "../../lib/MailtrapClient"
       );
@@
-      // The User-Agent should be set in the axios instance
-      // We can't easily test the internal axios instance, but we can verify
-      // that the function is called during client creation
-      expect(client).toBeDefined();
+      // Ensure dynamic UA is consulted during client creation
+      expect(client).toBeDefined();
+      expect(getAgentMock).toHaveBeenCalledTimes(1);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9da5776 and c403388.

📒 Files selected for processing (3)
  • src/__tests__/lib/get-agent.test.ts (1 hunks)
  • src/lib/MailtrapClient.ts (3 hunks)
  • src/lib/get-agent.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/lib/get-agent.ts
  • src/lib/MailtrapClient.ts
🔇 Additional comments (1)
src/__tests__/lib/get-agent.test.ts (1)

29-39: Good baseline coverage and scenarios

Nice coverage of defaults, cwd-based, call stack-based, and error edge cases.

Comment on lines +4 to +10
jest.mock("../../config", () => ({
CLIENT_SETTINGS: {
USER_AGENT:
"mailtrap-nodejs (https://github.com/railsware/mailtrap-nodejs)",
MCP_USER_AGENT: "mailtrap-mcp (https://github.com/railsware/mailtrap-mcp)",
},
}));
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Mock the default export shape of config to avoid interop breakage

The test imports CONFIG as a default export, but the mock returns a plain object. Under ts-jest/Babel interop, default may be undefined. Return { __esModule: true, default: ... }.

Apply this diff:

-jest.mock("../../config", () => ({
-  CLIENT_SETTINGS: {
-    USER_AGENT:
-      "mailtrap-nodejs (https://github.com/railsware/mailtrap-nodejs)",
-    MCP_USER_AGENT: "mailtrap-mcp (https://github.com/railsware/mailtrap-mcp)",
-  },
-}));
+jest.mock("../../config", () => ({
+  __esModule: true,
+  default: {
+    CLIENT_SETTINGS: {
+      USER_AGENT:
+        "mailtrap-nodejs (https://github.com/railsware/mailtrap-nodejs)",
+      MCP_USER_AGENT:
+        "mailtrap-mcp (https://github.com/railsware/mailtrap-mcp)",
+    },
+  },
+}));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
jest.mock("../../config", () => ({
CLIENT_SETTINGS: {
USER_AGENT:
"mailtrap-nodejs (https://github.com/railsware/mailtrap-nodejs)",
MCP_USER_AGENT: "mailtrap-mcp (https://github.com/railsware/mailtrap-mcp)",
},
}));
jest.mock("../../config", () => ({
__esModule: true,
default: {
CLIENT_SETTINGS: {
USER_AGENT:
"mailtrap-nodejs (https://github.com/railsware/mailtrap-nodejs)",
MCP_USER_AGENT:
"mailtrap-mcp (https://github.com/railsware/mailtrap-mcp)",
},
},
}));
🤖 Prompt for AI Agents
In src/__tests__/lib/get-agent.test.ts around lines 4 to 10, the jest.mock
currently returns a plain object which breaks default-import interop under
ts-jest/Babel; update the mock to export a proper ES module shape by returning
an object with __esModule: true and a default property containing the original
CLIENT_SETTINGS object so the imported CONFIG default is defined.

Comment on lines +13 to +27
let originalCwd: string;
let originalError: typeof Error;

beforeEach(() => {
// Store original values
originalCwd = process.cwd();
originalError = global.Error;
});

afterEach(() => {
// Restore original values
process.cwd = jest.fn().mockReturnValue(originalCwd);
global.Error = originalError;
jest.clearAllMocks();
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Restore the real process.cwd; don’t replace it with a mock

You’re saving the cwd value (string) and then replacing process.cwd with a mock that returns that string. This leaks a mocked function into global state across the suite.

Apply this minimal fix (and prefer spy-based mocking in tests):

-  let originalCwd: string;
+  let originalCwdFn: typeof process.cwd;
   let originalError: typeof Error;

   beforeEach(() => {
     // Store original values
-    originalCwd = process.cwd();
+    originalCwdFn = process.cwd;
     originalError = global.Error;
   });

   afterEach(() => {
     // Restore original values
-    process.cwd = jest.fn().mockReturnValue(originalCwd);
+    process.cwd = originalCwdFn;
     global.Error = originalError;
-    jest.clearAllMocks();
+    jest.restoreAllMocks();
   });

Follow-up (recommended): replace all direct assignments to process.cwd in tests with jest.spyOn(process, "cwd").mockReturnValue(...).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let originalCwd: string;
let originalError: typeof Error;
beforeEach(() => {
// Store original values
originalCwd = process.cwd();
originalError = global.Error;
});
afterEach(() => {
// Restore original values
process.cwd = jest.fn().mockReturnValue(originalCwd);
global.Error = originalError;
jest.clearAllMocks();
});
let originalCwdFn: typeof process.cwd;
let originalError: typeof Error;
beforeEach(() => {
// Store original values
originalCwdFn = process.cwd;
originalError = global.Error;
});
afterEach(() => {
// Restore original values
process.cwd = originalCwdFn;
global.Error = originalError;
jest.restoreAllMocks();
});
🤖 Prompt for AI Agents
In src/__tests__/lib/get-agent.test.ts around lines 13-27, the test saves only
the cwd string and then replaces process.cwd with a mock that returns that
string, leaking a mocked function into global state; change the setup to save
the original process.cwd function (e.g., originalCwdFn = process.cwd) and in
afterEach restore the original function (process.cwd = originalCwdFn) instead of
assigning a jest.fn(), and, going forward, use jest.spyOn(process,
"cwd").mockReturnValue(...) when mocking cwd in tests so the spy can be restored
automatically.

* Gets the appropriate User-Agent string based on the current context.
* @returns The User-Agent string for the current context
*/
function getDynamicUserAgent(): string {
Copy link
Contributor

Choose a reason for hiding this comment

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

If you ask me, bringing the MCP concern to the SDK project is a violation of the SRP 🙂 I would approach this differently and allow specifying the user agent directly via a configuration setting of the SDK client. And, override this setting in the MCP server NPM package.
It's simpler, and it's not a big deal to allow users of the SDK to override the user agent; it's fairly easy to do this anyway.
WDYT?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

if allowing users to override user agent is not a problem, then its simple solution. I thought that we don't want to enable users to change the user agent.

Copy link
Contributor

Choose a reason for hiding this comment

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

@yanchuk WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

I agree with @vittorius, it's cleaner to allow to override user agent, and it might be useful for other purposes (for the users), too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants