SkillAgentSearch skills...

Manifold

Operation-first .NET foundation for generating fast CLI and MCP surfaces from a single definition.

Install / Use

/learn @Garume/Manifold
About this skill

Quality Score

0/100

Supported Platforms

Claude Code
Cursor

README

<p align="center"> <img src="assets/logo/logo.svg" alt="Manifold" width="620" /> </p>

Manifold

日本語版 README

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:

  • GeneratedOperationRegistry
  • GeneratedCliInvoker
  • GeneratedMcpCatalog
  • GeneratedMcpInvoker

You compose those generated types into your own application.

Manifold supports two authoring styles:

  1. Static method operations
  2. 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:

  • CliApplication handles usage text and command dispatch
  • GeneratedCliInvoker is 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:

  • stdio
  • Streamable 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

View on GitHub
GitHub Stars148
CategoryDevelopment
Updated1h ago
Forks0

Languages

C#

Security Score

100/100

Audited on Apr 1, 2026

No findings