Skip to content
BetaDocs

Build an Extension

An Extension lives in its own repository. A typical repository contains an Extension manifest, a browser entry module, an optional provider backend, and deployment configuration for the provider’s chosen platform.

Use the Typeroll Extension starter as a GitHub template to get a working frontend, local host, provider example, manifest tooling, tests, and CI.

  • Node.js 22 or later;
  • a Typeroll organization and a site where you have admin permission;
  • an organization-scoped API key;
  • HTTPS origins for production assets and provider endpoints.

In Typeroll, open Developer → Extensions, register a lowercase namespaced Extension ID, and copy the client secret when the Extension uses admin SSO. The secret is shown once and must remain server-side.

An Extension ID belongs to one developer Organization on a Typeroll host; registering an ID that another Organization holds is refused with 409. Note the developer_org_id of your Extension (returned on registration and by read_developer_extension): your provider backend must check that every Typeroll token it accepts carries this value in its developer_org_id claim as well as your Extension ID in aud. See Bind every token to your developer organization. If you build from the Extension starter, add this check to its provider token verification.

  1. Add typeroll-extension.json to the repository root.
  2. Implement a browser module that exports mount(element, props, context).
  3. Build the frontend into immutable JavaScript and CSS assets.
  4. Put the exact SHA-256 asset hashes and public HTTPS URLs in the manifest.
  5. Validate and upload a draft.
  6. Install the draft on a development site owned by the same Typeroll organization and approve its requested scopes.
  7. Publish a private or unlisted version, or submit a public version for review.

The @typeroll/mcp-server package includes the typeroll developer CLI:

Terminal window
export TYPEROLL_API_URL="https://your-admin.example.com"
export TYPEROLL_API_KEY="your organization-scoped API key"
npx --package @typeroll/mcp-server typeroll extension validate
npx --package @typeroll/mcp-server typeroll extension push --draft
npx --package @typeroll/mcp-server typeroll extension install --site your-site-id \
--config extension-config.json
npx --package @typeroll/mcp-server typeroll extension configure --site your-site-id \
--installation your-installation-id --config extension-config.json
npx --package @typeroll/mcp-server typeroll extension promote 1.0.0

Local validation is a fast shape check. The portal remains authoritative for schema compatibility, trusted origins, permissions, configuration, and asset hash verification.

extension configure merges the schema-defined values in the JSON file into the current installation config and queues a production deploy by default. Omitted values, including masked secrets, are preserved. Use --no-deploy only when applying several changes, then trigger one deploy after the last update. The installation ID is returned by extension install and is also shown under Settings → Extensions.

Manifest v3 replaces the old gateway transport with a direct api contract. Provider requests must go straight from the browser to the provider backend; do not add a Typeroll relay or a customer Cloudflare Pages Function.

For a backend-free lead tool, first create the receiving form in Typeroll, declare it under the component’s form_bindings, and request forms:submit. The form ID must match on the installation’s site. Its fields and post-submit email or webhook actions remain managed in the ordinary Forms admin.

Draft and review versions can only be installed on sites owned by the Extension’s developer organization. Other organizations can only install a published version. A private Extension is installable by the organizations in allowed_org_ids and on the sites in allowed_site_ids. A site id is unique only within its organization, so name another organization’s site as {org_id}/{site_id}; a bare site id matches only a site of the developer organization itself. Typeroll refuses any other entry form. With the CLI, pass --site {org_id}/{site_id} to extension install and it adds that entry; a bare --site adds a bare id. You may change distribution while every saved version is still a draft; Typeroll updates those draft manifests and hashes together. Once any version has been published or submitted for review, distribution is locked.

The same developer API is available to agents as MCP tools when the MCP connection uses an organization-scoped key: list_developer_extensions, read_developer_extension, update_developer_extension, save_extension_version, publish_extension_version, set_extension_version_lifecycle and list_developer_extension_installations. Over REST these are the /api/developer/extensions/** routes the CLI calls, authenticated with Authorization: Bearer and an organization-scoped key; a site-scoped key is refused. Registering an Extension and rotating its client secret return the client_secret once, so they are done in the portal or the CLI and never through MCP, where the secret would land in the agent’s conversation. Publishing a release makes it available to every compatible installation, so publish only after review.

Everything a site administrator can do under Settings → Extensions is also available with a site administrator API key (an owned Site, a site-scoped key or an admin share) and through MCP. Paths are relative to /api/v1/sites/{siteId}/extensions.

Action MCP tool REST route
List and read installations list_extension_installations, read_extension_installation GET /, GET /{installationId}
Install install_extension POST /
Update configuration update_extension_installation_config PATCH /{installationId} with config
Activate a pending release activate_extension_release PATCH /{installationId} with version
Enable or disable set_extension_installation_status PATCH /{installationId} with status
Uninstall uninstall_extension DELETE /{installationId}
Diagnostics read_extension_diagnostics GET /{installationId}/diagnostics
Pair the token issuer pair_extension_issuer POST /{installationId}/pair
Open an admin page launch_extension_admin_page POST /{installationId}/launch
Call an approved native admin API call_extension_admin POST /{installationId}/admin-request

Notes:

  • install_extension grants only the granted_scopes you pass; each must be requested by the manifest. Install, enable, disable and uninstall do not deploy. Deploy afterwards to publish the change.
  • The server credential is rotated in the portal (Settings → Extensions), which shows the new tri_… credential once. It is not available through the API or MCP, so a new credential never lands in an agent’s conversation or a log.
  • launch_extension_admin_page returns a single-use launch code with the launch_url and the form fields to POST there before expires_at, for example from a browser automation tool. The provider sees the caller as api-key:{prefix}, and the page’s minimum_permission applies. Approved native admin pages are called directly with call_extension_admin instead.
  • Installation credentials cannot use these routes.

After installation, the component appears in the editor’s block picker. In HTML mode, use the provisioned block ID displayed under Settings → Extensions:

<x-extension
block="extension--install-abc--quote"
props='{"heading":"Your quote"}'
/>

This is an authoring directive, not a second script runtime. Preview and build replace it with the same inert mount shell used by a visual-editor block.

Published versions are immutable. Never replace asset bytes at a URL while reusing the same version and hashes. Release a new semantic version, upload it, and let Typeroll select it automatically for compatible installations.

An installation is a timeless connection to the Extension ID. Its approved scopes never expand automatically: a new release can declare another scope, but it cannot use that scope until a site administrator grants it separately. Keep component IDs, props, configuration, and provider data backward compatible. Use a new Extension ID for a deliberately breaking product.

If none of the published releases can run with the current runtime and stored configuration, Typeroll marks the Extension unavailable and omits it from the site build. Other pages and Extensions continue to build normally.

Continue with the frontend runtime and recipient-link contract.