using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization.Metadata; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using RhMcp.Router; using RhMcp.Router.Tools.Generated; RouterConfig config = RouterConfig.FromArgs(args); // Derived from the single source in rhino/Directory.Build.props (via // the assembly's informational version). Strip any SourceLink "+" // build-metadata suffix so ServerInfo reports a clean "0.1.3". The attribute is // always emitted for a normally-built assembly; its absence is a broken build, so // fail fast rather than advertise a fake version. string informationalVersion = Assembly.GetExecutingAssembly().GetCustomAttribute()?.InformationalVersion ?? throw new InvalidOperationException("router assembly missing InformationalVersion; check build config"); string routerVersion = informationalVersion.Split('+')[0]; HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); // Stdio MCP servers must not log to stdout — that's the JSON-RPC channel. // Route all logging to stderr. builder.Logging.ClearProviders(); builder.Logging.AddConsole(o => o.LogToStandardErrorThreshold = LogLevel.Trace); builder.Services.AddSingleton(config); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddHttpClient(); // JsonSerializerOptions used by the MCP server for tool arg/return serialization. // Source-generated RouterJsonContext is chained first so AOT publish stays clean; // MCP layers its own context on top of whatever we pass in. var jsonOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web); jsonOptions.TypeInfoResolverChain.Insert(0, RouterJsonContext.Default); jsonOptions.Converters.Add(new LenientStringConverter()); var mcpBuilder = builder.Services .AddMcpServer(o => { o.ServerInfo = new() { Name = "rhino-mcp-router", Version = routerVersion }; }) .WithStdioServerTransport(); // Replaces WithToolsFromAssembly() — that path uses Assembly.GetCallingAssembly() // which is unsupported under NativeAOT. RouterToolRegistrar is generated by // codegen and chains WithTools() per tool class. RouterToolRegistrar.RegisterAll(mcpBuilder, jsonOptions); var host = builder.Build(); // Adopt any user-started Rhinos that announced themselves before the router // came up. Cheap one-shot dir scan; later scans happen on every list_slots // and slot-less dispatch. host.Services.GetRequiredService().ScanAnnouncements(); // Clean up child Rhinos when Claude Code closes the stdio connection. host.Services.GetRequiredService().ApplicationStopping.Register(() => { host.Services.GetRequiredService().CloseAll(); }); await host.RunAsync();