多平台操作定义框架

Manifold是一个.NET基础框架,允许开发者定义一次操作并通过CLI和MCP两种接口暴露,适用于需要统一操作定义和多平台调用的开发场景。

  • by Garume
  • C#
  • 本地部署
  • 20 星标
  • 更新于
在 GitHub 查看 Garume/Manifold

使用说明

流形

日本语版README

Manifold 是a。NET基础,用于定义一次操作并通过CLI和MCP表面公开它。

模型很简单:

  • 手写一个操作
  • 让源代码生成器发出描述符和调用者
  • 将这些生成的工件连接到CLI和/或MCP主机中

Manifold 不拥有您的传输、托管模式或特定于产品的运行时。它侧重于操作定义、绑定、元数据和快速调度。

如果你想要一个可运行的起点,存储库包括以下示例主机 samples/.

包含内容

包装 用途
Manifold 核心契约、描述符、属性和绑定原语
Manifold.Cli CLI运行时帮助程序和生成的调用
Manifold.Generators 发出描述符和调用者的源代码生成器
Manifold.Mcp MCP元数据和调用帮助程序

核心概念

手术是唯一的真理来源。

根据该定义, Manifold.Generators 发射:

  • GeneratedOperationRegistry
  • GeneratedCliInvoker
  • GeneratedMcpCatalog
  • GeneratedMcpInvoker

您可以将这些生成的类型组合到自己的应用程序中。

Manifold 支持两种创作风格:

  1. 静态方法操作
  2. 基于类的操作实现 IOperation<TRequest, TResult>

安装

大多数消费者不应该安装所有四个软件包。

从核心包和生成器开始,然后只添加实际使用的曲面。

典型组合:

场景 包
仅定义操作 Manifold, Manifold.Generators
CLI应用程序 Manifold, Manifold.Generators, Manifold.Cli
MCP主机 Manifold, Manifold.Generators, Manifold.Mcp
CLI和MCP Manifold, Manifold.Generators, Manifold.Cli, Manifold.Mcp

CLI主机:

<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主机:

<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>

如果需要两个曲面,请将这两个示例结合起来。

创作操作

静态方法示例

当你想要尽可能简单的定义时,静态方法操作效果很好。

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;
    }
}

基于类的示例

当您需要专用的请求类型、更丰富的建模或DI管理的构造时,基于类的操作非常有用。

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; }
    }
}

对于基于类的操作,在使用生成的调用者之前,在DI中注册操作类型。

using Microsoft.Extensions.DependencyInjection;

ServiceCollection services = new();
services.AddTransient<AddOperation>();
ServiceProvider serviceProvider = services.BuildServiceProvider();

静态方法操作不需要DI注册,除非它们显式请求服务。

属性模型

主要属性包括:

属性 适用于 目的
[Operation("operation.id")] 方法、类 声明规范操作id。支持 Summary, Description,以及 Hidden.
[CliCommand("group", "verb")] 方法,类 声明CLI命令路径,例如 math add.
[McpTool("tool_name")] 方法,类 声明MCP工具名称,例如 math_add.
[CliOnly] 方法,类 仅在CLI界面上显示操作。
[McpOnly] 方法,类 仅在MCP表面上显示操作。
[ResultFormatter(typeof(...))] 方法,类 使用自定义格式化程序覆盖默认CLI文本呈现。
[Argument(position)] 参数,请求属性 绑定位置CLI参数。支持 Name, Description,以及 Required.
[Option("name")] 参数,请求属性 绑定一个命名的CLI选项。支持 Description 和 Required.
[Alias(...)] 方法、类、参数、请求属性 为命令、选项、参数或名称添加别名。
[CliName("...")] 方法、类、参数、请求属性 仅覆盖面向CLI的名称。
[McpName("...")] 方法、类、参数、请求属性 仅覆盖面向MCP的名称。
[FromServices] 参数 从DI而不是用户输入中解析值。

常见模式:

  • 使用 [Operation] 以及至少一个表面属性,例如 [CliCommand] 或 [McpTool]
  • 使用 [Argument] 用于有序的CLI输入和 [Option] 用于命名CLI输入
  • 使用 [CliName] 和 [McpName] 当同一概念字段应在每个曲面上以不同名称出现时
  • 使用 [CliOnly] 或 [McpOnly] 当一个操作不应该在两个表面上共享时
  • 使用 [FromServices] 用于运行时服务,如时钟、存储库或应用程序状态

示例:

  • 仅使用重命名CLI选项 [CliName("person")]
  • 将MCP参数重命名为 [McpName("targetName")]
  • 在生成的曲面中隐藏内部操作 [Operation("internal.sync", Hidden = true)]
  • 仅暴露于一个表面 [CliOnly] 或 [McpOnly]

命令行用法

在运行时,将生成的注册表和调用者组合成 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);

笔记:

  • CliApplication 处理使用文本和命令调度
  • GeneratedCliInvoker 是生成的绑定层
  • 快速同步和异步路径在可用时会自动选择

MCP使用

Manifold 不运送MCP传输主机。相反,它提供:

  • 通过以下方式生成工具元数据 GeneratedMcpCatalog
  • 通过以下方式生成执行 GeneratedMcpInvoker
  • 中的MCP参数解析和辅助API Manifold.Mcp

最小本地调用如下:

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);
}

元数据发现:

using Manifold.Generated;

foreach (var tool in GeneratedMcpCatalog.Tools)
{
    Console.WriteLine($"{tool.Name}: {tool.Description}");
}

MCP运输和样品

主要的MCP传输方式包括:

  • stdio
  • Streamable HTTP

Manifold 故意与传输无关,因此存储库包含示例主机,而不是将传输主机烘焙到核心包中。

这样运行它们:

dotnet run --project .\samples\Manifold.Samples.McpStdioHost\Manifold.Samples.McpStdioHost.csproj
dotnet run --project .\samples\Manifold.Samples.McpHttpHost\Manifold.Samples.McpHttpHost.csproj

HTTP示例侦听 http://127.0.0.1:38474/mcp.

注意:HTTP示例使用 ModelContextProtocol.AspNetCore,目前是一个预览包。稳定的核心MCP包是 ModelContextProtocol.

CLI示例主机

还有一个最小的可运行CLI主机:

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

依赖注入和服务

有两种服务访问模式。

基于方法的操作

使用 [FromServices] 在一个参数上。

[Operation("clock.now")]
[CliCommand("clock", "now")]
public static DateTimeOffset Now(
    [FromServices] IClock clock)
{
    return clock.UtcNow;
}

基于类的操作

使用构造函数注入,或通过以下方式请求服务 OperationContext.

public sealed class GreetingOperation(IGreetingService greetings)
    : IOperation<GreetingOperation.Request, string>
{
    public ValueTask<string> ExecuteAsync(Request request, OperationContext context)
        => ValueTask.FromResult(greetings.Format(request.Name));

    public sealed class Request
    {
        [Option("name")]
        public string Name { get; init; } = string.Empty;
    }
}

结果格式

要提供自定义CLI文本输出,同时保持JSON或MCP的结构化结果,请实现 IResultFormatter<TResult>.

using Manifold;

public sealed class WeatherFormatter : IResultFormatter<WeatherResult>
{
    public string? FormatText(WeatherResult result, OperationContext context)
        => $"{result.City}:{result.TemperatureC}";
}

然后附上:

[ResultFormatter(typeof(WeatherFormatter))]

生成的类型

发电机在以下情况下发出这些公共入口点 Manifold.Generated:

  • GeneratedOperationRegistry
  • GeneratedCliInvoker
  • GeneratedMcpCatalog
  • GeneratedMcpInvoker

这些是消费者的标准集成界面。

演出

Manifold 包括专用的BenchmarkDotNet套件 benchmarks/.

基准说明和比较表见 benchmarks/README.md.

比较集:

  • 命令行界面
    • Manifold.Cli
    • ConsoleAppFramework
    • System.CommandLine
  • 主控程序
    • Manifold.Mcp
    • 官方的 ModelContextProtocol
    • McpToolkit
    • mcpdotnet

当前快照:

CLI:

场景 歧管 控制台AppFramework 系统。命令行
阵地指挥 22.61 ns / 0 B 26.57 ns / 0 B 1730.82 ns / 4688 B
选项繁重的命令 28.89 ns / 0 B 24.76 ns / 0 B 2110.84 ns / 5632 B

MCP(往返形状):

场景 流形 模型上下文协议 McpToolkit McpDotNet
tools/list 回应 756.3 ns / 0 B 754.6 ns / 0 B 635.4 ns / 0 B 816.2 ns / 0 B
tools/call 回应 47.16 ns / 0 B 68.56 ns / 0 B 146.34 ns / 96 B 93.20 ns / 256 B

笔记:

  • CLI编号测量解析器+分派热路径
  • 上面的MCP数字反映了内存响应结构,而不是传输基准
  • 生的 ModelContextProtocol 调用微基准测试即将到来 ZeroMeasurement往返形状的桌子是更有意义的比较

有关方法和完整报告,请参阅 benchmarks/README.md.

构建

从存储库根目录:

./build/restore.ps1
./build/build.ps1 -NoRestore
./build/test.ps1 -NoBuild
./build/quality.ps1
./build/pack.ps1

./build/pack.ps1 写 .nupkg 和 .snupkg 文件到 .artifacts/packages/.

存储库状态

  • Windows第一脚本和CI
  • 麻省理工学院许可
  • CI验证构建、测试、格式化、架构检查和包创建

OSS客房服务

仓库布局

src/
  Manifold
  Manifold.Cli
  Manifold.Generators
  Manifold.Mcp
tests/
  Manifold.Tests
  Manifold.Cli.Tests
  Manifold.Generators.Tests
  Manifold.Mcp.Tests
benchmarks/
  Manifold.Benchmarks
  Manifold.Mcp.Benchmarks
samples/
  Manifold.Samples.Operations
  Manifold.Samples.CliHost
  Manifold.Samples.McpStdioHost
  Manifold.Samples.McpHttpHost