kSQL MCP サーバー仕様
- 作成日: 2026-05-25
- 対象:
kintone-sql-tools - 目的: kSQL の SQL 実行エンジンを MCP サーバーとして公開し、AI クライアントから安全に kintone データの検索・集計・比較・検証を行えるようにする
- ステータス: 仕様
1. 背景
kintone-sql-tools は、kintone アプリを SQL 風の構文で操作する CLI / Plugin を提供している。
既存実装では、SQL パーサー、実行エンジン、Node.js 向け kintone API クライアント、DML ガード、APP@profile による複数環境参照が実装済みである。
MCP サーバー化により、これらの機能を AI クライアントから構造化ツールとして呼び出せるようにする。
主な狙いは以下である。
- AI による kintone データ分析の精度を上げる
- 複数アプリ JOIN / GROUP BY / UNION / CTE を AI から安全に利用する
- 本番・検証・移行元・移行先などの複数環境比較を SQL として再現可能にする
- 金額集計や移行検証など、AI 側の手作業集計で間違いやすい処理を SQL 実行エンジン側に寄せる
-
EXPLAIN/ dry-run / DML ガードを MCP ツール設計に組み込む
2. 位置づけ
2.1 標準 kintone MCP サーバーとの関係
kSQL MCP サーバーは、標準 kintone MCP サーバーの置き換えではなく補完として位置づける。
| 項目 | 標準 kintone MCP サーバー | kSQL MCP サーバー |
|---|---|---|
| 主目的 | kintone REST API の標準操作 | SQL による検索・集計・比較 |
| 得意領域 | アプリ情報、フィールド、レコード CRUD、設定操作 | JOIN、GROUP BY、UNION、CTE、EXPLAIN、環境比較 |
| 操作粒度 | REST API に近い | 業務問い合わせ・分析に近い |
| 複数アプリ統合 | AI 側で処理しがち | SQL 実行エンジン側で処理 |
| 複数環境比較 | 可能だが比較処理は AI 側に寄りがち |
APP100@prod のように SQL で表現 |
| アプリ設定操作 | 強い | 対象外 |
| 金額集計・差分検証 | 実装次第で可能 | 主用途 |
推奨する使い分けは以下である。
| 用途 | 推奨 |
|---|---|
| kintone アプリ設定の取得・変更 | 標準 kintone MCP |
| フィールド定義やフォーム設定の操作 | 標準 kintone MCP |
| 単純なレコード取得・更新 | 標準 kintone MCP |
| 複数アプリ JOIN | kSQL MCP |
| 金額集計、部門別集計、月次集計 | kSQL MCP |
| 本番・検証・旧新環境の差分比較 | kSQL MCP |
| 移行検証レポート | kSQL MCP |
| SQL として再利用できる検証条件 | kSQL MCP |
2.2 kSQL MCP の基本方針
- 初期版は read-only を既定とする
- DML は SELECT 系ツールとは分離する
- DML は明示的な許可、対象件数上限、確認文字列を必須にする
- 実行結果は文字列ではなく構造化データとして返す
- AI が作成した SQL は、保存ツールを使った場合のみ永続化する
- SQL の実行前検証として
EXPLAINを利用できるようにする - 既存 CLI の config / profile / auth / DML ガード仕様と矛盾させない
3. 想定利用者
- kintone データを AI から分析したい開発者
- 複数アプリのデータ統合・突合を行う運用担当者
- 本番・検証・移行先環境の差分を確認したい管理者
- 月次集計、売上集計、案件集計などを再利用可能な SQL として管理したい利用者
- Claude Desktop / Claude Code / Codex / Cursor / VS Code 拡張などの MCP 対応クライアント利用者
4. スコープ
4.1 MVP スコープ
MVP では以下を対象とする。
- MCP stdio transport
- SELECT / SHOW APPS / DESCRIBE / EXPLAIN の実行
-
APP@profileを含む SQL の実行 - config / profile / tokenMap / userpass 認証の利用
-
maxRecords/onLimit/timeoutの指定 - 構造化 JSON 結果の返却
- DML の拒否
- Jest による MCP 実行層の単体テスト
4.2 実用版スコープ
実用版では以下を追加する。
- 保存 SQL の登録・一覧・取得・実行・削除
- read-only ツールと DML ツールの明確な分離
-
INSERT/UPDATE/UPSERT/DELETEの承認付き実行 - DML 対象件数上限
-
EXPLAIN先行を要求する安全モード - query catalog のファイル保存
- MCP ツール説明文の改善
- Claude Desktop / Claude Code での接続例
4.3 将来スコープ
将来検討として以下を扱う。
- Streamable HTTP transport
- OAuth または外部認可との連携
- 保存 SQL の署名・承認フロー
- query catalog のチーム共有
- 実行履歴・監査ログ
- スケジュール実行
- 標準 kintone MCP サーバーとの併用ガイド
- フィールド定義キャッシュの MCP resource 化
4.4 対象外
初期版では以下を対象外とする。
- kintone アプリ設定の変更
- フォームレイアウト編集
- プロセス管理設定変更
- プラグイン設定変更
- kintone REST API 全体のラップ
- AI モデルの内蔵
MCP サーバーは AI ではなく、AI クライアントから呼ばれるツールサーバーである。
5. アーキテクチャ
5.1 推奨ディレクトリ構成
src/
core/
index.ts
sql.ts
displayFormat.ts
cli/
index.ts
nodeKintoneClient.ts
node/
config.ts
appProfiles.ts
runtime.ts
dmlGuard.ts
output.ts
mcp/
index.ts
tools.ts
schemas.ts
savedQueries.ts
errors.ts
5.2 共通化方針
現在 CLI に閉じている処理のうち、MCP でも必要なものを src/node/ に切り出す。
| 既存の責務 | 現状 | 移動先候補 |
|---|---|---|
| config 読み込み | src/cli/index.ts |
src/node/config.ts |
| profile 解決 | src/cli/index.ts |
src/node/runtime.ts |
APP@profile 正規化 |
src/cli/index.ts |
src/node/appProfiles.ts |
| token/env 解決 | src/cli/index.ts |
src/node/runtime.ts |
| DML ガード | src/cli/index.ts |
src/node/dmlGuard.ts |
| 出力整形 | src/cli/index.ts |
src/node/output.ts |
| Node kintone client | src/cli/nodeKintoneClient.ts |
当面既存利用、将来 src/node/ へ移動検討 |
CLI は src/node/ の共通 runtime を利用する。
MCP も同じ runtime を利用する。
これにより、CLI と MCP の profile / auth / DML 安全ルールを一致させる。
5.3 実行フロー
AI クライアント
-> MCP tool call
-> src/mcp/tools.ts
-> src/node/runtime.ts
-> src/core/execute()
-> KintoneClient
-> kintone REST API
EXPLAIN や dryRun の場合は、kintone API を呼び出さない。
6. 設定
6.1 設定ファイル
既存 CLI と同じ ksql.config.json を利用する。
{
"defaultProfile": "prod",
"profiles": {
"prod": {
"baseUrl": "https://example.cybozu.com",
"auth": "token",
"tokenMap": {
"APP100": "env:KSQL_TOKEN_APP100",
"APP200": "env:KSQL_TOKEN_APP200"
},
"query": {
"maxRecords": 500,
"onLimit": "error",
"timeout": 30000
}
},
"stg": {
"baseUrl": "https://example-stg.cybozu.com",
"auth": "token",
"tokenMap": {
"APP100": "env:KSQL_STG_TOKEN_APP100"
}
}
}
}
6.2 MCP 起動設定
Claude Desktop などの MCP クライアントからは以下のように起動する。
{
"mcpServers": {
"ksql": {
"command": "ksql-mcp",
"args": [
"--config",
"C:/path/to/ksql.config.json"
],
"env": {
"KSQL_TOKEN_APP100": "...",
"KSQL_TOKEN_APP200": "..."
}
}
}
}
ローカル開発時は以下を想定する。
node dist-mcp/ksql-mcp.js --config ./ksql.config.json
MCP サーバーでは、config path は原則としてサーバー起動時に固定する。
各 tool call の入力には configPath を持たせない。
理由:
- MCP サーバーは起動時設定に基づいて安定して動作するほうが単純で安全
- tool call ごとに config を切り替えると、AI が意図せず接続先を変更するリスクがある
- 複数環境は config path の差し替えではなく
APP@profileとprofile入力で扱う
将来、複数 config を扱う必要が出た場合は、明示的な multi-config mode として別途設計する。
7. MCP ツール仕様
7.1 ksql_explain
SQL の実行計画を返す。
kintone API は呼ばない。
入力:
{
"sql": "SELECT 顧客コード, SUM(金額) AS 合計 FROM APP100 GROUP BY 顧客コード",
"profile": "prod"
}
出力:
{
"ok": true,
"type": "EXPLAIN",
"columns": ["plan"],
"rows": [
{ "plan": " mode: FULL_SCAN" }
],
"rowCount": 1,
"warnings": []
}
用途:
- AI が作成した SQL の事前確認
- kintone API 呼び出し予定の把握
- DML 前の安全確認
7.2 ksql_query
read-only SQL を実行する。
許可する文:
SELECTWITHUNIONSHOW APPSDESCRIBEEXPLAIN
拒否する文:
INSERTUPDATEUPSERTDELETEREORDER
入力:
{
"sql": "SELECT 部門, SUM(金額) AS 合計金額 FROM APP100@prod GROUP BY 部門",
"profile": "prod",
"maxRecords": 500,
"onLimit": "error"
}
format パラメーターは持たせない。
MCP tool result は常に構造化 JSON として返す。
onLimit は tool input 上の名前であり、execute() に渡すときは ExecuteOptions.onLimitReached に明示的にマッピングする。
maxRecords は MCP 層で既定値 500 を明示し、execute() に必ず渡す。
execute() 内部の既定値とは独立して、AI 利用時の安全側の既定値を MCP 層で固定する。
timeout は execute() の option ではなく、Node.js kintone client 作成時の HTTP timeout として解決する。
tool input として受ける場合も、execute() ではなく runtime/client 生成に渡す。
出力:
{
"ok": true,
"type": "SELECT",
"columns": ["部門", "合計金額"],
"rows": [
{ "部門": "営業", "合計金額": "1200000" }
],
"rowCount": 1,
"warnings": []
}
7.3 ksql_describe_app
指定アプリのフィールド一覧を返す。
入力:
{
"app": 100,
"profile": "prod"
}
内部的には以下の SQL と等価に扱ってよい。
DESCRIBE APP100
出力:
{
"ok": true,
"app": 100,
"profile": "prod",
"fields": [
{
"code": "顧客コード",
"label": "顧客コード",
"fieldType": "SINGLE_LINE_TEXT"
},
{
"code": "金額",
"label": "金額",
"fieldType": "NUMBER"
}
]
}
7.4 ksql_show_apps
利用可能な kintone アプリ一覧を返す。
入力:
{
"profile": "prod"
}
内部的には以下の SQL と等価に扱ってよい。
SHOW APPS
7.5 ksql_validate
SQL を解析し、実行前チェックのみ行う。
実施する検証:
- 構文解析
- 文種別判定
-
APP@profile正規化 - 参照 APP 抽出
- DML かどうか
- read-only ツールで実行可能か
-
UPDATE/DELETEの WHERE 有無 -
INSERT行数
入力:
{
"sql": "UPDATE APP100 SET ステータス = '完了' WHERE 顧客コード = 'C001'",
"profile": "prod"
}
出力:
{
"ok": true,
"statementType": "UPDATE",
"isDml": true,
"hasWhere": true,
"appIds": [100],
"canRunWithQueryTool": false,
"requiresMutationTool": true
}
7.6 ksql_mutate
DML を承認付きで実行する。
Phase 1.5 / Phase 2 で初期実装する。
初期実装で許可する文:
-
INSERT(VALUES 形式) UPDATEUPSERTDELETEREORDER
初期実装で拒否する文:
INSERT_SELECTUPSERT_SELECT
INSERT_SELECT / UPSERT_SELECT は、書き込み確認より前に source SELECT や既存レコード照合の API 読み取りが発生する。
また、現行 executeInsertSelect では ExecuteOptions.confirm が呼ばれない。
そのため、初期の ksql_mutate では対象外とする。
将来対応する場合は、SELECT source 件数確定後の confirm hook を追加するか、MCP 側で source SELECT preflight を行う方針を別途決める。
7.6.1 SELECT-based DML のリスク
INSERT_SELECT / UPSERT_SELECT は、通常の INSERT VALUES / UPDATE / UPSERT よりも MCP での安全制御が難しい。
主なリスク:
| リスク | 内容 | 影響 |
|---|---|---|
| 確認前の API 読み取り | source SELECT や既存レコード照合が書き込み確認より前に実行される | ユーザーが未承認の段階で API アクセスが発生する |
INSERT_SELECT の confirm 不足 |
現行 executeInsertSelect() は書き込み前に ExecuteOptions.confirm を呼ばない |
dmlMaxRows による大量 INSERT 防止が効かない |
UPSERT_SELECT の照合コスト |
source SELECT 後、既存レコード照合が必要になる | 大量 API call、遅延、rate limit のリスク |
| MCP 側 preflight の二重実行 | MCP 側で件数確認 SELECT を行うと、execute 本体でも SELECT が走る | 負荷増加、結果不一致、TOCTOU |
| TOCTOU | preflight と本実行の間に kintone データが変わる | 件数・対象の不一致 |
| 件数の意味の曖昧さ |
UPSERT_SELECT は insert 件数と update 件数が混在する |
dmlMaxRows の意味がユーザー期待とずれる可能性 |
7.6.2 SELECT-based DML の対応方針
初期実装では、INSERT_SELECT / UPSERT_SELECT を拒否する。
これは「危険な書き込みを防ぐ」だけでなく、「確認前に重い API 読み取りを実行しない」ための方針でもある。
将来対応する場合は、MCP 側で ad hoc に source SELECT preflight を組み立てるのではなく、実行エンジン側に書き込み前 confirm hook を追加する。
推奨する段階対応:
-
executeInsertSelect()に source SELECT 後・POST 前の confirm hook を追加する -
ExecuteOptions.confirmを operation 種別付きの object 引数へ拡張する - MCP の
ksql_mutateにallowSelectBasedDml: trueを追加する -
INSERT_SELECTのみ先に解禁する -
UPSERT_SELECTは API call 数・insert/update 内訳・rate limit の扱いを整理してから別フェーズで解禁する
UPSERT_SELECT は、source SELECT 件数だけでは実際の insert/update 件数が確定しない。
既存レコード照合後に toInsert.length + toUpdate.length を確認する必要があるため、INSERT_SELECT より後のフェーズで扱う。
7.6.3 将来の confirm hook 仕様案
現行の ExecuteOptions.confirm は以下の形である。
confirm?: (count: number, operation: "UPDATE" | "DELETE") => Promise<boolean>;
SELECT-based DML を安全に扱うには、operation と statement type をより明示できる object 型へ拡張する。
confirm?: (info: {
count: number;
operation: "INSERT" | "UPDATE" | "DELETE" | "UPSERT" | "REORDER";
statementType:
| "INSERT"
| "INSERT_SELECT"
| "UPDATE"
| "DELETE"
| "UPSERT"
| "UPSERT_SELECT"
| "REORDER";
phase: "beforeWrite";
}) => Promise<boolean>;
後方互換が必要な場合は、既存 callback 形式を維持しつつ、新しい confirmMutation を追加する案もある。
confirmMutation?: (info: {
count: number;
operation: "INSERT" | "UPDATE" | "DELETE" | "UPSERT" | "REORDER";
statementType: string;
phase: "beforeWrite";
}) => Promise<boolean>;
INSERT_SELECT 対応時の想定フロー:
- source SELECT を実行して行数を確定する
- 転送先フィールド数・型変換を検証する
-
confirmMutation({ count: rows.length, operation: "INSERT", statementType: "INSERT_SELECT", phase: "beforeWrite" })を呼ぶ -
dmlMaxRows超過なら MCP 側 confirm 実装がArgumentErrorを投げる - 確認成功後に POST する
UPSERT_SELECT 対応時の想定フロー:
- source SELECT を実行する
- key field を検証する
- 既存レコード照合を行い、
toInsert/toUpdateを確定する -
confirmMutation({ count: toInsert.length + toUpdate.length, operation: "UPSERT", statementType: "UPSERT_SELECT", phase: "beforeWrite" })を呼ぶ -
dmlMaxRows超過なら MCP 側 confirm 実装がArgumentErrorを投げる - 確認成功後に POST / PUT する
7.6.4 SELECT-based DML 解禁時の MCP 入力案
INSERT_SELECT / UPSERT_SELECT を解禁する場合は、通常 DML より強い明示承認を要求する。
{
"sql": "INSERT INTO APP200 (顧客名) SELECT 顧客名 FROM APP100 WHERE ランク = 'A'",
"profile": "prod",
"allowDml": true,
"confirmText": "yes",
"dmlMaxRows": 100,
"allowSelectBasedDml": true
}
allowSelectBasedDml がない場合、INSERT_SELECT / UPSERT_SELECT は引き続き拒否する。
解禁後も、以下は必須とする。
-
dmlMaxRowsによる上限確認 - confirm hook が呼ばれない文種は実行しない
-
UPSERT_SELECTは insert/update 合計件数をdmlMaxRowsと比較する -
ksql_explainまたは read-only SELECT による事前確認を推奨する
安全条件:
-
allowDml: trueが必須 -
confirmText: "yes"が必須 -
dmlMaxRowsが必須 -
UPDATE/DELETEは WHERE 必須 - WHERE なし実行を許可する
allowWithoutWhereは初期実装では提供しない - 対象件数が
dmlMaxRowsを超える場合は拒否 -
EXPLAIN結果または直前の validation 結果を要求するモードを検討する
DML の対象件数確認は、execute() の ExecuteOptions.confirm コールバックで行う。
UPDATE / DELETE / UPSERT / REORDER は実行エンジンが対象 ID または対象件数を解決した後に confirm(count, operation) を呼ぶため、MCP 側ではこの count を dmlMaxRows と比較し、超過時は false ではなく ArgumentError として拒否する。
INSERT(VALUES 形式)は confirm が呼ばれないため、execute() を呼ぶ前に parseSqlStatement() の結果から stmt.values.length を取得し、dmlMaxRows と比較する。
この方式では、件数確認のために MCP 側で別 SELECT を組み立てる必要はない。
ただし、ユーザー確認用には ksql_explain と read-only SELECT を先行させる運用を推奨する。
入力:
{
"sql": "UPDATE APP100@prod SET ステータス = '完了' WHERE 顧客コード = 'C001'",
"profile": "prod",
"allowDml": true,
"confirmText": "yes",
"dmlMaxRows": 10
}
出力:
{
"ok": true,
"type": "UPDATE",
"updatedCount": 1
}
7.7 保存 SQL ツール
実用版では、AI が作成した SQL を保存・再利用するためのツールを提供する。
ksql_save_query
入力:
{
"name": "monthly_sales_summary",
"title": "月別売上集計",
"description": "APP100 の金額を受注月ごとに集計する",
"sql": "SELECT DATE_FORMAT(受注日, 'YYYY-MM') AS 月, SUM(金額) AS 合計金額 FROM APP100@prod GROUP BY 月 ORDER BY 月",
"defaultProfile": "prod",
"readOnly": true,
"allowProfileOverride": false,
"tags": ["sales", "monthly"]
}
ksql_list_queries
保存済み SQL の一覧を返す。
ksql_get_query
保存済み SQL の内容を返す。
ksql_run_saved_query
保存済み SQL を実行する。
readOnly: true の保存 SQL は ksql_query と同じ安全条件で実行する。
readOnly: false の保存 SQL は ksql_mutate と同じく allowDml: true、confirmText: "yes"、dmlMaxRows を実行時に要求する。
ksql_delete_query
保存済み SQL を削除する。
8. 保存 SQL カタログ
8.1 保存場所
初期実装では、プロジェクトローカルの JSON ファイルを利用する。
.ksql/queries.json
.ksql/queries.json は個人用のローカルカタログとして扱い、commit しない。
保存先は tool input では指定しない。
優先順位は以下の通り。
- MCP サーバー起動環境の
KSQL_SAVED_QUERIES -
ksql.config.jsonのmcp.savedQueries.path - 既定値
.ksql/queries.json
mcp.savedQueries.path と既定値の相対パスは、--config で指定した config ファイルのディレクトリ基準で解決する。
これにより Claude Desktop / Windows で cwd が C:\WINDOWS\system32 になっても、保存先が system32 配下にならない。
設定例:
{
"defaultProfile": "prod",
"mcp": {
"savedQueries": {
"path": ".ksql/queries.json"
}
}
}
リポジトリ配布物として共有したい場合は、以下のようなディレクトリを検討する。
queries/
monthly_sales_summary.sql
migration_diff_app100.sql
8.2 保存形式
{
"version": 1,
"queries": [
{
"name": "monthly_sales_summary",
"title": "月別売上集計",
"description": "APP100 の金額を受注月ごとに集計する",
"sql": "SELECT ...",
"defaultProfile": "prod",
"readOnly": true,
"allowProfileOverride": false,
"createdAt": "2026-05-24T00:00:00+09:00",
"updatedAt": "2026-05-24T00:00:00+09:00",
"tags": ["sales", "monthly"]
}
]
}
8.3 保存 SQL の安全ルール
-
readOnly: trueの保存 SQL は DML 文を拒否する - DML 保存 SQL は
readOnly: falseを明示する - 保存時に
ksql_validate相当の検証を行う - 実行時にも再検証する
- 保存 SQL は
defaultProfileを持つ - 実行時 profile override は既定で禁止する
- override を許可する保存 SQL は
allowProfileOverride: trueを明示する -
allowProfileOverride: trueの場合も、実行時にksql_validateとEXPLAINを再実行する
保存 SQL の profile 方針は「既定は禁止、必要なクエリだけ明示許可」とする。
同名保存は上書きし、createdAt は維持して updatedAt を更新する。
query name は ASCII 英数字開始、英数字・_・- のみ、最大 64 文字とする。
9. 複数環境比較
9.1 基本方針
既存 CLI の APP@profile 記法を MCP でも利用する。
例:
SELECT p.顧客コード, p.会社名
FROM APP100@prod p
LEFT JOIN APP100@stg s
ON p.顧客コード = s.顧客コード
WHERE s.顧客コード IS NULL
LIMIT 50
これにより、同一 SQL 内で本番環境と検証環境を比較できる。
9.2 代表ユースケース
- prod にだけ存在するレコード
- stg にだけ存在するレコード
- prod と stg で値が異なるレコード
- 移行元と移行先の件数比較
- 移行元と移行先の金額合計比較
- マスタ未反映の検出
- 同一キー重複の検出
9.3 差分確認 SQL 例
prod にだけ存在する顧客:
SELECT p.顧客コード, p.会社名
FROM APP100@prod p
LEFT JOIN APP100@stg s
ON p.顧客コード = s.顧客コード
WHERE s.顧客コード IS NULL
LIMIT 50
値が異なる顧客:
SELECT
p.顧客コード,
p.会社名 AS prod会社名,
s.会社名 AS stg会社名,
p.ステータス AS prodステータス,
s.ステータス AS stgステータス
FROM APP100@prod p
JOIN APP100@stg s
ON p.顧客コード = s.顧客コード
WHERE
p.会社名 != s.会社名
OR p.ステータス != s.ステータス
LIMIT 50
移行前後の金額合計比較:
SELECT 'old' AS 環境, SUM(金額) AS 合計金額 FROM APP200@old
UNION ALL
SELECT 'new' AS 環境, SUM(金額) AS 合計金額 FROM APP200@new
10. 金額集計・複数アプリ統合
10.1 kSQL MCP が向いている理由
標準 MCP で金額集計や複数アプリ統合を行う場合、AI が以下を自前で行うことになりやすい。
- ページング
- 件数上限の扱い
- JOIN キーの突合
- 数値文字列の変換
- NULL / 空文字の扱い
- 重複キーの扱い
- 集計ロジックの再利用
kSQL MCP では、これらを SQL と実行エンジン側に寄せられる。
10.2 集計 SQL 例
部門別金額集計:
SELECT 部門, SUM(金額) AS 合計金額
FROM APP100@prod
GROUP BY 部門
ORDER BY 合計金額 DESC
顧客マスタと受注アプリの JOIN:
SELECT
c.顧客コード,
c.会社名,
SUM(o.金額) AS 受注合計
FROM APP100@prod c
JOIN APP200@prod o
ON c.顧客コード = o.顧客コード
GROUP BY c.顧客コード, c.会社名
ORDER BY 受注合計 DESC
LIMIT 100
11. プロンプト例
11.1 環境比較
prod と stg の APP100 を比較してください。
キーは 顧客コード です。
以下を確認してください。
1. prod にだけ存在するレコード
2. stg にだけ存在するレコード
3. 両方に存在するが、会社名・担当者・ステータス が異なるレコード
最初に EXPLAIN を実行してください。
SELECT のみ実行してください。
INSERT / UPDATE / DELETE は実行しないでください。
11.2 金額集計
APP200@prod の受注データを部門別に集計してください。
集計対象は 金額 フィールドです。
上位20件を合計金額の降順で表示してください。
実行前に DESCRIBE APP200 と EXPLAIN を確認してください。
11.3 保存 SQL の再利用
このSQLを「月別売上集計」として保存してください。
保存後、保存済みSQLとして実行してください。
12. セキュリティと安全制御
12.1 認証情報
- token / password は MCP tool result に含めない
- debug 出力でも認証ヘッダーはマスクする
-
env:参照を推奨する - 保存 SQL に token を含めない
- エラーに baseUrl や token を過剰に含めない
12.2 read-only 既定
ksql_query は read-only 文のみ許可する。
DML は ksql_mutate に分離する。
12.3 DML ガード
DML 実行時は以下を必須とする。
allowDml: trueconfirmText: "yes"dmlMaxRows- WHERE あり
- 実行前 validation
- 対象件数上限チェック
12.4 件数上限
MCP では maxRecords の既定値を CLI と同じく 500 とする。
onLimit の既定は error を推奨する。
実装上は、MCP tool input の onLimit を execute() の onLimitReached にマッピングする。
また、execute() 内部の既定値に依存せず、MCP 層から maxRecords: input.maxRecords ?? 500 を必ず渡す。
AI が集計結果を誤認しないよう、上限到達時は以下のいずれかとする。
- エラーで停止する
-
warningsに明示し、結果にtruncated: trueを含める
12.5 トランザクション制約
kintone 複数環境・複数アプリをまたぐ処理は、RDBMS のようなトランザクション保証を持たない。
統合作業では、以下の順序を推奨する。
ksql_explain- read-only SELECT による対象確認
- 件数・金額・キー重複の検証
- 保存 SQL として記録
- 必要な場合のみ
ksql_mutate
13. エラー形式
MCP tool result は、例外をそのまま返すのではなく、構造化された失敗結果に変換する。
{
"ok": false,
"error": {
"code": "ArgumentError",
"message": "DML is not allowed by ksql_query. Use ksql_mutate.",
"details": {
"statementType": "UPDATE"
}
}
}
代表的な code:
| code | 意味 |
|---|---|
ParseError |
SQL 構文エラー |
ArgumentError |
引数・安全制御エラー |
AuthError |
認証・token 解決エラー |
KintoneApiError |
kintone API エラー |
LimitError |
取得件数上限 |
OperationCancelled |
DML キャンセル |
14. build / package
14.1 package.json
bin に ksql-mcp を追加する。
{
"bin": {
"ksql": "dist-cli/ksql.js",
"ksql-mcp": "dist-mcp/ksql-mcp.js"
}
}
scripts には MCP build を追加する。
{
"scripts": {
"build": "npm run build:plugin && npm run build:cli && npm run build:mcp",
"build:mcp": "node build-mcp.mjs"
}
}
Phase 1 MVP では、ksql CLI と ksql-mcp を同一 npm package から提供する。
ただし、ksql-mcp の実行時に使う @modelcontextprotocol/sdk と zod は dependencies ではなく devDependencies に置く。
build:mcp で dist-mcp/ksql-mcp.js に完全 bundle し、npm 利用者が CLI / Plugin だけを使う場合に MCP SDK を追加取得しないようにする。
optionalDependencies は採用しない。
npm install --no-optional で ksql-mcp bin が壊れるためである。
公開前には、pack した tarball を devDependencies なしの環境に install し、ksql-mcp --help と API なし smoke test が動くことを確認する。
MCP が大きくなり bundle size や release 管理が問題になる場合は、Phase 2 以降で ksql-mcp の別 package 化を検討する。
ただし MVP では、同一 package の別 bin として提供する。
14.2 build-mcp.mjs
MCP サーバーは Node.js 向けに bundle する。
entry: src/mcp/index.ts
outfile: dist-mcp/ksql-mcp.js
platform: node
target: node18
format: cjs
15. テスト方針
15.1 単体テスト
追加するテスト:
-
ksql_explainが API を呼ばない -
ksql_queryが SELECT を実行する -
ksql_queryが UPDATE を拒否する -
ksql_validateが DML / WHERE 有無を判定する -
APP@profileが MCP 経由でも解決される -
maxRecords/onLimitがmaxRecords/onLimitReachedとしてexecute()に反映される - 保存 SQL の保存・一覧・取得・削除
-
formatとconfigPathが tool input に存在しない -
timeoutが HTTP client 作成に渡る
15.2 既存テスト
既存の以下のテストを継続して通す。
- parser
- lexer
- execute
- converter
- fetchAll
- CLI DML guard
- CLI console
- display format
15.3 手動検証
node dist-mcp/ksql-mcp.js --config ./ksql.config.json- MCP クライアントから
ksql_explain - MCP クライアントから
ksql_query - DML が拒否されること
-
APP@profileの環境比較 SQL が実行できること
16. 実装ロードマップ
16.1 Phase 0: 調査
- MCP TypeScript SDK の追加方法確認
- CLI の共通化対象関数を洗い出す
- DML ガードの MCP 向け仕様確定
16.2 Phase 1: read-only MVP
-
src/node/appProfiles.tsにAPP@profile正規化の最小共通化を追加 -
src/mcp/index.ts追加 -
ksql_explain追加 -
ksql_query追加 -
ksql_describe_app追加 -
ksql_show_apps追加 -
build-mcp.mjs追加 -
package.jsonにksql-mcpbin 追加 - Jest テスト追加
ksql_describe_app と ksql_show_apps は Phase 1 の必須範囲とする。
AI が SQL 構文を組み立てる前に、アプリ一覧とフィールド定義を確認できる必要があるためである。
Phase 1 では APP@profile を先送りしない。
複数環境比較は kSQL MCP の主要な差別化要素であるため、CLI runtime 全体の共通化前でも normalizeSqlAppProfiles、extractAppIds、normalizeAppKey などの最小関数は src/node/appProfiles.ts へ切り出して MCP から利用する。
16.3 Phase 2: runtime 共通化
- config 読み込みを
src/node/config.tsに切り出す - Phase 1 で作成した
src/node/appProfiles.tsを CLI 側にも適用する - profile / auth 解決を
src/node/runtime.tsに切り出す - CLI を共通 runtime 利用へ変更
- MCP も共通 runtime 利用へ変更
16.4 Phase 3: 保存 SQL
ksql_save_queryksql_list_queriesksql_get_queryksql_run_saved_queryksql_delete_query- 保存形式のテスト
16.5 Phase 1.5 / Phase 2: 承認付き DML
ksql_mutate- DML validation
dmlMaxRowsconfirmText- WHERE なし拒否
- 実行結果の構造化
16.6 Phase 5: ドキュメントと接続例
- README 追記
- Claude Desktop 接続例
- Claude Code 接続例
- Codex 利用例
- 標準 kintone MCP との併用ガイド
17. 評価
17.1 実現性
評価: 高い
理由:
-
execute(sql, client, options)が既に存在する -
KintoneClientが注入式で MCP から呼びやすい - Node.js 向け kintone client がある
-
EXPLAINが既にある - DML ガード思想が既にある
- 既存テストが多い
17.2 価値
評価: 高い
特に価値が高い用途:
- 金額集計
- 複数アプリ JOIN
- 本番・検証差分
- 移行検証
- 保存 SQL による再利用
- AI の集計ミス削減
17.3 リスク
評価: 中
主なリスク:
- DML の誤実行
- token / password の漏洩
- 大量取得による API 負荷
- 複数環境更新時のトランザクション不在
- SQL 生成ミス
- 保存 SQL の管理不備
対応:
- read-only 既定
- DML ツール分離
-
EXPLAIN先行 -
maxRecordsとonLimit=error - 認証情報マスク
- 保存 SQL の validate
18. 推奨結論
kSQL MCP サーバーは、kintone-sql-tools 本体リポジトリに src/mcp/ として追加するのがよい。
ただし、CLI と密結合させず、CLI に閉じている config / profile / auth / DML ガード処理を src/node/ に共通化する。
初期版は read-only MCP として実装し、金額集計・複数アプリ JOIN・複数環境比較の価値を確認する。
DML と保存 SQL は、read-only MVP の安定後に段階的に追加する。
最終的には以下の役割分担を推奨する。
標準 kintone MCP:
kintone REST API 操作、アプリ設定、フォーム設定、標準 CRUD
kSQL MCP:
SQL 検索、金額集計、複数アプリ JOIN、環境比較、移行検証、保存 SQL