| name | glue |
| displayName | Glue |
| description | Official Glue IDL guide for agents. Use when designing, writing, reviewing, validating, or generating code from Glue files and .gluerc config for TypeScript, Python, Rust, Go, OpenAPI, JSON Schema, or Protobuf. |
| license | MIT |
| metadata | {"author":"Guy Waldman","version":"1.12.1"} |
Glue
Glue is a compact Interface Definition Language (IDL) and toolchain for modeling data structures and API interfaces once, then generating useful artifacts for multiple ecosystems. Use Glue when you need a clear source of truth for models, enums, type aliases, API endpoints, OpenAPI, JSON Schema, Protobuf, or language-native types.
Canonical References
Prefer current public docs over memorized syntax when exact behavior matters:
Agent Workflow
- Clarify the domain boundary: identify the nouns, stable identifiers, enum vocabularies, request/response shapes, and API operations.
- Write the smallest useful
*.glue file first. Add config only when output paths, generator options, or multiple targets matter.
- Prefer explicit, reusable named models over deeply inlined anonymous structures. Reuse type aliases for IDs, timestamps, and repeated primitives.
- Validate with
glue check <file> when the CLI is available.
- Generate with
glue gen <target> -i <file> -o <output>, or use glue gen / glue gen --config .gluerc.yaml for config-driven generation. You may also use glue gen <target> -i <file> -o - to print to stdout when the output is small or for debugging.
- Only when you absolutely must dive deeper, inspect the AST with
glue ast <file>.
- When a user asks for a target-specific artifact, consult the target docs or
https://gluelang.dev/llms.txt before relying on memory.
- If the source uses imports, keep imports at the top of the file and resolve relative paths from the importing file.
Core Syntax
- Comments:
// line comment; doc comments: /// documentation.
- Primitive types:
string, int, uint, i8, i16, i32, i64, u8, u16, u32, u64, float, bool, any.
- Arrays:
Type[].
- Tuples:
(TypeA, TypeB).
- Maps:
Record<KeyType, ValueType>.
- Optional fields:
field?: Type.
- Defaults:
field: Type = value.
- Union types:
string | string[].
- Type aliases:
type UserId = string.
- Codegen emits public type aliases where suitable: TypeScript
export type, Rust tuple newtypes, Go type Name = ..., and Python TypeAlias. Aliases starting with _ are private and inlined.
- Constants:
const MAX_PAGE_SIZE = 100; optional annotations use const MAX_PAGE_SIZE: int = 100. Constants can be top-level or declared inside models.
- Model-scoped constants: public constants can be referenced as
Model.CONSTANT or Outer.Inner.CONSTANT; leading _ constants cannot be referenced from outside their owning model.
- Constant expressions support int
+/*, string +, parentheses, bool literals, and references to other constants, including qualified model constants.
- Models:
model User { id: UserId }.
- Model, anonymous model, endpoint, service, and RPC block members may be separated with commas for compact one-line shapes, e.g.
model User { id: string, email?: string }.
- Enums:
enum Status: "pending" | "paid" | "cancelled".
- Imports must appear before declarations:
import * from "common.glue"
import * as common from "common.glue"
import { User, Address as PostalAddress } from "domain.glue"
- Field metadata can use
@field(alias="external_name", example="value", proto_tag=1). alias and proto_tag can use constants, e.g. @field(alias=Aliases.USER_ID_ALIAS, proto_tag=Tags.USER_ID).
- Endpoints use
endpoint "METHOD /path/{param}" Name { ... } with optional body, headers, and responses.
- Protobuf services use
service Name { rpc Method { body: Request returns: Response } }.
Supported generation targets are typescript, python, rust, go, openapi, jsonschema, and protobuf.
Modeling Guidelines
- Model domain concepts, not one target language. Avoid naming or structuring fields around a single framework unless the user explicitly asks.
- Use stable aliases for IDs and repeated scalar concepts:
type UserId = string, type IsoTimestamp = string, type MoneyCents = int.
- Use enums for closed vocabularies. Keep enum values meaningful wire values, usually lowercase strings.
- Make optionality intentional. Use
? for data that may be absent; use defaults for values the producer can safely omit.
- Avoid global namespace pollution and leverage nested models, enums, aliases, and constants to keep related concepts together. For example, if
Order is the only model that uses OrderStatus, nesting OrderStatus within Order keeps the domain tidy and makes it clear that the enum is specific to orders.
- Keep endpoint payloads named. Prefer
CreateOrderRequest and OrderResponse over anonymous ad hoc endpoint-only models.
- Use
Record<string, T> for keyed objects and dictionaries; use arrays for ordered lists.
- Keep imports simple and acyclic. Split files by domain only when the resulting boundaries are obvious.
- Document public models and important fields with
///. Good doc comments improve generated schemas and help downstream agents. Internal comments are supported with // and are stripped from generated output, so they are ideal for implementation notes and reminders that should not be exposed downstream.
- Treat Glue as the source of truth. When editing generated code, consider whether the change belongs in the
.glue model instead.
- Validate early. A small
glue check pass is cheaper than debugging generated TypeScript, Python, or OpenAPI later.
Protobuf Guidance
Glue Protobuf generation emits proto3 files from models, enums, and services.
- Plain model fields do not need annotations; Protobuf field tags default to source order starting at
1.
- Enum constants are generated in
CONSTANT_CASE from Glue string values.
- Use
@field(proto_tag=<number>) when the schema needs stable wire compatibility across field reordering. If one field in a model has proto_tag, every field in that model must have one.
- Optional scalar and message fields generate
optional Protobuf fields. Optional repeated and map fields are accepted, but Protobuf has no presence for those shapes, so they emit as regular repeated and map fields.
Record<K, V> emits map<K, V> for supported Protobuf key/value types. Anonymous structs emit generated messages named from the owning field path.
- Unions emit
oneof only when every member is valid inside a Protobuf oneof; repeated fields, maps, optional members, and explicit proto_tag unions are rejected with an error.
- Services use
service and rpc declarations with body and returns; both should be message types. HTTP endpoint declarations are ignored by the Protobuf generator.
model GetUserRequest {
@field(proto_tag=1)
user_id: int
}
model User {
@field(proto_tag=1)
user_id: int
@field(proto_tag=2)
display_name?: string
@field(proto_tag=3)
tags?: string[]
}
service UserService {
rpc GetUser {
body: GetUserRequest
returns: User
}
}
Current Limitations
Do not invent unsupported syntax. As of the current docs, notable limitations include:
- No generics such as
model Response<T> { data: T }.
- No intersection types such as
type A = B & C.
- Endpoint path/query parameter typing and auth scheme modeling are limited compared with full OpenAPI.
When these areas matter, keep the Glue model simple and use generated OpenAPI or target-language code as the integration point for additional framework-specific detail.
CLI Patterns
# Validate one file.
glue check api.glue
# Generate one target from one file.
glue gen typescript -i api.glue -o generated/api.ts
glue gen python -i api.glue -o generated/api.py
glue gen openapi -i api.glue -o openapi.yaml
# Generate from stdin or a URL.
cat api.glue | glue gen jsonschema -o schema.json
glue gen go https://raw.githubusercontent.com/guywaldman/glue/refs/heads/main/examples/basic.glue -o generated/models.go
# Use auto-discovered .gluerc / .gluerc.yaml, or pass one explicitly.
glue gen
glue gen --config .gluerc.yaml
# Override config inline.
glue gen typescript -i api.glue -o generated/api.ts --set "watermark=none"
Config Pattern
Prefer .gluerc.yaml when a project generates multiple targets:
global:
output_base_dir: generated
diagnostics:
suppress_warnings:
- constant_case
config:
watermark: short
lint_suppressions: true
gen:
- mode: typescript
files: "schemas/*.glue"
output: "ts/{file_name}.{file_ext}"
config_overrides:
typescript:
zod: true
- mode: python
files:
- "schemas/*.glue"
- "shared/*.glue"
output: "py/{file_name}.{file_ext}"
config_overrides:
python:
data_model_library: pydantic
- mode: openapi
files: "schemas/api.glue"
output: "openapi/{file_name}.{file_ext}"
Documented Configuration Example
Use this pattern when a project has one or more .glue files and wants repeatable generation for several consumers. The config below assumes source files live under schemas/ and writes all generated files under generated/.
global:
output_base_dir: generated
config:
watermark: short
lint_suppressions: true
typescript:
zod: false
python:
data_model_library: pydantic
base_model: pydantic.BaseModel
rust:
extra_derives:
structs: ["PartialEq"]
enums: ["Ord", "PartialOrd"]
unions: ["PartialEq"]
type_aliases: ["Ord", "PartialOrd"]
go:
package_name: commerce
protobuf:
package_name: commerce.v1
gen:
- mode: typescript
files:
- "schemas/*.glue"
output: "typescript/{file_name}.{file_ext}"
config_overrides:
typescript:
zod: true
- mode: python
files:
- "schemas/*.glue"
output: "python/{file_name}.{file_ext}"
config_overrides:
python:
data_model_library: dataclasses
- mode: openapi
files:
- "schemas/commerce_api.glue"
output: "openapi/commerce.{file_ext}"
- mode: protobuf
files:
- "schemas/models.glue"
output: "protobuf/{file_name}.{file_ext}"
Run it with:
# Auto-discover .gluerc.yaml from the current/input directory.
glue gen
# Or choose a config explicitly.
glue gen --config .gluerc.yaml
# Inline overrides are useful in CI or one-off local generation.
glue gen --config .gluerc.yaml --set "watermark=none"
Comprehensive Example
The following single-file example demonstrates the most important Glue features in a compact domain. It is intentionally heavily documented so agents can copy patterns from it.
// commerce.glue
//
// Glue files are meant to be the source of truth for data contracts.
// This example models a small commerce API that could generate TypeScript
// types, Python models, Rust structs, Go structs, OpenAPI, JSON Schema, or
// Protobuf from the same IDL.
/// Reusable scalar aliases keep domain intent visible in every model.
/// They currently alias primitive types, but the names carry semantic meaning.
type UserId = string
type OrderId = string
type ProductId = string
type IsoTimestamp = string
type MoneyCents = int
/// String enums are ideal for closed vocabularies and wire-level states.
/// Use values that should appear in JSON/OpenAPI payloads.
enum OrderStatus: "draft" | "submitted" | "paid" | "fulfilled" | "cancelled"
enum CurrencyCode: "USD" | "EUR" | "GBP"
/// A user account in the commerce system.
model User {
/// Stable unique identifier, reused across endpoints and related models.
id: UserId
/// Field metadata can provide generator hints such as aliases and examples.
/// Here the generated wire/schema name can be `email`.
@field(alias="email", example="ada@example.com")
email_address: string
/// Optional fields use `?`; this may be absent from incoming payloads.
display_name?: string
/// Defaults describe safe producer defaults and can be reflected downstream.
active: bool = true
/// Arrays use the `Type[]` suffix.
roles: string[]
/// Nested models are useful for concepts owned by exactly one parent type.
profile: Profile
model Profile {
created_at: IsoTimestamp
marketing_opt_in: bool = false
}
}
/// A product that can be ordered.
model Product {
id: ProductId
sku: string
name: string
active: bool = true
/// Records model dictionaries/maps. Prefer string keys for JSON-like maps.
attributes?: Record<string, string>
}
/// Money is modeled explicitly so amount and currency do not drift apart.
model Money {
amount_cents: MoneyCents
currency: CurrencyCode = "USD"
}
/// A single line item within an order.
model OrderItem {
product_id: ProductId
quantity: int = 1
unit_price: Money
}
/// The aggregate order model reused by multiple API responses.
model Order {
id: OrderId
user_id: UserId
status: OrderStatus = "draft"
items: OrderItem[]
total: Money
created_at: IsoTimestamp
submitted_at?: IsoTimestamp
}
/// Request payload for creating an order.
/// Named request/response models are easier to reuse and generate cleanly.
model CreateOrderRequest {
user_id: UserId
items: OrderItem[]
}
/// A standard paginated response shape.
model OrderListResponse {
orders: Order[]
next_cursor?: string
}
/// A standard API error shape. Use it consistently in 4XX/5XX responses.
model ApiError {
code: string
message: string
details?: Record<string, any>
}
/// List orders.
/// Endpoint paths can include placeholders such as `{user_id}`.
/// Rich query/path parameter modeling is intentionally limited today, so keep
/// path semantics clear in the endpoint name and docs.
endpoint "GET /users/{user_id}/orders" ListOrders {
headers: {
/// Optional request ID for tracing across services.
"X-Request-ID"?: string
}
responses: {
200: OrderListResponse
4XX: ApiError
5XX: ApiError
}
}
/// Create a new order from a JSON request body.
endpoint "POST /orders" CreateOrder {
body: CreateOrderRequest
responses: {
201: Order
4XX: ApiError
5XX: ApiError
}
}
/// Submit an existing order. Multiple explicit media types can be used when
/// an endpoint supports more than one request representation.
endpoint "POST /orders/{order_id}/submit" SubmitOrder {
body: {
"application/json": Order
}
responses: {
200: Order
4XX: ApiError
5XX: ApiError
}
}
Review Checklist
Before handing a Glue file back to a user, check:
- Imports are at the top and paths are correct.
- Every referenced type is declared or imported.
- Optional fields use
?; defaults use =.
- Enums are pipe-separated quoted string values.
- Endpoint responses include success and error cases.
- No unsupported generics, intersections, or invented decorators/config keys appear.
glue check passes when the CLI is available.
- Generated output paths are explicit when the user asked for code generation.