mcpField
C
create-arc1-extension

create-arc1-extension

github.com/arc-mcp開発239219 回インストール2 日前に更新
リポジトリ

create-arc1-extension について

ARC-1 にフォークせずカスタムツールを追加する、開発者向けの TypeScript 拡張スキルです。SAP API と安全設定の選択から、ひな型作成、読み込み、SAP での動作確認まで案内します。

主な機能

  1. 01マニフェストと TypeScript の実装経路
  2. 02SAP API と操作種別の選択
  3. 03書き込み・実行の安全ゲート
  4. 04プラグインのひな型と読み込み手順
  5. 05ライブテストとエラーの読み解き

活用シーン

  1. 01既存の SAP エンドポイントを ARC-1 ツール化
  2. 02ARC-1 をフォークせず読み書きツールを追加
  3. 03許可されたコンソールクラスやレポートを実行

MCP 対応

ARC-1 の MCP インターフェースにカスタムツールを公開する拡張の作成を案内します。tools/list でツールの登録を確認する手順も含みます。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/arc-mcp/arc-1 --skill create-arc1-extension

インストール後、Claude Code で/create-arc1-extensionを入力して読み込まれたか確認します。

詳細

ARC-1 拡張機能の作成

開発者が ARC-1 拡張機能(フォークせずに ARC-1 インスタンスへ Custom_* ツールを追加するローカルプラグイン)を構築できるように案内します。ARC-1 の認証済み SAP クライアント、7 スコープと allow* による安全性の上限、監査、PP を再利用します。フレームワークの構築(PR1~PR5)で得た知見と、S/4HANA でのライブ検証の内容をまとめています。

正確な情報源 — まずはこちらを読み、内容に沿ってください:

  • ユーザーガイド(開発者をこちらへ案内してください): docs_page/extensions.md — 基準となる手順ガイドです(ティア、ctx.http/ctx.run、セキュリティ、CF/Docker デプロイ)。ドキュメントサイトの ARC-1 の使用 → 拡張機能(カスタムツール) に掲載されています。
  • 仕様: docs/research/2026-06-17-extension-framework-spec.md(v1)および extension-framework-v2-spec.md(延期された内容)。
  • 実装例: arc-mcp/arc-1-extension-sample — ADT と OData の 読み取り、マニフェストツール、Custom_RunClass と Custom_RunReport(名前を指定した実行のゲート制御)、OData の 書き込み(Custom_CreateSalesOrder)、および LISA カスタム ICF 統合(Custom_ListLanguages/GetTranslation/SetTranslation)一式を含みます。すべて S/4HANA でライブ検証済みです(実際の HTTP 201/200 書き込み)。最も近いツールをコピーして調整してください。

v1 の実態(誤解しないでください): 読み取りは制限なく利用できます(ctx.http.get/head)。書き込み(ctx.http.post/put/delete)は、オプトイン設定 SAP_ALLOW_PLUGIN_RAW_WRITES のもとで、ADT 以外のパス(OData/ICF)に対してのみ動作します。コンソールクラスとクラシックレポートは、オプトイン設定 SAP_ALLOW_PLUGIN_EXECUTE のもとで、ctx.run.classRun/ctx.run.programRun を使って実行します。ADT オブジェクトへの書き込み(/sap/bc/adt/… 経由の CLAS/DDLS/…)は常に拒否されます。これらは v2 のパッケージ対応 ctx.write で扱います。

トリガー

  • 「ARC-1 にカスタムツール/プラグイン/拡張機能を追加したい」
  • 「この SAP/ADT/OData エンドポイントを MCP ツールとしてラップしたい」
  • 「フォークせずに独自の ARC-1 ツールを作りたい」
  • 「ARC-1 上に診断ツールを構築したい」(SM37/SLG1/ゲートウェイログなど)

ステップ 1 — 方針を決める(推測せずに質問する)

AskUserQuestion を使用します。最初の質問で可否を確認します。

  1. バックエンド。 このツールは、ARC-1 が接続する同じ SAP システムとHTTP 経由で通信しますか(ADT、 OData、またはカスタム ICF/REST サービス)?
    • いいえ — 別の SAP 製品(Cloud ALM、BTP services、BW、HANA、Datasphere、SuccessFactors) または HTTP 以外のプロトコル(ネイティブ RFC、SAP GUI scripting)→ これは拡張機能ではありません。 独立した MCP サーバー(BTP 認証モジュールを基盤とする「独自サーバー」方式)です。ここで終了し、 そちらを案内してください。
    • はい → 続けます。
  2. ティア。
    • マニフェストティア(宣言型 JSON、コード不要)— ツールが「入力を検証 → 1 回の読み取り GET → 返却」で済む場合。ロジックや書き込みはありません。
    • コードティア(defineTool、TypeScript)— ロジック、レスポンスの整形、複数回の読み取り、 OData/ICF サービスへの書き込み(ctx.http.post/put/delete)、またはコンソール クラスやクラシックレポートの実行(ctx.run.classRun / ctx.run.programRun)が必要な場合。
  3. SAP API — ADT(/sap/bc/adt/…)、OData(/sap/opu/odata/…)、またはカスタム ICF(/sap/bc/http/…)。 カスタムエンドポイントの場合、SAP 上にすでに存在している必要があります — 拡張機能には ABAP は含まれません。
  4. 実行内容、およびスコープとopType:
    • 読み取り(3 種の API のいずれか)→ scope: 'read'、opType: OperationType.Read — ctx.http.get を使用します。
    • OData/ICF サービスへの書き込み(ctx.http.post/put/delete)→ scope: 'write'、opType Create/Update/Delete。管理者が SAP_ALLOW_PLUGIN_RAW_WRITES=true + SAP_ALLOW_WRITES=true を設定しない限り、拒否されます。パスは ADT 以外(/sap/opu/odata/… または /sap/bc/http/…)である必要があります。
    • コンソールクラスの実行(IF_OO_ADT_CLASSRUN)→ scope: 'write'、opType: OperationType.Workflow — ctx.run.classRun を使用します。SAP_ALLOW_PLUGIN_EXECUTE=true + SAP_ALLOW_WRITES=true を設定しない限り、拒否されます。
    • クラシックレポートの実行(PROG、オンプレミス)→ scope: 'write'、 opType: OperationType.Workflow、availableOn: 'onprem' — ctx.run.programRun を使用します。名前を入力し、テキストを出力する方式です。選択パラメーターやバリアントには対応していません。同じ実行および書き込みの許可条件が適用されます。
    • ADT オブジェクトの作成/更新/削除(/sap/bc/adt/… 経由の CLAS/DDLS/…)→ v1 では利用できません — 常に拒否されます。これは v2 のパッケージ対応 ctx.write で可能になります。ツールで必要な場合は、その旨を伝えて終了してください。

ステップ 2 — スキャフォールドを作成する(arc-1-extension-sample に倣う)

新しいリポジトリ arc1-plugin-<name> を作成します(純粋な TS、ABAP なし)。

  • package.json — "type":"module"、peerDep "arc-1": ">=<ver>"、devDeps typescript+zod、 build "tsc && node -e \"require('node:fs').cpSync('manifests','dist/manifests',{recursive:true})\"" (マニフェストがある場合のみ)。任意の "arc1": { "apiVersion": 1 } ブロックは将来に向けた宣言です — v1 ではローダーは apiVersion を Plugin のデフォルトエクスポート(src/index.ts)から読み取り、 requires:{scopes,packages} は v2(宣言済みですが、まだ適用されません)なので、これに依存しないでください。
  • 読み取り(コードティア) → src/tools/Custom_<X>.ts:
    import { z } from 'zod';
    import { defineTool, OperationType } from 'arc-1/public';
    export default defineTool({
      name: 'Custom_<X>',                 // MUST start with Custom_
      description: '…',
      schema: z.object({ /* … */ }),
      policy: { scope: 'read', opType: OperationType.Read },
      async handler(args, ctx) {
        const res = await ctx.http.get(`/sap/bc/adt/…`, { Accept: 'text/plain' });
        return { content: [{ type: 'text', text: /* shape res.body */ }] };
      },
    });
    
  • マニフェストティア → manifests/Custom_<X>.tool.json:
    { "name": "Custom_<X>", "description": "…", "scope": "read",
      "inputSchema": { "type": "object", "additionalProperties": false,
        "required": ["name"], "properties": { "name": { "type": "string", "pattern": "^[A-Za-z0-9_/]{1,40}$" } } },
      "request": { "method": "GET", "path": "/sap/bc/adt/…/{name}/source/main",
        "pathParams": { "name": "$.name" }, "accept": "text/plain" },
      "response": { "maxBytes": 50000 } }
    
  • 書き込み(コードティア) — OData / カスタム ICF の POST/PUT/DELETE → src/tools/Custom_<X>.ts:
    import { z } from 'zod';
    import { defineTool, OperationType } from 'arc-1/public';
    export default defineTool({
      name: 'Custom_<X>',
      description: 'Create something via an OData/ICF service.',
      schema: z.object({ /* … */ }),
      policy: { scope: 'write', opType: OperationType.Create },   // POST→Create / PUT→Update / DELETE→Delete
      async handler(args, ctx) {
        const body = JSON.stringify(/* entity / payload */);
        // path MUST be non-ADT (OData/ICF); CSRF is fetched + attached automatically.
        const res = await ctx.http.post('/sap/opu/odata/<ns>/<SERVICE>/<EntitySet>', body, 'application/json',
          { Accept: 'application/json' });
        return { content: [{ type: 'text', text: `HTTP ${res.statusCode}\n${res.body}` }] };
      },
    });
    
  • 実行(コードティア) — コンソールクラスまたはクラシックレポートの実行 → src/tools/Custom_<X>.ts。 コンソールクラスの例:
    import { z } from 'zod';
    import { defineTool, OperationType } from 'arc-1/public';
    export default defineTool({
      name: 'Custom_<X>',
      description: 'Execute an ABAP console class and return its output.',
      schema: z.object({ className: z.string().min(1).max(40) }),
      policy: { scope: 'write', opType: OperationType.Workflow },   // execute ⇒ write-class op
      async handler(args, ctx) {
        const out = await ctx.run.classRun((args as { className: string }).className);  // gated; see Step 1.4
        return { content: [{ type: 'text', text: out }] };
      },
    });
    
    クラシックレポートの例:
    import { z } from 'zod';
    import { defineTool, OperationType } from 'arc-1/public';
    export default defineTool({
      name: 'Custom_<X>',
      description: 'Execute a classic ABAP report and return SAP list or error text.',
      schema: z.object({ reportName: z.string().min(1).max(40) }),
      policy: { scope: 'write', opType: OperationType.Workflow },
      availableOn: 'onprem',
      async handler(args, ctx) {
        const out = await ctx.run.programRun((args as { reportName: string }).reportName);
        return { content: [{ type: 'text', text: out }] };
      },
    });
    
    programRun が受け取るのはレポート名のみです。レポートに実行時の入力が必要な場合は、小規模な IF_OO_ADT_CLASSRUN クラスを使用してください。名前付きの両操作は SAP のレスポンステキストをそのまま返します。SAP は、オブジェクトが見つからない場合や実行エラーを HTTP 200 のレスポンスに含めることがあります。
  • src/index.ts — export default { name, version, apiVersion: 1, tools: [...], manifests: ['manifests/Custom_<X>.tool.json'] } satisfies Plugin;
  • README — 機能の説明と読み込みコマンド。

ステップ 3 — ビルド + 読み込み + テスト(これは実環境で検証済みです)

# until arc-1 is published with the public API, link the local build:
( cd /path/to/arc-1 && npm link )
npm install && npm link arc-1 && npm run build

# load into an instance…
ARC1_PLUGINS=$PWD/dist/index.js  arc1 --transport http-streamable
# …or drive one read call (args MUST be --json, not positional):
ARC1_PLUGINS=$PWD/dist/index.js  arc1-cli call Custom_<X> --json '{"name":"RSPARAM"}'
# …a WRITE tool (OData/ICF) needs the raw-write opt-ins (else it's refused):
SAP_ALLOW_PLUGIN_RAW_WRITES=true SAP_ALLOW_WRITES=true \
  ARC1_PLUGINS=$PWD/dist/index.js  arc1-cli call Custom_<X> --json '{ … }'
# …an EXECUTE tool needs the execute opt-ins:
SAP_ALLOW_PLUGIN_EXECUTE=true SAP_ALLOW_WRITES=true \
  ARC1_PLUGINS=$PWD/dist/index.js  arc1-cli call Custom_<X> --json '{"className":"ZCL_FOO"}'
# …or, for an on-premise classic report:
SAP_ALLOW_PLUGIN_EXECUTE=true SAP_ALLOW_WRITES=true \
  ARC1_PLUGINS=$PWD/dist/index.js  arc1-cli call Custom_<X> --json '{"reportName":"ZREPORT"}'

終了コードだけでなく、結果を確認してください。ゲートによる拒否は AdtSafetyError(「…disabled」/「may not write to an ADT path」)です。SAP 側の問題(パスの誤り、サービスの不足、不正なペイロード)は、SAP のステータスと本文を含む AdtApiError です。これはゲートを通過し、リクエストが SAP に到達したことを意味します (有用な手がかりです)。名前付きクラス/レポートの実行は例外です。SAP は実行エラーを HTTP 200 のテキストレスポンスに含めることがあり、その場合 ctx.run はそのまま返します。SAP のエラーをもとにパス/ペイロードを調整してください。HTTP ステータスだけから成功を判断しないでください。

ツールが tools/list に表示され、呼び出しで実際の SAP データが返されることを確認してください。プラグインを BTP Cloud Foundry または Docker にデプロイする場合(所有者チェック/--chown の注意点、イメージとビルドパックとボリュームのトレードオフ)については、開発者にdocs_page/extensions.md の拡張機能のデプロイセクションを案内してください。

つまずきやすい点(苦労して学んだこと)

  • Custom_ 名前空間は必須で、名前が重複するとサーバーの起動に失敗します(フェイルファスト)。
  • ctx.http では読み取り(GET/HEAD)を自由に行えますが、書き込み(post/put/delete)が可能なのは NON-ADT パスのみです。また、管理者が SAP_ALLOW_PLUGIN_RAW_WRITES=true(および SAP_ALLOW_WRITES=true)を設定し、ツールが scope:'write' を宣言している場合に限ります。/sap/bc/adt/… のオブジェクトパスへの書き込みは常に拒否されます(生の書き込みではパッケージ許可リストを適用できません。ADT オブジェクトの書き込みには v2 の ctx.write ボキャブラリーを使用します)。CSRF トークンは自動的に取得され、付与されます。OData/カスタム ICF 書き込みサービスにはこちらを使用してください。
  • ctx.client は実行時の プレーン読み取り専用 ビューです。.http/.safety と、データ/SQL の読み取り(getTableContents/runQuery/runTableQuery)は実行時にブロックされます(キャストすると undefined になります)。v1 プラグインにはデータ/SQL の機能はありません。プレーン読み取りメソッド(または ctx.http.get)を使用してください。
  • policy.opType は scope と一致している必要があります。宣言したスコープが opType に必要なスコープを満たす必要があります(例:opType:'U' には scope:'write' が必要です)。満たさない場合、プラグインのサーバー起動に失敗します。前述の例と一致させてください。
  • 名前を指定した実行には特権が必要です。 ctx.run.classRun(name) は IF_OO_ADT_CLASSRUN コンソールクラスを実行します。ctx.run.programRun(name) はオンプレミスの従来型レポートを実行し、一覧/エラーテキストを返します(選択条件やバリアントはありません)。どちらも SAP_ALLOW_PLUGIN_EXECUTE=true および SAP_ALLOW_WRITES=true および write スコープのツールが必要です。名前は検証されます。デフォルトでは無効です。
  • OData のパスの特定 — 403 /IWFND/MED/170 "No service found" は通常、サービスが無効なだけでなく、パスが間違っていることを示します。 サービス名と名前空間の両方が重要です。たとえば、EPM デモは /sap/opu/odata/iwbep/GWSAMPLE_BASIC であり、/sap/opu/odata/sap/ZGWSAMPLE_BASIC ではありません。GET …/$metadata で正しいパスを確認してください(200 = 正しいパス、403 = パスが間違っているか、実際にサービスが無効です。その場合は /IWFND/MAINT_SERVICE を確認してください)。
  • OData V2 の 作成 に関する注意点: POST に $format=json クエリオプションを付けてはいけません(これは SystemQueryOption であり、400 "not allowed for this Request Type" が返されます)。代わりに Accept ヘッダーで JSON を指定してください。必須のエンティティフィールドは異なる場合があります。構造を確認するには GET …/<EntitySet>?$top=1 を実行してください。
  • カスタム ICF(LISA スタイル)サービス: 通常は JSON ボディを付けて POST /sap/bc/http/sap/<SERVICE>/<action> を実行します(アクションは URL パスに、パラメーターはボディに指定します)。これにより、次の 2 点に注意が必要です。(1)サービスが読み取りにも POST を使う場合、その読み取りツールにも SAP_ALLOW_PLUGIN_RAW_WRITES と scope:'write' が必要です(ctx.http は HTTP メソッドに基づいて制御します)。操作の意図を正しく示すには opType: Read を宣言してください。(2)書き込みにはオープン中の移送依頼が必要な場合があります。SAPTransport で作成してください(STMS ルートのないシステムではローカル依頼になりますが、問題ありません)。
  • SAP に書き込みが到達しても、2xx が返るとは限りません。 ゲート、CSRF、POST がすべて成功しても、SAP が 4xx を返すことがあります(ペイロードの不正、サービスが無効、移送の欠落など)。これはゲートによる拒否ではなく AdtApiError です。リクエストを修正してください。フレームワークは正しく動作しています。
  • マニフェスト層は読み取り専用の GET のみで、additionalProperties:false が必須です。path はホストを含まないテンプレートで、パスパラメーターはパーセントエンコードされます(パストラバーサル対策)。v1 では POST/ボディは使用できません。
  • availableOn: 'onprem' | 'btp'(任意、デフォルトは all)を指定すると、解決されたシステムタイプが異なる場合に tools/list からツールが非表示になります。ハイパーフォーカスモードではプラグインツールは一切表示されません。
  • ctx の elicit/notify/sampling は機能によって制御されます。MCP クライアントが対応している場合にのみ利用でき(CLI/stdio 経由では利用できません)、それ以外の場合は存在しません。
  • arc-1/public/testing の createMockToolContext を使ってハンドラーを単体テストしてください(ctx.http/ctx.run.classRun/ctx.run.programRun の呼び出しを記録し、設定済みの出力を返します。稼働中の SAP は不要です)。
  • 管理者用キルスイッチ: SAP_DENY_ACTIONS=Custom_*(すべて)または Custom_Foo(1 つ)を指定すると、プラグインツールが削除されます。

デプロイ(質問された場合)

docs_page/extensions.md の 拡張機能のデプロイを案内してください。 主なポイント:プラグインは ローカルファイルであり、絶対パスの ARC1_PLUGINS($HOME は展開されません)から読み込まれます。BTP CF では、派生 Docker イメージ(FROM ghcr.io/arc-mcp/arc-1、COPY --chown=arc1:arc1 を使用します。通常の COPY では root 所有になり、ローダーが所有者以外のファイル、または全ユーザーが書き込み可能なファイルを拒否します)を使用するか、ビルド済みの dist/ をビルドパックアプリのファイル領域(/home/vcap/app/...、vcap 所有)に一緒にデプロイします。ホットリロードには対応していません(変更するには再デプロイが必要です)。プラグインの追加に XSUAA の変更は不要です(スコープは再利用されます)。

コメント