@0xmaxma/claude-gateway 1.4.1 → 1.4.2
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/mcp/tools/image/module.ts +50 -10
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
|
+
import * as os from 'node:os';
|
|
2
3
|
import * as path from 'node:path';
|
|
3
4
|
import * as dns from 'node:dns';
|
|
4
5
|
import * as net from 'node:net';
|
|
@@ -60,8 +61,12 @@ export class ImageModule implements ToolModule {
|
|
|
60
61
|
toolVisibility: ToolVisibility = 'all-configured';
|
|
61
62
|
|
|
62
63
|
isEnabled(): boolean {
|
|
63
|
-
// Enabled when the
|
|
64
|
-
|
|
64
|
+
// Enabled when BOTH the endpoint and an auth token resolve (from env or the CLI config
|
|
65
|
+
// at ~/.claude/settings.json; see baseUrl()/authToken()) and the tool isn't explicitly
|
|
66
|
+
// turned off. Requiring the token here means a config that resolves a URL but no token
|
|
67
|
+
// disables the tool cleanly, instead of advertising it and then failing every call with a
|
|
68
|
+
// 401 on an empty Bearer.
|
|
69
|
+
if (!this.baseUrl() || !this.authToken() || process.env.IMAGE_DISABLED === 'true') return false;
|
|
65
70
|
// The Bearer proxy_secret rides every call — refuse a cleartext http URL to a
|
|
66
71
|
// PUBLIC host (that would leak the secret). http to a local/internal host is a
|
|
67
72
|
// trusted hop (e.g. host.docker.internal in dev) and stays allowed.
|
|
@@ -106,18 +111,53 @@ export class ImageModule implements ToolModule {
|
|
|
106
111
|
// ── config ────────────────────────────────────────────────────────────────
|
|
107
112
|
|
|
108
113
|
private baseUrl(): string {
|
|
109
|
-
// Image generation
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
+
// Image generation may target a provider separate from the LLM: IMAGE_BASE_URL
|
|
115
|
+
// (or ANTHROPIC_BASE_URL when they share a provider that fronts /v1/images
|
|
116
|
+
// alongside /v1/messages) overrides. When neither env var is set, fall back to
|
|
117
|
+
// the Claude Code CLI config at ~/.claude/settings.json — its `env` block carries
|
|
118
|
+
// ANTHROPIC_BASE_URL, and the CLI applies that block internally (not to the OS
|
|
119
|
+
// environment), so an MCP subprocess can't inherit it and reads the file instead.
|
|
120
|
+
const raw =
|
|
121
|
+
process.env.IMAGE_BASE_URL ||
|
|
122
|
+
process.env.ANTHROPIC_BASE_URL ||
|
|
123
|
+
this.settingsEnv('ANTHROPIC_BASE_URL');
|
|
114
124
|
return raw.replace(/\/+$/, '');
|
|
115
125
|
}
|
|
116
126
|
|
|
117
127
|
private authToken(): string {
|
|
118
|
-
//
|
|
119
|
-
// falls back to ANTHROPIC_AUTH_TOKEN
|
|
120
|
-
|
|
128
|
+
// IMAGE_API_KEY overrides so a separate image endpoint can carry its own secret;
|
|
129
|
+
// falls back to ANTHROPIC_AUTH_TOKEN, then to the CLI config's `env` block in
|
|
130
|
+
// ~/.claude/settings.json. Check BOTH token keys there: a proxy deployment may store the
|
|
131
|
+
// M2M secret under ANTHROPIC_AUTH_TOKEN (the same key the env path uses) or under
|
|
132
|
+
// CLAUDE_CODE_OAUTH_TOKEN — mirror the env precedence and accept either.
|
|
133
|
+
return (
|
|
134
|
+
process.env.IMAGE_API_KEY ||
|
|
135
|
+
process.env.ANTHROPIC_AUTH_TOKEN ||
|
|
136
|
+
this.settingsEnv('ANTHROPIC_AUTH_TOKEN') ||
|
|
137
|
+
this.settingsEnv('CLAUDE_CODE_OAUTH_TOKEN')
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Parsed `env` block of the CLI config, read once per instance (settings don't
|
|
142
|
+
// change within a session). undefined = not read yet; null = unreadable.
|
|
143
|
+
private settingsEnvCache?: Record<string, unknown> | null;
|
|
144
|
+
|
|
145
|
+
// settingsEnv reads a single key out of ~/.claude/settings.json's `env` block.
|
|
146
|
+
// CLAUDE_CONFIG_DIR overrides the ~/.claude location, matching Claude Code. Returns
|
|
147
|
+
// '' on any error (missing file, bad JSON, absent/non-string key) so callers degrade
|
|
148
|
+
// to "not configured" rather than throwing.
|
|
149
|
+
private settingsEnv(key: string): string {
|
|
150
|
+
if (this.settingsEnvCache === undefined) {
|
|
151
|
+
try {
|
|
152
|
+
const dir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
|
|
153
|
+
this.settingsEnvCache =
|
|
154
|
+
JSON.parse(fs.readFileSync(path.join(dir, 'settings.json'), 'utf8'))?.env ?? null;
|
|
155
|
+
} catch {
|
|
156
|
+
this.settingsEnvCache = null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const v = this.settingsEnvCache?.[key];
|
|
160
|
+
return typeof v === 'string' ? v : '';
|
|
121
161
|
}
|
|
122
162
|
|
|
123
163
|
private headers(): Record<string, string> {
|