Skip to content
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
1 change: 1 addition & 0 deletions src/commands/createNewProject/IProjectWizardContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export interface MCPProjectWizardContext extends IProjectWizardContext {
serverLanguage?: ProjectLanguage;
includeSnippets?: boolean;
sampleMcpRepoUrl?: string;
sampleToolFilePath?: string;
}

export type OpenBehavior = 'AddToWorkspace' | 'OpenInNewWindow' | 'OpenInCurrentWindow' | 'AlreadyOpen' | 'DontOpen';
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,68 @@ export class MCPDownloadSnippetsExecuteStep extends AzureWizardExecuteStepWithAc
progress.report({ message: localize('downloadingSampleCodeExecute', 'Downloading sample server code...') });
const sampleMcpRepoUrl: string = nonNullProp(context, 'sampleMcpRepoUrl');
const sampleFiles: GitHubFileMetadata[] = await feedUtils.getJsonFeed(context, sampleMcpRepoUrl);
await this.downloadFilesRecursively(context, sampleFiles, context.projectPath);
const downloadedFiles = await this.downloadFilesRecursively(context, sampleFiles, context.projectPath);
context.sampleToolFilePath = MCPDownloadSnippetsExecuteStep.selectSampleToolFile(context.serverLanguage, downloadedFiles);
}

private async downloadFilesRecursively(context: MCPProjectWizardContext, items: GitHubFileMetadata[], basePath: string): Promise<void> {
// Known entrypoint paths relative to the root of each language's sample GitHub repository
private static readonly knownToolPaths: Partial<Record<ProjectLanguage, string>> = {
[ProjectLanguage.TypeScript]: 'src/index.ts',
[ProjectLanguage.Python]: 'hello.py',
[ProjectLanguage.CSharp]: 'Tools/HelloTool.cs',
};

private static readonly languageExtensions: Partial<Record<ProjectLanguage, string>> = {
[ProjectLanguage.TypeScript]: '.ts',
[ProjectLanguage.Python]: '.py',
[ProjectLanguage.CSharp]: '.cs',
};

private static selectSampleToolFile(
language: ProjectLanguage | undefined,
files: { itemPath: string; destPath: string }[]
): string | undefined {
if (!language) {
return undefined;
}

// Prefer the known entrypoint path for the language
const knownPath = MCPDownloadSnippetsExecuteStep.knownToolPaths[language];
if (knownPath) {
const match = files.find(f => f.itemPath === knownPath);
if (match) {
return match.destPath;
}
}

// Fallback: any file with the right extension, sorted for determinism
const sourceExt = MCPDownloadSnippetsExecuteStep.languageExtensions[language];
if (sourceExt) {
const candidates = files
.filter(f => f.destPath.endsWith(sourceExt))
.sort((a, b) => a.itemPath.localeCompare(b.itemPath));
return candidates[0]?.destPath;
}

return undefined;
}

private async downloadFilesRecursively(
context: MCPProjectWizardContext,
items: GitHubFileMetadata[],
basePath: string
): Promise<{ itemPath: string; destPath: string }[]> {
const downloadedFiles: { itemPath: string; destPath: string }[] = [];
// Download all files and directories at this level in parallel
await Promise.all(items.map(async (item) => {
if (item.type === 'file') {
await MCPDownloadSnippetsExecuteStep.downloadSingleFile({
const destPath = await MCPDownloadSnippetsExecuteStep.downloadSingleFile({
context, item,
destDirPath: basePath,
serverLanguage: context.serverLanguage,
projectName: path.basename(context.projectPath)
});
downloadedFiles.push({ itemPath: item.path, destPath });
} else if (item.type === 'dir') {
// Create directory
const dirPath: string = path.join(basePath, item.name);
Expand All @@ -58,9 +107,11 @@ export class MCPDownloadSnippetsExecuteStep extends AzureWizardExecuteStepWithAc
const dirContents: GitHubFileMetadata[] = parseJson<GitHubFileMetadata[]>(nonNullProp(response, 'bodyAsText'));

// Recursively download directory contents
await this.downloadFilesRecursively(context, dirContents, dirPath);
const subFiles = await this.downloadFilesRecursively(context, dirContents, dirPath);
downloadedFiles.push(...subFiles);
}
}));
return downloadedFiles;
}

public shouldExecute(context: MCPProjectWizardContext): boolean {
Expand All @@ -73,7 +124,7 @@ export class MCPDownloadSnippetsExecuteStep extends AzureWizardExecuteStepWithAc
destDirPath: string,
projectName: string
serverLanguage?: ProjectLanguage,
}): Promise<void> {
}): Promise<string> {
const { context, item, destDirPath, serverLanguage, projectName } = options;
const fileUrl: string = item.download_url;
let destinationPath: string = path.join(destDirPath, item.name);
Expand All @@ -92,5 +143,6 @@ export class MCPDownloadSnippetsExecuteStep extends AzureWizardExecuteStepWithAc
}
}
await AzExtFsExtra.writeFile(destinationPath, fileContent ?? '');
return destinationPath;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,26 @@

import { AzExtFsExtra, AzureWizardExecuteStep } from '@microsoft/vscode-azext-utils';
import * as path from 'path';
import { Uri, window, workspace } from 'vscode';
import { Uri, ViewColumn, window, workspace } from 'vscode';
import { type MCPProjectWizardContext } from '../IProjectWizardContext';

export class MCPOpenFileStep extends AzureWizardExecuteStep<MCPProjectWizardContext> {
public priority: number = 240; // Execute before OpenFolderStep (priority 250) but after other project setup

public async execute(context: MCPProjectWizardContext): Promise<void> {
const mcpJsonFilePath: string = path.join(context.projectPath, '.vscode', 'mcp.json');

if (await AzExtFsExtra.pathExists(mcpJsonFilePath)) {
const mcpJsonFile = await workspace.openTextDocument(Uri.file(mcpJsonFilePath));
await window.showTextDocument(mcpJsonFile, { preview: false });
}

// Open sample tool file in a side-by-side editor
if (context.sampleToolFilePath) {
if (await AzExtFsExtra.pathExists(context.sampleToolFilePath)) {
const doc = await workspace.openTextDocument(Uri.file(context.sampleToolFilePath));
await window.showTextDocument(doc, { preview: false, viewColumn: ViewColumn.Beside });
}
}
}

public shouldExecute(context: MCPProjectWizardContext): boolean {
Expand Down
Loading