Skip to content

Commit 9082fa1

Browse files
azure-sdkbenbp
andauthored
Sync eng/common directory with azure-sdk-tools for PR 10470 (#40805)
* Initialize azure sdk MCP conventions/automation * Fixes --------- Co-authored-by: Ben Broderick Phillips <[email protected]>
1 parent f7761cc commit 9082fa1

File tree

5 files changed

+300
-173
lines changed

5 files changed

+300
-173
lines changed

eng/common/mcp/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Azure SDK MCP Servers
2+
3+
This document details how to author, publish and use [MCP servers](https://github.com/modelcontextprotocol) for azure sdk team usage.
4+
5+
## Using the Azure SDK MCP Server
6+
7+
Run the below command to download and run the azure sdk engsys mcp server manually:
8+
9+
```
10+
<repo root>/eng/common/mcp/azure-sdk-mcp.ps1 -Run
11+
```
12+
13+
To install the mcp server for use within vscode copilot agent mode, run the following then launch vscode from the repository root.
14+
15+
```
16+
<repo root>/eng/common/mcp/azure-sdk-mcp.ps1 -UpdateVsCodeConfig
17+
```
18+
19+
*When updating the config the script will not overwrite any other server configs.*
20+
21+
The script will install the latest version of the azsdk cli executable from [tools releases](https://github.com/Azure/azure-sdk-tools/releases) and install it to `$HOME/.azure-sdk-mcp/azsdk`.
22+
23+
## Authoring an MCP server
24+
25+
MCP server code should be placed in [azure-sdk-tools/tools/mcp/dotnet](https://github.com/Azure/azure-sdk-tools/tree/main/tools/mcp/dotnet).
26+
27+
Azure SDK MCP servers should support [stdio and sse transports](https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse).
28+
29+
When running in copilot the default is stdio mode, but SSE is useful to support for external debugging.
30+
31+
### Developing MCP servers in C#
32+
33+
See the [C# MCP SDK](https://github.com/modelcontextprotocol/csharp-sdk)
34+
35+
Add an [SSE transport](https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples/AspNetCoreSseServer)
36+
37+
TODO: Add the azsdk-cli project to pull in MCP server dependencies from the repo
38+

eng/common/mcp/azure-sdk-mcp.ps1

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/bin/env pwsh
2+
3+
param(
4+
[string]$FileName = 'azsdk',
5+
[string]$Package = 'azsdk',
6+
[string]$Version, # Default to latest
7+
[string]$InstallDirectory = (Join-Path $HOME ".azure-sdk-mcp" "azsdk"),
8+
[string]$Repository = 'Azure/azure-sdk-tools',
9+
[switch]$Run,
10+
[switch]$UpdateVsCodeConfig,
11+
[switch]$Clean
12+
)
13+
14+
$ErrorActionPreference = "Stop"
15+
16+
. (Join-Path $PSScriptRoot '..' 'scripts' 'Helpers' 'AzSdkTool-Helpers.ps1')
17+
18+
if ($Clean) {
19+
Clear-Directory -Path $InstallDirectory
20+
}
21+
22+
if ($UpdateVsCodeConfig) {
23+
$vscodeConfigPath = $PSScriptRoot + "../../../.vscode/mcp.json"
24+
if (Test-Path $vscodeConfigPath) {
25+
$vscodeConfig = Get-Content -Raw $vscodeConfig | ConvertFrom-Json -AsHashtable
26+
}
27+
else {
28+
$vscodeConfig = @{}
29+
}
30+
$serverKey = "azure-sdk-mcp"
31+
$serverConfig = @{
32+
"type" = "stdio"
33+
"command" = "/home/ben/azs/azure-sdk-tools/eng/common/mcp/azure-sdk-mcp.ps1"
34+
}
35+
$orderedServers = [ordered]@{
36+
$serverKey = $serverConfig
37+
}
38+
if (-not $vscodeConfig.ContainsKey('servers')) {
39+
$vscodeConfig['servers'] = @{}
40+
}
41+
foreach ($key in $vscodeConfig.servers.Keys) {
42+
if ($key -ne $serverKey) {
43+
$orderedServers[$key] = $vscodeConfig.servers[$key]
44+
}
45+
}
46+
$vscodeConfig.servers = $orderedServers
47+
Write-Host "Updating vscode mcp config at $vscodeConfigPath"
48+
$vscodeConfig | ConvertTo-Json -Depth 10 | Set-Content -Path $vscodeConfig -Force
49+
}
50+
51+
$exe = Install-Standalone-Tool `
52+
-Version $Version `
53+
-FileName $FileName `
54+
-Package $Package `
55+
-Directory $InstallDirectory `
56+
-Repository $Repository
57+
58+
if ($Run) {
59+
Start-Process -FilePath $exe -NoNewWindow -Wait
60+
}
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
Set-StrictMode -Version 4
2+
3+
function Get-SystemArchitecture {
4+
$unameOutput = uname -m
5+
switch ($unameOutput) {
6+
"x86_64" { return "X86_64" }
7+
"aarch64" { return "ARM64" }
8+
"arm64" { return "ARM64" }
9+
default { throw "Unable to determine system architecture. uname -m returned $unameOutput." }
10+
}
11+
}
12+
13+
function Get-Package-Meta(
14+
[Parameter(mandatory = $true)]
15+
$FileName,
16+
[Parameter(mandatory = $true)]
17+
$Package
18+
) {
19+
$ErrorActionPreferenceDefault = $ErrorActionPreference
20+
$ErrorActionPreference = "Stop"
21+
22+
$AVAILABLE_BINARIES = @{
23+
"Windows" = @{
24+
"AMD64" = @{
25+
"system" = "Windows"
26+
"machine" = "AMD64"
27+
"file_name" = "$FileName-standalone-win-x64.zip"
28+
"executable" = "$Package.exe"
29+
}
30+
}
31+
"Linux" = @{
32+
"X86_64" = @{
33+
"system" = "Linux"
34+
"machine" = "X86_64"
35+
"file_name" = "$FileName-standalone-linux-x64.tar.gz"
36+
"executable" = "$Package"
37+
}
38+
"ARM64" = @{
39+
"system" = "Linux"
40+
"machine" = "ARM64"
41+
"file_name" = "$FileName-standalone-linux-arm64.tar.gz"
42+
"executable" = "$Package"
43+
}
44+
}
45+
"Darwin" = @{
46+
"X86_64" = @{
47+
"system" = "Darwin"
48+
"machine" = "X86_64"
49+
"file_name" = "$FileName-standalone-osx-x64.zip"
50+
"executable" = "$Package"
51+
}
52+
"ARM64" = @{
53+
"system" = "Darwin"
54+
"machine" = "ARM64"
55+
"file_name" = "$FileName-standalone-osx-arm64.zip"
56+
"executable" = "$Package"
57+
}
58+
}
59+
}
60+
61+
if ($IsWindows) {
62+
$os = "Windows"
63+
# we only support x64 on windows, if that doesn't work the platform is unsupported
64+
$machine = "AMD64"
65+
}
66+
elseif ($IsLinux) {
67+
$os = "Linux"
68+
$machine = Get-SystemArchitecture
69+
}
70+
elseif ($IsMacOS) {
71+
$os = "Darwin"
72+
$machine = Get-SystemArchitecture
73+
}
74+
else {
75+
$os = "unknown"
76+
}
77+
78+
$ErrorActionPreference = $ErrorActionPreferenceDefault
79+
80+
return $AVAILABLE_BINARIES[$os][$machine]
81+
}
82+
83+
function Clear-Directory ($path) {
84+
if (Test-Path -Path $path) {
85+
Remove-Item -Path $path -Recurse -Force
86+
}
87+
New-Item -ItemType Directory -Path $path -Force
88+
}
89+
90+
function isNewVersion(
91+
[Parameter(mandatory = $true)]
92+
$Version,
93+
[Parameter(mandatory = $true)]
94+
$Directory
95+
) {
96+
$savedVersionTxt = Join-Path $Directory "downloaded_version.txt"
97+
if (Test-Path $savedVersionTxt) {
98+
$result = (Get-Content -Raw $savedVersionTxt).Trim()
99+
100+
if ($result -eq $Version) {
101+
return $false
102+
}
103+
}
104+
105+
return $true
106+
}
107+
108+
<#
109+
.SYNOPSIS
110+
Installs a standalone version of an engsys tool.
111+
.PARAMETER Version
112+
The version of the tool to install. Requires a full version to be provided. EG "1.0.0-dev.20240617.1"
113+
.PARAMETER Directory
114+
The directory within which the exe will exist after this function invokes. Defaults to "."
115+
#>
116+
function Install-Standalone-Tool (
117+
[Parameter()]
118+
[string]$Version,
119+
[Parameter(mandatory = $true)]
120+
[string]$FileName,
121+
[Parameter(mandatory = $true)]
122+
[string]$Package,
123+
[Parameter()]
124+
[string]$Repository = "Azure/azure-sdk-tools",
125+
[Parameter()]
126+
$Directory = "."
127+
) {
128+
$ErrorActionPreference = "Stop"
129+
$PSNativeCommandUseErrorActionPreference = $true
130+
131+
$systemDetails = Get-Package-Meta -FileName $FileName -Package $Package
132+
133+
if (!(Test-Path $Directory) -and $Directory -ne ".") {
134+
New-Item -ItemType Directory -Path $Directory -Force | Out-Null
135+
}
136+
137+
$tag = "${Package}_${Version}"
138+
139+
if (!$Version -or $Version -eq "*") {
140+
Write-Host "Attempting to find latest version for package '$Package'"
141+
$releasesUrl = "https://api.github.com/repos/$Repository/releases"
142+
$releases = Invoke-RestMethod -Uri $releasesUrl
143+
$found = $false
144+
foreach ($release in $releases) {
145+
if ($release.tag_name -like "$Package*") {
146+
$tag = $release.tag_name
147+
$Version = $release.tag_name -replace "${Package}_", ""
148+
$found = $true
149+
break
150+
}
151+
}
152+
if ($found -eq $false) {
153+
throw "No release found for package '$Package'"
154+
}
155+
}
156+
157+
$downloadFolder = Resolve-Path $Directory
158+
$downloadUrl = "https://github.com/$Repository/releases/download/$tag/$($systemDetails.file_name)"
159+
$downloadFile = $downloadUrl.Split('/')[-1]
160+
$downloadLocation = Join-Path $downloadFolder $downloadFile
161+
$savedVersionTxt = Join-Path $downloadFolder "downloaded_version.txt"
162+
$executable_path = Join-Path $downloadFolder $systemDetails.executable
163+
164+
if (isNewVersion $version $downloadFolder) {
165+
Write-Host "Installing '$Package' '$Version' to '$downloadFolder' from $downloadUrl"
166+
Invoke-WebRequest -Uri $downloadUrl -OutFile $downloadLocation
167+
168+
if ($downloadFile -like "*.zip") {
169+
Expand-Archive -Path $downloadLocation -DestinationPath $downloadFolder -Force
170+
}
171+
elseif ($downloadFile -like "*.tar.gz") {
172+
tar -xzf $downloadLocation -C $downloadFolder
173+
}
174+
else {
175+
throw "Unsupported file format"
176+
}
177+
178+
# Remove the downloaded file after extraction
179+
Remove-Item -Path $downloadLocation -Force
180+
181+
# Record downloaded version
182+
Set-Content -Path $savedVersionTxt -Value $Version
183+
184+
# Set executable permissions if on macOS (Darwin)
185+
if ($IsMacOS) {
186+
chmod 755 $executable_path
187+
}
188+
}
189+
else {
190+
Write-Host "Target version '$Version' already present in target directory '$downloadFolder'"
191+
}
192+
193+
return $executable_path
194+
}

eng/common/testproxy/install-test-proxy.ps1

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,15 @@ param(
1616
$InstallDirectory
1717
)
1818

19-
. (Join-Path $PSScriptRoot test-proxy.ps1)
19+
. (Join-Path $PSScriptRoot '..' 'scripts' 'Helpers' 'AzSdkTool-Helpers.ps1')
2020

2121
Write-Host "Attempting to download and install version `"$Version`" into `"$InstallDirectory`""
2222

23-
Install-Standalone-TestProxy -Version $Version -Directory $InstallDirectory
23+
$exe = Install-Standalone-Tool `
24+
-Version $Version `
25+
-FileName "test-proxy" `
26+
-Package "Azure.Sdk.Tools.TestProxy" `
27+
-Directory $InstallDirectory
2428

25-
$PROXY_EXE = ""
26-
27-
if ($IsWindows) {
28-
$PROXY_EXE = Join-Path $InstallDirectory "Azure.Sdk.Tools.TestProxy.exe"
29-
} else {
30-
$PROXY_EXE = Join-Path $InstallDirectory "Azure.Sdk.Tools.TestProxy"
31-
}
32-
Write-Host "Downloaded test-proxy available at $PROXY_EXE."
33-
Write-Host "##vso[task.setvariable variable=PROXY_EXE]$PROXY_EXE"
29+
Write-Host "Downloaded test-proxy available at $exe."
30+
Write-Host "##vso[task.setvariable variable=PROXY_EXE]$exe"

0 commit comments

Comments
 (0)