mcpbeat

Managing Target Frameworks

microsoft/managing-target-frameworks

> Manages target frameworks in .NET project files (.csproj, .vbproj, .fsproj). Handles adding, removing, replacing, and upgrading target frameworks, including converting between single and multi-targeting. Use when user mentions "add TFM", "remove TFM", "retarget", "upgrade target framework", "change framework version", "multi-target", "switch to net8.0/net9.0/net10.0", or modifies TargetFramework/TargetFrameworks properties.

2k tokens
context cost
the whole folder, loaded on every use
1
files
instructions only
0
copies elsewhere
how many repositories repackaged it
17
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/microsoft/upgrade-agent-plugins --skill managing-target-frameworks

The instruction itself

10 sections, as written by the author

Manage Project Target Frameworks

| Action | Skill |

|--------|-------|

| Add/update package references or resolve NuGet version conflicts | managing-package-references |

| Modify project properties | modifying-project-properties |

Defer to these skills for package versions, CPM, and property edits.

Workflow

Track progress using this checklist:

Task Progress:
- [ ] Step 1: Locate target framework property
- [ ] Step 2: Modify target frameworks
- [ ] Step 3: Restore and resolve errors
- [ ] Step 4: Apply conditional compilation (if needed)

Step 1: Locate the Target Framework Property

Before making changes, determine where the TargetFramework or TargetFrameworks property is defined. It may be in the project file, a Directory.Build.props, or another imported MSBuild file. Use the modifying-project-properties skill to locate and understand the property definition.

Step 2: Modify Target Frameworks

Determine the intended change from user context and apply the matching scenario:

| User intent | Current state | Action |

|-------------|---------------|--------|

| Replace/upgrade an existing TFM with another | Single or multi-targeted | Replace the specified TFM |

| Add support for an additional TFM | Single TargetFramework | Add multi-targeting (rename property to plural TargetFrameworks, keep existing TFM, add new one) |

| Add support for an additional TFM | Already uses TargetFrameworks | Append new TFM to existing list |

| Remove/drop a TFM | Multi-targeted | Remove from list (revert to singular if one remains) |

Replace a TFM — swap the value in place; preserve the singular/plural property name:

<!-- Single-targeted: stays singular -->
<TargetFramework>OldTFM</TargetFramework>
<!-- After -->
<TargetFramework>NewTFM</TargetFramework>
<!-- Multi-targeted: replace only the specific TFM -->
<TargetFrameworks>OldTFM;OtherTFM</TargetFrameworks>
<!-- After -->
<TargetFrameworks>NewTFM;OtherTFM</TargetFrameworks>

Add multi-targeting — convert singular TargetFramework to plural TargetFrameworks and add the new TFM:

<!-- Before -->
<TargetFramework>ExistingTFM</TargetFramework>

<!-- After -->
<TargetFrameworks>ExistingTFM;NewTFM</TargetFrameworks>

Append to existing multi-targeted project — add the new TFM to the existing list:

<!-- Before -->
<TargetFrameworks>TFM1;TFM2</TargetFrameworks>

<!-- After -->
<TargetFrameworks>TFM1;TFM2;NewTFM</TargetFrameworks>

Remove a TFM — remove the TFM from the list; revert to singular TargetFramework if only one remains:

<!-- Multiple TFMs remain: stays plural -->
<TargetFrameworks>TFM1;TFM2;TFMToRemove</TargetFrameworks>
<!-- After -->
<TargetFrameworks>TFM1;TFM2</TargetFrameworks>
<!-- One TFM remains: revert to singular -->
<TargetFrameworks>TFM1;TFMToRemove</TargetFrameworks>
<!-- After -->
<TargetFramework>TFM1</TargetFramework>

Order TFMs newest to oldest when multiple TFMs are present — MSBuild uses the first TFM as the default for IDE operations and dotnet run, so the newest framework provides the best developer experience.

Step 3: Resolve Errors

Restore first and resolve all NUxxxx errors before building. Then build and resolve MSBxxxx/CSxxxx errors. Common multi-targeting errors:

| Error | Cause | Resolution |

|-------|-------|------------|

| NU1202: Package not compatible with <TFM> | Package doesn't support added TFM | Check for a newer package version that supports the TFM, or add a conditional <PackageReference> (see below) |

| NU1202: PackageA -> PackageB -> requires <TFM> | Transitive dependency incompatible | Update the top-level package, or add a conditional reference excluding it for the incompatible TFM |

| MSB3644: Reference assemblies not found | Targeting pack not installed | Advise the user to install the required targeting pack or SDK — the LLM cannot install SDKs |

| CS0246: Type could not be found | API not available in added TFM | Add a #if directive for TFM-specific code, or find a cross-platform alternative API |

| CS1061: Type does not contain member | API differs between TFMs | Find a replacement method or overload available in the new TFM. If no equivalent exists, add a #if directive for the specific member call |

Multi-targeting-specific pattern — use conditional package references when no single package version works for all TFMs:

<ItemGroup Condition="'$(TargetFramework)' == 'SpecificTFM'">
  <PackageReference Include="PackageName" />
</ItemGroup>

Step 4: Apply Conditional Compilation (If Needed)

Use #if directives only when APIs genuinely differ between TFMs. Not every difference needs conditionals — some just need a different overload or parameter. Overusing #if makes code harder to maintain and review.

Preprocessor symbol convention: MSBuild automatically defines preprocessor symbols for each target framework. The symbol name is the TFM with . replaced by _, uppercased (e.g., net10.0NET10_0, net48NET48, netstandard2.0NETSTANDARD2_0). These symbols do not need to be declared manually — they are available in any #if directive.

#if NET10_0_OR_GREATER
    // API available only in .NET 10+
#elif NETFRAMEWORK
    // .NET Framework fallback
#endif

Use _OR_GREATER suffix symbols (e.g., NET8_0_OR_GREATER) when the code applies to a minimum version rather than an exact version. Use NETFRAMEWORK for any .NET Framework target, NETSTANDARD for any .NET Standard target.

Anti-Patterns

  • ❌ Using singular TargetFramework with multiple TFMs — MSBuild silently ignores all but the first value
  • ❌ Wrapping every API call in #if when only some APIs differ — creates unmaintainable code with unnecessary divergence
  • ❌ Stopping after the first TFM builds — each TFM can have different API surfaces, so verify all TFMs compile
  • ❌ Editing the project file directly without checking if the property is inherited from Directory.Build.props — changes would be overridden by the import
  • ❌ Converting TargetFramework to TargetFrameworks when only a TFM replacement was requested — changes build behavior unnecessarily
  • ❌ Leaving TargetFrameworks (plural) when only one TFM remains — causes MSBuild to produce a TFM-specific output folder structure instead of a flat bin/ layout

Success Criteria

  • [ ] TFM replaced in place without changing singular/plural when only a replacement was requested
  • [ ] TargetFramework changed to TargetFrameworks (plural) when adding multi-targeting
  • [ ] New TFM appended correctly when project already uses TargetFrameworks
  • [ ] TargetFrameworks reverted to singular TargetFramework when only one TFM remains after removal
  • [ ] TFMs semicolon-separated, newest to oldest
  • [ ] #if directives only where APIs genuinely differ
  • [ ] All TFMs build successfully

Resources

How to use it

Copy the folder

Take microsoft/managing-target-frameworks from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.