Open the sample file and the mcp.json in a split editor when creating self-hosted MCP projects#4946
Open the sample file and the mcp.json in a split editor when creating self-hosted MCP projects#4946
Conversation
… self-hosted MCP projects
There was a problem hiding this comment.
Pull request overview
This PR updates the self-hosted MCP server project creation flow to automatically open .vscode/mcp.json and the downloaded sample tool source file side-by-side, improving discoverability and onboarding (per Issue #4882).
Changes:
- Open
.vscode/mcp.jsonin the editor after project creation. - Track a “sample tool file” during snippet download and open it in a split editor alongside
mcp.json. - Extend the project wizard context to carry the tracked sample tool file path.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/commands/createNewProject/mcpServerSteps/MCPOpenFileStep.ts |
Opens mcp.json and the sample tool file in specific editor columns to create a split view. |
src/commands/createNewProject/mcpServerSteps/MCPDownloadSnippetsExecuteStep.ts |
Tracks a candidate “sample tool file” path while downloading snippets (and returns destination paths from downloads). |
src/commands/createNewProject/IProjectWizardContext.ts |
Adds sampleToolFilePath to the MCP wizard context. |
| 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') { | ||
| await MCPDownloadSnippetsExecuteStep.downloadSingleFile({ | ||
| 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; | ||
| } |
There was a problem hiding this comment.
sampleToolFilePath is set inside a Promise.all over downloads. Because the downloads resolve in nondeterministic order, the "first" matching file picked here is effectively whichever finishes first (and can even be overwritten if multiple resolves hit before the flag is observed). Consider collecting candidate paths during the traversal and selecting deterministically after the parallel work completes (e.g., prefer known tool paths per language using item.path, or pick by stable sort order) rather than mutating context from concurrent tasks.
| // Track the first source file matching the server language as the sample tool file | ||
| if (!context.sampleToolFilePath && sourceExt && destPath.endsWith(sourceExt)) { | ||
| context.sampleToolFilePath = destPath; | ||
| } |
There was a problem hiding this comment.
Using destPath.endsWith(sourceExt) to identify the tool file is fairly loose and can select the wrong file as soon as the sample repo contains more than one source file for that language (and it also ignores the known expected paths from the issue, e.g. src/index.ts, hello.py, Tools/HelloTool.cs). Prefer matching on item.path against those known relative paths (with a fallback strategy) to make the file that opens predictable and aligned with the intended entrypoint.
There was a problem hiding this comment.
@copilot apply changes based on this feedback
There was a problem hiding this comment.
Applied in commit 5856acf. The changes:
- Refactored
downloadFilesRecursivelyto collect{itemPath, destPath}tuples and return them instead of mutatingcontextinside a concurrentPromise.all - Added a
knownToolPathsmap with the exact repo-relative entrypoints (src/index.ts,hello.py,Tools/HelloTool.cs) - Added a
selectSampleToolFilestatic method that first matches onitem.pathagainst the known paths, then falls back to a lexicographically-sorted extension-based search for determinism context.sampleToolFilePathis now assigned once after all downloads complete
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/vscode-azurefunctions/sessions/e0865c26-ca93-495e-8072-8412eb458e26 Co-authored-by: nturinski <5290572+nturinski@users.noreply.github.com>
sampleToolFilePathselection issue inMCPDownloadSnippetsExecuteStep.tsdownloadFilesRecursivelyto collect{itemPath, destPath}tuples and return them (no context mutation during parallel downloads)knownToolPathsmap for exact entrypoint matching per language (src/index.ts,hello.py,Tools/HelloTool.cs)selectSampleToolFilestatic method: exact known-path match first, lexicographically-sorted extension fallbackcontext.sampleToolFilePathonce after all downloads complete