プラグイン開発

プラグインを書く

プラグインはAI WorkDeckに新しいツールを追加し、AIが会話の中からあなたのコードを呼び出せるようにします。このガイドは最小限の動作サンプルから始まり、レビュー提出までを扱います。

プラグインを公開できるユーザー

無料かつオープンソースのプラグインは、サインイン済みのアカウントなら誰でも提出できます。認証は不要です。

プラグインやPackの価格設定、クローズドソースコードの公開には、認証済み開発者アカウント(本人確認+受け取り手段、手動審査)が必要です。

開発者センター

SDK 1.6 views, connections, MCP, scoped settings, $schema/default compatibility fixes and host services require the new host. This implementation has not been released; installed clients do not gain support automatically. The simulator uses synthetic data and is not host compatibility evidence.

まず判断しましょう:プラグインが必要か、Skillが必要か

この2つは混同しやすく、誤った選択をすると多くの作業が無駄になります。違いは一文でまとめられます:コードが必要ならプラグインを、明確に説明するだけで十分ならSkillを書きましょう。

シナリオ使用するもの
外部システムのAPIを呼び出してデータを取得するSkill
特殊なファイル形式を解析するSkill
AIに貴事務所の形式でレビューコメントを書かせるSkill
あるタイプの案件のための手順と成果物の構造を定義するSkill

スキルはプレーンテキスト(プロンプトとトリガーワード)です。送信するとすぐに公開され、レビューは不要で、はるかに短時間で作成できます。スキルでニーズが満たせる場合は、スキルを作成しに行くして、このページの残りは読み飛ばしてください。

5分で最初のプラグインを実行する

プラグインは、JARにコンパイルされたJavaプロジェクトと、manifest.jsonをZIPに圧縮して送信したものです。JDK 21とMavenが必要です。

上記のテンプレートプロジェクトをダウンロードしてください。それ自体が完全に動作するサンプルです。重要なファイルは2つだけです。1つ目はツールクラスです:

package com.example.myplugin;

import dev.langchain4j.agent.tool.Tool;

/**
 * Plugin tool class.
 *
 * Three hard rules — break any one of them and the tool will not be visible to the AI:
 * 1. Must have a no-arg constructor (instantiated via reflection by the host);
 * 2. Annotate tool methods with @Tool; the method name is the tool name and must match manifest.json tools[].name;
 * 3. Use String parameters and return values for simplicity (serialize complex structures as JSON strings).
 *
 * The @Tool description is read by the AI and determines when it invokes this tool.
 * State clearly when to call it rather than only how it is implemented.
 */
public class MyTools {

    @Tool("Count characters and CJK characters in a text passage and return a readable summary. Call when the user asks for character or word counts.")
    public String countChinese(String text) {
        if (text == null || text.isBlank()) {
            return "Empty input: 0 characters";
        }
        long cjk = text.codePoints()
                .filter(cp -> Character.UnicodeScript.of(cp) == Character.UnicodeScript.HAN)
                .count();
        return String.format("Total %d chars, including %d CJK chars", text.length(), cjk);
    }
}

@Tool の説明はAIのために書かれます — AIが適切なタイミングでツールを呼び出すかどうかを直接左右します。「このメソッドが何をするか」よりも「いつ使うか」を書くほうがはるかに有用です。後者はAIが判断すべきことだからです。

2つ目は manifest.json で、プラグインの内容を記述します:

{
  "id": "my-plugin",
  "name": "My Plugin",
  "version": "1.0.0",
  "description": "One sentence explaining what this plugin does for users, shown on the marketplace card.",
  "author": "Your Name or Team",
  "homepage": "https://example.com",
  "permissions": [],
  "tools": [
    {
      "name": "countChinese",
      "description": "Count characters in text",
      "permissions": []
    }
  ],
  "backendJars": ["my-plugin-1.0.0.jar"]
}

ツール名は両側で一致している必要があります: tools[].name はJavaメソッド名と一致している必要があります。一致しない場合、ツールは登録に失敗し、AIからは見えなくなります。

次にパッケージ化します:

mvn package
mkdir -p dist && cp target/my-plugin-1.0.0.jar manifest.json dist/
cd dist && zip -r ../my-plugin-1.0.0.zip . && cd ..

zipにはファイル自体が含まれている必要があります。余分なディレクトリを入れ子にしないでください。解凍すると、フォルダではなくmanifest.jsonが直接表示されるようにしてください。

提出前に自分のマシンで試す

審査を待つ必要はありません。dist/ディレクトリ全体をローカルのプラグインフォルダにコピーし、AI WorkDeckを再起動してください:

# macOS / Linux
~/.aiworkdeck/plugins/my-plugin/

プラグインが「プラグインマーケットプレイス → インストール済み」に表示されるはずです。有効化したら、そのツールを使うはずの内容を会話で尋ね、AIが呼び出すかどうかを確認してください。呼び出されない場合、多くは@Toolのdescriptionが使用場面を適切に説明できていないことが原因です。

manifest.jsonの各フィールドの意味

フィールド説明
idグローバルに一意である必要があります。小文字、数字、ハイフンのみ使用可能です。公開後に変更することはできません。アップグレード時に「同じプラグイン」を識別するために使用されます。
versionセマンティックバージョンです。提出のたびに前回より高いバージョンである必要があります。そうでない場合は拒否されます。
name / descriptionマーケットプレイスのカードに表示されます。実装内容ではなく、ユーザーにとって何をしてくれるのかを記述してください。
author / homepage作者とプロジェクトのホームページです。任意ですが、ユーザーが信頼性を判断する材料となるため、設定を推奨します。
permissionsこのプラグインが使用する機能です。詳細は次のセクションを参照してください。
toolsツールリストです。nameはJavaメソッド名と一致している必要があり、目的が明確にわかるようにdescriptionを書いてください。
backendJarsパッケージのルートからの相対パスでJARファイル名を指定します。パッケージ外を指す ../ パスは使用できません。

permissions: 正直に申請する — 審査ではクロスチェックが行われます

4つの値から任意で選択できます。使用するものを申請してください:

  • file_readプロジェクトファイルの読み取り
  • file_writeファイルの作成・変更・削除
  • network外部ネットワークへのアクセス
  • editorドキュメントエディタの操作

これはサンドボックスではありません

プラグインはホストアプリと同じプロセスで動作するため、申請されていない動作を技術的にブロックすることはできません。権限を申請していないツールでもファイルを読み取ることは可能です。この申請は実行時の制限ではなく、審査の証拠です。JARの静的スキャン結果と突き合わせて確認されます。ネットワーク権限を申請していないのにネットワークAPIを参照している場合(またはその逆の場合)は、提出が拒否されます。

審査で確認されるポイント

すべてのバージョンは人間による審査を受けます。通常は1〜2営業日以内です。まず自動スキャンが実行され、そのレポートは審査担当者のデスクで、あなたの権限申請の隣に置かれます。

以下は即座に拒否されます:

  • 申請された権限と実際に呼び出されているAPIが一致していない
  • カスタムTrustManagerや、その他証明書検証を回避するあらゆる手段
  • ハードコードされたIPアドレス、または平文HTTPで送信されるデータ
  • ホストの内部オブジェクト(データベース接続、設定サービスなど)へのリフレクション
  • 難読化・パック化されたコード、またはコードの動作を隠すもの全般
  • バージョン番号が前のバージョンより高くないこと

承認されると、プラットフォームが秘密鍵でパッケージ全体に署名し、クライアントはインストール時にその署名を検証します。その後は、私たちを含めて誰も再署名なしにパッケージの内容を変更できません。

リリース後に問題が発覚した場合、当該バージョンを取り消します。クライアントは取消リストを取得し、自動的に無効化してユーザーに通知します。

ユーザーへの約束であり、あなたへの制約

当社のユーザーは弁護士であり、そのマシンにはクライアントの機密情報が保存されています。プラグインはホストアプリと同等のアクセス権を持ちます。それはプラグイン自体に必要な範囲をはるかに超えた権限です。

そのため審査は厳格側に倒しており、却下の際には理由を提示します。プラグインに本当に機密性の高そうな機能が必要な場合は、提出時の備考でその理由を説明してください。審査が大幅に早くなります。

Webプラグイン:UIをHTML/JSで構築する

求めているのがインターフェース(フォーム、検索ツール、ダッシュボードなど)であれば、Javaは不要です。パッケージに web/ ディレクトリ(静的なHTML/JS/CSSのみ、エントリは web/index.html)を置き、manifest の frontendEntry をそこに向けてください。JARを一切含めなくても構いません。

ここでの権限宣言は実際に機能します。 デスクトップアプリはWebプラグインをサンドボックス化されたiframeに読み込みますが、意図的に同一オリジンにはしません。プラグインのスクリプトはホストのセッションを一切見られず、すべての機能はpostMessageブリッジ経由でやり取りされ、ホストは各呼び出しをmanifestの権限と照合します。JARプラグインとは異なり、この宣言は自己紹介ではなく、強制の境界線となります。

テンプレートの web/awd-plugin-sdk.js がこのブリッジをラップしています。ハンドシェイクが完了するまで何も動作しません:

メソッドパラメータ結果権限
settings.open / settings.update{} / {values}{opened} / {}user / project settings; installation settings stay in trusted host UI
connections.list / connect / disconnect / request{id, operation?, params?}connection state / service JSONnetwork; host-held credentials and explicit developer trust
mcp.servers / mcp.call{} / {serverId, name, arguments}{servers} / MCP CallToolResultnetwork + project access; public HTTPS Streamable HTTP
billing.status / billing.checkout{connectionId?}{provider, active, plan?} / {opened}host purchase entitlement or developer subscription; no automatic charge
services.request / services.cancel{service, operation, params, requestId} / {requestId}operation resultexplicit service catalog; per-operation permission checks
context.get{}{ pluginId, projectId, language, theme, themeTokens }-
files.list{}{ files: [{ path, name, size }] }file_read
im.downloadAttachment{ conversationId, attachmentId }{ started, name }im (server-side attachment ACL check; no project write permission required)
notifications.publish{ eventKey, title, body?, actionState?, target? }notificationnotifications (current account inbox; target opens only this plugin)
notifications.resolve{ eventKey, state }notificationnotifications (updates only this plugin’s notifications; marking read never completes business actions)
im.request{ action, params }objectim (host 0.61.0+; fixed messaging actions, project archiving also requires file_write)
chat.addFile{ path }{}file_read (host 0.61.0+; attaches material without auto-sending)
files.prepareDrag{ path }{ fileId, dragToken, dragType, expiresAt, ... }file_read (host 0.61.0+; short-lived single-use drag ticket)
files.endDrag{}{}file_read (host 0.61.0+)
files.read{ path }{ path, content, truncated }file_read
ui.toast{ message }{}-
storage.get{ key }{ key, value }-
storage.set{ key, value }{}-
evidence.link{ anchor: { selection: true } | { quote }, docPath?, targets: [{ path, locator?, relation?, method?, note? }] }{ linkKey, targetIds }editor
evidence.list{ docPath?, path?, sectionPath?, status? }{ links: [{ linkKey, docPath, anchorText, sectionPath, status, targets }] }file_read
evidence.locate{ linkKey, targetId? }{}editor
tools.invoke{ name, args? }{ output }- (tool must be declared in this plugin’s manifest)
chat.send{ prompt }{}- (up to 4,000 characters)
ui.openFile{ path }{}file_read
doc.exec{ action, params? }{ result }editor (host 0.27.4+; action restricted to safe doc_/sheet_/slide_ subset)
doc.active{}{ fileId, kind }editor (host 0.27.4+)
events.subscribe{ events: ["files.changed" | "selection.changed" | "project.switched"] }{ subscribed }per event (host 0.27.4+)
events.unsubscribe{ events }{ subscribed }-
ai.request{ prompt, system?, purpose? }{ text, modelId }ai (host 0.27.4+; 16,000 chars, 10 calls/min, billed via user Credits)
settings.get{ key }{ key, value }- (host 0.28+; reads non-secret keys declared in top-level manifest settings)
<script src="awd-plugin-sdk.js"></script>
<script>
  const ctx = await awd.ready();                   // { pluginId, projectId, language, theme, themeTokens }
  const files = await awd.files.list();            // requires file_read
  const doc = await awd.files.read(files[0].path); // { path, content, truncated }
  await awd.ui.toast('Completed');
  await awd.storage.set('draft', { title: 'x' });  // plugin-scoped KV, capped at 64 KB
</script>

エラーはrejectされたPromiseとしてerr.code付きで届きます。manifestが呼び出しに必要な権限を宣言していない場合は permission_denied、ホストが認識できない場合は unknown_method です。files.readはテキストを5 MBに制限し(超過分はtruncatedがtrueになります)、storageは各プラグイン合計64 KBに制限します。

テーマチャネル(v2.6)

ハンドシェイクのコンテキストに theme('light'|'dark')と themeTokens(--awd-* CSS変数のテーブル)が含まれるようになりました。また、ホストは以降の切り替えのたびに { type: 'theme', theme, tokens } をプッシュします。SDKは自動的に適用します。data-themeの設定、awd-theme-light/awd-theme-darkクラスの切り替え、各トークンのCSSカスタムプロパティへの書き込みを行うため、プラグインのCSSはJS不要で var(--awd-surface) をそのまま使えます。スクリプトで動作を制御する場合は、awd.theme.get() で現在値を取得し、awd.theme.onChange(cb) で購読してください。ホストシミュレーターのヘッダーにはライト/ダークの切り替えボタンがあるため、デスクトップアプリをインストールせずに両方のテーマをテストできます。

テンプレートにはホストシミュレーター dev/host-simulator.html も同梱されています。デスクトップホストを装い、ハンドシェイクを実行し、すべてのメソッドにダミーデータで応答し、各ブリッジメッセージをログに記録します。ローカルの静的サーバーを起動すれば、AI WorkDeckをインストールせずにブラウザで開発できます。

python3 -m http.server 8000
# -> http://localhost:8000/dev/host-simulator.html

file:// で直接開くと通常は真っ白な画面が表示されます。ブラウザは file:// からのサンドボックス化された子ドキュメントの読み込みを拒否するためです。サーバーがURLを書き換えてもいけません:npx serve はデフォルトでクリーンURLを有効にし、/web/index.html を /web にリダイレクトするため、相対パスのSDK参照が404になります。

SDK 1.6 platform APIs (new host preview)

The Web download includes a standalone Node.js 20+ CLI. Keep scripts/ and sdk/plugin-sdk/ in their relative locations. Test UI in the simulator, then permissions, project switching, disabling, cancellation and reconnection in a compatible isolated host. Example domains are placeholders, not working services.

Declare sidebar/workspace views with project/global scope; one view per placement and an existing web/ entry. Sidebar docks are left/right/bottom. Settings default to installation scope; user/project settings may be changed by the plugin. Installation settings remain in trusted host UI. Secrets never return to the iframe; use connections for per-user credentials.

Connections declare a fixed public HTTPS origin and fixed GET/POST operations. Authentication is none, Bearer or public-client OAuth2 PKCE. connect opens trusted host UI; tokens stay in the host. Never pass arbitrary URLs, authorization headers or host setting keys. BYO services and shared host AI/speech channels are separate choices with their own permissions and usage constraints.

OAuth uses Authorization Code with S256 PKCE. A local host uses its actual port at http://127.0.0.1:<port>/api/plugins/oauth/callback; server deployments must set ai.plugins.oauth-redirect-uri to a trusted HTTPS callback and register it with the provider. Expired credentials without refresh or credentials rejected with 401/403 return needsReconnect and require host-side login again.

MCP currently supports public HTTPS Streamable HTTP only: no stdio, local processes, private addresses or redirects. Declare network, every server and tool; remote names and schemas must match. Input roots are objects. Supported schema keywords are type/title/description, object properties/required/boolean additionalProperties, array items and string enum. Types include boolean/integer/number. No $ref, oneOf, nullable or other keywords. Limits: depth 8, 64 properties per object and 64 tools per server. connectionId references a same-origin connection in this plugin. Legacy credentialKey only references a declared installation secret.

Development installs require explicit administrator service trust in the host settings page. Trust is bound to the full manifest SHA256 and expires when it changes. Packages must not contain .awd-dev, .awd-service-trust or .awd-bundled markers. Trust does not unlock development JARs or local processes.

speech, tasks and clipboard permissions grant only catalogued services.request operations; this is not an arbitrary backend proxy. services.cancel cancels this plugin’s requests. Handle unsupported hosts, missing permissions and failures explicitly; do not silently switch to external or paid services.

Platform billing currently covers plugin purchase entitlement only; submitted prices are one-time purchases. There is no platform per-call collection or revenue-sharing API. Developer-owned subscriptions may declare connections.billing status and checkout. An opened checkout is not a successful payment.

Host billing.status refreshes the current account entitlement and returns refreshed; false means the refresh did not succeed and active may come from a still-valid cache, not a fresh payment confirmation. Checkout waits for the browser-opening result and rejects failures. Test actual host login, token expiry, subscription status, MCP errors, account switching and shared/BYOK routing; a synthetic provider verifies host integration, not live OAuth or payment certification.

Complete the free developer eligibility review and generate a submission token in the developer center. Supply it through the local AWD_PLUGIN_TOKEN environment variable. Never put it in source, Agent prompts or packages. Valid submissions remain pending human review and signing; passing local validation is not marketplace approval.

node scripts/plugin-dev.mjs validate .
node scripts/plugin-dev.mjs package . --out ../my-plugin.zip
# Start the synthetic UI simulator, then open /dev/host-simulator.html
python3 -m http.server 8000
# Explicit development installation: select an isolated imported project/folder first.
# Replace PORT with the isolated host port. AWD_SESSION_ID is supplied locally if required.
# Do not paste credentials into source or an Agent prompt.
node scripts/plugin-dev.mjs install --endpoint http://127.0.0.1:PORT --project-id PROJECT_ID --folder-id FOLDER_ID
# Explicit marketplace submission only; provide AWD_PLUGIN_TOKEN locally.
node scripts/plugin-dev.mjs submit . --endpoint https://www.aiworkdeck.com/api/plugins --price-cents 0
node scripts/plugin-dev.mjs submissions --endpoint https://www.aiworkdeck.com/api/plugins
{
  "id": "my-service-plugin",
  "name": "Service panel",
  "version": "1.0.0",
  "description": "SDK 1.6 host preview example",
  "author": "Example developer",
  "frontendEntry": "web/index.html",
  "permissions": [
    "network",
    "ai",
    "speech",
    "tasks",
    "clipboard"
  ],
  "settings": [
    {
      "key": "language",
      "label": "Language",
      "type": "string",
      "scope": "user",
      "default": "zh-CN"
    },
    {
      "key": "summaryStyle",
      "label": "Summary style",
      "type": "string",
      "scope": "project",
      "default": "brief"
    }
  ],
  "contributes": {
    "views": [
      {
        "id": "service-panel",
        "name": "Service panel",
        "entry": "web/index.html",
        "placement": "sidebar",
        "scope": "project",
        "defaultDock": "left",
        "allowedDocks": [
          "left",
          "right",
          "bottom"
        ]
      }
    ],
    "connections": [
      {
        "id": "service",
        "label": "My service",
        "baseUrl": "https://api.example.com",
        "auth": {
          "type": "bearer"
        },
        "operations": {
          "summarize": {
            "method": "POST",
            "path": "/v1/summarize"
          },
          "status": {
            "method": "GET",
            "path": "/v1/subscription"
          }
        },
        "billing": {
          "statusOperation": "status",
          "checkoutUrl": "https://example.com/subscribe"
        }
      }
    ],
    "mcpServers": [
      {
        "id": "remote",
        "transport": "streamable-http",
        "url": "https://api.example.com/mcp",
        "connectionId": "service",
        "tools": [
          {
            "name": "summarize",
            "description": "Summarize supplied text",
            "inputSchema": {
              "type": "object",
              "properties": {
                "text": {
                  "type": "string"
                }
              },
              "required": [
                "text"
              ],
              "additionalProperties": false
            }
          }
        ]
      }
    ]
  }
}

Complete task to give your Agent

Implement this requirement using the downloaded AI WorkDeck Web plugin template: [feature, inputs, outputs, acceptance criteria].
Read README.md, sdk/plugin-sdk/awd-plugin-sdk.d.ts and manifest.schema.json first. Use minimum permissions. SDK 1.6 platform features require the unreleased new host; simulator success does not prove installed-client support.
Edit only this plugin. Preserve scripts/ and sdk/plugin-sdk/ layout. Do not modify the SDK to bypass checks. Support --awd-* theme tokens, keyboard interaction, narrow panels and light/dark themes.
Declare views and installation/user/project settings as needed. Credentials must use trusted host connections; never put tokens in the iframe, logs, repository or zip.
Choose shared host AI/speech/tasks/clipboard with the required permissions, or fixed HTTPS BYO operations with network permission. MCP must use public HTTPS Streamable HTTP and explicitly declared tools with the supported schema subset. No stdio, private hosts, arbitrary URLs or system settings.
Use synthetic simulator data to verify success, permission denial, disconnect, retry and cancel. Do not access customer data, real login or paid services. Use only my designated isolated project for host installation; ask the host administrator to grant service trust in settings after manifest changes.
Run node scripts/plugin-dev.mjs validate . and node scripts/plugin-dev.mjs package . --out ../my-plugin.zip. Verify no .env, credentials, private keys or host trust markers. Report changes, checks, unverified boundaries and zip location.
Only submit when I explicitly request it, using a locally provided AWD_PLUGIN_TOKEN without printing it. Report pending review accurately; never claim approval, payment or release.

MCP Streamable HTTP specification

準備完了

zipにまとめて提出してください。できるだけ早く審査します。