Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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,64 @@
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;

Check warning on line 59 in src/commands/createNewProject/mcpServerSteps/MCPDownloadSnippetsExecuteStep.ts

View workflow job for this annotation

GitHub Actions / Build / Build

Expected { after 'if' condition

// 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;

Check warning on line 65 in src/commands/createNewProject/mcpServerSteps/MCPDownloadSnippetsExecuteStep.ts

View workflow job for this annotation

GitHub Actions / Build / Build

Expected { after 'if' condition
}

// 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 +103,11 @@
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 +120,7 @@
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 +139,6 @@
}
}
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