Skip to content

Packaging & Distribution

A Repl app is a standard .NET console application — it packages like any other. The main distribution channels are global tools (NuGet), MCP server registry, and self-contained binaries.

The most common distribution model for developer-facing tools. Users install once with dotnet tool install and your app is available globally.

  1. Configure the project

    <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <PackAsTool>true</PackAsTool>
    <ToolCommandName>myapp</ToolCommandName>
    <PackageId>MyCompany.MyApp</PackageId>
    <Version>1.0.0</Version>
    </PropertyGroup>
  2. Pack

    Terminal window
    dotnet pack -c Release
  3. Install locally for testing

    Terminal window
    dotnet tool install --global --add-source ./nupkg MyCompany.MyApp
    myapp --version
  4. Publish to NuGet.org

    Terminal window
    dotnet nuget push ./nupkg/MyCompany.MyApp.1.0.0.nupkg \
    --api-key $NUGET_API_KEY \
    --source https://api.nuget.org/v3/index.json

User install:

Terminal window
dotnet tool install -g MyCompany.MyApp

If your app exposes MCP (app.UseMcpServer()), set PackageType=McpServer so NuGet.org and MCP-aware clients like VS Code can discover and install it automatically.

<PropertyGroup>
<PackAsTool>true</PackAsTool>
<ToolCommandName>myapp</ToolCommandName>
<PackageType>McpServer</PackageType>
</PropertyGroup>

This file lives at .mcp/server.json in your repository. It tells registries and clients how to discover and invoke the server. The schema has evolved significantly — use the current version:

{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
"name": "io.github.myorg/myapp",
"version": "1.0.0",
"description": "Contacts management MCP server.",
"title": "MyApp",
"websiteUrl": "https://myorg.github.io/myapp",
"repository": {
"url": "https://github.com/myorg/myapp",
"source": "github"
},
"packages": [
{
"registryType": "nuget",
"registryBaseUrl": "https://api.nuget.org",
"identifier": "MyOrg.MyApp",
"version": "1.0.0",
"runtimeHint": "dnx",
"transport": {
"type": "stdio"
},
"packageArguments": [
{ "type": "positional", "value": "mcp" },
{ "type": "positional", "value": "serve" }
]
}
]
}

Required: name (reverse-DNS format org/repo), description, version; inside packages[]: registryType, identifier, transport.

runtimeHint: "dnx" — clients invoke the tool via dnx MyOrg.MyApp@1.0.0 --yes rather than the installed global tool name. This works without a prior dotnet tool install.

packageArguments — positional args appended to the invocation. Repl activates stdio MCP mode when it receives mcp serve.

If your server requires secrets (API keys, connection strings), declare them so clients can prompt the user at install time:

"environmentVariables": [
{
"name": "MYAPP_API_KEY",
"description": "API key for the backing service.",
"isRequired": true,
"isSecret": true
}
]
<ItemGroup>
<None Include=".mcp/server.json" Pack="true" PackagePath=".mcp/" />
</ItemGroup>

After publishing, NuGet.org generates a Connect snippet for VS Code under the MCP tab on your package page — clients can install the server in one click.


For shipping to machines without .NET installed:

Terminal window
dotnet publish -c Release \
-r win-x64 \
--self-contained \
-p:PublishSingleFile=true \
-p:PublishTrimmed=true
PlatformRuntime ID
Windows x64win-x64
macOS ARMosx-arm64
Linux x64linux-x64

Repl Toolkit itself uses Nerdbank.GitVersioning — version numbers are derived from git history automatically. The same approach works well for your apps:

Terminal window
dotnet add package Nerdbank.GitVersioning

version.json:

{
"version": "1.0",
"publicReleaseRefSpec": ["^refs/heads/main$", "^refs/heads/release/.*$"]
}

Version is then 1.0.{commit-height} on release branches, 1.0.0-dev.{height} on other branches.


A minimal GitHub Actions release workflow:

name: Release
on:
push:
branches: [main, 'release/**']
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # needed for Nerdbank.GitVersioning
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.x'
- run: dotnet pack -c Release -o ./nupkg
- name: Publish to NuGet
if: startsWith(github.ref, 'refs/heads/release/')
run: |
dotnet nuget push ./nupkg/*.nupkg \
--api-key ${{ secrets.NUGET_API_KEY }} \
--source https://api.nuget.org/v3/index.json \
--skip-duplicate

AssetPurpose
.mcp/server.jsonMCP registry discovery
README.mdNuGet landing page
Icon (.png)NuGet gallery icon
CHANGELOG.mdRelease notes
Shell completion note in READMEGuide users to run myapp completion install

  • PackAsTool = true and ToolCommandName set
  • Version sourced from git (or explicit)
  • README.md has a quick-start snippet
  • dotnet tool install -g <pkg> tested clean on a fresh machine
  • myapp --help works after global install
  • myapp completion install works for your target shells
  • If MCP: myapp mcp serve starts without error, .mcp/server.json is present
  • NuGet API key stored in CI secrets, not in source