How the OpenAPI Effect Layer Exposes RPC Endpoints in magnitudedev/magnitude

The OpenAPI Effect layer compiles an OpenAPI specification into fully-typed RPC endpoints using compileOpenApi, which generates operation descriptors and a manifest that the ACN daemon registers with its RPC server, exposing them to SDK clients.

The magnitudedev/magnitude repository implements a protocol-driven architecture where API contracts are defined once in OpenAPI and automatically transformed into executable RPC endpoints. The @magnitudedev/openapi-effect package handles this transformation, bridging static specifications with the dynamic Effect-TS runtime used throughout the codebase.


From OpenAPI Spec to Generated Project

The entry point for RPC exposure is compileOpenApi in packages/openapi-effect/src/compiler.ts. This function performs a multi-stage compilation that converts an OpenAPI document into runnable code.

Compilation stages:

  • Parse and validate the OpenAPI JSON/YAML against the supported subset of the specification
  • Build an intermediate representation (IR) that normalizes paths, parameters, request bodies, and response schemas
  • Generate Effect Schemas for runtime validation and type inference
  • Produce operation descriptors (HttpOperationDescriptor for HTTP methods, StreamOperationDescriptor for streaming operations)
  • Emit a GenerationManifest listing all generated modules and their RPC signatures

The function returns a GeneratedProject containing:

interface GeneratedProject {
  files: Map<string, string>;     // filepath → TypeScript source
  manifest: GenerationManifest;   // RPC endpoint registry
}

This design ensures that every OpenAPI operation has a corresponding, type-safe RPC counterpart before any code is executed.


Runtime Client Generation

Generated projects include client-runtime.ts (packages/openapi-effect/src/client-runtime.ts), which exports factory functions that wire descriptors to Effect's RPC layer.

Key runtime helpers:

  • makeHttpOperation – Creates Effect functions from HttpOperationDescriptor instances
  • makeStreamOperation – Creates streaming RPC clients from StreamOperationDescriptor instances

These helpers internally call Rpc.make from acn-protocol, the core primitive for registering callable operations in the Magnitude protocol. The result is a client function that:

  1. Validates input against the generated Effect Schema
  2. Serializes the request according to the OpenAPI parameter location (path, query, header, body)
  3. Dispatches via the ACN RPC transport layer
  4. Deserializes and validates the response
// Generated client usage
import { makeHttpOperation } from "@magnitudedev/openapi-effect";

const getUser = makeHttpOperation({
  method: "GET",
  path: "/users/{userId}",
  // request/response schemas generated from OpenAPI components
});

// Effect-TS invocation
pipe(
  getUser({ userId: "123" }),
  Effect.runPromise
);

ACN Daemon Registration

The generated manifest feeds into the ACN (Agent Communication Network) daemon build process. In packages/acn-protocol/scripts/generate.ts, the protocol layer consumes compileOpenApi output to create concrete handler implementations.

Registration flow:

  1. Import the compiled manifest from the generated project
  2. Iterate over manifest.httpOperations and manifest.streamOperations
  3. Generate handler stubs in packages/acn-protocol/src/boundary/
  4. Register each handler with Rpc.make to expose it on the daemon's internal protocol
// Simplified daemon registration (from generate.ts pattern)
import { Rpc } from "@magnitudedev/acn-protocol";
import { manifest } from "./generated/manifest.js";

manifest.httpOperations.forEach((op) => {
  Rpc.make({
    name: op.name,
    handler: async (input) => {
      // Handler implementation calling underlying services
      return serviceLayer.execute(op, input);
    },
  });
});

This registration step is what actually exposes the RPC endpoints—without it, the generated client code would have no server counterpart to invoke.


SDK and Client Consumption

The packages/sdk layer re-exports the generated client functions, while packages/client-common provides the transport glue that connects them to the ACN daemon's protocol.

Client architecture:

  • CLI clients import directly from SDK and call operations as Effect programs
  • Web/desktop clients use the same generated functions through a platform-specific transport adapter
  • Type safety is preserved end-to-end because all clients share the same generated schemas

The OpenAPI Effect layer thus serves as the single source of truth: changes to the OpenAPI spec propagate automatically through compilation, daemon registration, and client code without manual synchronization.


Summary

  • compileOpenApi in packages/openapi-effect/src/compiler.ts transforms OpenAPI specs into GeneratedProject containing type-safe RPC descriptors
  • makeHttpOperation and makeStreamOperation in packages/openapi-effect/src/client-runtime.ts create Effect- compatible client functions wired to Rpc.make
  • The ACN daemon build script registers generated handlers from the manifest, exposing endpoints over the internal protocol
  • SDK clients consume these endpoints with full compile-time and runtime type safety, ensuring the OpenAPI contract is enforced at every layer

Frequently Asked Questions

How does compileOpenApi handle validation errors in the OpenAPI specification?

compileOpenApi performs schema validation during the parsing stage and throws descriptive errors for unsupported OpenAPI features or malformed documents. The validation logic is integrated with the intermediate representation builder, ensuring that only well-formed specs proceed to code generation.

Can I use the OpenAPI Effect layer without the ACN daemon?

Yes. The generated client-runtime.ts exports can be used with any Effect-compatible RPC transport. However, the full Magnitude stack expects daemon registration through acn-protocol for protocol compliance and service discovery.

What is the performance overhead of the generated RPC layer?

The overhead is minimal: operation descriptors are plain objects, and makeHttpOperation creates lightweight Effect functions. Runtime validation uses optimized Effect Schemas, and the actual network transport is handled by pluggable adapters in client-common, not by the generator itself.

How do streaming operations differ from HTTP operations in the generated code?

Streaming operations use makeStreamOperation instead of makeHttpOperation, producing Effect Streams (Stream<A, E, R>) rather than single-call Effects. The StreamOperationDescriptor includes additional metadata for subscription management and backpressure handling, as defined in the OpenAPI spec's x-stream extensions or AsyncAPI references.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →