Build your first engine
This guide creates a small onboarding capability and publishes the same implementation through application code, the CLI, and MCP. It stays deterministic so you can inspect the harness-independent Invokta boundary before connecting a model or another provider.
Generate the complete standalone version with npm create invokta-engine@latest my-engine and choose the complete profile when prompted,
or follow the steps below to assemble the same boundary manually. Non-terminal
automation can pass --profile complete; pseudo-TTY automation also passes
--yes.
-
Define the capability
Create
src/engine.ts:import { createEngine, defineCapability } from "@invokta/core";import { z } from "zod";const createWelcomeMessage = defineCapability({title: "Create a welcome message",description: "Create a welcome message for a new team member.",input: z.object({name: z.string().trim().min(1),}),output: z.object({message: z.string().min(1),}),access: "public",annotations: {readOnly: true,destructive: false,idempotent: true,openWorld: false,},async run({ input }) {return { message: `Welcome, ${input.name}!` };},});export const engine = createEngine({name: "hello-engine",version: "1.0.0",capabilities: {"onboarding.create-welcome-message": createWelcomeMessage,},});The key in
capabilitiesis the public capability ID. Consumers do not need to know how the welcome message is produced. -
Invoke it directly
Create
src/direct.ts:import { engine } from "./engine.js";const result = await engine.invoke("onboarding.create-welcome-message",{ name: "Ada" },{ source: "direct", principal: null },);process.stdout.write(`${JSON.stringify(result)}\n`);engine.invokevalidates the input, applies the access rule, runs the capability, and validates the result before returning it. -
Add a CLI composition root
Create
src/cli.ts:import { runCli } from "@invokta/cli";import { engine } from "./engine.js";process.exitCode = await runCli(engine, {principal: { id: "local:developer" },});Build the project, then inspect and run the capability:
Terminal window node dist/cli.js listnode dist/cli.js describe onboarding.create-welcome-messagenode dist/cli.js run onboarding.create-welcome-message --input '{"name":"Ada"}' -
Add an MCP stdio composition root
Create
src/mcp-stdio.ts:import { serveMcpStdio } from "@invokta/mcp";import { engine } from "./engine.js";await serveMcpStdio(engine, {principal: { id: "local:mcp-host" },});Start the compiled file directly with Node. Standard output is reserved for MCP protocol messages.
-
Inspect interactively with the devtools
Install
@invokta/devtoolsas a development dependency — projects generated bycreate-invokta-enginealready include it — then serve the built module:Terminal window npx @invokta/devtools serve dist/engine.jsThe inspector on
http://localhost:4100/lists the capability with its input and output JSON Schemas and invokes it from a schema-seeded editor. The Adapter switch chooses which execution path carries the call — direct, CLI, MCP stdio, or MCP HTTP — and shows what that path exchanged. A live trace records each invocation’s outcome and duration, and test identities mint developmentPrincipalvalues, soaccessrules can be exercised as different actors.
Next, read how execution channels converge on the same runtime path.