Skip to content
This repository was archived by the owner on May 20, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3330,6 +3330,22 @@
"experimental"
]
},
"github.copilot.chat.githubMcpServer.channel": {
"type": "string",
"default": "stable",
"enum": [
"stable",
"insiders"
],
"enumDescriptions": [
"%github.copilot.config.githubMcpServer.channel.stable%",
"%github.copilot.config.githubMcpServer.channel.insiders%"
],
"markdownDescription": "%github.copilot.config.githubMcpServer.channel%",
"tags": [
"experimental"
]
},
"github.copilot.chat.switchAgent.enabled": {
"type": "boolean",
"default": false,
Expand Down
3 changes: 3 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,9 @@
"github.copilot.config.githubMcpServer.toolsets": "Specify toolsets to use from the GitHub MCP Server. [Learn more](https://aka.ms/vscode-gh-mcp-toolsets).",
"github.copilot.config.githubMcpServer.readonly": "Enable read-only mode for the GitHub MCP Server. When enabled, only read tools are available. [Learn more](https://aka.ms/vscode-gh-mcp-readonly).",
"github.copilot.config.githubMcpServer.lockdown": "Enable lockdown mode for the GitHub MCP Server. When enabled, hides public issue details created by users without push access. [Learn more](https://aka.ms/vscode-gh-mcp-lockdown).",
"github.copilot.config.githubMcpServer.channel": "Select the channel for the GitHub MCP Server. When set to Insiders, enables access to experimental features that may change or be removed based on community feedback. [Learn more](https://aka.ms/vscode-gh-mcp-channel).",
"github.copilot.config.githubMcpServer.channel.stable": "Use the stable version of the GitHub MCP Server.",
"github.copilot.config.githubMcpServer.channel.insiders": "Connect to the Insiders version of the GitHub MCP Server with experimental features.",
"copilot.tools.runSubagent.name": "Run Subagent",
"copilot.tools.runSubagent.description": "Runs a task within an isolated subagent context. Enables efficient organization of tasks and context window management.",
"copilot.tools.searchSubagent.name": "Search Subagent",
Expand Down
14 changes: 14 additions & 0 deletions src/extension/githubMcp/common/githubMcpDefinitionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ export class GitHubMcpDefinitionProvider implements McpServerDefinitionProvider<
logService.debug('GitHubMcpDefinitionProvider: Configuration change affects GitHub MCP lockdown mode.');
return true;
}
// If they change the channel
if (e.affectsConfiguration(ConfigKey.GitHubMcpChannel.fullyQualifiedId)) {
logService.debug('GitHubMcpDefinitionProvider: Configuration change affects GitHub MCP channel.');
return true;
}
// If they change to GHE or GitHub.com
if (e.affectsConfiguration(ConfigKey.Shared.AuthProvider.fullyQualifiedId)) {
logService.debug('GitHubMcpDefinitionProvider: Configuration change affects GitHub auth provider.');
Expand Down Expand Up @@ -82,6 +87,10 @@ export class GitHubMcpDefinitionProvider implements McpServerDefinitionProvider<
return this.configurationService.getConfig<boolean>(ConfigKey.GitHubMcpLockdown);
}

private get channel(): ConfigKey.GitHubMcpChannelValue {
return this.configurationService.getConfig<ConfigKey.GitHubMcpChannelValue>(ConfigKey.GitHubMcpChannel);
}

private get gheConfig(): string | undefined {
return this.configurationService.getNonExtensionConfig<string>(EnterpriseURLConfig);
}
Expand All @@ -101,6 +110,7 @@ export class GitHubMcpDefinitionProvider implements McpServerDefinitionProvider<
const toolsets = this.toolsets.sort().join(',');
const readonly = this.readonly;
const lockdown = this.lockdown;
const channel = this.channel;
const isSignedIn = !!this.authenticationService.permissiveGitHubSession;

const basics = providerId === AuthProviderId.GitHubEnterprise
Expand All @@ -124,6 +134,10 @@ export class GitHubMcpDefinitionProvider implements McpServerDefinitionProvider<
headers['X-MCP-Lockdown'] = 'true';
version += '|lockdown';
}
if (channel === 'insiders') {
headers['X-MCP-Insiders'] = 'true';
version += '|insiders';
}
Comment thread
digitarald marked this conversation as resolved.
} else {
version = 'signedout';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ describe('GitHubMcpDefinitionProvider', () => {
toolsets?: string[];
readonly?: boolean;
lockdown?: boolean;
channel?: ConfigKey.GitHubMcpChannelValue;
hasPermissiveToken?: boolean;
}): Promise<GitHubMcpDefinitionProvider> {
const serviceCollection = new TestingServiceCollection();
Expand All @@ -103,6 +104,9 @@ describe('GitHubMcpDefinitionProvider', () => {
if (configOverrides?.lockdown !== undefined) {
await configService.setConfig(ConfigKey.GitHubMcpLockdown, configOverrides.lockdown);
}
if (configOverrides?.channel !== undefined) {
await configService.setConfig(ConfigKey.GitHubMcpChannel, configOverrides.channel);
}

serviceCollection.define(IConfigurationService, configService);
serviceCollection.define(ICopilotTokenStore, new SyncDescriptor(CopilotTokenStore));
Expand Down Expand Up @@ -251,6 +255,38 @@ describe('GitHubMcpDefinitionProvider', () => {
expect(definitions[0].version).toBe('default|readonly|lockdown');
});

test('includes X-MCP-Insiders header when channel is insiders', async () => {
const insidersProvider = await createProvider({ channel: 'insiders' });

const definitions = insidersProvider.provideMcpServerDefinitions();

expect(definitions[0].headers['X-MCP-Insiders']).toBe('true');
});

test('does not include X-MCP-Insiders header when channel is stable', async () => {
const stableProvider = await createProvider({ channel: 'stable' });

const definitions = stableProvider.provideMcpServerDefinitions();

expect(definitions[0].headers['X-MCP-Insiders']).toBeUndefined();
});

test('version includes insiders flag when channel is insiders', async () => {
const insidersProvider = await createProvider({ channel: 'insiders' });

const definitions = insidersProvider.provideMcpServerDefinitions();

expect(definitions[0].version).toBe('default|insiders');
});

test('version includes all flags when readonly, lockdown, and insiders are set', async () => {
const allFlagsProvider = await createProvider({ readonly: true, lockdown: true, channel: 'insiders' });

const definitions = allFlagsProvider.provideMcpServerDefinitions();

expect(definitions[0].version).toBe('default|readonly|lockdown|insiders');
});

test('version is just toolsets when readonly and lockdown are false', async () => {
const toolsets = ['issues', 'pull_requests'];
const normalProvider = await createProvider({ toolsets, readonly: false, lockdown: false });
Expand Down Expand Up @@ -327,6 +363,14 @@ describe('GitHubMcpDefinitionProvider', () => {

await eventPromise;
});

test('fires when channel configuration changes', async () => {
const eventPromise = Event.toPromise(provider.onDidChangeMcpServerDefinitions);

await configService.setConfig(ConfigKey.GitHubMcpChannel, 'insiders');

await eventPromise;
});
});

describe('edge cases', () => {
Expand Down
2 changes: 2 additions & 0 deletions src/platform/configuration/common/configurationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,8 @@ export namespace ConfigKey {
export const GitHubMcpToolsets = defineSetting<string[]>('chat.githubMcpServer.toolsets', ConfigType.Simple, ['default']);
export const GitHubMcpReadonly = defineSetting<boolean>('chat.githubMcpServer.readonly', ConfigType.Simple, false);
export const GitHubMcpLockdown = defineSetting<boolean>('chat.githubMcpServer.lockdown', ConfigType.Simple, false);
export type GitHubMcpChannelValue = 'stable' | 'insiders';
export const GitHubMcpChannel = defineSetting<GitHubMcpChannelValue>('chat.githubMcpServer.channel', ConfigType.Simple, 'stable');

export const GetSearchResultsViewSkill = defineSetting<boolean>('chat.getSearchViewResultsSkill.enabled', ConfigType.ExperimentBased, false);

Expand Down
Loading