fix(mcp): import opencode mcp config snippets (#1532)

This commit is contained in:
youzini
2026-06-11 21:55:38 +03:00
committed by GitHub
parent c9cd25d18f
commit abda2bc42b
2 changed files with 85 additions and 1 deletions
@@ -0,0 +1,60 @@
import { describe, expect, test } from 'bun:test';
import { parseImportedMcpSnippet } from './mcpImport';
describe('parseImportedMcpSnippet', () => {
test('imports OpenCode mcp wrapper config', () => {
const result = parseImportedMcpSnippet(JSON.stringify({
$schema: 'https://opencode.ai/config.json',
mcp: {
stitch: {
type: 'remote',
url: 'https://stitch.googleapis.com/mcp',
enabled: true,
headers: {
'X-Goog-Api-Key': 'test-key',
},
},
},
}));
expect(result.ok).toBe(true);
if (!result.ok) throw new Error(result.error);
expect(result.name).toBe('stitch');
expect(result.type).toBe('remote');
expect(result.url).toBe('https://stitch.googleapis.com/mcp');
expect(result.enabled).toBe(true);
expect(result.headers).toEqual([{ key: 'X-Goog-Api-Key', value: 'test-key' }]);
});
test('keeps existing mcpServers wrapper support', () => {
const result = parseImportedMcpSnippet(JSON.stringify({
mcpServers: {
localTool: {
command: 'node server.js',
args: ['--stdio'],
},
},
}));
expect(result.ok).toBe(true);
if (!result.ok) throw new Error(result.error);
expect(result.name).toBe('localTool');
expect(result.type).toBe('local');
expect(result.command).toEqual(['node', 'server.js', '--stdio']);
});
test('rejects multiple OpenCode mcp wrapper entries', () => {
const result = parseImportedMcpSnippet(JSON.stringify({
mcp: {
one: { type: 'remote', url: 'https://one.example/mcp' },
two: { type: 'remote', url: 'https://two.example/mcp' },
},
}));
expect(result.ok).toBe(false);
if (result.ok) throw new Error('Expected import to fail');
expect(result.error).toContain('Paste one server at a time');
expect(result.error).toContain('servers in mcp');
});
});
@@ -221,6 +221,7 @@ function isServerConfig(val: Record<string, unknown>): boolean {
*
* Supported shapes:
* { "mcpServers": { "name": { ... } } }
* { "mcp": { "name": { ... } } }
* { "name": { ... } }
* { ...serverConfig }
*/
@@ -270,6 +271,29 @@ export function parseImportedMcpSnippet(
return buildResult(serverName, inferType(entry as Record<string, unknown>), entry as Record<string, unknown>);
}
// Detect OpenCode config shape { "mcp": { "name": { ... } } }
const mcp = obj.mcp;
if (isObject(mcp)) {
const keys = Object.keys(mcp);
if (keys.length === 0) {
return buildError('mcp object is empty', parsed);
}
if (keys.length > 1) {
return buildError(
'Paste one server at a time. Found ' +
keys.length +
' servers in mcp',
parsed,
);
}
const serverName = keys[0]!;
const entry = mcp[serverName];
if (!isObject(entry)) {
return buildError('Server entry in mcp is not a valid object', parsed);
}
return buildResult(serverName, inferType(entry as Record<string, unknown>), entry as Record<string, unknown>);
}
// Detect single named entry { "serverName": { ... } }
const single = extractSingleServer(obj);
if (single) {
@@ -336,4 +360,4 @@ export function applyImportedMcpToDraft(
};
return draft;
}
}