SDK for building Construct apps — MCP server helper, TypeScript types, and manifest validation.
pnpm add @construct-computer/app-sdkimport { ConstructApp } from '@construct-computer/app-sdk';
const app = new ConstructApp({ name: 'my-app', version: '1.0.0' });
app.tool('greet', {
description: 'Say hello to someone',
parameters: {
name: { type: 'string', description: 'Who to greet' },
},
handler: async (args) => `Hello, ${args.name}!`,
});
// That's it — export as a Cloudflare Worker
export default app;export default app handles everything automatically:
POST /mcp— MCP JSON-RPC endpoint (tool calls, tool listing, initialization)GET /health— health check for the Construct desktop- Static asset serving via the Cloudflare
ASSETSbinding (if configured inwrangler.toml) /ui/*path rewriting to/*(so dev matches the published URL structure)- CORS headers on every response (required for Construct desktop dev mode)
name = "my-construct-app"
main = "server.ts"
compatibility_date = "2024-12-01"
[assets]
directory = "./ui"
binding = "ASSETS"
not_found_handling = "none"
run_worker_first = ["/*"]The run_worker_first = ["/*"] setting ensures all requests hit your server first, so /mcp and /health are handled by the SDK before falling through to static assets.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>My App</title>
<link rel="stylesheet" href="https://registry.construct.computer/sdk/construct.css">
<script src="https://registry.construct.computer/sdk/construct.js"></script>
</head>
<body>
<h1>My App</h1>
<button id="greet-btn">Greet</button>
<div id="output"></div>
<script src="app.js"></script>
</body>
</html>The construct.js and construct.css script/link tags are required in your HTML. The Construct desktop strips them at load time and injects its own bridge that exposes the construct.* APIs.
Copy src/construct-global.d.ts into your ui/ directory for full autocomplete in your UI code:
/// <reference path="./construct.d.ts" />
construct.ready(() => {
construct.ui.setTitle('My App');
document.getElementById('greet-btn').addEventListener('click', async () => {
const result = await construct.tools.callText('greet', { name: 'World' });
document.getElementById('output').textContent = result;
});
});Pair with a jsconfig.json in ui/ for VS Code autocomplete:
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"checkJs": true
}
}Add $schema for IDE validation:
{
"$schema": "https://raw.githubusercontent.com/construct-computer/app-sdk/main/schemas/manifest.schema.json",
"name": "My App",
"description": "What it does in one sentence.",
"author": { "name": "Your Name" },
"icon": "ui/icon.svg",
"categories": ["utilities"],
"tags": ["my-tag"],
"ui": {
"entry": "ui/index.html",
"width": 480,
"height": 600
}
}my-construct-app/
├── manifest.json # App metadata (required)
├── server.ts # MCP server entry point (required)
├── wrangler.toml # Cloudflare Workers config
├── tsconfig.json # TypeScript config (server)
├── package.json
├── ui/ # Visual interface (optional — omit for tools-only apps)
│ ├── index.html # UI entry point
│ ├── app.js # UI logic
│ ├── construct.d.ts # SDK type declarations (copy from app-sdk)
│ ├── jsconfig.json # JS project config (enables autocomplete)
│ └── icon.svg # App icon (256x256, PNG/SVG/JPG)
└── screenshots/ # Optional store screenshots
└── 1.png
The main class. Handles JSON-RPC 2.0 routing, tool registration, auth extraction, asset serving, and CORS.
import { ConstructApp } from '@construct-computer/app-sdk';
const app = new ConstructApp({ name: string, version: string });
// Register tools
app.tool('name', {
description: 'What the AI sees when deciding whether to use this tool',
parameters: {
input: { type: 'string', description: 'The input value' },
mode: { type: 'string', enum: ['a', 'b'], description: 'Operation mode' },
},
handler: async (args, ctx) => {
// Return string for simple text results
return 'result text';
// Or return ToolResult for complex/error content
return { content: [{ type: 'text', text: '...' }], isError: true };
},
});
// Export as Cloudflare Worker — handles MCP, health, assets, and CORS
export default app;You can also use the createApp() factory:
import { createApp } from '@construct-computer/app-sdk';
const app = createApp({ name: 'my-app', version: '1.0.0' });Available in every tool handler as the second argument:
interface RequestContext {
userId?: string; // From x-construct-user header
auth?: { // From x-construct-auth header
type: 'oauth2' | 'api_key' | 'bearer' | 'basic';
access_token?: string; // OAuth2
refresh_token?: string; // OAuth2
expires_at?: number; // OAuth2
[key: string]: unknown; // Dynamic fields from api_key/bearer/basic schemes
};
isAuthenticated: boolean; // Whether valid credentials are present
request: Request; // Raw incoming request
env: Record<string, string>; // App environment variables from x-construct-env
}The auth shape depends on the scheme configured in your manifest.json:
oauth2:access_token,refresh_token,expires_atapi_key/bearer/basic: fields match thenamevalues in yourauth.schemes[].fields[]definition
Throws if the user hasn't connected their account. Use in tools that need authentication:
import { requireAuth } from '@construct-computer/app-sdk';
app.tool('my_private_tool', {
description: 'Needs auth',
handler: async (args, ctx) => {
requireAuth(ctx); // throws if not authenticated
const res = await fetch('https://api.example.com', {
headers: { Authorization: `Bearer ${ctx.auth!.access_token}` },
});
return await res.text();
},
});The Construct platform injects these globals into every app iframe:
| API | Description |
|---|---|
construct.ready(callback) |
Run code when the SDK bridge is ready |
construct.tools.call(name, args) |
Call a tool, get the full result object |
construct.tools.callText(name, args) |
Call a tool, get just the text result |
construct.ui.setTitle(title) |
Update the window title bar |
construct.ui.getTheme() |
Get the current theme ({ mode, accent }) |
construct.ui.close() |
Close the app window |
construct.state.get() |
Read persistent app state |
construct.state.set(state) |
Write persistent app state |
construct.state.onUpdate(callback) |
Subscribe to state changes (from agent or other tabs) |
construct.agent.notify(message) |
Send a message to the AI agent |
CSS variables (--c-bg, --c-surface, --c-text, --c-accent, etc.) and utility classes are provided by construct.css for theme-aware styling.
Configure auth schemes in manifest.json. The SDK supports four types:
OAuth2:
{
"auth": {
"schemes": [{
"type": "oauth2",
"authorization_url": "https://example.com/oauth/authorize",
"token_url": "https://example.com/oauth/token",
"scopes": ["read", "write"]
}]
}
}API Key / Bearer / Basic:
{
"auth": {
"schemes": [{
"type": "api_key",
"label": "Connect your API key",
"instructions": "Get your API key from https://example.com/settings",
"fields": [
{ "name": "api_key", "displayName": "API Key", "type": "password", "required": true }
]
}]
}
}Credential values are delivered to your tool handlers via ctx.auth.
pnpm dev # Start local dev server (wrangler dev)Test your MCP endpoint:
# Health check
curl http://localhost:8787/health
# List tools
curl -X POST http://localhost:8787/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
# Call a tool
curl -X POST http://localhost:8787/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"greet","arguments":{"name":"World"}}}'With the dev server running:
- Open Construct > Settings > Developer
- Toggle Developer Mode on
- Under Connect Dev Server, paste
http://localhost:8787and click Connect
Construct calls your server's /health and /mcp endpoints to register the app, and opens your UI in a sandboxed window.
- Push your app to a public GitHub repository
- Fork construct-computer/app-registry
- Add
apps/your-app-id.json:{ "repo": "https://github.com/you/your-app", "versions": [ { "version": "1.0.0", "commit": "<40-char SHA>", "date": "2026-04-16" } ] } - Open a PR — CI validates automatically
- Once merged, your app appears in the Construct App Store
See the full guide at registry.construct.computer/publish.
- Construct Platform
- App Store
- Publishing Guide
- Sample App (Text Tools) — template repo for new apps