-
Notifications
You must be signed in to change notification settings - Fork 149
Expand file tree
/
Copy pathMCPDownloadSnippetsExecuteStep.ts
More file actions
108 lines (98 loc) · 6.05 KB
/
MCPDownloadSnippetsExecuteStep.ts
File metadata and controls
108 lines (98 loc) · 6.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzExtFsExtra, AzureWizardExecuteStepWithActivityOutput, type IActionContext } from '@microsoft/vscode-azext-utils';
import * as path from 'path';
import { type Progress } from 'vscode';
import { hostFileName, ProjectLanguage, type GitHubFileMetadata } from '../../../constants';
import { localize } from "../../../localize";
import { feedUtils } from '../../../utils/feedUtils';
import { nonNullProp } from '../../../utils/nonNull';
import { parseJson } from '../../../utils/parseJson';
import { requestUtils } from '../../../utils/requestUtils';
import { type MCPProjectWizardContext } from '../IProjectWizardContext';
export class MCPDownloadSnippetsExecuteStep extends AzureWizardExecuteStepWithActivityOutput<MCPProjectWizardContext> {
stepName: string = 'MCPDownloadSampleCodeExecuteStep';
protected getTreeItemLabel(context: MCPProjectWizardContext): string {
return localize('downloadSampleCode', 'Downloading {0} sample server code', nonNullProp(context, 'serverLanguage'));
}
protected getOutputLogSuccess(context: MCPProjectWizardContext): string {
return localize('downloadSampleCodeSuccess', 'Successfully downloaded {0} sample server code', nonNullProp(context, 'serverLanguage'));
}
protected getOutputLogFail(context: MCPProjectWizardContext): string {
return localize('downloadSampleCodeFail', 'Failed to download {0} sample server code', nonNullProp(context, 'serverLanguage'));
}
protected getOutputLogProgress(context: MCPProjectWizardContext): string {
return localize('downloadingSampleCode', 'Downloading {0} sample server code...', nonNullProp(context, 'serverLanguage'));
}
public priority: number = 12;
public async execute(context: MCPProjectWizardContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
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);
}
private static readonly languageExtensions: Partial<Record<ProjectLanguage, string>> = {
[ProjectLanguage.TypeScript]: '.ts',
[ProjectLanguage.Python]: '.py',
[ProjectLanguage.CSharp]: '.cs',
};
private async downloadFilesRecursively(context: MCPProjectWizardContext, items: GitHubFileMetadata[], basePath: string): Promise<void> {
const sourceExt = context.serverLanguage ? MCPDownloadSnippetsExecuteStep.languageExtensions[context.serverLanguage] : undefined;
// Download all files and directories at this level in parallel
await Promise.all(items.map(async (item) => {
if (item.type === 'file') {
const destPath = await MCPDownloadSnippetsExecuteStep.downloadSingleFile({
context, item,
destDirPath: basePath,
serverLanguage: context.serverLanguage,
projectName: path.basename(context.projectPath)
});
// Track the first source file matching the server language as the sample tool file
if (!context.sampleToolFilePath && sourceExt && destPath.endsWith(sourceExt)) {
context.sampleToolFilePath = destPath;
}
} else if (item.type === 'dir') {
// Create directory
const dirPath: string = path.join(basePath, item.name);
await AzExtFsExtra.ensureDir(dirPath);
// Get directory contents
const response = await requestUtils.sendRequestWithExtTimeout(context, { method: 'GET', url: item.url });
const dirContents: GitHubFileMetadata[] = parseJson<GitHubFileMetadata[]>(nonNullProp(response, 'bodyAsText'));
// Recursively download directory contents
await this.downloadFilesRecursively(context, dirContents, dirPath);
}
}));
}
public shouldExecute(context: MCPProjectWizardContext): boolean {
return context.includeSnippets === true;
}
public static async downloadSingleFile(options: {
context: IActionContext,
item: GitHubFileMetadata,
destDirPath: string,
projectName: string
serverLanguage?: ProjectLanguage,
}): Promise<string> {
const { context, item, destDirPath, serverLanguage, projectName } = options;
const fileUrl: string = item.download_url;
let destinationPath: string = path.join(destDirPath, item.name);
const response = await requestUtils.sendRequestWithExtTimeout(context, { method: 'GET', url: fileUrl });
let fileContent = response.bodyAsText;
if (serverLanguage === ProjectLanguage.CSharp) {
if (item.name === hostFileName) {
// for C#, we need to replace the host.json
// "arguments": ["<path to the compiled DLL, e.g. HelloWorld.dll>"] with the name of the actual DLL
const dllName: string = `${projectName}.dll`;
fileContent = fileContent?.replace('<path to the compiled DLL, e.g. HelloWorld.dll>', dllName);
} else if (item.name === 'dotnet.csproj') {
// for C#, the project file needs to be named after the project folder
const csprojFileName: string = `${projectName}.csproj`;
destinationPath = path.join(destDirPath, csprojFileName);
}
}
await AzExtFsExtra.writeFile(destinationPath, fileContent ?? '');
return destinationPath;
}
}