Skip to content

Commit 93b2dfd

Browse files
committed
fix: align claude mcp schema with ollama mode
1 parent 881a179 commit 93b2dfd

2 files changed

Lines changed: 48 additions & 5 deletions

File tree

cli.js

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9357,24 +9357,33 @@ async function applyToClaudeSettings(config = {}) {
93579357
return { success: false, mode: 'settings-file', error: '请先输入 API Key' };
93589358
}
93599359

9360-
const baseUrl = (config.baseUrl || (targetApi === 'ollama' ? 'http://127.0.0.1:11434' : 'https://open.bigmodel.cn/api/anthropic')).trim();
9360+
const configuredBaseUrl = typeof config.baseUrl === 'string' ? config.baseUrl.trim() : '';
9361+
const baseUrl = (configuredBaseUrl || (targetApi === 'ollama' ? 'http://127.0.0.1:11434' : 'https://open.bigmodel.cn/api/anthropic')).trim();
93619362
const model = (config.model || DEFAULT_CLAUDE_MODEL).trim();
93629363
let settingsBaseUrl = baseUrl;
93639364
let settingsApiKey = apiKey;
93649365
let proxyResult = null;
93659366

93669367
if (targetApi === 'chat_completions' || targetApi === 'ollama') {
9368+
const upstreamProviderName = typeof config.name === 'string' ? config.name.trim() : '';
9369+
if (targetApi === 'chat_completions' && !configuredBaseUrl && !upstreamProviderName) {
9370+
return {
9371+
success: false,
9372+
mode: 'claude-proxy',
9373+
error: 'chat_completions 模式需要显式的上游 Base URL 或可解析的 provider 名称'
9374+
};
9375+
}
93679376
await stopBuiltinClaudeProxyRuntime();
93689377
const proxyToken = crypto.randomBytes(24).toString('hex');
93699378
proxyResult = await startBuiltinClaudeProxyRuntime({
93709379
enabled: true,
93719380
host: DEFAULT_BUILTIN_CLAUDE_PROXY_SETTINGS.host,
9372-
provider: typeof config.name === 'string' ? config.name.trim() : '',
9381+
provider: upstreamProviderName,
93739382
authSource: 'provider',
93749383
targetApi,
93759384
timeoutMs: DEFAULT_BUILTIN_CLAUDE_PROXY_SETTINGS.timeoutMs,
9376-
upstreamProviderName: typeof config.name === 'string' ? config.name.trim() : '',
9377-
upstreamBaseUrl: baseUrl,
9385+
upstreamProviderName,
9386+
...(configuredBaseUrl ? { upstreamBaseUrl: configuredBaseUrl } : {}),
93789387
upstreamApiKey: apiKey
93799388
});
93809389
if (!proxyResult || proxyResult.error || proxyResult.success === false || !proxyResult.listenUrl) {
@@ -15825,7 +15834,16 @@ function createMcpTools(options = {}) {
1582515834
name: { type: 'string' },
1582615835
targetApi: { type: 'string' }
1582715836
},
15828-
required: ['apiKey'],
15837+
allOf: [{
15838+
if: {
15839+
not: {
15840+
type: 'object',
15841+
properties: { targetApi: { const: 'ollama' } },
15842+
required: ['targetApi']
15843+
}
15844+
},
15845+
then: { required: ['apiKey'] }
15846+
}],
1582915847
additionalProperties: false
1583015848
},
1583115849
handler: async (args = {}) => applyToClaudeSettings(args || {})

tests/unit/claude-settings-sync.test.mjs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { readBundledWebUiScript, readProjectFile } from './helpers/web-ui-source
33

44
const appSource = readBundledWebUiScript();
55
const claudeConfigModuleSource = readProjectFile('web-ui/modules/app.methods.claude-config.mjs');
6+
const cliSource = readProjectFile('cli.js');
67

78
function escapeRegExp(value) {
89
return String(value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -1116,3 +1117,27 @@ test('loadClaudeModels skips remote fetch for external-credential config without
11161117
assert.strictEqual(context.claudeModelsHasCurrent, true);
11171118
assert.deepStrictEqual(messages, []);
11181119
});
1120+
1121+
test('applyToClaudeSettings does not proxy chat completions through default Anthropic URL', () => {
1122+
const startIndex = cliSource.indexOf('async function applyToClaudeSettings');
1123+
assert.notStrictEqual(startIndex, -1);
1124+
const endIndex = cliSource.indexOf('async function cmdClaude', startIndex);
1125+
assert.notStrictEqual(endIndex, -1);
1126+
const source = cliSource.slice(startIndex, endIndex);
1127+
assert.match(source, /const configuredBaseUrl = typeof config\.baseUrl === 'string' \? config\.baseUrl\.trim\(\) : '';/);
1128+
assert.match(source, /targetApi === 'chat_completions' && !configuredBaseUrl && !upstreamProviderName/);
1129+
assert.match(source, /chat_completions Base URL provider /);
1130+
assert.match(source, /\.\.\.\(configuredBaseUrl \? \{ upstreamBaseUrl: configuredBaseUrl \} : \{\}\)/);
1131+
});
1132+
1133+
test('MCP Claude config schema allows Ollama without API key only for ollama target', () => {
1134+
const toolIndex = cliSource.indexOf("name: 'codexmate.claude.config.apply'");
1135+
assert.notStrictEqual(toolIndex, -1);
1136+
const schemaEnd = cliSource.indexOf('handler: async (args = {}) => applyToClaudeSettings(args || {})', toolIndex);
1137+
assert.notStrictEqual(schemaEnd, -1);
1138+
const schemaSource = cliSource.slice(toolIndex, schemaEnd);
1139+
assert.match(schemaSource, /allOf:\s*\[\{/);
1140+
assert.match(schemaSource, /properties:\s*\{ targetApi:\s*\{ const: 'ollama' \} \}/);
1141+
assert.match(schemaSource, /then:\s*\{ required:\s*\['apiKey'\] \}/);
1142+
assert.doesNotMatch(schemaSource, /required:\s*\['apiKey'\],\s*additionalProperties/);
1143+
});

0 commit comments

Comments
 (0)