Skip to content

Commit 48cede1

Browse files
authored
Merge pull request #172 from docker/cm/0.2.51-p2
Add contiinue.dev
2 parents c347c28 + 857e62b commit 48cede1

File tree

3 files changed

+122
-3
lines changed

3 files changed

+122
-3
lines changed

src/extension/ui/src/Registry.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,12 @@ export const syncRegistryWithConfig = async (client: v1.DockerDesktopClient, reg
106106
const oldRegString = JSON.stringify(registry)
107107
for (const [itemName, itemConfig] of Object.entries(config)) {
108108
const registryItem = registry[itemName]
109-
if (registryItem) {
110-
const mergedConfig = mergeDeep(registryItem.config || {}, itemConfig)
109+
if (registryItem && registryItem.config?.[itemName]) {
110+
const mergedConfig = mergeDeep(registryItem.config[itemName] || {}, itemConfig)
111111
registry[itemName].config = { [itemName]: mergedConfig }
112112
}
113113
}
114-
const newRegString = JSON.stringify({ registry })
114+
const newRegString = JSON.stringify(registry)
115115
if (oldRegString !== newRegString) {
116116
console.log('Updating registry with new config.', 'oldRegString', oldRegString, 'newRegString', newRegString)
117117
await writeFileToPromptsVolume(client, JSON.stringify({ files: [{ path: 'registry.yaml', content: stringify({ registry }) }] }))
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { v1 } from "@docker/extension-api-client-types";
2+
import { escapeJSONForPlatformShell, getUser } from "../FileUtils";
3+
import { MCPClient, SAMPLE_MCP_CONFIG } from "./MCPTypes";
4+
import { DOCKER_MCP_COMMAND } from "../Constants";
5+
import { mergeDeep } from "../MergeDeep";
6+
import { parse, stringify } from "yaml";
7+
class ContinueDotDev implements MCPClient {
8+
name = 'Continue.dev';
9+
url = 'https://continue.dev/';
10+
manualConfigSteps = [
11+
'Open <strong>Continue.dev Settings </strong>',
12+
'in your global .continue folder (~/.continue on Mac, %USERPROFILE%\.continue) within .continue/assistants. The name of the file will be used as the display name of the assistant, e.g. My Assistant.yaml',
13+
'Add block mcpServers:',
14+
stringify(SAMPLE_MCP_CONFIG)
15+
];
16+
expectedConfigPath = {
17+
darwin: '$HOME/.continue/config.yaml',
18+
linux: '$HOME/.continue/config.yaml',
19+
win32: '$USERPROFILE\\.continue\\config.yaml'
20+
};
21+
readConfig = async (client: v1.DockerDesktopClient) => {
22+
const platform = client.host.platform as keyof typeof this.expectedConfigPath;
23+
const configPath = this.expectedConfigPath[platform].replace('$USER', await getUser(client));
24+
try {
25+
const result = await client.docker.cli.exec('run', ['--rm', '--mount', `type=bind,source=${configPath},target=/continue/config.yaml`, 'alpine:latest', 'cat', '/continue/config.yaml']);
26+
return {
27+
content: result.stdout,
28+
path: configPath
29+
};
30+
} catch (e) {
31+
return {
32+
content: null,
33+
path: configPath
34+
};
35+
}
36+
};
37+
connect = async (client: v1.DockerDesktopClient) => {
38+
const config = await this.readConfig(client);
39+
let continueConfig = null;
40+
try {
41+
continueConfig = parse(config.content || '') as typeof SAMPLE_MCP_CONFIG;
42+
if (continueConfig.mcpServers?.MCP_DOCKER) {
43+
client.desktopUI.toast.success('Continue.dev MCP server already connected.');
44+
return;
45+
}
46+
} catch (e) {
47+
continueConfig = mergeDeep({}, SAMPLE_MCP_CONFIG);
48+
}
49+
const payload = mergeDeep(continueConfig, SAMPLE_MCP_CONFIG);
50+
try {
51+
await client.docker.cli.exec('run',
52+
[
53+
'--rm',
54+
'--mount',
55+
`type=bind,source="${config.path}",target=/continue/config.yaml`,
56+
'--workdir',
57+
'/continue', 'vonwig/function_write_files:latest',
58+
escapeJSONForPlatformShell({ files: [{ path: 'config.yaml', content: stringify(payload) }] }, client.host.platform)
59+
]
60+
);
61+
client.desktopUI.toast.success('Connected Continue.dev MCP Server.');
62+
} catch (e) {
63+
if ((e as any).stderr) {
64+
client.desktopUI.toast.error((e as any).stderr);
65+
} else {
66+
client.desktopUI.toast.error((e as Error).message);
67+
}
68+
}
69+
};
70+
disconnect = async (client: v1.DockerDesktopClient) => {
71+
const config = await this.readConfig(client);
72+
if (!config.content) {
73+
client.desktopUI.toast.error('No config found');
74+
return;
75+
}
76+
let continueConfig = null;
77+
try {
78+
continueConfig = parse(config.content) as typeof SAMPLE_MCP_CONFIG;
79+
if (!continueConfig.mcpServers?.MCP_DOCKER) {
80+
client.desktopUI.toast.error('Docker MCP Server not connected to Continue.dev');
81+
return;
82+
}
83+
} catch (e) {
84+
client.desktopUI.toast.error('Failed to disconnect. Invalid Continue.dev config found at ' + config.path);
85+
return;
86+
}
87+
const payload = {
88+
...continueConfig,
89+
mcpServers: Object.fromEntries(Object.entries(continueConfig.mcpServers).filter(([key]) => key !== 'MCP_DOCKER'))
90+
};
91+
try {
92+
await client.docker.cli.exec('run',
93+
[
94+
'--rm',
95+
'--mount',
96+
`type=bind,source="${config.path}",target=/continue/config.yaml`,
97+
'--workdir',
98+
'/continue', 'vonwig/function_write_files:latest',
99+
escapeJSONForPlatformShell({ files: [{ path: 'config.yaml', content: stringify(payload) }] }, client.host.platform)
100+
]
101+
);
102+
} catch (e) {
103+
if ((e as any).stderr) {
104+
client.desktopUI.toast.error((e as any).stderr);
105+
} else {
106+
client.desktopUI.toast.error((e as Error).message);
107+
}
108+
}
109+
};
110+
validateConfig = (content: string) => {
111+
const config = JSON.parse(content || '{}') as typeof SAMPLE_MCP_CONFIG;
112+
return !!config.mcpServers?.MCP_DOCKER;
113+
};
114+
}
115+
116+
export default new ContinueDotDev();

src/extension/ui/src/mcp-clients/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
* Each client is exported as a singleton instance and cached by the module system.
44
* Multiple imports of this module will always return the same client instances,
55
* ensuring consistent state across the application.
6+
* Barrels are bad except when they're good.
67
*/
78

89
import Cursor from "./Cursor";
910
import ClaudeDesktop from "./ClaudeDesktop";
11+
import ContinueDotDev from "./ContinueDotDev";
1012
import Gordon from "./Gordon";
1113
import Windsurf from "./Windsurf";
1214
import { MCPClient } from "./MCPTypes";
@@ -16,4 +18,5 @@ export const SUPPORTED_MCP_CLIENTS: MCPClient[] = [
1618
ClaudeDesktop,
1719
Cursor,
1820
Windsurf,
21+
ContinueDotDev,
1922
]

0 commit comments

Comments
 (0)