AI-native .NET framework — module-based handlers wired at compile time and exposed as JSON-RPC, HTTP & MCP…
**Module-based handler pipelines, compile-time registration, JSON-RPC hosting, M
以下摘录自该仓库的 README(片段) · 在 GitHub 读完整 README →
jobs.**
Declare intent next to your code; let source generators do the wiring. No runtime reflection scanning.
**AI-native by design:** the same handlers that power your API are exposed to AI agents
as MCP tools — generated from your code at compile time, with no separate tool
definitions or duplicated schemas.
---
Elarion's central idea is simple: **your application assemblies define modules and handlers; your
host assembly only wires infrastructure, transport, and deployment.** Everything that can be
discovered from your code — handlers, services, scheduled jobs, RPC methods, validation metadata, EF Core
DbSets — is emitted by source generators at compile time instead of scanned by reflection at
startup.
That one class is a use case, a registered service, a JSON-RPC method, an **MCP tool for AI agents**,
and (optionally) a schema-exported TypeScript contract — with **no entry added to any Program.cs
registration list.** The operation name is optional: [Handler] alone infers {module}.{operation} by
convention (here clients.get), while an explicit name is recommended for stable public/wire contracts.
Why Elarion
- **Compile-time, not reflection** — handlers, services, modules, RPC maps, validation metadata, and
scheduled jobs become ordinary DI code. Startup is deterministic and AOT-friendly; missing wiring
is a build error, not a runtime surprise.
- **Modules own their surface** — a module is a namespace plus an [AppModule] marker. Add a handler
under it and the module publishes it automatically.
- **Transport-neutral results** — handlers return Result with a transport-agnostic AppError;
the host maps failures to JSON-RPC, HTTP, or anything else.
- **One bus, many surfaces** — every handler maps onto a single transport-neutral HandlerDispatcher
(Elarion.Abstractions.Dispatch); JSON-RPC and MCP are thin **adapters** over that one bus, each serving
only the operations flagged for it. Define a handler once and choose its surfaces with the Transports
flag.
- **End-to-end JSON-RPC** — mark a handler with [Handler], export a schema at build time, and
generate a typed TypeScript + Zod client.
- **Validation that reaches the contract** — DataAnnotations on the request DTO are enforced by a
generated pipeline decorator under every transport **and** exported to the JSON-RPC schema, OpenAPI,
MCP tool schemas, and the Zod client (which pre-validates requests). One declaration, four surfaces;
business rules stay in the handler, inside the transaction (ADR-0027).
- **AI-native, no extra code** — expose the same [Handler] handlers to AI agents as an
MCP server. MCP is an adapter over the shared HandlerDispatcher, so a
tool is just a handler flagged HandlerTransports.Mcp. Tool names, descriptions, and input schemas are
generated from your handlers and [Description] attributes at compile time — no separate tool layer, no
duplicated schemas, no runtime reflection. Choose a handler's transports with
[Handler(Transports = …)] (JSON-RPC, MCP, or both) and rename a tool with [McpHandler].
- **Feature flags & variants** — every flag is declared once with exactly one owner (a resolver class or
the host's backend) and checked at compile time; gate any handler with [FeatureGate]; a generated decorator
evaluates the flag before the handler runs and a closed gate returns **404 Not Found**, so a
disabled feature is indistinguishable from one that doesn't exist (the name is never leaked).
[FeatureVariant] swaps a [Service] implementation per user behind a flag. Both work identically