Skip to content

Commit 249b61e

Browse files
committed
Merge branch 'refs/heads/main' into intellij_cache_diff
# Conflicts: # gui/src/redux/slices/sessionSlice.ts
2 parents 4b018a6 + ed6f001 commit 249b61e

File tree

95 files changed

+2105
-1262
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

95 files changed

+2105
-1262
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ Icon?
157157
notes.md
158158

159159
manual-testing-sandbox/.idea/**
160+
manual-testing-sandbox/.continue/**
160161
extensions/intellij/.idea/**
161162

162163
**/.idea/workspace.xml

binary/package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/commands/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export function slashFromCustomCommand(
1010
return {
1111
name: customCommand.name,
1212
description: customCommand.description ?? "",
13+
prompt: customCommand.prompt,
1314
run: async function* ({ input, llm, history, ide, completionOptions }) {
1415
// Remove slash command prefix from input
1516
let userInput = input;

core/commands/util.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import { v4 as uuidv4 } from "uuid";
2+
13
import { ContextItemWithId, RangeInFileWithContents } from "../";
24
import { findUriInDirs, getUriPathBasename } from "../util/uri";
3-
import { v4 as uuidv4 } from "uuid";
45

56
export function rifWithContentsToContextItem(
67
rif: RangeInFileWithContents,

core/config/ConfigHandler.ts

-10
Original file line numberDiff line numberDiff line change
@@ -387,16 +387,6 @@ export class ConfigHandler {
387387
this.ideSettingsPromise,
388388
);
389389

390-
// After login, default to the first org as the selected org
391-
try {
392-
const orgs = await this.controlPlaneClient.listOrganizations();
393-
if (orgs.length) {
394-
await this.setSelectedOrgId(orgs[0].id);
395-
}
396-
} catch (e) {
397-
console.error("Failed to fetch control plane profiles: ", e);
398-
}
399-
400390
this.fetchControlPlaneProfiles().catch(async (e) => {
401391
console.error("Failed to fetch control plane profiles: ", e);
402392
await this.loadLocalProfilesOnly();

core/config/load.ts

+17-55
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import {
2828
IdeType,
2929
ILLM,
3030
LLMOptions,
31-
MCPOptions,
3231
ModelDescription,
3332
RerankerDescription,
3433
SerializedContinueConfig,
@@ -45,7 +44,6 @@ import ContinueProxyContextProvider from "../context/providers/ContinueProxyCont
4544
import CustomContextProviderClass from "../context/providers/CustomContextProvider";
4645
import FileContextProvider from "../context/providers/FileContextProvider";
4746
import { contextProviderClassFromName } from "../context/providers/index";
48-
import PromptFilesContextProvider from "../context/providers/PromptFilesContextProvider";
4947
import { useHub } from "../control-plane/env";
5048
import { allEmbeddingsProviders } from "../indexing/allEmbeddingsProviders";
5149
import { BaseLLM } from "../llm";
@@ -401,8 +399,6 @@ async function intermediateToFinalConfig(
401399
...(!config.disableIndexing
402400
? [new CodebaseContextProvider(codebaseContextParams)]
403401
: []),
404-
// Add prompt files provider if enabled
405-
...(loadPromptFiles ? [new PromptFilesContextProvider({})] : []),
406402
];
407403

408404
const DEFAULT_CONTEXT_PROVIDERS_TITLES = DEFAULT_CONTEXT_PROVIDERS.map(
@@ -521,6 +517,8 @@ async function intermediateToFinalConfig(
521517
contextProviders,
522518
models,
523519
tools: allTools,
520+
mcpServerStatuses: [],
521+
slashCommands: config.slashCommands ?? [],
524522
modelsByRole: {
525523
chat: models,
526524
edit: models,
@@ -541,53 +539,18 @@ async function intermediateToFinalConfig(
541539
},
542540
};
543541

544-
// Apply MCP if specified
542+
// Trigger MCP server refreshes (Config is reloaded again once connected!)
545543
const mcpManager = MCPManagerSingleton.getInstance();
546-
function getMcpId(options: MCPOptions) {
547-
return JSON.stringify(options);
548-
}
549-
if (config.experimental?.modelContextProtocolServers) {
550-
await mcpManager.removeUnusedConnections(
551-
config.experimental.modelContextProtocolServers.map(getMcpId),
552-
);
553-
}
554-
555-
if (config.experimental?.modelContextProtocolServers) {
556-
const abortController = new AbortController();
557-
const mcpConnectionTimeout = setTimeout(
558-
() => abortController.abort(),
559-
5000,
560-
);
561-
562-
await Promise.allSettled(
563-
config.experimental.modelContextProtocolServers?.map(
564-
async (server, index) => {
565-
try {
566-
const mcpId = getMcpId(server);
567-
const mcpConnection = mcpManager.createConnection(mcpId, server);
568-
await mcpConnection.modifyConfig(
569-
continueConfig,
570-
mcpId,
571-
abortController.signal,
572-
"MCP Server",
573-
server.faviconUrl,
574-
);
575-
} catch (e) {
576-
let errorMessage = "Failed to load MCP server";
577-
if (e instanceof Error) {
578-
errorMessage += ": " + e.message;
579-
}
580-
errors.push({
581-
fatal: false,
582-
message: errorMessage,
583-
});
584-
} finally {
585-
clearTimeout(mcpConnectionTimeout);
586-
}
587-
},
588-
) || [],
589-
);
590-
}
544+
mcpManager.setConnections(
545+
(config.experimental?.modelContextProtocolServers ?? []).map(
546+
(server, index) => ({
547+
id: `continue-mcp-server-${index + 1}`,
548+
name: `MCP Server ${index + 1}`,
549+
...server,
550+
}),
551+
),
552+
false,
553+
);
591554

592555
// Handle experimental modelRole config values for apply and edit
593556
const inlineEditModel = getModelByRole(continueConfig, "inlineEdit")?.title;
@@ -667,11 +630,9 @@ async function finalToBrowserConfig(
667630
models: final.models.map(llmToSerializedModelDescription),
668631
systemMessage: final.systemMessage,
669632
completionOptions: final.completionOptions,
670-
slashCommands: final.slashCommands?.map((s) => ({
671-
name: s.name,
672-
description: s.description,
673-
params: s.params, // TODO: is this why params aren't referenced properly by slash commands?
674-
})),
633+
slashCommands: final.slashCommands?.map(
634+
({ run, ...slashCommandDescription }) => slashCommandDescription,
635+
),
675636
contextProviders: final.contextProviders?.map((c) => c.description),
676637
disableIndexing: final.disableIndexing,
677638
disableSessionTitles: final.disableSessionTitles,
@@ -681,6 +642,7 @@ async function finalToBrowserConfig(
681642
rules: final.rules,
682643
docs: final.docs,
683644
tools: final.tools,
645+
mcpServerStatuses: final.mcpServerStatuses,
684646
tabAutocompleteOptions: final.tabAutocompleteOptions,
685647
usePlatform: await useHub(ide.getIdeSettings()),
686648
modelsByRole: Object.fromEntries(

core/config/profile/ControlPlaneProfileLoader.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ConfigJson } from "@continuedev/config-types";
22
import { ConfigResult } from "@continuedev/config-yaml";
33

44
import { ControlPlaneClient } from "../../control-plane/client.js";
5+
import { PRODUCTION_ENV } from "../../control-plane/env.js";
56
import {
67
ContinueConfig,
78
IDE,
@@ -10,7 +11,6 @@ import {
1011
} from "../../index.js";
1112
import { ProfileDescription } from "../ProfileLifecycleManager.js";
1213

13-
import { PRODUCTION_ENV } from "../../control-plane/env.js";
1414
import doLoadConfig from "./doLoadConfig.js";
1515
import { IProfileLoader } from "./IProfileLoader.js";
1616

core/config/profile/PlatformProfileLoader.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { AssistantUnrolled, ConfigResult } from "@continuedev/config-yaml";
22

33
import { ControlPlaneClient } from "../../control-plane/client.js";
4+
import { getControlPlaneEnv } from "../../control-plane/env.js";
45
import { ContinueConfig, IDE, IdeSettings } from "../../index.js";
5-
66
import { ProfileDescription } from "../ProfileLifecycleManager.js";
77

8-
import { getControlPlaneEnv } from "../../control-plane/env.js";
98
import doLoadConfig from "./doLoadConfig.js";
109
import { IProfileLoader } from "./IProfileLoader.js";
1110

core/config/profile/doLoadConfig.ts

+87-5
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,24 @@ import {
66
ConfigValidationError,
77
ModelRole,
88
} from "@continuedev/config-yaml";
9+
910
import {
1011
ContinueConfig,
1112
ContinueRcJson,
1213
IDE,
1314
IdeSettings,
1415
SerializedContinueConfig,
16+
Tool,
1517
} from "../../";
18+
import { constructMcpSlashCommand } from "../../commands/slash/mcp";
19+
import { MCPManagerSingleton } from "../../context/mcp";
20+
import MCPContextProvider from "../../context/providers/MCPContextProvider";
1621
import { ControlPlaneProxyInfo } from "../../control-plane/analytics/IAnalyticsProvider.js";
1722
import { ControlPlaneClient } from "../../control-plane/client.js";
1823
import { getControlPlaneEnv } from "../../control-plane/env.js";
1924
import { TeamAnalytics } from "../../control-plane/TeamAnalytics.js";
2025
import ContinueProxy from "../../llm/llms/stubs/ContinueProxy";
26+
import { encodeMCPToolUri } from "../../tools/callTool";
2127
import { getConfigJsonPath, getConfigYamlPath } from "../../util/paths";
2228
import { localPathOrUriToPath } from "../../util/pathToUri";
2329
import { Telemetry } from "../../util/posthog";
@@ -26,6 +32,7 @@ import { loadContinueConfigFromJson } from "../load";
2632
import { migrateJsonSharedConfig } from "../migrateSharedConfig";
2733
import { rectifySelectedModelsFromGlobalContext } from "../selectedModels";
2834
import { loadContinueConfigFromYaml } from "../yaml/loadYaml";
35+
2936
import { PlatformConfigMetadata } from "./PlatformProfileLoader";
3037

3138
export default async function doLoadConfig(
@@ -93,15 +100,90 @@ export default async function doLoadConfig(
93100
configLoadInterrupted = result.configLoadInterrupted;
94101
}
95102

96-
// Rectify model selections for each role
97-
if (newConfig) {
98-
newConfig = rectifySelectedModelsFromGlobalContext(newConfig, profileId);
99-
}
100-
101103
if (configLoadInterrupted || !newConfig) {
102104
return { errors, config: newConfig, configLoadInterrupted: true };
103105
}
104106

107+
// TODO using config result but result with non-fatal errors is an antipattern?
108+
// Remove ability have undefined errors, just have an array
109+
errors = [...(errors ?? [])];
110+
111+
// Rectify model selections for each role
112+
newConfig = rectifySelectedModelsFromGlobalContext(newConfig, profileId);
113+
114+
// Add things from MCP servers
115+
const mcpManager = MCPManagerSingleton.getInstance();
116+
const mcpServerStatuses = mcpManager.getStatuses();
117+
118+
// Slightly hacky just need connection's client to make slash command for now
119+
const serializableStatuses = mcpServerStatuses.map((server) => {
120+
const { client, ...rest } = server;
121+
return rest;
122+
});
123+
newConfig.mcpServerStatuses = serializableStatuses;
124+
125+
for (const server of mcpServerStatuses) {
126+
if (server.status === "connected") {
127+
const serverTools: Tool[] = server.tools.map((tool) => ({
128+
displayTitle: server.name + " " + tool.name,
129+
function: {
130+
description: tool.description,
131+
name: tool.name,
132+
parameters: tool.inputSchema,
133+
},
134+
faviconUrl: server.faviconUrl,
135+
readonly: false,
136+
type: "function" as const,
137+
wouldLikeTo: "",
138+
uri: encodeMCPToolUri(server.id, tool.name),
139+
group: server.name,
140+
}));
141+
newConfig.tools.push(...serverTools);
142+
143+
const serverSlashCommands = server.prompts.map((prompt) =>
144+
constructMcpSlashCommand(
145+
server.client,
146+
prompt.name,
147+
prompt.description,
148+
prompt.arguments?.map((a: any) => a.name),
149+
),
150+
);
151+
newConfig.slashCommands.push(...serverSlashCommands);
152+
153+
const submenuItems = server.resources.map((resource) => ({
154+
title: resource.name,
155+
description: resource.description ?? resource.name,
156+
id: resource.uri,
157+
icon: server.faviconUrl,
158+
}));
159+
if (submenuItems.length > 0) {
160+
const serverContextProvider = new MCPContextProvider({
161+
submenuItems,
162+
mcpId: server.id,
163+
});
164+
newConfig.contextProviders.push(serverContextProvider);
165+
}
166+
}
167+
}
168+
169+
// Detect duplicate tool names
170+
const counts: Record<string, number> = {};
171+
newConfig.tools.forEach((tool) => {
172+
if (counts[tool.function.name]) {
173+
counts[tool.function.name] = counts[tool.function.name] + 1;
174+
} else {
175+
counts[tool.function.name] = 1;
176+
}
177+
});
178+
Object.entries(counts).forEach(([toolName, count]) => {
179+
if (count > 1) {
180+
errors!.push({
181+
fatal: false,
182+
message: `Duplicate (${count}) tools named "${toolName}" detected. Permissions will conflict and usage may be unpredictable`,
183+
});
184+
}
185+
});
186+
105187
newConfig.allowAnonymousTelemetry =
106188
newConfig.allowAnonymousTelemetry && (await ide.isTelemetryEnabled());
107189

core/config/validation.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ConfigValidationError } from "@continuedev/config-yaml";
2+
23
import { ModelDescription, SerializedContinueConfig } from "../";
34
import { Telemetry } from "../util/posthog";
45

0 commit comments

Comments
 (0)