Skip to content

Commit a2ad1d6

Browse files
committed
feat: add controlled client-side logging that respects NODE_ENV
- Add logging utility that suppresses output when NODE_ENV=production - Replace all console usage in client code with logger - Add ESLint no-console rule for client code (except logging.ts) This prevents unwanted console output in production environments like Claude Code while still allowing debugging in development.
1 parent 2a23890 commit a2ad1d6

File tree

4 files changed

+55
-5
lines changed

4 files changed

+55
-5
lines changed

eslint.config.mjs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,12 @@ export default tseslint.config(
1515
{ "argsIgnorePattern": "^_" }
1616
]
1717
}
18+
},
19+
{
20+
files: ["src/client/**/*.ts"],
21+
ignores: ["src/client/logging.ts"],
22+
rules: {
23+
"no-console": "error"
24+
}
1825
}
1926
);

src/client/auth.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { LATEST_PROTOCOL_VERSION } from "../types.js";
33
import type { OAuthClientMetadata, OAuthClientInformation, OAuthTokens, OAuthMetadata, OAuthClientInformationFull, OAuthProtectedResourceMetadata } from "../shared/auth.js";
44
import { OAuthClientInformationFullSchema, OAuthMetadataSchema, OAuthProtectedResourceMetadataSchema, OAuthTokensSchema } from "../shared/auth.js";
55
import { resourceUrlFromServerUrl } from "../shared/auth-utils.js";
6+
import { logger } from "./logging.js";
67

78
/**
89
* Implements an end-to-end OAuth client to be used with one MCP server.
@@ -117,7 +118,7 @@ export async function auth(
117118
authorizationServerUrl = resourceMetadata.authorization_servers[0];
118119
}
119120
} catch (error) {
120-
console.warn("Could not load OAuth Protected Resource metadata, falling back to /.well-known/oauth-authorization-server", error)
121+
logger.warn("Could not load OAuth Protected Resource metadata, falling back to /.well-known/oauth-authorization-server", error)
121122
}
122123

123124
const resource: URL | undefined = await selectResourceURL(serverUrl, provider, resourceMetadata);
@@ -176,7 +177,7 @@ export async function auth(
176177
await provider.saveTokens(newTokens);
177178
return "AUTHORIZED";
178179
} catch (error) {
179-
console.error("Could not refresh OAuth tokens:", error);
180+
logger.error("Could not refresh OAuth tokens:", error);
180181
}
181182
}
182183

@@ -222,7 +223,7 @@ export function extractResourceMetadataUrl(res: Response): URL | undefined {
222223

223224
const [type, scheme] = authenticateHeader.split(' ');
224225
if (type.toLowerCase() !== 'bearer' || !scheme) {
225-
console.log("Invalid WWW-Authenticate header format, expected 'Bearer'");
226+
logger.log("Invalid WWW-Authenticate header format, expected 'Bearer'");
226227
return undefined;
227228
}
228229
const regex = /resource_metadata="([^"]*)"/;
@@ -235,7 +236,7 @@ export function extractResourceMetadataUrl(res: Response): URL | undefined {
235236
try {
236237
return new URL(match[1]);
237238
} catch {
238-
console.log("Invalid resource metadata url: ", match[1]);
239+
logger.log("Invalid resource metadata url: ", match[1]);
239240
return undefined;
240241
}
241242
}

src/client/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import {
4545
} from "../types.js";
4646
import Ajv from "ajv";
4747
import type { ValidateFunction } from "ajv";
48+
import { logger } from "./logging.js";
4849

4950
export type ClientOptions = ProtocolOptions & {
5051
/**
@@ -487,7 +488,7 @@ export class Client<
487488
const validator = this._ajv.compile(tool.outputSchema);
488489
this._cachedToolOutputValidators.set(tool.name, validator);
489490
} catch (error) {
490-
console.warn(`Failed to compile output schema for tool ${tool.name}: ${error}`);
491+
logger.warn(`Failed to compile output schema for tool ${tool.name}: ${error}`);
491492
}
492493
}
493494
}

src/client/logging.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Client-side logging utility that suppresses console output in production environments.
3+
* Logs are shown when NODE_ENV is not set to 'production'.
4+
*/
5+
6+
const isDebugEnabled = (): boolean => {
7+
// Allow logging unless explicitly in production
8+
if (typeof process !== "undefined" && process.env) {
9+
return process.env.NODE_ENV !== "production";
10+
}
11+
// If process.env is not available, default to allowing logs
12+
return true;
13+
};
14+
15+
const debugEnabled = isDebugEnabled();
16+
17+
export const logger = {
18+
log: (...args: unknown[]): void => {
19+
if (debugEnabled) {
20+
console.log(...args);
21+
}
22+
},
23+
24+
warn: (...args: unknown[]): void => {
25+
if (debugEnabled) {
26+
console.warn(...args);
27+
}
28+
},
29+
30+
error: (...args: unknown[]): void => {
31+
if (debugEnabled) {
32+
console.error(...args);
33+
}
34+
},
35+
36+
debug: (...args: unknown[]): void => {
37+
if (debugEnabled) {
38+
console.debug(...args);
39+
}
40+
},
41+
};

0 commit comments

Comments
 (0)