kuroco-admin-api-browser
Kuroco admin_api Browser Skill
概要
Kurocoの管理API(admin_api)をclaude-in-chrome MCPツール経由で操作するスキル。
admin_apiの5つのモード:
- whoami: セッション情報の取得(GET) — member_id, name, group_ids, expiresAt を返す
- discover: 利用可能なモジュール・コントローラの探索(GET)
- schema: コントローラ/サービスのリクエスト・レスポンススキーマ取得(GET)
- advise: 自然言語でやりたいことを伝えると、呼ぶべきAPIと手順をAIが回答(POST)
- execute: APIの実行(GET/POST) —
columnsパラメータでレスポンスカラム選択可能
認証方式: ブラウザのセッションCookie(__Host-rcms_api_access_token、HttpOnly)を利用。javascript_toolでfetch()を実行すれば、Cookieが自動送信されるためトークンの抽出は不要。
前提条件:
- claude-in-chrome MCPが利用可能(未設定の場合は下記「操作フロー」Step 0 で案内)
- ユーザーがKuroco管理画面(
https://*.*.kuroco-mng.app)にログイン済み - 対象サイトが不明な場合は、サイトキーまたは管理画面URLをユーザーに確認して特定する
- すべての操作はログイン中ユーザーの権限で実行される
最小操作フロー(読み取り: 4-5回、書き込み: 5-6回の tool call):
javascript_tool: 認証チェック(MODE=whoami)navigate+get_page_text:/llms.txtでモジュール/コントローラ特定navigate+get_page_text:/direct/rcms_api/llms/?mt={module}でフィールド名・パラメータ仕様を取得- (書き込み時)
javascript_tool: MODE=schema でフィールド型・必須項目・ブロックエディタ構造を取得javascript_tool: API実行(mt/ct形式)Step 3 は必須。
/llms.txt(概要)にはモジュール一覧しかなく、ext_colのフィールド名は含まれない。フィールド名の把握にはモジュール詳細が必要。 Step 4 は書き込み操作で必須。 スキーマはdata.request.propertiesにJSON Schema形式でフィールド定義を返す。topics_group_idを指定することでext_col/ブロックエディタの必須フィールドが判明する。 discoverモードは通常不要。llms.txtの2段階(概要→詳細)で十分な情報が得られる。
操作フロー
すべてのAPI操作はこのフローに従うこと。
操作開始前の判断: ユーザーから「〜を確認して」「〜を検索して」「〜のデータを見て」等のリクエストを受けた場合、まずそれが**データ取得(API)**で解決できるか判断する。管理画面のUIが目の前にあっても、反射的にクリック操作に入らないこと。APIで取得できるデータはAPIで取得する。UIブラウザ操作はAPIでは不可能な操作(ファイルアップロード、プレビュー、GUI固有の設定変更等)にのみ使用する。
Step 0: claude-in-chrome MCP の確認
このスキルは mcp__claude-in-chrome__* ツール群に依存する。操作開始前に、これらのツールが利用可能か確認する。
確認方法: mcp__claude-in-chrome__tabs_context_mcp を呼び出す。
- 成功した場合 → Step 1 へ進む
- ツールが見つからない / 接続エラーの場合 → ユーザーにセットアップを案内する:
ブラウザ操作に必要な claude-in-chrome MCP が利用できません。
以下の手順でセットアップしてください。
【必要なもの】
1. Google Chrome または Microsoft Edge
2. Claude in Chrome 拡張機能(v1.0.36以上)
→ Chrome Web Store からインストール:
https://chromewebstore.google.com/detail/claude/fcoeoabgfenejglbffodgkkbkcdhcgfn
3. Claude Code v2.0.73 以上
【有効化の手順】
- Claude Code セッション内で `/chrome` を実行
- または `claude --chrome` で起動
セットアップ完了後、もう一度お試しください。
注意: Chrome連携は Anthropic の直接プラン(Pro, Max, Teams, Enterprise)が必要です。サードパーティプロバイダ(Amazon Bedrock, Google Cloud Vertex AI 等)経由では利用できません。
Step 1: 対象サイトの特定
mcp__claude-in-chrome__tabs_context_mcpを呼び出し、URLパターン *.*.kuroco-mng.app に一致するタブを探す。
- タブが1つ見つかった場合 → そのタブのURLからベースURL(
https://{site_key}.{env}.kuroco-mng.app)を取得し、以降の操作で使用 - 複数のKurocoタブが見つかった場合 → どのサイトを操作するかユーザーに確認
- タブが見つからない場合 →
AskUserQuestionツールでサイトキーまたは管理画面URLを確認:
{
"questions": [{
"question": "操作対象のKuroco管理画面を特定できませんでした。サイトキーまたは管理画面URLを教えてください(例: demo / https://demo.g.kuroco-mng.app/management/ / https://demo.stg.kuroco-mng.app/management/)",
"header": "対象サイト",
"options": [
{ "label": "URLを入力する", "description": "管理画面のURL(https://***.*.kuroco-mng.app/management/)を入力" },
{ "label": "サイトキーを入力する", "description": "サイトキー(例: demo)を入力。環境はタブのURLまたはユーザーに確認" }
],
"multiSelect": false
}]
}
ユーザーの回答からベースURLを特定し、mcp__claude-in-chrome__tabs_create_mcp で管理画面を開く:
- URLが提供された場合 → URLからホスト部分を抽出(例:
https://demo.g.kuroco-mng.app) - サイトキーが提供された場合 →
https://{site_key}.{env}.kuroco-mng.appを構築(envはユーザーに確認、デフォルトはg)
注意: すべての操作はブラウザでログイン中のユーザーの権限で実行されます。そのユーザーがアクセス権を持たないモデルやメソッドは操作できません。
Step 2: 認証チェック(軽量リクエスト)
mcp__claude-in-chrome__javascript_toolで以下を実行:
(async () => {
const r = await fetch('/direct/rcms_api/admin_api/?MODE=whoami', {credentials:'include'});
const d = await r.json();
return JSON.stringify({status: r.status, ok: r.ok, member_id: d.member_id, name: (d.name2 || '') + ' ' + (d.name1 || ''), group_ids: d.group_ids});
})()
重要:
javascript_toolではトップレベルawaitは使えない。 ページコンテキストでの評価となるため、awaitを使う場合は必ず(async () => { ... })()でラップすること。トップレベルでawaitを書くとSyntaxError: await is only valid in async functionsになる。
MODE=whoamiはセッション情報(member_id, name1, name2, group_ids, expiresAt)を返す軽量エンドポイント。認証確認と同時にログインユーザーの権限情報が取得できる。
Step 3: llms.txt でサイト構造把握(必須)
navigate + get_page_text で llms.txt を読む。1回のリクエストで以下が得られる:
- 全モジュール・コントローラ一覧
- コンテンツ定義・API・フォーム等のサイトマップ
- admin_api の使い方・パラメータ仕様
- ワークフロー例
navigate → /llms.txt
get_page_text → サイト全体の構造を取得
注意:
- 管理画面ページ巡回の前に必ずllms.txtを確認。大半の構造情報はここで得られる(discoverモード通常不要)
- llms.txt はKurocoがログインユーザーの権限に基づいて動的に生成する。ユーザーの権限外のAPIは表示されない
/llms.txt(概要)にはモジュール一覧のみ。ext_colフィールド名は次の Step 3.5 で取得
Step 3.5: モジュール詳細でフィールド名を把握(必須)
操作対象のモジュールが特定できたら、必ずモジュール詳細を読んでフィールド名を把握する。
navigate → /direct/rcms_api/llms/?mt={module}
get_page_text → ext_colのフィールド名、Available Columns、パラメータ仕様を取得
このステップを省略してはならない。 ext_col_01〜20のフィールド名(例: ext_col_03 = to, ext_col_04 = cc)はモジュール詳細にしか記載されていない。管理画面のDOMを巡回したり、APIレスポンスの中身を推測するのは非効率でエラーの原因になる。
Step 4: スキーマ取得(書き込み操作では必須)
書き込み操作(INSERT/UPDATE)を行う場合は、必ずスキーマを取得してフィールド構造を確認すること。 特にブロックエディタや拡張項目(ext_col)を持つコンテンツ定義では、必須フィールド名や型がスキーマでしか把握できない。
重要: スキーマレスポンスはJSON Schema形式。
data.request直下にはフィールド情報はない。data.request.propertiesにフィールド定義が、data.request.requiredに必須フィールドが格納されている。
コンテンツ定義固有のフィールドを取得するには topics_group_id を指定する:
// javascript_tool — topics_group_id指定でext_col含むフィールド一覧を取得
(async () => {
const r = await fetch('/direct/rcms_api/admin_api/?MODE=schema&mt=topics&ct=topics_edit&topics_group_id=99', {credentials:'include'});
const data = await r.json();
const props = data.request?.properties || {};
const required = Array.isArray(data.request?.required) ? data.request.required : Object.keys(data.request?.required || {});
const fields = {};
Object.entries(props).forEach(([k, v]) => {
fields[k] = {type: v.type, description: v.description, kurocoType: v['x-kuroco-type'], required: required.includes(k)};
});
return JSON.stringify(fields, null, 2);
})();
注意:
topics_group_idを省略すると汎用フィールドのみ返る(ext_col が含まれない)topics_group_idを指定するとブロックエディタ・拡張項目のフィールド名(ext_1,ext_14等)、型、必須フラグが取得できる- list系コントローラ(
topics_list等)はスキーマが空を返すため、書き込み用コントローラ(topics_edit)を指定すること- レスポンスの
x-kuroco-typeでフィールドの種類(wysiwyg,json,checkbox,file等)がわかる
Step 5: API実行
認証確認・構造把握が完了したら、API実行パターンに従ってAPIを実行する。
Step 6: 認証失敗・セッション切れ対応
401または403が返った場合:
- ユーザーにログインが必要な旨を伝える
- ログインURL:
{base_url}/management/login/login/(base_urlはタブのURLから取得) - ユーザーがログインするまで待機
- ログイン後に再度認証チェック
複数ステップの操作中に401/403が発生した場合:
- 即座に操作を停止
- どこまで成功したかをユーザーに報告
- 再認証を案内
- 再認証後、未完了の操作から再開
手段選択の判断基準(最重要)
原則: データの取得・検索・フィルタリングが目的なら、UIではなくAPIを使う。
ブラウザで管理画面が開いていても、UI要素をクリックして情報を探すのは人間の操作方法であり、AIエージェントの操作方法ではない。ブラウザのセッションCookieは javascript_tool の fetch() でも自動送信されるため、UIナビゲーションとAPI呼び出しの認証コストは同じ。
| 目的 | 手段 | 理由 |
|---|---|---|
| データ一覧の取得・検索・フィルタリング | API(admin_api) | 構造化JSON、プログラム的に処理可能 |
| 特定コンテンツの詳細確認 | API(admin_api) | columns指定で必要項目だけ取得可能 |
| サイト構造・API仕様の把握 | llms.txt | 1回で全体把握可能 |
| ファイルアップロード・プレビュー・GUI固有の設定変更 | UIブラウザ操作 | APIでは不可能な操作のみ |
アンチパターン
| やりがち | 問題 | 推奨 |
|---|---|---|
| 管理画面のUI要素をクリックしてデータを検索・閲覧する | 人間の操作を模倣しているだけ。遅く、結果がHTMLで構造化されていない。改善・自動化に繋がらない | admin_apiのfetch()でJSON取得。同じセッションCookieが使えるのでUIを経由する理由がない |
columnsを指定せずにGETリクエスト |
レスポンスが巨大でブロックされる | 常にcolumns=必要カラムを指定(効かない場合は.map()で抽出) |
fetch() でHTMLを取得→DOMParserでパース |
Vue.jsレンダリング部分が空になる | admin_api でJSON取得 |
document.querySelector('main').textContent で全文取得 |
Cookieブロックのリスク | 必要なデータのみ抽出 |
トップレベル await を使う |
SyntaxError になる |
必ず (async () => { ... })() でラップ |
| メールアドレスを含むフィールド値をそのまま返す | [BLOCKED] になる |
.includes() で判定結果のみ返す |
Object.keys(data.request) でスキーマのフィールドを探す |
JSON Schema形式なので type,properties,required しか返らない |
data.request.properties でフィールド一覧を取得する |
スキーマ取得時に topics_group_id を省略する |
汎用フィールドのみでext_colが含まれない | &topics_group_id={id} を指定してコンテンツ定義固有のフィールドを取得 |
API実行パターン
パラメータ形式
admin_apiでは mt(モジュール)と ct(コントローラ)の形式を使用する。
| パラメータ | 説明 | 例 |
|---|---|---|
mt |
モジュール名(小文字) | topics, member, inquiry |
ct |
コントローラ名 | topics_list, topics_edit, member_list |
注意:
model=Topics&method=list形式は使用しないこと。HTMLが返却される場合がある。必ずmt/ct形式を使用する。
GETリクエスト(一覧取得・検索)
// javascript_tool — columns + cnt で必要最小限のレスポンスを取得
(async () => {
const r = await fetch('/direct/rcms_api/admin_api/?mt=topics&ct=topics_list&topics_group_id[]=1&cnt=10&columns=topics_id,subject,ymd', {credentials:'include'});
const data = await r.json();
return JSON.stringify(data, null, 2);
// → columns対応エンドポイントではサーバー側フィルタ(各アイテム {topics_id, subject, ymd} のみ)
// → 非対応の場合は .map() でクライアント側抽出:
// data.topics_list?.map(t => ({id: t.topics_id, subject: t.subject}))
})()
columns原則: GETリクエストでは常に
columnsを指定。対応エンドポイントではサーバー側フィルタリング、非対応でも無視されるだけなので常に指定して問題ない。効かなかった場合はクライアント側.map()で抽出。columnsはexecuteモード専用(discoverモードには適用されない)。
POSTリクエスト(作成・更新・削除)
// javascript_tool — MODE=INSERTで新規作成
(async () => {
const r = await fetch('/direct/rcms_api/admin_api/?mt=topics&ct=topics_edit_api&MODE=INSERT', {
method: 'POST',
credentials: 'include',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
subject: 'タイトル',
contents: '本文',
topics_group_id: 1
})
});
const data = await r.json();
return JSON.stringify(data, null, 2);
})()
重要なルール
| ルール | 説明 |
|---|---|
| credentials | 全リクエストに credentials: 'include' 必須 |
| パラメータ形式 | 必ず mt/ct 形式を使用(model/method 形式は不可) |
| GET配列パラメータ | [] suffix必須(例: topics_group_id[]=1) |
| POST配列パラメータ | ネイティブJSON配列(例: {"topics_group_id": [1]}) |
| 件数制限 | レスポンスが大きい場合は cnt パラメータで制限 |
| columns指定 | columns=col1,col2 でレスポンスカラムを最小限にする。対応エンドポイントでは必ず指定すること(javascript_toolの出力制限対策・レスポンス効率化)。効かないエンドポイントもあるため、その場合はクライアント側で抽出 |
| 変更操作の確認 | insert/update/deleteは実行前にユーザーに確認すること |
| レスポンス切り詰め | javascript_toolの出力制限があるため、大きなレスポンスは必要部分のみ抽出 |
レスポンス構造が不明な場合
.map() する前に Object.keys() で構造を確認。プロパティ名を推測して複数回試行せず、1回で構造を把握する:
(async () => {
const r = await fetch(url, {credentials:'include'});
const data = await r.json();
const keys = Object.keys(data);
const firstArrayKey = keys.find(k => Array.isArray(data[k]));
const firstItem = firstArrayKey ? data[firstArrayKey][0] : null;
return JSON.stringify({
topLevelKeys: keys, firstArrayKey,
firstItemKeys: firstItem ? Object.keys(firstItem) : null
}, null, 2);
})();
詳細は references/error-handling.md を参照
API探索
adviseモード(AI支援、推奨)
やりたいことを自然言語で伝えると、どのAPIをどの順序で呼べばよいかAIが回答する。レスポンスの endpoint, api_spec はシステムが自動生成(ハルシネーションなし)。summary, description, body_example, note はAIが生成。
// javascript_tool — やりたいことからAPI手順を取得
(async () => {
const r = await fetch('/direct/rcms_api/admin_api/?MODE=advise', {
method: 'POST',
credentials: 'include',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({request: '記事を新しく作成したい'})
});
const data = await r.json();
return JSON.stringify(data.advice?.steps?.map(s => ({
step: s.step, description: s.description, mt: s.mt, ct: s.ct,
http_method: s.http_method, mode: s.mode, endpoint: s.endpoint
})), null, 2);
})()
discoverモード(構造化JSON、通常不要)
// javascript_tool — モジュール一覧取得(特定モジュールは &mt=topics を追加)
(async () => {
const r = await fetch('/direct/rcms_api/admin_api/?MODE=discover', {credentials:'include'});
const data = await r.json();
return JSON.stringify(data.modules.map(m => ({mt: m.mt, controllers: m.controllers.map(c => c.ct)})), null, 2);
})()
使い分け
| 方法 | 用途 |
|---|---|
| llms.txt概要(推奨) | サイト全体のAPI構造を把握(最初に使う) |
| llms.txt詳細 | 特定モジュールのパラメータ仕様を確認 |
| advise | やりたいことからAPI手順をAIが提案(mt/ctが不明な場合に有用) |
| discover | プログラム的にモジュール/コントローラを列挙(通常不要) |
詳細は references/api-discovery.md を参照
管理画面ブラウザ操作
API呼び出しだけでは不十分な場合(ファイルアップロード、プレビュー、GUI設定変更等)に使用。
操作手順
mcp__claude-in-chrome__navigateで管理画面ページへ移動mcp__claude-in-chrome__read_pageでアクセシビリティツリー取得mcp__claude-in-chrome__find/mcp__claude-in-chrome__form_input/mcp__claude-in-chrome__computerでフォーム操作mcp__claude-in-chrome__get_page_textでページ内容取得
主要URL
| ページ | URL |
|---|---|
| コンテンツ新規作成 | /management/topics/topics_edit/?topics_group_id={id} |
| コンテンツ編集 | /management/topics/topics_edit/?topics_id={id} |
| コンテンツ一覧 | /management/topics/topics_list/?topics_group_id={id} |
URLパターンは
/management/{module}/{action}/。全URL一覧は references/admin-ui-patterns.md を参照
エラーハンドリング
| コード | 原因 | 対処 |
|---|---|---|
| 401/403 | セッション切れ/権限不足 | ログインURL案内、再認証後リトライ |
| 400 | 不正パラメータ | llms.txt詳細で仕様確認、パラメータ修正 |
| 404 | モジュール/コントローラ不在 | discoverで存在確認 |
| 500 | サーバーエラー | リクエスト内容を確認、再試行 |
| Network Error | タブが別ドメイン | Kurocoドメインのタブで実行しているか確認 |
詳細は references/error-handling.md を参照
セキュリティ注意事項
- Cookie値を表示・ログ出力しないこと — セッション情報の漏洩防止
document.cookieでのトークン抽出は禁止 — HttpOnlyで取得不可かつ不要- 変更操作は必ずユーザー確認後に実行 — insert/update/deleteは取り消しが困難
- APIレスポンスのファイル保存はユーザー同意必須 — 個人情報を含む可能性
- 認証情報をjavascript_toolの出力に含めない — ヘッダーやCookieの内容を返さない
javascript_tool の安全な使い方
ブロックの仕組み
javascript_tool の返却値は chrome拡張のセキュリティフィルタを通過する。以下が検出されると [BLOCKED] に置き換えられる:
- セッションCookie値、
email/login_id等のセンシティブなクエリパラメータ - メールアドレスを含むフィールド値(ext_colに格納されている場合も対象)
- 個人情報を含む大量テキスト
回避ルールと推奨パターン
| ルール | 理由 |
|---|---|
| 返却値は必要最小限に絞る | ID・件数・名前のみ等、必要なフィールドだけ抽出 |
| fetch() の生レスポンスをそのまま返さない | Cookie関連データが混入する可能性 |
| DOM全体のtextContentを返さない | hidden inputやURL文字列にセンシティブ情報が含まれる |
| URLパラメータが多いページでは注意 | URL自体にセンシティブなパラメータ名が含まれるとブロック対象 |
// ✅ 安全 — admin_apiからcolumns指定+必要フィールドのみ抽出
(async () => {
const r = await fetch('/direct/rcms_api/admin_api/?mt=topics&ct=topics_group_list&columns=topics_group_id,group_nm', {credentials:'include'});
const data = await r.json();
return JSON.stringify(data.topics_group_list?.map(g => ({id: g.topics_group_id, name: g.group_nm})));
})();
// ✅ 安全 — メールアドレスは値を返さず判定結果だけ返す
(async () => {
const target = 'user@example.com';
const r = await fetch('/direct/rcms_api/admin_api/?mt=topics&ct=topics_list&topics_group_id[]=1&cnt=30&columns=topics_id,subject,ext_col_03&keyword=' + encodeURIComponent(target), {credentials:'include'});
const data = await r.json();
return JSON.stringify(data.topics_list?.filter(t => {
return (t.ext_col_03 || []).some(v => v && v.includes(target));
}).map(t => ({id: t.topics_id, subject: t.subject})));
})();
ツール別の安全度
| ツール | ブロックリスク | 用途 |
|---|---|---|
get_page_text |
低 (拡張がフィルタ済みテキストを返す) | ページ全文の読み取り |
read_page |
低 (アクセシビリティツリー、Cookie含まない) | DOM構造の把握 |
javascript_tool + admin_api JSON |
低 (必要フィールドのみ抽出すれば安全) | データ操作・API実行 |
javascript_tool + fetch() → 生テキスト |
高 | 避ける |
javascript_tool + DOM全文取得 |
中〜高 (ページ内容による) | 特定要素のみ抽出に限定 |
他スキルとの連携
| スキル | 用途 | 使い分け |
|---|---|---|
/kuroco-api-content |
フロントエンドAPI設計・認証パターン、コンテンツCRUDパターン | エンドユーザー向けAPI |
/kuroco-frontend-integration |
Nuxt.js/Next.js統合、AI自動デプロイ | フロントエンド実装 |
/kuroco-docs |
Kurocoドキュメント参照 | 公式ドキュメント検索 |
本スキルはブラウザ経由のadmin_api実行と管理画面操作に特化。 フロントエンドAPI(*.g.kuroco.app)の操作には上記の関連スキルを使用すること。