Skip to main content
Glama

Kintone MCP Server

by r3-yamauchi
ErrorHandlers.js18.4 kB
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js'; import { LoggingUtils } from '../../utils/LoggingUtils.js'; import { KintoneApiError } from '../../repositories/base/http/KintoneApiError.js'; function formatErrorMessage(errorType, errorDetail, suggestions) { return ` 【エラーの種類】 ${errorType} 【エラーの詳細】 ${errorDetail} 【対応方法】 ${suggestions.map((s, i) => `${i+1}. ${s}`).join('\n')} `; } function handleChoiceFieldError(error) { if (error.message.includes("選択肢") && error.message.includes("label")) { return formatErrorMessage( "選択肢フィールドの設定エラー", "options オブジェクトの label 設定に問題があります。", [ "options オブジェクトの各キーと label の値が完全に一致しているか確認してください。\n 正しい例: \"status\": { \"label\": \"status\", \"index\": \"0\" }\n 誤った例: \"status\": { \"label\": \"ステータス\", \"index\": \"0\" }", "get_field_type_documentation ツールを使用して、正しい形式を確認してください:\n 例: get_field_type_documentation({ field_type: \"RADIO_BUTTON\" })", "create_choice_field ツールを使用して、正しい形式のフィールド設定を生成することもできます:\n 例: create_choice_field({\n field_type: \"RADIO_BUTTON\",\n code: \"status\",\n label: \"ステータス\",\n choices: [\"not_started\", \"in_progress\", \"completed\"]\n })" ] ); } if (error.message.includes("選択肢") && error.message.includes("index")) { return formatErrorMessage( "選択肢フィールドの index 設定エラー", "options オブジェクトの index 設定に問題があります。", [ "index は文字列型の数値(\"0\", \"1\"など)で指定されているか確認してください。\n 正しい例: \"status\": { \"label\": \"status\", \"index\": \"0\" }\n 誤った例: \"status\": { \"label\": \"status\", \"index\": 0 }", "index は 0 以上の整数値である必要があります。", "get_field_type_documentation ツールを使用して、正しい形式を確認してください:\n 例: get_field_type_documentation({ field_type: \"RADIO_BUTTON\" })" ] ); } return null; } function handleCalcFieldError(error) { if (!(error.message.includes("計算") || error.message.includes("expression") || error.message.includes("CALC"))) { return null; } if (error.message.includes("関数はkintoneではサポートされていません")) { return error.message; } if (error.message.includes("サブテーブル内のフィールド") || error.message.includes("テーブル名を指定せず")) { return formatErrorMessage( "計算フィールドのフィールド参照エラー", "サブテーブル内のフィールド参照方法が正しくありません。", [ "サブテーブル内のフィールドを参照する場合は、テーブル名を指定せず、フィールドコードのみを使用してください。\n 正しい例: SUM(金額)\n 誤った例: SUM(経費明細.金額)", "kintoneでは、フィールドコードはアプリ内で一意であるため、サブテーブル名を指定する必要はありません。", "get_field_type_documentation ツールで計算フィールドの仕様を確認してください:\n 例: get_field_type_documentation({ field_type: \"CALC\" })" ] ); } const helpText = formatErrorMessage( "計算フィールドの設定エラー", "計算式または計算フィールドの設定に問題があります。", [ "kintoneの計算フィールドでサポートされている主な関数:\n - SUM: 合計を計算\n - ROUND, ROUNDUP, ROUNDDOWN: 数値の丸め処理\n - IF, AND, OR, NOT: 条件分岐\n - DATE_FORMAT: 日付の書式設定と計算", "計算式の構文が正しいか確認してください。\n - 括弧の対応が取れているか\n - 演算子の使用方法が正しいか", "参照しているフィールドが存在するか確認してください。\n - フィールドコードのスペルミスがないか\n - 参照先のフィールドが既に作成されているか", "サブテーブル内のフィールドを参照する場合は、テーブル名を指定せず、フィールドコードのみを使用してください。\n 正しい例: SUM(金額)\n 誤った例: SUM(経費明細.金額)", "日付の計算例:\n - 日付の差分: DATE_FORMAT(日付1, \"YYYY/MM/DD\") - DATE_FORMAT(日付2, \"YYYY/MM/DD\")", "循環参照がないか確認してください。\n - フィールドA→フィールドB→フィールドAのような参照関係がないか", "get_field_type_documentation ツールで計算フィールドの仕様を確認してください:\n 例: get_field_type_documentation({ field_type: \"CALC\" })" ] ); return helpText + "\n\n【kintone計算フィールドの詳細仕様の確認方法】\n" + "1. get_field_type_documentation ツールを使用: get_field_type_documentation({ field_type: \"CALC\" })\n" + "2. 計算フィールドの作成例: create_calc_field({ code: \"total\", label: \"合計\", expression: \"price * quantity\" })\n" + "3. kintone公式ドキュメント: https://jp.cybozu.help/k/ja/user/app_settings/form/form_parts/field_calculation.html"; } function handleLookupFieldError(error) { if (!(error.message.includes("lookup") || error.message.includes("LOOKUP"))) { return null; } return formatErrorMessage( "ルックアップフィールドの設定エラー", "ルックアップフィールドの設定に問題があります。", [ "参照先アプリが存在するか確認してください。\n - アプリIDまたはコードが正しいか\n - アプリが運用環境にデプロイされているか", "フィールドマッピングが正しいか確認してください。\n - 参照先のフィールドが存在するか\n - マッピング先のフィールドが既に作成されているか\n - フィールドの型が互換性を持つか", "get_field_type_documentation ツールでルックアップフィールドの仕様を確認してください:\n 例: get_field_type_documentation({ field_type: \"LOOKUP\" })" ] ); } function handleLayoutError(error) { if (!(error.message.includes("layout") || error.message.includes("レイアウト"))) { return null; } return formatErrorMessage( "レイアウト設定エラー", "フォームレイアウトの設定に問題があります。", [ "レイアウト要素の型が正しいか確認してください。\n - ROW, GROUP, SUBTABLE, FIELD, LABEL, SPACER, HR, REFERENCE_TABLE のいずれか", "参照しているフィールドが存在するか確認してください。\n - フィールドコードのスペルミスがないか\n - 参照先のフィールドが既に作成されているか", "レイアウト構造が正しいか確認してください。\n - ROW内にはフィールド要素のみ配置可能\n - GROUP内にはROW, GROUP, SUBTABLEのみ配置可能\n - トップレベルにはROW, GROUP, SUBTABLEのみ配置可能", "get_field_type_documentation ツールでレイアウトの仕様を確認してください:\n 例: get_field_type_documentation({ field_type: \"LAYOUT\" })" ] ); } function handleQueryError(error) { if (!(error.message.includes("query") || error.message.includes("クエリ") || error.message.includes("Invalid operator") || error.message.includes("Invalid field") || error.message.includes("limit") || error.message.includes("クエリー構文"))) { return null; } let suggestions = [ "基本的なクエリ構文: フィールドコード 演算子 値", "文字列は必ずダブルクオートで囲む: Customer = \"サイボウズ株式会社\"", "複数値はin演算子で括弧内に記述: Status in (\"対応中\",\"未対応\")", "テーブル内フィールドは=でなくinを使用: ResponseDate in (\"2022-09-29T05:00:00Z\")", "オプションの順序を守る: order by → limit → offset", "limit句のみの指定は使用できません: 検索条件またはorder by句と組み合わせてください" ]; if (error.message.includes("Invalid operator")) { suggestions = [ "フィールドタイプに対応した演算子を使用してください:", "• 文字列(1行): = != in not_in like not_like", "• 文字列(複数行): like not_like is is_not", "• 数値: = != > < >= <= in not_in", "• ドロップダウン/チェックボックス: in not_in", "• 日付: = != > < >= <= (TODAY()などの関数も使用可能)", "• ユーザー選択: in not_in (LOGINUSER()関数も使用可能)", ...suggestions ]; } if (error.message.includes("Invalid field") || error.message.includes("Field not found")) { suggestions = [ "フィールドコードのスペルを確認してください", "フィールドが存在するか確認してください (get_fieldsツールで確認可能)", "関連レコードの場合: 関連レコードフィールド.参照先フィールド", "テーブル内フィールドの場合: フィールドコードのみ (テーブル名は不要)", ...suggestions ]; } return formatErrorMessage( "クエリ構文エラー", "検索クエリの構文に問題があります。", suggestions.concat([ "詳細なクエリ構文はget_query_language_documentationツールで確認できます", "よく使われるクエリ例:\n" + " • Status = \"完了\" (完全一致)\n" + " • Customer like \"株式会社\" (部分一致)\n" + " • LimitDay >= \"2022-09-29\" and LimitDay <= \"2022-10-29\" (範囲指定)\n" + " • Status in (\"対応中\",\"未対応\") order by 更新日時 desc (ソート付き)\n" + " • $id > 0 limit 10 (条件付きlimit)\n" + " • order by $id desc limit 10 (ソート付きlimit)" ]) ); } function handleKintoneApiError(error) { if (!(error instanceof KintoneApiError)) { return null; } let errorMessage = error.message; let helpText = ""; // クエリエラーのチェックを最初に行う const queryErrorHelp = handleQueryError(error); if (queryErrorHelp) { return { errorMessage, helpText: queryErrorHelp }; } if (error.errors) { errorMessage += "\n\nエラーの詳細情報:"; for (const [key, value] of Object.entries(error.errors)) { errorMessage += `\n- ${key}: ${JSON.stringify(value)}`; } } if (error.code === "GAIA_AP01" || error.message.includes("存在しません")) { helpText = formatErrorMessage( "アプリが見つからないエラー", "指定されたアプリが見つかりません。", [ "アプリがまだプレビュー環境にのみ存在し、運用環境にデプロイされていない可能性があります。", "デプロイ処理が完了していない可能性があります。", "新規作成したアプリの場合は、get_preview_app_settings ツールを使用してプレビュー環境の情報を取得してください。", "アプリをデプロイするには、deploy_app ツールを使用してください。", "デプロイ状態を確認するには、get_deploy_status ツールを使用してください。", "デプロイが完了したら、運用環境のAPIを使用できます。" ] ); helpText += "\n\n【kintoneアプリのライフサイクル】\n1. create_app: アプリを作成(プレビュー環境に作成される)\n2. add_fields: フィールドを追加(プレビュー環境に追加される)\n3. deploy_app: アプリをデプロイ(運用環境へ反映)\n4. get_deploy_status: デプロイ状態を確認(完了するまで待機)\n5. get_app_settings: 運用環境の設定を取得(デプロイ完了後)"; } else if (error.code === "CB_VA01" && error.errors) { const missingFields = []; for (const key in error.errors) { if (key.includes('.value')) { const fieldMatch = key.match(/record\.([^.]+)\.values\.value/); if (fieldMatch) { missingFields.push(fieldMatch[1]); } } } if (missingFields.length > 0) { helpText = formatErrorMessage( "必須フィールド不足エラー", `必須フィールドが不足しています:${missingFields.join(', ')}`, [ "必須フィールドの値が指定されているか確認してください。", "フィールドの形式が正しいか確認してください。", "フィールドの型が正しいか確認してください。" ] ); helpText += "\n\n【使用例】\n```json\n{\n \"app_id\": 123,\n \"fields\": {\n \"project_name\": { \"value\": \"プロジェクト名\" },\n \"project_manager\": { \"value\": \"山田太郎\" }\n }\n}\n```"; } } else if (error.message.includes("value") || error.message.includes("record") || error.message.includes("fields")) { helpText = formatErrorMessage( "フィールド形式エラー", "レコードのフィールド値の形式が正しくありません。", [ "各フィールドは { \"value\": ... } の形式で指定する必要があります。", "フィールドタイプに応じて適切な値の形式が異なります:", "- 文字儗1行: { \"value\": \"テキスト\" }", "- 文字列複数行: { \"value\": \"テキスト\\nテキスト2\" }", "- 数値: { \"value\": \"20\" } (文字列として指定)", "- 日時: { \"value\": \"2014-02-16T08:57:00Z\" }", "- チェックボックス: { \"value\": [\"選択肢1\", \"選択肢2\"] } (配列)", "- ユーザー選択: { \"value\": [{ \"code\": \"ユーザーコード\" }] } (オブジェクトの配列)", "- ドロップダウン: { \"value\": \"選択肢1\" }", "- リンク: { \"value\": \"https://www.cybozu.com\" }", "- テーブル: { \"value\": [{ \"value\": { \"テーブル文字列\": { \"value\": \"テスト\" } } }] } (入れ子構造)" ] ); helpText += "\n\n【レコード作成の使用例】\n```json\n{\n \"app_id\": 1,\n \"fields\": {\n \"文字儗1行\": { \"value\": \"テスト\" },\n \"文字列複数行\": { \"value\": \"テスト\\nテスト2\" },\n \"数値\": { \"value\": \"20\" },\n \"日時\": { \"value\": \"2014-02-16T08:57:00Z\" },\n \"チェックボックス\": { \"value\": [\"sample1\", \"sample2\"] },\n \"ユーザー選択\": { \"value\": [{ \"code\": \"sato\" }] },\n \"ドロップダウン\": { \"value\": \"sample1\" },\n \"リンク_ウェブ\": { \"value\": \"https://www.cybozu.com\" },\n \"テーブル\": { \"value\": [{ \"value\": { \"テーブル文字列\": { \"value\": \"テスト\" } } }] }\n }\n}\n```"; helpText += "\n\n【レコード更新の使用例】\n```json\n{\n \"app_id\": 1,\n \"record_id\": 1001,\n \"fields\": {\n \"文字共1行_0\": { \"value\": \"character string is changed\" },\n \"テーブル_0\": { \"value\": [{\n \"id\": 1,\n \"value\": {\n \"文字共1行_1\": { \"value\": \"character string is changed\" }\n }\n }]}\n }\n}\n```"; } return { errorMessage, helpText }; } export function handleToolError(error) { let errorCode = ErrorCode.InternalError; let errorMessage = error.message; let helpText = ""; helpText = handleChoiceFieldError(error) || handleCalcFieldError(error) || handleLookupFieldError(error) || handleLayoutError(error) || handleQueryError(error) || ""; if (error instanceof McpError) { return { content: [ { type: 'text', text: error.message } ], isError: true }; } if (error instanceof KintoneApiError) { errorCode = error.status >= 500 ? ErrorCode.InternalError : ErrorCode.InvalidRequest; const kintoneErrorResult = handleKintoneApiError(error); if (kintoneErrorResult) { errorMessage = kintoneErrorResult.errorMessage; if (kintoneErrorResult.helpText) { helpText = kintoneErrorResult.helpText; } } } if (helpText) { errorMessage += "\n\n" + helpText; } return { content: [ { type: 'text', text: errorMessage } ], isError: true }; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/r3-yamauchi/kintone-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server