Skip to content

Manifest Reference

Every Stina extension must include a manifest.json file at its root. This file declares the extension’s identity, required permissions, and everything it contributes to Stina. The manifest is validated at install time using a Zod schema defined in the @stina/extension-api package.

FieldTypeDescription
idstringUnique extension identifier. Must be lowercase alphanumeric characters and hyphens only (regex: ^[a-z0-9-]+$). Example: "my-extension"
namestringHuman-readable display name shown in the UI. Must be non-empty. Example: "My Extension"
versionstringSemver version string. Must match the pattern X.Y.Z. Example: "1.0.0"
descriptionstringShort description of what the extension does. Must be non-empty.
authorobjectAuthor information. See Author Object.
mainstringEntry point file, relative to the extension root. This is the JavaScript file loaded when the extension is activated. Example: "index.js"
permissionsstring[]List of required permissions. The user is shown these at install time. See Permissions.
FieldTypeRequiredDescription
namestringYesAuthor or organization name. Must be non-empty.
urlstringNoAuthor URL. Must be a valid URL if provided.
{
"author": {
"name": "Stina Team",
"url": "https://github.com/einord"
}
}
FieldTypeDefaultDescription
$schemastringJSON Schema URL for IDE validation and autocompletion. Use "https://stina.app/schemas/extension-manifest.json".
type"provider" | "tools"Extension type. Use "provider" for AI model providers and "tools" for feature extensions. Used for categorization in the extension registry.
repositorystringGitHub repository URL. Must be a valid URL. Example: "https://github.com/einord/stina-ext-mail"
licensestringSPDX license identifier. Example: "MIT"
enginesobjectEngine compatibility requirements. See Engines.
platforms("web" | "electron" | "tui")[]All platformsRestrict the extension to specific platforms. If omitted, the extension is available on all platforms.
contributesobjectDeclares what the extension adds to Stina. See Contributes.

The engines field specifies minimum version requirements. Currently only stina is supported.

{
"engines": {
"stina": ">=0.5.0"
}
}
FieldTypeDescription
stinastringMinimum Stina version required. Uses semver range syntax.

The permissions array declares what capabilities the extension needs. Users see these at install time and can decide whether to trust the extension.

PermissionDescription
network:*Unrestricted network access to any host
network:localhostAccess to localhost only
network:localhost:<port>Access to a specific localhost port (e.g., network:localhost:11434)
network:<host>Access to a specific host (e.g., network:api.openai.com)
PermissionDescription
storage.collectionsCreate and use document collections for persistent data
secrets.manageStore and retrieve secrets (API keys, tokens, passwords)
PermissionDescription
user.profile.readRead the user’s profile information (name, locale)
user.location.readRead the user’s location
chat.history.readRead past chat conversations
chat.current.readRead the current chat conversation
PermissionDescription
provider.registerRegister as an AI model provider
tools.registerRegister tools the AI can call
actions.registerRegister actions (internal functions callable from UI components)
settings.registerRegister user-configurable settings
commands.registerRegister slash commands
panels.registerRegister right-side panel views
events.emitEmit custom events
scheduler.registerRegister scheduled/recurring tasks
chat.message.writeSend messages to the chat programmatically
background.workersRun background worker threads
PermissionDescription
files.readRead files from the local filesystem
files.writeWrite files to the local filesystem
clipboard.readRead from the system clipboard
clipboard.writeWrite to the system clipboard

The contributes field declares what the extension adds to Stina. All sub-fields are optional. You only include the sections relevant to your extension.

{
"contributes": {
"providers": [],
"tools": [],
"commands": [],
"settings": [],
"toolSettings": [],
"panels": [],
"storage": {},
"prompts": []
}
}

Type: ProviderDefinition[]

For AI provider extensions. Each entry declares a provider that can serve AI models. Provider extensions typically have type: "provider" and require the provider.register permission.

FieldTypeRequiredDescription
idstringYesUnique provider identifier within this extension.
namestringYesDisplay name shown in the provider selection UI.
descriptionstringNoShort description of the provider.
suggestedDefaultModelstringNoModel name to suggest when the user first configures this provider.
defaultSettingsRecord<string, unknown>NoDefault values for provider settings (e.g., { "url": "http://localhost:11434" }).
configSchemaProviderConfigSchemaNoSchema for the provider-specific configuration UI. See Provider Config Schema.

The configSchema generates a settings form in the UI for configuring the provider.

FieldTypeRequiredDescription
propertiesRecord<string, ProviderConfigProperty>YesProperty definitions, keyed by setting name.
orderstring[]NoDisplay order of properties. Defaults to object key order.
FieldTypeRequiredDescription
typestringYesUI control type. One of: "string", "number", "boolean", "select", "password", "url".
titlestringYesDisplay label for the field.
descriptionstringNoHelp text shown below the input.
defaultunknownNoDefault value.
requiredbooleanNoWhether the field must be filled.
placeholderstringNoPlaceholder text for input fields.
options{ value: string, label: string }[]NoFor select type: the available options.
validationProviderConfigValidationNoValidation rules. See below.

Property types:

  • string — Free-text input field.
  • number — Numeric input field.
  • boolean — Toggle/checkbox.
  • select — Dropdown with predefined options.
  • password — Masked input field (value stored securely).
  • url — URL input field with URL validation.

ProviderConfigValidation:

FieldTypeDescription
patternstringRegex pattern the value must match.
minLengthnumberMinimum string length.
maxLengthnumberMaximum string length.
minnumberMinimum numeric value.
maxnumberMaximum numeric value.
{
"providers": [
{
"id": "ollama",
"name": "Ollama",
"description": "Local AI models via Ollama",
"suggestedDefaultModel": "llama3.2:8b",
"defaultSettings": {
"url": "http://localhost:11434",
"thinking": "off"
},
"configSchema": {
"order": ["url", "thinking"],
"properties": {
"url": {
"type": "url",
"title": "Server URL",
"description": "URL to your Ollama server",
"placeholder": "http://localhost:11434",
"default": "http://localhost:11434",
"required": true
},
"thinking": {
"type": "select",
"title": "Thinking Mode",
"description": "Enable thinking/reasoning for supported models",
"default": "off",
"options": [
{ "value": "off", "label": "Off" },
{ "value": "on", "label": "On" },
{ "value": "medium", "label": "Medium (extended)" }
]
}
}
}
}
]
}

Type: ToolDefinition[]

Tools the AI can call during conversations. Each tool is invoked by Stina when the AI decides to use it. The actual implementation is registered in your extension’s code; the manifest only declares metadata.

Requires the tools.register permission.

FieldTypeRequiredDescription
idstringYesUnique tool identifier within this extension. Used in code to match the tool handler.
nameLocalizedStringYesDisplay name. Supports localization.
descriptionLocalizedStringYesDescription of what the tool does. The AI receives the English text (or fallback) to decide when to use the tool. Supports localization.
parametersRecord<string, unknown>NoJSON Schema describing the tool’s parameters.
confirmationToolConfirmationConfigNoIf set, user must confirm before the tool runs.
FieldTypeRequiredDescription
promptLocalizedStringNoCustom confirmation prompt shown to the user. If omitted, a generic prompt like “Allow {toolName} to run?” is used.
{
"tools": [
{
"id": "mail_list_recent",
"name": "List Recent Emails",
"description": "List recent emails from configured accounts."
},
{
"id": "mail_send",
"name": { "en": "Send Email", "sv": "Skicka e-post" },
"description": { "en": "Send an email", "sv": "Skicka ett e-postmeddelande" },
"confirmation": {
"prompt": { "en": "Allow sending this email?", "sv": "Tillåt att skicka detta e-postmeddelande?" }
}
}
]
}

Type: CommandDefinition[]

Slash commands that users can type in the chat input (e.g., /weather). Requires the commands.register permission.

FieldTypeRequiredDescription
idstringYesCommand identifier. This is what follows the / in the chat. For example, "weather" creates the /weather command.
namestringYesDisplay name shown in the command palette/autocomplete.
descriptionstringYesDescription shown alongside the command in the UI.
{
"commands": [
{
"id": "weather",
"name": "/weather",
"description": "Get weather forecast for your location"
}
]
}

Type: SettingDefinition[]

User-configurable settings that appear in the extension’s settings page. Requires the settings.register permission.

FieldTypeRequiredDescription
idstringYesSetting identifier. Automatically namespaced by the extension ID.
titlestringYesDisplay label in the settings UI.
descriptionstringNoHelp text shown below the input.
typestringYesSetting type. One of: "string", "number", "boolean", "select".
defaultunknownNoDefault value.
options{ value: string, label: string }[]NoFor select type: static list of options.
optionsToolIdstringNoFor select type: tool ID that returns dynamic options.
optionsParamsRecord<string, unknown>NoParameters to pass to the options tool.
optionsMappingSettingOptionsMappingNoMapping from the options tool response to option values/labels.
createToolIdstringNoFor select type: tool ID for creating a new option inline.
createLabelstringNoLabel for the create action button.
createFieldsSettingDefinition[]NoFields shown in the inline create form. Recursive — uses the same SettingDefinition structure.
createParamsRecord<string, unknown>NoStatic parameters always sent to the create tool.
createMappingSettingCreateMappingNoMapping from the create tool response to the new option value.
validationobjectNoValidation rules. See below.

Setting types:

  • string — Free-text input field.
  • number — Numeric input field.
  • boolean — Toggle/checkbox.
  • select — Dropdown. Must provide either options (static) or optionsToolId (dynamic).

Validation rules:

FieldTypeDescription
requiredbooleanWhether the field must be filled.
minnumberMinimum value (for numbers) or minimum length (for strings).
maxnumberMaximum value (for numbers) or maximum length (for strings).
patternstringRegex pattern the value must match.

SettingOptionsMapping (for optionsToolId):

FieldTypeRequiredDescription
itemsKeystringYesKey for the items array in the tool result data.
valueKeystringYesKey for the option value within each item.
labelKeystringYesKey for the option display label within each item.
descriptionKeystringNoKey for an optional description within each item.

SettingCreateMapping (for createToolId):

FieldTypeRequiredDescription
resultKeystringNoKey for the result data object in the tool response.
valueKeystringYesKey for the new option’s value (defaults to "id").
{
"settings": [
{
"id": "gmail_client_id",
"title": "Gmail Client ID",
"description": "OAuth2 Client ID from Google Cloud Console for Gmail access",
"type": "string"
},
{
"id": "outlook_tenant_id",
"title": "Outlook Tenant ID",
"description": "Azure AD Tenant ID (use 'common' for multi-tenant)",
"type": "string",
"default": "common"
}
]
}

Type: ToolSettingsViewDefinition[]

Declarative UI views for managing extension-specific data. These appear as tabs in the extension’s settings page and can display lists, forms, or custom component layouts. There are two view kinds: list and component.

Requires the actions.register permission (for component views) or tools.register (for list views).

FieldTypeRequiredDescription
idstringYesUnique view identifier within the extension.
titlestringYesDisplay title shown as the tab/section label.
descriptionstringNoHelp text describing the view’s purpose.
viewToolSettingsViewYesView configuration. Either a list view or a component view.
fieldsSettingDefinition[]NoField definitions for create/edit forms. Uses the same SettingDefinition structure as contributes.settings. Primarily used with list views to define the add/edit form fields.

A list view displays items from a tool-backed data source with built-in search, pagination, and CRUD operations.

{
"view": {
"kind": "list",
"listToolId": "people_list",
"getToolId": "people_get",
"upsertToolId": "people_upsert",
"deleteToolId": "people_delete",
"mapping": {
"itemsKey": "people",
"countKey": "count",
"idKey": "id",
"labelKey": "name",
"descriptionKey": "description",
"secondaryKey": "relationship"
},
"searchParam": "query",
"limitParam": "limit",
"idParam": "id"
}
}

List view fields:

FieldTypeRequiredDescription
kind"list"YesMust be "list".
listToolIdstringYesTool ID called to list items.
getToolIdstringNoTool ID called to fetch a single item’s details (for edit forms).
upsertToolIdstringNoTool ID called to create or update an item.
deleteToolIdstringNoTool ID called to delete an item.
mappingToolSettingsListMappingYesMaps tool response data to UI fields. See below.
searchParamstringNoParameter name sent to the list tool for search queries. Default: "query".
limitParamstringNoParameter name sent to the list tool for pagination limit. Default: "limit".
idParamstringNoParameter name sent to get/delete tools for the item ID. Default: "id".
listParamsRecord<string, unknown>NoStatic parameters always sent to the list tool.

ToolSettingsListMapping:

FieldTypeRequiredDescription
itemsKeystringYesKey for the items array in the tool’s result data.
countKeystringNoKey for the total count in the tool’s result data (for pagination).
idKeystringYesKey for the item’s unique identifier.
labelKeystringYesKey for the item’s primary display label.
descriptionKeystringNoKey for the item’s description text.
secondaryKeystringNoKey for a secondary label displayed below or beside the primary.

A component view uses the declarative component DSL to render custom UI. Data is fetched via actions and made available as scope variables.

{
"view": {
"kind": "component",
"data": {
"settings": {
"action": "getSettings",
"refreshOn": ["mail.settings.changed"]
}
},
"content": {
"component": "VerticalStack",
"gap": 1,
"children": [
{
"component": "TextInput",
"label": "Instruction",
"value": "$settings.instruction",
"onChangeAction": {
"action": "updateSetting",
"params": { "key": "instruction", "value": "$value" }
}
}
]
}
}
}

Component view fields:

FieldTypeRequiredDescription
kind"component"YesMust be "component".
dataRecord<string, ActionDataSource>NoData sources. Each key becomes a scope variable accessible in the component tree as $keyName.
contentExtensionComponentDataYesRoot component to render. Uses the declarative component DSL.

ActionDataSource:

FieldTypeRequiredDescription
actionstringYesAction ID to call for fetching data. Must be registered by the extension’s code.
paramsRecord<string, unknown>NoStatic parameters to pass to the action.
refreshOnstring[]NoEvent names that trigger an automatic refresh of this data source.

Type: PanelDefinition[]

Right-side panel views that display persistent, auto-refreshing UI alongside the chat. Panels use the same component view system as tool settings. Requires the panels.register permission.

FieldTypeRequiredDescription
idstringYesUnique panel identifier within the extension.
titlestringYesDisplay title shown in the panel header and panel selector.
iconstringNoIcon name (from the Huge Icons set). Displayed in the panel selector.
viewPanelViewYesPanel view schema. Uses the same component view structure as tool settings.

The view field supports kind: "component" with the same data and content structure described in Component View.

{
"panels": [
{
"id": "work.todos",
"title": "Work",
"icon": "check-list",
"view": {
"kind": "component",
"data": {
"groups": {
"action": "getGroups",
"refreshOn": ["work.todo.changed", "work.project.changed"]
}
},
"content": {
"component": "VerticalStack",
"gap": 1,
"children": {
"each": "$groups",
"as": "group",
"items": [
{
"component": "Panel",
"title": "$group.title",
"content": {
"component": "Label",
"text": "$group.summary"
}
}
]
}
}
}
}
]
}

Type: object

Declares document collections the extension needs for persistent data storage. Requires the storage.collections permission.

Collections are schemaless document stores. You declare the collection names and which fields should be indexed for fast queries.

FieldTypeRequiredDescription
collectionsRecord<string, CollectionConfig>YesCollection definitions, keyed by collection name.

CollectionConfig:

FieldTypeRequiredDescription
indexesstring[]NoField names to index for efficient querying.
{
"storage": {
"collections": {
"accounts": {
"indexes": ["email", "provider", "enabled"]
},
"settings": {},
"processed": {
"indexes": ["accountId", "messageId"]
}
}
}
}

Type: PromptContribution[]

System prompt contributions that are injected into the AI’s system prompt. This is how extensions tell the AI about available tools and how to use them.

FieldTypeRequiredDescription
idstringYesUnique identifier within the extension.
titlestringNoOptional title for the prompt section.
sectionstringNoWhere in the system prompt to place this text. One of: "system", "behavior", "tools". See below.
textstringNoPlain text prompt content. Use this for single-language prompts.
i18nRecord<string, string>NoLocalized prompt content, keyed by locale code (e.g., "en", "sv"). The AI receives the prompt in the user’s preferred language.
ordernumberNoOrdering hint within the section. Lower values come first.

You must provide either text or i18n (or both). If both are provided, i18n takes precedence when a matching locale is available.

Prompt sections:

  • "system" — Core system-level instructions. Use sparingly.
  • "behavior" — Behavioral guidelines for the AI (tone, style, rules).
  • "tools" — Instructions about available tools and how/when to use them. This is the most common section for extensions.
{
"prompts": [
{
"id": "mail-reader-instructions",
"section": "tools",
"i18n": {
"en": "You have access to Mail Reader tools for monitoring incoming emails. Use mail_accounts_list to see configured accounts. Use mail_list_recent to get recent emails.",
"sv": "Du har tillgang till Mail Reader-verktyg for att overvaka inkommande e-post. Anvand mail_accounts_list for att se konfigurerade konton."
}
}
]
}

Many text fields in the manifest support the LocalizedString type. A LocalizedString is either a plain string or an object mapping language codes to translations.

// Simple string -- used as-is regardless of locale
"Get Weather"
// Localized -- Stina selects the best match for the user's language
{ "en": "Get Weather", "sv": "Hamta vader", "de": "Wetter abrufen" }

Resolution order: preferred locale -> "en" (fallback) -> first available value -> empty string.

Fields that support LocalizedString:

  • contributes.tools[].name
  • contributes.tools[].description
  • contributes.tools[].confirmation.prompt

Other text fields (such as name, description at the top level, and panel/command labels) are plain strings.


Below is a full manifest.json for a hypothetical tool extension that manages bookmarks. It demonstrates multiple contribution types working together.

{
"$schema": "https://stina.app/schemas/extension-manifest.json",
"id": "bookmarks",
"name": "Bookmarks",
"version": "1.0.0",
"description": "Save and organize bookmarks from conversations.",
"type": "tools",
"author": {
"name": "Stina Team",
"url": "https://github.com/einord"
},
"repository": "https://github.com/einord/stina-ext-bookmarks",
"license": "MIT",
"engines": {
"stina": ">=0.5.0"
},
"platforms": ["web", "electron", "tui"],
"main": "index.js",
"permissions": [
"tools.register",
"actions.register",
"panels.register",
"storage.collections",
"events.emit"
],
"contributes": {
"storage": {
"collections": {
"bookmarks": {
"indexes": ["title", "category", "createdAt"]
},
"categories": {
"indexes": ["name"]
}
}
},
"tools": [
{
"id": "bookmarks_list",
"name": "List Bookmarks",
"description": "List saved bookmarks with optional filtering."
},
{
"id": "bookmarks_add",
"name": { "en": "Add Bookmark", "sv": "Lagg till bokmarke" },
"description": { "en": "Save a new bookmark.", "sv": "Spara ett nytt bokmarke." }
},
{
"id": "bookmarks_delete",
"name": "Delete Bookmark",
"description": "Delete a bookmark by ID.",
"confirmation": {
"prompt": "Allow deleting this bookmark?"
}
}
],
"commands": [
{
"id": "bookmark",
"name": "/bookmark",
"description": "Save the current topic as a bookmark"
}
],
"settings": [
{
"id": "default_category",
"title": "Default Category",
"description": "Category assigned to new bookmarks when none is specified",
"type": "string",
"default": "Uncategorized"
}
],
"toolSettings": [
{
"id": "bookmarks",
"title": "Bookmarks",
"description": "Manage saved bookmarks.",
"view": {
"kind": "list",
"listToolId": "bookmarks_list",
"getToolId": "bookmarks_get",
"upsertToolId": "bookmarks_upsert",
"deleteToolId": "bookmarks_delete",
"mapping": {
"itemsKey": "bookmarks",
"countKey": "count",
"idKey": "id",
"labelKey": "title",
"descriptionKey": "url"
},
"searchParam": "query",
"limitParam": "limit",
"idParam": "id"
},
"fields": [
{
"id": "title",
"title": "Title",
"type": "string",
"validation": { "required": true }
},
{
"id": "url",
"title": "URL",
"type": "string",
"validation": { "required": true }
},
{
"id": "category",
"title": "Category",
"type": "string"
}
]
}
],
"panels": [
{
"id": "bookmarks-panel",
"title": "Bookmarks",
"icon": "bookmark-02",
"view": {
"kind": "component",
"data": {
"bookmarks": {
"action": "getBookmarks",
"refreshOn": ["bookmarks.changed"]
}
},
"content": {
"component": "VerticalStack",
"gap": 0.5,
"children": {
"each": "$bookmarks",
"as": "item",
"items": [
{
"component": "HorizontalStack",
"gap": 0.5,
"children": [
{ "component": "Label", "text": "$item.title" },
{
"component": "Pill",
"text": "$item.category",
"variant": "info"
}
]
}
]
}
}
}
}
],
"prompts": [
{
"id": "bookmarks-instructions",
"section": "tools",
"i18n": {
"en": "You have access to Bookmarks tools. Use bookmarks_add to save interesting links or topics the user mentions. Use bookmarks_list to recall saved bookmarks.",
"sv": "Du har tillgang till Bookmarks-verktyg. Anvand bookmarks_add for att spara intressanta lankar eller amnen som anvandaren namner. Anvand bookmarks_list for att hamta sparade bokmarken."
}
}
]
}
}