Extensions

Extension Config and Manifest

Reference for extension.config.json, the manifest.json the CLI builds from it, and the install directory layout.
Preview
Extensions are in preview and can change in breaking ways between releases. Use them in non-production environments only. See Stability. Written for Monospace 1.0.0, @monospace/cli 0.2 and @monospace/extension-kit 0.3.

Overview

An extension is a package that adds capabilities to your Monospace instance (a running Monospace deployment). It has two JSON files. You write the extension config (extension.config.json) in your project. The CLI builds it into the manifest (manifest.json), which is the file your instance reads.

extension.config.jsonmanifest.json
Written byYoumonospace extension build
Lives inYour project rootThe built extension directory
manifestVersionNot allowedRequired, always 1
$schemaOptional, for editor validationNot allowed; the instance rejects it

Both files share every other field and the same JSON Schema rules. Unknown fields are rejected at every level of both files. Omit optional fields instead of setting them to null.

The instance checks more than the schema when it loads a manifest. Some values pass the schema and monospace extension build but reject the extension at load time. Each entry type's reference lists its own load-time checks.

Write the Extension Config

This extension config belongs to a data connector extension for the Art Institute of Chicago API and declares its one entry. It is the config the Data Connector Quickstart builds. Every field inside the entry except type is defined by the data connector entry type:

extension.config.json
{
    "$schema": "./node_modules/@monospace/cli/schemas/extension.config.schema.json",
    "entries": [
        {
            "engine": {
                "entrypoint": "./src/data-connectors/artic/index.ts",
                "permissions": [
                    { "permission": "net:api.artic.edu", "reason": "Read artworks and artists from the Art Institute of Chicago API" }
                ]
            },
            "icon": "lucide:landmark",
            "id": "example/artic-connector",
            "name": "Art Institute of Chicago",
            "type": "dataConnector",
            "version": "0.1.0"
        }
    ],
    "icon": "lucide:landmark",
    "id": "example/artic",
    "name": "Art Institute of Chicago",
    "version": "0.1.0"
}

The $schema path resolves to the JSON Schema that ships with @monospace/cli, so your editor validates the file as you type. monospace extension create writes this line for you.

Extension Fields

These fields sit at the root of both files and apply to every extension, whatever its entries are.

FieldTypeRequiredRules
idstringYesThe extension's identifier, in id format
versionstringYesThe extension's release, in version format
namestringYesDisplay name. Must contain at least one non-whitespace character
descriptionstringNoDisplay description
iconstringNoIcon name. See Icons
iconForegroundstringNoGlyph color of a one-color icon. See Icons
iconBackgroundstringNoTile color behind the icon. See Icons
entriesarrayYesAt least one entry. See Entry Types
$schemastringNoExtension config only. Editor schema location; the build removes it
manifestVersionnumberManifest onlyAlways 1. Versions the manifest format, not your extension

Entry Types

An entry is one capability your extension contributes. Its type decides which other fields the entry has and what its code must do. The instance and the build reject any type not listed here.

typeEntryFields and code contract
dataConnectorA data connector: code that exposes an external data source as collectionsData Connector API

dataConnector is the only entry type today. Each entry type's reference defines the entry's other fields, how the instance runs it, and what it may access.

Id Format

Extension and entry ids use the same format, namespace/name:

  • Exactly one / separating two segments.
  • Each segment uses lowercase ASCII letters and digits, with single hyphens between groups.
  • No leading, trailing or repeated hyphens, no uppercase, no underscores, no whitespace.
IdValidReason
acme/art-instituteYes
123/456Yes
art-instituteNoNo namespace
Acme/art-instituteNoUppercase
acme/art--instituteNoRepeated hyphen
acme/art_instituteNoUnderscore
acme/art/instituteNoMore than one /

Entry ids are global across the instance. When two installed entries share an id, the instance loads the first one and skips the other with a warning. Two extensions with the same extension id follow the same rule. The instance scans extension directories in sorted order and skips the later one.

Version Format

version takes an exact SemVer 2.0.0 version. Prerelease and build metadata are allowed.

VersionValidReason
0.1.0Yes
2.3.0-beta.1+build.7Yes
1.0.0+001Yes
^1.0.0, latestNoRanges and tags are rejected
1.0NoIncomplete
01.0.0NoLeading zero
1.0.0-01NoLeading zero in a numeric prerelease

The instance doesn't compare versions. Overwriting an installed extension with a lower version downgrades it without complaint.

Icons

icon, iconForeground and iconBackground are free-form strings in both files. The extension and each entry set their own. Studio renders them as follows:

FieldRendered as
iconAn Iconify name in prefix:name form (e.g., lucide:landmark)
iconForegroundThe color of a one-color icon's glyph (e.g., #ffffff). A full-color logo keeps its own colors and ignores it. Without it, the glyph is white on a filled tile and a muted neutral otherwise
iconBackgroundThe color of the tile behind the icon (e.g., #8b5cf6). Without it, the tile uses the theme's neutral surface

Studio ignores a color value it can't parse. Both files reject iconColor as an unknown field.

Lucide icons (lucide:*) ship with Studio. Studio's extension list shows lucide:blocks for an extension without icon.

Capabilities

A data connector entry declares its query capabilities in engine.capabilities.query. Each value is a promise about what the connector's answers to writes carry, on every collection of every data source that uses the entry:

ValueThe connector promises
insertReturningThe answer to create carries the created items, each with every field in the request's select
updateReturningThe answers to updateMany and updateOne carry the updated items that way
deleteReturningThe answers to deleteMany and deleteOne carry the deleted items that way

The Stripe connector from Build a Stripe Data Connector answers every write with the items it wrote. Creates and updates answer the objects Stripe returns, and deletes answer the items read before deleting. Its entry declares all three:

engine in extension.config.json
{
    "capabilities": { "query": ["insertReturning", "updateReturning", "deleteReturning"] },
    "entrypoint": "./src/data-connectors/stripe/index.ts",
    "permissions": [
        { "permission": "net:api.stripe.com", "reason": "Read and write customers, products and prices through the Stripe API" }
    ]
}
RuleDetail
Shapecapabilities is an object whose only key is query. query is an array of unique values from the table above, in any order
AbsentAn absent capabilities, "capabilities": {} and "query": [] all declare no capability. monospace extension create scaffolds an entry without capabilities
Read-only connectorsDeclare nothing. The capabilities only change how Engine writes
In the manifestThe build copies engine.capabilities into manifest.json unchanged

Without a capability, Engine gets the written items through extra calls of its own, and the collection's declaration must carry them. Declare Query Capabilities lists those calls and the rules they add.

Engine reads the capabilities when it builds a workspace. A restart loads a build with new capabilities directly. With automatic reloading on, loading a build whose capabilities differ deactivates the data sources that use the entry until the instance restarts or the workspace is rebuilt. See Update Data Sources After an Extension Update.

monospace extension build rejects any other value or key before it writes anything. A value outside the three, including the same name in PascalCase (InsertReturning), fails with:

terminal
error: extension.config.json does not match the extension contract:
  /entries/0/engine/capabilities/query/0 must be one of "insertReturning", "updateReturning", "deleteReturning"
  Nothing was changed.
  fix: Correct each field listed above. Unknown fields are rejected.

An unknown key next to query fails with /entries/0/engine/capabilities has an unknown field "schema", and a value listed twice with /entries/0/engine/capabilities/query must NOT have duplicate items (items ## 0 and 1 are identical).

Build the Manifest

monospace extension build validates the extension config, bundles each entry, and writes manifest.json. See CLI for flags and errors.

The build changes three things and copies everything else unchanged, including an entry's engine.capabilities:

  1. Removes $schema.
  2. Adds "manifestVersion": 1 after the other fields.
  3. Points each entry at its built code. For a data connector entry, engine.entrypoint becomes the built file, named <entry id>.js.

The extension config above builds into this manifest, with the config's key order:

dist/manifest.json
{
    "entries": [
        {
            "engine": {
                "entrypoint": "example/artic-connector.js",
                "permissions": [
                    {
                        "permission": "net:api.artic.edu",
                        "reason": "Read artworks and artists from the Art Institute of Chicago API"
                    }
                ]
            },
            "icon": "lucide:landmark",
            "id": "example/artic-connector",
            "name": "Art Institute of Chicago",
            "type": "dataConnector",
            "version": "0.1.0"
        }
    ],
    "icon": "lucide:landmark",
    "id": "example/artic",
    "name": "Art Institute of Chicago",
    "version": "0.1.0",
    "manifestVersion": 1
}

The build also rejects what the schema alone can't catch:

ProblemResult
A key repeated in the same JSON objectThe build fails and names the key and line
Two entries with the same idThe build fails
Comments or trailing commasThe build fails; the file must be plain JSON

Install Directory Layout

Your instance loads extensions from the directory set by MONOSPACE_EXTENSIONS__INSTALL_LOCATION. It defaults to ./extensions, relative to the instance's working directory. See Install and Manage Extensions for how to place extensions there.

A running instance picks up changed files on its next start. With MONOSPACE_EXTENSIONS__AUTO_RELOAD on, it reloads them automatically once the files stop changing. Auto-reload is off by default and needs the install directory to exist when the instance starts. See Reload Extensions Automatically.

Each extension is one top-level directory with manifest.json at its root:

extensions/
extensions/
└── example__artic/
    ├── manifest.json
    └── example/
        └── artic-connector.js

The instance reads every top-level directory and ignores the directory name. It skips top-level symlinks, so place a real directory there, not a link to your build output. monospace extension build --install-to names the directory <namespace>__<name> after the extension id.

The instance skips an extension whose manifest fails validation, logs a warning, and loads the others. On a reload, an installed extension whose manifest has become invalid is treated as removed. For what that means for data connectors, see Before You Remove an Extension. Each entry type adds its own checks on the files an entry points to. For data connectors, see Entrypoint Rules.

See Also

MonospaceThe governed API layer for every app, person, and agent.

Copyright © 2026 Monospace Inc.