386 lines
12 KiB
Markdown
386 lines
12 KiB
Markdown
# Manifest
|
|
|
|
Every plugin package has an `idea-plugin.json` file at the archive root.
|
|
|
|
For development installs, the same file must exist at the root of the plugin
|
|
source directory selected in IdeA with `Paramètres > Plugins > Installer depuis
|
|
un dossier…`.
|
|
|
|
## Minimal Plugin Directory
|
|
|
|
A plugin directory must contain the manifest at its root and a built ESM
|
|
entrypoint matching the manifest `main` field.
|
|
|
|
```text
|
|
my-plugin/
|
|
├── idea-plugin.json
|
|
├── package.json
|
|
├── tsconfig.json
|
|
├── src/
|
|
│ └── index.ts
|
|
└── dist/
|
|
└── index.js
|
|
```
|
|
|
|
Create the directory:
|
|
|
|
```sh
|
|
mkdir -p my-plugin/src
|
|
cd my-plugin
|
|
npm init -y
|
|
npm install --save-dev typescript @idea/plugin-sdk
|
|
```
|
|
|
|
Use a package script that emits JavaScript into `dist/`:
|
|
|
|
```json
|
|
{
|
|
"type": "module",
|
|
"scripts": {
|
|
"build": "tsc -p tsconfig.json"
|
|
},
|
|
"devDependencies": {
|
|
"@idea/plugin-sdk": "^0.3.0",
|
|
"typescript": "^5.0.0"
|
|
}
|
|
}
|
|
```
|
|
|
|
When developing against a local SDK checkout instead of a published package,
|
|
replace the SDK dependency with a `file:` reference, for example:
|
|
|
|
```json
|
|
{
|
|
"devDependencies": {
|
|
"@idea/plugin-sdk": "file:../IdeaSDK",
|
|
"typescript": "^5.0.0"
|
|
}
|
|
}
|
|
```
|
|
|
|
Minimal `tsconfig.json`:
|
|
|
|
```json
|
|
{
|
|
"compilerOptions": {
|
|
"target": "ES2022",
|
|
"module": "NodeNext",
|
|
"moduleResolution": "NodeNext",
|
|
"outDir": "dist",
|
|
"rootDir": "src",
|
|
"strict": true
|
|
},
|
|
"include": ["src"]
|
|
}
|
|
```
|
|
|
|
Minimal `src/index.ts`:
|
|
|
|
```ts
|
|
import type { IdeAPluginModule } from "@idea/plugin-sdk";
|
|
|
|
const plugin: IdeAPluginModule = {
|
|
activate(ctx) {
|
|
ctx.logger.info("plugin activated", { pluginId: ctx.pluginId });
|
|
}
|
|
};
|
|
|
|
export default plugin;
|
|
```
|
|
|
|
Build before installing or reloading:
|
|
|
|
```sh
|
|
npm run build
|
|
```
|
|
|
|
Then install the `my-plugin/` directory in IdeA. For hot reload, keep installing
|
|
from the directory, rebuild after source changes, then run `idea_plugin_reload`
|
|
for the installed plugin id. Do not install from a ZIP archive for a hot-reload
|
|
development loop.
|
|
|
|
```json
|
|
{
|
|
"ideaPluginManifestVersion": 1,
|
|
"id": "com.example.hello-plugin",
|
|
"displayName": "Hello Plugin",
|
|
"publisher": "Example",
|
|
"version": "0.1.0",
|
|
"description": "Example IdeA plugin.",
|
|
"main": "dist/index.js",
|
|
"engines": {
|
|
"idea": ">=0.1.0"
|
|
},
|
|
"trustLevel": "full",
|
|
"capabilities": ["ui", "tooling"],
|
|
"activationScope": "app",
|
|
"contributes": {
|
|
"skills": [],
|
|
"menus": [],
|
|
"menuItems": [],
|
|
"layouts": [],
|
|
"mcpServers": []
|
|
}
|
|
}
|
|
```
|
|
|
|
## Required Fields
|
|
|
|
- `ideaPluginManifestVersion`: currently `1`.
|
|
- `id`: stable lowercase id using letters, digits, dots and dashes. It must start
|
|
and end with an alphanumeric character.
|
|
- `displayName`: human-readable plugin name.
|
|
- `version`: semver version.
|
|
- `main`: package-relative ESM entrypoint loaded by IdeA.
|
|
- `trustLevel`: currently `"full"`.
|
|
|
|
## Optional Fields
|
|
|
|
- `description`, `publisher`.
|
|
- `engines.idea`: host compatibility hint.
|
|
- `activationScope`: `"app"` by default, or `"project"` when activation needs a
|
|
focused project immediately.
|
|
- `capabilities`: `"ui"`, `"tooling"` and/or `"mcp"`.
|
|
|
|
## Contributions
|
|
|
|
`contributes.skills` declares read-only agent skills shipped inside the IdeA
|
|
plugin package. These skills are IdeA-native: IdeA can expose them in project
|
|
plugin settings, assign them to agents, inject them into the agent skill
|
|
catalogue, and serve their Markdown body through `idea_skill_read` for every
|
|
supported harness that receives IdeA capabilities, including Claude Code,
|
|
Codex, OpenCode local and OpenCode cloud.
|
|
|
|
```json
|
|
{
|
|
"contributes": {
|
|
"skills": [
|
|
{
|
|
"id": "unity.build-debug",
|
|
"name": "unity-build-debug",
|
|
"description": "Diagnose Unity build failures.",
|
|
"kind": "workflow",
|
|
"path": "skills/unity-build-debug/SKILL.md"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
Skill contribution rules:
|
|
|
|
- `id` is stable within the plugin and is used by project/agent assignments.
|
|
- `name` is the agent-facing name shown in the injected skill catalogue and
|
|
accepted by `idea_skill_read`.
|
|
- `description` is the short affordance shown before the agent loads the skill.
|
|
- `kind` defaults to `"workflow"`; `"reference"` is also supported.
|
|
- `path` must be a package-relative Markdown file path ending in `.md`.
|
|
- Installing a plugin globally is not enough: a project must enable the plugin,
|
|
then assign the skill to the target agent.
|
|
|
|
See [Project Plugin Assignments](project-plugin-assignments.md) for the
|
|
`.ideai/project-plugins.json` schema, validation rules, assignment order, and
|
|
agent relaunch requirements.
|
|
|
|
`contributes.layouts` is the only surface for plugin layouts. The same layout
|
|
contribution can be mounted inside an IdeA layout cell or opened in a detached OS
|
|
window. Do not add a separate manifest contribution type for windows.
|
|
|
|
For a command that opens a visible plugin window, declare both the menu command
|
|
and the layout type:
|
|
|
|
```json
|
|
{
|
|
"ideaPluginManifestVersion": 1,
|
|
"id": "com.example.unity-plugin",
|
|
"displayName": "Unity Developer Tools",
|
|
"publisher": "Example",
|
|
"version": "0.1.0",
|
|
"description": "Unity integration for IdeA.",
|
|
"main": "dist/index.js",
|
|
"engines": {
|
|
"idea": ">=0.1.0"
|
|
},
|
|
"trustLevel": "full",
|
|
"capabilities": ["ui", "tooling"],
|
|
"activationScope": "app",
|
|
"contributes": {
|
|
"menus": [
|
|
{
|
|
"id": "unity-plugin.menu",
|
|
"label": "Unity Developer Tools",
|
|
"topLevel": true,
|
|
"order": 100
|
|
}
|
|
],
|
|
"menuItems": [
|
|
{
|
|
"id": "unity-plugin.health.item",
|
|
"targetMenuId": "unity-plugin.menu",
|
|
"label": "Health",
|
|
"command": "unity-plugin.health",
|
|
"order": 10,
|
|
"when": "projectOpen"
|
|
}
|
|
],
|
|
"layouts": [
|
|
{
|
|
"type": "unity-plugin.health",
|
|
"label": "Unity Health",
|
|
"component": "UnityHealth",
|
|
"order": 10
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
`capabilities: ["ui"]` is required when a plugin relies on layouts or windows as
|
|
human-facing UI. Add `"tooling"` when the same plugin uses workspace, task,
|
|
tooling or terminal services. The `contributes.layouts[].type` value is the
|
|
public id used by both `ctx.layouts.register({ type, component })` and
|
|
`ctx.services.windows.open({ layoutType })`.
|
|
|
|
Contribution ids are part of the runtime contract:
|
|
|
|
- commands can only register ids declared by `contributes.menuItems[*].command`;
|
|
- layouts can only register `type` values declared by `contributes.layouts`;
|
|
- `services.windows.open({ layoutType })` only accepts a layout type declared by
|
|
the calling plugin.
|
|
|
|
## MCP Servers And Agent Tools
|
|
|
|
`contributes.mcpServers` is the IdeA-native contract for plugin-provided
|
|
agent tools. A plugin does not register individual MCP tools from `activate(ctx)`.
|
|
Instead, the plugin ships or references a stdio MCP server process, declares it
|
|
in the manifest, and that server advertises its own tools through the MCP
|
|
`tools/list` protocol.
|
|
|
|
```json
|
|
{
|
|
"capabilities": ["mcp"],
|
|
"contributes": {
|
|
"mcpServers": [
|
|
{
|
|
"id": "unity-editor",
|
|
"displayName": "Unity Editor Tools",
|
|
"command": "scripts/unity-mcp-server.mjs",
|
|
"args": [],
|
|
"cwd": "${pluginRoot}",
|
|
"transport": "stdio",
|
|
"autoStart": true,
|
|
"tools": [
|
|
{
|
|
"name": "unity_get_scene",
|
|
"description": "Read the active Unity scene."
|
|
},
|
|
{
|
|
"name": "unity_run_tests"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
MCP server contribution rules:
|
|
|
|
- `id` is stable within the plugin and is used by project/agent assignments.
|
|
- `displayName` is the human-readable server name shown in plugin settings.
|
|
- `transport` must currently be `"stdio"`.
|
|
- `autoStart: true` is required for IdeA to start the server during plugin MCP
|
|
reconciliation. Non-auto-start servers are manifest metadata only today.
|
|
- A relative `command` is resolved under the installed plugin package root.
|
|
- A literal absolute `command` is rejected unless `allowAbsoluteCommand: true`
|
|
is set. Use this only for an intentional dependency on a host binary.
|
|
- A `command` that becomes absolute after `${pluginRoot}` or `${appDataDir}`
|
|
substitution is allowed and is passed through as substituted.
|
|
- `cwd` defaults to `${pluginRoot}` when omitted.
|
|
- `tools` is optional static display metadata for IdeA's capability-assignment
|
|
UI. Each entry has an exact non-empty `name` and an optional non-empty
|
|
`description`; names must be unique within the server.
|
|
- This metadata does not replace or cache MCP `tools/list`, and declaring a tool
|
|
here does not register it or prove that the running server exposes it.
|
|
|
|
The tools an agent can call are assigned with the triplet
|
|
`pluginId`/`serverId`/`toolName` in project plugin settings. `toolName` must
|
|
match a tool name advertised by the MCP server. IdeA does not currently provide
|
|
a runtime API such as `ctx.mcp.registerTool()` or `ctx.mcp.registerServer()`.
|
|
|
|
For multi-harness workflows, keep `contributes.skills` as instructions and
|
|
expose executable behavior through assigned MCP tools. Do not make skill
|
|
Markdown depend on filesystem paths relative to the Markdown returned by
|
|
`idea_skill_read`; that body is served as read-only content, not mounted as a
|
|
working directory. If a skill needs packaged scripts, put the script behind a
|
|
manifest-declared MCP server and tell the agent to call the assigned tool.
|
|
|
|
## MCP Server Paths
|
|
|
|
`contributes.mcpServers` entries are resolved by the host before starting a
|
|
declared MCP server. In `command`, `args`, `env` and `cwd`, the host expands:
|
|
|
|
- `${pluginRoot}` to the installed plugin package root.
|
|
- `${appDataDir}` to the host-owned application data directory.
|
|
|
|
There is currently no `${node}`, `${runtimeNode}`, `${hostNode}` substitution
|
|
and no `runtime` field. IdeA does not provide a plugin-scoped Node.js runtime
|
|
for MCP servers.
|
|
|
|
This string substitution is limited to manifest-declared MCP server startup.
|
|
Runtime handlers receive the same installed package location separately as
|
|
`ctx.pluginRoot`; `ctx.services.tasks.runCommand()` does not perform placeholder
|
|
substitution, and workspace APIs remain project-confined.
|
|
|
|
`command` is not shell syntax. IdeA starts it directly and passes `args`
|
|
separately. Do not rely on shell startup files, shell aliases, or a user's login
|
|
shell `PATH`.
|
|
|
|
Prefer a packaged executable for plugin-owned MCP servers:
|
|
|
|
```json
|
|
{
|
|
"command": "scripts/unity-mcp-server",
|
|
"transport": "stdio",
|
|
"autoStart": true
|
|
}
|
|
```
|
|
|
|
Because the command is package-relative, IdeA resolves it to
|
|
`${pluginRoot}/scripts/unity-mcp-server`. If this file is a script, its shebang
|
|
and executable bit must be valid in the installed package. A JavaScript shebang
|
|
such as `#!/usr/bin/env node` still depends on a `node` binary visible to the
|
|
IdeA app process, not necessarily the user's interactive shell.
|
|
|
|
For JavaScript MCP servers that must not depend on the user's shell `PATH`, use
|
|
one of these approaches:
|
|
|
|
- Ship an executable server that includes its runtime, or install a managed
|
|
runtime under plugin/app-owned storage and point `command` at that executable
|
|
with `${pluginRoot}` or `${appDataDir}`.
|
|
- Declare an explicit host dependency with an absolute command such as
|
|
`/usr/bin/node`, set `allowAbsoluteCommand: true`, and keep plugin-owned
|
|
script paths in `args`.
|
|
|
|
Avoid `command: "node"` for a packaged MCP server. With
|
|
`allowAbsoluteCommand: false`, it is treated as package-relative and resolves to
|
|
`${pluginRoot}/node`. With `allowAbsoluteCommand: true`, it is passed as a host
|
|
command name and depends on the environment inherited by IdeA, which may differ
|
|
from an interactive shell.
|
|
|
|
The same rule applies to shell wrappers. `command: "bash"` resolves to
|
|
`${pluginRoot}/bash` unless absolute-command handling is explicitly enabled. Use
|
|
a package-relative wrapper executable, or use an absolute shell such as
|
|
`/bin/bash` with `allowAbsoluteCommand: true` when that host dependency is
|
|
intentional.
|
|
|
|
## Validation
|
|
|
|
Use the SDK validator in tests or build tooling:
|
|
|
|
```ts
|
|
import { assertPluginManifest } from "@idea/plugin-sdk";
|
|
|
|
assertPluginManifest(JSON.parse(manifestText));
|
|
```
|