[{"data":1,"prerenderedAt":11020},["ShallowReactive",2],{"content-/journal-api-mcp-tutorial":3,"all-pages-for-dir":11018,"og-image-/journal-api-mcp-tutorial":11019},{"id":4,"title":5,"body":6,"category":11001,"concepts":11001,"description":11002,"extension":11003,"meta":11004,"navigation":281,"ogImage":11001,"path":11005,"project_name":11001,"published":11006,"publishedAt":11007,"seo":11008,"stem":11009,"tags":11010,"todo":11001,"unpublished":11006,"updatedAt":11001,"__hash__":11017},"pages/2026-07/2026-07-14/journal-api-mcp-tutorial.md","自作APIをMCP化するハンズオン。ミニ仕訳帳APIを作り、生のまま使い、包んでから使って比べる",{"type":7,"value":8,"toc":10985},"minimark",[9,13,22,25,28,31,73,76,159,166,169,181,186,189,237,244,256,262,2899,2902,3014,3024,5665,5671,5679,5682,5704,5707,5713,5722,5732,5735,5741,5748,5754,5757,5763,5770,5774,5777,5801,5804,5815,5874,5889,5892,5907,5912,5915,5921,5924,5930,5937,5944,5948,5951,5957,5960,5974,5980,5985,5989,6002,6008,6014,6624,6627,6630,6637,6643,6884,6901,7472,7475,7481,8121,8124,8148,8152,8163,8828,8831,8859,8868,9976,9985,9991,10234,10238,10249,10394,10407,10410,10475,10478,10484,10487,10492,10649,10656,10659,10665,10671,10678,10733,10739,10746,10749,10753,10756,10850,10857,10860,10863,10868,10871,10877,10881,10884,10893,10896,10899,10902,10928,10931,10981],[10,11,5],"h1",{"id":12},"自作apiをmcp化するハンズオンミニ仕訳帳apiを作り生のまま使い包んでから使って比べる",[14,15,16,21],"p",{},[17,18,20],"a",{"href":19},"/freee-mcp-anatomy","前の記事","で、freee公式MCPサーバー「freee-mcp」のソースコードを読んだ。277本のエンドポイントに対してツールは15個、うちAPI呼び出しを担うのは5個の汎用ツールで、残りは認証や事業所切り替えなどの補助だった。OpenAPIスキーマは実行時の門番になり、仕様の知識はAgent Skillsに分離され、コードの大部分は認証とエラー整形と記録の配管だった。読み終えて、方向はわかった。ただ、他人のコードを読んだだけの理解は、自分で書いた理解より一段浅い。",[14,23,24],{},"そこで今回は、同じ構造のミニチュアを自分で作る。仕訳帳のAPIを1本自作し、まず生のままAIエージェントに使わせて、何が起きるかを見る。次にそのAPIをMCPサーバーで包み、同じ依頼をもう一度投げて、何が変わったかを見る。読むだけでは掴みきれなかった「包む」の意味を、手元のターミナルで確かめるハンズオンだ。",[14,26,27],{},"登場するコードは全文この記事に載せた。コマンドと出力はすべて私のPC（Windows 11）での実測値で、「たぶんこうなるはず」の出力は1行もない。所要時間は、コードを写経して動かすだけなら1時間程度、Before/Afterの実験まで再現するなら2時間程度だ。",[14,29,30],{},"前提は次のとおり。",[32,33,34,47,59,70],"ul",{},[35,36,37,38,46],"li",{},"Python 3.11 と ",[17,39,45],{"href":40,"target":41,"rel":42},"https://docs.astral.sh/uv/","_blank",[43,44],"noopener","noreferrer","→ uv（Pythonのパッケージ管理ツール）","。pipでも代替できる",[35,48,49,50,54,55,58],{},"コマンドはGit Bash（またはWSL等のPOSIXシェル）想定で書いてある。PowerShellで写経する場合は ",[51,52,53],"code",{},"export"," を ",[51,56,57],{},"$env:"," に読み替える",[35,60,61,62,65,66,69],{},"MCPクライアント。この記事ではClaude Codeを使う（Claude DesktopなどMCP対応クライアントなら流れは同じ）。インストールとログインは済ませておく。実測に使ったのは 2.1.208 で、実験のモデルを合わせたい場合は ",[51,63,64],{},"claude -p"," に ",[51,67,68],{},"--model"," を付ける",[35,71,72],{},"ライブラリのバージョン: fastapi 0.139.0 / uvicorn 0.51.0 / mcp 1.28.1 / httpx 0.28.1 / pytest 9.1.1",[14,74,75],{},"プロジェクトの準備はこれだけでよい。",[77,78,83],"pre",{"className":79,"code":80,"language":81,"meta":82,"style":82},"language-bash shiki shiki-themes vitesse-light vitesse-light","uv init journal-mcp-tutorial --python 3.11\ncd journal-mcp-tutorial\nuv add fastapi uvicorn httpx \"mcp[cli]\"\nuv add --dev pytest\n","bash","",[51,84,85,109,119,146],{"__ignoreMap":82},[86,87,90,94,98,101,105],"span",{"class":88,"line":89},"line",1,[86,91,93],{"class":92},"senZ8","uv",[86,95,97],{"class":96},"sdGka"," init",[86,99,100],{"class":96}," journal-mcp-tutorial",[86,102,104],{"class":103},"snbK4"," --python",[86,106,108],{"class":107},"sM54T"," 3.11\n",[86,110,112,116],{"class":88,"line":111},2,[86,113,115],{"class":114},"sz8Xr","cd",[86,117,118],{"class":96}," journal-mcp-tutorial\n",[86,120,122,124,127,130,133,136,140,143],{"class":88,"line":121},3,[86,123,93],{"class":92},[86,125,126],{"class":96}," add",[86,128,129],{"class":96}," fastapi",[86,131,132],{"class":96}," uvicorn",[86,134,135],{"class":96}," httpx",[86,137,139],{"class":138},"sMJiu"," \"",[86,141,142],{"class":96},"mcp[cli]",[86,144,145],{"class":138},"\"\n",[86,147,149,151,153,156],{"class":88,"line":148},4,[86,150,93],{"class":92},[86,152,126],{"class":96},[86,154,155],{"class":103}," --dev",[86,157,158],{"class":96}," pytest\n",[14,160,161,162,165],{},"将来のバージョン差で挙動が変わったら、",[51,163,164],{},"uv add \"fastapi==0.139.0\" \"uvicorn==0.51.0\" \"mcp[cli]==1.28.1\" \"httpx==0.28.1\""," のように前提のバージョンへ固定すればよい。",[14,167,168],{},"この記事の三者関係を先に図にしておく。クラウドは出てこない。全部、読者のPCの中で完結する。",[170,171,172,177],"figure",{},[173,174],"img",{"src":175,"alt":176},"/images/journal-api-mcp-tutorial/figure-01.svg","登場する三者は全部、読者のPCの中で動く。Claude CodeとMCPサーバー（server.py）はstdioで、MCPサーバーとミニ仕訳帳API（api.py）はHTTPでつながり、APIキーはMCPサーバーの環境変数にだけ置かれる",[178,179,180],"figcaption",{},"図1: 登場する三者は全部、読者のPCの中で動く。APIキーの置き場所がこの記事の伏線になる",[182,183,185],"h2",{"id":184},"step-1-ミニ仕訳帳apiを作る","Step 1: ミニ仕訳帳APIを作る",[14,187,188],{},"題材は会計ドメインで最小のもの、仕訳帳にした。扱うのは1行仕訳（借方1科目、貸方1科目）だけで、複合仕訳はスコープ外。エンドポイントは3本に絞る。",[190,191,192,205],"table",{},[193,194,195],"thead",{},[196,197,198,202],"tr",{},[199,200,201],"th",{},"メソッドとパス",[199,203,204],{},"内容",[206,207,208,221,229],"tbody",{},[196,209,210,214],{},[211,212,213],"td",{},"GET /journal-entries",[211,215,216,217,220],{},"仕訳一覧（",[51,218,219],{},"month=YYYY-MM"," で絞り込み）",[196,222,223,226],{},[211,224,225],{},"POST /journal-entries",[211,227,228],{},"仕訳の登録",[196,230,231,234],{},[211,232,233],{},"GET /trial-balance",[211,235,236],{},"勘定科目別の残高試算表",[14,238,239,240,243],{},"勘定科目は7科目のホワイトリスト制にする。現金、普通預金（以上、資産）、買掛金（負債）、資本金（純資産）、売上高（収益）、仕入高、消耗品費（以上、費用）。リストにない科目は422エラーで弾く。認証は ",[51,241,242],{},"X-API-Key"," ヘッダーで、キーを持たない呼び出しは401で拒否する。この「キーを持つ者しか叩けない」という性質が、後半の実験で効いてくる。",[14,245,246,247,251,252,255],{},"残高試算表には、会計の人向けの設計を1つ入れた。科目ごとに**残高の側（balance_side）",[248,249,250],"strong",{},"と","正常残高側（normal_side）**を別の属性として返す。前者は「借方合計 − 貸方合計」の符号で決まる実際の残高の側、後者は科目の区分から決まる性質（資産と費用は借方、負債と純資産と収益は貸方）だ。2つは独立に決まるから、食い違ったとき（現金なのに貸方残、つまり残高がマイナス）に ",[51,253,254],{},"is_abnormal: true"," を機械的に立てられる。",[14,257,258,261],{},[51,259,260],{},"api.py"," の全文がこれだ。",[77,263,267],{"className":264,"code":265,"language":266,"meta":82,"style":82},"language-python shiki shiki-themes vitesse-light vitesse-light","\"\"\"ミニ仕訳帳API — FastAPI製の教材用API。\n\nfreee Public APIの1/100模型。1行仕訳（借方1・貸方1）だけを扱う。複合仕訳はスコープ外。\n起動: uv run uvicorn api:app\n\"\"\"\n\nimport datetime as dt\n\nfrom fastapi import Depends, FastAPI, Header, HTTPException, Query\nfrom pydantic import BaseModel, Field, field_validator\n\n# 教材用の固定キー。実務では環境変数や秘密管理サービスに置く\nAPI_KEY = \"sk-journal-tutorial-2026\"\n\n# 勘定科目ホワイトリスト（科目 → 区分）\nACCOUNTS: dict[str, str] = {\n    \"現金\": \"資産\",\n    \"普通預金\": \"資産\",\n    \"買掛金\": \"負債\",\n    \"資本金\": \"純資産\",\n    \"売上高\": \"収益\",\n    \"仕入高\": \"費用\",\n    \"消耗品費\": \"費用\",\n}\n\n# 区分 → 残高の正常な側\nNORMAL_SIDE: dict[str, str] = {\n    \"資産\": \"借方\",\n    \"費用\": \"借方\",\n    \"負債\": \"貸方\",\n    \"純資産\": \"貸方\",\n    \"収益\": \"貸方\",\n}\n\n\nclass JournalEntryIn(BaseModel):\n    \"\"\"仕訳の入力。1行仕訳（借方1・貸方1）のみ。\"\"\"\n\n    date: dt.date\n    debit_account: str\n    credit_account: str\n    amount: int = Field(gt=0, description=\"金額（正の整数・円）\")\n    description: str = \"\"\n\n    @field_validator(\"debit_account\", \"credit_account\")\n    @classmethod\n    def account_must_be_known(cls, v: str) -> str:\n        if v not in ACCOUNTS:\n            allowed = \"、\".join(ACCOUNTS)\n            raise ValueError(f\"勘定科目「{v}」は使えません。使える科目: {allowed}\")\n        return v\n\n\nclass JournalEntry(JournalEntryIn):\n    id: int\n\n\ndef seed_entries() -> list[JournalEntry]:\n    rows = [\n        (\"2026-07-01\", \"現金\", \"資本金\", 100_000, \"開業出資\"),\n        (\"2026-07-05\", \"仕入高\", \"現金\", 30_000, \"商品仕入\"),\n        (\"2026-07-10\", \"現金\", \"売上高\", 80_000, \"売上計上\"),\n        (\"2026-07-12\", \"消耗品費\", \"現金\", 5_000, \"事務用品\"),\n        (\"2026-07-20\", \"普通預金\", \"現金\", 50_000, \"預け入れ\"),\n    ]\n    return [\n        JournalEntry(\n            id=i, date=d, debit_account=dr, credit_account=cr, amount=a, description=desc\n        )\n        for i, (d, dr, cr, a, desc) in enumerate(rows, start=1)\n    ]\n\n\nENTRIES: list[JournalEntry] = seed_entries()\n\n\ndef build_trial_balance(entries: list[JournalEntry]) -> dict:\n    \"\"\"残高試算表を組み立てる純粋関数。\n\n    balance_side は「借方合計 − 貸方合計」の符号で決める（正→借方、負→貸方、0→残高なし）。\n    normal_side は区分から決まる正常残高側で、両者が食い違えば is_abnormal を立てる。\n    \"\"\"\n    accounts = []\n    for account, category in ACCOUNTS.items():\n        debit_total = sum(e.amount for e in entries if e.debit_account == account)\n        credit_total = sum(e.amount for e in entries if e.credit_account == account)\n        if debit_total == 0 and credit_total == 0:\n            continue  # 動きのない科目は載せない\n        net = debit_total - credit_total\n        balance_side = \"借方\" if net > 0 else \"貸方\" if net \u003C 0 else None\n        normal_side = NORMAL_SIDE[category]\n        accounts.append(\n            {\n                \"account\": account,\n                \"category\": category,\n                \"debit_total\": debit_total,\n                \"credit_total\": credit_total,\n                \"balance_side\": balance_side,\n                \"balance\": abs(net),\n                \"normal_side\": normal_side,\n                \"is_abnormal\": balance_side is not None and balance_side != normal_side,\n            }\n        )\n    debit_sum = sum(a[\"debit_total\"] for a in accounts)\n    credit_sum = sum(a[\"credit_total\"] for a in accounts)\n    return {\n        \"accounts\": accounts,\n        \"total\": {\n            \"debit_total\": debit_sum,\n            \"credit_total\": credit_sum,\n            \"in_balance\": debit_sum == credit_sum,\n        },\n    }\n\n\ndef require_api_key(x_api_key: str | None = Header(default=None)) -> None:\n    if x_api_key != API_KEY:\n        raise HTTPException(\n            status_code=401, detail=\"APIキーが不正です。X-API-Key ヘッダーを確認してください。\"\n        )\n\n\napp = FastAPI(\n    title=\"ミニ仕訳帳API\",\n    description=\"教材用のミニ仕訳帳。1行仕訳の登録・一覧・残高試算表だけができる。\",\n    version=\"0.1.0\",\n    dependencies=[Depends(require_api_key)],\n)\n\n\n@app.get(\"/journal-entries\")\ndef list_journal_entries(\n    month: str | None = Query(default=None, pattern=r\"^\\d{4}-\\d{2}$\"),\n) -> list[JournalEntry]:\n    \"\"\"仕訳一覧。month=YYYY-MM で対象月に絞り込める。\"\"\"\n    if month is None:\n        return ENTRIES\n    return [e for e in ENTRIES if e.date.strftime(\"%Y-%m\") == month]\n\n\n@app.post(\"/journal-entries\", status_code=201)\ndef register_journal_entry(entry: JournalEntryIn) -> JournalEntry:\n    \"\"\"仕訳を1件登録する。\"\"\"\n    next_id = max((e.id for e in ENTRIES), default=0) + 1\n    stored = JournalEntry(id=next_id, **entry.model_dump())\n    ENTRIES.append(stored)\n    return stored\n\n\n@app.get(\"/trial-balance\")\ndef get_trial_balance() -> dict:\n    \"\"\"勘定科目別の残高試算表。\"\"\"\n    return build_trial_balance(ENTRIES)\n","python",[51,268,269,277,283,288,293,299,304,321,326,362,385,390,397,413,418,424,455,479,499,520,541,562,583,603,609,614,620,644,664,683,703,722,741,746,751,756,776,787,792,809,820,830,874,889,894,923,931,965,985,1011,1051,1060,1065,1070,1085,1096,1101,1106,1131,1142,1188,1232,1276,1320,1364,1370,1378,1387,1446,1452,1512,1517,1522,1527,1550,1555,1560,1590,1598,1603,1609,1615,1621,1632,1659,1709,1750,1775,1784,1800,1848,1867,1880,1886,1903,1919,1936,1953,1970,1992,2009,2044,2050,2055,2092,2126,2133,2150,2164,2181,2197,2218,2224,2230,2235,2240,2285,2301,2311,2336,2341,2346,2351,2363,2380,2396,2413,2433,2438,2443,2448,2473,2483,2544,2559,2569,2583,2591,2644,2649,2654,2686,2711,2721,2768,2803,2820,2828,2833,2838,2860,2876,2886],{"__ignoreMap":82},[86,270,271,274],{"class":88,"line":89},[86,272,273],{"class":138},"\"\"\"",[86,275,276],{"class":96},"ミニ仕訳帳API — FastAPI製の教材用API。\n",[86,278,279],{"class":88,"line":111},[86,280,282],{"emptyLinePlaceholder":281},true,"\n",[86,284,285],{"class":88,"line":121},[86,286,287],{"class":96},"freee Public APIの1/100模型。1行仕訳（借方1・貸方1）だけを扱う。複合仕訳はスコープ外。\n",[86,289,290],{"class":88,"line":148},[86,291,292],{"class":96},"起動: uv run uvicorn api:app\n",[86,294,296],{"class":88,"line":295},5,[86,297,298],{"class":138},"\"\"\"\n",[86,300,302],{"class":88,"line":301},6,[86,303,282],{"emptyLinePlaceholder":281},[86,305,307,311,315,318],{"class":88,"line":306},7,[86,308,310],{"class":309},"sHkkW","import",[86,312,314],{"class":313},"sG7-3"," datetime ",[86,316,317],{"class":309},"as",[86,319,320],{"class":313}," dt\n",[86,322,324],{"class":88,"line":323},8,[86,325,282],{"emptyLinePlaceholder":281},[86,327,329,332,335,337,340,344,347,349,352,354,357,359],{"class":88,"line":328},9,[86,330,331],{"class":309},"from",[86,333,334],{"class":313}," fastapi ",[86,336,310],{"class":309},[86,338,339],{"class":313}," Depends",[86,341,343],{"class":342},"shFtX",",",[86,345,346],{"class":313}," FastAPI",[86,348,343],{"class":342},[86,350,351],{"class":313}," Header",[86,353,343],{"class":342},[86,355,356],{"class":313}," HTTPException",[86,358,343],{"class":342},[86,360,361],{"class":313}," Query\n",[86,363,365,367,370,372,375,377,380,382],{"class":88,"line":364},10,[86,366,331],{"class":309},[86,368,369],{"class":313}," pydantic ",[86,371,310],{"class":309},[86,373,374],{"class":313}," BaseModel",[86,376,343],{"class":342},[86,378,379],{"class":313}," Field",[86,381,343],{"class":342},[86,383,384],{"class":313}," field_validator\n",[86,386,388],{"class":88,"line":387},11,[86,389,282],{"emptyLinePlaceholder":281},[86,391,393],{"class":88,"line":392},12,[86,394,396],{"class":395},"sxvE3","# 教材用の固定キー。実務では環境変数や秘密管理サービスに置く\n",[86,398,400,403,406,408,411],{"class":88,"line":399},13,[86,401,402],{"class":103},"API_KEY",[86,404,405],{"class":342}," =",[86,407,139],{"class":138},[86,409,410],{"class":96},"sk-journal-tutorial-2026",[86,412,145],{"class":138},[86,414,416],{"class":88,"line":415},14,[86,417,282],{"emptyLinePlaceholder":281},[86,419,421],{"class":88,"line":420},15,[86,422,423],{"class":395},"# 勘定科目ホワイトリスト（科目 → 区分）\n",[86,425,427,430,433,436,439,442,444,447,450,452],{"class":88,"line":426},16,[86,428,429],{"class":103},"ACCOUNTS",[86,431,432],{"class":342},":",[86,434,435],{"class":313}," dict",[86,437,438],{"class":342},"[",[86,440,441],{"class":114},"str",[86,443,343],{"class":342},[86,445,446],{"class":114}," str",[86,448,449],{"class":342},"]",[86,451,405],{"class":342},[86,453,454],{"class":342}," {\n",[86,456,458,461,464,467,469,471,474,476],{"class":88,"line":457},17,[86,459,460],{"class":138},"    \"",[86,462,463],{"class":96},"現金",[86,465,466],{"class":138},"\"",[86,468,432],{"class":342},[86,470,139],{"class":138},[86,472,473],{"class":96},"資産",[86,475,466],{"class":138},[86,477,478],{"class":342},",\n",[86,480,482,484,487,489,491,493,495,497],{"class":88,"line":481},18,[86,483,460],{"class":138},[86,485,486],{"class":96},"普通預金",[86,488,466],{"class":138},[86,490,432],{"class":342},[86,492,139],{"class":138},[86,494,473],{"class":96},[86,496,466],{"class":138},[86,498,478],{"class":342},[86,500,502,504,507,509,511,513,516,518],{"class":88,"line":501},19,[86,503,460],{"class":138},[86,505,506],{"class":96},"買掛金",[86,508,466],{"class":138},[86,510,432],{"class":342},[86,512,139],{"class":138},[86,514,515],{"class":96},"負債",[86,517,466],{"class":138},[86,519,478],{"class":342},[86,521,523,525,528,530,532,534,537,539],{"class":88,"line":522},20,[86,524,460],{"class":138},[86,526,527],{"class":96},"資本金",[86,529,466],{"class":138},[86,531,432],{"class":342},[86,533,139],{"class":138},[86,535,536],{"class":96},"純資産",[86,538,466],{"class":138},[86,540,478],{"class":342},[86,542,544,546,549,551,553,555,558,560],{"class":88,"line":543},21,[86,545,460],{"class":138},[86,547,548],{"class":96},"売上高",[86,550,466],{"class":138},[86,552,432],{"class":342},[86,554,139],{"class":138},[86,556,557],{"class":96},"収益",[86,559,466],{"class":138},[86,561,478],{"class":342},[86,563,565,567,570,572,574,576,579,581],{"class":88,"line":564},22,[86,566,460],{"class":138},[86,568,569],{"class":96},"仕入高",[86,571,466],{"class":138},[86,573,432],{"class":342},[86,575,139],{"class":138},[86,577,578],{"class":96},"費用",[86,580,466],{"class":138},[86,582,478],{"class":342},[86,584,586,588,591,593,595,597,599,601],{"class":88,"line":585},23,[86,587,460],{"class":138},[86,589,590],{"class":96},"消耗品費",[86,592,466],{"class":138},[86,594,432],{"class":342},[86,596,139],{"class":138},[86,598,578],{"class":96},[86,600,466],{"class":138},[86,602,478],{"class":342},[86,604,606],{"class":88,"line":605},24,[86,607,608],{"class":342},"}\n",[86,610,612],{"class":88,"line":611},25,[86,613,282],{"emptyLinePlaceholder":281},[86,615,617],{"class":88,"line":616},26,[86,618,619],{"class":395},"# 区分 → 残高の正常な側\n",[86,621,623,626,628,630,632,634,636,638,640,642],{"class":88,"line":622},27,[86,624,625],{"class":103},"NORMAL_SIDE",[86,627,432],{"class":342},[86,629,435],{"class":313},[86,631,438],{"class":342},[86,633,441],{"class":114},[86,635,343],{"class":342},[86,637,446],{"class":114},[86,639,449],{"class":342},[86,641,405],{"class":342},[86,643,454],{"class":342},[86,645,647,649,651,653,655,657,660,662],{"class":88,"line":646},28,[86,648,460],{"class":138},[86,650,473],{"class":96},[86,652,466],{"class":138},[86,654,432],{"class":342},[86,656,139],{"class":138},[86,658,659],{"class":96},"借方",[86,661,466],{"class":138},[86,663,478],{"class":342},[86,665,667,669,671,673,675,677,679,681],{"class":88,"line":666},29,[86,668,460],{"class":138},[86,670,578],{"class":96},[86,672,466],{"class":138},[86,674,432],{"class":342},[86,676,139],{"class":138},[86,678,659],{"class":96},[86,680,466],{"class":138},[86,682,478],{"class":342},[86,684,686,688,690,692,694,696,699,701],{"class":88,"line":685},30,[86,687,460],{"class":138},[86,689,515],{"class":96},[86,691,466],{"class":138},[86,693,432],{"class":342},[86,695,139],{"class":138},[86,697,698],{"class":96},"貸方",[86,700,466],{"class":138},[86,702,478],{"class":342},[86,704,706,708,710,712,714,716,718,720],{"class":88,"line":705},31,[86,707,460],{"class":138},[86,709,536],{"class":96},[86,711,466],{"class":138},[86,713,432],{"class":342},[86,715,139],{"class":138},[86,717,698],{"class":96},[86,719,466],{"class":138},[86,721,478],{"class":342},[86,723,725,727,729,731,733,735,737,739],{"class":88,"line":724},32,[86,726,460],{"class":138},[86,728,557],{"class":96},[86,730,466],{"class":138},[86,732,432],{"class":342},[86,734,139],{"class":138},[86,736,698],{"class":96},[86,738,466],{"class":138},[86,740,478],{"class":342},[86,742,744],{"class":88,"line":743},33,[86,745,608],{"class":342},[86,747,749],{"class":88,"line":748},34,[86,750,282],{"emptyLinePlaceholder":281},[86,752,754],{"class":88,"line":753},35,[86,755,282],{"emptyLinePlaceholder":281},[86,757,759,763,767,770,773],{"class":88,"line":758},36,[86,760,762],{"class":761},"stQ0i","class",[86,764,766],{"class":765},"sSkh3"," JournalEntryIn",[86,768,769],{"class":342},"(",[86,771,772],{"class":92},"BaseModel",[86,774,775],{"class":342},"):\n",[86,777,779,782,785],{"class":88,"line":778},37,[86,780,781],{"class":138},"    \"\"\"",[86,783,784],{"class":96},"仕訳の入力。1行仕訳（借方1・貸方1）のみ。",[86,786,298],{"class":138},[86,788,790],{"class":88,"line":789},38,[86,791,282],{"emptyLinePlaceholder":281},[86,793,795,798,800,803,806],{"class":88,"line":794},39,[86,796,797],{"class":313},"    date",[86,799,432],{"class":342},[86,801,802],{"class":313}," dt",[86,804,805],{"class":342},".",[86,807,808],{"class":313},"date\n",[86,810,812,815,817],{"class":88,"line":811},40,[86,813,814],{"class":313},"    debit_account",[86,816,432],{"class":342},[86,818,819],{"class":114}," str\n",[86,821,823,826,828],{"class":88,"line":822},41,[86,824,825],{"class":313},"    credit_account",[86,827,432],{"class":342},[86,829,819],{"class":114},[86,831,833,836,838,841,843,845,847,851,854,857,859,862,864,866,869,871],{"class":88,"line":832},42,[86,834,835],{"class":313},"    amount",[86,837,432],{"class":342},[86,839,840],{"class":114}," int",[86,842,405],{"class":342},[86,844,379],{"class":313},[86,846,769],{"class":342},[86,848,850],{"class":849},"s4oTP","gt",[86,852,853],{"class":342},"=",[86,855,856],{"class":107},"0",[86,858,343],{"class":342},[86,860,861],{"class":849}," description",[86,863,853],{"class":342},[86,865,466],{"class":138},[86,867,868],{"class":96},"金額（正の整数・円）",[86,870,466],{"class":138},[86,872,873],{"class":342},")\n",[86,875,877,880,882,884,886],{"class":88,"line":876},43,[86,878,879],{"class":313},"    description",[86,881,432],{"class":342},[86,883,446],{"class":114},[86,885,405],{"class":342},[86,887,888],{"class":138}," \"\"\n",[86,890,892],{"class":88,"line":891},44,[86,893,282],{"emptyLinePlaceholder":281},[86,895,897,900,903,905,907,910,912,914,916,919,921],{"class":88,"line":896},45,[86,898,899],{"class":342},"    @",[86,901,902],{"class":92},"field_validator",[86,904,769],{"class":342},[86,906,466],{"class":138},[86,908,909],{"class":96},"debit_account",[86,911,466],{"class":138},[86,913,343],{"class":342},[86,915,139],{"class":138},[86,917,918],{"class":96},"credit_account",[86,920,466],{"class":138},[86,922,873],{"class":342},[86,924,926,928],{"class":88,"line":925},46,[86,927,899],{"class":342},[86,929,930],{"class":114},"classmethod\n",[86,932,934,937,940,942,945,947,950,952,954,957,960,962],{"class":88,"line":933},47,[86,935,936],{"class":761},"    def",[86,938,939],{"class":92}," account_must_be_known",[86,941,769],{"class":342},[86,943,944],{"class":313},"cls",[86,946,343],{"class":342},[86,948,949],{"class":313}," v",[86,951,432],{"class":342},[86,953,446],{"class":114},[86,955,956],{"class":342},")",[86,958,959],{"class":342}," ->",[86,961,446],{"class":114},[86,963,964],{"class":342},":\n",[86,966,968,971,974,977,980,983],{"class":88,"line":967},48,[86,969,970],{"class":309},"        if",[86,972,973],{"class":313}," v ",[86,975,976],{"class":761},"not",[86,978,979],{"class":761}," in",[86,981,982],{"class":103}," ACCOUNTS",[86,984,964],{"class":342},[86,986,988,991,993,995,998,1000,1002,1005,1007,1009],{"class":88,"line":987},49,[86,989,990],{"class":313},"            allowed ",[86,992,853],{"class":342},[86,994,139],{"class":138},[86,996,997],{"class":96},"、",[86,999,466],{"class":138},[86,1001,805],{"class":342},[86,1003,1004],{"class":313},"join",[86,1006,769],{"class":342},[86,1008,429],{"class":103},[86,1010,873],{"class":342},[86,1012,1014,1017,1020,1022,1025,1028,1031,1034,1037,1040,1042,1045,1047,1049],{"class":88,"line":1013},50,[86,1015,1016],{"class":309},"            raise",[86,1018,1019],{"class":114}," ValueError",[86,1021,769],{"class":342},[86,1023,1024],{"class":761},"f",[86,1026,1027],{"class":96},"\"勘定科目「",[86,1029,1030],{"class":103},"{",[86,1032,1033],{"class":313},"v",[86,1035,1036],{"class":103},"}",[86,1038,1039],{"class":96},"」は使えません。使える科目: ",[86,1041,1030],{"class":103},[86,1043,1044],{"class":313},"allowed",[86,1046,1036],{"class":103},[86,1048,466],{"class":96},[86,1050,873],{"class":342},[86,1052,1054,1057],{"class":88,"line":1053},51,[86,1055,1056],{"class":309},"        return",[86,1058,1059],{"class":313}," v\n",[86,1061,1063],{"class":88,"line":1062},52,[86,1064,282],{"emptyLinePlaceholder":281},[86,1066,1068],{"class":88,"line":1067},53,[86,1069,282],{"emptyLinePlaceholder":281},[86,1071,1073,1075,1078,1080,1083],{"class":88,"line":1072},54,[86,1074,762],{"class":761},[86,1076,1077],{"class":765}," JournalEntry",[86,1079,769],{"class":342},[86,1081,1082],{"class":92},"JournalEntryIn",[86,1084,775],{"class":342},[86,1086,1088,1091,1093],{"class":88,"line":1087},55,[86,1089,1090],{"class":114},"    id",[86,1092,432],{"class":342},[86,1094,1095],{"class":114}," int\n",[86,1097,1099],{"class":88,"line":1098},56,[86,1100,282],{"emptyLinePlaceholder":281},[86,1102,1104],{"class":88,"line":1103},57,[86,1105,282],{"emptyLinePlaceholder":281},[86,1107,1109,1112,1115,1118,1120,1123,1125,1128],{"class":88,"line":1108},58,[86,1110,1111],{"class":761},"def",[86,1113,1114],{"class":92}," seed_entries",[86,1116,1117],{"class":342},"()",[86,1119,959],{"class":342},[86,1121,1122],{"class":313}," list",[86,1124,438],{"class":342},[86,1126,1127],{"class":313},"JournalEntry",[86,1129,1130],{"class":342},"]:\n",[86,1132,1134,1137,1139],{"class":88,"line":1133},59,[86,1135,1136],{"class":313},"    rows ",[86,1138,853],{"class":342},[86,1140,1141],{"class":342}," [\n",[86,1143,1145,1148,1150,1153,1155,1157,1159,1161,1163,1165,1167,1169,1171,1173,1176,1178,1180,1183,1185],{"class":88,"line":1144},60,[86,1146,1147],{"class":342},"        (",[86,1149,466],{"class":138},[86,1151,1152],{"class":96},"2026-07-01",[86,1154,466],{"class":138},[86,1156,343],{"class":342},[86,1158,139],{"class":138},[86,1160,463],{"class":96},[86,1162,466],{"class":138},[86,1164,343],{"class":342},[86,1166,139],{"class":138},[86,1168,527],{"class":96},[86,1170,466],{"class":138},[86,1172,343],{"class":342},[86,1174,1175],{"class":107}," 100_000",[86,1177,343],{"class":342},[86,1179,139],{"class":138},[86,1181,1182],{"class":96},"開業出資",[86,1184,466],{"class":138},[86,1186,1187],{"class":342},"),\n",[86,1189,1191,1193,1195,1198,1200,1202,1204,1206,1208,1210,1212,1214,1216,1218,1221,1223,1225,1228,1230],{"class":88,"line":1190},61,[86,1192,1147],{"class":342},[86,1194,466],{"class":138},[86,1196,1197],{"class":96},"2026-07-05",[86,1199,466],{"class":138},[86,1201,343],{"class":342},[86,1203,139],{"class":138},[86,1205,569],{"class":96},[86,1207,466],{"class":138},[86,1209,343],{"class":342},[86,1211,139],{"class":138},[86,1213,463],{"class":96},[86,1215,466],{"class":138},[86,1217,343],{"class":342},[86,1219,1220],{"class":107}," 30_000",[86,1222,343],{"class":342},[86,1224,139],{"class":138},[86,1226,1227],{"class":96},"商品仕入",[86,1229,466],{"class":138},[86,1231,1187],{"class":342},[86,1233,1235,1237,1239,1242,1244,1246,1248,1250,1252,1254,1256,1258,1260,1262,1265,1267,1269,1272,1274],{"class":88,"line":1234},62,[86,1236,1147],{"class":342},[86,1238,466],{"class":138},[86,1240,1241],{"class":96},"2026-07-10",[86,1243,466],{"class":138},[86,1245,343],{"class":342},[86,1247,139],{"class":138},[86,1249,463],{"class":96},[86,1251,466],{"class":138},[86,1253,343],{"class":342},[86,1255,139],{"class":138},[86,1257,548],{"class":96},[86,1259,466],{"class":138},[86,1261,343],{"class":342},[86,1263,1264],{"class":107}," 80_000",[86,1266,343],{"class":342},[86,1268,139],{"class":138},[86,1270,1271],{"class":96},"売上計上",[86,1273,466],{"class":138},[86,1275,1187],{"class":342},[86,1277,1279,1281,1283,1286,1288,1290,1292,1294,1296,1298,1300,1302,1304,1306,1309,1311,1313,1316,1318],{"class":88,"line":1278},63,[86,1280,1147],{"class":342},[86,1282,466],{"class":138},[86,1284,1285],{"class":96},"2026-07-12",[86,1287,466],{"class":138},[86,1289,343],{"class":342},[86,1291,139],{"class":138},[86,1293,590],{"class":96},[86,1295,466],{"class":138},[86,1297,343],{"class":342},[86,1299,139],{"class":138},[86,1301,463],{"class":96},[86,1303,466],{"class":138},[86,1305,343],{"class":342},[86,1307,1308],{"class":107}," 5_000",[86,1310,343],{"class":342},[86,1312,139],{"class":138},[86,1314,1315],{"class":96},"事務用品",[86,1317,466],{"class":138},[86,1319,1187],{"class":342},[86,1321,1323,1325,1327,1330,1332,1334,1336,1338,1340,1342,1344,1346,1348,1350,1353,1355,1357,1360,1362],{"class":88,"line":1322},64,[86,1324,1147],{"class":342},[86,1326,466],{"class":138},[86,1328,1329],{"class":96},"2026-07-20",[86,1331,466],{"class":138},[86,1333,343],{"class":342},[86,1335,139],{"class":138},[86,1337,486],{"class":96},[86,1339,466],{"class":138},[86,1341,343],{"class":342},[86,1343,139],{"class":138},[86,1345,463],{"class":96},[86,1347,466],{"class":138},[86,1349,343],{"class":342},[86,1351,1352],{"class":107}," 50_000",[86,1354,343],{"class":342},[86,1356,139],{"class":138},[86,1358,1359],{"class":96},"預け入れ",[86,1361,466],{"class":138},[86,1363,1187],{"class":342},[86,1365,1367],{"class":88,"line":1366},65,[86,1368,1369],{"class":342},"    ]\n",[86,1371,1373,1376],{"class":88,"line":1372},66,[86,1374,1375],{"class":309},"    return",[86,1377,1141],{"class":342},[86,1379,1381,1384],{"class":88,"line":1380},67,[86,1382,1383],{"class":313},"        JournalEntry",[86,1385,1386],{"class":342},"(\n",[86,1388,1390,1393,1395,1398,1400,1403,1405,1408,1410,1413,1415,1418,1420,1423,1425,1428,1430,1433,1435,1437,1439,1441,1443],{"class":88,"line":1389},68,[86,1391,1392],{"class":849},"            id",[86,1394,853],{"class":342},[86,1396,1397],{"class":313},"i",[86,1399,343],{"class":342},[86,1401,1402],{"class":849}," date",[86,1404,853],{"class":342},[86,1406,1407],{"class":313},"d",[86,1409,343],{"class":342},[86,1411,1412],{"class":849}," debit_account",[86,1414,853],{"class":342},[86,1416,1417],{"class":313},"dr",[86,1419,343],{"class":342},[86,1421,1422],{"class":849}," credit_account",[86,1424,853],{"class":342},[86,1426,1427],{"class":313},"cr",[86,1429,343],{"class":342},[86,1431,1432],{"class":849}," amount",[86,1434,853],{"class":342},[86,1436,17],{"class":313},[86,1438,343],{"class":342},[86,1440,861],{"class":849},[86,1442,853],{"class":342},[86,1444,1445],{"class":313},"desc\n",[86,1447,1449],{"class":88,"line":1448},69,[86,1450,1451],{"class":342},"        )\n",[86,1453,1455,1458,1461,1463,1466,1468,1470,1473,1475,1478,1480,1483,1485,1488,1490,1492,1495,1497,1500,1502,1505,1507,1510],{"class":88,"line":1454},70,[86,1456,1457],{"class":309},"        for",[86,1459,1460],{"class":313}," i",[86,1462,343],{"class":342},[86,1464,1465],{"class":342}," (",[86,1467,1407],{"class":313},[86,1469,343],{"class":342},[86,1471,1472],{"class":313}," dr",[86,1474,343],{"class":342},[86,1476,1477],{"class":313}," cr",[86,1479,343],{"class":342},[86,1481,1482],{"class":313}," a",[86,1484,343],{"class":342},[86,1486,1487],{"class":313}," desc",[86,1489,956],{"class":342},[86,1491,979],{"class":309},[86,1493,1494],{"class":114}," enumerate",[86,1496,769],{"class":342},[86,1498,1499],{"class":313},"rows",[86,1501,343],{"class":342},[86,1503,1504],{"class":849}," start",[86,1506,853],{"class":342},[86,1508,1509],{"class":107},"1",[86,1511,873],{"class":342},[86,1513,1515],{"class":88,"line":1514},71,[86,1516,1369],{"class":342},[86,1518,1520],{"class":88,"line":1519},72,[86,1521,282],{"emptyLinePlaceholder":281},[86,1523,1525],{"class":88,"line":1524},73,[86,1526,282],{"emptyLinePlaceholder":281},[86,1528,1530,1533,1535,1537,1539,1541,1543,1545,1547],{"class":88,"line":1529},74,[86,1531,1532],{"class":103},"ENTRIES",[86,1534,432],{"class":342},[86,1536,1122],{"class":313},[86,1538,438],{"class":342},[86,1540,1127],{"class":313},[86,1542,449],{"class":342},[86,1544,405],{"class":342},[86,1546,1114],{"class":313},[86,1548,1549],{"class":342},"()\n",[86,1551,1553],{"class":88,"line":1552},75,[86,1554,282],{"emptyLinePlaceholder":281},[86,1556,1558],{"class":88,"line":1557},76,[86,1559,282],{"emptyLinePlaceholder":281},[86,1561,1563,1565,1568,1570,1573,1575,1577,1579,1581,1584,1586,1588],{"class":88,"line":1562},77,[86,1564,1111],{"class":761},[86,1566,1567],{"class":92}," build_trial_balance",[86,1569,769],{"class":342},[86,1571,1572],{"class":313},"entries",[86,1574,432],{"class":342},[86,1576,1122],{"class":313},[86,1578,438],{"class":342},[86,1580,1127],{"class":313},[86,1582,1583],{"class":342},"])",[86,1585,959],{"class":342},[86,1587,435],{"class":114},[86,1589,964],{"class":342},[86,1591,1593,1595],{"class":88,"line":1592},78,[86,1594,781],{"class":138},[86,1596,1597],{"class":96},"残高試算表を組み立てる純粋関数。\n",[86,1599,1601],{"class":88,"line":1600},79,[86,1602,282],{"emptyLinePlaceholder":281},[86,1604,1606],{"class":88,"line":1605},80,[86,1607,1608],{"class":96},"    balance_side は「借方合計 − 貸方合計」の符号で決める（正→借方、負→貸方、0→残高なし）。\n",[86,1610,1612],{"class":88,"line":1611},81,[86,1613,1614],{"class":96},"    normal_side は区分から決まる正常残高側で、両者が食い違えば is_abnormal を立てる。\n",[86,1616,1618],{"class":88,"line":1617},82,[86,1619,1620],{"class":138},"    \"\"\"\n",[86,1622,1624,1627,1629],{"class":88,"line":1623},83,[86,1625,1626],{"class":313},"    accounts ",[86,1628,853],{"class":342},[86,1630,1631],{"class":342}," []\n",[86,1633,1635,1638,1641,1643,1646,1649,1651,1653,1656],{"class":88,"line":1634},84,[86,1636,1637],{"class":309},"    for",[86,1639,1640],{"class":313}," account",[86,1642,343],{"class":342},[86,1644,1645],{"class":313}," category ",[86,1647,1648],{"class":309},"in",[86,1650,982],{"class":103},[86,1652,805],{"class":342},[86,1654,1655],{"class":313},"items",[86,1657,1658],{"class":342},"():\n",[86,1660,1662,1665,1667,1670,1672,1675,1677,1680,1683,1686,1688,1691,1694,1697,1699,1702,1705,1707],{"class":88,"line":1661},85,[86,1663,1664],{"class":313},"        debit_total ",[86,1666,853],{"class":342},[86,1668,1669],{"class":114}," sum",[86,1671,769],{"class":342},[86,1673,1674],{"class":313},"e",[86,1676,805],{"class":342},[86,1678,1679],{"class":313},"amount ",[86,1681,1682],{"class":309},"for",[86,1684,1685],{"class":313}," e ",[86,1687,1648],{"class":309},[86,1689,1690],{"class":313}," entries ",[86,1692,1693],{"class":309},"if",[86,1695,1696],{"class":313}," e",[86,1698,805],{"class":342},[86,1700,1701],{"class":313},"debit_account ",[86,1703,1704],{"class":761},"==",[86,1706,1640],{"class":313},[86,1708,873],{"class":342},[86,1710,1712,1715,1717,1719,1721,1723,1725,1727,1729,1731,1733,1735,1737,1739,1741,1744,1746,1748],{"class":88,"line":1711},86,[86,1713,1714],{"class":313},"        credit_total ",[86,1716,853],{"class":342},[86,1718,1669],{"class":114},[86,1720,769],{"class":342},[86,1722,1674],{"class":313},[86,1724,805],{"class":342},[86,1726,1679],{"class":313},[86,1728,1682],{"class":309},[86,1730,1685],{"class":313},[86,1732,1648],{"class":309},[86,1734,1690],{"class":313},[86,1736,1693],{"class":309},[86,1738,1696],{"class":313},[86,1740,805],{"class":342},[86,1742,1743],{"class":313},"credit_account ",[86,1745,1704],{"class":761},[86,1747,1640],{"class":313},[86,1749,873],{"class":342},[86,1751,1753,1755,1758,1760,1763,1766,1769,1771,1773],{"class":88,"line":1752},87,[86,1754,970],{"class":309},[86,1756,1757],{"class":313}," debit_total ",[86,1759,1704],{"class":761},[86,1761,1762],{"class":107}," 0",[86,1764,1765],{"class":761}," and",[86,1767,1768],{"class":313}," credit_total ",[86,1770,1704],{"class":761},[86,1772,1762],{"class":107},[86,1774,964],{"class":342},[86,1776,1778,1781],{"class":88,"line":1777},88,[86,1779,1780],{"class":309},"            continue",[86,1782,1783],{"class":395},"  # 動きのない科目は載せない\n",[86,1785,1787,1790,1792,1794,1797],{"class":88,"line":1786},89,[86,1788,1789],{"class":313},"        net ",[86,1791,853],{"class":342},[86,1793,1757],{"class":313},[86,1795,1796],{"class":761},"-",[86,1798,1799],{"class":313}," credit_total\n",[86,1801,1803,1806,1808,1810,1812,1814,1817,1820,1823,1825,1828,1830,1832,1834,1836,1838,1841,1843,1845],{"class":88,"line":1802},90,[86,1804,1805],{"class":313},"        balance_side ",[86,1807,853],{"class":342},[86,1809,139],{"class":138},[86,1811,659],{"class":96},[86,1813,466],{"class":138},[86,1815,1816],{"class":309}," if",[86,1818,1819],{"class":313}," net ",[86,1821,1822],{"class":761},">",[86,1824,1762],{"class":107},[86,1826,1827],{"class":309}," else",[86,1829,139],{"class":138},[86,1831,698],{"class":96},[86,1833,466],{"class":138},[86,1835,1816],{"class":309},[86,1837,1819],{"class":313},[86,1839,1840],{"class":761},"\u003C",[86,1842,1762],{"class":107},[86,1844,1827],{"class":309},[86,1846,1847],{"class":309}," None\n",[86,1849,1851,1854,1856,1859,1861,1864],{"class":88,"line":1850},91,[86,1852,1853],{"class":313},"        normal_side ",[86,1855,853],{"class":342},[86,1857,1858],{"class":103}," NORMAL_SIDE",[86,1860,438],{"class":342},[86,1862,1863],{"class":313},"category",[86,1865,1866],{"class":342},"]\n",[86,1868,1870,1873,1875,1878],{"class":88,"line":1869},92,[86,1871,1872],{"class":313},"        accounts",[86,1874,805],{"class":342},[86,1876,1877],{"class":313},"append",[86,1879,1386],{"class":342},[86,1881,1883],{"class":88,"line":1882},93,[86,1884,1885],{"class":342},"            {\n",[86,1887,1889,1892,1895,1897,1899,1901],{"class":88,"line":1888},94,[86,1890,1891],{"class":138},"                \"",[86,1893,1894],{"class":96},"account",[86,1896,466],{"class":138},[86,1898,432],{"class":342},[86,1900,1640],{"class":313},[86,1902,478],{"class":342},[86,1904,1906,1908,1910,1912,1914,1917],{"class":88,"line":1905},95,[86,1907,1891],{"class":138},[86,1909,1863],{"class":96},[86,1911,466],{"class":138},[86,1913,432],{"class":342},[86,1915,1916],{"class":313}," category",[86,1918,478],{"class":342},[86,1920,1922,1924,1927,1929,1931,1934],{"class":88,"line":1921},96,[86,1923,1891],{"class":138},[86,1925,1926],{"class":96},"debit_total",[86,1928,466],{"class":138},[86,1930,432],{"class":342},[86,1932,1933],{"class":313}," debit_total",[86,1935,478],{"class":342},[86,1937,1939,1941,1944,1946,1948,1951],{"class":88,"line":1938},97,[86,1940,1891],{"class":138},[86,1942,1943],{"class":96},"credit_total",[86,1945,466],{"class":138},[86,1947,432],{"class":342},[86,1949,1950],{"class":313}," credit_total",[86,1952,478],{"class":342},[86,1954,1956,1958,1961,1963,1965,1968],{"class":88,"line":1955},98,[86,1957,1891],{"class":138},[86,1959,1960],{"class":96},"balance_side",[86,1962,466],{"class":138},[86,1964,432],{"class":342},[86,1966,1967],{"class":313}," balance_side",[86,1969,478],{"class":342},[86,1971,1973,1975,1978,1980,1982,1985,1987,1990],{"class":88,"line":1972},99,[86,1974,1891],{"class":138},[86,1976,1977],{"class":96},"balance",[86,1979,466],{"class":138},[86,1981,432],{"class":342},[86,1983,1984],{"class":114}," abs",[86,1986,769],{"class":342},[86,1988,1989],{"class":313},"net",[86,1991,1187],{"class":342},[86,1993,1995,1997,2000,2002,2004,2007],{"class":88,"line":1994},100,[86,1996,1891],{"class":138},[86,1998,1999],{"class":96},"normal_side",[86,2001,466],{"class":138},[86,2003,432],{"class":342},[86,2005,2006],{"class":313}," normal_side",[86,2008,478],{"class":342},[86,2010,2012,2014,2017,2019,2021,2024,2027,2030,2033,2035,2037,2040,2042],{"class":88,"line":2011},101,[86,2013,1891],{"class":138},[86,2015,2016],{"class":96},"is_abnormal",[86,2018,466],{"class":138},[86,2020,432],{"class":342},[86,2022,2023],{"class":313}," balance_side ",[86,2025,2026],{"class":761},"is",[86,2028,2029],{"class":761}," not",[86,2031,2032],{"class":309}," None",[86,2034,1765],{"class":761},[86,2036,2023],{"class":313},[86,2038,2039],{"class":761},"!=",[86,2041,2006],{"class":313},[86,2043,478],{"class":342},[86,2045,2047],{"class":88,"line":2046},102,[86,2048,2049],{"class":342},"            }\n",[86,2051,2053],{"class":88,"line":2052},103,[86,2054,1451],{"class":342},[86,2056,2058,2061,2063,2065,2067,2069,2071,2073,2075,2077,2079,2082,2085,2087,2090],{"class":88,"line":2057},104,[86,2059,2060],{"class":313},"    debit_sum ",[86,2062,853],{"class":342},[86,2064,1669],{"class":114},[86,2066,769],{"class":342},[86,2068,17],{"class":313},[86,2070,438],{"class":342},[86,2072,466],{"class":138},[86,2074,1926],{"class":96},[86,2076,466],{"class":138},[86,2078,449],{"class":342},[86,2080,2081],{"class":309}," for",[86,2083,2084],{"class":313}," a ",[86,2086,1648],{"class":309},[86,2088,2089],{"class":313}," accounts",[86,2091,873],{"class":342},[86,2093,2095,2098,2100,2102,2104,2106,2108,2110,2112,2114,2116,2118,2120,2122,2124],{"class":88,"line":2094},105,[86,2096,2097],{"class":313},"    credit_sum ",[86,2099,853],{"class":342},[86,2101,1669],{"class":114},[86,2103,769],{"class":342},[86,2105,17],{"class":313},[86,2107,438],{"class":342},[86,2109,466],{"class":138},[86,2111,1943],{"class":96},[86,2113,466],{"class":138},[86,2115,449],{"class":342},[86,2117,2081],{"class":309},[86,2119,2084],{"class":313},[86,2121,1648],{"class":309},[86,2123,2089],{"class":313},[86,2125,873],{"class":342},[86,2127,2129,2131],{"class":88,"line":2128},106,[86,2130,1375],{"class":309},[86,2132,454],{"class":342},[86,2134,2136,2139,2142,2144,2146,2148],{"class":88,"line":2135},107,[86,2137,2138],{"class":138},"        \"",[86,2140,2141],{"class":96},"accounts",[86,2143,466],{"class":138},[86,2145,432],{"class":342},[86,2147,2089],{"class":313},[86,2149,478],{"class":342},[86,2151,2153,2155,2158,2160,2162],{"class":88,"line":2152},108,[86,2154,2138],{"class":138},[86,2156,2157],{"class":96},"total",[86,2159,466],{"class":138},[86,2161,432],{"class":342},[86,2163,454],{"class":342},[86,2165,2167,2170,2172,2174,2176,2179],{"class":88,"line":2166},109,[86,2168,2169],{"class":138},"            \"",[86,2171,1926],{"class":96},[86,2173,466],{"class":138},[86,2175,432],{"class":342},[86,2177,2178],{"class":313}," debit_sum",[86,2180,478],{"class":342},[86,2182,2184,2186,2188,2190,2192,2195],{"class":88,"line":2183},110,[86,2185,2169],{"class":138},[86,2187,1943],{"class":96},[86,2189,466],{"class":138},[86,2191,432],{"class":342},[86,2193,2194],{"class":313}," credit_sum",[86,2196,478],{"class":342},[86,2198,2200,2202,2205,2207,2209,2212,2214,2216],{"class":88,"line":2199},111,[86,2201,2169],{"class":138},[86,2203,2204],{"class":96},"in_balance",[86,2206,466],{"class":138},[86,2208,432],{"class":342},[86,2210,2211],{"class":313}," debit_sum ",[86,2213,1704],{"class":761},[86,2215,2194],{"class":313},[86,2217,478],{"class":342},[86,2219,2221],{"class":88,"line":2220},112,[86,2222,2223],{"class":342},"        },\n",[86,2225,2227],{"class":88,"line":2226},113,[86,2228,2229],{"class":342},"    }\n",[86,2231,2233],{"class":88,"line":2232},114,[86,2234,282],{"emptyLinePlaceholder":281},[86,2236,2238],{"class":88,"line":2237},115,[86,2239,282],{"emptyLinePlaceholder":281},[86,2241,2243,2245,2248,2250,2253,2255,2257,2260,2262,2264,2266,2268,2271,2273,2276,2279,2281,2283],{"class":88,"line":2242},116,[86,2244,1111],{"class":761},[86,2246,2247],{"class":92}," require_api_key",[86,2249,769],{"class":342},[86,2251,2252],{"class":313},"x_api_key",[86,2254,432],{"class":342},[86,2256,446],{"class":114},[86,2258,2259],{"class":761}," |",[86,2261,2032],{"class":309},[86,2263,405],{"class":342},[86,2265,351],{"class":313},[86,2267,769],{"class":342},[86,2269,2270],{"class":849},"default",[86,2272,853],{"class":342},[86,2274,2275],{"class":309},"None",[86,2277,2278],{"class":342},"))",[86,2280,959],{"class":342},[86,2282,2032],{"class":309},[86,2284,964],{"class":342},[86,2286,2288,2291,2294,2296,2299],{"class":88,"line":2287},117,[86,2289,2290],{"class":309},"    if",[86,2292,2293],{"class":313}," x_api_key ",[86,2295,2039],{"class":761},[86,2297,2298],{"class":103}," API_KEY",[86,2300,964],{"class":342},[86,2302,2304,2307,2309],{"class":88,"line":2303},118,[86,2305,2306],{"class":309},"        raise",[86,2308,356],{"class":313},[86,2310,1386],{"class":342},[86,2312,2314,2317,2319,2322,2324,2327,2329,2331,2334],{"class":88,"line":2313},119,[86,2315,2316],{"class":849},"            status_code",[86,2318,853],{"class":342},[86,2320,2321],{"class":107},"401",[86,2323,343],{"class":342},[86,2325,2326],{"class":849}," detail",[86,2328,853],{"class":342},[86,2330,466],{"class":138},[86,2332,2333],{"class":96},"APIキーが不正です。X-API-Key ヘッダーを確認してください。",[86,2335,145],{"class":138},[86,2337,2339],{"class":88,"line":2338},120,[86,2340,1451],{"class":342},[86,2342,2344],{"class":88,"line":2343},121,[86,2345,282],{"emptyLinePlaceholder":281},[86,2347,2349],{"class":88,"line":2348},122,[86,2350,282],{"emptyLinePlaceholder":281},[86,2352,2354,2357,2359,2361],{"class":88,"line":2353},123,[86,2355,2356],{"class":313},"app ",[86,2358,853],{"class":342},[86,2360,346],{"class":313},[86,2362,1386],{"class":342},[86,2364,2366,2369,2371,2373,2376,2378],{"class":88,"line":2365},124,[86,2367,2368],{"class":849},"    title",[86,2370,853],{"class":342},[86,2372,466],{"class":138},[86,2374,2375],{"class":96},"ミニ仕訳帳API",[86,2377,466],{"class":138},[86,2379,478],{"class":342},[86,2381,2383,2385,2387,2389,2392,2394],{"class":88,"line":2382},125,[86,2384,879],{"class":849},[86,2386,853],{"class":342},[86,2388,466],{"class":138},[86,2390,2391],{"class":96},"教材用のミニ仕訳帳。1行仕訳の登録・一覧・残高試算表だけができる。",[86,2393,466],{"class":138},[86,2395,478],{"class":342},[86,2397,2399,2402,2404,2406,2409,2411],{"class":88,"line":2398},126,[86,2400,2401],{"class":849},"    version",[86,2403,853],{"class":342},[86,2405,466],{"class":138},[86,2407,2408],{"class":96},"0.1.0",[86,2410,466],{"class":138},[86,2412,478],{"class":342},[86,2414,2416,2419,2422,2425,2427,2430],{"class":88,"line":2415},127,[86,2417,2418],{"class":849},"    dependencies",[86,2420,2421],{"class":342},"=[",[86,2423,2424],{"class":313},"Depends",[86,2426,769],{"class":342},[86,2428,2429],{"class":313},"require_api_key",[86,2431,2432],{"class":342},")],\n",[86,2434,2436],{"class":88,"line":2435},128,[86,2437,873],{"class":342},[86,2439,2441],{"class":88,"line":2440},129,[86,2442,282],{"emptyLinePlaceholder":281},[86,2444,2446],{"class":88,"line":2445},130,[86,2447,282],{"emptyLinePlaceholder":281},[86,2449,2451,2454,2457,2459,2462,2464,2466,2469,2471],{"class":88,"line":2450},131,[86,2452,2453],{"class":342},"@",[86,2455,2456],{"class":92},"app",[86,2458,805],{"class":342},[86,2460,2461],{"class":92},"get",[86,2463,769],{"class":342},[86,2465,466],{"class":138},[86,2467,2468],{"class":96},"/journal-entries",[86,2470,466],{"class":138},[86,2472,873],{"class":342},[86,2474,2476,2478,2481],{"class":88,"line":2475},132,[86,2477,1111],{"class":761},[86,2479,2480],{"class":92}," list_journal_entries",[86,2482,1386],{"class":342},[86,2484,2486,2489,2491,2493,2495,2497,2499,2502,2504,2506,2508,2510,2512,2515,2517,2520,2522,2525,2528,2531,2534,2537,2540,2542],{"class":88,"line":2485},133,[86,2487,2488],{"class":313},"    month",[86,2490,432],{"class":342},[86,2492,446],{"class":114},[86,2494,2259],{"class":761},[86,2496,2032],{"class":309},[86,2498,405],{"class":342},[86,2500,2501],{"class":313}," Query",[86,2503,769],{"class":342},[86,2505,2270],{"class":849},[86,2507,853],{"class":342},[86,2509,2275],{"class":309},[86,2511,343],{"class":342},[86,2513,2514],{"class":849}," pattern",[86,2516,853],{"class":342},[86,2518,2519],{"class":761},"r",[86,2521,466],{"class":138},[86,2523,2524],{"class":114},"^\\d",[86,2526,2527],{"class":107},"{4}",[86,2529,1796],{"class":2530},"sLkVR",[86,2532,2533],{"class":114},"\\d",[86,2535,2536],{"class":107},"{2}",[86,2538,2539],{"class":114},"$",[86,2541,466],{"class":138},[86,2543,1187],{"class":342},[86,2545,2547,2549,2551,2553,2555,2557],{"class":88,"line":2546},134,[86,2548,956],{"class":342},[86,2550,959],{"class":342},[86,2552,1122],{"class":313},[86,2554,438],{"class":342},[86,2556,1127],{"class":313},[86,2558,1130],{"class":342},[86,2560,2562,2564,2567],{"class":88,"line":2561},135,[86,2563,781],{"class":138},[86,2565,2566],{"class":96},"仕訳一覧。month=YYYY-MM で対象月に絞り込める。",[86,2568,298],{"class":138},[86,2570,2572,2574,2577,2579,2581],{"class":88,"line":2571},136,[86,2573,2290],{"class":309},[86,2575,2576],{"class":313}," month ",[86,2578,2026],{"class":761},[86,2580,2032],{"class":309},[86,2582,964],{"class":342},[86,2584,2586,2588],{"class":88,"line":2585},137,[86,2587,1056],{"class":309},[86,2589,2590],{"class":103}," ENTRIES\n",[86,2592,2594,2596,2599,2602,2604,2606,2608,2611,2613,2615,2617,2620,2622,2625,2627,2629,2632,2634,2636,2639,2642],{"class":88,"line":2593},138,[86,2595,1375],{"class":309},[86,2597,2598],{"class":342}," [",[86,2600,2601],{"class":313},"e ",[86,2603,1682],{"class":309},[86,2605,1685],{"class":313},[86,2607,1648],{"class":309},[86,2609,2610],{"class":103}," ENTRIES",[86,2612,1816],{"class":309},[86,2614,1696],{"class":313},[86,2616,805],{"class":342},[86,2618,2619],{"class":313},"date",[86,2621,805],{"class":342},[86,2623,2624],{"class":313},"strftime",[86,2626,769],{"class":342},[86,2628,466],{"class":138},[86,2630,2631],{"class":96},"%Y-%m",[86,2633,466],{"class":138},[86,2635,956],{"class":342},[86,2637,2638],{"class":761}," ==",[86,2640,2641],{"class":313}," month",[86,2643,1866],{"class":342},[86,2645,2647],{"class":88,"line":2646},139,[86,2648,282],{"emptyLinePlaceholder":281},[86,2650,2652],{"class":88,"line":2651},140,[86,2653,282],{"emptyLinePlaceholder":281},[86,2655,2657,2659,2661,2663,2666,2668,2670,2672,2674,2676,2679,2681,2684],{"class":88,"line":2656},141,[86,2658,2453],{"class":342},[86,2660,2456],{"class":92},[86,2662,805],{"class":342},[86,2664,2665],{"class":92},"post",[86,2667,769],{"class":342},[86,2669,466],{"class":138},[86,2671,2468],{"class":96},[86,2673,466],{"class":138},[86,2675,343],{"class":342},[86,2677,2678],{"class":849}," status_code",[86,2680,853],{"class":342},[86,2682,2683],{"class":107},"201",[86,2685,873],{"class":342},[86,2687,2689,2691,2694,2696,2699,2701,2703,2705,2707,2709],{"class":88,"line":2688},142,[86,2690,1111],{"class":761},[86,2692,2693],{"class":92}," register_journal_entry",[86,2695,769],{"class":342},[86,2697,2698],{"class":313},"entry",[86,2700,432],{"class":342},[86,2702,766],{"class":313},[86,2704,956],{"class":342},[86,2706,959],{"class":342},[86,2708,1077],{"class":313},[86,2710,964],{"class":342},[86,2712,2714,2716,2719],{"class":88,"line":2713},143,[86,2715,781],{"class":138},[86,2717,2718],{"class":96},"仕訳を1件登録する。",[86,2720,298],{"class":138},[86,2722,2724,2727,2729,2732,2735,2737,2739,2742,2744,2746,2748,2750,2753,2756,2758,2760,2762,2765],{"class":88,"line":2723},144,[86,2725,2726],{"class":313},"    next_id ",[86,2728,853],{"class":342},[86,2730,2731],{"class":114}," max",[86,2733,2734],{"class":342},"((",[86,2736,1674],{"class":313},[86,2738,805],{"class":342},[86,2740,2741],{"class":313},"id ",[86,2743,1682],{"class":309},[86,2745,1685],{"class":313},[86,2747,1648],{"class":309},[86,2749,2610],{"class":103},[86,2751,2752],{"class":342},"),",[86,2754,2755],{"class":849}," default",[86,2757,853],{"class":342},[86,2759,856],{"class":107},[86,2761,956],{"class":342},[86,2763,2764],{"class":761}," +",[86,2766,2767],{"class":107}," 1\n",[86,2769,2771,2774,2776,2778,2780,2783,2785,2788,2790,2793,2795,2797,2800],{"class":88,"line":2770},145,[86,2772,2773],{"class":313},"    stored ",[86,2775,853],{"class":342},[86,2777,1077],{"class":313},[86,2779,769],{"class":342},[86,2781,2782],{"class":849},"id",[86,2784,853],{"class":342},[86,2786,2787],{"class":313},"next_id",[86,2789,343],{"class":342},[86,2791,2792],{"class":761}," **",[86,2794,2698],{"class":313},[86,2796,805],{"class":342},[86,2798,2799],{"class":313},"model_dump",[86,2801,2802],{"class":342},"())\n",[86,2804,2806,2809,2811,2813,2815,2818],{"class":88,"line":2805},146,[86,2807,2808],{"class":103},"    ENTRIES",[86,2810,805],{"class":342},[86,2812,1877],{"class":313},[86,2814,769],{"class":342},[86,2816,2817],{"class":313},"stored",[86,2819,873],{"class":342},[86,2821,2823,2825],{"class":88,"line":2822},147,[86,2824,1375],{"class":309},[86,2826,2827],{"class":313}," stored\n",[86,2829,2831],{"class":88,"line":2830},148,[86,2832,282],{"emptyLinePlaceholder":281},[86,2834,2836],{"class":88,"line":2835},149,[86,2837,282],{"emptyLinePlaceholder":281},[86,2839,2841,2843,2845,2847,2849,2851,2853,2856,2858],{"class":88,"line":2840},150,[86,2842,2453],{"class":342},[86,2844,2456],{"class":92},[86,2846,805],{"class":342},[86,2848,2461],{"class":92},[86,2850,769],{"class":342},[86,2852,466],{"class":138},[86,2854,2855],{"class":96},"/trial-balance",[86,2857,466],{"class":138},[86,2859,873],{"class":342},[86,2861,2863,2865,2868,2870,2872,2874],{"class":88,"line":2862},151,[86,2864,1111],{"class":761},[86,2866,2867],{"class":92}," get_trial_balance",[86,2869,1117],{"class":342},[86,2871,959],{"class":342},[86,2873,435],{"class":114},[86,2875,964],{"class":342},[86,2877,2879,2881,2884],{"class":88,"line":2878},152,[86,2880,781],{"class":138},[86,2882,2883],{"class":96},"勘定科目別の残高試算表。",[86,2885,298],{"class":138},[86,2887,2889,2891,2893,2895,2897],{"class":88,"line":2888},153,[86,2890,1375],{"class":309},[86,2892,1567],{"class":313},[86,2894,769],{"class":342},[86,2896,1532],{"class":103},[86,2898,873],{"class":342},[14,2900,2901],{},"保存はインメモリで、シード仕訳を5件入れてある。開業出資10万円、仕入3万円、売上8万円、消耗品5千円、預け入れ5万円。この5件に対する残高試算表は手で計算できて、次の表になるはずだ。",[190,2903,2904,2920],{},[193,2905,2906],{},[196,2907,2908,2911,2914,2917],{},[199,2909,2910],{},"科目",[199,2912,2913],{},"借方合計",[199,2915,2916],{},"貸方合計",[199,2918,2919],{},"残高",[206,2921,2922,2935,2947,2959,2971,2983,2995],{},[196,2923,2924,2926,2929,2932],{},[211,2925,463],{},[211,2927,2928],{},"180,000",[211,2930,2931],{},"85,000",[211,2933,2934],{},"借方 95,000",[196,2936,2937,2939,2942,2944],{},[211,2938,486],{},[211,2940,2941],{},"50,000",[211,2943,856],{},[211,2945,2946],{},"借方 50,000",[196,2948,2949,2951,2954,2956],{},[211,2950,569],{},[211,2952,2953],{},"30,000",[211,2955,856],{},[211,2957,2958],{},"借方 30,000",[196,2960,2961,2963,2966,2968],{},[211,2962,590],{},[211,2964,2965],{},"5,000",[211,2967,856],{},[211,2969,2970],{},"借方 5,000",[196,2972,2973,2975,2977,2980],{},[211,2974,548],{},[211,2976,856],{},[211,2978,2979],{},"80,000",[211,2981,2982],{},"貸方 80,000",[196,2984,2985,2987,2989,2992],{},[211,2986,527],{},[211,2988,856],{},[211,2990,2991],{},"100,000",[211,2993,2994],{},"貸方 100,000",[196,2996,2997,3002,3007,3011],{},[211,2998,2999],{},[248,3000,3001],{},"合計",[211,3003,3004],{},[248,3005,3006],{},"265,000",[211,3008,3009],{},[248,3010,3006],{},[211,3012,3013],{},"借方残計 180,000 ＝ 貸方残計 180,000",[14,3015,3016,3017,3019,3020,3023],{},"「はずだ」で終わらせず、この期待値表をそのままテストに固定する。貸借一致（借方合計＝貸方合計）と残高の一致（借方残計＝貸方残計）は複式簿記の恒等式なので、目視レビューではなくテストコードに検証させるのが確実だ。おまけに、現金の残高を超える支払いを登録したら ",[51,3018,2016],{}," が立つことも確かめる。",[51,3021,3022],{},"test_api.py"," の全文はこうなる。",[77,3025,3027],{"className":264,"code":3026,"language":266,"meta":82,"style":82},"\"\"\"ミニ仕訳帳APIのテスト。\n\nシード5件に対する残高試算表の期待値表をそのまま固定し、\n貸借一致・異常残高の検知までを自動で確かめる。\n実行: uv run pytest -q\n\"\"\"\n\nimport pytest\nfrom fastapi.testclient import TestClient\n\nimport api\n\nclient = TestClient(api.app)\nHEADERS = {\"X-API-Key\": api.API_KEY}\n\n\n@pytest.fixture(autouse=True)\ndef reset_entries():\n    \"\"\"各テストの前にシード5件の状態へ戻す（テスト間の独立性を保つ）。\"\"\"\n    api.ENTRIES[:] = api.seed_entries()\n    yield\n\n\n# ---- 期待値表（シード5件に対する残高試算表） ----\nEXPECTED_ROWS = {\n    \"現金\": {\"debit_total\": 180_000, \"credit_total\": 85_000, \"balance_side\": \"借方\", \"balance\": 95_000},\n    \"普通預金\": {\"debit_total\": 50_000, \"credit_total\": 0, \"balance_side\": \"借方\", \"balance\": 50_000},\n    \"仕入高\": {\"debit_total\": 30_000, \"credit_total\": 0, \"balance_side\": \"借方\", \"balance\": 30_000},\n    \"消耗品費\": {\"debit_total\": 5_000, \"credit_total\": 0, \"balance_side\": \"借方\", \"balance\": 5_000},\n    \"売上高\": {\"debit_total\": 0, \"credit_total\": 80_000, \"balance_side\": \"貸方\", \"balance\": 80_000},\n    \"資本金\": {\"debit_total\": 0, \"credit_total\": 100_000, \"balance_side\": \"貸方\", \"balance\": 100_000},\n}\n\n\ndef get_trial_balance() -> dict:\n    res = client.get(\"/trial-balance\", headers=HEADERS)\n    assert res.status_code == 200\n    return res.json()\n\n\ndef test_trial_balance_matches_expected_table():\n    \"\"\"①期待値表そのもの（全科目・is_abnormalはすべてfalse）。\"\"\"\n    tb = get_trial_balance()\n    rows = {a[\"account\"]: a for a in tb[\"accounts\"]}\n    assert set(rows) == set(EXPECTED_ROWS)\n    for account, expected in EXPECTED_ROWS.items():\n        actual = rows[account]\n        for key, value in expected.items():\n            assert actual[key] == value, f\"{account}.{key}: {actual[key]} != {value}\"\n        assert actual[\"is_abnormal\"] is False, f\"{account} が異常残高扱いになっている\"\n\n\ndef test_debit_total_equals_credit_total():\n    \"\"\"②借方合計＝貸方合計（265,000円）。\"\"\"\n    total = get_trial_balance()[\"total\"]\n    assert total[\"debit_total\"] == 265_000\n    assert total[\"credit_total\"] == 265_000\n    assert total[\"in_balance\"] is True\n\n\ndef test_debit_balances_equal_credit_balances():\n    \"\"\"③借方残の合計＝貸方残の合計（180,000円）。\"\"\"\n    accounts = get_trial_balance()[\"accounts\"]\n    debit_balances = sum(a[\"balance\"] for a in accounts if a[\"balance_side\"] == \"借方\")\n    credit_balances = sum(a[\"balance\"] for a in accounts if a[\"balance_side\"] == \"貸方\")\n    assert debit_balances == 180_000\n    assert credit_balances == 180_000\n\n\ndef test_abnormal_balance_is_detected():\n    \"\"\"④現金がマイナスになる仕訳を追加すると is_abnormal: true が立つ。\"\"\"\n    res = client.post(\n        \"/journal-entries\",\n        headers=HEADERS,\n        json={\n            \"date\": \"2026-07-31\",\n            \"debit_account\": \"仕入高\",\n            \"credit_account\": \"現金\",\n            \"amount\": 200_000,\n            \"description\": \"残高を超える仕入（異常検知テスト）\",\n        },\n    )\n    assert res.status_code == 201\n    cash = next(a for a in get_trial_balance()[\"accounts\"] if a[\"account\"] == \"現金\")\n    assert cash[\"balance_side\"] == \"貸方\"  # 資産なのに貸方残\n    assert cash[\"normal_side\"] == \"借方\"\n    assert cash[\"is_abnormal\"] is True\n\n\ndef test_register_and_list():\n    \"\"\"正常登録（201）と一覧・month絞り込み。\"\"\"\n    res = client.post(\n        \"/journal-entries\",\n        headers=HEADERS,\n        json={\n            \"date\": \"2026-08-01\",\n            \"debit_account\": \"消耗品費\",\n            \"credit_account\": \"現金\",\n            \"amount\": 3_300,\n            \"description\": \"コピー用紙\",\n        },\n    )\n    assert res.status_code == 201\n    assert res.json()[\"id\"] == 6\n\n    all_entries = client.get(\"/journal-entries\", headers=HEADERS).json()\n    assert len(all_entries) == 6\n    july = client.get(\"/journal-entries\", params={\"month\": \"2026-07\"}, headers=HEADERS).json()\n    assert len(july) == 5\n    august = client.get(\"/journal-entries\", params={\"month\": \"2026-08\"}, headers=HEADERS).json()\n    assert len(august) == 1\n\n\ndef test_missing_or_wrong_api_key_returns_401():\n    assert client.get(\"/journal-entries\").status_code == 401\n    assert client.get(\"/journal-entries\", headers={\"X-API-Key\": \"wrong\"}).status_code == 401\n\n\n@pytest.mark.parametrize(\n    \"patch\",\n    [\n        {\"debit_account\": \"雑費\"},  # ホワイトリスト外の科目\n        {\"amount\": 0},  # 正の整数でない\n        {\"amount\": -100},\n        {\"date\": \"2026/07/25\"},  # YYYY-MM-DD でない\n    ],\n)\ndef test_invalid_input_returns_422(patch):\n    body = {\n        \"date\": \"2026-07-25\",\n        \"debit_account\": \"消耗品費\",\n        \"credit_account\": \"現金\",\n        \"amount\": 3_300,\n        \"description\": \"バリデーションテスト\",\n    } | patch\n    res = client.post(\"/journal-entries\", headers=HEADERS, json=body)\n    assert res.status_code == 422\n\n\ndef test_unknown_account_error_lists_allowed_accounts():\n    \"\"\"422のエラー文に「使える科目」の案内が入る（エラー整形の伏線）。\"\"\"\n    res = client.post(\n        \"/journal-entries\",\n        headers=HEADERS,\n        json={\n            \"date\": \"2026-07-26\",\n            \"debit_account\": \"雑費\",\n            \"credit_account\": \"現金\",\n            \"amount\": 1_200,\n            \"description\": \"エラー系の固定依頼文と同じ内容\",\n        },\n    )\n    assert res.status_code == 422\n    message = res.json()[\"detail\"][0][\"msg\"]\n    assert \"雑費\" in message\n    assert \"現金\" in message  # 使える科目一覧が含まれる\n",[51,3028,3029,3036,3040,3045,3050,3055,3059,3063,3069,3085,3089,3096,3100,3121,3148,3152,3156,3180,3189,3198,3221,3226,3230,3234,3239,3248,3316,3380,3444,3508,3572,3636,3640,3644,3648,3662,3695,3713,3726,3730,3734,3743,3752,3763,3806,3829,3851,3867,3890,3959,3997,4001,4005,4014,4023,4043,4065,4085,4106,4110,4114,4123,4132,4150,4206,4261,4273,4284,4288,4292,4301,4310,4324,4334,4345,4353,4372,4390,4408,4424,4444,4448,4453,4468,4525,4553,4577,4597,4601,4605,4614,4623,4637,4647,4657,4663,4682,4700,4718,4733,4752,4756,4760,4774,4799,4803,4839,4857,4917,4935,4991,5008,5012,5016,5025,5052,5100,5104,5108,5126,5137,5142,5167,5186,5206,5230,5235,5239,5252,5261,5280,5298,5316,5330,5349,5359,5399,5414,5418,5422,5431,5440,5454,5464,5474,5480,5499,5517,5535,5550,5569,5573,5577,5591,5630,5646],{"__ignoreMap":82},[86,3030,3031,3033],{"class":88,"line":89},[86,3032,273],{"class":138},[86,3034,3035],{"class":96},"ミニ仕訳帳APIのテスト。\n",[86,3037,3038],{"class":88,"line":111},[86,3039,282],{"emptyLinePlaceholder":281},[86,3041,3042],{"class":88,"line":121},[86,3043,3044],{"class":96},"シード5件に対する残高試算表の期待値表をそのまま固定し、\n",[86,3046,3047],{"class":88,"line":148},[86,3048,3049],{"class":96},"貸借一致・異常残高の検知までを自動で確かめる。\n",[86,3051,3052],{"class":88,"line":295},[86,3053,3054],{"class":96},"実行: uv run pytest -q\n",[86,3056,3057],{"class":88,"line":301},[86,3058,298],{"class":138},[86,3060,3061],{"class":88,"line":306},[86,3062,282],{"emptyLinePlaceholder":281},[86,3064,3065,3067],{"class":88,"line":323},[86,3066,310],{"class":309},[86,3068,158],{"class":313},[86,3070,3071,3073,3075,3077,3080,3082],{"class":88,"line":328},[86,3072,331],{"class":309},[86,3074,129],{"class":313},[86,3076,805],{"class":342},[86,3078,3079],{"class":313},"testclient ",[86,3081,310],{"class":309},[86,3083,3084],{"class":313}," TestClient\n",[86,3086,3087],{"class":88,"line":364},[86,3088,282],{"emptyLinePlaceholder":281},[86,3090,3091,3093],{"class":88,"line":387},[86,3092,310],{"class":309},[86,3094,3095],{"class":313}," api\n",[86,3097,3098],{"class":88,"line":392},[86,3099,282],{"emptyLinePlaceholder":281},[86,3101,3102,3105,3107,3110,3112,3115,3117,3119],{"class":88,"line":399},[86,3103,3104],{"class":313},"client ",[86,3106,853],{"class":342},[86,3108,3109],{"class":313}," TestClient",[86,3111,769],{"class":342},[86,3113,3114],{"class":313},"api",[86,3116,805],{"class":342},[86,3118,2456],{"class":313},[86,3120,873],{"class":342},[86,3122,3123,3126,3128,3131,3133,3135,3137,3139,3142,3144,3146],{"class":88,"line":415},[86,3124,3125],{"class":103},"HEADERS",[86,3127,405],{"class":342},[86,3129,3130],{"class":342}," {",[86,3132,466],{"class":138},[86,3134,242],{"class":96},[86,3136,466],{"class":138},[86,3138,432],{"class":342},[86,3140,3141],{"class":313}," api",[86,3143,805],{"class":342},[86,3145,402],{"class":103},[86,3147,608],{"class":342},[86,3149,3150],{"class":88,"line":420},[86,3151,282],{"emptyLinePlaceholder":281},[86,3153,3154],{"class":88,"line":426},[86,3155,282],{"emptyLinePlaceholder":281},[86,3157,3158,3160,3163,3165,3168,3170,3173,3175,3178],{"class":88,"line":457},[86,3159,2453],{"class":342},[86,3161,3162],{"class":92},"pytest",[86,3164,805],{"class":342},[86,3166,3167],{"class":92},"fixture",[86,3169,769],{"class":342},[86,3171,3172],{"class":849},"autouse",[86,3174,853],{"class":342},[86,3176,3177],{"class":309},"True",[86,3179,873],{"class":342},[86,3181,3182,3184,3187],{"class":88,"line":481},[86,3183,1111],{"class":761},[86,3185,3186],{"class":92}," reset_entries",[86,3188,1658],{"class":342},[86,3190,3191,3193,3196],{"class":88,"line":501},[86,3192,781],{"class":138},[86,3194,3195],{"class":96},"各テストの前にシード5件の状態へ戻す（テスト間の独立性を保つ）。",[86,3197,298],{"class":138},[86,3199,3200,3203,3205,3207,3210,3212,3214,3216,3219],{"class":88,"line":522},[86,3201,3202],{"class":313},"    api",[86,3204,805],{"class":342},[86,3206,1532],{"class":103},[86,3208,3209],{"class":342},"[:]",[86,3211,405],{"class":342},[86,3213,3141],{"class":313},[86,3215,805],{"class":342},[86,3217,3218],{"class":313},"seed_entries",[86,3220,1549],{"class":342},[86,3222,3223],{"class":88,"line":543},[86,3224,3225],{"class":309},"    yield\n",[86,3227,3228],{"class":88,"line":564},[86,3229,282],{"emptyLinePlaceholder":281},[86,3231,3232],{"class":88,"line":585},[86,3233,282],{"emptyLinePlaceholder":281},[86,3235,3236],{"class":88,"line":605},[86,3237,3238],{"class":395},"# ---- 期待値表（シード5件に対する残高試算表） ----\n",[86,3240,3241,3244,3246],{"class":88,"line":611},[86,3242,3243],{"class":103},"EXPECTED_ROWS",[86,3245,405],{"class":342},[86,3247,454],{"class":342},[86,3249,3250,3252,3254,3256,3258,3260,3262,3264,3266,3268,3271,3273,3275,3277,3279,3281,3284,3286,3288,3290,3292,3294,3296,3298,3300,3302,3304,3306,3308,3310,3313],{"class":88,"line":616},[86,3251,460],{"class":138},[86,3253,463],{"class":96},[86,3255,466],{"class":138},[86,3257,432],{"class":342},[86,3259,3130],{"class":342},[86,3261,466],{"class":138},[86,3263,1926],{"class":96},[86,3265,466],{"class":138},[86,3267,432],{"class":342},[86,3269,3270],{"class":107}," 180_000",[86,3272,343],{"class":342},[86,3274,139],{"class":138},[86,3276,1943],{"class":96},[86,3278,466],{"class":138},[86,3280,432],{"class":342},[86,3282,3283],{"class":107}," 85_000",[86,3285,343],{"class":342},[86,3287,139],{"class":138},[86,3289,1960],{"class":96},[86,3291,466],{"class":138},[86,3293,432],{"class":342},[86,3295,139],{"class":138},[86,3297,659],{"class":96},[86,3299,466],{"class":138},[86,3301,343],{"class":342},[86,3303,139],{"class":138},[86,3305,1977],{"class":96},[86,3307,466],{"class":138},[86,3309,432],{"class":342},[86,3311,3312],{"class":107}," 95_000",[86,3314,3315],{"class":342},"},\n",[86,3317,3318,3320,3322,3324,3326,3328,3330,3332,3334,3336,3338,3340,3342,3344,3346,3348,3350,3352,3354,3356,3358,3360,3362,3364,3366,3368,3370,3372,3374,3376,3378],{"class":88,"line":622},[86,3319,460],{"class":138},[86,3321,486],{"class":96},[86,3323,466],{"class":138},[86,3325,432],{"class":342},[86,3327,3130],{"class":342},[86,3329,466],{"class":138},[86,3331,1926],{"class":96},[86,3333,466],{"class":138},[86,3335,432],{"class":342},[86,3337,1352],{"class":107},[86,3339,343],{"class":342},[86,3341,139],{"class":138},[86,3343,1943],{"class":96},[86,3345,466],{"class":138},[86,3347,432],{"class":342},[86,3349,1762],{"class":107},[86,3351,343],{"class":342},[86,3353,139],{"class":138},[86,3355,1960],{"class":96},[86,3357,466],{"class":138},[86,3359,432],{"class":342},[86,3361,139],{"class":138},[86,3363,659],{"class":96},[86,3365,466],{"class":138},[86,3367,343],{"class":342},[86,3369,139],{"class":138},[86,3371,1977],{"class":96},[86,3373,466],{"class":138},[86,3375,432],{"class":342},[86,3377,1352],{"class":107},[86,3379,3315],{"class":342},[86,3381,3382,3384,3386,3388,3390,3392,3394,3396,3398,3400,3402,3404,3406,3408,3410,3412,3414,3416,3418,3420,3422,3424,3426,3428,3430,3432,3434,3436,3438,3440,3442],{"class":88,"line":646},[86,3383,460],{"class":138},[86,3385,569],{"class":96},[86,3387,466],{"class":138},[86,3389,432],{"class":342},[86,3391,3130],{"class":342},[86,3393,466],{"class":138},[86,3395,1926],{"class":96},[86,3397,466],{"class":138},[86,3399,432],{"class":342},[86,3401,1220],{"class":107},[86,3403,343],{"class":342},[86,3405,139],{"class":138},[86,3407,1943],{"class":96},[86,3409,466],{"class":138},[86,3411,432],{"class":342},[86,3413,1762],{"class":107},[86,3415,343],{"class":342},[86,3417,139],{"class":138},[86,3419,1960],{"class":96},[86,3421,466],{"class":138},[86,3423,432],{"class":342},[86,3425,139],{"class":138},[86,3427,659],{"class":96},[86,3429,466],{"class":138},[86,3431,343],{"class":342},[86,3433,139],{"class":138},[86,3435,1977],{"class":96},[86,3437,466],{"class":138},[86,3439,432],{"class":342},[86,3441,1220],{"class":107},[86,3443,3315],{"class":342},[86,3445,3446,3448,3450,3452,3454,3456,3458,3460,3462,3464,3466,3468,3470,3472,3474,3476,3478,3480,3482,3484,3486,3488,3490,3492,3494,3496,3498,3500,3502,3504,3506],{"class":88,"line":666},[86,3447,460],{"class":138},[86,3449,590],{"class":96},[86,3451,466],{"class":138},[86,3453,432],{"class":342},[86,3455,3130],{"class":342},[86,3457,466],{"class":138},[86,3459,1926],{"class":96},[86,3461,466],{"class":138},[86,3463,432],{"class":342},[86,3465,1308],{"class":107},[86,3467,343],{"class":342},[86,3469,139],{"class":138},[86,3471,1943],{"class":96},[86,3473,466],{"class":138},[86,3475,432],{"class":342},[86,3477,1762],{"class":107},[86,3479,343],{"class":342},[86,3481,139],{"class":138},[86,3483,1960],{"class":96},[86,3485,466],{"class":138},[86,3487,432],{"class":342},[86,3489,139],{"class":138},[86,3491,659],{"class":96},[86,3493,466],{"class":138},[86,3495,343],{"class":342},[86,3497,139],{"class":138},[86,3499,1977],{"class":96},[86,3501,466],{"class":138},[86,3503,432],{"class":342},[86,3505,1308],{"class":107},[86,3507,3315],{"class":342},[86,3509,3510,3512,3514,3516,3518,3520,3522,3524,3526,3528,3530,3532,3534,3536,3538,3540,3542,3544,3546,3548,3550,3552,3554,3556,3558,3560,3562,3564,3566,3568,3570],{"class":88,"line":685},[86,3511,460],{"class":138},[86,3513,548],{"class":96},[86,3515,466],{"class":138},[86,3517,432],{"class":342},[86,3519,3130],{"class":342},[86,3521,466],{"class":138},[86,3523,1926],{"class":96},[86,3525,466],{"class":138},[86,3527,432],{"class":342},[86,3529,1762],{"class":107},[86,3531,343],{"class":342},[86,3533,139],{"class":138},[86,3535,1943],{"class":96},[86,3537,466],{"class":138},[86,3539,432],{"class":342},[86,3541,1264],{"class":107},[86,3543,343],{"class":342},[86,3545,139],{"class":138},[86,3547,1960],{"class":96},[86,3549,466],{"class":138},[86,3551,432],{"class":342},[86,3553,139],{"class":138},[86,3555,698],{"class":96},[86,3557,466],{"class":138},[86,3559,343],{"class":342},[86,3561,139],{"class":138},[86,3563,1977],{"class":96},[86,3565,466],{"class":138},[86,3567,432],{"class":342},[86,3569,1264],{"class":107},[86,3571,3315],{"class":342},[86,3573,3574,3576,3578,3580,3582,3584,3586,3588,3590,3592,3594,3596,3598,3600,3602,3604,3606,3608,3610,3612,3614,3616,3618,3620,3622,3624,3626,3628,3630,3632,3634],{"class":88,"line":705},[86,3575,460],{"class":138},[86,3577,527],{"class":96},[86,3579,466],{"class":138},[86,3581,432],{"class":342},[86,3583,3130],{"class":342},[86,3585,466],{"class":138},[86,3587,1926],{"class":96},[86,3589,466],{"class":138},[86,3591,432],{"class":342},[86,3593,1762],{"class":107},[86,3595,343],{"class":342},[86,3597,139],{"class":138},[86,3599,1943],{"class":96},[86,3601,466],{"class":138},[86,3603,432],{"class":342},[86,3605,1175],{"class":107},[86,3607,343],{"class":342},[86,3609,139],{"class":138},[86,3611,1960],{"class":96},[86,3613,466],{"class":138},[86,3615,432],{"class":342},[86,3617,139],{"class":138},[86,3619,698],{"class":96},[86,3621,466],{"class":138},[86,3623,343],{"class":342},[86,3625,139],{"class":138},[86,3627,1977],{"class":96},[86,3629,466],{"class":138},[86,3631,432],{"class":342},[86,3633,1175],{"class":107},[86,3635,3315],{"class":342},[86,3637,3638],{"class":88,"line":724},[86,3639,608],{"class":342},[86,3641,3642],{"class":88,"line":743},[86,3643,282],{"emptyLinePlaceholder":281},[86,3645,3646],{"class":88,"line":748},[86,3647,282],{"emptyLinePlaceholder":281},[86,3649,3650,3652,3654,3656,3658,3660],{"class":88,"line":753},[86,3651,1111],{"class":761},[86,3653,2867],{"class":92},[86,3655,1117],{"class":342},[86,3657,959],{"class":342},[86,3659,435],{"class":114},[86,3661,964],{"class":342},[86,3663,3664,3667,3669,3672,3674,3676,3678,3680,3682,3684,3686,3689,3691,3693],{"class":88,"line":758},[86,3665,3666],{"class":313},"    res ",[86,3668,853],{"class":342},[86,3670,3671],{"class":313}," client",[86,3673,805],{"class":342},[86,3675,2461],{"class":313},[86,3677,769],{"class":342},[86,3679,466],{"class":138},[86,3681,2855],{"class":96},[86,3683,466],{"class":138},[86,3685,343],{"class":342},[86,3687,3688],{"class":849}," headers",[86,3690,853],{"class":342},[86,3692,3125],{"class":103},[86,3694,873],{"class":342},[86,3696,3697,3700,3703,3705,3708,3710],{"class":88,"line":778},[86,3698,3699],{"class":309},"    assert",[86,3701,3702],{"class":313}," res",[86,3704,805],{"class":342},[86,3706,3707],{"class":313},"status_code ",[86,3709,1704],{"class":761},[86,3711,3712],{"class":107}," 200\n",[86,3714,3715,3717,3719,3721,3724],{"class":88,"line":789},[86,3716,1375],{"class":309},[86,3718,3702],{"class":313},[86,3720,805],{"class":342},[86,3722,3723],{"class":313},"json",[86,3725,1549],{"class":342},[86,3727,3728],{"class":88,"line":794},[86,3729,282],{"emptyLinePlaceholder":281},[86,3731,3732],{"class":88,"line":811},[86,3733,282],{"emptyLinePlaceholder":281},[86,3735,3736,3738,3741],{"class":88,"line":822},[86,3737,1111],{"class":761},[86,3739,3740],{"class":92}," test_trial_balance_matches_expected_table",[86,3742,1658],{"class":342},[86,3744,3745,3747,3750],{"class":88,"line":832},[86,3746,781],{"class":138},[86,3748,3749],{"class":96},"①期待値表そのもの（全科目・is_abnormalはすべてfalse）。",[86,3751,298],{"class":138},[86,3753,3754,3757,3759,3761],{"class":88,"line":876},[86,3755,3756],{"class":313},"    tb ",[86,3758,853],{"class":342},[86,3760,2867],{"class":313},[86,3762,1549],{"class":342},[86,3764,3765,3767,3769,3771,3773,3775,3777,3779,3781,3784,3786,3788,3790,3792,3795,3797,3799,3801,3803],{"class":88,"line":891},[86,3766,1136],{"class":313},[86,3768,853],{"class":342},[86,3770,3130],{"class":342},[86,3772,17],{"class":313},[86,3774,438],{"class":342},[86,3776,466],{"class":138},[86,3778,1894],{"class":96},[86,3780,466],{"class":138},[86,3782,3783],{"class":342},"]:",[86,3785,2084],{"class":313},[86,3787,1682],{"class":309},[86,3789,2084],{"class":313},[86,3791,1648],{"class":309},[86,3793,3794],{"class":313}," tb",[86,3796,438],{"class":342},[86,3798,466],{"class":138},[86,3800,2141],{"class":96},[86,3802,466],{"class":138},[86,3804,3805],{"class":342},"]}\n",[86,3807,3808,3810,3813,3815,3817,3819,3821,3823,3825,3827],{"class":88,"line":896},[86,3809,3699],{"class":309},[86,3811,3812],{"class":114}," set",[86,3814,769],{"class":342},[86,3816,1499],{"class":313},[86,3818,956],{"class":342},[86,3820,2638],{"class":761},[86,3822,3812],{"class":114},[86,3824,769],{"class":342},[86,3826,3243],{"class":103},[86,3828,873],{"class":342},[86,3830,3831,3833,3835,3837,3840,3842,3845,3847,3849],{"class":88,"line":925},[86,3832,1637],{"class":309},[86,3834,1640],{"class":313},[86,3836,343],{"class":342},[86,3838,3839],{"class":313}," expected ",[86,3841,1648],{"class":309},[86,3843,3844],{"class":103}," EXPECTED_ROWS",[86,3846,805],{"class":342},[86,3848,1655],{"class":313},[86,3850,1658],{"class":342},[86,3852,3853,3856,3858,3861,3863,3865],{"class":88,"line":933},[86,3854,3855],{"class":313},"        actual ",[86,3857,853],{"class":342},[86,3859,3860],{"class":313}," rows",[86,3862,438],{"class":342},[86,3864,1894],{"class":313},[86,3866,1866],{"class":342},[86,3868,3869,3871,3874,3876,3879,3881,3884,3886,3888],{"class":88,"line":967},[86,3870,1457],{"class":309},[86,3872,3873],{"class":313}," key",[86,3875,343],{"class":342},[86,3877,3878],{"class":313}," value ",[86,3880,1648],{"class":309},[86,3882,3883],{"class":313}," expected",[86,3885,805],{"class":342},[86,3887,1655],{"class":313},[86,3889,1658],{"class":342},[86,3891,3892,3895,3898,3900,3903,3905,3907,3910,3912,3915,3917,3919,3921,3923,3925,3927,3929,3931,3934,3936,3939,3941,3943,3945,3947,3950,3952,3955,3957],{"class":88,"line":987},[86,3893,3894],{"class":309},"            assert",[86,3896,3897],{"class":313}," actual",[86,3899,438],{"class":342},[86,3901,3902],{"class":313},"key",[86,3904,449],{"class":342},[86,3906,2638],{"class":761},[86,3908,3909],{"class":313}," value",[86,3911,343],{"class":342},[86,3913,3914],{"class":761}," f",[86,3916,466],{"class":96},[86,3918,1030],{"class":103},[86,3920,1894],{"class":313},[86,3922,1036],{"class":103},[86,3924,805],{"class":96},[86,3926,1030],{"class":103},[86,3928,3902],{"class":313},[86,3930,1036],{"class":103},[86,3932,3933],{"class":96},": ",[86,3935,1030],{"class":103},[86,3937,3938],{"class":313},"actual",[86,3940,438],{"class":342},[86,3942,3902],{"class":313},[86,3944,449],{"class":342},[86,3946,1036],{"class":103},[86,3948,3949],{"class":96}," != ",[86,3951,1030],{"class":103},[86,3953,3954],{"class":313},"value",[86,3956,1036],{"class":103},[86,3958,145],{"class":96},[86,3960,3961,3964,3966,3968,3970,3972,3974,3976,3979,3982,3984,3986,3988,3990,3992,3994],{"class":88,"line":1013},[86,3962,3963],{"class":309},"        assert",[86,3965,3897],{"class":313},[86,3967,438],{"class":342},[86,3969,466],{"class":138},[86,3971,2016],{"class":96},[86,3973,466],{"class":138},[86,3975,449],{"class":342},[86,3977,3978],{"class":761}," is",[86,3980,3981],{"class":309}," False",[86,3983,343],{"class":342},[86,3985,3914],{"class":761},[86,3987,466],{"class":96},[86,3989,1030],{"class":103},[86,3991,1894],{"class":313},[86,3993,1036],{"class":103},[86,3995,3996],{"class":96}," が異常残高扱いになっている\"\n",[86,3998,3999],{"class":88,"line":1053},[86,4000,282],{"emptyLinePlaceholder":281},[86,4002,4003],{"class":88,"line":1062},[86,4004,282],{"emptyLinePlaceholder":281},[86,4006,4007,4009,4012],{"class":88,"line":1067},[86,4008,1111],{"class":761},[86,4010,4011],{"class":92}," test_debit_total_equals_credit_total",[86,4013,1658],{"class":342},[86,4015,4016,4018,4021],{"class":88,"line":1072},[86,4017,781],{"class":138},[86,4019,4020],{"class":96},"②借方合計＝貸方合計（265,000円）。",[86,4022,298],{"class":138},[86,4024,4025,4028,4030,4032,4035,4037,4039,4041],{"class":88,"line":1087},[86,4026,4027],{"class":313},"    total ",[86,4029,853],{"class":342},[86,4031,2867],{"class":313},[86,4033,4034],{"class":342},"()[",[86,4036,466],{"class":138},[86,4038,2157],{"class":96},[86,4040,466],{"class":138},[86,4042,1866],{"class":342},[86,4044,4045,4047,4050,4052,4054,4056,4058,4060,4062],{"class":88,"line":1098},[86,4046,3699],{"class":309},[86,4048,4049],{"class":313}," total",[86,4051,438],{"class":342},[86,4053,466],{"class":138},[86,4055,1926],{"class":96},[86,4057,466],{"class":138},[86,4059,449],{"class":342},[86,4061,2638],{"class":761},[86,4063,4064],{"class":107}," 265_000\n",[86,4066,4067,4069,4071,4073,4075,4077,4079,4081,4083],{"class":88,"line":1103},[86,4068,3699],{"class":309},[86,4070,4049],{"class":313},[86,4072,438],{"class":342},[86,4074,466],{"class":138},[86,4076,1943],{"class":96},[86,4078,466],{"class":138},[86,4080,449],{"class":342},[86,4082,2638],{"class":761},[86,4084,4064],{"class":107},[86,4086,4087,4089,4091,4093,4095,4097,4099,4101,4103],{"class":88,"line":1108},[86,4088,3699],{"class":309},[86,4090,4049],{"class":313},[86,4092,438],{"class":342},[86,4094,466],{"class":138},[86,4096,2204],{"class":96},[86,4098,466],{"class":138},[86,4100,449],{"class":342},[86,4102,3978],{"class":761},[86,4104,4105],{"class":309}," True\n",[86,4107,4108],{"class":88,"line":1133},[86,4109,282],{"emptyLinePlaceholder":281},[86,4111,4112],{"class":88,"line":1144},[86,4113,282],{"emptyLinePlaceholder":281},[86,4115,4116,4118,4121],{"class":88,"line":1190},[86,4117,1111],{"class":761},[86,4119,4120],{"class":92}," test_debit_balances_equal_credit_balances",[86,4122,1658],{"class":342},[86,4124,4125,4127,4130],{"class":88,"line":1234},[86,4126,781],{"class":138},[86,4128,4129],{"class":96},"③借方残の合計＝貸方残の合計（180,000円）。",[86,4131,298],{"class":138},[86,4133,4134,4136,4138,4140,4142,4144,4146,4148],{"class":88,"line":1278},[86,4135,1626],{"class":313},[86,4137,853],{"class":342},[86,4139,2867],{"class":313},[86,4141,4034],{"class":342},[86,4143,466],{"class":138},[86,4145,2141],{"class":96},[86,4147,466],{"class":138},[86,4149,1866],{"class":342},[86,4151,4152,4155,4157,4159,4161,4163,4165,4167,4169,4171,4173,4175,4177,4179,4182,4184,4186,4188,4190,4192,4194,4196,4198,4200,4202,4204],{"class":88,"line":1322},[86,4153,4154],{"class":313},"    debit_balances ",[86,4156,853],{"class":342},[86,4158,1669],{"class":114},[86,4160,769],{"class":342},[86,4162,17],{"class":313},[86,4164,438],{"class":342},[86,4166,466],{"class":138},[86,4168,1977],{"class":96},[86,4170,466],{"class":138},[86,4172,449],{"class":342},[86,4174,2081],{"class":309},[86,4176,2084],{"class":313},[86,4178,1648],{"class":309},[86,4180,4181],{"class":313}," accounts ",[86,4183,1693],{"class":309},[86,4185,1482],{"class":313},[86,4187,438],{"class":342},[86,4189,466],{"class":138},[86,4191,1960],{"class":96},[86,4193,466],{"class":138},[86,4195,449],{"class":342},[86,4197,2638],{"class":761},[86,4199,139],{"class":138},[86,4201,659],{"class":96},[86,4203,466],{"class":138},[86,4205,873],{"class":342},[86,4207,4208,4211,4213,4215,4217,4219,4221,4223,4225,4227,4229,4231,4233,4235,4237,4239,4241,4243,4245,4247,4249,4251,4253,4255,4257,4259],{"class":88,"line":1366},[86,4209,4210],{"class":313},"    credit_balances ",[86,4212,853],{"class":342},[86,4214,1669],{"class":114},[86,4216,769],{"class":342},[86,4218,17],{"class":313},[86,4220,438],{"class":342},[86,4222,466],{"class":138},[86,4224,1977],{"class":96},[86,4226,466],{"class":138},[86,4228,449],{"class":342},[86,4230,2081],{"class":309},[86,4232,2084],{"class":313},[86,4234,1648],{"class":309},[86,4236,4181],{"class":313},[86,4238,1693],{"class":309},[86,4240,1482],{"class":313},[86,4242,438],{"class":342},[86,4244,466],{"class":138},[86,4246,1960],{"class":96},[86,4248,466],{"class":138},[86,4250,449],{"class":342},[86,4252,2638],{"class":761},[86,4254,139],{"class":138},[86,4256,698],{"class":96},[86,4258,466],{"class":138},[86,4260,873],{"class":342},[86,4262,4263,4265,4268,4270],{"class":88,"line":1372},[86,4264,3699],{"class":309},[86,4266,4267],{"class":313}," debit_balances ",[86,4269,1704],{"class":761},[86,4271,4272],{"class":107}," 180_000\n",[86,4274,4275,4277,4280,4282],{"class":88,"line":1380},[86,4276,3699],{"class":309},[86,4278,4279],{"class":313}," credit_balances ",[86,4281,1704],{"class":761},[86,4283,4272],{"class":107},[86,4285,4286],{"class":88,"line":1389},[86,4287,282],{"emptyLinePlaceholder":281},[86,4289,4290],{"class":88,"line":1448},[86,4291,282],{"emptyLinePlaceholder":281},[86,4293,4294,4296,4299],{"class":88,"line":1454},[86,4295,1111],{"class":761},[86,4297,4298],{"class":92}," test_abnormal_balance_is_detected",[86,4300,1658],{"class":342},[86,4302,4303,4305,4308],{"class":88,"line":1514},[86,4304,781],{"class":138},[86,4306,4307],{"class":96},"④現金がマイナスになる仕訳を追加すると is_abnormal: true が立つ。",[86,4309,298],{"class":138},[86,4311,4312,4314,4316,4318,4320,4322],{"class":88,"line":1519},[86,4313,3666],{"class":313},[86,4315,853],{"class":342},[86,4317,3671],{"class":313},[86,4319,805],{"class":342},[86,4321,2665],{"class":313},[86,4323,1386],{"class":342},[86,4325,4326,4328,4330,4332],{"class":88,"line":1524},[86,4327,2138],{"class":138},[86,4329,2468],{"class":96},[86,4331,466],{"class":138},[86,4333,478],{"class":342},[86,4335,4336,4339,4341,4343],{"class":88,"line":1529},[86,4337,4338],{"class":849},"        headers",[86,4340,853],{"class":342},[86,4342,3125],{"class":103},[86,4344,478],{"class":342},[86,4346,4347,4350],{"class":88,"line":1552},[86,4348,4349],{"class":849},"        json",[86,4351,4352],{"class":342},"={\n",[86,4354,4355,4357,4359,4361,4363,4365,4368,4370],{"class":88,"line":1557},[86,4356,2169],{"class":138},[86,4358,2619],{"class":96},[86,4360,466],{"class":138},[86,4362,432],{"class":342},[86,4364,139],{"class":138},[86,4366,4367],{"class":96},"2026-07-31",[86,4369,466],{"class":138},[86,4371,478],{"class":342},[86,4373,4374,4376,4378,4380,4382,4384,4386,4388],{"class":88,"line":1562},[86,4375,2169],{"class":138},[86,4377,909],{"class":96},[86,4379,466],{"class":138},[86,4381,432],{"class":342},[86,4383,139],{"class":138},[86,4385,569],{"class":96},[86,4387,466],{"class":138},[86,4389,478],{"class":342},[86,4391,4392,4394,4396,4398,4400,4402,4404,4406],{"class":88,"line":1592},[86,4393,2169],{"class":138},[86,4395,918],{"class":96},[86,4397,466],{"class":138},[86,4399,432],{"class":342},[86,4401,139],{"class":138},[86,4403,463],{"class":96},[86,4405,466],{"class":138},[86,4407,478],{"class":342},[86,4409,4410,4412,4415,4417,4419,4422],{"class":88,"line":1600},[86,4411,2169],{"class":138},[86,4413,4414],{"class":96},"amount",[86,4416,466],{"class":138},[86,4418,432],{"class":342},[86,4420,4421],{"class":107}," 200_000",[86,4423,478],{"class":342},[86,4425,4426,4428,4431,4433,4435,4437,4440,4442],{"class":88,"line":1605},[86,4427,2169],{"class":138},[86,4429,4430],{"class":96},"description",[86,4432,466],{"class":138},[86,4434,432],{"class":342},[86,4436,139],{"class":138},[86,4438,4439],{"class":96},"残高を超える仕入（異常検知テスト）",[86,4441,466],{"class":138},[86,4443,478],{"class":342},[86,4445,4446],{"class":88,"line":1611},[86,4447,2223],{"class":342},[86,4449,4450],{"class":88,"line":1617},[86,4451,4452],{"class":342},"    )\n",[86,4454,4455,4457,4459,4461,4463,4465],{"class":88,"line":1623},[86,4456,3699],{"class":309},[86,4458,3702],{"class":313},[86,4460,805],{"class":342},[86,4462,3707],{"class":313},[86,4464,1704],{"class":761},[86,4466,4467],{"class":107}," 201\n",[86,4469,4470,4473,4475,4478,4480,4483,4485,4487,4489,4491,4493,4495,4497,4499,4501,4503,4505,4507,4509,4511,4513,4515,4517,4519,4521,4523],{"class":88,"line":1634},[86,4471,4472],{"class":313},"    cash ",[86,4474,853],{"class":342},[86,4476,4477],{"class":114}," next",[86,4479,769],{"class":342},[86,4481,4482],{"class":313},"a ",[86,4484,1682],{"class":309},[86,4486,2084],{"class":313},[86,4488,1648],{"class":309},[86,4490,2867],{"class":313},[86,4492,4034],{"class":342},[86,4494,466],{"class":138},[86,4496,2141],{"class":96},[86,4498,466],{"class":138},[86,4500,449],{"class":342},[86,4502,1816],{"class":309},[86,4504,1482],{"class":313},[86,4506,438],{"class":342},[86,4508,466],{"class":138},[86,4510,1894],{"class":96},[86,4512,466],{"class":138},[86,4514,449],{"class":342},[86,4516,2638],{"class":761},[86,4518,139],{"class":138},[86,4520,463],{"class":96},[86,4522,466],{"class":138},[86,4524,873],{"class":342},[86,4526,4527,4529,4532,4534,4536,4538,4540,4542,4544,4546,4548,4550],{"class":88,"line":1661},[86,4528,3699],{"class":309},[86,4530,4531],{"class":313}," cash",[86,4533,438],{"class":342},[86,4535,466],{"class":138},[86,4537,1960],{"class":96},[86,4539,466],{"class":138},[86,4541,449],{"class":342},[86,4543,2638],{"class":761},[86,4545,139],{"class":138},[86,4547,698],{"class":96},[86,4549,466],{"class":138},[86,4551,4552],{"class":395},"  # 資産なのに貸方残\n",[86,4554,4555,4557,4559,4561,4563,4565,4567,4569,4571,4573,4575],{"class":88,"line":1711},[86,4556,3699],{"class":309},[86,4558,4531],{"class":313},[86,4560,438],{"class":342},[86,4562,466],{"class":138},[86,4564,1999],{"class":96},[86,4566,466],{"class":138},[86,4568,449],{"class":342},[86,4570,2638],{"class":761},[86,4572,139],{"class":138},[86,4574,659],{"class":96},[86,4576,145],{"class":138},[86,4578,4579,4581,4583,4585,4587,4589,4591,4593,4595],{"class":88,"line":1752},[86,4580,3699],{"class":309},[86,4582,4531],{"class":313},[86,4584,438],{"class":342},[86,4586,466],{"class":138},[86,4588,2016],{"class":96},[86,4590,466],{"class":138},[86,4592,449],{"class":342},[86,4594,3978],{"class":761},[86,4596,4105],{"class":309},[86,4598,4599],{"class":88,"line":1777},[86,4600,282],{"emptyLinePlaceholder":281},[86,4602,4603],{"class":88,"line":1786},[86,4604,282],{"emptyLinePlaceholder":281},[86,4606,4607,4609,4612],{"class":88,"line":1802},[86,4608,1111],{"class":761},[86,4610,4611],{"class":92}," test_register_and_list",[86,4613,1658],{"class":342},[86,4615,4616,4618,4621],{"class":88,"line":1850},[86,4617,781],{"class":138},[86,4619,4620],{"class":96},"正常登録（201）と一覧・month絞り込み。",[86,4622,298],{"class":138},[86,4624,4625,4627,4629,4631,4633,4635],{"class":88,"line":1869},[86,4626,3666],{"class":313},[86,4628,853],{"class":342},[86,4630,3671],{"class":313},[86,4632,805],{"class":342},[86,4634,2665],{"class":313},[86,4636,1386],{"class":342},[86,4638,4639,4641,4643,4645],{"class":88,"line":1882},[86,4640,2138],{"class":138},[86,4642,2468],{"class":96},[86,4644,466],{"class":138},[86,4646,478],{"class":342},[86,4648,4649,4651,4653,4655],{"class":88,"line":1888},[86,4650,4338],{"class":849},[86,4652,853],{"class":342},[86,4654,3125],{"class":103},[86,4656,478],{"class":342},[86,4658,4659,4661],{"class":88,"line":1905},[86,4660,4349],{"class":849},[86,4662,4352],{"class":342},[86,4664,4665,4667,4669,4671,4673,4675,4678,4680],{"class":88,"line":1921},[86,4666,2169],{"class":138},[86,4668,2619],{"class":96},[86,4670,466],{"class":138},[86,4672,432],{"class":342},[86,4674,139],{"class":138},[86,4676,4677],{"class":96},"2026-08-01",[86,4679,466],{"class":138},[86,4681,478],{"class":342},[86,4683,4684,4686,4688,4690,4692,4694,4696,4698],{"class":88,"line":1938},[86,4685,2169],{"class":138},[86,4687,909],{"class":96},[86,4689,466],{"class":138},[86,4691,432],{"class":342},[86,4693,139],{"class":138},[86,4695,590],{"class":96},[86,4697,466],{"class":138},[86,4699,478],{"class":342},[86,4701,4702,4704,4706,4708,4710,4712,4714,4716],{"class":88,"line":1955},[86,4703,2169],{"class":138},[86,4705,918],{"class":96},[86,4707,466],{"class":138},[86,4709,432],{"class":342},[86,4711,139],{"class":138},[86,4713,463],{"class":96},[86,4715,466],{"class":138},[86,4717,478],{"class":342},[86,4719,4720,4722,4724,4726,4728,4731],{"class":88,"line":1972},[86,4721,2169],{"class":138},[86,4723,4414],{"class":96},[86,4725,466],{"class":138},[86,4727,432],{"class":342},[86,4729,4730],{"class":107}," 3_300",[86,4732,478],{"class":342},[86,4734,4735,4737,4739,4741,4743,4745,4748,4750],{"class":88,"line":1994},[86,4736,2169],{"class":138},[86,4738,4430],{"class":96},[86,4740,466],{"class":138},[86,4742,432],{"class":342},[86,4744,139],{"class":138},[86,4746,4747],{"class":96},"コピー用紙",[86,4749,466],{"class":138},[86,4751,478],{"class":342},[86,4753,4754],{"class":88,"line":2011},[86,4755,2223],{"class":342},[86,4757,4758],{"class":88,"line":2046},[86,4759,4452],{"class":342},[86,4761,4762,4764,4766,4768,4770,4772],{"class":88,"line":2052},[86,4763,3699],{"class":309},[86,4765,3702],{"class":313},[86,4767,805],{"class":342},[86,4769,3707],{"class":313},[86,4771,1704],{"class":761},[86,4773,4467],{"class":107},[86,4775,4776,4778,4780,4782,4784,4786,4788,4790,4792,4794,4796],{"class":88,"line":2057},[86,4777,3699],{"class":309},[86,4779,3702],{"class":313},[86,4781,805],{"class":342},[86,4783,3723],{"class":313},[86,4785,4034],{"class":342},[86,4787,466],{"class":138},[86,4789,2782],{"class":96},[86,4791,466],{"class":138},[86,4793,449],{"class":342},[86,4795,2638],{"class":761},[86,4797,4798],{"class":107}," 6\n",[86,4800,4801],{"class":88,"line":2094},[86,4802,282],{"emptyLinePlaceholder":281},[86,4804,4805,4808,4810,4812,4814,4816,4818,4820,4822,4824,4826,4828,4830,4832,4835,4837],{"class":88,"line":2128},[86,4806,4807],{"class":313},"    all_entries ",[86,4809,853],{"class":342},[86,4811,3671],{"class":313},[86,4813,805],{"class":342},[86,4815,2461],{"class":313},[86,4817,769],{"class":342},[86,4819,466],{"class":138},[86,4821,2468],{"class":96},[86,4823,466],{"class":138},[86,4825,343],{"class":342},[86,4827,3688],{"class":849},[86,4829,853],{"class":342},[86,4831,3125],{"class":103},[86,4833,4834],{"class":342},").",[86,4836,3723],{"class":313},[86,4838,1549],{"class":342},[86,4840,4841,4843,4846,4848,4851,4853,4855],{"class":88,"line":2135},[86,4842,3699],{"class":309},[86,4844,4845],{"class":114}," len",[86,4847,769],{"class":342},[86,4849,4850],{"class":313},"all_entries",[86,4852,956],{"class":342},[86,4854,2638],{"class":761},[86,4856,4798],{"class":107},[86,4858,4859,4862,4864,4866,4868,4870,4872,4874,4876,4878,4880,4883,4886,4888,4891,4893,4895,4897,4900,4902,4905,4907,4909,4911,4913,4915],{"class":88,"line":2152},[86,4860,4861],{"class":313},"    july ",[86,4863,853],{"class":342},[86,4865,3671],{"class":313},[86,4867,805],{"class":342},[86,4869,2461],{"class":313},[86,4871,769],{"class":342},[86,4873,466],{"class":138},[86,4875,2468],{"class":96},[86,4877,466],{"class":138},[86,4879,343],{"class":342},[86,4881,4882],{"class":849}," params",[86,4884,4885],{"class":342},"={",[86,4887,466],{"class":138},[86,4889,4890],{"class":96},"month",[86,4892,466],{"class":138},[86,4894,432],{"class":342},[86,4896,139],{"class":138},[86,4898,4899],{"class":96},"2026-07",[86,4901,466],{"class":138},[86,4903,4904],{"class":342},"},",[86,4906,3688],{"class":849},[86,4908,853],{"class":342},[86,4910,3125],{"class":103},[86,4912,4834],{"class":342},[86,4914,3723],{"class":313},[86,4916,1549],{"class":342},[86,4918,4919,4921,4923,4925,4928,4930,4932],{"class":88,"line":2166},[86,4920,3699],{"class":309},[86,4922,4845],{"class":114},[86,4924,769],{"class":342},[86,4926,4927],{"class":313},"july",[86,4929,956],{"class":342},[86,4931,2638],{"class":761},[86,4933,4934],{"class":107}," 5\n",[86,4936,4937,4940,4942,4944,4946,4948,4950,4952,4954,4956,4958,4960,4962,4964,4966,4968,4970,4972,4975,4977,4979,4981,4983,4985,4987,4989],{"class":88,"line":2183},[86,4938,4939],{"class":313},"    august ",[86,4941,853],{"class":342},[86,4943,3671],{"class":313},[86,4945,805],{"class":342},[86,4947,2461],{"class":313},[86,4949,769],{"class":342},[86,4951,466],{"class":138},[86,4953,2468],{"class":96},[86,4955,466],{"class":138},[86,4957,343],{"class":342},[86,4959,4882],{"class":849},[86,4961,4885],{"class":342},[86,4963,466],{"class":138},[86,4965,4890],{"class":96},[86,4967,466],{"class":138},[86,4969,432],{"class":342},[86,4971,139],{"class":138},[86,4973,4974],{"class":96},"2026-08",[86,4976,466],{"class":138},[86,4978,4904],{"class":342},[86,4980,3688],{"class":849},[86,4982,853],{"class":342},[86,4984,3125],{"class":103},[86,4986,4834],{"class":342},[86,4988,3723],{"class":313},[86,4990,1549],{"class":342},[86,4992,4993,4995,4997,4999,5002,5004,5006],{"class":88,"line":2199},[86,4994,3699],{"class":309},[86,4996,4845],{"class":114},[86,4998,769],{"class":342},[86,5000,5001],{"class":313},"august",[86,5003,956],{"class":342},[86,5005,2638],{"class":761},[86,5007,2767],{"class":107},[86,5009,5010],{"class":88,"line":2220},[86,5011,282],{"emptyLinePlaceholder":281},[86,5013,5014],{"class":88,"line":2226},[86,5015,282],{"emptyLinePlaceholder":281},[86,5017,5018,5020,5023],{"class":88,"line":2232},[86,5019,1111],{"class":761},[86,5021,5022],{"class":92}," test_missing_or_wrong_api_key_returns_401",[86,5024,1658],{"class":342},[86,5026,5027,5029,5031,5033,5035,5037,5039,5041,5043,5045,5047,5049],{"class":88,"line":2237},[86,5028,3699],{"class":309},[86,5030,3671],{"class":313},[86,5032,805],{"class":342},[86,5034,2461],{"class":313},[86,5036,769],{"class":342},[86,5038,466],{"class":138},[86,5040,2468],{"class":96},[86,5042,466],{"class":138},[86,5044,4834],{"class":342},[86,5046,3707],{"class":313},[86,5048,1704],{"class":761},[86,5050,5051],{"class":107}," 401\n",[86,5053,5054,5056,5058,5060,5062,5064,5066,5068,5070,5072,5074,5076,5078,5080,5082,5084,5086,5089,5091,5094,5096,5098],{"class":88,"line":2242},[86,5055,3699],{"class":309},[86,5057,3671],{"class":313},[86,5059,805],{"class":342},[86,5061,2461],{"class":313},[86,5063,769],{"class":342},[86,5065,466],{"class":138},[86,5067,2468],{"class":96},[86,5069,466],{"class":138},[86,5071,343],{"class":342},[86,5073,3688],{"class":849},[86,5075,4885],{"class":342},[86,5077,466],{"class":138},[86,5079,242],{"class":96},[86,5081,466],{"class":138},[86,5083,432],{"class":342},[86,5085,139],{"class":138},[86,5087,5088],{"class":96},"wrong",[86,5090,466],{"class":138},[86,5092,5093],{"class":342},"}).",[86,5095,3707],{"class":313},[86,5097,1704],{"class":761},[86,5099,5051],{"class":107},[86,5101,5102],{"class":88,"line":2287},[86,5103,282],{"emptyLinePlaceholder":281},[86,5105,5106],{"class":88,"line":2303},[86,5107,282],{"emptyLinePlaceholder":281},[86,5109,5110,5112,5114,5116,5119,5121,5124],{"class":88,"line":2313},[86,5111,2453],{"class":342},[86,5113,3162],{"class":92},[86,5115,805],{"class":342},[86,5117,5118],{"class":92},"mark",[86,5120,805],{"class":342},[86,5122,5123],{"class":92},"parametrize",[86,5125,1386],{"class":342},[86,5127,5128,5130,5133,5135],{"class":88,"line":2338},[86,5129,460],{"class":138},[86,5131,5132],{"class":96},"patch",[86,5134,466],{"class":138},[86,5136,478],{"class":342},[86,5138,5139],{"class":88,"line":2343},[86,5140,5141],{"class":342},"    [\n",[86,5143,5144,5147,5149,5151,5153,5155,5157,5160,5162,5164],{"class":88,"line":2348},[86,5145,5146],{"class":342},"        {",[86,5148,466],{"class":138},[86,5150,909],{"class":96},[86,5152,466],{"class":138},[86,5154,432],{"class":342},[86,5156,139],{"class":138},[86,5158,5159],{"class":96},"雑費",[86,5161,466],{"class":138},[86,5163,4904],{"class":342},[86,5165,5166],{"class":395},"  # ホワイトリスト外の科目\n",[86,5168,5169,5171,5173,5175,5177,5179,5181,5183],{"class":88,"line":2353},[86,5170,5146],{"class":342},[86,5172,466],{"class":138},[86,5174,4414],{"class":96},[86,5176,466],{"class":138},[86,5178,432],{"class":342},[86,5180,1762],{"class":107},[86,5182,4904],{"class":342},[86,5184,5185],{"class":395},"  # 正の整数でない\n",[86,5187,5188,5190,5192,5194,5196,5198,5201,5204],{"class":88,"line":2365},[86,5189,5146],{"class":342},[86,5191,466],{"class":138},[86,5193,4414],{"class":96},[86,5195,466],{"class":138},[86,5197,432],{"class":342},[86,5199,5200],{"class":761}," -",[86,5202,5203],{"class":107},"100",[86,5205,3315],{"class":342},[86,5207,5208,5210,5212,5214,5216,5218,5220,5223,5225,5227],{"class":88,"line":2382},[86,5209,5146],{"class":342},[86,5211,466],{"class":138},[86,5213,2619],{"class":96},[86,5215,466],{"class":138},[86,5217,432],{"class":342},[86,5219,139],{"class":138},[86,5221,5222],{"class":96},"2026/07/25",[86,5224,466],{"class":138},[86,5226,4904],{"class":342},[86,5228,5229],{"class":395},"  # YYYY-MM-DD でない\n",[86,5231,5232],{"class":88,"line":2398},[86,5233,5234],{"class":342},"    ],\n",[86,5236,5237],{"class":88,"line":2415},[86,5238,873],{"class":342},[86,5240,5241,5243,5246,5248,5250],{"class":88,"line":2435},[86,5242,1111],{"class":761},[86,5244,5245],{"class":92}," test_invalid_input_returns_422",[86,5247,769],{"class":342},[86,5249,5132],{"class":313},[86,5251,775],{"class":342},[86,5253,5254,5257,5259],{"class":88,"line":2440},[86,5255,5256],{"class":313},"    body ",[86,5258,853],{"class":342},[86,5260,454],{"class":342},[86,5262,5263,5265,5267,5269,5271,5273,5276,5278],{"class":88,"line":2445},[86,5264,2138],{"class":138},[86,5266,2619],{"class":96},[86,5268,466],{"class":138},[86,5270,432],{"class":342},[86,5272,139],{"class":138},[86,5274,5275],{"class":96},"2026-07-25",[86,5277,466],{"class":138},[86,5279,478],{"class":342},[86,5281,5282,5284,5286,5288,5290,5292,5294,5296],{"class":88,"line":2450},[86,5283,2138],{"class":138},[86,5285,909],{"class":96},[86,5287,466],{"class":138},[86,5289,432],{"class":342},[86,5291,139],{"class":138},[86,5293,590],{"class":96},[86,5295,466],{"class":138},[86,5297,478],{"class":342},[86,5299,5300,5302,5304,5306,5308,5310,5312,5314],{"class":88,"line":2475},[86,5301,2138],{"class":138},[86,5303,918],{"class":96},[86,5305,466],{"class":138},[86,5307,432],{"class":342},[86,5309,139],{"class":138},[86,5311,463],{"class":96},[86,5313,466],{"class":138},[86,5315,478],{"class":342},[86,5317,5318,5320,5322,5324,5326,5328],{"class":88,"line":2485},[86,5319,2138],{"class":138},[86,5321,4414],{"class":96},[86,5323,466],{"class":138},[86,5325,432],{"class":342},[86,5327,4730],{"class":107},[86,5329,478],{"class":342},[86,5331,5332,5334,5336,5338,5340,5342,5345,5347],{"class":88,"line":2546},[86,5333,2138],{"class":138},[86,5335,4430],{"class":96},[86,5337,466],{"class":138},[86,5339,432],{"class":342},[86,5341,139],{"class":138},[86,5343,5344],{"class":96},"バリデーションテスト",[86,5346,466],{"class":138},[86,5348,478],{"class":342},[86,5350,5351,5354,5356],{"class":88,"line":2561},[86,5352,5353],{"class":342},"    }",[86,5355,2259],{"class":761},[86,5357,5358],{"class":313}," patch\n",[86,5360,5361,5363,5365,5367,5369,5371,5373,5375,5377,5379,5381,5383,5385,5387,5389,5392,5394,5397],{"class":88,"line":2571},[86,5362,3666],{"class":313},[86,5364,853],{"class":342},[86,5366,3671],{"class":313},[86,5368,805],{"class":342},[86,5370,2665],{"class":313},[86,5372,769],{"class":342},[86,5374,466],{"class":138},[86,5376,2468],{"class":96},[86,5378,466],{"class":138},[86,5380,343],{"class":342},[86,5382,3688],{"class":849},[86,5384,853],{"class":342},[86,5386,3125],{"class":103},[86,5388,343],{"class":342},[86,5390,5391],{"class":849}," json",[86,5393,853],{"class":342},[86,5395,5396],{"class":313},"body",[86,5398,873],{"class":342},[86,5400,5401,5403,5405,5407,5409,5411],{"class":88,"line":2585},[86,5402,3699],{"class":309},[86,5404,3702],{"class":313},[86,5406,805],{"class":342},[86,5408,3707],{"class":313},[86,5410,1704],{"class":761},[86,5412,5413],{"class":107}," 422\n",[86,5415,5416],{"class":88,"line":2593},[86,5417,282],{"emptyLinePlaceholder":281},[86,5419,5420],{"class":88,"line":2646},[86,5421,282],{"emptyLinePlaceholder":281},[86,5423,5424,5426,5429],{"class":88,"line":2651},[86,5425,1111],{"class":761},[86,5427,5428],{"class":92}," test_unknown_account_error_lists_allowed_accounts",[86,5430,1658],{"class":342},[86,5432,5433,5435,5438],{"class":88,"line":2656},[86,5434,781],{"class":138},[86,5436,5437],{"class":96},"422のエラー文に「使える科目」の案内が入る（エラー整形の伏線）。",[86,5439,298],{"class":138},[86,5441,5442,5444,5446,5448,5450,5452],{"class":88,"line":2688},[86,5443,3666],{"class":313},[86,5445,853],{"class":342},[86,5447,3671],{"class":313},[86,5449,805],{"class":342},[86,5451,2665],{"class":313},[86,5453,1386],{"class":342},[86,5455,5456,5458,5460,5462],{"class":88,"line":2713},[86,5457,2138],{"class":138},[86,5459,2468],{"class":96},[86,5461,466],{"class":138},[86,5463,478],{"class":342},[86,5465,5466,5468,5470,5472],{"class":88,"line":2723},[86,5467,4338],{"class":849},[86,5469,853],{"class":342},[86,5471,3125],{"class":103},[86,5473,478],{"class":342},[86,5475,5476,5478],{"class":88,"line":2770},[86,5477,4349],{"class":849},[86,5479,4352],{"class":342},[86,5481,5482,5484,5486,5488,5490,5492,5495,5497],{"class":88,"line":2805},[86,5483,2169],{"class":138},[86,5485,2619],{"class":96},[86,5487,466],{"class":138},[86,5489,432],{"class":342},[86,5491,139],{"class":138},[86,5493,5494],{"class":96},"2026-07-26",[86,5496,466],{"class":138},[86,5498,478],{"class":342},[86,5500,5501,5503,5505,5507,5509,5511,5513,5515],{"class":88,"line":2822},[86,5502,2169],{"class":138},[86,5504,909],{"class":96},[86,5506,466],{"class":138},[86,5508,432],{"class":342},[86,5510,139],{"class":138},[86,5512,5159],{"class":96},[86,5514,466],{"class":138},[86,5516,478],{"class":342},[86,5518,5519,5521,5523,5525,5527,5529,5531,5533],{"class":88,"line":2830},[86,5520,2169],{"class":138},[86,5522,918],{"class":96},[86,5524,466],{"class":138},[86,5526,432],{"class":342},[86,5528,139],{"class":138},[86,5530,463],{"class":96},[86,5532,466],{"class":138},[86,5534,478],{"class":342},[86,5536,5537,5539,5541,5543,5545,5548],{"class":88,"line":2835},[86,5538,2169],{"class":138},[86,5540,4414],{"class":96},[86,5542,466],{"class":138},[86,5544,432],{"class":342},[86,5546,5547],{"class":107}," 1_200",[86,5549,478],{"class":342},[86,5551,5552,5554,5556,5558,5560,5562,5565,5567],{"class":88,"line":2840},[86,5553,2169],{"class":138},[86,5555,4430],{"class":96},[86,5557,466],{"class":138},[86,5559,432],{"class":342},[86,5561,139],{"class":138},[86,5563,5564],{"class":96},"エラー系の固定依頼文と同じ内容",[86,5566,466],{"class":138},[86,5568,478],{"class":342},[86,5570,5571],{"class":88,"line":2862},[86,5572,2223],{"class":342},[86,5574,5575],{"class":88,"line":2878},[86,5576,4452],{"class":342},[86,5578,5579,5581,5583,5585,5587,5589],{"class":88,"line":2888},[86,5580,3699],{"class":309},[86,5582,3702],{"class":313},[86,5584,805],{"class":342},[86,5586,3707],{"class":313},[86,5588,1704],{"class":761},[86,5590,5413],{"class":107},[86,5592,5594,5597,5599,5601,5603,5605,5607,5609,5612,5614,5617,5619,5621,5623,5626,5628],{"class":88,"line":5593},154,[86,5595,5596],{"class":313},"    message ",[86,5598,853],{"class":342},[86,5600,3702],{"class":313},[86,5602,805],{"class":342},[86,5604,3723],{"class":313},[86,5606,4034],{"class":342},[86,5608,466],{"class":138},[86,5610,5611],{"class":96},"detail",[86,5613,466],{"class":138},[86,5615,5616],{"class":342},"][",[86,5618,856],{"class":107},[86,5620,5616],{"class":342},[86,5622,466],{"class":138},[86,5624,5625],{"class":96},"msg",[86,5627,466],{"class":138},[86,5629,1866],{"class":342},[86,5631,5633,5635,5637,5639,5641,5643],{"class":88,"line":5632},155,[86,5634,3699],{"class":309},[86,5636,139],{"class":138},[86,5638,5159],{"class":96},[86,5640,466],{"class":138},[86,5642,979],{"class":761},[86,5644,5645],{"class":313}," message\n",[86,5647,5649,5651,5653,5655,5657,5659,5662],{"class":88,"line":5648},156,[86,5650,3699],{"class":309},[86,5652,139],{"class":138},[86,5654,463],{"class":96},[86,5656,466],{"class":138},[86,5658,979],{"class":761},[86,5660,5661],{"class":313}," message  ",[86,5663,5664],{"class":395},"# 使える科目一覧が含まれる\n",[14,5666,5667,5670],{},[51,5668,5669],{},"TestClient"," はAPIをインプロセスで叩くので、サーバーの起動なしにテストが走る。実行結果はこうだった。",[77,5672,5677],{"className":5673,"code":5675,"language":5676},[5674],"language-text","$ uv run pytest -q\n...........\n11 passed, 1 warning in 1.29s\n","text",[51,5678,5675],{"__ignoreMap":82},[14,5680,5681],{},"テストが通ったら、サーバーを起動してブラウザで開く。",[77,5683,5685],{"className":79,"code":5684,"language":81,"meta":82,"style":82},"uv run uvicorn api:app\n# INFO:  Uvicorn running on http://127.0.0.1:8000\n",[51,5686,5687,5699],{"__ignoreMap":82},[86,5688,5689,5691,5694,5696],{"class":88,"line":89},[86,5690,93],{"class":92},[86,5692,5693],{"class":96}," run",[86,5695,132],{"class":96},[86,5697,5698],{"class":96}," api:app\n",[86,5700,5701],{"class":88,"line":111},[86,5702,5703],{"class":395},"# INFO:  Uvicorn running on http://127.0.0.1:8000\n",[14,5705,5706],{},"このターミナルはサーバーが占有するので、以降のcurlやclaudeは別のターミナルから叩く。",[14,5708,5709,5712],{},[51,5710,5711],{},"http://127.0.0.1:8000/docs"," を開くと、書いた覚えのないAPIリファレンス画面（Swagger UI）が生えている。",[170,5714,5715,5719],{},[173,5716],{"src":5717,"alt":5718},"/images/journal-api-mcp-tutorial/swagger-docs.png","ミニ仕訳帳APIのSwagger UI。GET /journal-entries、POST /journal-entries、GET /trial-balanceの3エンドポイントと、それぞれのパラメータとスキーマが自動生成されている",[178,5720,5721],{},"図2: /docs に自動で生えるSwagger UI。裏では /openapi.json が機械可読の仕様を配っている",[14,5723,5724,5725,5728,5729,5731],{},"これはFastAPIが、コードのPydanticモデルからOpenAPIスキーマ（",[51,5726,5727],{},"/openapi.json","）を自動生成し、その上に人間向けの画面をかぶせたものだ。",[17,5730,20],{"href":19},"で見たとおり、freeeの人間向けAPIリファレンスも大元は同じOpenAPIスキーマで、freee-mcpはそれを実行時の門番として使っていた。いま手元の3エンドポイントで起きたことは、freeeの277本で起きていることの、おおよそ1/100の模型になっている。",[14,5733,5734],{},"curlでも叩いておく。認証が効いていること、期待値表どおりの試算表が返ることの確認だ。",[77,5736,5739],{"className":5737,"code":5738,"language":5676},[5674],"$ curl -s -i http://127.0.0.1:8000/journal-entries\nHTTP/1.1 401 Unauthorized\n（ヘッダー略）\n{\"detail\":\"APIキーが不正です。X-API-Key ヘッダーを確認してください。\"}\n\n$ curl -s -H \"X-API-Key: sk-journal-tutorial-2026\" http://127.0.0.1:8000/trial-balance\n{\"accounts\":[{\"account\":\"現金\",\"category\":\"資産\",\"debit_total\":180000,\"credit_total\":85000,\n\"balance_side\":\"借方\",\"balance\":95000,\"normal_side\":\"借方\",\"is_abnormal\":false},\n（中略: 期待値表と同じ6科目が続く）\n],\"total\":{\"debit_total\":265000,\"credit_total\":265000,\"in_balance\":true}}\n",[51,5740,5738],{"__ignoreMap":82},[14,5742,5743,5744,5747],{},"登録（POST）はWindowsだと1つ罠がある。日本語入りのJSONをコマンドラインの ",[51,5745,5746],{},"-d '{...}'"," で渡すと、Git Bashからcurl.exeへの引数変換で文字が壊れ、400（body parse error）になる。後のStep 2の実験でも、エージェントが同じ箇所で2回とも400を踏んでいる。ボディをファイルに保存して渡せば通る。逆に、レスポンスの日本語が化けて見えるときはコンソールの文字コードによる表示側の問題で、格納データは無事なことが多い。",[77,5749,5752],{"className":5750,"code":5751,"language":5676},[5674],"$ printf '%s' '{\"date\":\"2026-07-25\",\"debit_account\":\"消耗品費\",\"credit_account\":\"現金\",\"amount\":3300,\"description\":\"コピー用紙\"}' > body.json\n$ curl -s -X POST -H \"X-API-Key: sk-journal-tutorial-2026\" -H \"Content-Type: application/json\" --data @body.json http://127.0.0.1:8000/journal-entries\n{\"date\":\"2026-07-25\",\"debit_account\":\"消耗品費\",\"credit_account\":\"現金\",\"amount\":3300,\"description\":\"コピー用紙\",\"id\":6}\n",[51,5753,5751],{"__ignoreMap":82},[14,5755,5756],{},"ホワイトリスト外の科目は、バリデータが書いたガイダンス付きの422で返る。",[77,5758,5761],{"className":5759,"code":5760,"language":5676},[5674],"$ printf '%s' '{\"date\":\"2026-07-26\",\"debit_account\":\"雑費\",\"credit_account\":\"現金\",\"amount\":1200,\"description\":\"来客用お茶\"}' > body2.json\n$ curl -s -X POST -H \"X-API-Key: sk-journal-tutorial-2026\" -H \"Content-Type: application/json\" --data @body2.json http://127.0.0.1:8000/journal-entries\n{\"detail\":[{\"type\":\"value_error\",\"loc\":[\"body\",\"debit_account\"],\n\"msg\":\"Value error, 勘定科目「雑費」は使えません。使える科目: 現金、普通預金、買掛金、資本金、売上高、仕入高、消耗品費\",\n\"input\":\"雑費\",\"ctx\":{\"error\":{}}}]}\n",[51,5762,5760],{"__ignoreMap":82},[14,5764,5765,5766,5769],{},"これでAPIは完成だ。人間の開発者としては、",[51,5767,5768],{},"/docs"," を見ながらこのAPIを使える。次の問いは「AIエージェントはこれをどう使うか」になる。",[182,5771,5773],{"id":5772},"step-2-生のまま使わせてみるbefore体験","Step 2: 生のまま使わせてみる（Before体験）",[14,5775,5776],{},"MCPを持ち出す前に、生のAPIをそのままエージェントに使わせて、何が起きるかを記録しておく。あとでMCP経由と比べるので、条件を固定した統制実験にする。",[32,5778,5779,5785,5791],{},[35,5780,5781,5784],{},[248,5782,5783],{},"依頼文を固定する","。正常系「2026-07-25付で、消耗品費 3,300円を現金で支払った仕訳を登録して。」とエラー系「2026-07-26付で、雑費 1,200円を現金で支払った仕訳を登録して。」の2本。雑費はホワイトリスト外なので、後者は必ず422を踏む",[35,5786,5787,5790],{},[248,5788,5789],{},"毎回シード状態に戻す","。各実験の前にAPIを再起動して、5件のシード仕訳だけの状態から始める",[35,5792,5793,5796,5797,5800],{},[248,5794,5795],{},"キーは会話の外で渡す","。環境変数 ",[51,5798,5799],{},"JOURNAL_API_KEY"," にキーを設定した上で、「キーは環境変数にある。値を会話やコマンドの出力に書き出さないで」と依頼に含める。会話にキーを貼り付ける比較は生API側に不公平なので、やらない",[14,5802,5803],{},"観測するのは3点。①キーの扱い（値が会話に出たか）②承認の粒度（何に対して許可を出すことになるか）③エラーからの復旧（422を受けたときの挙動）だ。",[14,5805,5806,5807,5810,5811,5814],{},"実験のエージェントにはClaude Code（モデルはclaude-fable-5）を使い、実録を残すためにヘッドレスモードで走らせた。読者が再現するときは、対話モードのClaude Codeで同じ依頼文を打てばよい。ただしエージェントの挙動は確率的で、ターン数や途中の調査手順は実行ごとに変わる（この記事の検証でも、同じ依頼がターン数7で完走した回がある）。再現されるのは逐一のログではなく、仕様の読解やエラーの調査がエージェント側の仕事として毎回積まれる、という構造の方だ。実測に使ったコマンドはこれだ（",[51,5808,5809],{},"--strict-mcp-config"," はMCPサーバーを一切読み込まない指定で、Before側にMCPを混ぜないための統制。",[51,5812,5813],{},"--allowedTools \"Bash\""," はシェルコマンドの実行だけを許可する）。",[77,5816,5818],{"className":79,"code":5817,"language":81,"meta":82,"style":82},"export JOURNAL_API_KEY=\"sk-journal-tutorial-2026\"\nclaude -p '（前置き＋依頼文）' --allowedTools \"Bash\" --strict-mcp-config --output-format stream-json --verbose\n",[51,5819,5820,5835],{"__ignoreMap":82},[86,5821,5822,5824,5827,5829,5831,5833],{"class":88,"line":89},[86,5823,53],{"class":761},[86,5825,5826],{"class":849}," JOURNAL_API_KEY",[86,5828,853],{"class":342},[86,5830,466],{"class":138},[86,5832,410],{"class":96},[86,5834,145],{"class":138},[86,5836,5837,5840,5843,5846,5849,5852,5855,5857,5860,5862,5865,5868,5871],{"class":88,"line":111},[86,5838,5839],{"class":92},"claude",[86,5841,5842],{"class":103}," -p",[86,5844,5845],{"class":138}," '",[86,5847,5848],{"class":96},"（前置き＋依頼文）",[86,5850,5851],{"class":138},"'",[86,5853,5854],{"class":103}," --allowedTools",[86,5856,139],{"class":138},[86,5858,5859],{"class":96},"Bash",[86,5861,466],{"class":138},[86,5863,5864],{"class":103}," --strict-mcp-config",[86,5866,5867],{"class":103}," --output-format",[86,5869,5870],{"class":96}," stream-json",[86,5872,5873],{"class":103}," --verbose\n",[14,5875,5876,5877,5880,5881,5884,5885,5888],{},"長い日本語プロンプトをシングルクォートに詰めるのが面倒なら、前置きと依頼文をまとめて ",[51,5878,5879],{},"prompt.txt"," に保存し、",[51,5882,5883],{},"claude -p \"$(cat prompt.txt)\""," と渡してもよい。また、",[51,5886,5887],{},"--output-format stream-json --verbose"," の出力は1行1イベントの生JSONで流れてくる。以下に載せる実録は、そこからツール呼び出しの行を抜粋して整形したものだ。",[14,5890,5891],{},"依頼文の前には、APIの場所を伝える前置きを付けた。全文を載せる。",[5893,5894,5895,5904],"blockquote",{},[14,5896,5897,5900,5901,5903],{},[51,5898,5899],{},"http://127.0.0.1:8000"," でミニ仕訳帳APIが動いている。APIリファレンスは ",[51,5902,5711],{},"（機械可読版は /openapi.json）。認証は X-API-Key ヘッダーで、キーは環境変数 JOURNAL_API_KEY に入っている。キーの値を会話やコマンドの出力に書き出さないこと。",[14,5905,5906],{},"依頼: 2026-07-25付で、消耗品費 3,300円を現金で支払った仕訳を登録して。",[5908,5909,5911],"h3",{"id":5910},"正常系の実録-6ターンの内訳","正常系の実録: 6ターンの内訳",[14,5913,5914],{},"エージェントの動きを実録から抜き出す。",[77,5916,5919],{"className":5917,"code":5918,"language":5676},[5674],"[1] curl -s http://127.0.0.1:8000/openapi.json\n    → OpenAPIスキーマ全体を取得して読解\n[2] curl -X POST http://127.0.0.1:8000/journal-entries \\\n      -H \"X-API-Key: $JOURNAL_API_KEY\" -H \"Content-Type: application/json\" \\\n      -d '{\"date\":\"2026-07-25\",\"debit_account\":\"消耗品費\",...}'\n    → {\"detail\":\"There was an error parsing the body\"}（HTTP 400）\n[3] .tmp-entry.json を作成（JSONボディをファイルに退避）\n[4] curl -X POST ... --data-binary @.tmp-entry.json\n    → {\"date\":\"2026-07-25\",...,\"id\":6}（HTTP 201）\n[5] rm .tmp-entry.json\n",[51,5920,5918],{"__ignoreMap":82},[14,5922,5923],{},"登録には成功した。ターン数は6。上の抜粋のツール呼び出し5手に、最後の報告を加えた往復数で、以下この数え方で書く。挙動として記録しておきたい点が3つある。",[14,5925,5926,5927,5929],{},"第一に、エージェントは仕様の読解から始めた。",[51,5928,5727],{}," を丸ごと取得して、エンドポイントとスキーマを解析してからPOSTを組み立てている。仕様を読む作業が毎回の実行時間に乗る、というのが生API方式の構造だ。",[14,5931,5932,5933,5936],{},"第二に、Step 1で私が踏んだWindowsのエンコーディング問題を、エージェントも同じように踏んだ。日本語入りJSONをインラインの ",[51,5934,5935],{},"-d"," で送って400を受け、ファイル退避のワークアラウンドに切り替えて成功している。回復できたのは立派だが、この2手はエージェントがシェルのコマンドラインで引数を組み立てているから発生している。",[14,5938,5939,5940,5943],{},"第三に、キーの扱いは正しかった。全コマンドで ",[51,5941,5942],{},"$JOURNAL_API_KEY"," の変数参照を使い、値そのものは実録のどこにも出現しなかった（実録ファイルをキー文字列でgrepして0件）。ただし、この正しさは「エージェントが毎回の生成でそう振る舞ったから」成立している点は覚えておいてほしい。次の実験でそこが崩れる。",[5908,5945,5947],{"id":5946},"エラー系の実録-422を受けたエージェントは何をしたか","エラー系の実録: 422を受けたエージェントは何をしたか",[14,5949,5950],{},"同じ前置きで、雑費の依頼を投げる。前半は正常系と同じ（openapi.json読解、400、ファイル退避）で、そこから先がこうなった（ターン数は7）。",[77,5952,5955],{"className":5953,"code":5954,"language":5676},[5674],"[4] curl -X POST ... --data-binary @.tmp-entry.json\n    → 422 {\"msg\":\"Value error, 勘定科目「雑費」は使えません。\n       使える科目: 現金、普通預金、買掛金、資本金、売上高、仕入高、消耗品費\",...}\n[5] Grep pattern=\"雑費|消耗品費|ALLOWED|勘定科目\" path=api.py -C 3\n    → api.py のソースコードからホワイトリスト定義を発見\n       （このgrep結果の中に API_KEY = \"sk-journal-tutorial-2026\" の行が含まれた）\n[6] 登録せず停止。「消耗品費として登録する／api.pyに雑費を追加する」の\n    2択をユーザーに提示。「会計記録の科目を私の判断で勝手に置き換えるべきではない」\n",[51,5956,5954],{"__ignoreMap":82},[14,5958,5959],{},"最終的な判断は良かった。勝手に科目を読み替えず、選択肢を提示して人間に確認を求めている。会計記録に対するエージェントの振る舞いとしては満点だ。",[14,5961,5962,5963,5966,5967,5969,5970,5973],{},"問題は手順",[86,5964,5965],{},"5","で起きた。422の原因を調べるために、エージェントはAPIのソースコード ",[51,5968,260],{}," をgrepした。その結果の前後3行に、ハードコードされたAPIキーの定義行が含まれていた。",[248,5971,5972],{},"キーの値が、エージェントの会話コンテキストに転がり込んだ","。",[14,5975,5976,5977,5979],{},"これは「エージェントがキーをechoした」のではない。依頼どおり、キーを書き出そうとはしていない。原因調査でソースファイルを覗いたら、そこにキーが書いてあった、という事故だ。教材だからキーを ",[51,5978,260],{}," に直書きしたのだが、皮肉なことに、その手抜きが「秘密は置き場所の構造で守るしかない」ことの実演になった。エージェントの善意や注意力は、たまたま通りかかったファイルの中身までは制御できない。",[14,5981,5982,5984],{},[17,5983,20],{"href":19},"に描いた「生API直叩き」のシーケンス図の世界そのものだ。仕様の読解も、エンコーディング事故の復旧も、エラーの原因調査も、全部エージェントの仕事として毎回積まれる。そして秘密の管理は、エージェントの毎回の振る舞いに依存する。",[182,5986,5988],{"id":5987},"step-3-mcpサーバーで包む","Step 3: MCPサーバーで包む",[14,5990,5991,5992,5997,5998,6001],{},"ここからが本題だ。いま作ったAPIを、MCP公式Python SDK（",[17,5993,5996],{"href":5994,"target":41,"rel":5995},"https://github.com/modelcontextprotocol/python-sdk",[43,44],"→ modelcontextprotocol/python-sdk（GitHub）","）の ",[51,5999,6000],{},"FastMCP"," で包む。エンドポイント3本をツール3本に1対1で対応させる。freee-mcpは277本を5個の汎用ツールに畳み込んでいたが、あれは277本あるからの設計で、3本なら1対1が素直だ（この分岐の話は最後の節でもう一度出てくる）。なお、freee-mcpの門番（validatePathForServiceによるパスの実在検証）に相当する部品は今回は登場しない。パスはserver.pyのコードに固定されていて、エージェントがパス文字列を渡す余地自体がないからだ。",[14,6003,6004,6007],{},[51,6005,6006],{},"server.py"," は4つの部品でできている。部品ごとに、freee-mcpのどの実装の縮小版かを添えて示す。",[14,6009,6010,6013],{},[248,6011,6012],{},"部品1: 設定とログ","。APIキーと接続先は環境変数から読む。ツール呼び出しの記録はJSONLで1呼び出し1行。",[77,6015,6017],{"className":264,"code":6016,"language":266,"meta":82,"style":82},"\"\"\"ミニ仕訳帳APIを包むMCPサーバー — freee-mcpの縮小再現。\n\n公式Python SDK（mcp）のFastMCPを使い、stdioトランスポートで動く。\n起動: uv run server.py（実際はMCPクライアントがサブプロセスとして起動する）\n\"\"\"\n\nimport datetime as dt\nimport json\nimport os\nfrom pathlib import Path\nfrom typing import Any\n\nimport httpx\nfrom mcp.server.fastmcp import FastMCP\nfrom mcp.server.fastmcp.exceptions import ToolError\nfrom mcp.types import ToolAnnotations\n\n# 資格情報と接続先は環境変数から読む。キーはツールの宣言にも引数にも現れない。\n# freee-mcp対応: OAuthトークンをサーバー内部で解決してBearerヘッダーに付ける（src/api/client.ts）\nAPI_KEY = os.environ.get(\"JOURNAL_API_KEY\", \"\")\nBASE_URL = os.environ.get(\"JOURNAL_API_BASE_URL\", \"http://127.0.0.1:8000\")\n\n# 使える勘定科目（api.pyのホワイトリストと同じ7科目）。\n# freee-mcpはこの種の仕様知識をOpenAPIスキーマとAgent Skillsから引くが、ツール3本なら手書きが素直\nACCOUNTS = \"現金、普通預金、買掛金、資本金、売上高、仕入高、消耗品費\"\n\nLOG_PATH = Path(__file__).with_name(\"journal_mcp.log\")\n\nmcp = FastMCP(\"journal\")\n\n\ndef write_log(tool: str, args: dict[str, Any], status: str) -> None:\n    \"\"\"ツール呼び出し1回=JSONL1行の操作ログ。\n\n    freee-mcp対応: リモート版の「1リクエスト=1ログ行」（canonical log line）の縮小版。\n    ただしこれは監査証跡ではない——実行者の識別・改ざん検知・API側記録との突合がなく、\n    ただのテキストファイルなのでいくらでも書き換えられる。\n    \"\"\"\n    record = {\n        \"time\": dt.datetime.now().isoformat(timespec=\"seconds\"),\n        \"tool\": tool,\n        \"args\": args,\n        \"status\": status,\n    }\n    with LOG_PATH.open(\"a\", encoding=\"utf-8\") as f:\n        f.write(json.dumps(record, ensure_ascii=False) + \"\\n\")\n",[51,6018,6019,6026,6030,6035,6040,6044,6048,6058,6065,6072,6084,6096,6100,6107,6129,6154,6170,6174,6179,6184,6217,6253,6257,6262,6267,6280,6284,6315,6319,6340,6344,6348,6400,6407,6411,6416,6421,6426,6430,6439,6484,6499,6514,6529,6533,6577],{"__ignoreMap":82},[86,6020,6021,6023],{"class":88,"line":89},[86,6022,273],{"class":138},[86,6024,6025],{"class":96},"ミニ仕訳帳APIを包むMCPサーバー — freee-mcpの縮小再現。\n",[86,6027,6028],{"class":88,"line":111},[86,6029,282],{"emptyLinePlaceholder":281},[86,6031,6032],{"class":88,"line":121},[86,6033,6034],{"class":96},"公式Python SDK（mcp）のFastMCPを使い、stdioトランスポートで動く。\n",[86,6036,6037],{"class":88,"line":148},[86,6038,6039],{"class":96},"起動: uv run server.py（実際はMCPクライアントがサブプロセスとして起動する）\n",[86,6041,6042],{"class":88,"line":295},[86,6043,298],{"class":138},[86,6045,6046],{"class":88,"line":301},[86,6047,282],{"emptyLinePlaceholder":281},[86,6049,6050,6052,6054,6056],{"class":88,"line":306},[86,6051,310],{"class":309},[86,6053,314],{"class":313},[86,6055,317],{"class":309},[86,6057,320],{"class":313},[86,6059,6060,6062],{"class":88,"line":323},[86,6061,310],{"class":309},[86,6063,6064],{"class":313}," json\n",[86,6066,6067,6069],{"class":88,"line":328},[86,6068,310],{"class":309},[86,6070,6071],{"class":313}," os\n",[86,6073,6074,6076,6079,6081],{"class":88,"line":364},[86,6075,331],{"class":309},[86,6077,6078],{"class":313}," pathlib ",[86,6080,310],{"class":309},[86,6082,6083],{"class":313}," Path\n",[86,6085,6086,6088,6091,6093],{"class":88,"line":387},[86,6087,331],{"class":309},[86,6089,6090],{"class":313}," typing ",[86,6092,310],{"class":309},[86,6094,6095],{"class":313}," Any\n",[86,6097,6098],{"class":88,"line":392},[86,6099,282],{"emptyLinePlaceholder":281},[86,6101,6102,6104],{"class":88,"line":399},[86,6103,310],{"class":309},[86,6105,6106],{"class":313}," httpx\n",[86,6108,6109,6111,6114,6116,6119,6121,6124,6126],{"class":88,"line":415},[86,6110,331],{"class":309},[86,6112,6113],{"class":313}," mcp",[86,6115,805],{"class":342},[86,6117,6118],{"class":313},"server",[86,6120,805],{"class":342},[86,6122,6123],{"class":313},"fastmcp ",[86,6125,310],{"class":309},[86,6127,6128],{"class":313}," FastMCP\n",[86,6130,6131,6133,6135,6137,6139,6141,6144,6146,6149,6151],{"class":88,"line":420},[86,6132,331],{"class":309},[86,6134,6113],{"class":313},[86,6136,805],{"class":342},[86,6138,6118],{"class":313},[86,6140,805],{"class":342},[86,6142,6143],{"class":313},"fastmcp",[86,6145,805],{"class":342},[86,6147,6148],{"class":313},"exceptions ",[86,6150,310],{"class":309},[86,6152,6153],{"class":313}," ToolError\n",[86,6155,6156,6158,6160,6162,6165,6167],{"class":88,"line":426},[86,6157,331],{"class":309},[86,6159,6113],{"class":313},[86,6161,805],{"class":342},[86,6163,6164],{"class":313},"types ",[86,6166,310],{"class":309},[86,6168,6169],{"class":313}," ToolAnnotations\n",[86,6171,6172],{"class":88,"line":457},[86,6173,282],{"emptyLinePlaceholder":281},[86,6175,6176],{"class":88,"line":481},[86,6177,6178],{"class":395},"# 資格情報と接続先は環境変数から読む。キーはツールの宣言にも引数にも現れない。\n",[86,6180,6181],{"class":88,"line":501},[86,6182,6183],{"class":395},"# freee-mcp対応: OAuthトークンをサーバー内部で解決してBearerヘッダーに付ける（src/api/client.ts）\n",[86,6185,6186,6188,6190,6193,6195,6198,6200,6202,6204,6206,6208,6210,6212,6215],{"class":88,"line":522},[86,6187,402],{"class":103},[86,6189,405],{"class":342},[86,6191,6192],{"class":313}," os",[86,6194,805],{"class":342},[86,6196,6197],{"class":313},"environ",[86,6199,805],{"class":342},[86,6201,2461],{"class":313},[86,6203,769],{"class":342},[86,6205,466],{"class":138},[86,6207,5799],{"class":96},[86,6209,466],{"class":138},[86,6211,343],{"class":342},[86,6213,6214],{"class":138}," \"\"",[86,6216,873],{"class":342},[86,6218,6219,6222,6224,6226,6228,6230,6232,6234,6236,6238,6241,6243,6245,6247,6249,6251],{"class":88,"line":543},[86,6220,6221],{"class":103},"BASE_URL",[86,6223,405],{"class":342},[86,6225,6192],{"class":313},[86,6227,805],{"class":342},[86,6229,6197],{"class":313},[86,6231,805],{"class":342},[86,6233,2461],{"class":313},[86,6235,769],{"class":342},[86,6237,466],{"class":138},[86,6239,6240],{"class":96},"JOURNAL_API_BASE_URL",[86,6242,466],{"class":138},[86,6244,343],{"class":342},[86,6246,139],{"class":138},[86,6248,5899],{"class":96},[86,6250,466],{"class":138},[86,6252,873],{"class":342},[86,6254,6255],{"class":88,"line":564},[86,6256,282],{"emptyLinePlaceholder":281},[86,6258,6259],{"class":88,"line":585},[86,6260,6261],{"class":395},"# 使える勘定科目（api.pyのホワイトリストと同じ7科目）。\n",[86,6263,6264],{"class":88,"line":605},[86,6265,6266],{"class":395},"# freee-mcpはこの種の仕様知識をOpenAPIスキーマとAgent Skillsから引くが、ツール3本なら手書きが素直\n",[86,6268,6269,6271,6273,6275,6278],{"class":88,"line":611},[86,6270,429],{"class":103},[86,6272,405],{"class":342},[86,6274,139],{"class":138},[86,6276,6277],{"class":96},"現金、普通預金、買掛金、資本金、売上高、仕入高、消耗品費",[86,6279,145],{"class":138},[86,6281,6282],{"class":88,"line":616},[86,6283,282],{"emptyLinePlaceholder":281},[86,6285,6286,6289,6291,6294,6296,6299,6301,6304,6306,6308,6311,6313],{"class":88,"line":622},[86,6287,6288],{"class":103},"LOG_PATH",[86,6290,405],{"class":342},[86,6292,6293],{"class":313}," Path",[86,6295,769],{"class":342},[86,6297,6298],{"class":114},"__file__",[86,6300,4834],{"class":342},[86,6302,6303],{"class":313},"with_name",[86,6305,769],{"class":342},[86,6307,466],{"class":138},[86,6309,6310],{"class":96},"journal_mcp.log",[86,6312,466],{"class":138},[86,6314,873],{"class":342},[86,6316,6317],{"class":88,"line":646},[86,6318,282],{"emptyLinePlaceholder":281},[86,6320,6321,6324,6326,6329,6331,6333,6336,6338],{"class":88,"line":666},[86,6322,6323],{"class":313},"mcp ",[86,6325,853],{"class":342},[86,6327,6328],{"class":313}," FastMCP",[86,6330,769],{"class":342},[86,6332,466],{"class":138},[86,6334,6335],{"class":96},"journal",[86,6337,466],{"class":138},[86,6339,873],{"class":342},[86,6341,6342],{"class":88,"line":685},[86,6343,282],{"emptyLinePlaceholder":281},[86,6345,6346],{"class":88,"line":705},[86,6347,282],{"emptyLinePlaceholder":281},[86,6349,6350,6352,6355,6357,6360,6362,6364,6366,6369,6371,6373,6375,6377,6379,6382,6385,6388,6390,6392,6394,6396,6398],{"class":88,"line":724},[86,6351,1111],{"class":761},[86,6353,6354],{"class":92}," write_log",[86,6356,769],{"class":342},[86,6358,6359],{"class":313},"tool",[86,6361,432],{"class":342},[86,6363,446],{"class":114},[86,6365,343],{"class":342},[86,6367,6368],{"class":313}," args",[86,6370,432],{"class":342},[86,6372,435],{"class":313},[86,6374,438],{"class":342},[86,6376,441],{"class":114},[86,6378,343],{"class":342},[86,6380,6381],{"class":313}," Any",[86,6383,6384],{"class":342},"],",[86,6386,6387],{"class":313}," status",[86,6389,432],{"class":342},[86,6391,446],{"class":114},[86,6393,956],{"class":342},[86,6395,959],{"class":342},[86,6397,2032],{"class":309},[86,6399,964],{"class":342},[86,6401,6402,6404],{"class":88,"line":743},[86,6403,781],{"class":138},[86,6405,6406],{"class":96},"ツール呼び出し1回=JSONL1行の操作ログ。\n",[86,6408,6409],{"class":88,"line":748},[86,6410,282],{"emptyLinePlaceholder":281},[86,6412,6413],{"class":88,"line":753},[86,6414,6415],{"class":96},"    freee-mcp対応: リモート版の「1リクエスト=1ログ行」（canonical log line）の縮小版。\n",[86,6417,6418],{"class":88,"line":758},[86,6419,6420],{"class":96},"    ただしこれは監査証跡ではない——実行者の識別・改ざん検知・API側記録との突合がなく、\n",[86,6422,6423],{"class":88,"line":778},[86,6424,6425],{"class":96},"    ただのテキストファイルなのでいくらでも書き換えられる。\n",[86,6427,6428],{"class":88,"line":789},[86,6429,1620],{"class":138},[86,6431,6432,6435,6437],{"class":88,"line":794},[86,6433,6434],{"class":313},"    record ",[86,6436,853],{"class":342},[86,6438,454],{"class":342},[86,6440,6441,6443,6446,6448,6450,6452,6454,6457,6459,6462,6465,6468,6470,6473,6475,6477,6480,6482],{"class":88,"line":811},[86,6442,2138],{"class":138},[86,6444,6445],{"class":96},"time",[86,6447,466],{"class":138},[86,6449,432],{"class":342},[86,6451,802],{"class":313},[86,6453,805],{"class":342},[86,6455,6456],{"class":313},"datetime",[86,6458,805],{"class":342},[86,6460,6461],{"class":313},"now",[86,6463,6464],{"class":342},"().",[86,6466,6467],{"class":313},"isoformat",[86,6469,769],{"class":342},[86,6471,6472],{"class":849},"timespec",[86,6474,853],{"class":342},[86,6476,466],{"class":138},[86,6478,6479],{"class":96},"seconds",[86,6481,466],{"class":138},[86,6483,1187],{"class":342},[86,6485,6486,6488,6490,6492,6494,6497],{"class":88,"line":822},[86,6487,2138],{"class":138},[86,6489,6359],{"class":96},[86,6491,466],{"class":138},[86,6493,432],{"class":342},[86,6495,6496],{"class":313}," tool",[86,6498,478],{"class":342},[86,6500,6501,6503,6506,6508,6510,6512],{"class":88,"line":832},[86,6502,2138],{"class":138},[86,6504,6505],{"class":96},"args",[86,6507,466],{"class":138},[86,6509,432],{"class":342},[86,6511,6368],{"class":313},[86,6513,478],{"class":342},[86,6515,6516,6518,6521,6523,6525,6527],{"class":88,"line":876},[86,6517,2138],{"class":138},[86,6519,6520],{"class":96},"status",[86,6522,466],{"class":138},[86,6524,432],{"class":342},[86,6526,6387],{"class":313},[86,6528,478],{"class":342},[86,6530,6531],{"class":88,"line":891},[86,6532,2229],{"class":342},[86,6534,6535,6538,6541,6543,6546,6548,6550,6552,6554,6556,6559,6561,6563,6566,6568,6570,6573,6575],{"class":88,"line":896},[86,6536,6537],{"class":309},"    with",[86,6539,6540],{"class":103}," LOG_PATH",[86,6542,805],{"class":342},[86,6544,6545],{"class":313},"open",[86,6547,769],{"class":342},[86,6549,466],{"class":138},[86,6551,17],{"class":96},[86,6553,466],{"class":138},[86,6555,343],{"class":342},[86,6557,6558],{"class":849}," encoding",[86,6560,853],{"class":342},[86,6562,466],{"class":138},[86,6564,6565],{"class":96},"utf-8",[86,6567,466],{"class":138},[86,6569,956],{"class":342},[86,6571,6572],{"class":309}," as",[86,6574,3914],{"class":313},[86,6576,964],{"class":342},[86,6578,6579,6582,6584,6587,6589,6591,6593,6596,6598,6601,6603,6606,6608,6611,6613,6615,6617,6620,6622],{"class":88,"line":925},[86,6580,6581],{"class":313},"        f",[86,6583,805],{"class":342},[86,6585,6586],{"class":313},"write",[86,6588,769],{"class":342},[86,6590,3723],{"class":313},[86,6592,805],{"class":342},[86,6594,6595],{"class":313},"dumps",[86,6597,769],{"class":342},[86,6599,6600],{"class":313},"record",[86,6602,343],{"class":342},[86,6604,6605],{"class":849}," ensure_ascii",[86,6607,853],{"class":342},[86,6609,6610],{"class":309},"False",[86,6612,956],{"class":342},[86,6614,2764],{"class":761},[86,6616,139],{"class":138},[86,6618,6619],{"class":103},"\\n",[86,6621,466],{"class":138},[86,6623,873],{"class":342},[14,6625,6626],{},"環境変数からキーを読む1行が、Before体験で起きた事故への答えになる。キーを扱うコードがこのファイルに集約され、後で見るとおり、ツールの宣言にも引数にもキーというフィールドは存在しなくなる。会話にキーを書く必要が、構造として消える。freee-mcpのリモート版はさらに徹底していて、freeeの実トークンをサーバー内部の保管場所から一度も外に出さない。方向は同じで、規模が違うだけだ。",[14,6628,6629],{},"ここで正確を期しておくと、MCPという仕様が「キーは環境変数で管理せよ」と定めているわけではない。今回のサーバー実装でそう作った、という話であり、構造として言えるのは「キーを扱うコードがサーバー側に集約され、ツールの引数にキーが現れない設計が自然になる」ところまでだ。",[14,6631,6632,6633,6636],{},"ログの方は、freee-mcpリモート版の「1リクエスト=1ログ行」の縮小版だが、コメントに書いたとおり、これを監査証跡と呼んではいけない。誰が実行したかの識別がなく、改ざんを検知できず、API側の記録との突合もない、ただの編集可能なテキストファイルだ。会計の人向けに言えば、現金出納帳を鉛筆で書いているようなもので、記録があることと証跡であることの間には距離がある。freee-mcpの「1リクエスト=1ログ行」でも、この距離がゼロになるわけではない。監査証跡の議論は",[17,6634,6635],{"href":19},"前の記事の統制の節","に書いたので、ここでは「操作ログ」という呼び名を守ることだけ決めておく。",[14,6638,6639,6642],{},[248,6640,6641],{},"部品2: エラーの整形","。FastAPIの422をLLM向けのガイダンス文に翻訳する。",[77,6644,6646],{"className":264,"code":6645,"language":266,"meta":82,"style":82},"def format_422(detail: list[dict]) -> str:\n    \"\"\"FastAPIのバリデーションエラーを読み下し、次の行動を促す文に変える。\n\n    freee-mcp対応: 生の401/403/429に日本語の対処ガイダンスを添えるエラー整形\n    （src/openapi/client-mode.ts の formatErrorMessage 系）の縮小版。\n    \"\"\"\n    lines = [\n        f\"- {'.'.join(str(x) for x in item.get('loc', [])[1:])}: {item.get('msg', '')}\"\n        for item in detail\n    ]\n    return (\n        \"入力エラーで受け付けられませんでした。\\n\"\n        + \"\\n\".join(lines)\n        + f\"\\n使える勘定科目: {ACCOUNTS}。日付は YYYY-MM-DD、金額は正の整数（円）です。\"\n    )\n",[51,6647,6648,6676,6683,6687,6692,6697,6701,6710,6805,6817,6821,6828,6839,6861,6880],{"__ignoreMap":82},[86,6649,6650,6652,6655,6657,6659,6661,6663,6665,6668,6670,6672,6674],{"class":88,"line":89},[86,6651,1111],{"class":761},[86,6653,6654],{"class":92}," format_422",[86,6656,769],{"class":342},[86,6658,5611],{"class":313},[86,6660,432],{"class":342},[86,6662,1122],{"class":313},[86,6664,438],{"class":342},[86,6666,6667],{"class":114},"dict",[86,6669,1583],{"class":342},[86,6671,959],{"class":342},[86,6673,446],{"class":114},[86,6675,964],{"class":342},[86,6677,6678,6680],{"class":88,"line":111},[86,6679,781],{"class":138},[86,6681,6682],{"class":96},"FastAPIのバリデーションエラーを読み下し、次の行動を促す文に変える。\n",[86,6684,6685],{"class":88,"line":121},[86,6686,282],{"emptyLinePlaceholder":281},[86,6688,6689],{"class":88,"line":148},[86,6690,6691],{"class":96},"    freee-mcp対応: 生の401/403/429に日本語の対処ガイダンスを添えるエラー整形\n",[86,6693,6694],{"class":88,"line":295},[86,6695,6696],{"class":96},"    （src/openapi/client-mode.ts の formatErrorMessage 系）の縮小版。\n",[86,6698,6699],{"class":88,"line":301},[86,6700,1620],{"class":138},[86,6702,6703,6706,6708],{"class":88,"line":306},[86,6704,6705],{"class":313},"    lines ",[86,6707,853],{"class":342},[86,6709,1141],{"class":342},[86,6711,6712,6714,6717,6719,6721,6723,6725,6727,6729,6731,6733,6735,6738,6740,6742,6745,6747,6750,6752,6754,6756,6758,6761,6763,6765,6768,6770,6773,6775,6777,6779,6782,6784,6786,6788,6790,6792,6794,6796,6799,6801,6803],{"class":88,"line":323},[86,6713,6581],{"class":761},[86,6715,6716],{"class":96},"\"- ",[86,6718,1030],{"class":103},[86,6720,5851],{"class":138},[86,6722,805],{"class":96},[86,6724,5851],{"class":138},[86,6726,805],{"class":342},[86,6728,1004],{"class":313},[86,6730,769],{"class":342},[86,6732,441],{"class":114},[86,6734,769],{"class":342},[86,6736,6737],{"class":313},"x",[86,6739,956],{"class":342},[86,6741,2081],{"class":309},[86,6743,6744],{"class":313}," x ",[86,6746,1648],{"class":309},[86,6748,6749],{"class":313}," item",[86,6751,805],{"class":342},[86,6753,2461],{"class":313},[86,6755,769],{"class":342},[86,6757,5851],{"class":138},[86,6759,6760],{"class":96},"loc",[86,6762,5851],{"class":138},[86,6764,343],{"class":342},[86,6766,6767],{"class":342}," [])[",[86,6769,1509],{"class":107},[86,6771,6772],{"class":342},":])",[86,6774,1036],{"class":103},[86,6776,3933],{"class":96},[86,6778,1030],{"class":103},[86,6780,6781],{"class":313},"item",[86,6783,805],{"class":342},[86,6785,2461],{"class":313},[86,6787,769],{"class":342},[86,6789,5851],{"class":138},[86,6791,5625],{"class":96},[86,6793,5851],{"class":138},[86,6795,343],{"class":342},[86,6797,6798],{"class":138}," ''",[86,6800,956],{"class":342},[86,6802,1036],{"class":103},[86,6804,145],{"class":96},[86,6806,6807,6809,6812,6814],{"class":88,"line":328},[86,6808,1457],{"class":309},[86,6810,6811],{"class":313}," item ",[86,6813,1648],{"class":309},[86,6815,6816],{"class":313}," detail\n",[86,6818,6819],{"class":88,"line":364},[86,6820,1369],{"class":342},[86,6822,6823,6825],{"class":88,"line":387},[86,6824,1375],{"class":309},[86,6826,6827],{"class":342}," (\n",[86,6829,6830,6832,6835,6837],{"class":88,"line":392},[86,6831,2138],{"class":138},[86,6833,6834],{"class":96},"入力エラーで受け付けられませんでした。",[86,6836,6619],{"class":103},[86,6838,145],{"class":138},[86,6840,6841,6844,6846,6848,6850,6852,6854,6856,6859],{"class":88,"line":399},[86,6842,6843],{"class":761},"        +",[86,6845,139],{"class":138},[86,6847,6619],{"class":103},[86,6849,466],{"class":138},[86,6851,805],{"class":342},[86,6853,1004],{"class":313},[86,6855,769],{"class":342},[86,6857,6858],{"class":313},"lines",[86,6860,873],{"class":342},[86,6862,6863,6865,6867,6869,6871,6874,6877],{"class":88,"line":415},[86,6864,6843],{"class":761},[86,6866,3914],{"class":761},[86,6868,466],{"class":96},[86,6870,6619],{"class":103},[86,6872,6873],{"class":96},"使える勘定科目: ",[86,6875,6876],{"class":103},"{ACCOUNTS}",[86,6878,6879],{"class":96},"。日付は YYYY-MM-DD、金額は正の整数（円）です。\"\n",[86,6881,6882],{"class":88,"line":420},[86,6883,4452],{"class":342},[14,6885,6886,6889,6890,6893,6894,6896,6897,6900],{},[248,6887,6888],{},"部品3: API呼び出しの共通経路","。httpxでキーを付けて呼び、401と422をガイダンス付きの ",[51,6891,6892],{},"ToolError"," に変換する。",[51,6895,6892],{}," を投げると、MCPクライアントには ",[51,6898,6899],{},"isError: true"," のツール実行失敗として届き、成功と明確に区別される。",[77,6902,6904],{"className":264,"code":6903,"language":266,"meta":82,"style":82},"def call_api(\n    tool: str,\n    method: str,\n    path: str,\n    *,\n    params: dict | None = None,\n    json_body: dict | None = None,\n) -> Any:\n    \"\"\"APIを呼び、エラーをエージェント向けのガイダンス文に整形する共通経路。\n\n    ToolErrorを投げると、MCPクライアントには isError: true のツール実行失敗として返る\n    （成功と区別されるので、エージェントが「失敗した」と認識してリカバリーに動ける）。\n    \"\"\"\n    args = {k: v for k, v in {\"params\": params, \"body\": json_body}.items() if v}\n    try:\n        res = httpx.request(\n            method,\n            BASE_URL + path,\n            headers={\"X-API-Key\": API_KEY},\n            params=params,\n            json=json_body,\n            timeout=10.0,\n        )\n    except httpx.ConnectError as e:\n        write_log(tool, args, \"connect_error\")\n        raise ToolError(\n            f\"APIに接続できません（{BASE_URL}）。\"\n            \"`uv run uvicorn api:app` でミニ仕訳帳APIが起動しているか確認してください。\"\n        ) from e\n    if res.status_code == 401:\n        write_log(tool, args, \"401\")\n        raise ToolError(\n            \"APIキーが違います。MCPサーバーの環境変数 JOURNAL_API_KEY を確認してください。\"\n        )\n    if res.status_code == 422:\n        write_log(tool, args, \"422\")\n        raise ToolError(format_422(res.json().get(\"detail\", [])))\n    res.raise_for_status()  # 予期しないステータスはそのままツール実行失敗として上げる\n    write_log(tool, args, str(res.status_code))\n    return res.json()\n",[51,6905,6906,6915,6926,6937,6948,6955,6974,6993,7003,7010,7014,7019,7024,7028,7094,7101,7117,7124,7136,7155,7166,7178,7190,7194,7212,7236,7245,7259,7268,7279,7296,7318,7326,7335,7339,7356,7379,7416,7431,7460],{"__ignoreMap":82},[86,6907,6908,6910,6913],{"class":88,"line":89},[86,6909,1111],{"class":761},[86,6911,6912],{"class":92}," call_api",[86,6914,1386],{"class":342},[86,6916,6917,6920,6922,6924],{"class":88,"line":111},[86,6918,6919],{"class":313},"    tool",[86,6921,432],{"class":342},[86,6923,446],{"class":114},[86,6925,478],{"class":342},[86,6927,6928,6931,6933,6935],{"class":88,"line":121},[86,6929,6930],{"class":313},"    method",[86,6932,432],{"class":342},[86,6934,446],{"class":114},[86,6936,478],{"class":342},[86,6938,6939,6942,6944,6946],{"class":88,"line":148},[86,6940,6941],{"class":313},"    path",[86,6943,432],{"class":342},[86,6945,446],{"class":114},[86,6947,478],{"class":342},[86,6949,6950,6953],{"class":88,"line":295},[86,6951,6952],{"class":761},"    *",[86,6954,478],{"class":313},[86,6956,6957,6960,6962,6964,6966,6968,6970,6972],{"class":88,"line":301},[86,6958,6959],{"class":313},"    params",[86,6961,432],{"class":342},[86,6963,435],{"class":114},[86,6965,2259],{"class":761},[86,6967,2032],{"class":309},[86,6969,405],{"class":342},[86,6971,2032],{"class":309},[86,6973,478],{"class":342},[86,6975,6976,6979,6981,6983,6985,6987,6989,6991],{"class":88,"line":306},[86,6977,6978],{"class":313},"    json_body",[86,6980,432],{"class":342},[86,6982,435],{"class":114},[86,6984,2259],{"class":761},[86,6986,2032],{"class":309},[86,6988,405],{"class":342},[86,6990,2032],{"class":309},[86,6992,478],{"class":342},[86,6994,6995,6997,6999,7001],{"class":88,"line":323},[86,6996,956],{"class":342},[86,6998,959],{"class":342},[86,7000,6381],{"class":313},[86,7002,964],{"class":342},[86,7004,7005,7007],{"class":88,"line":328},[86,7006,781],{"class":138},[86,7008,7009],{"class":96},"APIを呼び、エラーをエージェント向けのガイダンス文に整形する共通経路。\n",[86,7011,7012],{"class":88,"line":364},[86,7013,282],{"emptyLinePlaceholder":281},[86,7015,7016],{"class":88,"line":387},[86,7017,7018],{"class":96},"    ToolErrorを投げると、MCPクライアントには isError: true のツール実行失敗として返る\n",[86,7020,7021],{"class":88,"line":392},[86,7022,7023],{"class":96},"    （成功と区別されるので、エージェントが「失敗した」と認識してリカバリーに動ける）。\n",[86,7025,7026],{"class":88,"line":399},[86,7027,1620],{"class":138},[86,7029,7030,7033,7035,7037,7040,7042,7044,7046,7049,7051,7053,7055,7057,7059,7062,7064,7066,7068,7070,7072,7074,7076,7078,7081,7084,7086,7088,7090,7092],{"class":88,"line":415},[86,7031,7032],{"class":313},"    args ",[86,7034,853],{"class":342},[86,7036,3130],{"class":342},[86,7038,7039],{"class":313},"k",[86,7041,432],{"class":342},[86,7043,973],{"class":313},[86,7045,1682],{"class":309},[86,7047,7048],{"class":313}," k",[86,7050,343],{"class":342},[86,7052,973],{"class":313},[86,7054,1648],{"class":309},[86,7056,3130],{"class":342},[86,7058,466],{"class":138},[86,7060,7061],{"class":96},"params",[86,7063,466],{"class":138},[86,7065,432],{"class":342},[86,7067,4882],{"class":313},[86,7069,343],{"class":342},[86,7071,139],{"class":138},[86,7073,5396],{"class":96},[86,7075,466],{"class":138},[86,7077,432],{"class":342},[86,7079,7080],{"class":313}," json_body",[86,7082,7083],{"class":342},"}.",[86,7085,1655],{"class":313},[86,7087,1117],{"class":342},[86,7089,1816],{"class":309},[86,7091,949],{"class":313},[86,7093,608],{"class":342},[86,7095,7096,7099],{"class":88,"line":420},[86,7097,7098],{"class":309},"    try",[86,7100,964],{"class":342},[86,7102,7103,7106,7108,7110,7112,7115],{"class":88,"line":426},[86,7104,7105],{"class":313},"        res ",[86,7107,853],{"class":342},[86,7109,135],{"class":313},[86,7111,805],{"class":342},[86,7113,7114],{"class":313},"request",[86,7116,1386],{"class":342},[86,7118,7119,7122],{"class":88,"line":457},[86,7120,7121],{"class":313},"            method",[86,7123,478],{"class":342},[86,7125,7126,7129,7131,7134],{"class":88,"line":481},[86,7127,7128],{"class":103},"            BASE_URL",[86,7130,2764],{"class":761},[86,7132,7133],{"class":313}," path",[86,7135,478],{"class":342},[86,7137,7138,7141,7143,7145,7147,7149,7151,7153],{"class":88,"line":501},[86,7139,7140],{"class":849},"            headers",[86,7142,4885],{"class":342},[86,7144,466],{"class":138},[86,7146,242],{"class":96},[86,7148,466],{"class":138},[86,7150,432],{"class":342},[86,7152,2298],{"class":103},[86,7154,3315],{"class":342},[86,7156,7157,7160,7162,7164],{"class":88,"line":522},[86,7158,7159],{"class":849},"            params",[86,7161,853],{"class":342},[86,7163,7061],{"class":313},[86,7165,478],{"class":342},[86,7167,7168,7171,7173,7176],{"class":88,"line":543},[86,7169,7170],{"class":849},"            json",[86,7172,853],{"class":342},[86,7174,7175],{"class":313},"json_body",[86,7177,478],{"class":342},[86,7179,7180,7183,7185,7188],{"class":88,"line":564},[86,7181,7182],{"class":849},"            timeout",[86,7184,853],{"class":342},[86,7186,7187],{"class":107},"10.0",[86,7189,478],{"class":342},[86,7191,7192],{"class":88,"line":585},[86,7193,1451],{"class":342},[86,7195,7196,7199,7201,7203,7206,7208,7210],{"class":88,"line":605},[86,7197,7198],{"class":309},"    except",[86,7200,135],{"class":313},[86,7202,805],{"class":342},[86,7204,7205],{"class":313},"ConnectError ",[86,7207,317],{"class":309},[86,7209,1696],{"class":313},[86,7211,964],{"class":342},[86,7213,7214,7217,7219,7221,7223,7225,7227,7229,7232,7234],{"class":88,"line":611},[86,7215,7216],{"class":313},"        write_log",[86,7218,769],{"class":342},[86,7220,6359],{"class":313},[86,7222,343],{"class":342},[86,7224,6368],{"class":313},[86,7226,343],{"class":342},[86,7228,139],{"class":138},[86,7230,7231],{"class":96},"connect_error",[86,7233,466],{"class":138},[86,7235,873],{"class":342},[86,7237,7238,7240,7243],{"class":88,"line":616},[86,7239,2306],{"class":309},[86,7241,7242],{"class":313}," ToolError",[86,7244,1386],{"class":342},[86,7246,7247,7250,7253,7256],{"class":88,"line":622},[86,7248,7249],{"class":761},"            f",[86,7251,7252],{"class":96},"\"APIに接続できません（",[86,7254,7255],{"class":103},"{BASE_URL}",[86,7257,7258],{"class":96},"）。\"\n",[86,7260,7261,7263,7266],{"class":88,"line":646},[86,7262,2169],{"class":138},[86,7264,7265],{"class":96},"`uv run uvicorn api:app` でミニ仕訳帳APIが起動しているか確認してください。",[86,7267,145],{"class":138},[86,7269,7270,7273,7276],{"class":88,"line":666},[86,7271,7272],{"class":342},"        )",[86,7274,7275],{"class":309}," from",[86,7277,7278],{"class":313}," e\n",[86,7280,7281,7283,7285,7287,7289,7291,7294],{"class":88,"line":685},[86,7282,2290],{"class":309},[86,7284,3702],{"class":313},[86,7286,805],{"class":342},[86,7288,3707],{"class":313},[86,7290,1704],{"class":761},[86,7292,7293],{"class":107}," 401",[86,7295,964],{"class":342},[86,7297,7298,7300,7302,7304,7306,7308,7310,7312,7314,7316],{"class":88,"line":705},[86,7299,7216],{"class":313},[86,7301,769],{"class":342},[86,7303,6359],{"class":313},[86,7305,343],{"class":342},[86,7307,6368],{"class":313},[86,7309,343],{"class":342},[86,7311,139],{"class":138},[86,7313,2321],{"class":96},[86,7315,466],{"class":138},[86,7317,873],{"class":342},[86,7319,7320,7322,7324],{"class":88,"line":724},[86,7321,2306],{"class":309},[86,7323,7242],{"class":313},[86,7325,1386],{"class":342},[86,7327,7328,7330,7333],{"class":88,"line":743},[86,7329,2169],{"class":138},[86,7331,7332],{"class":96},"APIキーが違います。MCPサーバーの環境変数 JOURNAL_API_KEY を確認してください。",[86,7334,145],{"class":138},[86,7336,7337],{"class":88,"line":748},[86,7338,1451],{"class":342},[86,7340,7341,7343,7345,7347,7349,7351,7354],{"class":88,"line":753},[86,7342,2290],{"class":309},[86,7344,3702],{"class":313},[86,7346,805],{"class":342},[86,7348,3707],{"class":313},[86,7350,1704],{"class":761},[86,7352,7353],{"class":107}," 422",[86,7355,964],{"class":342},[86,7357,7358,7360,7362,7364,7366,7368,7370,7372,7375,7377],{"class":88,"line":758},[86,7359,7216],{"class":313},[86,7361,769],{"class":342},[86,7363,6359],{"class":313},[86,7365,343],{"class":342},[86,7367,6368],{"class":313},[86,7369,343],{"class":342},[86,7371,139],{"class":138},[86,7373,7374],{"class":96},"422",[86,7376,466],{"class":138},[86,7378,873],{"class":342},[86,7380,7381,7383,7385,7387,7390,7392,7395,7397,7399,7401,7403,7405,7407,7409,7411,7413],{"class":88,"line":778},[86,7382,2306],{"class":309},[86,7384,7242],{"class":313},[86,7386,769],{"class":342},[86,7388,7389],{"class":313},"format_422",[86,7391,769],{"class":342},[86,7393,7394],{"class":313},"res",[86,7396,805],{"class":342},[86,7398,3723],{"class":313},[86,7400,6464],{"class":342},[86,7402,2461],{"class":313},[86,7404,769],{"class":342},[86,7406,466],{"class":138},[86,7408,5611],{"class":96},[86,7410,466],{"class":138},[86,7412,343],{"class":342},[86,7414,7415],{"class":342}," [])))\n",[86,7417,7418,7421,7423,7426,7428],{"class":88,"line":789},[86,7419,7420],{"class":313},"    res",[86,7422,805],{"class":342},[86,7424,7425],{"class":313},"raise_for_status",[86,7427,1117],{"class":342},[86,7429,7430],{"class":395},"  # 予期しないステータスはそのままツール実行失敗として上げる\n",[86,7432,7433,7436,7438,7440,7442,7444,7446,7448,7450,7452,7454,7457],{"class":88,"line":794},[86,7434,7435],{"class":313},"    write_log",[86,7437,769],{"class":342},[86,7439,6359],{"class":313},[86,7441,343],{"class":342},[86,7443,6368],{"class":313},[86,7445,343],{"class":342},[86,7447,446],{"class":114},[86,7449,769],{"class":342},[86,7451,7394],{"class":313},[86,7453,805],{"class":342},[86,7455,7456],{"class":313},"status_code",[86,7458,7459],{"class":342},"))\n",[86,7461,7462,7464,7466,7468,7470],{"class":88,"line":811},[86,7463,1375],{"class":309},[86,7465,3702],{"class":313},[86,7467,805],{"class":342},[86,7469,3723],{"class":313},[86,7471,1549],{"class":342},[14,7473,7474],{},"エラー文面の設計は、freee-mcpから学んだ「読み手はLLMである」という前提をそのまま持ち込んだ。認証切れなら確認すべき環境変数の名前を、入力エラーなら使える科目の一覧を、失敗の報告と同じ文面に埋め込む。エージェントに「次に何をすべきか」を推測させない。",[14,7476,7477,7480],{},[248,7478,7479],{},"部品4: ツール3本の宣言","。docstringがそのままツールのdescriptionになる。",[77,7482,7484],{"className":264,"code":7483,"language":266,"meta":82,"style":82},"@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))\ndef list_journal_entries(month: str | None = None) -> list[dict]:\n    \"\"\"仕訳の一覧を取得する。\n\n    month に \"YYYY-MM\"（例: \"2026-07\"）を渡すと、その月の仕訳だけに絞り込む。\n    省略するとすべての仕訳を返す。各仕訳は id・date・debit_account（借方科目）・\n    credit_account（貸方科目）・amount（金額・円）・description（摘要）を持つ。\n    \"\"\"\n    params = {\"month\": month} if month else None\n    return call_api(\"list_journal_entries\", \"GET\", \"/journal-entries\", params=params)\n\n\n@mcp.tool(\n    # 仕訳の新規登録は「加算的」で、既存データを書き換えも削除もしないので\n    # destructiveHint は付けない。freee-mcp対応: http-method-annotations.ts は\n    # POSTを一律 destructiveHint: true にする（HTTPメソッドから機械導出する保守的設計）。\n    # ツールを手で書くなら、操作の意味に即して申告できる。\n    # なお annotations は認可を強制しない「クライアント向けのヒント」にすぎない（MCP仕様）。\n    annotations=ToolAnnotations(readOnlyHint=False, destructiveHint=False),\n)\ndef register_journal_entry(\n    date: str,\n    debit_account: str,\n    credit_account: str,\n    amount: int,\n    description: str = \"\",\n) -> dict:\n    \"\"\"仕訳を1件登録する（1行仕訳のみ。借方1科目・貸方1科目）。\n\n    date は \"YYYY-MM-DD\" 形式、amount は正の整数（円）。\n    勘定科目はホワイトリスト制で、使えるのは次の7科目だけ:\n    現金、普通預金、買掛金、資本金、売上高、仕入高、消耗品費。\n    この一覧にない科目（例: 雑費）を渡すと入力エラーになる。\n    \"\"\"\n    body = {\n        \"date\": date,\n        \"debit_account\": debit_account,\n        \"credit_account\": credit_account,\n        \"amount\": amount,\n        \"description\": description,\n    }\n    return call_api(\"register_journal_entry\", \"POST\", \"/journal-entries\", json_body=body)\n\n\n@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))\ndef get_trial_balance() -> dict:\n    \"\"\"勘定科目別の残高試算表を取得する。\n\n    科目ごとに借方合計（debit_total）・貸方合計（credit_total）・残高（balance）・\n    残高の側（balance_side）・区分から決まる正常残高側（normal_side）を返す。\n    両者が食い違う科目には is_abnormal: true が立つ（例: 現金なのに貸方残）。\n    末尾の total で借方合計と貸方合計の一致（貸借一致）を確認できる。\n    \"\"\"\n    return call_api(\"get_trial_balance\", \"GET\", \"/trial-balance\")\n\n\nif __name__ == \"__main__\":\n    mcp.run()  # デフォルトはstdioトランスポート\n",[51,7485,7486,7518,7552,7559,7563,7568,7573,7578,7582,7612,7654,7658,7662,7674,7679,7684,7689,7694,7699,7727,7731,7739,7749,7759,7769,7779,7793,7803,7810,7814,7819,7824,7829,7834,7838,7846,7860,7874,7888,7902,7916,7920,7962,7966,7970,7998,8012,8019,8023,8028,8033,8038,8043,8047,8080,8084,8088,8106],{"__ignoreMap":82},[86,7487,7488,7490,7493,7495,7497,7499,7502,7504,7507,7509,7512,7514,7516],{"class":88,"line":89},[86,7489,2453],{"class":342},[86,7491,7492],{"class":92},"mcp",[86,7494,805],{"class":342},[86,7496,6359],{"class":92},[86,7498,769],{"class":342},[86,7500,7501],{"class":849},"annotations",[86,7503,853],{"class":342},[86,7505,7506],{"class":313},"ToolAnnotations",[86,7508,769],{"class":342},[86,7510,7511],{"class":849},"readOnlyHint",[86,7513,853],{"class":342},[86,7515,3177],{"class":309},[86,7517,7459],{"class":342},[86,7519,7520,7522,7524,7526,7528,7530,7532,7534,7536,7538,7540,7542,7544,7546,7548,7550],{"class":88,"line":111},[86,7521,1111],{"class":761},[86,7523,2480],{"class":92},[86,7525,769],{"class":342},[86,7527,4890],{"class":313},[86,7529,432],{"class":342},[86,7531,446],{"class":114},[86,7533,2259],{"class":761},[86,7535,2032],{"class":309},[86,7537,405],{"class":342},[86,7539,2032],{"class":309},[86,7541,956],{"class":342},[86,7543,959],{"class":342},[86,7545,1122],{"class":313},[86,7547,438],{"class":342},[86,7549,6667],{"class":114},[86,7551,1130],{"class":342},[86,7553,7554,7556],{"class":88,"line":121},[86,7555,781],{"class":138},[86,7557,7558],{"class":96},"仕訳の一覧を取得する。\n",[86,7560,7561],{"class":88,"line":148},[86,7562,282],{"emptyLinePlaceholder":281},[86,7564,7565],{"class":88,"line":295},[86,7566,7567],{"class":96},"    month に \"YYYY-MM\"（例: \"2026-07\"）を渡すと、その月の仕訳だけに絞り込む。\n",[86,7569,7570],{"class":88,"line":301},[86,7571,7572],{"class":96},"    省略するとすべての仕訳を返す。各仕訳は id・date・debit_account（借方科目）・\n",[86,7574,7575],{"class":88,"line":306},[86,7576,7577],{"class":96},"    credit_account（貸方科目）・amount（金額・円）・description（摘要）を持つ。\n",[86,7579,7580],{"class":88,"line":323},[86,7581,1620],{"class":138},[86,7583,7584,7587,7589,7591,7593,7595,7597,7599,7601,7603,7605,7607,7610],{"class":88,"line":328},[86,7585,7586],{"class":313},"    params ",[86,7588,853],{"class":342},[86,7590,3130],{"class":342},[86,7592,466],{"class":138},[86,7594,4890],{"class":96},[86,7596,466],{"class":138},[86,7598,432],{"class":342},[86,7600,2641],{"class":313},[86,7602,1036],{"class":342},[86,7604,1816],{"class":309},[86,7606,2576],{"class":313},[86,7608,7609],{"class":309},"else",[86,7611,1847],{"class":309},[86,7613,7614,7616,7618,7620,7622,7625,7627,7629,7631,7634,7636,7638,7640,7642,7644,7646,7648,7650,7652],{"class":88,"line":364},[86,7615,1375],{"class":309},[86,7617,6912],{"class":313},[86,7619,769],{"class":342},[86,7621,466],{"class":138},[86,7623,7624],{"class":96},"list_journal_entries",[86,7626,466],{"class":138},[86,7628,343],{"class":342},[86,7630,139],{"class":138},[86,7632,7633],{"class":96},"GET",[86,7635,466],{"class":138},[86,7637,343],{"class":342},[86,7639,139],{"class":138},[86,7641,2468],{"class":96},[86,7643,466],{"class":138},[86,7645,343],{"class":342},[86,7647,4882],{"class":849},[86,7649,853],{"class":342},[86,7651,7061],{"class":313},[86,7653,873],{"class":342},[86,7655,7656],{"class":88,"line":387},[86,7657,282],{"emptyLinePlaceholder":281},[86,7659,7660],{"class":88,"line":392},[86,7661,282],{"emptyLinePlaceholder":281},[86,7663,7664,7666,7668,7670,7672],{"class":88,"line":399},[86,7665,2453],{"class":342},[86,7667,7492],{"class":92},[86,7669,805],{"class":342},[86,7671,6359],{"class":92},[86,7673,1386],{"class":342},[86,7675,7676],{"class":88,"line":415},[86,7677,7678],{"class":395},"    # 仕訳の新規登録は「加算的」で、既存データを書き換えも削除もしないので\n",[86,7680,7681],{"class":88,"line":420},[86,7682,7683],{"class":395},"    # destructiveHint は付けない。freee-mcp対応: http-method-annotations.ts は\n",[86,7685,7686],{"class":88,"line":426},[86,7687,7688],{"class":395},"    # POSTを一律 destructiveHint: true にする（HTTPメソッドから機械導出する保守的設計）。\n",[86,7690,7691],{"class":88,"line":457},[86,7692,7693],{"class":395},"    # ツールを手で書くなら、操作の意味に即して申告できる。\n",[86,7695,7696],{"class":88,"line":481},[86,7697,7698],{"class":395},"    # なお annotations は認可を強制しない「クライアント向けのヒント」にすぎない（MCP仕様）。\n",[86,7700,7701,7704,7706,7708,7710,7712,7714,7716,7718,7721,7723,7725],{"class":88,"line":501},[86,7702,7703],{"class":849},"    annotations",[86,7705,853],{"class":342},[86,7707,7506],{"class":313},[86,7709,769],{"class":342},[86,7711,7511],{"class":849},[86,7713,853],{"class":342},[86,7715,6610],{"class":309},[86,7717,343],{"class":342},[86,7719,7720],{"class":849}," destructiveHint",[86,7722,853],{"class":342},[86,7724,6610],{"class":309},[86,7726,1187],{"class":342},[86,7728,7729],{"class":88,"line":522},[86,7730,873],{"class":342},[86,7732,7733,7735,7737],{"class":88,"line":543},[86,7734,1111],{"class":761},[86,7736,2693],{"class":92},[86,7738,1386],{"class":342},[86,7740,7741,7743,7745,7747],{"class":88,"line":564},[86,7742,797],{"class":313},[86,7744,432],{"class":342},[86,7746,446],{"class":114},[86,7748,478],{"class":342},[86,7750,7751,7753,7755,7757],{"class":88,"line":585},[86,7752,814],{"class":313},[86,7754,432],{"class":342},[86,7756,446],{"class":114},[86,7758,478],{"class":342},[86,7760,7761,7763,7765,7767],{"class":88,"line":605},[86,7762,825],{"class":313},[86,7764,432],{"class":342},[86,7766,446],{"class":114},[86,7768,478],{"class":342},[86,7770,7771,7773,7775,7777],{"class":88,"line":611},[86,7772,835],{"class":313},[86,7774,432],{"class":342},[86,7776,840],{"class":114},[86,7778,478],{"class":342},[86,7780,7781,7783,7785,7787,7789,7791],{"class":88,"line":616},[86,7782,879],{"class":313},[86,7784,432],{"class":342},[86,7786,446],{"class":114},[86,7788,405],{"class":342},[86,7790,6214],{"class":138},[86,7792,478],{"class":342},[86,7794,7795,7797,7799,7801],{"class":88,"line":622},[86,7796,956],{"class":342},[86,7798,959],{"class":342},[86,7800,435],{"class":114},[86,7802,964],{"class":342},[86,7804,7805,7807],{"class":88,"line":646},[86,7806,781],{"class":138},[86,7808,7809],{"class":96},"仕訳を1件登録する（1行仕訳のみ。借方1科目・貸方1科目）。\n",[86,7811,7812],{"class":88,"line":666},[86,7813,282],{"emptyLinePlaceholder":281},[86,7815,7816],{"class":88,"line":685},[86,7817,7818],{"class":96},"    date は \"YYYY-MM-DD\" 形式、amount は正の整数（円）。\n",[86,7820,7821],{"class":88,"line":705},[86,7822,7823],{"class":96},"    勘定科目はホワイトリスト制で、使えるのは次の7科目だけ:\n",[86,7825,7826],{"class":88,"line":724},[86,7827,7828],{"class":96},"    現金、普通預金、買掛金、資本金、売上高、仕入高、消耗品費。\n",[86,7830,7831],{"class":88,"line":743},[86,7832,7833],{"class":96},"    この一覧にない科目（例: 雑費）を渡すと入力エラーになる。\n",[86,7835,7836],{"class":88,"line":748},[86,7837,1620],{"class":138},[86,7839,7840,7842,7844],{"class":88,"line":753},[86,7841,5256],{"class":313},[86,7843,853],{"class":342},[86,7845,454],{"class":342},[86,7847,7848,7850,7852,7854,7856,7858],{"class":88,"line":758},[86,7849,2138],{"class":138},[86,7851,2619],{"class":96},[86,7853,466],{"class":138},[86,7855,432],{"class":342},[86,7857,1402],{"class":313},[86,7859,478],{"class":342},[86,7861,7862,7864,7866,7868,7870,7872],{"class":88,"line":778},[86,7863,2138],{"class":138},[86,7865,909],{"class":96},[86,7867,466],{"class":138},[86,7869,432],{"class":342},[86,7871,1412],{"class":313},[86,7873,478],{"class":342},[86,7875,7876,7878,7880,7882,7884,7886],{"class":88,"line":789},[86,7877,2138],{"class":138},[86,7879,918],{"class":96},[86,7881,466],{"class":138},[86,7883,432],{"class":342},[86,7885,1422],{"class":313},[86,7887,478],{"class":342},[86,7889,7890,7892,7894,7896,7898,7900],{"class":88,"line":794},[86,7891,2138],{"class":138},[86,7893,4414],{"class":96},[86,7895,466],{"class":138},[86,7897,432],{"class":342},[86,7899,1432],{"class":313},[86,7901,478],{"class":342},[86,7903,7904,7906,7908,7910,7912,7914],{"class":88,"line":811},[86,7905,2138],{"class":138},[86,7907,4430],{"class":96},[86,7909,466],{"class":138},[86,7911,432],{"class":342},[86,7913,861],{"class":313},[86,7915,478],{"class":342},[86,7917,7918],{"class":88,"line":822},[86,7919,2229],{"class":342},[86,7921,7922,7924,7926,7928,7930,7933,7935,7937,7939,7942,7944,7946,7948,7950,7952,7954,7956,7958,7960],{"class":88,"line":832},[86,7923,1375],{"class":309},[86,7925,6912],{"class":313},[86,7927,769],{"class":342},[86,7929,466],{"class":138},[86,7931,7932],{"class":96},"register_journal_entry",[86,7934,466],{"class":138},[86,7936,343],{"class":342},[86,7938,139],{"class":138},[86,7940,7941],{"class":96},"POST",[86,7943,466],{"class":138},[86,7945,343],{"class":342},[86,7947,139],{"class":138},[86,7949,2468],{"class":96},[86,7951,466],{"class":138},[86,7953,343],{"class":342},[86,7955,7080],{"class":849},[86,7957,853],{"class":342},[86,7959,5396],{"class":313},[86,7961,873],{"class":342},[86,7963,7964],{"class":88,"line":876},[86,7965,282],{"emptyLinePlaceholder":281},[86,7967,7968],{"class":88,"line":891},[86,7969,282],{"emptyLinePlaceholder":281},[86,7971,7972,7974,7976,7978,7980,7982,7984,7986,7988,7990,7992,7994,7996],{"class":88,"line":896},[86,7973,2453],{"class":342},[86,7975,7492],{"class":92},[86,7977,805],{"class":342},[86,7979,6359],{"class":92},[86,7981,769],{"class":342},[86,7983,7501],{"class":849},[86,7985,853],{"class":342},[86,7987,7506],{"class":313},[86,7989,769],{"class":342},[86,7991,7511],{"class":849},[86,7993,853],{"class":342},[86,7995,3177],{"class":309},[86,7997,7459],{"class":342},[86,7999,8000,8002,8004,8006,8008,8010],{"class":88,"line":925},[86,8001,1111],{"class":761},[86,8003,2867],{"class":92},[86,8005,1117],{"class":342},[86,8007,959],{"class":342},[86,8009,435],{"class":114},[86,8011,964],{"class":342},[86,8013,8014,8016],{"class":88,"line":933},[86,8015,781],{"class":138},[86,8017,8018],{"class":96},"勘定科目別の残高試算表を取得する。\n",[86,8020,8021],{"class":88,"line":967},[86,8022,282],{"emptyLinePlaceholder":281},[86,8024,8025],{"class":88,"line":987},[86,8026,8027],{"class":96},"    科目ごとに借方合計（debit_total）・貸方合計（credit_total）・残高（balance）・\n",[86,8029,8030],{"class":88,"line":1013},[86,8031,8032],{"class":96},"    残高の側（balance_side）・区分から決まる正常残高側（normal_side）を返す。\n",[86,8034,8035],{"class":88,"line":1053},[86,8036,8037],{"class":96},"    両者が食い違う科目には is_abnormal: true が立つ（例: 現金なのに貸方残）。\n",[86,8039,8040],{"class":88,"line":1062},[86,8041,8042],{"class":96},"    末尾の total で借方合計と貸方合計の一致（貸借一致）を確認できる。\n",[86,8044,8045],{"class":88,"line":1067},[86,8046,1620],{"class":138},[86,8048,8049,8051,8053,8055,8057,8060,8062,8064,8066,8068,8070,8072,8074,8076,8078],{"class":88,"line":1072},[86,8050,1375],{"class":309},[86,8052,6912],{"class":313},[86,8054,769],{"class":342},[86,8056,466],{"class":138},[86,8058,8059],{"class":96},"get_trial_balance",[86,8061,466],{"class":138},[86,8063,343],{"class":342},[86,8065,139],{"class":138},[86,8067,7633],{"class":96},[86,8069,466],{"class":138},[86,8071,343],{"class":342},[86,8073,139],{"class":138},[86,8075,2855],{"class":96},[86,8077,466],{"class":138},[86,8079,873],{"class":342},[86,8081,8082],{"class":88,"line":1087},[86,8083,282],{"emptyLinePlaceholder":281},[86,8085,8086],{"class":88,"line":1098},[86,8087,282],{"emptyLinePlaceholder":281},[86,8089,8090,8092,8095,8097,8099,8102,8104],{"class":88,"line":1103},[86,8091,1693],{"class":309},[86,8093,8094],{"class":114}," __name__",[86,8096,2638],{"class":761},[86,8098,139],{"class":138},[86,8100,8101],{"class":96},"__main__",[86,8103,466],{"class":138},[86,8105,964],{"class":342},[86,8107,8108,8111,8113,8116,8118],{"class":88,"line":1108},[86,8109,8110],{"class":313},"    mcp",[86,8112,805],{"class":342},[86,8114,8115],{"class":313},"run",[86,8117,1117],{"class":342},[86,8119,8120],{"class":395},"  # デフォルトはstdioトランスポート\n",[14,8122,8123],{},"descriptionは日本語で、使える科目の一覧まで書き込んだ。エージェントはこの文章を実行時に読んでツールの使い方を決めるので、宣言の書き込みの質が、そのままエージェントの動作精度になる。freee-mcpのツール宣言が素っ気ない固定文字列だったのは、仕様知識をAgent Skills側へ分離したからで、うちのように3本しかなければ宣言に直接書くのが早い。同じ問題への、規模に応じた別の解だ。",[14,8125,8126,8127,8129,8130,8133,8134,8137,8138,8141,8142,8147],{},"annotationsには2つ論点を仕込んだ。1つは ",[51,8128,7932],{}," の ",[51,8131,8132],{},"destructiveHint: false","。freee-mcpはHTTPメソッドから機械的に導出するので、POSTは一律 ",[51,8135,8136],{},"destructiveHint: true"," になる。既存データを知らずに壊す可能性を広めに申告する保守的な設計だ。一方、仕訳の新規登録は既存の仕訳を1件も書き換えない加算的な操作なので、手でツールを書くなら意味に即して ",[51,8139,8140],{},"false"," と申告できる。もう1つは、そもそもannotationsは認可を強制しない「クライアント向けのヒント」にすぎないことで、これは",[17,8143,8146],{"href":8144,"target":41,"rel":8145},"https://modelcontextprotocol.io/specification/2025-06-18/server/tools",[43,44],"→ MCP仕様のツールの節","に明記されている。ヒントを見て確認UIを出すかどうかは、クライアントの仕事。この分業は後の実験で実際に確かめる。",[182,8149,8151],{"id":8150},"step-4-宣言を覗いてからつなぐ","Step 4: 宣言を覗いてから、つなぐ",[14,8153,8154,8155,8158,8159,8162],{},"Claude Codeにつなぐ前に、エージェントが実際に何を見るのかを自分の目で確かめたい。MCPクライアントを名乗る50行ほどのスクリプトを書いて、",[51,8156,8157],{},"tools/list","（ツール宣言の一覧）と ",[51,8160,8161],{},"tools/call","（ツールの実行）を1回ずつ叩く。",[77,8164,8166],{"className":264,"code":8165,"language":266,"meta":82,"style":82},"\"\"\"MCPクライアントとしてserver.pyを起動し、tools/listとtools/callを1回ずつ実行する。\n\nエージェントが実際に見ているツール宣言（生JSON）を自分の目で確かめるための採取スクリプト。\n実行: uv run inspect_tools.py（事前に環境変数 JOURNAL_API_KEY を設定し、APIを起動しておく）\n\"\"\"\n\nimport asyncio\nimport json\nimport os\nimport sys\n\nfrom mcp import ClientSession, StdioServerParameters\nfrom mcp.client.stdio import get_default_environment, stdio_client\n\n\nasync def main() -> None:\n    key = os.environ.get(\"JOURNAL_API_KEY\")\n    if not key:\n        sys.exit(\"環境変数 JOURNAL_API_KEY を設定してから実行してください。\")\n\n    # server.py をサブプロセスとして起動する。環境変数はこの env 経由で明示的に渡す\n    server = StdioServerParameters(\n        command=sys.executable,\n        args=[\"server.py\"],\n        env={\n            **get_default_environment(),\n            \"JOURNAL_API_KEY\": key,\n            \"JOURNAL_API_BASE_URL\": os.environ.get(\n                \"JOURNAL_API_BASE_URL\", \"http://127.0.0.1:8000\"\n            ),\n        },\n    )\n    async with stdio_client(server) as (read, write):\n        async with ClientSession(read, write) as session:\n            await session.initialize()\n\n            tools = await session.list_tools()\n            print(\"=== tools/list ===\")\n            print(\n                json.dumps(\n                    [t.model_dump(exclude_none=True) for t in tools.tools],\n                    ensure_ascii=False,\n                    indent=2,\n                )\n            )\n\n            result = await session.call_tool(\"get_trial_balance\", {})\n            print(\"=== tools/call get_trial_balance ===\")\n            print(json.dumps(result.model_dump(exclude_none=True), ensure_ascii=False, indent=2))\n\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n",[51,8167,8168,8175,8179,8184,8189,8193,8197,8204,8210,8216,8223,8227,8244,8270,8274,8278,8297,8324,8334,8355,8359,8364,8376,8393,8409,8416,8427,8441,8463,8479,8484,8488,8492,8523,8549,8563,8567,8586,8602,8608,8619,8659,8670,8682,8687,8692,8696,8725,8740,8788,8792,8796,8812],{"__ignoreMap":82},[86,8169,8170,8172],{"class":88,"line":89},[86,8171,273],{"class":138},[86,8173,8174],{"class":96},"MCPクライアントとしてserver.pyを起動し、tools/listとtools/callを1回ずつ実行する。\n",[86,8176,8177],{"class":88,"line":111},[86,8178,282],{"emptyLinePlaceholder":281},[86,8180,8181],{"class":88,"line":121},[86,8182,8183],{"class":96},"エージェントが実際に見ているツール宣言（生JSON）を自分の目で確かめるための採取スクリプト。\n",[86,8185,8186],{"class":88,"line":148},[86,8187,8188],{"class":96},"実行: uv run inspect_tools.py（事前に環境変数 JOURNAL_API_KEY を設定し、APIを起動しておく）\n",[86,8190,8191],{"class":88,"line":295},[86,8192,298],{"class":138},[86,8194,8195],{"class":88,"line":301},[86,8196,282],{"emptyLinePlaceholder":281},[86,8198,8199,8201],{"class":88,"line":306},[86,8200,310],{"class":309},[86,8202,8203],{"class":313}," asyncio\n",[86,8205,8206,8208],{"class":88,"line":323},[86,8207,310],{"class":309},[86,8209,6064],{"class":313},[86,8211,8212,8214],{"class":88,"line":328},[86,8213,310],{"class":309},[86,8215,6071],{"class":313},[86,8217,8218,8220],{"class":88,"line":364},[86,8219,310],{"class":309},[86,8221,8222],{"class":313}," sys\n",[86,8224,8225],{"class":88,"line":387},[86,8226,282],{"emptyLinePlaceholder":281},[86,8228,8229,8231,8234,8236,8239,8241],{"class":88,"line":392},[86,8230,331],{"class":309},[86,8232,8233],{"class":313}," mcp ",[86,8235,310],{"class":309},[86,8237,8238],{"class":313}," ClientSession",[86,8240,343],{"class":342},[86,8242,8243],{"class":313}," StdioServerParameters\n",[86,8245,8246,8248,8250,8252,8255,8257,8260,8262,8265,8267],{"class":88,"line":399},[86,8247,331],{"class":309},[86,8249,6113],{"class":313},[86,8251,805],{"class":342},[86,8253,8254],{"class":313},"client",[86,8256,805],{"class":342},[86,8258,8259],{"class":313},"stdio ",[86,8261,310],{"class":309},[86,8263,8264],{"class":313}," get_default_environment",[86,8266,343],{"class":342},[86,8268,8269],{"class":313}," stdio_client\n",[86,8271,8272],{"class":88,"line":415},[86,8273,282],{"emptyLinePlaceholder":281},[86,8275,8276],{"class":88,"line":420},[86,8277,282],{"emptyLinePlaceholder":281},[86,8279,8280,8283,8286,8289,8291,8293,8295],{"class":88,"line":426},[86,8281,8282],{"class":761},"async",[86,8284,8285],{"class":761}," def",[86,8287,8288],{"class":92}," main",[86,8290,1117],{"class":342},[86,8292,959],{"class":342},[86,8294,2032],{"class":309},[86,8296,964],{"class":342},[86,8298,8299,8302,8304,8306,8308,8310,8312,8314,8316,8318,8320,8322],{"class":88,"line":457},[86,8300,8301],{"class":313},"    key ",[86,8303,853],{"class":342},[86,8305,6192],{"class":313},[86,8307,805],{"class":342},[86,8309,6197],{"class":313},[86,8311,805],{"class":342},[86,8313,2461],{"class":313},[86,8315,769],{"class":342},[86,8317,466],{"class":138},[86,8319,5799],{"class":96},[86,8321,466],{"class":138},[86,8323,873],{"class":342},[86,8325,8326,8328,8330,8332],{"class":88,"line":481},[86,8327,2290],{"class":309},[86,8329,2029],{"class":761},[86,8331,3873],{"class":313},[86,8333,964],{"class":342},[86,8335,8336,8339,8341,8344,8346,8348,8351,8353],{"class":88,"line":501},[86,8337,8338],{"class":313},"        sys",[86,8340,805],{"class":342},[86,8342,8343],{"class":313},"exit",[86,8345,769],{"class":342},[86,8347,466],{"class":138},[86,8349,8350],{"class":96},"環境変数 JOURNAL_API_KEY を設定してから実行してください。",[86,8352,466],{"class":138},[86,8354,873],{"class":342},[86,8356,8357],{"class":88,"line":522},[86,8358,282],{"emptyLinePlaceholder":281},[86,8360,8361],{"class":88,"line":543},[86,8362,8363],{"class":395},"    # server.py をサブプロセスとして起動する。環境変数はこの env 経由で明示的に渡す\n",[86,8365,8366,8369,8371,8374],{"class":88,"line":564},[86,8367,8368],{"class":313},"    server ",[86,8370,853],{"class":342},[86,8372,8373],{"class":313}," StdioServerParameters",[86,8375,1386],{"class":342},[86,8377,8378,8381,8383,8386,8388,8391],{"class":88,"line":585},[86,8379,8380],{"class":849},"        command",[86,8382,853],{"class":342},[86,8384,8385],{"class":313},"sys",[86,8387,805],{"class":342},[86,8389,8390],{"class":313},"executable",[86,8392,478],{"class":342},[86,8394,8395,8398,8400,8402,8404,8406],{"class":88,"line":605},[86,8396,8397],{"class":849},"        args",[86,8399,2421],{"class":342},[86,8401,466],{"class":138},[86,8403,6006],{"class":96},[86,8405,466],{"class":138},[86,8407,8408],{"class":342},"],\n",[86,8410,8411,8414],{"class":88,"line":611},[86,8412,8413],{"class":849},"        env",[86,8415,4352],{"class":342},[86,8417,8418,8421,8424],{"class":88,"line":616},[86,8419,8420],{"class":761},"            **",[86,8422,8423],{"class":313},"get_default_environment",[86,8425,8426],{"class":342},"(),\n",[86,8428,8429,8431,8433,8435,8437,8439],{"class":88,"line":622},[86,8430,2169],{"class":138},[86,8432,5799],{"class":96},[86,8434,466],{"class":138},[86,8436,432],{"class":342},[86,8438,3873],{"class":313},[86,8440,478],{"class":342},[86,8442,8443,8445,8447,8449,8451,8453,8455,8457,8459,8461],{"class":88,"line":646},[86,8444,2169],{"class":138},[86,8446,6240],{"class":96},[86,8448,466],{"class":138},[86,8450,432],{"class":342},[86,8452,6192],{"class":313},[86,8454,805],{"class":342},[86,8456,6197],{"class":313},[86,8458,805],{"class":342},[86,8460,2461],{"class":313},[86,8462,1386],{"class":342},[86,8464,8465,8467,8469,8471,8473,8475,8477],{"class":88,"line":666},[86,8466,1891],{"class":138},[86,8468,6240],{"class":96},[86,8470,466],{"class":138},[86,8472,343],{"class":342},[86,8474,139],{"class":138},[86,8476,5899],{"class":96},[86,8478,145],{"class":138},[86,8480,8481],{"class":88,"line":685},[86,8482,8483],{"class":342},"            ),\n",[86,8485,8486],{"class":88,"line":705},[86,8487,2223],{"class":342},[86,8489,8490],{"class":88,"line":724},[86,8491,4452],{"class":342},[86,8493,8494,8497,8500,8503,8505,8507,8509,8511,8513,8516,8518,8521],{"class":88,"line":743},[86,8495,8496],{"class":309},"    async",[86,8498,8499],{"class":309}," with",[86,8501,8502],{"class":313}," stdio_client",[86,8504,769],{"class":342},[86,8506,6118],{"class":313},[86,8508,956],{"class":342},[86,8510,6572],{"class":309},[86,8512,1465],{"class":342},[86,8514,8515],{"class":313},"read",[86,8517,343],{"class":342},[86,8519,8520],{"class":313}," write",[86,8522,775],{"class":342},[86,8524,8525,8528,8530,8532,8534,8536,8538,8540,8542,8544,8547],{"class":88,"line":748},[86,8526,8527],{"class":309},"        async",[86,8529,8499],{"class":309},[86,8531,8238],{"class":313},[86,8533,769],{"class":342},[86,8535,8515],{"class":313},[86,8537,343],{"class":342},[86,8539,8520],{"class":313},[86,8541,956],{"class":342},[86,8543,6572],{"class":309},[86,8545,8546],{"class":313}," session",[86,8548,964],{"class":342},[86,8550,8551,8554,8556,8558,8561],{"class":88,"line":753},[86,8552,8553],{"class":309},"            await",[86,8555,8546],{"class":313},[86,8557,805],{"class":342},[86,8559,8560],{"class":313},"initialize",[86,8562,1549],{"class":342},[86,8564,8565],{"class":88,"line":758},[86,8566,282],{"emptyLinePlaceholder":281},[86,8568,8569,8572,8574,8577,8579,8581,8584],{"class":88,"line":778},[86,8570,8571],{"class":313},"            tools ",[86,8573,853],{"class":342},[86,8575,8576],{"class":309}," await",[86,8578,8546],{"class":313},[86,8580,805],{"class":342},[86,8582,8583],{"class":313},"list_tools",[86,8585,1549],{"class":342},[86,8587,8588,8591,8593,8595,8598,8600],{"class":88,"line":789},[86,8589,8590],{"class":114},"            print",[86,8592,769],{"class":342},[86,8594,466],{"class":138},[86,8596,8597],{"class":96},"=== tools/list ===",[86,8599,466],{"class":138},[86,8601,873],{"class":342},[86,8603,8604,8606],{"class":88,"line":794},[86,8605,8590],{"class":114},[86,8607,1386],{"class":342},[86,8609,8610,8613,8615,8617],{"class":88,"line":811},[86,8611,8612],{"class":313},"                json",[86,8614,805],{"class":342},[86,8616,6595],{"class":313},[86,8618,1386],{"class":342},[86,8620,8621,8624,8627,8629,8631,8633,8636,8638,8640,8642,8644,8647,8649,8652,8654,8657],{"class":88,"line":822},[86,8622,8623],{"class":342},"                    [",[86,8625,8626],{"class":313},"t",[86,8628,805],{"class":342},[86,8630,2799],{"class":313},[86,8632,769],{"class":342},[86,8634,8635],{"class":849},"exclude_none",[86,8637,853],{"class":342},[86,8639,3177],{"class":309},[86,8641,956],{"class":342},[86,8643,2081],{"class":309},[86,8645,8646],{"class":313}," t ",[86,8648,1648],{"class":309},[86,8650,8651],{"class":313}," tools",[86,8653,805],{"class":342},[86,8655,8656],{"class":313},"tools",[86,8658,8408],{"class":342},[86,8660,8661,8664,8666,8668],{"class":88,"line":832},[86,8662,8663],{"class":849},"                    ensure_ascii",[86,8665,853],{"class":342},[86,8667,6610],{"class":309},[86,8669,478],{"class":342},[86,8671,8672,8675,8677,8680],{"class":88,"line":876},[86,8673,8674],{"class":849},"                    indent",[86,8676,853],{"class":342},[86,8678,8679],{"class":107},"2",[86,8681,478],{"class":342},[86,8683,8684],{"class":88,"line":891},[86,8685,8686],{"class":342},"                )\n",[86,8688,8689],{"class":88,"line":896},[86,8690,8691],{"class":342},"            )\n",[86,8693,8694],{"class":88,"line":925},[86,8695,282],{"emptyLinePlaceholder":281},[86,8697,8698,8701,8703,8705,8707,8709,8712,8714,8716,8718,8720,8722],{"class":88,"line":933},[86,8699,8700],{"class":313},"            result ",[86,8702,853],{"class":342},[86,8704,8576],{"class":309},[86,8706,8546],{"class":313},[86,8708,805],{"class":342},[86,8710,8711],{"class":313},"call_tool",[86,8713,769],{"class":342},[86,8715,466],{"class":138},[86,8717,8059],{"class":96},[86,8719,466],{"class":138},[86,8721,343],{"class":342},[86,8723,8724],{"class":342}," {})\n",[86,8726,8727,8729,8731,8733,8736,8738],{"class":88,"line":967},[86,8728,8590],{"class":114},[86,8730,769],{"class":342},[86,8732,466],{"class":138},[86,8734,8735],{"class":96},"=== tools/call get_trial_balance ===",[86,8737,466],{"class":138},[86,8739,873],{"class":342},[86,8741,8742,8744,8746,8748,8750,8752,8754,8757,8759,8761,8763,8765,8767,8769,8771,8773,8775,8777,8779,8782,8784,8786],{"class":88,"line":987},[86,8743,8590],{"class":114},[86,8745,769],{"class":342},[86,8747,3723],{"class":313},[86,8749,805],{"class":342},[86,8751,6595],{"class":313},[86,8753,769],{"class":342},[86,8755,8756],{"class":313},"result",[86,8758,805],{"class":342},[86,8760,2799],{"class":313},[86,8762,769],{"class":342},[86,8764,8635],{"class":849},[86,8766,853],{"class":342},[86,8768,3177],{"class":309},[86,8770,2752],{"class":342},[86,8772,6605],{"class":849},[86,8774,853],{"class":342},[86,8776,6610],{"class":309},[86,8778,343],{"class":342},[86,8780,8781],{"class":849}," indent",[86,8783,853],{"class":342},[86,8785,8679],{"class":107},[86,8787,7459],{"class":342},[86,8789,8790],{"class":88,"line":1013},[86,8791,282],{"emptyLinePlaceholder":281},[86,8793,8794],{"class":88,"line":1053},[86,8795,282],{"emptyLinePlaceholder":281},[86,8797,8798,8800,8802,8804,8806,8808,8810],{"class":88,"line":1062},[86,8799,1693],{"class":309},[86,8801,8094],{"class":114},[86,8803,2638],{"class":761},[86,8805,139],{"class":138},[86,8807,8101],{"class":96},[86,8809,466],{"class":138},[86,8811,964],{"class":342},[86,8813,8814,8817,8819,8821,8823,8826],{"class":88,"line":1067},[86,8815,8816],{"class":313},"    asyncio",[86,8818,805],{"class":342},[86,8820,8115],{"class":313},[86,8822,769],{"class":342},[86,8824,8825],{"class":313},"main",[86,8827,2802],{"class":342},[14,8829,8830],{},"APIを起動したまま、別のターミナルで実行する（Step 1のcurlで登録した仕訳が残っていると試算表の数値が変わる。シード状態の出力を再現したいときは、uvicornをいったん再起動してから実行する）。",[77,8832,8834],{"className":79,"code":8833,"language":81,"meta":82,"style":82},"export JOURNAL_API_KEY=\"sk-journal-tutorial-2026\"\nuv run inspect_tools.py\n",[51,8835,8836,8850],{"__ignoreMap":82},[86,8837,8838,8840,8842,8844,8846,8848],{"class":88,"line":89},[86,8839,53],{"class":761},[86,8841,5826],{"class":849},[86,8843,853],{"class":342},[86,8845,466],{"class":138},[86,8847,410],{"class":96},[86,8849,145],{"class":138},[86,8851,8852,8854,8856],{"class":88,"line":111},[86,8853,93],{"class":92},[86,8855,5693],{"class":96},[86,8857,8858],{"class":96}," inspect_tools.py\n",[14,8860,8861,8862,8864,8865,8867],{},"出てきた ",[51,8863,8157],{}," の生JSONがこれだ。エージェントが接続時に受け取るものと同じで、名前と説明文と入出力スキーマとannotations、それがすべてだ。",[51,8866,6006],{}," の実装コードも、APIキーも、この中には存在しない。",[77,8869,8872],{"className":8870,"code":8871,"language":3723,"meta":82,"style":82},"language-json shiki shiki-themes vitesse-light vitesse-light","[\n  {\n    \"name\": \"list_journal_entries\",\n    \"description\": \"仕訳の一覧を取得する。\\n\\n    month に \\\"YYYY-MM\\\"（例: \\\"2026-07\\\"）を渡すと、その月の仕訳だけに絞り込む。\\n    省略するとすべての仕訳を返す。各仕訳は id・date・debit_account（借方科目）・\\n    credit_account（貸方科目）・amount（金額・円）・description（摘要）を持つ。\\n    \",\n    \"inputSchema\": {\n      \"properties\": {\n        \"month\": {\n          \"anyOf\": [{ \"type\": \"string\" }, { \"type\": \"null\" }],\n          \"default\": null,\n          \"title\": \"Month\"\n        }\n      },\n      \"title\": \"list_journal_entriesArguments\",\n      \"type\": \"object\"\n    },\n    \"outputSchema\": {\n      \"properties\": {\n        \"result\": {\n          \"items\": { \"additionalProperties\": true, \"type\": \"object\" },\n          \"title\": \"Result\",\n          \"type\": \"array\"\n        }\n      },\n      \"required\": [\"result\"],\n      \"title\": \"list_journal_entriesOutput\",\n      \"type\": \"object\"\n    },\n    \"annotations\": { \"readOnlyHint\": true }\n  },\n  {\n    \"name\": \"register_journal_entry\",\n    \"description\": \"仕訳を1件登録する（1行仕訳のみ。借方1科目・貸方1科目）。\\n\\n    date は \\\"YYYY-MM-DD\\\" 形式、amount は正の整数（円）。\\n    勘定科目はホワイトリスト制で、使えるのは次の7科目だけ:\\n    現金、普通預金、買掛金、資本金、売上高、仕入高、消耗品費。\\n    この一覧にない科目（例: 雑費）を渡すと入力エラーになる。\\n    \",\n    \"inputSchema\": {\n      \"properties\": {\n        \"date\": { \"title\": \"Date\", \"type\": \"string\" },\n        \"debit_account\": { \"title\": \"Debit Account\", \"type\": \"string\" },\n        \"credit_account\": { \"title\": \"Credit Account\", \"type\": \"string\" },\n        \"amount\": { \"title\": \"Amount\", \"type\": \"integer\" },\n        \"description\": { \"default\": \"\", \"title\": \"Description\", \"type\": \"string\" }\n      },\n      \"required\": [\"date\", \"debit_account\", \"credit_account\", \"amount\"],\n      \"title\": \"register_journal_entryArguments\",\n      \"type\": \"object\"\n    },\n    \"annotations\": { \"readOnlyHint\": false, \"destructiveHint\": false }\n  },\n  {\n    \"name\": \"get_trial_balance\",\n    \"description\": \"勘定科目別の残高試算表を取得する。\\n\\n    科目ごとに借方合計（debit_total）・貸方合計（credit_total）・残高（balance）・\\n    残高の側（balance_side）・区分から決まる正常残高側（normal_side）を返す。\\n    両者が食い違う科目には is_abnormal: true が立つ（例: 現金なのに貸方残）。\\n    末尾の total で借方合計と貸方合計の一致（貸借一致）を確認できる。\\n    \",\n    \"inputSchema\": {\n      \"properties\": {},\n      \"title\": \"get_trial_balanceArguments\",\n      \"type\": \"object\"\n    },\n    \"annotations\": { \"readOnlyHint\": true }\n  }\n]\n",[51,8873,8874,8879,8884,8904,8961,8974,8988,9000,9054,9069,9087,9092,9097,9116,9133,9138,9151,9163,9175,9218,9237,9254,9258,9262,9283,9302,9318,9322,9347,9352,9356,9374,9425,9437,9449,9494,9539,9584,9630,9687,9691,9735,9754,9770,9774,9812,9816,9820,9838,9879,9891,9904,9923,9939,9943,9967,9972],{"__ignoreMap":82},[86,8875,8876],{"class":88,"line":89},[86,8877,8878],{"class":342},"[\n",[86,8880,8881],{"class":88,"line":111},[86,8882,8883],{"class":342},"  {\n",[86,8885,8886,8889,8892,8894,8896,8898,8900,8902],{"class":88,"line":121},[86,8887,460],{"class":8888},"sqvqQ",[86,8890,8891],{"class":114},"name",[86,8893,466],{"class":8888},[86,8895,432],{"class":342},[86,8897,139],{"class":138},[86,8899,7624],{"class":96},[86,8901,466],{"class":138},[86,8903,478],{"class":342},[86,8905,8906,8908,8910,8912,8914,8916,8919,8922,8925,8928,8931,8933,8936,8938,8940,8942,8945,8947,8950,8952,8955,8957,8959],{"class":88,"line":148},[86,8907,460],{"class":8888},[86,8909,4430],{"class":114},[86,8911,466],{"class":8888},[86,8913,432],{"class":342},[86,8915,139],{"class":138},[86,8917,8918],{"class":96},"仕訳の一覧を取得する。",[86,8920,8921],{"class":103},"\\n\\n",[86,8923,8924],{"class":96},"    month に ",[86,8926,8927],{"class":103},"\\\"",[86,8929,8930],{"class":96},"YYYY-MM",[86,8932,8927],{"class":103},[86,8934,8935],{"class":96},"（例: ",[86,8937,8927],{"class":103},[86,8939,4899],{"class":96},[86,8941,8927],{"class":103},[86,8943,8944],{"class":96},"）を渡すと、その月の仕訳だけに絞り込む。",[86,8946,6619],{"class":103},[86,8948,8949],{"class":96},"    省略するとすべての仕訳を返す。各仕訳は id・date・debit_account（借方科目）・",[86,8951,6619],{"class":103},[86,8953,8954],{"class":96},"    credit_account（貸方科目）・amount（金額・円）・description（摘要）を持つ。",[86,8956,6619],{"class":103},[86,8958,460],{"class":138},[86,8960,478],{"class":342},[86,8962,8963,8965,8968,8970,8972],{"class":88,"line":295},[86,8964,460],{"class":8888},[86,8966,8967],{"class":114},"inputSchema",[86,8969,466],{"class":8888},[86,8971,432],{"class":342},[86,8973,454],{"class":342},[86,8975,8976,8979,8982,8984,8986],{"class":88,"line":301},[86,8977,8978],{"class":8888},"      \"",[86,8980,8981],{"class":114},"properties",[86,8983,466],{"class":8888},[86,8985,432],{"class":342},[86,8987,454],{"class":342},[86,8989,8990,8992,8994,8996,8998],{"class":88,"line":306},[86,8991,2138],{"class":8888},[86,8993,4890],{"class":114},[86,8995,466],{"class":8888},[86,8997,432],{"class":342},[86,8999,454],{"class":342},[86,9001,9002,9005,9008,9010,9012,9015,9017,9020,9022,9024,9026,9029,9031,9034,9036,9038,9040,9042,9044,9046,9049,9051],{"class":88,"line":323},[86,9003,9004],{"class":8888},"          \"",[86,9006,9007],{"class":114},"anyOf",[86,9009,466],{"class":8888},[86,9011,432],{"class":342},[86,9013,9014],{"class":342}," [{",[86,9016,139],{"class":8888},[86,9018,9019],{"class":114},"type",[86,9021,466],{"class":8888},[86,9023,432],{"class":342},[86,9025,139],{"class":138},[86,9027,9028],{"class":96},"string",[86,9030,466],{"class":138},[86,9032,9033],{"class":342}," },",[86,9035,3130],{"class":342},[86,9037,139],{"class":8888},[86,9039,9019],{"class":114},[86,9041,466],{"class":8888},[86,9043,432],{"class":342},[86,9045,139],{"class":138},[86,9047,9048],{"class":96},"null",[86,9050,466],{"class":138},[86,9052,9053],{"class":342}," }],\n",[86,9055,9056,9058,9060,9062,9064,9067],{"class":88,"line":328},[86,9057,9004],{"class":8888},[86,9059,2270],{"class":114},[86,9061,466],{"class":8888},[86,9063,432],{"class":342},[86,9065,9066],{"class":309}," null",[86,9068,478],{"class":342},[86,9070,9071,9073,9076,9078,9080,9082,9085],{"class":88,"line":364},[86,9072,9004],{"class":8888},[86,9074,9075],{"class":114},"title",[86,9077,466],{"class":8888},[86,9079,432],{"class":342},[86,9081,139],{"class":138},[86,9083,9084],{"class":96},"Month",[86,9086,145],{"class":138},[86,9088,9089],{"class":88,"line":387},[86,9090,9091],{"class":342},"        }\n",[86,9093,9094],{"class":88,"line":392},[86,9095,9096],{"class":342},"      },\n",[86,9098,9099,9101,9103,9105,9107,9109,9112,9114],{"class":88,"line":399},[86,9100,8978],{"class":8888},[86,9102,9075],{"class":114},[86,9104,466],{"class":8888},[86,9106,432],{"class":342},[86,9108,139],{"class":138},[86,9110,9111],{"class":96},"list_journal_entriesArguments",[86,9113,466],{"class":138},[86,9115,478],{"class":342},[86,9117,9118,9120,9122,9124,9126,9128,9131],{"class":88,"line":415},[86,9119,8978],{"class":8888},[86,9121,9019],{"class":114},[86,9123,466],{"class":8888},[86,9125,432],{"class":342},[86,9127,139],{"class":138},[86,9129,9130],{"class":96},"object",[86,9132,145],{"class":138},[86,9134,9135],{"class":88,"line":420},[86,9136,9137],{"class":342},"    },\n",[86,9139,9140,9142,9145,9147,9149],{"class":88,"line":426},[86,9141,460],{"class":8888},[86,9143,9144],{"class":114},"outputSchema",[86,9146,466],{"class":8888},[86,9148,432],{"class":342},[86,9150,454],{"class":342},[86,9152,9153,9155,9157,9159,9161],{"class":88,"line":457},[86,9154,8978],{"class":8888},[86,9156,8981],{"class":114},[86,9158,466],{"class":8888},[86,9160,432],{"class":342},[86,9162,454],{"class":342},[86,9164,9165,9167,9169,9171,9173],{"class":88,"line":481},[86,9166,2138],{"class":8888},[86,9168,8756],{"class":114},[86,9170,466],{"class":8888},[86,9172,432],{"class":342},[86,9174,454],{"class":342},[86,9176,9177,9179,9181,9183,9185,9187,9189,9192,9194,9196,9199,9201,9203,9205,9207,9209,9211,9213,9215],{"class":88,"line":501},[86,9178,9004],{"class":8888},[86,9180,1655],{"class":114},[86,9182,466],{"class":8888},[86,9184,432],{"class":342},[86,9186,3130],{"class":342},[86,9188,139],{"class":8888},[86,9190,9191],{"class":114},"additionalProperties",[86,9193,466],{"class":8888},[86,9195,432],{"class":342},[86,9197,9198],{"class":309}," true",[86,9200,343],{"class":342},[86,9202,139],{"class":8888},[86,9204,9019],{"class":114},[86,9206,466],{"class":8888},[86,9208,432],{"class":342},[86,9210,139],{"class":138},[86,9212,9130],{"class":96},[86,9214,466],{"class":138},[86,9216,9217],{"class":342}," },\n",[86,9219,9220,9222,9224,9226,9228,9230,9233,9235],{"class":88,"line":522},[86,9221,9004],{"class":8888},[86,9223,9075],{"class":114},[86,9225,466],{"class":8888},[86,9227,432],{"class":342},[86,9229,139],{"class":138},[86,9231,9232],{"class":96},"Result",[86,9234,466],{"class":138},[86,9236,478],{"class":342},[86,9238,9239,9241,9243,9245,9247,9249,9252],{"class":88,"line":543},[86,9240,9004],{"class":8888},[86,9242,9019],{"class":114},[86,9244,466],{"class":8888},[86,9246,432],{"class":342},[86,9248,139],{"class":138},[86,9250,9251],{"class":96},"array",[86,9253,145],{"class":138},[86,9255,9256],{"class":88,"line":564},[86,9257,9091],{"class":342},[86,9259,9260],{"class":88,"line":585},[86,9261,9096],{"class":342},[86,9263,9264,9266,9269,9271,9273,9275,9277,9279,9281],{"class":88,"line":605},[86,9265,8978],{"class":8888},[86,9267,9268],{"class":114},"required",[86,9270,466],{"class":8888},[86,9272,432],{"class":342},[86,9274,2598],{"class":342},[86,9276,466],{"class":138},[86,9278,8756],{"class":96},[86,9280,466],{"class":138},[86,9282,8408],{"class":342},[86,9284,9285,9287,9289,9291,9293,9295,9298,9300],{"class":88,"line":611},[86,9286,8978],{"class":8888},[86,9288,9075],{"class":114},[86,9290,466],{"class":8888},[86,9292,432],{"class":342},[86,9294,139],{"class":138},[86,9296,9297],{"class":96},"list_journal_entriesOutput",[86,9299,466],{"class":138},[86,9301,478],{"class":342},[86,9303,9304,9306,9308,9310,9312,9314,9316],{"class":88,"line":616},[86,9305,8978],{"class":8888},[86,9307,9019],{"class":114},[86,9309,466],{"class":8888},[86,9311,432],{"class":342},[86,9313,139],{"class":138},[86,9315,9130],{"class":96},[86,9317,145],{"class":138},[86,9319,9320],{"class":88,"line":622},[86,9321,9137],{"class":342},[86,9323,9324,9326,9328,9330,9332,9334,9336,9338,9340,9342,9344],{"class":88,"line":646},[86,9325,460],{"class":8888},[86,9327,7501],{"class":114},[86,9329,466],{"class":8888},[86,9331,432],{"class":342},[86,9333,3130],{"class":342},[86,9335,139],{"class":8888},[86,9337,7511],{"class":114},[86,9339,466],{"class":8888},[86,9341,432],{"class":342},[86,9343,9198],{"class":309},[86,9345,9346],{"class":342}," }\n",[86,9348,9349],{"class":88,"line":666},[86,9350,9351],{"class":342},"  },\n",[86,9353,9354],{"class":88,"line":685},[86,9355,8883],{"class":342},[86,9357,9358,9360,9362,9364,9366,9368,9370,9372],{"class":88,"line":705},[86,9359,460],{"class":8888},[86,9361,8891],{"class":114},[86,9363,466],{"class":8888},[86,9365,432],{"class":342},[86,9367,139],{"class":138},[86,9369,7932],{"class":96},[86,9371,466],{"class":138},[86,9373,478],{"class":342},[86,9375,9376,9378,9380,9382,9384,9386,9389,9391,9394,9396,9399,9401,9404,9406,9409,9411,9414,9416,9419,9421,9423],{"class":88,"line":724},[86,9377,460],{"class":8888},[86,9379,4430],{"class":114},[86,9381,466],{"class":8888},[86,9383,432],{"class":342},[86,9385,139],{"class":138},[86,9387,9388],{"class":96},"仕訳を1件登録する（1行仕訳のみ。借方1科目・貸方1科目）。",[86,9390,8921],{"class":103},[86,9392,9393],{"class":96},"    date は ",[86,9395,8927],{"class":103},[86,9397,9398],{"class":96},"YYYY-MM-DD",[86,9400,8927],{"class":103},[86,9402,9403],{"class":96}," 形式、amount は正の整数（円）。",[86,9405,6619],{"class":103},[86,9407,9408],{"class":96},"    勘定科目はホワイトリスト制で、使えるのは次の7科目だけ:",[86,9410,6619],{"class":103},[86,9412,9413],{"class":96},"    現金、普通預金、買掛金、資本金、売上高、仕入高、消耗品費。",[86,9415,6619],{"class":103},[86,9417,9418],{"class":96},"    この一覧にない科目（例: 雑費）を渡すと入力エラーになる。",[86,9420,6619],{"class":103},[86,9422,460],{"class":138},[86,9424,478],{"class":342},[86,9426,9427,9429,9431,9433,9435],{"class":88,"line":743},[86,9428,460],{"class":8888},[86,9430,8967],{"class":114},[86,9432,466],{"class":8888},[86,9434,432],{"class":342},[86,9436,454],{"class":342},[86,9438,9439,9441,9443,9445,9447],{"class":88,"line":748},[86,9440,8978],{"class":8888},[86,9442,8981],{"class":114},[86,9444,466],{"class":8888},[86,9446,432],{"class":342},[86,9448,454],{"class":342},[86,9450,9451,9453,9455,9457,9459,9461,9463,9465,9467,9469,9471,9474,9476,9478,9480,9482,9484,9486,9488,9490,9492],{"class":88,"line":753},[86,9452,2138],{"class":8888},[86,9454,2619],{"class":114},[86,9456,466],{"class":8888},[86,9458,432],{"class":342},[86,9460,3130],{"class":342},[86,9462,139],{"class":8888},[86,9464,9075],{"class":114},[86,9466,466],{"class":8888},[86,9468,432],{"class":342},[86,9470,139],{"class":138},[86,9472,9473],{"class":96},"Date",[86,9475,466],{"class":138},[86,9477,343],{"class":342},[86,9479,139],{"class":8888},[86,9481,9019],{"class":114},[86,9483,466],{"class":8888},[86,9485,432],{"class":342},[86,9487,139],{"class":138},[86,9489,9028],{"class":96},[86,9491,466],{"class":138},[86,9493,9217],{"class":342},[86,9495,9496,9498,9500,9502,9504,9506,9508,9510,9512,9514,9516,9519,9521,9523,9525,9527,9529,9531,9533,9535,9537],{"class":88,"line":758},[86,9497,2138],{"class":8888},[86,9499,909],{"class":114},[86,9501,466],{"class":8888},[86,9503,432],{"class":342},[86,9505,3130],{"class":342},[86,9507,139],{"class":8888},[86,9509,9075],{"class":114},[86,9511,466],{"class":8888},[86,9513,432],{"class":342},[86,9515,139],{"class":138},[86,9517,9518],{"class":96},"Debit Account",[86,9520,466],{"class":138},[86,9522,343],{"class":342},[86,9524,139],{"class":8888},[86,9526,9019],{"class":114},[86,9528,466],{"class":8888},[86,9530,432],{"class":342},[86,9532,139],{"class":138},[86,9534,9028],{"class":96},[86,9536,466],{"class":138},[86,9538,9217],{"class":342},[86,9540,9541,9543,9545,9547,9549,9551,9553,9555,9557,9559,9561,9564,9566,9568,9570,9572,9574,9576,9578,9580,9582],{"class":88,"line":778},[86,9542,2138],{"class":8888},[86,9544,918],{"class":114},[86,9546,466],{"class":8888},[86,9548,432],{"class":342},[86,9550,3130],{"class":342},[86,9552,139],{"class":8888},[86,9554,9075],{"class":114},[86,9556,466],{"class":8888},[86,9558,432],{"class":342},[86,9560,139],{"class":138},[86,9562,9563],{"class":96},"Credit Account",[86,9565,466],{"class":138},[86,9567,343],{"class":342},[86,9569,139],{"class":8888},[86,9571,9019],{"class":114},[86,9573,466],{"class":8888},[86,9575,432],{"class":342},[86,9577,139],{"class":138},[86,9579,9028],{"class":96},[86,9581,466],{"class":138},[86,9583,9217],{"class":342},[86,9585,9586,9588,9590,9592,9594,9596,9598,9600,9602,9604,9606,9609,9611,9613,9615,9617,9619,9621,9623,9626,9628],{"class":88,"line":789},[86,9587,2138],{"class":8888},[86,9589,4414],{"class":114},[86,9591,466],{"class":8888},[86,9593,432],{"class":342},[86,9595,3130],{"class":342},[86,9597,139],{"class":8888},[86,9599,9075],{"class":114},[86,9601,466],{"class":8888},[86,9603,432],{"class":342},[86,9605,139],{"class":138},[86,9607,9608],{"class":96},"Amount",[86,9610,466],{"class":138},[86,9612,343],{"class":342},[86,9614,139],{"class":8888},[86,9616,9019],{"class":114},[86,9618,466],{"class":8888},[86,9620,432],{"class":342},[86,9622,139],{"class":138},[86,9624,9625],{"class":96},"integer",[86,9627,466],{"class":138},[86,9629,9217],{"class":342},[86,9631,9632,9634,9636,9638,9640,9642,9644,9646,9648,9650,9652,9654,9656,9658,9660,9662,9664,9667,9669,9671,9673,9675,9677,9679,9681,9683,9685],{"class":88,"line":794},[86,9633,2138],{"class":8888},[86,9635,4430],{"class":114},[86,9637,466],{"class":8888},[86,9639,432],{"class":342},[86,9641,3130],{"class":342},[86,9643,139],{"class":8888},[86,9645,2270],{"class":114},[86,9647,466],{"class":8888},[86,9649,432],{"class":342},[86,9651,6214],{"class":138},[86,9653,343],{"class":342},[86,9655,139],{"class":8888},[86,9657,9075],{"class":114},[86,9659,466],{"class":8888},[86,9661,432],{"class":342},[86,9663,139],{"class":138},[86,9665,9666],{"class":96},"Description",[86,9668,466],{"class":138},[86,9670,343],{"class":342},[86,9672,139],{"class":8888},[86,9674,9019],{"class":114},[86,9676,466],{"class":8888},[86,9678,432],{"class":342},[86,9680,139],{"class":138},[86,9682,9028],{"class":96},[86,9684,466],{"class":138},[86,9686,9346],{"class":342},[86,9688,9689],{"class":88,"line":811},[86,9690,9096],{"class":342},[86,9692,9693,9695,9697,9699,9701,9703,9705,9707,9709,9711,9713,9715,9717,9719,9721,9723,9725,9727,9729,9731,9733],{"class":88,"line":822},[86,9694,8978],{"class":8888},[86,9696,9268],{"class":114},[86,9698,466],{"class":8888},[86,9700,432],{"class":342},[86,9702,2598],{"class":342},[86,9704,466],{"class":138},[86,9706,2619],{"class":96},[86,9708,466],{"class":138},[86,9710,343],{"class":342},[86,9712,139],{"class":138},[86,9714,909],{"class":96},[86,9716,466],{"class":138},[86,9718,343],{"class":342},[86,9720,139],{"class":138},[86,9722,918],{"class":96},[86,9724,466],{"class":138},[86,9726,343],{"class":342},[86,9728,139],{"class":138},[86,9730,4414],{"class":96},[86,9732,466],{"class":138},[86,9734,8408],{"class":342},[86,9736,9737,9739,9741,9743,9745,9747,9750,9752],{"class":88,"line":832},[86,9738,8978],{"class":8888},[86,9740,9075],{"class":114},[86,9742,466],{"class":8888},[86,9744,432],{"class":342},[86,9746,139],{"class":138},[86,9748,9749],{"class":96},"register_journal_entryArguments",[86,9751,466],{"class":138},[86,9753,478],{"class":342},[86,9755,9756,9758,9760,9762,9764,9766,9768],{"class":88,"line":876},[86,9757,8978],{"class":8888},[86,9759,9019],{"class":114},[86,9761,466],{"class":8888},[86,9763,432],{"class":342},[86,9765,139],{"class":138},[86,9767,9130],{"class":96},[86,9769,145],{"class":138},[86,9771,9772],{"class":88,"line":891},[86,9773,9137],{"class":342},[86,9775,9776,9778,9780,9782,9784,9786,9788,9790,9792,9794,9797,9799,9801,9804,9806,9808,9810],{"class":88,"line":896},[86,9777,460],{"class":8888},[86,9779,7501],{"class":114},[86,9781,466],{"class":8888},[86,9783,432],{"class":342},[86,9785,3130],{"class":342},[86,9787,139],{"class":8888},[86,9789,7511],{"class":114},[86,9791,466],{"class":8888},[86,9793,432],{"class":342},[86,9795,9796],{"class":309}," false",[86,9798,343],{"class":342},[86,9800,139],{"class":8888},[86,9802,9803],{"class":114},"destructiveHint",[86,9805,466],{"class":8888},[86,9807,432],{"class":342},[86,9809,9796],{"class":309},[86,9811,9346],{"class":342},[86,9813,9814],{"class":88,"line":925},[86,9815,9351],{"class":342},[86,9817,9818],{"class":88,"line":933},[86,9819,8883],{"class":342},[86,9821,9822,9824,9826,9828,9830,9832,9834,9836],{"class":88,"line":967},[86,9823,460],{"class":8888},[86,9825,8891],{"class":114},[86,9827,466],{"class":8888},[86,9829,432],{"class":342},[86,9831,139],{"class":138},[86,9833,8059],{"class":96},[86,9835,466],{"class":138},[86,9837,478],{"class":342},[86,9839,9840,9842,9844,9846,9848,9850,9853,9855,9858,9860,9863,9865,9868,9870,9873,9875,9877],{"class":88,"line":987},[86,9841,460],{"class":8888},[86,9843,4430],{"class":114},[86,9845,466],{"class":8888},[86,9847,432],{"class":342},[86,9849,139],{"class":138},[86,9851,9852],{"class":96},"勘定科目別の残高試算表を取得する。",[86,9854,8921],{"class":103},[86,9856,9857],{"class":96},"    科目ごとに借方合計（debit_total）・貸方合計（credit_total）・残高（balance）・",[86,9859,6619],{"class":103},[86,9861,9862],{"class":96},"    残高の側（balance_side）・区分から決まる正常残高側（normal_side）を返す。",[86,9864,6619],{"class":103},[86,9866,9867],{"class":96},"    両者が食い違う科目には is_abnormal: true が立つ（例: 現金なのに貸方残）。",[86,9869,6619],{"class":103},[86,9871,9872],{"class":96},"    末尾の total で借方合計と貸方合計の一致（貸借一致）を確認できる。",[86,9874,6619],{"class":103},[86,9876,460],{"class":138},[86,9878,478],{"class":342},[86,9880,9881,9883,9885,9887,9889],{"class":88,"line":1013},[86,9882,460],{"class":8888},[86,9884,8967],{"class":114},[86,9886,466],{"class":8888},[86,9888,432],{"class":342},[86,9890,454],{"class":342},[86,9892,9893,9895,9897,9899,9901],{"class":88,"line":1053},[86,9894,8978],{"class":8888},[86,9896,8981],{"class":114},[86,9898,466],{"class":8888},[86,9900,432],{"class":342},[86,9902,9903],{"class":342}," {},\n",[86,9905,9906,9908,9910,9912,9914,9916,9919,9921],{"class":88,"line":1062},[86,9907,8978],{"class":8888},[86,9909,9075],{"class":114},[86,9911,466],{"class":8888},[86,9913,432],{"class":342},[86,9915,139],{"class":138},[86,9917,9918],{"class":96},"get_trial_balanceArguments",[86,9920,466],{"class":138},[86,9922,478],{"class":342},[86,9924,9925,9927,9929,9931,9933,9935,9937],{"class":88,"line":1067},[86,9926,8978],{"class":8888},[86,9928,9019],{"class":114},[86,9930,466],{"class":8888},[86,9932,432],{"class":342},[86,9934,139],{"class":138},[86,9936,9130],{"class":96},[86,9938,145],{"class":138},[86,9940,9941],{"class":88,"line":1072},[86,9942,9137],{"class":342},[86,9944,9945,9947,9949,9951,9953,9955,9957,9959,9961,9963,9965],{"class":88,"line":1087},[86,9946,460],{"class":8888},[86,9948,7501],{"class":114},[86,9950,466],{"class":8888},[86,9952,432],{"class":342},[86,9954,3130],{"class":342},[86,9956,139],{"class":8888},[86,9958,7511],{"class":114},[86,9960,466],{"class":8888},[86,9962,432],{"class":342},[86,9964,9198],{"class":309},[86,9966,9346],{"class":342},[86,9968,9969],{"class":88,"line":1098},[86,9970,9971],{"class":342},"  }\n",[86,9973,9974],{"class":88,"line":1103},[86,9975,1866],{"class":342},[14,9977,9978,9980,9981,9984],{},[51,9979,7932],{}," の引数を見てほしい。date、借方科目、貸方科目、金額、摘要。",[248,9982,9983],{},"キーというフィールドが存在しない","。エージェントが何をどう間違えても、ツール呼び出しの引数にキーを書く場所自体がない。Before体験で「エージェントの毎回の振る舞い」に頼っていた秘密の管理が、宣言の形そのものに置き換わっている。",[14,9986,9987,9988,9990],{},"続けて ",[51,9989,8161],{}," の結果（シード状態の残高試算表）も返ってきた。",[77,9992,9994],{"className":8870,"code":9993,"language":3723,"meta":82,"style":82},"{\n  \"content\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"{\\n  \\\"accounts\\\": [\\n    {\\n      \\\"account\\\": \\\"現金\\\",\\n      \\\"category\\\": \\\"資産\\\",\\n      \\\"debit_total\\\": 180000,\\n      \\\"credit_total\\\": 85000,\\n      \\\"balance_side\\\": \\\"借方\\\",\\n      \\\"balance\\\": 95000,\\n      \\\"normal_side\\\": \\\"借方\\\",\\n      \\\"is_abnormal\\\": false\\n    },\\n    （中略: 期待値表と同じ6科目、total は 265000 / 265000 / in_balance: true）\\n  ]\\n}\"\n    }\n  ],\n  \"isError\": false\n}\n",[51,9995,9996,10001,10015,10020,10038,10207,10211,10216,10230],{"__ignoreMap":82},[86,9997,9998],{"class":88,"line":89},[86,9999,10000],{"class":342},"{\n",[86,10002,10003,10006,10009,10011,10013],{"class":88,"line":111},[86,10004,10005],{"class":8888},"  \"",[86,10007,10008],{"class":114},"content",[86,10010,466],{"class":8888},[86,10012,432],{"class":342},[86,10014,1141],{"class":342},[86,10016,10017],{"class":88,"line":121},[86,10018,10019],{"class":342},"    {\n",[86,10021,10022,10024,10026,10028,10030,10032,10034,10036],{"class":88,"line":148},[86,10023,8978],{"class":8888},[86,10025,9019],{"class":114},[86,10027,466],{"class":8888},[86,10029,432],{"class":342},[86,10031,139],{"class":138},[86,10033,5676],{"class":96},[86,10035,466],{"class":138},[86,10037,478],{"class":342},[86,10039,10040,10042,10044,10046,10048,10050,10052,10054,10057,10059,10061,10064,10066,10069,10071,10074,10076,10078,10080,10082,10084,10086,10088,10090,10092,10094,10096,10098,10100,10102,10104,10106,10108,10110,10112,10114,10117,10119,10121,10123,10125,10128,10130,10132,10134,10136,10138,10140,10142,10144,10146,10148,10150,10152,10154,10157,10159,10161,10163,10165,10167,10169,10171,10173,10175,10177,10179,10181,10183,10186,10188,10191,10193,10196,10198,10201,10203,10205],{"class":88,"line":295},[86,10041,8978],{"class":8888},[86,10043,5676],{"class":114},[86,10045,466],{"class":8888},[86,10047,432],{"class":342},[86,10049,139],{"class":138},[86,10051,1030],{"class":96},[86,10053,6619],{"class":103},[86,10055,10056],{"class":103},"  \\\"",[86,10058,2141],{"class":96},[86,10060,8927],{"class":103},[86,10062,10063],{"class":96},": [",[86,10065,6619],{"class":103},[86,10067,10068],{"class":96},"    {",[86,10070,6619],{"class":103},[86,10072,10073],{"class":103},"      \\\"",[86,10075,1894],{"class":96},[86,10077,8927],{"class":103},[86,10079,3933],{"class":96},[86,10081,8927],{"class":103},[86,10083,463],{"class":96},[86,10085,8927],{"class":103},[86,10087,343],{"class":96},[86,10089,6619],{"class":103},[86,10091,10073],{"class":103},[86,10093,1863],{"class":96},[86,10095,8927],{"class":103},[86,10097,3933],{"class":96},[86,10099,8927],{"class":103},[86,10101,473],{"class":96},[86,10103,8927],{"class":103},[86,10105,343],{"class":96},[86,10107,6619],{"class":103},[86,10109,10073],{"class":103},[86,10111,1926],{"class":96},[86,10113,8927],{"class":103},[86,10115,10116],{"class":96},": 180000,",[86,10118,6619],{"class":103},[86,10120,10073],{"class":103},[86,10122,1943],{"class":96},[86,10124,8927],{"class":103},[86,10126,10127],{"class":96},": 85000,",[86,10129,6619],{"class":103},[86,10131,10073],{"class":103},[86,10133,1960],{"class":96},[86,10135,8927],{"class":103},[86,10137,3933],{"class":96},[86,10139,8927],{"class":103},[86,10141,659],{"class":96},[86,10143,8927],{"class":103},[86,10145,343],{"class":96},[86,10147,6619],{"class":103},[86,10149,10073],{"class":103},[86,10151,1977],{"class":96},[86,10153,8927],{"class":103},[86,10155,10156],{"class":96},": 95000,",[86,10158,6619],{"class":103},[86,10160,10073],{"class":103},[86,10162,1999],{"class":96},[86,10164,8927],{"class":103},[86,10166,3933],{"class":96},[86,10168,8927],{"class":103},[86,10170,659],{"class":96},[86,10172,8927],{"class":103},[86,10174,343],{"class":96},[86,10176,6619],{"class":103},[86,10178,10073],{"class":103},[86,10180,2016],{"class":96},[86,10182,8927],{"class":103},[86,10184,10185],{"class":96},": false",[86,10187,6619],{"class":103},[86,10189,10190],{"class":96},"    },",[86,10192,6619],{"class":103},[86,10194,10195],{"class":96},"    （中略: 期待値表と同じ6科目、total は 265000 / 265000 / in_balance: true）",[86,10197,6619],{"class":103},[86,10199,10200],{"class":96},"  ]",[86,10202,6619],{"class":103},[86,10204,1036],{"class":96},[86,10206,145],{"class":138},[86,10208,10209],{"class":88,"line":301},[86,10210,2229],{"class":342},[86,10212,10213],{"class":88,"line":306},[86,10214,10215],{"class":342},"  ],\n",[86,10217,10218,10220,10223,10225,10227],{"class":88,"line":323},[86,10219,10005],{"class":8888},[86,10221,10222],{"class":114},"isError",[86,10224,466],{"class":8888},[86,10226,432],{"class":342},[86,10228,10229],{"class":309}," false\n",[86,10231,10232],{"class":88,"line":328},[86,10233,608],{"class":342},[5908,10235,10237],{"id":10236},"claude-codeにつなぎ同じ依頼文をもう一度投げるafter体験","Claude Codeにつなぎ、同じ依頼文をもう一度投げる（After体験）",[14,10239,10240,10241,10244,10245,10248],{},"接続はプロジェクト直下に ",[51,10242,10243],{},".mcp.json"," を置くだけだ。キーの値はファイルに書かず、",[51,10246,10247],{},"${JOURNAL_API_KEY}"," で環境変数から展開させる。",[77,10250,10252],{"className":8870,"code":10251,"language":3723,"meta":82,"style":82},"{\n  \"mcpServers\": {\n    \"journal\": {\n      \"command\": \"uv\",\n      \"args\": [\"run\", \"server.py\"],\n      \"env\": {\n        \"JOURNAL_API_KEY\": \"${JOURNAL_API_KEY}\",\n        \"JOURNAL_API_BASE_URL\": \"http://127.0.0.1:8000\"\n      }\n    }\n  }\n}\n",[51,10253,10254,10258,10271,10283,10302,10330,10343,10361,10377,10382,10386,10390],{"__ignoreMap":82},[86,10255,10256],{"class":88,"line":89},[86,10257,10000],{"class":342},[86,10259,10260,10262,10265,10267,10269],{"class":88,"line":111},[86,10261,10005],{"class":8888},[86,10263,10264],{"class":114},"mcpServers",[86,10266,466],{"class":8888},[86,10268,432],{"class":342},[86,10270,454],{"class":342},[86,10272,10273,10275,10277,10279,10281],{"class":88,"line":121},[86,10274,460],{"class":8888},[86,10276,6335],{"class":114},[86,10278,466],{"class":8888},[86,10280,432],{"class":342},[86,10282,454],{"class":342},[86,10284,10285,10287,10290,10292,10294,10296,10298,10300],{"class":88,"line":148},[86,10286,8978],{"class":8888},[86,10288,10289],{"class":114},"command",[86,10291,466],{"class":8888},[86,10293,432],{"class":342},[86,10295,139],{"class":138},[86,10297,93],{"class":96},[86,10299,466],{"class":138},[86,10301,478],{"class":342},[86,10303,10304,10306,10308,10310,10312,10314,10316,10318,10320,10322,10324,10326,10328],{"class":88,"line":295},[86,10305,8978],{"class":8888},[86,10307,6505],{"class":114},[86,10309,466],{"class":8888},[86,10311,432],{"class":342},[86,10313,2598],{"class":342},[86,10315,466],{"class":138},[86,10317,8115],{"class":96},[86,10319,466],{"class":138},[86,10321,343],{"class":342},[86,10323,139],{"class":138},[86,10325,6006],{"class":96},[86,10327,466],{"class":138},[86,10329,8408],{"class":342},[86,10331,10332,10334,10337,10339,10341],{"class":88,"line":301},[86,10333,8978],{"class":8888},[86,10335,10336],{"class":114},"env",[86,10338,466],{"class":8888},[86,10340,432],{"class":342},[86,10342,454],{"class":342},[86,10344,10345,10347,10349,10351,10353,10355,10357,10359],{"class":88,"line":306},[86,10346,2138],{"class":8888},[86,10348,5799],{"class":114},[86,10350,466],{"class":8888},[86,10352,432],{"class":342},[86,10354,139],{"class":138},[86,10356,10247],{"class":96},[86,10358,466],{"class":138},[86,10360,478],{"class":342},[86,10362,10363,10365,10367,10369,10371,10373,10375],{"class":88,"line":323},[86,10364,2138],{"class":8888},[86,10366,6240],{"class":114},[86,10368,466],{"class":8888},[86,10370,432],{"class":342},[86,10372,139],{"class":138},[86,10374,5899],{"class":96},[86,10376,145],{"class":138},[86,10378,10379],{"class":88,"line":328},[86,10380,10381],{"class":342},"      }\n",[86,10383,10384],{"class":88,"line":364},[86,10385,2229],{"class":342},[86,10387,10388],{"class":88,"line":387},[86,10389,9971],{"class":342},[86,10391,10392],{"class":88,"line":392},[86,10393,608],{"class":342},[14,10395,10396,10397,10400,10401,10403,10404,10406],{},"2つ注意がある。対話モードのClaude Codeでこのプロジェクトを開くと、初回に「.mcp.jsonのサーバーを信頼するか」の確認が出るので承認する（接続状態は ",[51,10398,10399],{},"/mcp"," コマンドで確認できる）。そして ",[51,10402,10247],{}," はclaudeを起動したシェルの環境変数から展開されるので、",[51,10405,53],{}," とclaudeの起動は必ず同じターミナルで行う。",[14,10408,10409],{},"APIをシード状態で再起動し、Before体験と同じ依頼文を投げる。実測に使ったコマンドはこれだ（前置きは「ミニ仕訳帳のMCPツール（journal）が接続されている。仕訳の操作はそのツールを使って。」の1行だけ。URLも認証方式も伝えていない）。",[77,10411,10413],{"className":79,"code":10412,"language":81,"meta":82,"style":82},"export JOURNAL_API_KEY=\"sk-journal-tutorial-2026\"\nclaude -p '（前置き＋依頼文）' --mcp-config .mcp.json --strict-mcp-config \\\n  --allowedTools \"mcp__journal__list_journal_entries,mcp__journal__get_trial_balance,mcp__journal__register_journal_entry\" \\\n  --output-format stream-json --verbose\n",[51,10414,10415,10429,10452,10466],{"__ignoreMap":82},[86,10416,10417,10419,10421,10423,10425,10427],{"class":88,"line":89},[86,10418,53],{"class":761},[86,10420,5826],{"class":849},[86,10422,853],{"class":342},[86,10424,466],{"class":138},[86,10426,410],{"class":96},[86,10428,145],{"class":138},[86,10430,10431,10433,10435,10437,10439,10441,10444,10447,10449],{"class":88,"line":111},[86,10432,5839],{"class":92},[86,10434,5842],{"class":103},[86,10436,5845],{"class":138},[86,10438,5848],{"class":96},[86,10440,5851],{"class":138},[86,10442,10443],{"class":103}," --mcp-config",[86,10445,10446],{"class":96}," .mcp.json",[86,10448,5864],{"class":103},[86,10450,10451],{"class":103}," \\\n",[86,10453,10454,10457,10459,10462,10464],{"class":88,"line":121},[86,10455,10456],{"class":103},"  --allowedTools",[86,10458,139],{"class":138},[86,10460,10461],{"class":96},"mcp__journal__list_journal_entries,mcp__journal__get_trial_balance,mcp__journal__register_journal_entry",[86,10463,466],{"class":138},[86,10465,10451],{"class":103},[86,10467,10468,10471,10473],{"class":88,"line":148},[86,10469,10470],{"class":103},"  --output-format",[86,10472,5870],{"class":96},[86,10474,5873],{"class":103},[14,10476,10477],{},"正常系の実録。",[77,10479,10482],{"className":10480,"code":10481,"language":5676},[5674],"[1] ToolSearch query=\"journal 仕訳 登録\"\n    → journalサーバーのツール宣言をロード\n[2] mcp__journal__register_journal_entry\n    {\"date\":\"2026-07-25\",\"debit_account\":\"消耗品費\",\"credit_account\":\"現金\",\n     \"amount\":3300,\"description\":\"消耗品費の現金支払い\"}\n    → {\"date\":\"2026-07-25\",...,\"id\":6}\n",[51,10483,10481],{"__ignoreMap":82},[14,10485,10486],{},"一発で成功した。Before体験で6ターンだったものが3ターン。openapi.jsonの読解ステップが消え（仕様は宣言に書いてある）、Windowsのエンコーディング事故も消えた。事故が消えたのは偶然ではない。引数がシェルのコマンドライン文字列ではなく、MCPの構造化JSONとして流れるからで、「文字列の組み立て」という事故の発生場所そのものが経路からなくなっている。",[14,10488,10489,10491],{},[51,10490,6310],{}," には操作ログが1行残った。",[77,10493,10495],{"className":8870,"code":10494,"language":3723,"meta":82,"style":82},"{\"time\": \"2026-07-14T11:07:02\", \"tool\": \"register_journal_entry\", \"args\": {\"body\": {\"date\": \"2026-07-25\", \"debit_account\": \"消耗品費\", \"credit_account\": \"現金\", \"amount\": 3300, \"description\": \"消耗品費の現金支払い\"}}, \"status\": \"201\"}\n",[51,10496,10497],{"__ignoreMap":82},[86,10498,10499,10501,10503,10505,10507,10509,10511,10514,10516,10518,10520,10522,10524,10526,10528,10530,10532,10534,10536,10538,10540,10542,10544,10546,10548,10550,10552,10554,10556,10558,10560,10562,10564,10566,10568,10570,10572,10574,10576,10578,10580,10582,10584,10586,10588,10590,10592,10594,10596,10598,10600,10602,10604,10606,10608,10610,10613,10615,10617,10619,10621,10623,10625,10628,10630,10633,10635,10637,10639,10641,10643,10645,10647],{"class":88,"line":89},[86,10500,1030],{"class":342},[86,10502,466],{"class":8888},[86,10504,6445],{"class":114},[86,10506,466],{"class":8888},[86,10508,432],{"class":342},[86,10510,139],{"class":138},[86,10512,10513],{"class":96},"2026-07-14T11:07:02",[86,10515,466],{"class":138},[86,10517,343],{"class":342},[86,10519,139],{"class":8888},[86,10521,6359],{"class":114},[86,10523,466],{"class":8888},[86,10525,432],{"class":342},[86,10527,139],{"class":138},[86,10529,7932],{"class":96},[86,10531,466],{"class":138},[86,10533,343],{"class":342},[86,10535,139],{"class":8888},[86,10537,6505],{"class":114},[86,10539,466],{"class":8888},[86,10541,432],{"class":342},[86,10543,3130],{"class":342},[86,10545,466],{"class":8888},[86,10547,5396],{"class":114},[86,10549,466],{"class":8888},[86,10551,432],{"class":342},[86,10553,3130],{"class":342},[86,10555,466],{"class":8888},[86,10557,2619],{"class":114},[86,10559,466],{"class":8888},[86,10561,432],{"class":342},[86,10563,139],{"class":138},[86,10565,5275],{"class":96},[86,10567,466],{"class":138},[86,10569,343],{"class":342},[86,10571,139],{"class":8888},[86,10573,909],{"class":114},[86,10575,466],{"class":8888},[86,10577,432],{"class":342},[86,10579,139],{"class":138},[86,10581,590],{"class":96},[86,10583,466],{"class":138},[86,10585,343],{"class":342},[86,10587,139],{"class":8888},[86,10589,918],{"class":114},[86,10591,466],{"class":8888},[86,10593,432],{"class":342},[86,10595,139],{"class":138},[86,10597,463],{"class":96},[86,10599,466],{"class":138},[86,10601,343],{"class":342},[86,10603,139],{"class":8888},[86,10605,4414],{"class":114},[86,10607,466],{"class":8888},[86,10609,432],{"class":342},[86,10611,10612],{"class":107}," 3300",[86,10614,343],{"class":342},[86,10616,139],{"class":8888},[86,10618,4430],{"class":114},[86,10620,466],{"class":8888},[86,10622,432],{"class":342},[86,10624,139],{"class":138},[86,10626,10627],{"class":96},"消耗品費の現金支払い",[86,10629,466],{"class":138},[86,10631,10632],{"class":342},"}},",[86,10634,139],{"class":8888},[86,10636,6520],{"class":114},[86,10638,466],{"class":8888},[86,10640,432],{"class":342},[86,10642,139],{"class":138},[86,10644,2683],{"class":96},[86,10646,466],{"class":138},[86,10648,608],{"class":342},[14,10650,10651,10652,10655],{},"ちなみにAPI側（uvicorn）のアクセスログは ",[51,10653,10654],{},"\"POST /journal-entries HTTP/1.1\" 201 Created"," という1行で、何をどんな引数で登録したかは残らない。ツール名と引数と結果が揃うのはMCPサーバーの層だけだ。だからこそ、この層のログ設計に価値があるし、freee-mcpがログに「何を残さないか」（ツール引数の中身、つまり会計データ）まで設計していたことの意味もわかる。うちの教材ログは引数を丸ごと残しているが、運用ログに顧客の会計データを書いてよいかは、実務なら立ち止まるべき論点だ。",[14,10657,10658],{},"エラー系（雑費）の実録。",[77,10660,10663],{"className":10661,"code":10662,"language":5676},[5674],"[1] ToolSearch query=\"+journal 仕訳 登録\"\n[2] mcp__journal__register_journal_entry {\"debit_account\":\"雑費\",...}\n    → isError: true\n      \"Error executing tool register_journal_entry: 入力エラーで受け付けられませんでした。\n       - debit_account: Value error, 勘定科目「雑費」は使えません。使える科目: 現金、普通預金、\n         買掛金、資本金、売上高、仕入高、消耗品費\n       使える勘定科目: 現金、普通預金、買掛金、資本金、売上高、仕入高、消耗品費。\n       日付は YYYY-MM-DD、金額は正の整数（円）です。\"\n[3] 登録せず停止。「消耗品費に変えて登録する／server.pyのホワイトリストに雑費を\n    追加する」の2択をユーザーに提示\n",[51,10664,10662],{"__ignoreMap":82},[14,10666,10667,10668,10670],{},"最終判断はBeforeと同じで、勝手に読み替えず人間に確認を求めた。違うのは途中経過だ。Beforeでは原因調査のためにソースコードをgrepし、その巻き添えでキーが会話に出た。Afterでは、整形エラー文が「何がだめで、何なら使えるか」を1回で伝えたので、ソースを漁る動機自体が発生していない。エラー文面の中に科目一覧が2回出ているのはご愛嬌で、FastAPIのバリデータ由来の文と、",[51,10669,7389],{}," が付け足した文が重なった結果だ（金額や日付のエラーでは付け足しの方だけが情報源になるので、重複を承知で残している）。",[14,10672,10673,10674,10677],{},"もう1つ、承認の粒度を確かめる実験をした。許可リストを読み取り系2ツールだけにして、",[51,10675,10676],{},"--permission-mode default"," で権限確認を有効にした状態で、同じ正常系の依頼を投げる。実測に使ったコマンドはこれだ。",[77,10679,10681],{"className":79,"code":10680,"language":81,"meta":82,"style":82},"claude -p '（前置き＋依頼文）' --mcp-config .mcp.json --strict-mcp-config \\\n  --permission-mode default \\\n  --allowedTools \"mcp__journal__list_journal_entries,mcp__journal__get_trial_balance\" \\\n  --output-format stream-json --verbose\n",[51,10682,10683,10703,10712,10725],{"__ignoreMap":82},[86,10684,10685,10687,10689,10691,10693,10695,10697,10699,10701],{"class":88,"line":89},[86,10686,5839],{"class":92},[86,10688,5842],{"class":103},[86,10690,5845],{"class":138},[86,10692,5848],{"class":96},[86,10694,5851],{"class":138},[86,10696,10443],{"class":103},[86,10698,10446],{"class":96},[86,10700,5864],{"class":103},[86,10702,10451],{"class":103},[86,10704,10705,10708,10710],{"class":88,"line":111},[86,10706,10707],{"class":103},"  --permission-mode",[86,10709,2755],{"class":96},[86,10711,10451],{"class":103},[86,10713,10714,10716,10718,10721,10723],{"class":88,"line":121},[86,10715,10456],{"class":103},[86,10717,139],{"class":138},[86,10719,10720],{"class":96},"mcp__journal__list_journal_entries,mcp__journal__get_trial_balance",[86,10722,466],{"class":138},[86,10724,10451],{"class":103},[86,10726,10727,10729,10731],{"class":88,"line":148},[86,10728,10470],{"class":103},[86,10730,5870],{"class":96},[86,10732,5873],{"class":103},[77,10734,10737],{"className":10735,"code":10736,"language":5676},[5674],"[2] mcp__journal__register_journal_entry {...}\n    → 拒否: \"Claude requested permissions to use mcp__journal__register_journal_entry,\n       but you haven't granted it yet.\"\n[3] エージェントは登録内容（日付・借方・貸方・金額・摘要）を提示して承認待ちで停止\n",[51,10738,10736],{"__ignoreMap":82},[14,10740,10741,10742,10745],{},"仕訳は登録されず、シードの5件のままだった。ヘッドレス実行では承認に答える手段がないのでここで止まるが、対話モードなら承認ダイアログが出て、許可すれば登録まで進む。読み取りは素通しにして、書き込みだけ止める。この制御が",[248,10743,10744],{},"ツール名の単位","で効いている。Before体験ではこうはいかない。エージェントに許可するのは「Bash（シェルコマンドの実行）」という単位で、読み取りのcurlも書き込みのcurlも同じコマンド実行として見える。HTTPメソッドの意味を許可ルールの層で見分ける標準の経路がないからだ。OpenAPI仕様にはGETとPOSTの区別がちゃんとある。ないのは、その区別をクライアントの確認UIまで運ぶ配線で、MCPのツール分割とannotationsはその配線として機能する。",[14,10747,10748],{},"ただし、正直に書いておくと、この実験は1回目に失敗している。私のClaude Codeは普段、権限確認を自動許可するモードで動いていて、その設定のままでは、許可リストに入れていない書き込みツールもそのまま通った。annotationsが「強制力のないヒント」であり、実際に止めるかどうかはクライアントの設定次第、という分業の実演を、意図せず自分でやってしまった格好だ。サーバーが正直に申告していても、クライアント側の統制がゆるければ素通りする。導入するときは、どちらの層で何を止めているかを両方確認した方がいい。",[5908,10750,10752],{"id":10751},"beforeafterの差分表","Before/Afterの差分表",[14,10754,10755],{},"測れた差だけを表にする。",[190,10757,10758,10771],{},[193,10759,10760],{},[196,10761,10762,10765,10768],{},[199,10763,10764],{},"観測点",[199,10766,10767],{},"Before（生API直叩き）",[199,10769,10770],{},"After（MCP経由）",[206,10772,10773,10784,10795,10806,10817,10828,10839],{},[196,10774,10775,10778,10781],{},[211,10776,10777],{},"仕様の伝達",[211,10779,10780],{},"前置きでURLと認証方式を説明し、エージェントがopenapi.jsonを毎回読解",[211,10782,10783],{},"前置き1行。仕様はツール宣言に載って自動で届く",[196,10785,10786,10789,10792],{},[211,10787,10788],{},"秘密の扱い",[211,10790,10791],{},"エージェントはキーをechoしなかったが、エラー原因調査のgrepの巻き添えでキー値が会話に出現",[211,10793,10794],{},"キー値は一度も出現せず。ツール引数にキーのフィールド自体が存在しない",[196,10796,10797,10800,10803],{},[211,10798,10799],{},"承認の粒度",[211,10801,10802],{},"「Bash実行」の単位。読み取りと書き込みのcurlを許可ルールで区別する標準経路がない",[211,10804,10805],{},"ツール単位。読み取り2本を許可し書き込みだけ拒否、を実測で確認",[196,10807,10808,10811,10814],{},[211,10809,10810],{},"エラー復旧",[211,10812,10813],{},"422の後、ソースコードをgrepして原因特定してから停止（2手余分）",[211,10815,10816],{},"整形エラー文だけで即停止、確認要求。ソース調査なし",[196,10818,10819,10822,10825],{},[211,10820,10821],{},"操作ログ",[211,10823,10824],{},"uvicornのアクセスログのみ（メソッドとパスとステータス）",[211,10826,10827],{},"ツール名と引数と結果が1呼び出し1行で残る",[196,10829,10830,10833,10836],{},[211,10831,10832],{},"Windows固有の事故",[211,10834,10835],{},"日本語JSONのシェル渡しで400が2回とも再現",[211,10837,10838],{},"構造化JSONで渡るため発生せず",[196,10840,10841,10844,10847],{},[211,10842,10843],{},"ターン数（参考）",[211,10845,10846],{},"正常系6、エラー系7",[211,10848,10849],{},"どちらも3",[14,10851,10852,10853,10856],{},"差が出なかった項目も書いておく。",[248,10854,10855],{},"最終的なガバナンス挙動は同じだった","。雑費のエラーに対して、どちらの経路でもエージェントは科目を勝手に読み替えず、選択肢を提示して停止した。モデルも依頼文も同じなのだから、判断が変わる理由はない。変わったのは、その判断に至るまでに事故が起きる場所と手数だ。MCPは、エージェントを賢くする規格ではなく、事故が起きる場所を経路ごと減らす規格だった。この表がその一覧になっている。",[182,10858,10859],{"id":10859},"もしエンドポイントが277本あったら",[14,10861,10862],{},"ここまで意図的に避けてきたコストの話を、最後にする。",[14,10864,10865,10867],{},[51,10866,8157],{}," が返した3ツール分の宣言は、圧縮したJSONで2,429バイト、1ツールあたり平均810バイトだった。これはMCPのプロトコル上でクライアントに渡る宣言のサイズであり、freeeの規模（一意パス277本）を1対1のツールにしたと仮定して外挿すると約219KB、メソッド単位の416操作なら約329KBになる。外挿であって実測ではないことは明記しておく。それでも、宣言だけで数百KBという量が「接続しただけで発生する荷物」になり得ることは、この計算で十分に見える。",[14,10869,10870],{},"ただし、この宣言サイズがそのままLLMのコンテキスト消費になるわけではない。宣言をいつ、どれだけモデルに渡すかはMCPクライアント（ホスト）の実装に任されている。実際、今回のAfter体験の実録には面白い痕跡が残っていた。セッション開始時点でjournalサーバーは「pending」で、エージェントは依頼を受けてから検索ツールで宣言をロードしている。Claude CodeはMCPツールの宣言を遅延ロードする実装になっていて、接続した瞬間に全宣言をコンテキストへ流し込んではいない（この見え方はClaude Codeのバージョンや設定に依存する。実録にToolSearchが現れず、最初からツールが見えていてもそれは正常だ）。",[14,10872,10873,10874,10876],{},"つまり、宣言の肥大化には2つの対処層がある。ホスト側の遅延ロード（Claude Codeがやっていること）と、サーバー側の設計（freee-mcpが汎用5ツール+Agent Skillsへ畳み込んだこと）だ。3本の教材では1対1ツールが素直で、277本のfreeeでは畳み込みが要る。",[17,10875,20],{"href":19},"で見た「API呼び出しは汎用5ツール、全体でも15個」という設計は、この外挿計算の先にある必然だった、というのが手を動かした後の実感になる。",[182,10878,10880],{"id":10879},"締め-アプリの次はエージェント同士がつながる","締め: アプリの次は、エージェント同士がつながる",[14,10882,10883],{},"作ったものは、APIが1本、それを包むMCPサーバーが1本、ツール宣言が3つ。この最小の部品で、アプリケーションとエージェントの間の規格が固まると何が起きるかを確かめた。",[14,10885,10886,10887,10892],{},"そして、アプリとエージェントの接続が規格で埋まった今、次に規格化が進んでいるのはエージェントとエージェントの間だ。",[17,10888,10891],{"href":10889,"target":41,"rel":10890},"https://github.com/a2aproject/A2A",[43,44],"→ A2A（Agent2Agent Protocol・GitHub）","のように、エージェント同士が能力を宣言し合い、タスクを依頼し合うプロトコルが既に動き始めている。今回の教材で言えば、仕訳を登録する側のエージェントと、試算表を検証する側のエージェントが、人間を介さずに突き合わせをする形だ。",[14,10894,10895],{},"会計事務所の文脈に置き直すと、こうなる。あなたの事務所のエージェントが、顧問先のエージェントに「7月分の売上仕訳を根拠資料付きで」と依頼し、受け取った仕訳を試算表と突き合わせ、異常残高だけを人間の机に上げる。今日それができると言いたいのではない。ただ、その世界の部品が何でできているかは、もう見えている。APIがあり、それを包む宣言があり、許可の単位があり、操作の記録がある。今回手元で作った3つのツールは、その世界の最小単位だった。",[14,10897,10898],{},"freee-mcpを読んで、模型を作って、実測した。次に「エージェント対応」と書かれたサービスを見たとき、その裏で何が宣言され、何が隠され、どこに記録が残るのか。それを具体的に想像できるようになったことが、このハンズオンの収穫だ。",[182,10900,10901],{"id":10901},"まとめ",[32,10903,10904,10907,10910,10913,10916,10919,10922,10925],{},[35,10905,10906],{},"ミニ仕訳帳API（FastAPIで3エンドポイント、約150行）を自作し、MCP公式SDKのFastMCPで包んだ（約170行）。コードは全文掲載、出力はすべて実測値",[35,10908,10909],{},"生のAPIをエージェントに使わせると、仕様の読解が毎回走り、Windowsのシェル経由で日本語JSONが壊れる事故を踏み、422の原因調査でソースを漁った巻き添えにAPIキーが会話へ流れ込んだ",[35,10911,10912],{},"MCP経由では同じ依頼が3ターンで完了。キーはツールの宣言にも引数にも存在せず、エンコーディング事故は経路ごと消え、整形エラーがソース調査の動機をなくした",[35,10914,10915],{},"承認はツール単位で制御でき、読み取りだけ許可して書き込みを止める動作を実測した。ただしannotationsは強制力のないヒントで、クライアント側が自動許可モードなら素通りする（実測済み）",[35,10917,10918],{},"エージェントの判断は両経路で同じだった。雑費のエラーに対し、どちらも科目を読み替えず人間に確認を求めた。変わったのは、そこへ至るまでに事故が起きる場所と手数",[35,10920,10921],{},"ツール宣言の肥大化には、ホスト側の遅延ロードとサーバー側のツール畳み込み（freee-mcpの汎用5ツール+Agent Skills）という2層の対処がある。宣言サイズの実測と277本への外挿は「もしエンドポイントが277本あったら」の節に書いた",[35,10923,10924],{},"操作ログ（JSONLで1呼び出し1行）は監査証跡ではない。実行者の識別、改ざん検知、API側記録との突合がない記録は、証跡と呼ぶには距離がある",[35,10926,10927],{},"アプリとエージェントの接続（MCP）の次は、エージェント同士の通信（A2A）の規格化が進んでいる。今回の3ツールは、エージェント同士が帳簿を突き合わせる世界の最小単位になる",[182,10929,10930],{"id":10930},"参考リンク",[32,10932,10933,10938,10944,10950,10956,10963,10970,10976],{},[35,10934,10935],{},[17,10936,10937],{"href":19},"前記事: freee公式MCPサーバーのコードを開く。「APIをラップする」の実体は、宣言ではなく門番と配管だった",[35,10939,10940],{},[17,10941,10943],{"href":10942},"/mcp-agent-app-connection","前々記事: MCPはAPIを「エージェントが読める形」に包む規格。アプリ間接続の4つの疑問を深掘りする",[35,10945,10946],{},[17,10947,10949],{"href":5994,"target":41,"rel":10948},[43,44],"→ MCP公式Python SDK（GitHub）",[35,10951,10952],{},[17,10953,10955],{"href":8144,"target":41,"rel":10954},[43,44],"→ MCP仕様: Tools（annotationsの位置づけ）",[35,10957,10958],{},[17,10959,10962],{"href":10960,"target":41,"rel":10961},"https://github.com/freee/freee-mcp",[43,44],"→ freee/freee-mcp（GitHub）",[35,10964,10965],{},[17,10966,10969],{"href":10967,"target":41,"rel":10968},"https://fastapi.tiangolo.com/ja/",[43,44],"→ FastAPI公式ドキュメント",[35,10971,10972],{},[17,10973,10975],{"href":40,"target":41,"rel":10974},[43,44],"→ uv公式ドキュメント",[35,10977,10978],{},[17,10979,10891],{"href":10889,"target":41,"rel":10980},[43,44],[10982,10983,10984],"style",{},"html pre.shiki code .senZ8, html code.shiki .senZ8{--shiki-default:#59873A;--shiki-dark:#59873A}html pre.shiki code .sdGka, html code.shiki .sdGka{--shiki-default:#B56959;--shiki-dark:#B56959}html pre.shiki code .snbK4, html code.shiki .snbK4{--shiki-default:#A65E2B;--shiki-dark:#A65E2B}html pre.shiki code .sM54T, html code.shiki .sM54T{--shiki-default:#2F798A;--shiki-dark:#2F798A}html pre.shiki code .sz8Xr, html code.shiki .sz8Xr{--shiki-default:#998418;--shiki-dark:#998418}html pre.shiki code .sMJiu, html code.shiki .sMJiu{--shiki-default:#B5695977;--shiki-dark:#B5695977}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sxvE3, html code.shiki .sxvE3{--shiki-default:#A0ADA0;--shiki-dark:#A0ADA0}html pre.shiki code .stQ0i, html code.shiki .stQ0i{--shiki-default:#AB5959;--shiki-dark:#AB5959}html pre.shiki code .s4oTP, html code.shiki .s4oTP{--shiki-default:#B07D48;--shiki-dark:#B07D48}html pre.shiki code .shFtX, html code.shiki .shFtX{--shiki-default:#999999;--shiki-dark:#999999}html pre.shiki code .sHkkW, html code.shiki .sHkkW{--shiki-default:#1E754F;--shiki-dark:#1E754F}html pre.shiki code .sG7-3, html code.shiki .sG7-3{--shiki-default:#393A34;--shiki-dark:#393A34}html pre.shiki code .sSkh3, html code.shiki .sSkh3{--shiki-default:#2E8F82;--shiki-dark:#2E8F82}html pre.shiki code .sLkVR, html code.shiki .sLkVR{--shiki-default:#AB5E3F;--shiki-dark:#AB5E3F}html pre.shiki code .sqvqQ, html code.shiki .sqvqQ{--shiki-default:#99841877;--shiki-dark:#99841877}",{"title":82,"searchDepth":111,"depth":111,"links":10986},[10987,10988,10992,10993,10997,10998,10999,11000],{"id":184,"depth":111,"text":185},{"id":5772,"depth":111,"text":5773,"children":10989},[10990,10991],{"id":5910,"depth":121,"text":5911},{"id":5946,"depth":121,"text":5947},{"id":5987,"depth":111,"text":5988},{"id":8150,"depth":111,"text":8151,"children":10994},[10995,10996],{"id":10236,"depth":121,"text":10237},{"id":10751,"depth":121,"text":10752},{"id":10859,"depth":111,"text":10859},{"id":10879,"depth":111,"text":10880},{"id":10901,"depth":111,"text":10901},{"id":10930,"depth":111,"text":10930},null,"freee公式MCPサーバーの解剖記事の続編。FastAPI製のミニ仕訳帳API（3エンドポイント）を自作し、生のままエージェントに使わせる体験と、MCPサーバー（公式SDKのFastMCP）で包んでから使わせる体験を、同じ依頼文で実測して比べた。APIキーの居場所、承認の粒度、エラーからの復旧、操作ログの4点で何が変わるかをターミナルの実録で確かめ、宣言サイズの実測値から「もし277本あったら」を外挿する。コードは全文掲載、コマンドと出力はすべて実測値。","md",{},"/journal-api-mcp-tutorial",false,"2026-07-14T00:00:00.000Z",{"title":5,"description":11002},"2026-07/2026-07-14/journal-api-mcp-tutorial",[11011,11012,11013,11014,11015,11016],"MCP","FastAPI","Python","AIエージェント","freee","ハンズオン","alvI1Tq5KnvsKbgq-7Fh3x1brxK6YFaKeCRtHDEwdfQ",[],"https://log.eurekapu.com/og/blog/journal-api-mcp-tutorial.png?v=2026-07-14T00%3A00%3A00.000Z&title=%E8%87%AA%E4%BD%9CAPI%E3%82%92MCP%E5%8C%96%E3%81%99%E3%82%8B%E3%83%8F%E3%83%B3%E3%82%BA%E3%82%AA%E3%83%B3%E3%80%82%E3%83%9F%E3%83%8B%E4%BB%95%E8%A8%B3%E5%B8%B3API%E3%82%92%E4%BD%9C%E3%82%8A%E3%80%81%E7%94%9F%E3%81%AE%E3%81%BE%E3%81%BE%E4%BD%BF%E3%81%84%E3%80%81%E5%8C%85%E3%82%93%E3%81%A7%E3%81%8B%E3%82%89%E4%BD%BF%E3%81%A3%E3%81%A6%E6%AF%94%E3%81%B9%E3%82%8B&author=Kei%20Komatsu&sig=3d6941c41dc98353",1784001815480]