Ecosystem Bridges

Turn absorbs the existing ecosystem without asking you to migrate everything at once. Three native primitives give you a migration path from any developer context: REST APIs, GraphQL schemas, gRPC services, FHIR endpoints, existing MCP servers, and legacy CLI scripts.


Compile-Time Schema Adapters

The Problem They Solve

Every API ecosystem ships its own SDK. Integrating Stripe, GitHub, a hospital's FHIR endpoint, or a gRPC microservice traditionally requires installing a language-specific package, reading the documentation, writing wrapper code, and then exposing that wrapper to your LLM tool list. This creates SDK bloat, stale bindings, and hidden runtime costs.

Turn inverts the model. There is no SDK. The compiler reads the schema once at build time and generates native Turn closures. At runtime the LLM calls plain Turn functions with no awareness of the underlying protocol.

How It Works

A schema adapter is a Rust crate compiled to wasm32-unknown-unknown: a sandboxed WebAssembly module with no filesystem, no network, and no host access. At compile time the Turn compiler:

  1. Fetches the remote schema URL.
  2. Loads the adapter in a Wasmtime sandbox.
  3. Calls expand_schema() and receives a JSON array of Turn AST nodes.
  4. Inlines those nodes into the program as if they were hand-written Turn code.

Zero runtime overhead. The schema is resolved once. The LLM never pays for it.

Supported Adapters

KeywordSchema formatGenerated output
use schema::openapi("url")OpenAPI 3.x JSONOne tool closure per operation
use schema::graphql("url")GraphQL Introspection JSONStructDef per type; closures per Query and Mutation
use schema::swagger("url")Swagger v2 JSONOne tool closure per path and method
use schema::grpc("url")Protobuf .proto textStructDef per message; closure per RPC
use schema::fhir("url")FHIR Conformance Statement JSONStructDef per resource; CRUD closures
use schema::soap("url")SOAP WSDL XMLOne tool closure per exposed SOAP action
use schema::odata("url")OData Metadata XML/JSONStructDef per Entity; closure per Action/Function

Example

adapters.tn
// GraphQL: fetches introspection schema at compile time.
// At runtime, gh is a map of native Turn tools.
let gh = use schema::graphql("https://api.github.com/graphql");

// Stripe REST: every endpoint becomes a callable Turn function.
let stripe = use schema::swagger("https://api.stripe.com/openapi.json");

// The LLM calls these natively. No HTTP. No JSON parsing.
let invoice = infer StripeInvoice with [stripe.createInvoice] {
    "Create an invoice for Alice for $49.99.";
};

The mcp() Bridge

The Model Context Protocol (MCP) represents a large existing investment across thousands of servers and tools. Turn does not displace MCP. It absorbs it as a subprocess.

mcp.tn
// Spawns a stdio JSON-RPC MCP server as an OS subprocess.
// Returns: McpServer { pid: 12345, status: "active" }
let legacy = mcp("stdio://npx @modelcontextprotocol/server-stripe");

When the Turn VM executes mcp(), it validates the stdio:// URL scheme, extracts the binary and arguments, and spawns the process using std::process::Command. No shell is involved. The subprocess lifecycle is tied to the owning agent.

Migration path: mcp() is a bridge, not a destination. Once a team rewrites their MCP server logic in Turn, the mcp() call is replaced with use schema::openapi(...) and the subprocess overhead disappears entirely.


CLI Domestication (sys_exec)

LLMs composing bash -c strings or injecting shell metacharacters is a critical security failure mode. Turn eliminates it structurally.

sys_exec.tn
// Each token is a separate, typed Str in a Turn map.
// No shell is invoked. Shell injection is structurally impossible.
let output = call("sys_exec", {
    "bin":  "python3",
    "arg1": "process_data.py",
    "arg2": user_provided_input   // Str: a positional arg, not a shell string
});

The sys_exec handler in the Turn ToolRegistry enforces:

  • The argument must be a Map. A raw string causes an immediate typed error.
  • Every map value must be a Str. Numbers or nested structures cause an immediate typed error.
  • The OS call is Command::new(binary).args(args) with no shell and no interpolation.
  • Returns Str (stdout) on success, or a typed error string on non-zero exit.

Design Summary

MechanismWhen to useRuntime overhead
use schema::*Stable API with a public schema URLZero. Resolved at compile time.
mcp("stdio://...")Existing MCP server you operate1 OS subprocess per agent
sys_execLegacy script or binary that does one thing1 child process per call

Next Steps

  • Design Mandate The philosophy and constraints behind every language decision.
  • Implementation The compiler pipeline, bytecode instruction set, and VM internals.