Fundamentals
The Manifest
Complete field-by-field reference for oxp.json — the heart of every OXP extension.
Every OXP extension is defined by a single oxp.json file at its root. This manifest declares identity, permissions, entry points, compatibility, and more. It conforms to the JSON Schema at spec/v1/manifest.schema.json.
Minimal Example
{
"specVersion": "1",
"id": "@acme/hello",
"publisher": "acme",
"version": "1.0.0",
"displayName": "Hello Extension",
"license": "MIT",
"engines": { "oxp": "^1.0.0" },
"main": { "ui": "ui/index.html" },
"permissions": []
}Field Reference
Identity Fields
| Field | Type | Required | Description |
|---|---|---|---|
specVersion | "1" | ✅ | Always "1" for v1 extensions |
id | string | ✅ | Unique ID in @publisher/slug format |
publisher | string | ✅ | Lowercase kebab-case handle, must match the id |
version | string | ✅ | Strict semver 2.0.0 (e.g. "1.2.3") |
displayName | string | ✅ | Human-readable name shown in UIs |
description | string | — | Short description for search and browse |
license | string | ✅ | SPDX identifier or "UNLICENSED" |
Entry Points
The main object tells the host how to load your extension:
{ "main": { "ui": "ui/index.html" } }
{ "main": { "wasm": "wasm/core.wasm" } }
{ "main": { "ui": "ui/index.html", "wasm": "wasm/core.wasm" } }At least one of ui or wasm is required. Setting both creates a hybrid-v1 bundle.
Bundle Kind
{ "kind": "ui-v1" }Explicit classification. When omitted, derived from main:
main.uionly →ui-v1(declarative, no code)main.wasmonly →component-v1(WASI component)- Both →
hybrid-v1
Permissions
{
"permissions": [
{ "id": "fs.read", "scope": ["/workspace/**"], "rationale": "Read project files" },
{ "id": "net.fetch", "scope": ["https://api.example.com/*"], "rationale": "Fetch data" }
]
}Each permission has an id from the capability catalog, optional scope globs, and a rationale shown to users at install. See Permissions for the full catalog.
UI Hints
{
"ui": {
"components": "oxp-ui-v1",
"preferredSurface": "panel",
"themeable": true
}
}| Value | Meaning |
|---|---|
oxp-ui-only | Declarative tree only, no code execution path |
oxp-ui-v1 | Standard V1 component vocabulary |
escape-hatch | Full HTML/JS (deprecated — closed by WASM pivot) |
preferredSurface can be "sidebar", "panel", "editor", "modal", or "statusbar".
Host Compatibility
{
"hosts": {
"vscode": { "compatible": true, "minVersion": "1.95.0" },
"cursor": { "compatible": true },
"jetbrains": { "compatible": false, "reason": "L2 adapter pending" }
}
}Contributions
The contributes object declares UI surfaces and behaviors your extension registers with the host. Each contribution is declarative — the host parses it at install time and wires up the chrome (commands, view containers, menus) without running any of your code. This is what lets a single bundle paint native UI in VS Code, Cursor, Windsurf, VSCodium, and JetBrains.
{
"contributes": {
"commands": [ ... ],
"viewsContainers": { ... },
"views": { ... },
"keybindings": [ ... ],
"mcpServers": [ ... ]
}
}Contributions may be inlined as shown, or pulled from sibling files for tidiness:
{
"contributes": {
"commands": "contributions/commands.json",
"viewsContainers": "contributions/viewsContainers.json"
}
}contributes.commands
Each command registers a callable handler that shows up in the IDE's command palette and can be bound to keybindings, menus, or invoked programmatically by your extension.
{
"contributes": {
"commands": [
{
"id": "hello.greet",
"title": "Hello: Greet the World",
"category": "Hello",
"icon": "$(megaphone)",
"when": "workspaceFolderCount > 0"
},
{
"id": "hello.refresh",
"title": "Hello: Refresh View",
"category": "Hello",
"icon": "$(refresh)"
}
]
}
}| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Unique within the extension. Convention: <prefix>.<verb> (e.g. hello.greet) |
title | string | yes | Shown in the command palette and menus |
category | string | no | Groups commands in the palette (e.g. "Hello" → Hello: Greet the World) |
icon | string | no | Codicon ($(name)) on VS Code family; mapped to the closest IntelliJ icon on JetBrains |
when | string | no | Boolean expression evaluated against host context keys; command is hidden when false |
Commands are dispatched to your extension over the commands/execute RPC. Register the handler in your extension code:
import { commands } from "@oxprotocol/sdk";
commands.register("hello.greet", async () => {
// your handler
});contributes.viewsContainers
A view container is a top-level UI slot in the IDE chrome — the activity-bar icon (VS Code family) or tool-window stripe button (JetBrains) that owns your extension's sidebar.
{
"contributes": {
"viewsContainers": {
"activitybar": [
{
"id": "hello-sidebar",
"title": "Hello",
"icon": "media/hello.svg"
}
],
"panel": [
{
"id": "hello-panel",
"title": "Hello Logs",
"icon": "$(output)"
}
]
}
}
}| Field | Type | Required | Description | |
|---|---|---|---|---|
id | string | yes | Referenced by contributes.views to mount views inside this container | |
title | string | yes | Tooltip text and accessible label | |
icon | string \ | $(codicon) | yes | Path to an SVG (16×16, monochrome) or a codicon reference |
Two locations are supported in v0.1:
activitybar— the vertical icon strip on the left in VS Code family, the right tool-window stripe in JetBrains.panel— the horizontal bottom panel in VS Code family, the bottom tool-window strip in JetBrains.
Views inside a container are declared with contributes.views (keyed by container id) — covered separately in UI Components.
contributes.keybindings
{
"contributes": {
"keybindings": [
{ "command": "hello.greet", "key": "ctrl+alt+h", "mac": "cmd+alt+h" }
]
}
}contributes.mcpServers
Auto-registers Model Context Protocol servers with the IDE's MCP client. See the MCP Integration guide for the full schema.
Categories
Valid categories: ai, database, data-tools, debuggers, devops, editor, education, formatters, language-support, linters, notebooks, other, productivity, scm, snippets, testing, themes, visualization.
WIT Pin (Component Extensions)
Required for component-v1 and hybrid-v1 bundles:
{
"wit": {
"package": "oxp:extension",
"version": "0.1.0",
"sha256": "a1b2c3..."
}
}The SHA-256 is computed over the canonical form of the WIT world. The registry rejects mismatches on publish; the host refuses to instantiate mismatched components on install.
Resource Limits
{
"limits": {
"timeMsPerCall": 100,
"maxMemoryMb": 64
}
}| Limit | Default | Maximum |
|---|---|---|
timeMsPerCall | 100 ms | 5,000 ms |
maxMemoryMb | 64 MiB | 256 MiB |
Auto-Generated Fields
These are set by oxp publish — do not write them by hand:
- **
integrity.bundleSha256** — SHA-256 of the uncompressed tar stream - **
integrity.signedBy** — publisher key ID - **
integrity.signatureAlgo** —"ed25519"or"sigstore"