Skip to content

Add support to publish servers as MCP Bundles#1681

Open
vcolin7 wants to merge 40 commits intomainfrom
vcolin7/test-mcpb-signing
Open

Add support to publish servers as MCP Bundles#1681
vcolin7 wants to merge 40 commits intomainfrom
vcolin7/test-mcpb-signing

Conversation

@vcolin7
Copy link
Contributor

@vcolin7 vcolin7 commented Feb 9, 2026

What does this PR do?

This PR implements end-to-end MCPB (MCP Bundle) packaging and signing infrastructure using Microsoft's ESRP (Enterprise Signing and Release Pipeline) service. MCPB is a standardized bundle format for distributing MCP servers, similar to Chrome extensions (.crx) or VS Code extensions (.vsix).

Documentation

PowerShell Scripts

  • Added:
    • Pack-Mcpb.ps1 - Packages trimmed and self-contained server binaries into MCPB format
    • Stage-McpbForSigning.ps1 - Prepares MCPB files for ESRP signing
    • Apply-McpbSignatures.ps1 - Applies ESRP detached signatures to MCPB files
    • Verify-McpbSignatures.ps1 - Validates MCPB signatures
    • Update-ServerJsonMcpbHashes.ps1 - Updates server.json with MCPB hashes
  • Updated:
    • New-ServerJson.ps1 - Added entries for MCPB files

Pipeline Templates

  • Added:
    • pack-and-sign-mcpb.yml - Main packaging and signing job
    • release-mcpb.yml - Release MCPB files to GitHub releases
  • Updated:
    • common.yml
    • release.yml
    • sign-and-pack.yml
    • update-mcp-repository.yml
    • Each server's build.yml
    • 1es-reditect - Fixed issue with Go package downloads. Module downloads now go through Microsoft's internal proxy server instead of going directly to the public internet (proxy.golang.org).

Server Manifests

  • Added a manifest.json for all servers. These files serve as a base that gets updated during release to include the most recent server metadata (version, tools, etc.).
  • Updated server.json for Azure and Template servers to list MCP Bundles in the official MCP registry.

Testing

  • MCPB packaging produces valid bundles for all servers
  • ESRP signing workflow completes successfully
  • mcpb verify validates signed bundles
  • GitHub release includes signed MCPB files
  • Official MCP Registry shows MCPB entries for Template MCP Server
image

GitHub issue number?

Fixes: #128

Pre-merge Checklist

  • Required for All PRs
    • Read contribution guidelines
    • PR title clearly describes the change
    • Commit history is clean with descriptive messages (cleanup guide)
    • Added comprehensive tests for new/modified functionality
    • Updated servers/Azure.Mcp.Server/CHANGELOG.md and/or servers/Fabric.Mcp.Server/CHANGELOG.md for product changes (features, bug fixes, UI/UX, updated dependencies)
  • For MCP tool changes:
    • One tool per PR: This PR adds or modifies only one MCP tool for faster review cycles
    • Updated servers/Azure.Mcp.Server/README.md and/or servers/Fabric.Mcp.Server/README.md documentation
    • Validate README.md changes using script at eng/scripts/Process-PackageReadMe.ps1. See Package README
    • Updated command list in /servers/Azure.Mcp.Server/docs/azmcp-commands.md and/or /docs/fabric-commands.md
    • Run .\eng\scripts\Update-AzCommandsMetadata.ps1 to update tool metadata in azmcp-commands.md (required for CI)
    • For new or modified tool descriptions, ran ToolDescriptionEvaluator and obtained a score of 0.4 or more and a top 3 ranking for all related test prompts
    • For tools with new names, including new tools or renamed tools, update consolidated-tools.json
    • For new tools associated with Azure services or publicly available tools/APIs/products, add URL to documentation in the PR description
  • Extra steps for Azure MCP Server tool changes:
    • Updated test prompts in /servers/Azure.Mcp.Server/docs/e2eTestPrompts.md
    • 👉 For Community (non-Microsoft team member) PRs:
      • Security review: Reviewed code for security vulnerabilities, malicious code, or suspicious activities before running tests (crypto mining, spam, data exfiltration, etc.)
      • Manual tests run: added comment /azp run mcp - pullrequest - live to run Live Test Pipeline

vcolin7 and others added 30 commits January 30, 2026 17:04
@vcolin7 vcolin7 force-pushed the vcolin7/test-mcpb-signing branch from 1082b6f to c107d61 Compare February 9, 2026 21:18
@vcolin7 vcolin7 marked this pull request as ready for review February 9, 2026 21:24
@vcolin7 vcolin7 requested a review from a team as a code owner February 9, 2026 21:24
Copilot AI review requested due to automatic review settings February 9, 2026 21:24
@vcolin7 vcolin7 requested review from a team as code owners February 9, 2026 21:24
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces end-to-end infrastructure to package MCP servers as MCP Bundles (.mcpb), sign them via ESRP detached PKCS#7 signing, publish bundles to GitHub Releases, and surface MCPB entries in server.json (including SHA256 hash updates during publishing).

Changes:

  • Added MCPB packaging/signing PowerShell scripts and new pipeline job templates to pack, sign, verify, and publish .mcpb artifacts.
  • Added per-server MCPB manifests/icons and updated server registry metadata (server.json) to include MCPB package entries.
  • Added/updated documentation describing MCPB packaging/signing and updated engineering docs/pipeline parameters to support PackageMCPB.

Reviewed changes

Copilot reviewed 24 out of 28 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
servers/Template.Mcp.Server/server.json Adds MCPB package entries (URLs + SHA placeholders) to registry metadata.
servers/Template.Mcp.Server/mcpb/servericon.png Adds MCPB icon asset for Template server.
servers/Template.Mcp.Server/mcpb/manifest.json Adds MCPB bundle manifest for Template server.
servers/Template.Mcp.Server/build.yml Introduces PackageMCPB parameter wiring for Template pipeline.
servers/Fabric.Mcp.Server/mcpb/servericon.png Adds MCPB icon asset for Fabric server.
servers/Fabric.Mcp.Server/mcpb/manifest.json Adds MCPB bundle manifest for Fabric server.
servers/Fabric.Mcp.Server/build.yml Introduces PackageMCPB parameter wiring for Fabric pipeline.
servers/Azure.Mcp.Server/server.json Adds MCPB package entries (URLs + SHA placeholders) to registry metadata.
servers/Azure.Mcp.Server/mcpb/servericon.png Adds MCPB icon asset for Azure server.
servers/Azure.Mcp.Server/mcpb/manifest.json Adds MCPB bundle manifest for Azure server.
servers/Azure.Mcp.Server/changelog-entries/1770669516879.yaml Adds changelog entry noting MCPB support.
servers/Azure.Mcp.Server/build.yml Enables PackageMCPB by default for Azure pipeline.
eng/scripts/Verify-McpbSignatures.ps1 Adds MCPB signature verification script (mcpb info/verify).
eng/scripts/Update-ServerJsonMcpbHashes.ps1 Adds script to compute SHA256 for signed bundles and update server.json.
eng/scripts/Stage-McpbForSigning.ps1 Adds staging script for ESRP detached signing workflow.
eng/scripts/Pack-Mcpb.ps1 Adds packaging script to generate .mcpb bundles from trimmed binaries + manifests.
eng/scripts/New-ServerJson.ps1 Updates server.json generation to populate MCPB download URLs.
eng/scripts/Apply-McpbSignatures.ps1 Adds script to embed detached PKCS#7 signatures into MCPB format.
eng/pipelines/templates/jobs/update-mcp-repository.yml Updates MCP repository publish job to apply MCPB SHA256 hashes.
eng/pipelines/templates/jobs/sign-and-pack.yml Adds PackageMCPB parameter and MCPB pack/sign job inclusion.
eng/pipelines/templates/jobs/release.yml Adds MCPB publishing job and wires MCPB into release flow.
eng/pipelines/templates/jobs/mcpb/release-mcpb.yml Adds job template to upload signed .mcpb assets to GitHub Releases.
eng/pipelines/templates/jobs/mcpb/pack-and-sign-mcpb.yml Adds job template to pack, ESRP-sign, apply signatures, and verify MCPB bundles.
eng/pipelines/templates/common.yml Plumbs PackageMCPB parameter through common pipeline template.
eng/pipelines/templates/1es-redirect.yml Enables 1ES golang internal module proxy feature flag.
eng/README.md Documents MCPB artifact type and related scripts/pipelines.
docs/design/mcpb-packaging-and-signing-via-esrp.md Adds detailed design document for MCPB packaging/signing via ESRP.
.gitignore Ignores .vscode/settings.json.


# Note: --repo is required because checkout is not allowed in release jobs,
# so gh cannot determine the repository from the git context
& gh release upload $tag $mcpb.FullName --repo microsoft/mcp --clobber
Copy link
Member

@hallipr hallipr Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will break when we move to immutable releases. If we need to ensure the files are part of the GitHub release, we should come up with a scheme to include them in some binaries_github artifact, then have the generic release job upload everything in that artifact to GH when the release is created.

@@ -83,9 +91,12 @@ jobs:
- template: /eng/pipelines/templates/jobs/update-mcp-repository.yml
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does update-mcp-repository know if it needs to update a docker entry without a PackageDocker parameter?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default, it assumes we ship them all at the same time; that there aren't partial updates to our public releases. We would have to modify the script if we are expecting partial updates.

Comment on lines +97 to +121
$signedContent = New-Object byte[] ($mcpbContent.Length + $sigV1MarkerBytes.Length + $lengthBytes.Length + $signatureBytes.Length + $sigEndMarkerBytes.Length)

$offset = 0

# Copy MCPB content
[Array]::Copy($mcpbContent, 0, $signedContent, $offset, $mcpbContent.Length)
$offset += $mcpbContent.Length

# Copy MCPB_SIG_V1 marker
[Array]::Copy($sigV1MarkerBytes, 0, $signedContent, $offset, $sigV1MarkerBytes.Length)
$offset += $sigV1MarkerBytes.Length

# Copy length prefix
[Array]::Copy($lengthBytes, 0, $signedContent, $offset, $lengthBytes.Length)
$offset += $lengthBytes.Length

# Copy signature
[Array]::Copy($signatureBytes, 0, $signedContent, $offset, $signatureBytes.Length)
$offset += $signatureBytes.Length

# Copy MCPB_SIG_END marker
[Array]::Copy($sigEndMarkerBytes, 0, $signedContent, $offset, $sigEndMarkerBytes.Length)

# Write signed file
[System.IO.File]::WriteAllBytes($OutputFile, $signedContent)
Copy link
Member

@hallipr hallipr Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're already working in .net types, we could use List<byte> and avoid all the offset math by leaning on .AddRange(bytes)

i.e.

Suggested change
$signedContent = New-Object byte[] ($mcpbContent.Length + $sigV1MarkerBytes.Length + $lengthBytes.Length + $signatureBytes.Length + $sigEndMarkerBytes.Length)
$offset = 0
# Copy MCPB content
[Array]::Copy($mcpbContent, 0, $signedContent, $offset, $mcpbContent.Length)
$offset += $mcpbContent.Length
# Copy MCPB_SIG_V1 marker
[Array]::Copy($sigV1MarkerBytes, 0, $signedContent, $offset, $sigV1MarkerBytes.Length)
$offset += $sigV1MarkerBytes.Length
# Copy length prefix
[Array]::Copy($lengthBytes, 0, $signedContent, $offset, $lengthBytes.Length)
$offset += $lengthBytes.Length
# Copy signature
[Array]::Copy($signatureBytes, 0, $signedContent, $offset, $signatureBytes.Length)
$offset += $signatureBytes.Length
# Copy MCPB_SIG_END marker
[Array]::Copy($sigEndMarkerBytes, 0, $signedContent, $offset, $sigEndMarkerBytes.Length)
# Write signed file
[System.IO.File]::WriteAllBytes($OutputFile, $signedContent)
$signedContent = [System.Collections.Generic.List[byte]]::new()
$signedContent.AddRange($mcpbContent)
$signedContent.AddRange($sigV1MarkerBytes)
$signedContent.AddRange($lengthBytes)
$signedContent.AddRange($signatureBytes)
$signedContent.AddRange($sigEndMarkerBytes)
[System.IO.File]::WriteAllBytes($OutputFile, $signedContent)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have a server icon in each server directory and we refer to it in each server's csproj. New-BuildInfo is responsible for ensuring that the csproj relative path is converted to a repo-root relative path in buildInfo.json.

The packaging manifests refer to the image in a static location and the packaging scripts use the server's imagePath property to copy the server image into that location:

https://github.com/microsoft/mcp/blob/main/servers/Azure.Mcp.Server/images/azureicon.png

vscode/package.json

    "icon": "resources/package-icon.png",

Pack-Vsix.ps1

        Write-Host "Copying server icon from $($server.packageIcon) to $tempPath/resources/package-icon.png"
        New-Item -Path "$tempPath/resources" -ItemType Directory -Force | Out-Null
        Copy-Item -Path "$RepoRoot/$($server.packageIcon)" -Destination "$tempPath/resources/package-icon.png" -Force

}

$signedCount = 0
$failedCount = 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we keep track of the failed items as objects so we don't have to recurse through the file directory multiple times? (powershell does support classes.)

$platformKey = $Matches[1]

# Map placeholder suffix to MCPB filename convention
$platformMap = @{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should go into New-BuildInfo.ps1. I Believe we wanted to keep all these different platform monikers in one location.

continue
}

Write-Host "`n========================================" -ForegroundColor Cyan
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use LogInfo since we imported the common.ps1

}

# Map platform names to their placeholder suffixes
$platformToPlaceholder = @{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment about using build_info for keeping these in one location.


# Main script logic
if (!(Test-Path $ArtifactsPath)) {
LogError "Staging directory not found: $ArtifactsPath"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Artifacts directory rather than staging directory?

$identifier = $package.identifier

# Extract platform from placeholder (e.g., "WinX64" from "<<McpbUrlWinX64>>")
if ($identifier -match '<<McpbUrl(\w+)>>') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking through server.json for the source of truth for package information seems a little fragile. I feel like we could continue the trend of keeping these as properties in the csproj and derive the platforms supported from it... like

<McbcPlatforms>win-x64,mac-x64</McpbPlatforms>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

TSG/Docs: How to change account or tenant?

3 participants