npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@construct-computer/app-sdk

v0.2.6

Published

SDK for building Construct apps — MCP server helper, TypeScript types, and dev tools

Readme

npm License: MIT

@construct-computer/app-sdk

SDK for building Construct apps — MCP server helper, TypeScript types, and manifest validation.

Quick Start

pnpm add @construct-computer/app-sdk

Server (server.ts)

import { 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 ASSETS binding (if configured in wrangler.toml)
  • /ui/* path rewriting to /* (so dev matches the published URL structure)
  • CORS headers on every response (required for Construct desktop dev mode)

wrangler.toml

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.

UI (ui/index.html)

<!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.

UI Types (ui/construct.d.ts)

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
  }
}

Manifest (manifest.json)

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
  }
}

App Structure

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

API Reference

ConstructApp

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' });

RequestContext

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_at
  • api_key / bearer / basic: fields match the name values in your auth.schemes[].fields[] definition

requireAuth(ctx)

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();
  },
});

Client-side SDK (construct.*)

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.

Authentication

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.

Development

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"}}}'

Testing in Construct

With the dev server running:

  1. Open Construct > Settings > Developer
  2. Toggle Developer Mode on
  3. Under Connect Dev Server, paste http://localhost:8787 and click Connect

Construct calls your server's /health and /mcp endpoints to register the app, and opens your UI in a sandboxed window.

Publishing

  1. Push your app to a public GitHub repository
  2. Fork construct-computer/app-registry
  3. 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" }
      ]
    }
  4. Open a PR — CI validates automatically
  5. Once merged, your app appears in the Construct App Store

See the full guide at registry.construct.computer/publish.

Links