NuGet Trusted Publishing Setup
Set up NuGet trusted publishing on a GitHub Actions repo. Replaces long-lived API keys with OIDC-based short-lived tokens — no secrets to rotate or leak.
Prerequisites
- GitHub Actions — this skill covers GitHub Actions setup only
- nuget.org account — the user needs access to create trusted publishing policies
When to Use This Skill
Use this skill when:
- Setting up trusted publishing for a NuGet package
- Migrating from
secrets.NUGET_API_KEYto OIDC-based publishing - Asked about keyless or secure NuGet publishing
- Creating a new NuGet publish workflow from scratch
- Asked to "remove NuGet API key" or "use NuGet/login"
- Setting up publishing for a dotnet tool, MCP server, or template package
- Asked about
NuGet/login@v1orid-token: write
Safety Rules
⚠️ Bail-out rule: If any phase fails after one fix attempt on an infrastructure/auth issue, stop and ask the user. Don't loop on environment problems.
⚠️ Never delete or overwrite without confirmation: Removing API key secrets, deleting tags/releases, removing workflow steps, or changing package IDs. NuGet package IDs are permanent — mistakes can't be undone.
Process
Fast-path for greenfield repos: When the user has a simple setup (one packable project, no existing publish workflow), don't gate on multi-turn assessment. Combine phases: create the workflow immediately, include nuget.org policy guidance, local pack recommendation, and filename-matching warning all in one response. The full phased process below is for complex or migration scenarios.
Phase 1: Assess
Inspect the repo and report findings before making any changes.
-
Find and classify packable projects — check
.csprojfiles andDirectory.Build.props(package metadata is often set repo-wide). Classify in this order (earlier matches win):<PackageType>Template</PackageType>→ Template<PackageType>McpServer</PackageType>→ MCP server (also a dotnet tool)<PackAsTool>true</PackAsTool>→ Dotnet tool- Class library (
IsPackable=trueor noOutputType) → Library <OutputType>Exe</OutputType>with<IsPackable>true</IsPackable>→ Application package (not a tool, but still publishable)<OutputType>Exe</OutputType>withoutPackAsToolorIsPackable→ Not packable by default (ask user if they intend to publish it)
-
Validate structure for each project's type:
-
Find existing publish workflows in
.github/workflows/— look fordotnet nuget push,nuget push, ordotnet pack. -
Check version consistency — for MCP servers, verify
.csproj<Version>matches bothserver.jsonversion fields (rootversionandpackages[].version). Flag any mismatch. -
Report findings to the user: classification, missing properties, version mismatches, existing workflows. For multi-project repos, note whether one workflow or separate workflows per package are needed. Offer to fix gaps — use
ask_userbefore modifying project files.
❌ See references/package-types.md [blocked] for per-type details and required properties.
Phase 2: Local Verification
Pack and verify locally before touching nuget.org — publishing errors waste a permanent version number.
⚠️ Always mention this step, even if you defer running it. Tell the user: "Before your first publish, run
dotnet pack -c Release -o ./artifactsto verify the .nupkg is created correctly."
dotnet pack -c Release -o ./artifacts— verify.nupkgis created- For tools/MCP servers: install from
./artifacts, run--help, uninstall - For libraries: inspect the
.nupkgcontents (it's a zip)
Phase 3: nuget.org Policy
This phase requires the user to act on nuget.org — guide them with exact values.
-
Determine the repo owner, repo name, and the workflow filename that will publish.
❌ The policy requires the exact workflow filename (e.g.,
publish.ymlorpublish.yaml) — just the filename, no path prefix. Matching is case-insensitive. Don't use the workflowname:field. -
Guide the user to create the trusted publishing policy:
Go to nuget.org/account/trustedpublishing → Add policy
- Repository Owner:
{owner} - Repository:
{repo} - Workflow File:
{filename}.yml - Environment:
release(only if the workflow usesenvironment:; leave blank otherwise)
Policy ownership: the user chooses individual account or organization. Org-owned policies apply to all packages owned by that org.
For private repos: policy is "temporarily active" for 7 days — becomes permanent after the first successful publish.
- Repository Owner:
-
Guide the user to create a GitHub Environment (recommended but optional — provides secret scoping + approval gates):
Repo Settings → Environments → New environment →
releaseAdd environment secret: Name =
NUGET_USER, Value = nuget.org username (NOT email)Optional: add Required reviewers for an approval gate.
⚠️ Wait for the user to confirm they've created the policy before asking them to remove old API keys/secrets or before attempting to run/publish with the workflow. Drafting or showing the workflow file itself is OK before confirmation.
Phase 4: Workflow Setup
Create or modify the publish workflow. The workflow must always be created or shown in your response — you may draft/show it even if the nuget.org policy is not yet confirmed, but do not guide the user to actually run/publish or remove old secrets until after confirmation.
Greenfield: Create publish.yml from the template in references/publish-workflow.md [blocked]. Adapt .NET version, project path, and environment name. Ensure your output explicitly mentions id-token: write and NuGet/login@v1.
Migration (existing workflow with API key): Modify in place —
-
Add OIDC permission and environment to the publishing job:
-
Add the NuGet login step before push:
-
Replace the API key in the push step:
-
Verify: Ask the user to trigger a publish and confirm the package appears on nuget.org.
❌ Don't delete the old API key secret until trusted publishing is verified. Removing it is a one-way door — wait for confirmation.
Troubleshooting
⚠️ If any blocker persists after one fix attempt, stop and ask the user.
References
- Package type details: references/package-types.md [blocked] — detection logic, required properties, minimal .csproj examples
- Publish workflow template: references/publish-workflow.md [blocked] — complete tag-triggered workflow ready to adapt
- Microsoft docs: NuGet Trusted Publishing


