Skip to content
Build
Markdown

Write a work

A JavaScript handler, a small manifest, and an immutable version each time you deploy.

A work has two parts: source code and a manifest describing its input, entry point, permissions, and data scope. The examples below illustrate the interface; deploy them only as part of an authorized task.

Write the handler

Export one default async function. Littleworks supplies input, db, shopify, context, and log. Return a JSON-compatible value or Response.json(body, { status }).

notes.save.js
export default async ({ input, db }) => {
  const notes = db.collection("notes");

  const note = await notes.create({
    message: input.message,
    status: "new",
  });

  return { id: note.id };
};

This example stores a note and returns its ID. It has no public endpoint and does not use Shopify. The manifest validates the message before the handler runs.

Describe the work

Manifest
{
  "name": "notes.save",
  "title": "Save a note",
  "feature": "notes",
  "environment": "preview",
  "trigger": {
    "type": "callable"
  },
  "permissions": {
    "shopify": false
  },
  "inputSchema": {
    "type": "object",
    "properties": {
      "message": {
        "type": "string",
        "minLength": 1,
        "maxLength": 1000
      }
    },
    "required": [
      "message"
    ],
    "additionalProperties": false
  }
}
FieldPurpose
nameStable work identifier, such as notes.save. Lowercase letter first, then lowercase letters, digits, dots, underscores, or hyphens; up to 80 characters.
titleMerchant-facing name, up to 100 characters. Optional description is limited to 400 characters.
featureData namespace shared by related works. Keep it unchanged when redeploying an existing work.
environmentpreview or production. Shopify admin calls production Live.
triggerPrivate callable, or an explicit HTTP GET or POST entry point.
permissions.shopifyWhether this work can use the Shopify GraphQL client.
inputSchemaJSON Schema checked before execution. Declare required properties and reject unexpected fields where appropriate.
runtimeOptional. The current supported profile is portable-js-v1.

Deploy and test

Call deploy_function with { manifest, source }. Pass the full manifest object and handler source string. The deployment saves an immutable version and activates it in that environment.

invoke_function arguments
{
  "name": "notes.save",
  "environment": "preview",
  "input": {
    "message": "A test note"
  },
  "idempotencyKey": "notes-preview-test-001"
}

Choose a new idempotency key for each intentional invocation. Reusing a key prevents replay with a conflict response; it does not return the original result. Use read_collection to inspect the note and list_runs to find the execution.

Move to Live

Deploy the tested source with environment: "production" when the merchant’s instructions authorize it. Preview records are not copied into Live. Redeploying a paused work leaves it paused; use set_function_state to resume it.

Keep the source self-contained

MCP accepts JavaScript ES modules without imports or TypeScript syntax. It does not install dependencies or run a hosted build. The local development CLI can bundle compatible source before deployment. Packages cannot bypass the runtime’s network or capability restrictions.

The context contains storeId, shop, environment, runId, and mode. Credentials and provider database bindings are never passed to your code. See Limits and security.