Plugin manifest reference
Every plugin describes itself in a JSON file called plugin.json. This page lists every field Syntic Code reads from it, with types, defaults and how each one is used.
Where the manifest lives
Syntic Code looks in the plugin folder for, in order:
.syntic-plugin/plugin.jsonplugin.jsonat the root
The first one found is used. If there is neither, but the folder has a SKILL.md at its root, the folder still loads as a plugin: its name is the folder’s name, its version is 0.0.0, and its only component is that skill.
Unknown fields
Fields the loader does not know are ignored, so a manifest written for a newer version still loads on an older one. The flip side is that a misspelled field ("command" instead of "commands") is silently ignored at load time. Run the validator to catch these.
Validate
syntic plugin validate ./my-plugin # a folder or the manifest file itself
syntic plugin validate ./my-plugin --json # machine-readable result| Finding | Severity |
|---|---|
| Not valid JSON, or not a JSON object | error |
name missing or empty | error |
A component path containing .., or absolute (/…, ~…, C:…) | error |
dependencies not an array, or an entry that is not a non-empty string | error |
name not lowercase letters, digits and hyphens | warning |
version, description or author missing | warning |
category, categories, tags, keywords, strict or source present | warning: these belong in the marketplace entry |
Errors make the command exit with code 1. Warnings do not.
At load time the rules are a little looser than the validator: only name and version must be present and non-empty. A manifest without version passes the validator with a warning but does not load. Always set a version.
Fields
Identity and metadata
| Field | Type | Required | Meaning |
|---|---|---|---|
name | string | yes | The plugin’s id within its marketplace. Used in name@marketplace, as the command and agent prefix, and in its data folder name. Use lowercase letters, digits and hyphens. |
version | string | yes | The version recorded at install and shown by update. Semantic versioning recommended. Also used by syntic plugin tag. |
description | string | no | One or two sentences shown in listings. |
author | string or object | no | "Jane Doe", or { "name": "...", "email": "...", "url": "..." }. In the object form, name is required. |
homepage | string | no | Project web page. |
repository | string | no | Source repository address. |
license | string | no | License name or SPDX id. |
keywords | array of strings | no | Kept with the manifest. Search and categories come from the marketplace entry, so the validator suggests putting them there. |
Behaviour
| Field | Type | Default | Meaning |
|---|---|---|---|
defaultEnabled | boolean | true | false installs the plugin switched off. A plugin installed as someone’s dependency is switched on regardless. |
dependencies | array of strings | [] | Plugins this one needs: "name" (same marketplace) or "name@marketplace". A trailing @^x.y constraint is accepted and ignored. See Dependencies. |
settings | object | none | Default settings. Only agent is honoured. If the plugin also has a root settings.json, that file is used instead. See Default settings. |
monitors | array of objects | none | Background watchers. See monitors. |
userConfig | object | none | Options users can set. See userConfig. |
channels | any | none | Accepted and kept, but not used yet. |
Component paths
These say where the plugin’s components are. Each is optional; without it, the component’s default location is used when it exists.
| Field | Default location | What each path may be |
|---|---|---|
skills | skills/ | A skill folder (with SKILL.md), or a folder of skill folders |
commands | commands/ | A .md file, or a folder whose .md files are commands |
agents | agents/ | A .md file, or a folder of them |
outputStyles | output-styles/ | A .md file, or a folder of them |
hooks | hooks/hooks.json | A JSON file of hooks |
mcpServers | .mcp.json | A JSON file of MCP servers |
lspServers | .lsp.json | A JSON file of language servers |
outputStyles, mcpServers and lspServers also accept the spellings output_styles, mcp_servers and lsp_servers.
Path rules
- One path or several. Each field takes a string or an array of strings:
"commands": "./cmds"or"commands": ["cmds/release.md", "cmds/hotfix.md"]. - Relative to the plugin folder. A leading
./is fine. - Must stay inside. A path containing
.., or one that is absolute, is skipped at load time without a message.validatereports it as an error. - Must exist. Paths that do not exist are skipped.
- Replaces the default. Naming a path for a component means its default location is no longer read for that component. To keep both, list both:
"commands": ["commands", "extra-commands"]. - Markdown folders are flat. For
agentsandoutputStyles, a folder contributes the.mdfiles directly inside it. (Commands found through the standardcommands/layout may be nested; sub-folders become part of the name.) - Skill folders need
SKILL.md. In a folder of skills, only sub-folders that contain one count. - Server files may be wrapped.
mcpServersfiles may be{ "mcpServers": { … } }or a bare map;lspServersfiles may be{ "lspServers": { … } }or a bare map;hooksfiles may be{ "hooks": { … } }or the bare event map. A file that does not parse is skipped.
monitors
"monitors": [
{ "name": "ci", "command": "${SYNTIC_PLUGIN_ROOT}/bin/watch-ci", "description": "CI status", "when": "launch" }
]| Field | Type | Required | Meaning |
|---|---|---|---|
name | string | yes | Identifies the monitor within the plugin. |
command | string | yes | Shell command to run, with the plugin’s environment. |
description | string | no | Label for the running watch. Default <plugin>: <name>. |
when | string | no | launch (default) or skill:<skill-name>. Any other value drops the monitor. |
Entries missing name or command are skipped.
userConfig
A map from option key to its definition:
"userConfig": {
"registry": { "type": "string", "default": "https://registry.example.com" },
"token": { "type": "string", "required": true, "sensitive": true },
"port": { "type": "number", "min": 1, "max": 65535 },
"verbose": { "type": "boolean", "default": false },
"include": { "type": "directory", "multiple": true }
}| Field | Type | Default | Meaning |
|---|---|---|---|
type | string, number, boolean, file or directory | required | The value’s type. file and directory are stored as text. |
required | boolean | false | Must have a value. A default counts as a value. |
sensitive | boolean | false | A secret: stored in the system credential store and never put into text the model reads. |
multiple | boolean | false | The value is a list. On the command line, separate items with commas. At least one item is needed when the option is required. |
min, max | number | none | Inclusive bounds for number options. |
default | any | none | Used until the user sets a value. |
Where values are stored
| Kind | Location |
|---|---|
| Plain values | ~/.syntic/plugins/.options/<plugin@marketplace>.json, readable only by you |
| Sensitive values | The system credential store, under plugin-option:<plugin@marketplace>:<key> |
Using a value
| In | Write | Notes |
|---|---|---|
| Command and skill bodies, agent prompts | ${user_config.KEY} | Sensitive values are left unreplaced. |
MCP and language server command, args, cwd, url, headers, env | ${user_config.KEY} | Sensitive values included. Unknown keys stay as written. |
| Hook commands, inline shell, monitors | $SYNTIC_USER_CONFIG_KEY or ${user_config.KEY} | The placeholder becomes a quoted variable reference, so the value cannot inject shell code. |
Plugin variables
| Variable | Value |
|---|---|
SYNTIC_PLUGIN_ROOT | The installed plugin folder (replaced on update). |
SYNTIC_PLUGIN_DATA | ~/.syntic/plugins/data/<id>/, kept across updates. |
SYNTIC_USER_CONFIG_<KEY> | Each option’s value; the key upper-cased with other characters turned into _. |
SYNTIC_SKILL_DIR | In a SKILL.md command body, that skill’s folder. |
SYNTIC_SESSION_ID | In command bodies, the current session id. |
In text the model reads and in server settings, ${NAME} is replaced before use. In anything run by a shell, the variables are exported in front of the command instead, and the plugin’s bin/ is added to the front of PATH. Only these exact SYNTIC_ names are recognised.
A complete example
{
"name": "release-helper",
"version": "1.5.0",
"description": "Release chores: version bumps, changelog, tags",
"author": { "name": "Platform team", "email": "platform@example.com" },
"homepage": "https://docs.example.com/release-helper",
"repository": "https://github.com/acme/tools",
"license": "MIT",
"defaultEnabled": true,
"dependencies": ["core"],
"commands": ["commands", "extra-commands"],
"mcpServers": "config/servers.json",
"monitors": [
{ "name": "ci", "command": "${SYNTIC_PLUGIN_ROOT}/bin/watch-ci", "when": "skill:ship" }
],
"userConfig": {
"registry": { "type": "string", "default": "https://registry.example.com" },
"token": { "type": "string", "required": true, "sensitive": true }
}
}Marketplace entries and the manifest
The marketplace entry that lists a plugin has its own fields (name, source, description, category, tags, strict). They describe the plugin in the catalog. What loads is always decided by the plugin’s own plugin.json. See the marketplace reference.