docs

Platform SDK

Define a model. Validate it before it runs.

Für alle, die auf der Plattform bauen: Hier steht, wie das System aufgebaut ist, welche Teile des SDK Sie nutzen können und wie Apps für Assistenten entstehen. Die Seite ist auf Englisch, weil Code, Schnittstellen und Fehlermeldungen es auch sind.

Applications import @platform/sdk or a specific subpath. Start with a small domain definition, check incoming JSON, and keep network calls and storage outside the model.

Available in the Platform workspace. External package distribution is not open yet. To use the services without the SDK, start with the apps or connect ChatGPT.

Reach every live surface with one ID

Each surface below is an MCP server behind the platform's own sign-in. A hosted assistant adds the address as a custom connector and signs in as you; a person building or operating on the platform signs in once from a terminal with platform login --env dev for the compass-platform.dev surfaces and platform login --env prod for those on compass-platform.com, then lets platform agent compose the same servers for Claude Code or Codex. The table is rendered from the service catalogue, so it cannot say more than the catalogue does.

SurfaceConnector addressChatGPT clientClaude client
Memoryhttps://memory.compass-platform.com/mcpchatgpt-memory unmeasuredclaude-ai-memory unmeasured
Ticketshttps://tickets.compass-platform.com/mcpchatgpt-tickets unmeasuredclaude-ai-tickets unmeasured
Decisionshttps://decisions.compass-platform.com/mcpchatgpt-decisions unmeasuredclaude-ai-decisions unmeasured
System Maphttps://map.compass-platform.com/mcpchatgpt-map unmeasuredclaude-ai-map unmeasured
Factoryhttps://factory.compass-platform.com/mcpchatgpt-factory unmeasuredclaude-ai-factory unmeasured

First workflow Connect Decisions and Tickets, ask the assistant to inspect one open decision, rule it yourself in the Factory, then have the assistant open the ticket that ruling implies. The getting started journey walks the same path with prompts you can paste.

Find the import for your app

The package root exports SDK_VERSION and SURFACE. Check where an import can run: in any JavaScript environment, in the browser, on the server, or during a build.

discover.tsAvailable imports · TypeScript
import { SDK_VERSION, SURFACE } from "@platform/sdk"

// Show the first three available imports.
console.log("Compass Platform SDK", SDK_VERSION)
for (const entry of SURFACE.slice(0, 3)) {
  console.log(entry.specifier, entry.runtime)
}

Expected resultNot run on this page

Compass Platform SDK 0.1.0
@platform/sdk/domain universal
@platform/sdk/domain/core universal
@platform/sdk/domain/schema universal

Output from the reference SDK baseline. Your checkout may list different imports or a different package version.

Next: define an order using the domain core.

Define an order and its rules

Describe the states an order can have and the changes it accepts. Use @platform/sdk/domain/core to check the definition before connecting it to storage or other services.

  1. DefineCheck the model
  2. CompilePrepare an inert plan
  3. BindResolve approved functions

Save this as order.domain.ts in a Platform workspace package that depends on @platform/sdk, then run bun order.domain.ts. It prints Order without creating records, making network calls, or granting permission.

order.domain.tsOrder definition · TypeScript
import type { DomainDefinitionJSON } from "@platform/sdk/domain/schema"
import { bindDomain, compileDomain, defineDomain } from "@platform/sdk/domain/core"

export const orderDomain = {
  schemaVersion: "domain-def@5",
  name: "Order",
  version: "1.0.0",
  description: "An order that can be placed once.",
  types: {
    OrderState: { status: "'open' | 'placed'" },
    Open: { status: "'open'" },
    Empty: { "+": "reject" },
  },
  aggregates: {
    Order: {
      description: "One order, identified by orderId.",
      urnPattern: "urn:shop:order:{orderId}", identity: "{orderId}",
      state: { type: "OrderState", initial: { status: "open" } },
      commands: {
        place: {
          description: "Place an open order.", inputType: "Empty",
          cases: [{ when: "Open", emit: [{ type: "Placed", payload: {} }] }],
        },
      },
      events: {
        Placed: { description: "The order was placed.", type: "Empty",
          apply: [{ op: "set", path: "status", value: "placed" }] },
      },
    },
  },
  resources: {},
  authz: {
    scopes: {},
    policies: {},
    defaults: { unauthenticatedAccess: "deny", missingRolesBehavior: "deny" },
  },
} as const satisfies DomainDefinitionJSON

const definition = defineDomain(orderDomain)
const compiled = compileDomain(definition) // no I/O and no authority
const bound = bindDomain(compiled, { maps: {}, resources: {}, events: {} })

console.log(bound.compiled.source.definition.name) // Order

Expected resultNot run on this page

Order

The definition passed its checks. No data was saved and no service was called.

Next: see what happens when you place the order.

What do define, compile and bind do?

defineDomain preserves literal inference, compileDomain validates and prepares deterministic behavior, and bindDomain resolves symbolic references against a reviewed catalog.

Authority boundary A compiled domain is inert until you bind approved effects. Binding is not permission to act. The core preserves scope and confirmation metadata; it does not enforce caller authorization or human approval. Application adapters must enforce those gates and own network calls, identity, clocks, persistence, and external tools.

See a command change state

An aggregate keeps state and the rules that change it together. Here, an open order accepts place, emits Placed, and becomes placed. The pure kernel simulates that transition without writing to a store. A second place is refused by the state rule.

  1. OpenAccept place
  2. Placed eventApply the declared change
  3. PlacedRefuse another place

Save as simulate.ts beside order.domain.ts and run bun simulate.ts. After the imported example's Order line, it prints open → placed.

simulate.tsOrder simulation · TypeScript
import { apply, compileDomain, decide, defineDomain, initialState } from "@platform/sdk/domain/core"
import { orderDomain } from "./order.domain"

const plan = compileDomain(defineDomain(orderDomain))
const initial = initialState(plan, "Order")
if (!initial.ok) throw new Error(initial.error.code)
const decision = decide(plan, "Order", initial.value, {
  name: "place", input: {}, identity: "example-1",
})
if (!decision.ok) throw new Error(decision.error.code)
let state = initial.value
for (const event of decision.value.events) {
  const next = apply(plan, "Order", state, event, {
    occurred_at: "2026-09-08T12:00:00.000Z", // explicit simulation time
  })
  if (!next.ok) throw new Error(next.error.code)
  state = next.value
}
console.log(initial.value.status, "→", state.status) // open → placed

Expected resultNot run on this page

Order
open → placed

Importing the definition prints the first line. The second line shows the state change in memory; it is not a saved order.

Next: check a definition received as JSON.

From simulation to a running workflow

loadAggregate and executeCommand adapt aggregates to event streams. Generated aggregate MCP tools expose commands allowed by state; the host still enforces caller permission. Process runners support human suspension/resumption, explicit retry policies and call budgets. The host supplies inference and tool bindings, identity, clocks and storage.

Check a definition received as JSON

Use parseDomainDefinition(input: unknown) for data supplied by a caller. If the definition is valid, you receive a normalized copy. Otherwise, each error identifies the field to fix.

Save beside the previous example as parse-domain.ts. Run bun parse-domain.ts: the imported example prints Order, then this script prints the canonical JSON. The example uses the order definition as its input; replace that stand-in with your caller's input.

parse-domain.tsJSON input · uses order.domain.ts above · TypeScript
import { compileDomain, parseDomainDefinition } from "@platform/sdk/domain/core"
import { orderDomain } from "./order.domain"

// Stand-in for JSON supplied by your caller. Replace with their input.
const receivedText = JSON.stringify(orderDomain)
const input: unknown = JSON.parse(receivedText)
const parsed = parseDomainDefinition(input)

if (!parsed.ok) {
  for (const diagnostic of parsed.error.diagnostics) {
    console.error(diagnostic.code, diagnostic.path, diagnostic.message)
  }
  throw new Error("Domain definition is invalid")
}

// Review any diagnostics before connecting the model to services.
for (const diagnostic of parsed.value.diagnostics) {
  console.warn(diagnostic.code, diagnostic.path, diagnostic.message)
}
const compiled = compileDomain(parsed.value)
// The compiled result remains inert until an approved catalog is bound.
console.log(compiled.source.canonicalJson) // canonical definition JSON

Expected resultNot run on this page

Prints Order, then the validated definition as canonical JSON.

Result summary, not a literal console transcript. The complete JSON includes the order state, command and event rules.

Understand validation results

A malformed JSON string fails at JSON.parse; a parsed but invalid definition returns diagnostics. No invalid definition reaches compilation. Successful parsing can also return diagnostics; review them and resolve affected adapter paths before activating the model. Literal and JSON inputs share the same structural, reference, semantic, and policy checks.

Next: connect a tool to its view.

Connect an MCP tool to its view

An interactive MCP result is two related contracts: the tool advertises a predeclared UI resource, and the host reads that resource when it renders the result. Structured data must remain useful even when the visual is unavailable.

Metadata shape only. This fragment does not register a tool or resource and cannot render a widget by itself. Your MCP adapter must serve the declared resource, and the client must support MCP Apps.

interactive-tool.tsMetadata shape only · TypeScript
interface InteractiveToolMetadata {
  _meta: {
    ui: { resourceUri: `ui://decisions/briefing.html` }
  }
}

const metadata: InteractiveToolMetadata = {
  _meta: { ui: { resourceUri: "ui://decisions/briefing.html" } },
}

Expected resultNot run on this page

Metadata only · no widget rendered

Defines the resource address ui://decisions/briefing.html. This fragment prints nothing. Register the tool, serve that resource, and test it in a host that supports MCP Apps.

Tool result

Validated data for the model and any client that does not mount the visual.

UI resource

The address of the view the host can load. The view must be served separately.

Use the root workspace today

Platform applications currently consume the SDK through the repository's single Bun workspace. Do not create a nested workspace or import an implementation package as a competing application-facing authority.

Forge, the platform's Git service, holds your repositories under your platform sign-in: git.compass-platform.dev.

Distribution status

External package distribution is not open yet. A reproducible Docker toolchain and the eventual package delivery contract are not an installation path offered by this reference.

Updating an existing integration? See the release notes for the import contract, compatibility guidance and checks.

Powered by Compass Platform™