Manifold
Operation-first .NET foundation for generating fast CLI and MCP surfaces from a single definition.
Install / Use
/learn @Garume/ManifoldQuality Score
Category
Development & EngineeringSupported Platforms
README
Manifold
Manifold is a .NET foundation for defining an operation once and exposing it through both CLI and MCP surfaces.
The model is simple:
- Write one operation by hand
- Let the source generator emit descriptors and invokers
- Wire those generated artifacts into your CLI and/or MCP host
Manifold does not own your transport, hosting model, or product-specific runtime. It focuses on operation definition, binding, metadata, and fast dispatch.
If you want a runnable starting point, the repository includes sample hosts under samples/.
What's Included
| Package | Purpose |
| --- | --- |
| Manifold | Core contracts, descriptors, attributes, and binding primitives |
| Manifold.Cli | CLI runtime helpers and generated invocation |
| Manifold.Generators | Source generator that emits descriptors and invokers |
| Manifold.Mcp | MCP metadata and invocation helpers |
Core Concepts
An operation is the single source of truth.
From that definition, Manifold.Generators emits:
GeneratedOperationRegistryGeneratedCliInvokerGeneratedMcpCatalogGeneratedMcpInvoker
You compose those generated types into your own application.
Manifold supports two authoring styles:
- Static method operations
- Class-based operations implementing
IOperation<TRequest, TResult>
Install
Most consumers should not install all four packages.
Start with the core package and the generator, then add only the surfaces you actually use.
Typical combinations:
| Scenario | Packages |
| --- | --- |
| Define operations only | Manifold, Manifold.Generators |
| CLI app | Manifold, Manifold.Generators, Manifold.Cli |
| MCP host | Manifold, Manifold.Generators, Manifold.Mcp |
| Both CLI and MCP | Manifold, Manifold.Generators, Manifold.Cli, Manifold.Mcp |
CLI host:
<ItemGroup>
<PackageReference Include="Manifold" Version="1.0.0" />
<PackageReference Include="Manifold.Generators" Version="1.0.0" PrivateAssets="all" />
<PackageReference Include="Manifold.Cli" Version="1.0.0" />
</ItemGroup>
MCP host:
<ItemGroup>
<PackageReference Include="Manifold" Version="1.0.0" />
<PackageReference Include="Manifold.Generators" Version="1.0.0" PrivateAssets="all" />
<PackageReference Include="Manifold.Mcp" Version="1.0.0" />
</ItemGroup>
If you need both surfaces, combine the two examples.
Authoring Operations
Static Method Example
Static method operations work well when you want the simplest possible definition.
using Manifold;
public static class MathOperations
{
[Operation("math.add", Summary = "Adds two integers.")]
[CliCommand("math", "add")]
[McpTool("math_add")]
public static int Add(
[Argument(0, Name = "x")] int x,
[Argument(1, Name = "y")] int y)
{
return x + y;
}
}
Class-Based Example
Class-based operations are useful when you need a dedicated request type, richer modeling, or DI-managed construction.
using Manifold;
[Operation("math.add", Summary = "Adds two integers.")]
[CliCommand("math", "add")]
[McpTool("math_add")]
public sealed class AddOperation : IOperation<AddOperation.Request, int>
{
public ValueTask<int> ExecuteAsync(Request request, OperationContext context)
=> ValueTask.FromResult(request.X + request.Y);
public sealed class Request
{
[Argument(0, Name = "x")]
[McpName("x")]
public int X { get; init; }
[Argument(1, Name = "y")]
[McpName("y")]
public int Y { get; init; }
}
}
For class-based operations, register the operation type in DI before using the generated invokers.
using Microsoft.Extensions.DependencyInjection;
ServiceCollection services = new();
services.AddTransient<AddOperation>();
ServiceProvider serviceProvider = services.BuildServiceProvider();
Static method operations do not require DI registration unless they explicitly request services.
Attribute Model
The main attributes are:
| Attribute | Applies To | Purpose |
| --- | --- | --- |
| [Operation("operation.id")] | Method, class | Declares the canonical operation id. Supports Summary, Description, and Hidden. |
| [CliCommand("group", "verb")] | Method, class | Declares the CLI command path, for example math add. |
| [McpTool("tool_name")] | Method, class | Declares the MCP tool name, for example math_add. |
| [CliOnly] | Method, class | Exposes the operation only on the CLI surface. |
| [McpOnly] | Method, class | Exposes the operation only on the MCP surface. |
| [ResultFormatter(typeof(...))] | Method, class | Overrides default CLI text rendering with a custom formatter. |
| [Argument(position)] | Parameter, request property | Binds a positional CLI argument. Supports Name, Description, and Required. |
| [Option("name")] | Parameter, request property | Binds a named CLI option. Supports Description and Required. |
| [Alias(...)] | Method, class, parameter, request property | Adds aliases for commands, options, arguments, or names. |
| [CliName("...")] | Method, class, parameter, request property | Overrides the CLI-facing name only. |
| [McpName("...")] | Method, class, parameter, request property | Overrides the MCP-facing name only. |
| [FromServices] | Parameter | Resolves the value from DI instead of user input. |
Common patterns:
- Use
[Operation]together with at least one surface attribute such as[CliCommand]or[McpTool] - Use
[Argument]for ordered CLI inputs and[Option]for named CLI inputs - Use
[CliName]and[McpName]when the same conceptual field should appear under different names on each surface - Use
[CliOnly]or[McpOnly]when an operation should not be shared across both surfaces - Use
[FromServices]for runtime services such as clocks, repositories, or application state
Examples:
- Rename an option for CLI only with
[CliName("person")] - Rename an MCP argument with
[McpName("targetName")] - Hide an internal operation from generated surfaces with
[Operation("internal.sync", Hidden = true)] - Expose to only one surface with
[CliOnly]or[McpOnly]
CLI Usage
At runtime, compose the generated registry and invoker into a CliApplication.
using Manifold.Cli;
using Manifold.Generated;
using Microsoft.Extensions.DependencyInjection;
ServiceCollection services = new();
services.AddTransient<AddOperation>();
ServiceProvider serviceProvider = services.BuildServiceProvider();
CliApplication cli = new(
GeneratedOperationRegistry.Operations,
new GeneratedCliInvoker(),
serviceProvider);
StringWriter output = new();
StringWriter error = new();
int exitCode = await cli.ExecuteAsync(
["math", "add", "2", "3"],
output,
error,
CancellationToken.None);
Notes:
CliApplicationhandles usage text and command dispatchGeneratedCliInvokeris the generated binding layer- Fast sync and async paths are selected automatically when available
MCP Usage
Manifold does not ship an MCP transport host. Instead, it provides:
- Generated tool metadata via
GeneratedMcpCatalog - Generated execution via
GeneratedMcpInvoker - MCP argument parsing and helper APIs in
Manifold.Mcp
A minimal local invocation looks like this:
using System.Text.Json;
using Manifold.Generated;
using Manifold.Mcp;
using Microsoft.Extensions.DependencyInjection;
ServiceCollection services = new();
services.AddTransient<AddOperation>();
ServiceProvider serviceProvider = services.BuildServiceProvider();
JsonElement args = JsonSerializer.Deserialize<JsonElement>(
"{\"x\":2,\"y\":3}");
GeneratedMcpInvoker invoker = new();
if (invoker.TryInvokeFast(
"math_add",
args,
serviceProvider,
CancellationToken.None,
out ValueTask<FastMcpInvocationResult> invocation))
{
FastMcpInvocationResult result = await invocation;
Console.WriteLine(result.Number);
}
Metadata discovery:
using Manifold.Generated;
foreach (var tool in GeneratedMcpCatalog.Tools)
{
Console.WriteLine($"{tool.Name}: {tool.Description}");
}
MCP Transports and Samples
The primary MCP transports are:
stdioStreamable HTTP
Manifold is intentionally transport-agnostic, so the repository includes sample hosts rather than baking transport hosting into the core packages.
Run them like this:
dotnet run --project .\samples\Manifold.Samples.McpStdioHost\Manifold.Samples.McpStdioHost.csproj
dotnet run --project .\samples\Manifold.Samples.McpHttpHost\Manifold.Samples.McpHttpHost.csproj
The HTTP sample listens on http://127.0.0.1:38474/mcp.
Note: the HTTP sample uses ModelContextProtocol.AspNetCore, which is currently a preview package. The stable core MCP package is ModelContextProtocol.
CLI Sample Host
There is also a minimal runnable CLI host:
dotnet run --project .\samples\Manifold.Samples.CliHost\Manifold.Samples.CliHost.csproj -- math add 2 3
dotnet run --project .\samples\Manifold.Samples.CliHost\Manifold.Samples.CliHost.csproj -- weather preview --city Tokyo --days 3
Dependency Injection and Services
There are two service access patterns.
Method-Based Operations
Use [FromServices] on a parameter.
[Operation("clock.now")]
[CliCommand("clock", "now")]
public static DateTimeOffset Now(
[FromServices] IClock clock)
{
return clock.UtcNow;
}
Class-Based Operations
Use constructor injection, or request s
