@him0/freee-mcp 0.6.5 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +226 -0
- package/bin/cli.js +17 -17
- package/dist/openapi/minimal/accounting.json +3 -3
- package/dist/openapi/minimal/sm.json +26 -0
- package/package.json +19 -4
package/README.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# @him0/freee-mcp
|
|
2
|
+
|
|
3
|
+
freee API を Claude から使えるようにする MCP サーバー & Claude Plugin です。
|
|
4
|
+
|
|
5
|
+
MCP サーバー(API 呼び出し機能)と skill(API リファレンス)を組み合わせて利用することを想定しています。
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@him0/freee-mcp)
|
|
8
|
+
|
|
9
|
+
> Note: このプロジェクトは開発中であり、予期せぬ不具合が発生する可能性があります。問題を発見された場合は [Issue](https://github.com/him0/freee-mcp/issues) として報告していただけると幸いです。
|
|
10
|
+
|
|
11
|
+
## 特徴
|
|
12
|
+
|
|
13
|
+
- MCP サーバー: freee API を Claude Desktop / Claude Code から直接呼び出し
|
|
14
|
+
- Claude Plugin: 詳細な API リファレンスドキュメント付きスキルを提供
|
|
15
|
+
- 複数 API 対応: 会計・人事労務・請求書・工数管理・販売の5つの freee API をサポート
|
|
16
|
+
- OAuth 2.0 + PKCE: セキュアな認証フロー、トークン自動更新
|
|
17
|
+
- 複数事業所対応: 事業所の動的切り替えが可能
|
|
18
|
+
|
|
19
|
+
## SKILL と MCP の通信の流れ
|
|
20
|
+
|
|
21
|
+
Claude Code では、SKILL(API リファレンス)と MCP サーバー(API 呼び出し)を組み合わせて利用します。
|
|
22
|
+
|
|
23
|
+
```mermaid
|
|
24
|
+
sequenceDiagram
|
|
25
|
+
participant User as ユーザー
|
|
26
|
+
participant Claude as Claude Code
|
|
27
|
+
participant Skill as Agent Skill<br/>(API リファレンス)
|
|
28
|
+
participant MCP as MCP サーバー<br/>(ローカル)
|
|
29
|
+
participant API as freee API
|
|
30
|
+
|
|
31
|
+
User->>Claude: リクエスト<br/>「取引一覧を取得して」
|
|
32
|
+
|
|
33
|
+
Note over Claude,Skill: 1. SKILL からリファレンスを取得
|
|
34
|
+
Claude->>Skill: freee-api-skill 呼び出し
|
|
35
|
+
Skill-->>Claude: API リファレンス注入<br/>(エンドポイント、パラメータ仕様)
|
|
36
|
+
|
|
37
|
+
Note over Claude,MCP: 2. MCP Tool で API を実行
|
|
38
|
+
Claude->>MCP: freee_api_get 呼び出し<br/>path: /api/1/deals
|
|
39
|
+
MCP->>MCP: OpenAPI スキーマで検証
|
|
40
|
+
MCP->>MCP: 認証トークン付与
|
|
41
|
+
|
|
42
|
+
Note over MCP,API: 3. freee API への通信
|
|
43
|
+
MCP->>API: GET /api/1/deals<br/>Authorization: Bearer xxx
|
|
44
|
+
API-->>MCP: JSON レスポンス
|
|
45
|
+
|
|
46
|
+
MCP-->>Claude: 取引データ
|
|
47
|
+
Claude-->>User: 結果を整形して表示
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
この仕組みにより:
|
|
51
|
+
- SKILL: 必要な API リファレンスを段階的にコンテキストに注入(コンテキスト効率化)
|
|
52
|
+
- MCP: 認証・リクエスト検証・API 呼び出しを担当
|
|
53
|
+
|
|
54
|
+
## クイックスタート
|
|
55
|
+
|
|
56
|
+
### 1. freee アプリケーションの登録
|
|
57
|
+
|
|
58
|
+
[freee アプリストア](https://app.secure.freee.co.jp/developers) で新しいアプリを作成:
|
|
59
|
+
|
|
60
|
+
- コールバックURL: `http://127.0.0.1:54321/callback`
|
|
61
|
+
- Client ID と Client Secret を取得
|
|
62
|
+
- 必要な権限にチェック
|
|
63
|
+
|
|
64
|
+
### 2. セットアップ
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx @him0/freee-mcp configure
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
対話式ウィザードが認証情報の設定、OAuth認証、事業所選択を行います。
|
|
71
|
+
|
|
72
|
+
### 3. Claude Desktop に追加
|
|
73
|
+
|
|
74
|
+
`configure` が出力する設定を Claude Desktop の設定ファイルに追加:
|
|
75
|
+
|
|
76
|
+
| OS | 設定ファイルパス |
|
|
77
|
+
| ------- | ----------------------------------------------------------------- |
|
|
78
|
+
| macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |
|
|
79
|
+
| Windows | `%APPDATA%\Claude\claude_desktop_config.json` |
|
|
80
|
+
| Linux | `~/.config/Claude/claude_desktop_config.json` |
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"mcpServers": {
|
|
85
|
+
"freee": {
|
|
86
|
+
"command": "npx",
|
|
87
|
+
"args": ["@him0/freee-mcp"]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
> ⚠️ 環境変数での設定について
|
|
94
|
+
> 環境変数(`FREEE_CLIENT_ID`、`FREEE_CLIENT_SECRET` など)を使った設定は非推奨です。
|
|
95
|
+
> 代わりに `npx @him0/freee-mcp configure` を実行して設定ファイルに移行してください。
|
|
96
|
+
> 環境変数設定は将来のバージョンで削除される予定です。
|
|
97
|
+
|
|
98
|
+
## Claude Plugin として使う
|
|
99
|
+
|
|
100
|
+
Claude Code でプラグインとしてインストールすると、API リファレンス付きのスキルが利用できます:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npx add-skill him0/freee-mcp
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
[add-skill](https://github.com/anthropics/add-skill) は Claude Code、Cursor、OpenCode など複数のコーディングエージェントに対応したスキルインストーラーです。グローバルインストール(`-g`)や特定スキルのみのインストール(`-s`)も可能です。
|
|
107
|
+
|
|
108
|
+
### 含まれるリファレンス
|
|
109
|
+
|
|
110
|
+
| API | 内容 | ファイル数 |
|
|
111
|
+
| -------- | -------------------------------------------- | ---------- |
|
|
112
|
+
| 会計 | 取引、勘定科目、取引先、請求書、経費申請など | 31 |
|
|
113
|
+
| 人事労務 | 従業員、勤怠、給与明細、年末調整など | 27 |
|
|
114
|
+
| 請求書 | 請求書、見積書、納品書 | 3 |
|
|
115
|
+
| 工数管理 | ユーザー情報 | 1 |
|
|
116
|
+
| 販売 | 案件、受注 | 2 |
|
|
117
|
+
|
|
118
|
+
Claude との会話中に API の使い方を質問すると、これらのリファレンスを参照して正確な情報を提供します。
|
|
119
|
+
|
|
120
|
+
### データ作成のベストプラクティス
|
|
121
|
+
|
|
122
|
+
請求書や経費精算など、同じ形式のデータを繰り返し作成する場合は、以前に作成したデータを参照することで効率的に作業できます:
|
|
123
|
+
|
|
124
|
+
- 請求書作成: 過去の請求書を取得して、取引先・品目・税区分などを参考にする
|
|
125
|
+
- 経費精算: 過去の申請を参照して、勘定科目や部門の指定を正確に行う
|
|
126
|
+
- 取引登録: 類似の取引を参考にして、入力ミスを防ぐ
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
例: 「先月の○○社への請求書を参考に、今月分を作成して」
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## 利用可能なツール
|
|
133
|
+
|
|
134
|
+
### 管理ツール
|
|
135
|
+
|
|
136
|
+
| ツール | 説明 |
|
|
137
|
+
| -------------------------- | ------------------ |
|
|
138
|
+
| `freee_authenticate` | OAuth 認証を実行 |
|
|
139
|
+
| `freee_auth_status` | 認証状態を確認 |
|
|
140
|
+
| `freee_clear_auth` | 認証情報をクリア |
|
|
141
|
+
| `freee_set_current_company`| 事業所を切り替え |
|
|
142
|
+
| `freee_get_current_company`| 現在の事業所を表示 |
|
|
143
|
+
| `freee_list_companies` | 事業所一覧を取得 |
|
|
144
|
+
| `freee_current_user` | 現在のユーザー情報 |
|
|
145
|
+
|
|
146
|
+
### API ツール
|
|
147
|
+
|
|
148
|
+
HTTPメソッドごとのシンプルなツール構成:
|
|
149
|
+
|
|
150
|
+
| ツール | 説明 | 例 |
|
|
151
|
+
| ---------------------- | ------------------ | ------------------ |
|
|
152
|
+
| `freee_api_get` | データ取得 | `/api/1/deals` |
|
|
153
|
+
| `freee_api_post` | 新規作成 | `/api/1/deals` |
|
|
154
|
+
| `freee_api_put` | 更新 | `/api/1/deals/123` |
|
|
155
|
+
| `freee_api_delete` | 削除 | `/api/1/deals/123` |
|
|
156
|
+
| `freee_api_patch` | 部分更新 | `/api/1/deals/123` |
|
|
157
|
+
| `freee_api_list_paths` | エンドポイント一覧 | - |
|
|
158
|
+
|
|
159
|
+
パスは OpenAPI スキーマに対して自動検証されます。
|
|
160
|
+
|
|
161
|
+
### company_id の取り扱い
|
|
162
|
+
|
|
163
|
+
リクエスト(パラメータまたはボディ)に `company_id` を含める場合、現在の事業所と一致している必要があります。不一致の場合はエラーになります。
|
|
164
|
+
|
|
165
|
+
- 事業所の確認: `freee_get_current_company`
|
|
166
|
+
- 事業所の切り替え: `freee_set_company`
|
|
167
|
+
- company_id を含まない API(例: `/api/1/companies`)はそのまま実行可能
|
|
168
|
+
|
|
169
|
+
## コントリビューション
|
|
170
|
+
|
|
171
|
+
Issue での不具合報告や機能要望、フィードバックは大歓迎です!
|
|
172
|
+
|
|
173
|
+
このリポジトリでは AI を活用した開発フローを採用しており、issue をもとに実装することでコードの一貫性と品質を保っています。
|
|
174
|
+
そのため、ホワイトリストに登録されていないユーザーからの PR は自動的に close され、代わりに提案内容を反映した issue が作成されます。
|
|
175
|
+
メンテナーが issue をもとに実装方針を検討し、対応を進めます。
|
|
176
|
+
|
|
177
|
+
### Contributors
|
|
178
|
+
|
|
179
|
+
<!-- CONTRIBUTORS-START -->
|
|
180
|
+
<a href="https://github.com/dais0n"><img src="https://github.com/dais0n.png" width="40" height="40" alt="@dais0n"></a>
|
|
181
|
+
<a href="https://github.com/HikaruEgashira"><img src="https://github.com/HikaruEgashira.png" width="40" height="40" alt="@HikaruEgashira"></a>
|
|
182
|
+
<a href="https://github.com/nakanoasaservice"><img src="https://github.com/nakanoasaservice.png" width="40" height="40" alt="@nakanoasaservice"></a>
|
|
183
|
+
<a href="https://github.com/tackeyy"><img src="https://github.com/tackeyy.png" width="40" height="40" alt="@tackeyy"></a>
|
|
184
|
+
<a href="https://github.com/worldscandy"><img src="https://github.com/worldscandy.png" width="40" height="40" alt="@worldscandy"></a>
|
|
185
|
+
<!-- CONTRIBUTORS-END -->
|
|
186
|
+
|
|
187
|
+
## 開発者向け
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
git clone https://github.com/him0/freee-mcp.git
|
|
191
|
+
cd freee-mcp
|
|
192
|
+
pnpm install
|
|
193
|
+
|
|
194
|
+
pnpm dev # 開発サーバー(ウォッチモード)
|
|
195
|
+
pnpm build # ビルド
|
|
196
|
+
pnpm typecheck # 型チェック
|
|
197
|
+
pnpm lint # リント
|
|
198
|
+
pnpm test:run # テスト
|
|
199
|
+
|
|
200
|
+
# API リファレンスの再生成
|
|
201
|
+
pnpm generate:references
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 技術スタック
|
|
205
|
+
|
|
206
|
+
TypeScript / Model Context Protocol SDK / OAuth 2.0 + PKCE / Zod / esbuild
|
|
207
|
+
|
|
208
|
+
### アーキテクチャ詳細
|
|
209
|
+
|
|
210
|
+
プロジェクトのアーキテクチャ、内部構造、開発ガイドラインについては [CLAUDE.md](./CLAUDE.md) を参照してください。
|
|
211
|
+
|
|
212
|
+
## ライセンス
|
|
213
|
+
|
|
214
|
+
ISC
|
|
215
|
+
|
|
216
|
+
## コミュニティ
|
|
217
|
+
|
|
218
|
+
質問や情報交換は Discord サーバーで行っています。お気軽にご参加ください。
|
|
219
|
+
|
|
220
|
+
- [Discord サーバー](https://discord.gg/9ddTPGyxPw)
|
|
221
|
+
|
|
222
|
+
## 関連リンク
|
|
223
|
+
|
|
224
|
+
- [紹介記事: Public API を MCP化するとき Agent Skill 併用が良さそう with freee-mcp](https://zenn.dev/him0/articles/766798ca1315e0)
|
|
225
|
+
- [freee API ドキュメント](https://developer.freee.co.jp/docs)
|
|
226
|
+
- [Model Context Protocol](https://modelcontextprotocol.io)
|
package/bin/cli.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
2
|
|
|
3
|
-
var At=Object.defineProperty;var pe=(t,e)=>()=>(t&&(e=t(t=0)),e);var Me=(t,e)=>{for(var r in e)At(t,r,{get:e[r],enumerable:!0})};import Fe from"path";import Tt from"os";function
|
|
4
|
-
`),e=process.env.FREEE_CLIENT_ID||"",r=process.env.FREEE_CLIENT_SECRET||"",n=process.env.FREEE_CALLBACK_PORT?parseInt(process.env.FREEE_CALLBACK_PORT,10):N;else{if(!t.clientId||!t.clientSecret)throw new Error("\u8A8D\u8A3C\u60C5\u5831\u304C\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002\n`freee-mcp configure` \u3092\u5B9F\u884C\u3057\u3066\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3057\u3066\u304F\u3060\u3055\u3044\u3002");e=t.clientId,r=t.clientSecret,n=t.callbackPort||N}return O={freee:{clientId:e,clientSecret:r,companyId:"0",apiUrl:Q},oauth:{callbackPort:n,redirectUri:`http://127.0.0.1:${n}/callback`,authorizationEndpoint:"https://accounts.secure.freee.co.jp/public_api/authorize",tokenEndpoint:"https://accounts.secure.freee.co.jp/public_api/token",scope:"read write"},server:{name:"freee",version:"1.0.0"},auth:{timeoutMs:R}},O}function h(){if(!O)throw new Error("Config not loaded. Call loadConfig() first in async context.");return O}var O,
|
|
3
|
+
var At=Object.defineProperty;var pe=(t,e)=>()=>(t&&(e=t(t=0)),e);var Me=(t,e)=>{for(var r in e)At(t,r,{get:e[r],enumerable:!0})};import Fe from"path";import Tt from"os";function T(){return process.env.XDG_CONFIG_HOME?Fe.join(process.env.XDG_CONFIG_HOME,Oe):Fe.join(Tt.homedir(),".config",Oe)}var Oe,N,R,X,Q,St,$,w=pe(()=>{"use strict";Oe="freee-mcp";N=54321,R=300*1e3,X=384,Q="https://api.freee.co.jp",St="0.6.7",$=`freee-mcp/${St} (MCP Server; +https://github.com/him0/freee-mcp)`});var Ue={};Me(Ue,{CompanyConfigSchema:()=>je,FullConfigSchema:()=>Ne,getCompanyInfo:()=>U,getCurrentCompanyId:()=>x,getDownloadDir:()=>de,loadFullConfig:()=>I,saveFullConfig:()=>M,setCurrentCompany:()=>me});import ue from"fs/promises";import De from"path";import Pt from"os";import{z as d}from"zod";function fe(){return De.join(T(),"config.json")}async function vt(){let t=De.dirname(fe());await ue.mkdir(t,{recursive:!0})}function bt(t){return t!=null&&typeof t=="object"&&"defaultCompanyId"in t&&"currentCompanyId"in t&&"companies"in t&&!("clientId"in t)}function It(t){return console.error("\u{1F4E6} \u53E4\u3044\u8A2D\u5B9A\u5F62\u5F0F\u3092\u691C\u51FA\u3057\u307E\u3057\u305F\u3002\u65B0\u3057\u3044\u5F62\u5F0F\u306B\u79FB\u884C\u3057\u307E\u3059..."),{clientId:void 0,clientSecret:void 0,callbackPort:void 0,defaultCompanyId:t.defaultCompanyId,currentCompanyId:t.currentCompanyId,companies:t.companies}}async function I(){let t=fe();try{let e=await ue.readFile(t,"utf8"),r=JSON.parse(e);if(bt(r)){let o=It(r);return await M(o),o}let n=Ne.safeParse(r);if(!n.success)throw new Error(`Invalid config file: ${n.error.message}`);return n.data}catch(e){if(e.code==="ENOENT"){let r={clientId:void 0,clientSecret:void 0,callbackPort:void 0,defaultCompanyId:"0",currentCompanyId:"0",companies:{}};return await M(r),r}throw e}}async function M(t){await vt();let e=fe();await ue.writeFile(e,JSON.stringify(t,null,2),{mode:X})}async function x(){return(await I()).currentCompanyId}async function me(t,e,r){let n=await I();n.companies[t]?(e||r)&&(e&&(n.companies[t].name=e),r&&(n.companies[t].description=r)):n.companies[t]={id:t,name:e||`Company ${t}`,description:r||void 0,addedAt:Date.now()},n.companies[t].lastUsed=Date.now(),n.currentCompanyId=t,await M(n)}async function U(t){return(await I()).companies[t]||null}async function de(){return(await I()).downloadDir||Pt.tmpdir()}var je,Ne,F=pe(()=>{"use strict";w();je=d.object({id:d.string(),name:d.string().optional(),description:d.string().optional(),addedAt:d.number(),lastUsed:d.number().optional()}),Ne=d.object({clientId:d.string().optional(),clientSecret:d.string().optional(),callbackPort:d.number().optional(),defaultCompanyId:d.string(),currentCompanyId:d.string(),companies:d.record(d.string(),je),downloadDir:d.string().optional()})});var Le={};Me(Le,{getConfig:()=>h,loadConfig:()=>ge});function xt(){return!!(process.env.FREEE_CLIENT_ID||process.env.FREEE_CLIENT_SECRET)}async function ge(){if(O)return O;let t=await I(),e,r,n;if(xt())console.error("Warning: \u74B0\u5883\u5909\u6570\u3067\u306E\u8A8D\u8A3C\u60C5\u5831\u8A2D\u5B9A\u306F\u975E\u63A8\u5968\u3067\u3059\u3002"),console.error(" `freee-mcp configure` \u3092\u5B9F\u884C\u3057\u3066\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u306B\u79FB\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002"),console.error(` \u74B0\u5883\u5909\u6570\u8A2D\u5B9A\u306F\u5C06\u6765\u306E\u30D0\u30FC\u30B8\u30E7\u30F3\u3067\u524A\u9664\u3055\u308C\u308B\u4E88\u5B9A\u3067\u3059\u3002
|
|
4
|
+
`),e=process.env.FREEE_CLIENT_ID||"",r=process.env.FREEE_CLIENT_SECRET||"",n=process.env.FREEE_CALLBACK_PORT?parseInt(process.env.FREEE_CALLBACK_PORT,10):N;else{if(!t.clientId||!t.clientSecret)throw new Error("\u8A8D\u8A3C\u60C5\u5831\u304C\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002\n`freee-mcp configure` \u3092\u5B9F\u884C\u3057\u3066\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3057\u3066\u304F\u3060\u3055\u3044\u3002");e=t.clientId,r=t.clientSecret,n=t.callbackPort||N}return O={freee:{clientId:e,clientSecret:r,companyId:"0",apiUrl:Q},oauth:{callbackPort:n,redirectUri:`http://127.0.0.1:${n}/callback`,authorizationEndpoint:"https://accounts.secure.freee.co.jp/public_api/authorize",tokenEndpoint:"https://accounts.secure.freee.co.jp/public_api/token",scope:"read write"},server:{name:"freee",version:"1.0.0"},auth:{timeoutMs:R}},O}function h(){if(!O)throw new Error("Config not loaded. Call loadConfig() first in async context.");return O}var O,S=pe(()=>{"use strict";F();w();O=null});S();import{McpServer as Kt}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as Wt}from"@modelcontextprotocol/sdk/server/stdio.js";S();import Lt from"crypto";import{z as P}from"zod";S();S();w();import ee from"fs/promises";import Ve from"path";import{z as C}from"zod";async function _(t){try{return{success:!0,data:await t.json()}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function l(t){return{content:[{type:"text",text:t}]}}function k(t){return t instanceof Error?t.message:String(t)}function Y(t,e){return{access_token:t.access_token,refresh_token:t.refresh_token||e.refreshToken||"",expires_at:Date.now()+t.expires_in*1e3,token_type:t.token_type||"Bearer",scope:t.scope||e.scope}}w();import Z from"fs/promises";import he from"path";async function Je(){let t=T();try{return(await Z.readdir(t)).filter(r=>r.startsWith("tokens-")&&r.endsWith(".json"))}catch{return[]}}async function He(t){let e=T();try{let r=await Je();if(r.length>0){let n=he.join(e,r[0]),o=await Z.readFile(n,"utf8"),a=JSON.parse(o),i=ye.safeParse(a);if(!i.success)return console.error("[error] Invalid legacy token file:",i.error.message),null;let s=i.data;return await t(s),await Promise.all(r.map(p=>Z.unlink(he.join(e,p)).catch(u=>{console.error(`[warn] Failed to clean up legacy token file ${p}:`,u)}))),console.error("[info] Migrated legacy company-specific tokens to user-based tokens"),s}}catch(r){console.error("[warn] Error during legacy token migration attempt:",r)}return null}async function Be(){let t=T();try{let e=await Je();await Promise.all(e.map(r=>Z.unlink(he.join(t,r)).catch(n=>{console.error(`[warn] Failed to clear legacy token file ${r}:`,n)}))),e.length>0&&console.error("[info] Cleared legacy company-specific token files")}catch(e){console.error("[warn] Error during legacy token cleanup:",e)}}var ye=C.object({access_token:C.string(),refresh_token:C.string(),expires_at:C.number(),token_type:C.string(),scope:C.string()}),Ce=C.object({access_token:C.string(),refresh_token:C.string().optional(),expires_in:C.number(),token_type:C.string().optional(),scope:C.string().optional()});function we(){return Ve.join(T(),"tokens.json")}async function te(t){let e=we(),r=Ve.dirname(e);try{console.error(`[info] Creating directory: ${r}`),await ee.mkdir(r,{recursive:!0}),console.error(`[info] Writing tokens to: ${e}`),await ee.writeFile(e,JSON.stringify(t,null,2),{mode:X}),console.error("[info] Tokens saved successfully")}catch(n){throw console.error("[error] Failed to save tokens:",n),n}}async function _e(){let t=we();try{let e=await ee.readFile(t,"utf8"),r=JSON.parse(e),n=ye.safeParse(r);return n.success?n.data:(console.error("[error] Invalid token file:",n.error.message),null)}catch(e){if(e.code==="ENOENT"){let r=await He(te);return r||null}throw console.error("[error] Failed to load tokens:",e),e}}function Rt(t){return Date.now()<t.expires_at}async function $t(t){let e=h(),r=await fetch(e.oauth.tokenEndpoint,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded","User-Agent":$},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:t,client_id:e.freee.clientId,client_secret:e.freee.clientSecret})});if(!r.ok){let i=await _(r),s=i.success?JSON.stringify(i.data):`(JSON parse failed: ${i.error})`;throw new Error(`Token refresh failed: ${r.status} ${s}`)}let n=await r.json(),o=Ce.safeParse(n);if(!o.success)throw new Error(`Invalid token response format: ${o.error.message}`);let a=Y(o.data,{refreshToken:t,scope:e.oauth.scope});return await te(a),a}async function ze(){let t=we();try{await ee.unlink(t),console.error("[info] Tokens cleared successfully")}catch(e){if(e.code==="ENOENT"){console.error("[info] No tokens to clear (file does not exist)");return}throw console.error("[error] Failed to clear tokens:",e),e}await Be()}async function Ge(){let t=await _e();return t?Rt(t)?t.access_token:(await $t(t.refresh_token)).access_token:null}F();w();import Mt from"fs/promises";import Ft from"path";function qe(t){return typeof t=="object"&&t!==null&&"type"in t&&t.type==="binary"}function Ot(t){return["application/pdf","application/octet-stream","image/"].some(r=>t.includes(r))}function Dt(t){let e={"application/pdf":".pdf","image/png":".png","image/jpeg":".jpg","image/gif":".gif","image/webp":".webp","text/csv":".csv"};for(let[r,n]of Object.entries(e))if(t.includes(r))return n;return t.includes("image/"),".bin"}async function L(t,e,r,n,o){let a=o||h().freee.apiUrl,i=await x(),s=await Ge();if(!s)throw new Error(`\u8A8D\u8A3C\u304C\u5FC5\u8981\u3067\u3059\u3002freee_authenticate \u30C4\u30FC\u30EB\u3092\u4F7F\u7528\u3057\u3066\u8A8D\u8A3C\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
|
|
5
5
|
\u73FE\u5728\u306E\u4E8B\u696D\u6240ID: ${i}
|
|
6
|
-
\u307E\u305F\u306F\u3001FREEE_CLIENT_ID\u74B0\u5883\u5909\u6570\u304C\u6B63\u3057\u304F\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u308B\u304B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002`);let p=a.endsWith("/")?a:a+"/",u=e.startsWith("/")?e.slice(1):e,
|
|
6
|
+
\u307E\u305F\u306F\u3001FREEE_CLIENT_ID\u74B0\u5883\u5909\u6570\u304C\u6B63\u3057\u304F\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u308B\u304B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002`);let p=a.endsWith("/")?a:a+"/",u=e.startsWith("/")?e.slice(1):e,E=new URL(u,p);r&&Object.entries(r).forEach(([m,f])=>{f!==void 0&&E.searchParams.append(m,String(f))});let ce=r?.company_id;if(ce!==void 0&&String(ce)!==String(i))throw new Error(`company_id \u306E\u4E0D\u6574\u5408: \u30EA\u30AF\u30A8\u30B9\u30C8\u306E company_id (${ce}) \u3068\u73FE\u5728\u306E\u4E8B\u696D\u6240 (${i}) \u304C\u7570\u306A\u308A\u307E\u3059\u3002
|
|
7
7
|
freee_set_current_company \u3067\u4E8B\u696D\u6240\u3092\u5207\u308A\u66FF\u3048\u308B\u304B\u3001\u30EA\u30AF\u30A8\u30B9\u30C8\u306E company_id \u3092\u4FEE\u6B63\u3057\u3066\u304F\u3060\u3055\u3044\u3002`);let le=n?.company_id;if(le!==void 0&&String(le)!==String(i))throw new Error(`company_id \u306E\u4E0D\u6574\u5408: \u30EA\u30AF\u30A8\u30B9\u30C8\u30DC\u30C7\u30A3\u306E company_id (${le}) \u3068\u73FE\u5728\u306E\u4E8B\u696D\u6240 (${i}) \u304C\u7570\u306A\u308A\u307E\u3059\u3002
|
|
8
|
-
freee_set_current_company \u3067\u4E8B\u696D\u6240\u3092\u5207\u308A\u66FF\u3048\u308B\u304B\u3001\u30EA\u30AF\u30A8\u30B9\u30C8\u306E company_id \u3092\u4FEE\u6B63\u3057\u3066\u304F\u3060\u3055\u3044\u3002`);let y=await fetch(
|
|
8
|
+
freee_set_current_company \u3067\u4E8B\u696D\u6240\u3092\u5207\u308A\u66FF\u3048\u308B\u304B\u3001\u30EA\u30AF\u30A8\u30B9\u30C8\u306E company_id \u3092\u4FEE\u6B63\u3057\u3066\u304F\u3060\u3055\u3044\u3002`);let y=await fetch(E.toString(),{method:t,headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json","User-Agent":$},body:n?JSON.stringify(typeof n=="string"?JSON.parse(n):n):void 0});if(y.status===401){let m=await _(y),f=m.success?JSON.stringify(m.data):`(JSON parse failed: ${m.error})`;throw new Error(`\u8A8D\u8A3C\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002freee_authenticate \u30C4\u30FC\u30EB\u3092\u4F7F\u7528\u3057\u3066\u518D\u8A8D\u8A3C\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
|
|
9
9
|
\u73FE\u5728\u306E\u4E8B\u696D\u6240ID: ${i}
|
|
10
10
|
\u30A8\u30E9\u30FC\u8A73\u7D30: ${y.status} ${f}
|
|
11
11
|
|
|
@@ -17,7 +17,7 @@ freee_set_current_company \u3067\u4E8B\u696D\u6240\u3092\u5207\u308A\u66FF\u3048
|
|
|
17
17
|
\u4E8B\u696D\u6240ID: ${i}
|
|
18
18
|
|
|
19
19
|
\u30EC\u30FC\u30C8\u30EA\u30DF\u30C3\u30C8\u306E\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002\u6570\u5206\u5F85\u3063\u3066\u304B\u3089\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
20
|
-
\u305D\u308C\u3067\u3082\u89E3\u6C7A\u3057\u306A\u3044\u5834\u5408\u306F\u3001\u30A2\u30D7\u30EA\u306E\u6A29\u9650\u8A2D\u5B9A\u3092\u78BA\u8A8D\u3059\u308B\u304B\u3001freee_authenticate \u3067\u518D\u8A8D\u8A3C\u3057\u3066\u304F\u3060\u3055\u3044\u3002`)}if(!y.ok){let m=await _(y),f=`API request failed: ${y.status}`;if(m.success){let
|
|
20
|
+
\u305D\u308C\u3067\u3082\u89E3\u6C7A\u3057\u306A\u3044\u5834\u5408\u306F\u3001\u30A2\u30D7\u30EA\u306E\u6A29\u9650\u8A2D\u5B9A\u3092\u78BA\u8A8D\u3059\u308B\u304B\u3001freee_authenticate \u3067\u518D\u8A8D\u8A3C\u3057\u3066\u304F\u3060\u3055\u3044\u3002`)}if(!y.ok){let m=await _(y),f=`API request failed: ${y.status}`;if(m.success){let b=m.data;if(b&&b.errors&&Array.isArray(b.errors)){let j=[];for(let A of b.errors)A&&typeof A=="object"&&"messages"in A&&Array.isArray(A.messages)&&j.push(...A.messages);j.length>0&&(f+=`
|
|
21
21
|
|
|
22
22
|
\u30A8\u30E9\u30FC\u8A73\u7D30:
|
|
23
23
|
${j.join(`
|
|
@@ -25,11 +25,11 @@ ${j.join(`
|
|
|
25
25
|
|
|
26
26
|
\u30D2\u30F3\u30C8: \u4E0D\u6B63\u306A\u30EA\u30AF\u30A8\u30B9\u30C8\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002`,f+=`
|
|
27
27
|
\u65E2\u5B58\u306E\u30C7\u30FC\u30BF\u3092\u53D6\u5F97\u3057\u3066\u6B63\u3057\u3044\u69CB\u9020\u3092\u78BA\u8A8D\u3059\u308B\u3053\u3068\u3092\u304A\u52E7\u3081\u3057\u307E\u3059\u3002`,f+=`
|
|
28
|
-
\u4F8B: freee_api_get \u3067\u65E2\u5B58\u30C7\u30FC\u30BF\u3092\u53D6\u5F97\u3057\u3001\u6B63\u3057\u3044\u69CB\u9020\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002`))}
|
|
28
|
+
\u4F8B: freee_api_get \u3067\u65E2\u5B58\u30C7\u30FC\u30BF\u3092\u53D6\u5F97\u3057\u3001\u6B63\u3057\u3044\u69CB\u9020\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002`))}b?.errors||(f+=`
|
|
29
29
|
|
|
30
|
-
\u8A73\u7D30: ${JSON.stringify(
|
|
30
|
+
\u8A73\u7D30: ${JSON.stringify(b)}`)}else f+=`
|
|
31
31
|
|
|
32
|
-
\u8A73\u7D30: (JSON parse failed: ${m.error})`;throw new Error(f)}let W=y.headers.get("content-type")||"";if(Ot(W)){let m=await de(),f=Dt(W),j=`freee_download_${Date.now()}${f}`,
|
|
32
|
+
\u8A73\u7D30: (JSON parse failed: ${m.error})`;throw new Error(f)}let W=y.headers.get("content-type")||"";if(Ot(W)){let m=await de(),f=Dt(W),j=`freee_download_${Date.now()}${f}`,A=Ft.join(m,j),$e=await y.arrayBuffer();return await Mt.writeFile(A,Buffer.from($e)),{type:"binary",filePath:A,mimeType:W,size:$e.byteLength}}let Re=await y.text();try{return JSON.parse(Re)}catch{throw new Error(`Failed to parse API response as JSON. Status: ${y.status}, Content-Type: ${W}, Body preview: ${Re.slice(0,200)}`)}}S();import Ke from"crypto";w();function We(){let t=Ke.randomBytes(32).toString("base64url"),e=Ke.createHash("sha256").update(t).digest("base64url");return{codeVerifier:t,codeChallenge:e}}function re(t,e,r){let n=h(),o=new URLSearchParams({response_type:"code",client_id:n.freee.clientId,redirect_uri:r,scope:n.oauth.scope,state:e,code_challenge:t,code_challenge_method:"S256"});return`${n.oauth.authorizationEndpoint}?${o.toString()}`}async function ne(t,e,r){let n=h(),o=await fetch(n.oauth.tokenEndpoint,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded","User-Agent":$},body:new URLSearchParams({grant_type:"authorization_code",client_id:n.freee.clientId,client_secret:n.freee.clientSecret,code:t,redirect_uri:r,code_verifier:e})});if(!o.ok){let p=await _(o),u=p.success?JSON.stringify(p.data):`(JSON parse failed: ${p.error})`;throw new Error(`Token exchange failed: ${o.status} ${u}`)}let a=await o.json(),i=Ce.safeParse(a);if(!i.success)throw new Error(`Invalid token response format: ${i.error.message}`);let s=Y(i.data,{scope:n.oauth.scope});return await te(s),s}S();import jt from"http";import{URL as Nt}from"url";import Ut from"net";var ke=class{pendingAuthentications=new Map;cliAuthHandlers=new Map;registerAuthentication(e,r){console.error(`Registering authentication request with state: ${e.substring(0,10)}...`),console.error(`Code verifier: ${r.substring(0,10)}...`);let n=setTimeout(()=>{this.pendingAuthentications.delete(e),console.error(`Authentication timeout for state: ${e.substring(0,10)}...`)},h().auth.timeoutMs);this.pendingAuthentications.set(e,{codeVerifier:r,resolve:o=>{console.error("Authentication completed successfully!")},reject:o=>{console.error("Authentication failed:",o)},timeout:n}),console.error(`Registration complete. Total pending: ${this.pendingAuthentications.size}`)}getPendingAuthentication(e){return this.pendingAuthentications.get(e)}removePendingAuthentication(e){let r=this.pendingAuthentications.get(e);r&&(clearTimeout(r.timeout),this.pendingAuthentications.delete(e))}clearAllPending(){for(let[e,r]of this.pendingAuthentications)clearTimeout(r.timeout),r.reject(new Error("Server shutdown"));this.pendingAuthentications.clear()}get pendingCount(){return this.pendingAuthentications.size}registerCliAuthHandler(e,r){this.cliAuthHandlers.set(e,r)}getCliAuthHandler(e){return this.cliAuthHandlers.get(e)}removeCliAuthHandler(e){this.cliAuthHandlers.delete(e)}},Ee=class{server=null;port=null;authManager;autoStopTimeout=null;constructor(e){this.authManager=e}clearAutoStopTimeout(){this.autoStopTimeout&&(clearTimeout(this.autoStopTimeout),this.autoStopTimeout=null)}scheduleAutoStop(e){this.clearAutoStopTimeout(),this.autoStopTimeout=setTimeout(()=>{console.error("OAuth callback server auto-stopping after timeout"),this.stop()},e)}getRedirectUri(){if(this.port===null)throw new Error("Callback server not started. Call start() first.");return`http://127.0.0.1:${this.port}/callback`}getPort(){return this.port}isRunning(){return this.server!==null}async start(){if(this.server){console.error("OAuth callback server is already running. If authentication is not working, try restarting the MCP server.");return}let e=h().oauth.callbackPort;if(!await this.checkPortAvailable(e)){let n=`http://127.0.0.1:${e}/callback`;throw new Error(`\u30DD\u30FC\u30C8 ${e} \u306F\u65E2\u306B\u4F7F\u7528\u3055\u308C\u3066\u3044\u307E\u3059\u3002
|
|
33
33
|
|
|
34
34
|
freee \u30A2\u30D7\u30EA\u306B\u30B3\u30FC\u30EB\u30D0\u30C3\u30AFURL (${n}) \u3092\u767B\u9332\u3057\u3066\u3044\u308B\u5834\u5408\u3001\u30DD\u30FC\u30C8\u3092\u5909\u66F4\u3059\u308B\u3068\u8A8D\u8A3C\u304C\u5931\u6557\u3057\u307E\u3059\u3002
|
|
35
35
|
|
|
@@ -37,7 +37,7 @@ freee \u30A2\u30D7\u30EA\u306B\u30B3\u30FC\u30EB\u30D0\u30C3\u30AFURL (${n}) \u3
|
|
|
37
37
|
1. \u30DD\u30FC\u30C8 ${e} \u3092\u4F7F\u7528\u3057\u3066\u3044\u308B\u30D7\u30ED\u30BB\u30B9\u3092\u7D42\u4E86\u3059\u308B
|
|
38
38
|
(\u4F8B: lsof -i :${e} \u3067\u30D7\u30ED\u30BB\u30B9\u3092\u78BA\u8A8D)
|
|
39
39
|
2. \u307E\u305F\u306F\u3001\u8A2D\u5B9A\u3067\u30DD\u30FC\u30C8\u3092\u5909\u66F4\u3057\u3001freee \u30A2\u30D7\u30EA\u306E\u30B3\u30FC\u30EB\u30D0\u30C3\u30AFURL\u3082\u66F4\u65B0\u3059\u308B
|
|
40
|
-
(freee-mcp configure \u3092\u5B9F\u884C\u3057\u3066\u518D\u8A2D\u5B9A)`)}return this.port=e,new Promise((n,o)=>{this.server=jt.createServer((a,i)=>{console.error(`Callback request: ${a.method} ${a.url}`);let s=new Nt(a.url,`http://127.0.0.1:${e}`);s.pathname==="/callback"?this.handleCallback(s,i):s.pathname==="/"?(i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),i.end("<h1>freee MCP OAuth Server</h1><p>\u30B3\u30FC\u30EB\u30D0\u30C3\u30AF\u30B5\u30FC\u30D0\u30FC\u304C\u7A3C\u50CD\u4E2D\u3067\u3059\u3002</p>")):(i.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),i.end("<h1>404 Not Found</h1><p>\u3053\u306E\u30D1\u30B9\u306F\u5B58\u5728\u3057\u307E\u305B\u3093\u3002</p>"))}),this.server.on("error",a=>{let i=a instanceof Error?a.message:String(a);console.error(`OAuth callback server failed to start: ${i}`),this.server=null,this.port=null,o(new Error(`Failed to start OAuth callback server: ${i}`))}),this.server.listen(e,"127.0.0.1",()=>{console.error(`OAuth callback server listening on http://127.0.0.1:${e}`),n()})})}stop(){this.clearAutoStopTimeout(),this.server&&(this.authManager.clearAllPending(),this.server.close(()=>{console.error("OAuth callback server stopped")}),this.server=null,this.port=null)}async checkPortAvailable(e){return new Promise(r=>{let n=Ut.createServer();n.listen(e,"127.0.0.1",()=>{n.close(()=>{r(!0)})}),n.on("error",()=>{r(!1)})})}handleCallback(e,r){let n=e.searchParams.get("code"),o=e.searchParams.get("state"),a=e.searchParams.get("error"),i=e.searchParams.get("error_description");console.error(`Callback received - URL: ${e.toString()}`),console.error("Callback parameters:",{code:n?`${n.substring(0,10)}...`:null,state:o?`${o.substring(0,10)}...`:null,error:a,errorDescription:i}),console.error(`Pending authentications count: ${this.authManager.pendingCount}`);let s=o?this.authManager.getCliAuthHandler(o):void 0;if(a){let u=i||a;if(console.error(`OAuth error: ${a} - ${i}`),r.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),r.end(`<h1>\u8A8D\u8A3C\u30A8\u30E9\u30FC</h1><p>\u8A8D\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${u}</p>`),s)s.reject(new Error(`OAuth error: ${a} - ${i}`));else if(o){let
|
|
40
|
+
(freee-mcp configure \u3092\u5B9F\u884C\u3057\u3066\u518D\u8A2D\u5B9A)`)}return this.port=e,new Promise((n,o)=>{this.server=jt.createServer((a,i)=>{console.error(`Callback request: ${a.method} ${a.url}`);let s=new Nt(a.url,`http://127.0.0.1:${e}`);s.pathname==="/callback"?this.handleCallback(s,i):s.pathname==="/"?(i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),i.end("<h1>freee MCP OAuth Server</h1><p>\u30B3\u30FC\u30EB\u30D0\u30C3\u30AF\u30B5\u30FC\u30D0\u30FC\u304C\u7A3C\u50CD\u4E2D\u3067\u3059\u3002</p>")):(i.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),i.end("<h1>404 Not Found</h1><p>\u3053\u306E\u30D1\u30B9\u306F\u5B58\u5728\u3057\u307E\u305B\u3093\u3002</p>"))}),this.server.on("error",a=>{let i=a instanceof Error?a.message:String(a);console.error(`OAuth callback server failed to start: ${i}`),this.server=null,this.port=null,o(new Error(`Failed to start OAuth callback server: ${i}`))}),this.server.listen(e,"127.0.0.1",()=>{console.error(`OAuth callback server listening on http://127.0.0.1:${e}`),n()})})}stop(){this.clearAutoStopTimeout(),this.server&&(this.authManager.clearAllPending(),this.server.close(()=>{console.error("OAuth callback server stopped")}),this.server=null,this.port=null)}async checkPortAvailable(e){return new Promise(r=>{let n=Ut.createServer();n.listen(e,"127.0.0.1",()=>{n.close(()=>{r(!0)})}),n.on("error",()=>{r(!1)})})}async handleCallback(e,r){let n=e.searchParams.get("code"),o=e.searchParams.get("state"),a=e.searchParams.get("error"),i=e.searchParams.get("error_description");console.error(`Callback received - URL: ${e.toString()}`),console.error("Callback parameters:",{code:n?`${n.substring(0,10)}...`:null,state:o?`${o.substring(0,10)}...`:null,error:a,errorDescription:i}),console.error(`Pending authentications count: ${this.authManager.pendingCount}`);let s=o?this.authManager.getCliAuthHandler(o):void 0;if(a){let u=i||a;if(console.error(`OAuth error: ${a} - ${i}`),r.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),r.end(`<h1>\u8A8D\u8A3C\u30A8\u30E9\u30FC</h1><p>\u8A8D\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${u}</p>`),s)s.reject(new Error(`OAuth error: ${a} - ${i}`));else if(o){let E=this.authManager.getPendingAuthentication(o);E&&(clearTimeout(E.timeout),E.reject(new Error(`OAuth error: ${a} - ${i}`)),this.authManager.removePendingAuthentication(o))}return}if(!n||!o){console.error("Missing code or state"),r.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),r.end("<h1>\u8A8D\u8A3C\u30A8\u30E9\u30FC</h1><p>\u8A8D\u8A3C\u30B3\u30FC\u30C9\u307E\u305F\u306F\u72B6\u614B\u30D1\u30E9\u30E1\u30FC\u30BF\u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059\u3002</p>");return}if(s){console.error("Valid CLI callback received"),r.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),r.end("<h1>\u8A8D\u8A3C\u5B8C\u4E86</h1><p>\u8A8D\u8A3C\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F\u3002\u3053\u306E\u30DA\u30FC\u30B8\u3092\u9589\u3058\u3066\u30BF\u30FC\u30DF\u30CA\u30EB\u306B\u623B\u3063\u3066\u304F\u3060\u3055\u3044\u3002</p>"),s.resolve(n);return}let p=this.authManager.getPendingAuthentication(o);if(!p){console.error(`Unknown state: ${o}`),r.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),r.end("<h1>\u8A8D\u8A3C\u30A8\u30E9\u30FC</h1><p>\u4E0D\u660E\u306A\u8A8D\u8A3C\u72B6\u614B\u3067\u3059\u3002\u8A8D\u8A3C\u3092\u518D\u958B\u3057\u3066\u304F\u3060\u3055\u3044\u3002</p>");return}console.error("Valid callback received, exchanging code for tokens...");try{let u=await ne(n,p.codeVerifier,this.getRedirectUri());console.error("Token exchange successful!"),p.resolve(u),r.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),r.end("<h1>\u8A8D\u8A3C\u5B8C\u4E86</h1><p>\u8A8D\u8A3C\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F\u3002\u3053\u306E\u30DA\u30FC\u30B8\u3092\u9589\u3058\u3066\u304F\u3060\u3055\u3044\u3002</p>")}catch(u){console.error("Token exchange failed:",u),p.reject(u);let E=u instanceof Error?u.message:String(u);r.writeHead(500,{"Content-Type":"text/html; charset=utf-8"}),r.end(`<h1>\u8A8D\u8A3C\u30A8\u30E9\u30FC</h1><p>\u30C8\u30FC\u30AF\u30F3\u4EA4\u63DB\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${E}</p>`)}finally{this.authManager.removePendingAuthentication(o)}}},Ae=new ke,J=new Ee(Ae);function H(){return J.getRedirectUri()}async function Xe(){return J.start()}async function Qe(t){await J.start(),J.scheduleAutoStop(t)}function Ye(t,e){Ae.registerAuthentication(t,e)}function Ze(){J.stop()}function et(){return Ae}F();w();function tt(t){t.tool("freee_current_user","\u73FE\u5728\u306E\u30E6\u30FC\u30B6\u30FC\u60C5\u5831\u3092\u53D6\u5F97\u3002\u8A73\u7D30\u30AC\u30A4\u30C9\u306Ffreee-mcp skill\u3092\u53C2\u7167\u3002",{},async()=>{try{let e=await x(),r=await U(e);if(!e)return l("\u4F1A\u793EID\u304C\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002freee_set_current_company \u3067\u8A2D\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");let n=await L("GET","/api/1/users/me");return l(`\u73FE\u5728\u306E\u30E6\u30FC\u30B6\u30FC\u60C5\u5831:
|
|
41
41
|
\u4F1A\u793EID: ${e}
|
|
42
42
|
\u4F1A\u793E\u540D: ${r?.name||"Unknown"}
|
|
43
43
|
\u30E6\u30FC\u30B6\u30FC\u8A73\u7D30:
|
|
@@ -45,13 +45,13 @@ ${JSON.stringify(n,null,2)}`)}catch(e){return l(`\u30E6\u30FC\u30B6\u30FC\u60C5\
|
|
|
45
45
|
OAuth\u8A8D\u8A3C\u3092\u884C\u3046\u306B\u306F\u3001freee developers\u3067\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u3092\u4F5C\u6210\u3057\u3001
|
|
46
46
|
\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8ID\u3092\u74B0\u5883\u5909\u6570\u306B\u8A2D\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002`);if(!h().freee.clientSecret)return l(`FREEE_CLIENT_SECRET\u74B0\u5883\u5909\u6570\u304C\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002
|
|
47
47
|
OAuth\u8A8D\u8A3C\u3092\u884C\u3046\u306B\u306F\u3001freee developers\u3067\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u3092\u4F5C\u6210\u3057\u3001
|
|
48
|
-
\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8\u30B7\u30FC\u30AF\u30EC\u30C3\u30C8\u3092\u74B0\u5883\u5909\u6570\u306B\u8A2D\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002`);await Qe(R);let{codeVerifier:e,codeChallenge:r}=We(),n=Lt.randomBytes(16).toString("hex"),o=re(r,n,
|
|
48
|
+
\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8\u30B7\u30FC\u30AF\u30EC\u30C3\u30C8\u3092\u74B0\u5883\u5909\u6570\u306B\u8A2D\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002`);await Qe(R);let{codeVerifier:e,codeChallenge:r}=We(),n=Lt.randomBytes(16).toString("hex"),o=re(r,n,H());return Ye(n,e),console.error(`Authentication URL: ${o}`),l(`\u8A8D\u8A3CURL: ${o}
|
|
49
49
|
|
|
50
50
|
\u30D6\u30E9\u30A6\u30B6\u3067\u958B\u3044\u3066\u8A8D\u8A3C\u3057\u3066\u304F\u3060\u3055\u3044\u30025\u5206\u3067\u30BF\u30A4\u30E0\u30A2\u30A6\u30C8\u3057\u307E\u3059\u3002`)}catch(e){return l(`\u8A8D\u8A3C\u958B\u59CB\u306B\u5931\u6557: ${k(e)}`)}}),t.tool("freee_auth_status","\u8A8D\u8A3C\u72B6\u614B\u3092\u78BA\u8A8D\u3002",{},async()=>{try{let e=await _e();if(!e)return l("\u672A\u8A8D\u8A3C\u3002freee_authenticate \u3067\u8A8D\u8A3C\u3057\u3066\u304F\u3060\u3055\u3044\u3002");let r=Date.now()<e.expires_at,n=new Date(e.expires_at).toLocaleString();return l(`\u8A8D\u8A3C\u72B6\u614B: ${r?"\u6709\u52B9":"\u671F\u9650\u5207\u308C"}
|
|
51
51
|
\u6709\u52B9\u671F\u9650: ${n}`+(r?"":`
|
|
52
|
-
\u6B21\u56DEAPI\u4F7F\u7528\u6642\u306B\u81EA\u52D5\u66F4\u65B0\u3055\u308C\u307E\u3059\u3002`))}catch(e){return l(`\u8A8D\u8A3C\u72B6\u614B\u306E\u78BA\u8A8D\u306B\u5931\u6557: ${k(e)}`)}}),t.tool("freee_clear_auth","\u8A8D\u8A3C\u60C5\u5831\u3092\u30AF\u30EA\u30A2\u3002",{},async()=>{try{return await ze(),l("\u8A8D\u8A3C\u60C5\u5831\u3092\u30AF\u30EA\u30A2\u3057\u307E\u3057\u305F\u3002\u518D\u8A8D\u8A3C\u3059\u308B\u306B\u306F freee_authenticate \u3092\u4F7F\u7528\u3002")}catch(e){return l(`\u8A8D\u8A3C\u60C5\u5831\u306E\u30AF\u30EA\u30A2\u306B\u5931\u6557: ${k(e)}`)}}),t.tool("freee_set_current_company","\u4E8B\u696D\u6240\u3092\u8A2D\u5B9A\u30FB\u5207\u308A\u66FF\u3048\u3002",{company_id:
|
|
52
|
+
\u6B21\u56DEAPI\u4F7F\u7528\u6642\u306B\u81EA\u52D5\u66F4\u65B0\u3055\u308C\u307E\u3059\u3002`))}catch(e){return l(`\u8A8D\u8A3C\u72B6\u614B\u306E\u78BA\u8A8D\u306B\u5931\u6557: ${k(e)}`)}}),t.tool("freee_clear_auth","\u8A8D\u8A3C\u60C5\u5831\u3092\u30AF\u30EA\u30A2\u3002",{},async()=>{try{return await ze(),l("\u8A8D\u8A3C\u60C5\u5831\u3092\u30AF\u30EA\u30A2\u3057\u307E\u3057\u305F\u3002\u518D\u8A8D\u8A3C\u3059\u308B\u306B\u306F freee_authenticate \u3092\u4F7F\u7528\u3002")}catch(e){return l(`\u8A8D\u8A3C\u60C5\u5831\u306E\u30AF\u30EA\u30A2\u306B\u5931\u6557: ${k(e)}`)}}),t.tool("freee_set_current_company","\u4E8B\u696D\u6240\u3092\u8A2D\u5B9A\u30FB\u5207\u308A\u66FF\u3048\u3002",{company_id:P.string().describe("\u4E8B\u696D\u6240ID"),name:P.string().optional().describe("\u4E8B\u696D\u6240\u540D"),description:P.string().optional().describe("\u8AAC\u660E")},async e=>{try{let{company_id:r,name:n,description:o}=e;await me(r,n,o);let a=await U(r);return l(`\u4E8B\u696D\u6240\u3092\u8A2D\u5B9A: ${a?.name||r}`)}catch(r){return l(`\u4E8B\u696D\u6240\u306E\u8A2D\u5B9A\u306B\u5931\u6557: ${k(r)}`)}}),t.tool("freee_get_current_company","\u73FE\u5728\u306E\u4E8B\u696D\u6240\u60C5\u5831\u3092\u8868\u793A\u3002",{},async()=>{try{let e=await x(),r=await U(e);return r?l(`\u4E8B\u696D\u6240: ${r.name} (ID: ${r.id})`):l(`\u4E8B\u696D\u6240ID: ${e} (\u8A73\u7D30\u60C5\u5831\u306A\u3057)`)}catch(e){return l(`\u4E8B\u696D\u6240\u60C5\u5831\u306E\u53D6\u5F97\u306B\u5931\u6557: ${k(e)}`)}}),t.tool("freee_list_companies","\u4E8B\u696D\u6240\u4E00\u89A7\u3092\u8868\u793A\u3002",{},async()=>{try{let e=P.object({companies:P.array(P.object({id:P.number(),name:P.string()})).optional()}),r=await L("GET","/api/1/companies"),n=e.safeParse(r);if(!n.success)return{content:[{type:"text",text:`API\u30EC\u30B9\u30DD\u30F3\u30B9\u306E\u5F62\u5F0F\u304C\u4E0D\u6B63\u3067\u3059: ${n.error.message}`}]};let o=n.data,a=await x();if(!o?.companies?.length)return l("\u4E8B\u696D\u6240\u60C5\u5831\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002");let i=o.companies.map(s=>{let p=s.id===parseInt(a)?" *":"";return`${s.name} (${s.id})${p}`}).join(`
|
|
53
53
|
`);return l(`\u4E8B\u696D\u6240\u4E00\u89A7:
|
|
54
|
-
${i}`)}catch(e){return l(`\u4E8B\u696D\u6240\u4E00\u89A7\u306E\u53D6\u5F97\u306B\u5931\u6557: ${k(e)}`)}})}import{z as c}from"zod";import it from"node:fs";import V from"node:path";import{fileURLToPath as
|
|
54
|
+
${i}`)}catch(e){return l(`\u4E8B\u696D\u6240\u4E00\u89A7\u306E\u53D6\u5F97\u306B\u5931\u6557: ${k(e)}`)}})}import{z as c}from"zod";import it from"node:fs";import V from"node:path";import{fileURLToPath as Bt}from"node:url";import{z as g}from"zod";var Jt=g.object({name:g.string(),in:g.enum(["path","query"]),required:g.boolean().optional(),description:g.string().optional(),type:g.string()}),B=g.object({summary:g.string().optional(),description:g.string().optional(),parameters:g.array(Jt).optional(),hasJsonBody:g.boolean().optional()}),Ht=g.object({get:B.optional(),post:B.optional(),put:B.optional(),delete:B.optional(),patch:B.optional()}),rt=g.object({paths:g.record(g.string(),Ht)});var Te=V.dirname(Bt(import.meta.url));function Vt(){let t=[V.resolve(Te,"./openapi/minimal"),V.resolve(Te,"../dist/openapi/minimal"),V.resolve(Te,"../../openapi/minimal")];for(let e of t)if(it.existsSync(e))return e;throw new Error(`Could not find minimal schema directory. Searched paths:
|
|
55
55
|
${t.join(`
|
|
56
56
|
`)}`)}var zt=Vt();function Gt(t){let e=V.join(zt,t),r=it.readFileSync(e,"utf-8"),n=JSON.parse(r),o=rt.safeParse(n);if(!o.success)throw new Error(`Invalid schema file ${t}: ${o.error.message}`);return o.data}var oe={accounting:{schemaFile:"accounting.json",baseUrl:"https://api.freee.co.jp",prefix:"accounting",name:"freee\u4F1A\u8A08 API"},hr:{schemaFile:"hr.json",baseUrl:"https://api.freee.co.jp/hr",prefix:"hr",name:"freee\u4EBA\u4E8B\u52B4\u52D9 API"},invoice:{schemaFile:"invoice.json",baseUrl:"https://api.freee.co.jp/iv",prefix:"invoice",name:"freee\u8ACB\u6C42\u66F8 API"},pm:{schemaFile:"pm.json",baseUrl:"https://api.freee.co.jp/pm",prefix:"pm",name:"freee\u5DE5\u6570\u7BA1\u7406 API"},sm:{schemaFile:"sm.json",baseUrl:"https://api.freee.co.jp/sm",prefix:"sm",name:"freee\u8CA9\u58F2 API"}},Se={};function nt(t){if(!Se[t]){let e=oe[t];Se[t]={schema:Gt(e.schemaFile),baseUrl:e.baseUrl,prefix:e.prefix,name:e.name}}return Se[t]}var Pe=new Proxy({},{get(t,e){if(e in oe)return nt(e)},ownKeys(){return Object.keys(oe)},getOwnPropertyDescriptor(t,e){if(e in oe)return{enumerable:!0,configurable:!0,value:nt(e)}}});function ot(t,e,r,n){let o=n.schema.paths;if(e in o){let a=o[e];if(t in a)return{isValid:!0,message:"Valid path and method",operation:a[t],actualPath:e,apiType:r,baseUrl:n.baseUrl}}for(let a of Object.keys(o)){let i=a.replace(/\{[^}]+\}/g,"[^/]+");if(new RegExp(`^${i}$`).test(e)){let p=o[a];if(t in p)return{isValid:!0,message:"Valid path and method",operation:p[t],actualPath:e,apiType:r,baseUrl:n.baseUrl}}}return null}function at(t,e,r){let n=t.toLowerCase();if(r!==void 0){let o=Pe[r],a=ot(n,e,r,o);return a||{isValid:!1,message:`Path '${e}' not found in ${o.name} schema. Please check the path format or use freee_api_list_paths to see available endpoints.`}}for(let[o,a]of Object.entries(Pe)){let i=ot(n,e,o,a);if(i)return i}return{isValid:!1,message:`Path '${e}' not found in any freee API schema. Please check the path format or use freee_api_list_paths to see available endpoints.`}}function st(){let t=[];for(let[,e]of Object.entries(Pe)){let r=e.schema.paths,n=[];Object.entries(r).forEach(([o,a])=>{let i=Object.keys(a).filter(s=>["get","post","put","delete","patch"].includes(s)).map(s=>s.toUpperCase());i.length>0&&n.push(` ${i.join("|")} ${o}`)}),n.length>0&&t.push(`
|
|
57
57
|
## ${e.name} (${e.baseUrl})
|
|
@@ -69,22 +69,22 @@ MIME\u30BF\u30A4\u30D7: ${t.mimeType}
|
|
|
69
69
|
freee_api_get { "service": "accounting", "path": "/api/1/deals", "query": { "limit": 10 } }
|
|
70
70
|
freee_api_get { "service": "invoice", "path": "/invoices" }
|
|
71
71
|
freee_api_post { "service": "accounting", "path": "/api/1/deals", "body": { "issue_date": "2024-01-01", ... } }`)})}async function lt(){let t=await ge(),e=new Kt({name:t.server.name,version:t.server.version});tt(e),ct(e);let r=new Wt;await e.connect(r),console.error("Freee MCP Server running on stdio")}import se from"prompts";import ie from"node:fs/promises";import D from"node:path";import K from"node:os";var ae="freee-mcp",Xt={command:"npx",args:["@him0/freee-mcp"]};function ve(t){let e=K.platform();if(t==="claude-code")return D.join(K.homedir(),".claude.json");if(e==="darwin")return D.join(K.homedir(),"Library","Application Support","Claude","claude_desktop_config.json");if(e==="win32"){let r=process.env.APPDATA||D.join(K.homedir(),"AppData","Roaming");return D.join(r,"Claude","claude_desktop_config.json")}else return D.join(K.homedir(),".config","Claude","claude_desktop_config.json")}function pt(t){return t==="claude-code"?"Claude Code":"Claude Desktop"}async function be(t){try{let e=await ie.readFile(t,"utf-8");return JSON.parse(e)}catch{return null}}async function ut(t,e){let r=D.dirname(t);await ie.mkdir(r,{recursive:!0}),await ie.writeFile(t,JSON.stringify(e,null,2)+`
|
|
72
|
-
`,"utf-8")}async function ft(t){let e=ve(t);try{await ie.access(e);let r=await be(e);return{path:e,exists:!0,hasFreeeConfig:r?.mcpServers?.[ae]!==void 0}}catch{return{path:e,exists:!1,hasFreeeConfig:!1}}}async function mt(t){let e=ve(t),r=await be(e);r||(r={}),r.mcpServers||(r.mcpServers={}),r.mcpServers[ae]={...Xt},await ut(e,r)}async function dt(t){let e=ve(t),r=await be(e);r?.mcpServers?.[ae]&&(delete r.mcpServers[ae],Object.keys(r.mcpServers).length===0&&delete r.mcpServers,await ut(e,r))}w();w();import{z as
|
|
72
|
+
`,"utf-8")}async function ft(t){let e=ve(t);try{await ie.access(e);let r=await be(e);return{path:e,exists:!0,hasFreeeConfig:r?.mcpServers?.[ae]!==void 0}}catch{return{path:e,exists:!1,hasFreeeConfig:!1}}}async function mt(t){let e=ve(t),r=await be(e);r||(r={}),r.mcpServers||(r.mcpServers={}),r.mcpServers[ae]={...Xt},await ut(e,r)}async function dt(t){let e=ve(t),r=await be(e);r?.mcpServers?.[ae]&&(delete r.mcpServers[ae],Object.keys(r.mcpServers).length===0&&delete r.mcpServers,await ut(e,r))}w();w();import{z as v}from"zod";var Qt=v.object({id:v.number(),name:v.string().nullable(),name_kana:v.string().nullable().optional(),display_name:v.string(),role:v.string()}),gt=v.object({companies:v.array(Qt).optional()});async function ht(t){let e=await fetch(`${Q}/api/1/companies`,{headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"}});if(!e.ok){let o=await _(e),a=o.success?JSON.stringify(o.data):`(JSON parse failed: ${o.error})`;throw new Error(`\u4E8B\u696D\u6240\u4E00\u89A7\u306E\u53D6\u5F97\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${e.status} ${a}`)}let r=await e.json(),n=gt.safeParse(r);if(!n.success)throw new Error(`Invalid companies response format: ${n.error.message}`);return n.data.companies||[]}async function Ct(){let t=await Promise.resolve().then(()=>(F(),Ue)).then(i=>i.loadFullConfig()),e=!!(t.clientId&&t.clientSecret);e&&(console.log("\u65E2\u5B58\u306E\u8A2D\u5B9A\u304C\u898B\u3064\u304B\u308A\u307E\u3057\u305F\u3002"),console.log(` \u5909\u66F4\u3057\u306A\u3044\u9805\u76EE\u306F\u305D\u306E\u307E\u307E Enter \u3092\u62BC\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
73
73
|
`)),console.log(`\u30B9\u30C6\u30C3\u30D7 1/3: OAuth\u8A8D\u8A3C\u60C5\u5831\u306E\u5165\u529B
|
|
74
74
|
`);let r=await se([{type:"text",name:"clientId",message:"FREEE_CLIENT_ID:",initial:t.clientId||void 0,validate:i=>i.trim()?!0:"CLIENT_ID \u306F\u5FC5\u9808\u3067\u3059"},{type:"password",name:"clientSecret",message:e?"FREEE_CLIENT_SECRET (\u5909\u66F4\u3057\u306A\u3044\u5834\u5408\u306F\u7A7A\u6B04):":"FREEE_CLIENT_SECRET:",validate:i=>e&&!i.trim()||i.trim()?!0:"CLIENT_SECRET \u306F\u5FC5\u9808\u3067\u3059"},{type:"text",name:"callbackPort",message:"FREEE_CALLBACK_PORT:",initial:String(t.callbackPort||N)}]);if(!r.clientId)throw new Error("\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u304C\u30AD\u30E3\u30F3\u30BB\u30EB\u3055\u308C\u307E\u3057\u305F\u3002");let n=r.clientId.trim(),o=r.clientSecret.trim()||t.clientSecret,a=parseInt(r.callbackPort.trim(),10);if(!o)throw new Error("CLIENT_SECRET \u306F\u5FC5\u9808\u3067\u3059\u3002");return process.env.FREEE_CLIENT_ID=n,process.env.FREEE_CLIENT_SECRET=o,process.env.FREEE_CALLBACK_PORT=String(a),console.log(`
|
|
75
75
|
\u8A8D\u8A3C\u60C5\u5831\u3092\u53D7\u3051\u53D6\u308A\u307E\u3057\u305F\u3002
|
|
76
76
|
`),{clientId:n,clientSecret:o,callbackPort:a}}async function wt(t){console.log(`\u30B9\u30C6\u30C3\u30D7 3/3: \u30C7\u30D5\u30A9\u30EB\u30C8\u4E8B\u696D\u6240\u306E\u9078\u629E
|
|
77
77
|
`),console.log("\u4E8B\u696D\u6240\u4E00\u89A7\u3092\u53D6\u5F97\u4E2D...");let e=await ht(t);if(e.length===0)throw new Error("\u5229\u7528\u53EF\u80FD\u306A\u4E8B\u696D\u6240\u304C\u3042\u308A\u307E\u305B\u3093\u3002");let r=await se({type:"select",name:"companyId",message:"\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u4E8B\u696D\u6240\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044:",choices:e.map(o=>({title:`${o.display_name||o.name} (ID: ${o.id}) - ${o.role}`,value:o.id}))});if(!r.companyId)throw new Error("\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u304C\u30AD\u30E3\u30F3\u30BB\u30EB\u3055\u308C\u307E\u3057\u305F\u3002");let n=e.find(o=>o.id===r.companyId);if(!n)throw new Error(`\u9078\u629E\u3057\u305F\u4E8B\u696D\u6240\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ID ${r.companyId}`);return console.log(`
|
|
78
78
|
${n.display_name||n.name} \u3092\u9078\u629E\u3057\u307E\u3057\u305F\u3002
|
|
79
|
-
`),{selected:{id:n.id,name:n.name,displayName:n.display_name||n.name,role:n.role},all:e}}async function yt(t){let e=pt(t),r=await ft(t);if(r.hasFreeeConfig){let{action:n}=await se({type:"select",name:"action",message:`${e} \u306B freee-mcp \u304C\u8A2D\u5B9A\u6E08\u307F\u3067\u3059\u3002\u3069\u3046\u3057\u307E\u3059\u304B?`,choices:[{title:"\u305D\u306E\u307E\u307E (\u5909\u66F4\u306A\u3057)",value:"keep"},{title:"\u524A\u9664\u3059\u308B",value:"remove"}],initial:0});n==="remove"?(await dt(t),console.log(` \u2713 ${e} \u304B\u3089 freee-mcp \u3092\u524A\u9664\u3057\u307E\u3057\u305F\u3002`)):console.log(` - ${e} \u306E\u8A2D\u5B9A\u306F\u5909\u66F4\u3057\u307E\u305B\u3093\u3002`)}else{let{shouldAdd:n}=await se({type:"confirm",name:"shouldAdd",message:`${e} \u306B freee-mcp \u3092\u8FFD\u52A0\u3057\u307E\u3059\u304B?`,initial:!0});n?(await mt(t),console.log(` \u2713 ${e} \u306B freee-mcp \u3092\u8FFD\u52A0\u3057\u307E\u3057\u305F\u3002`),console.log(` \u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB: ${r.path}`)):console.log(` - ${e} \u3078\u306E\u8FFD\u52A0\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u307E\u3057\u305F\u3002`)}}async function _t(){console.log(`=== MCP\u8A2D\u5B9A ===
|
|
79
|
+
`),{selected:{id:n.id,name:n.name,displayName:n.display_name||n.name||"",role:n.role},all:e}}async function yt(t){let e=pt(t),r=await ft(t);if(r.hasFreeeConfig){let{action:n}=await se({type:"select",name:"action",message:`${e} \u306B freee-mcp \u304C\u8A2D\u5B9A\u6E08\u307F\u3067\u3059\u3002\u3069\u3046\u3057\u307E\u3059\u304B?`,choices:[{title:"\u305D\u306E\u307E\u307E (\u5909\u66F4\u306A\u3057)",value:"keep"},{title:"\u524A\u9664\u3059\u308B",value:"remove"}],initial:0});n==="remove"?(await dt(t),console.log(` \u2713 ${e} \u304B\u3089 freee-mcp \u3092\u524A\u9664\u3057\u307E\u3057\u305F\u3002`)):console.log(` - ${e} \u306E\u8A2D\u5B9A\u306F\u5909\u66F4\u3057\u307E\u305B\u3093\u3002`)}else{let{shouldAdd:n}=await se({type:"confirm",name:"shouldAdd",message:`${e} \u306B freee-mcp \u3092\u8FFD\u52A0\u3057\u307E\u3059\u304B?`,initial:!0});n?(await mt(t),console.log(` \u2713 ${e} \u306B freee-mcp \u3092\u8FFD\u52A0\u3057\u307E\u3057\u305F\u3002`),console.log(` \u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB: ${r.path}`)):console.log(` - ${e} \u3078\u306E\u8FFD\u52A0\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u307E\u3057\u305F\u3002`)}}async function _t(){console.log(`=== MCP\u8A2D\u5B9A ===
|
|
80
80
|
`),console.log(`Claude Code / Claude Desktop \u306B freee-mcp \u3092\u8A2D\u5B9A\u3067\u304D\u307E\u3059\u3002
|
|
81
81
|
`),await yt("claude-code"),console.log(""),await yt("claude-desktop"),console.log(""),console.log("\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5B8C\u4E86!"),console.log(`\u5909\u66F4\u3092\u53CD\u6620\u3059\u308B\u306B\u306F\u3001Claude Code / Claude Desktop \u3092\u518D\u8D77\u52D5\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
82
82
|
`)}import Ie from"node:crypto";import Yt from"open";w();async function kt(){console.log(`\u30B9\u30C6\u30C3\u30D7 2/3: OAuth\u8A8D\u8A3C
|
|
83
|
-
`),console.log("\u30D6\u30E9\u30A6\u30B6\u3067\u8A8D\u8A3C\u30DA\u30FC\u30B8\u3092\u958B\u304D\u307E\u3059..."),await Promise.resolve().then(()=>(
|
|
83
|
+
`),console.log("\u30D6\u30E9\u30A6\u30B6\u3067\u8A8D\u8A3C\u30DA\u30FC\u30B8\u3092\u958B\u304D\u307E\u3059..."),await Promise.resolve().then(()=>(S(),Le)).then(i=>i.loadConfig()),await Xe();let t=Ie.randomBytes(32).toString("base64url"),e=Ie.createHash("sha256").update(t).digest("base64url"),r=Ie.randomBytes(16).toString("base64url"),n=re(e,r,H());console.log(`
|
|
84
84
|
\u8A8D\u8A3CURL: ${n}
|
|
85
85
|
`),await Yt(n),console.log("\u30D6\u30E9\u30A6\u30B6\u3067\u8A8D\u8A3C\u3092\u5B8C\u4E86\u3057\u3066\u304F\u3060\u3055\u3044..."),console.log(`\u8A8D\u8A3C\u304C\u5B8C\u4E86\u3059\u308B\u3068\u81EA\u52D5\u7684\u306B\u6B21\u306E\u30B9\u30C6\u30C3\u30D7\u306B\u9032\u307F\u307E\u3059\u3002
|
|
86
|
-
`);let o=et(),a=new Promise((i,s)=>{let p=setTimeout(()=>{s(new Error("\u8A8D\u8A3C\u304C\u30BF\u30A4\u30E0\u30A2\u30A6\u30C8\u3057\u307E\u3057\u305F\uFF085\u5206\uFF09"))},R);o.registerCliAuthHandler(r,{resolve:u=>{clearTimeout(p),i(u)},reject:u=>{clearTimeout(p),s(u)},codeVerifier:t})});try{let i=await a;console.log("\u8A8D\u8A3C\u30B3\u30FC\u30C9\u3092\u53D7\u3051\u53D6\u308A\u307E\u3057\u305F\u3002"),console.log("\u30C8\u30FC\u30AF\u30F3\u3092\u53D6\u5F97\u4E2D...");let s=await ne(i,t,
|
|
87
|
-
`),{accessToken:s.access_token,refreshToken:s.refresh_token}}finally{o.removeCliAuthHandler(r)}}F();async function Et(t,e,r){let n={clientId:t.clientId,clientSecret:t.clientSecret,callbackPort:t.callbackPort,defaultCompanyId:String(e.id),currentCompanyId:String(e.id),companies:{}};r.forEach(o=>{n.companies[String(o.id)]={id:String(o.id),name:o.display_name||o.name,description:`Role: ${o.role}`,addedAt:Date.now(),lastUsed:o.id===e.id?Date.now():void 0}}),await M(n),console.log(`\u8A2D\u5B9A\u60C5\u5831\u3092\u4FDD\u5B58\u3057\u307E\u3057\u305F\u3002
|
|
86
|
+
`);let o=et(),a=new Promise((i,s)=>{let p=setTimeout(()=>{s(new Error("\u8A8D\u8A3C\u304C\u30BF\u30A4\u30E0\u30A2\u30A6\u30C8\u3057\u307E\u3057\u305F\uFF085\u5206\uFF09"))},R);o.registerCliAuthHandler(r,{resolve:u=>{clearTimeout(p),i(u)},reject:u=>{clearTimeout(p),s(u)},codeVerifier:t})});try{let i=await a;console.log("\u8A8D\u8A3C\u30B3\u30FC\u30C9\u3092\u53D7\u3051\u53D6\u308A\u307E\u3057\u305F\u3002"),console.log("\u30C8\u30FC\u30AF\u30F3\u3092\u53D6\u5F97\u4E2D...");let s=await ne(i,t,H());return console.log(`\u30C8\u30FC\u30AF\u30F3\u3092\u53D6\u5F97\u3057\u307E\u3057\u305F\u3002
|
|
87
|
+
`),{accessToken:s.access_token,refreshToken:s.refresh_token}}finally{o.removeCliAuthHandler(r)}}F();async function Et(t,e,r){let n={clientId:t.clientId,clientSecret:t.clientSecret,callbackPort:t.callbackPort,defaultCompanyId:String(e.id),currentCompanyId:String(e.id),companies:{}};r.forEach(o=>{n.companies[String(o.id)]={id:String(o.id),name:o.display_name||o.name||void 0,description:`Role: ${o.role}`,addedAt:Date.now(),lastUsed:o.id===e.id?Date.now():void 0}}),await M(n),console.log(`\u8A2D\u5B9A\u60C5\u5831\u3092\u4FDD\u5B58\u3057\u307E\u3057\u305F\u3002
|
|
88
88
|
`),console.log("\u8A8D\u8A3C\u60C5\u5831\u306F ~/.config/freee-mcp/config.json \u306B\u4FDD\u5B58\u3055\u308C\u307E\u3057\u305F\u3002"),console.log(`\u30C8\u30FC\u30AF\u30F3\u306F ~/.config/freee-mcp/tokens.json \u306B\u4FDD\u5B58\u3055\u308C\u307E\u3057\u305F\u3002
|
|
89
89
|
`)}async function xe(){console.log(`
|
|
90
90
|
=== freee-mcp Configuration Setup ===
|
|
@@ -3968,7 +3968,7 @@
|
|
|
3968
3968
|
},
|
|
3969
3969
|
"post": {
|
|
3970
3970
|
"summary": "経費申請の作成",
|
|
3971
|
-
"description": "\n<h2 id=\"_1\">概要</h2>\n\n<p>指定した事業所の経費申請を作成する</p>\n\n<p>経費精算APIの使い方については、<a href=\"https://developer.freee.co.jp/tips/accounting-expense-applications\" target=\"_blank\">freee会計経費精算APIの使い方</a>をご参照ください</p>\n\n<h2 id=\"_2\">注意点</h2>\n<ul>\n <li>\n 申請ステータス(下書き、申請中)の指定と変更、及び承認操作(承認する、却下する、申請者へ差し戻す、特権承認する、承認済み・却下済みを取り消す)は以下を参考にして行ってください。\n <ul>\n <li>\n 承認操作は申請ステータスが申請中、承認済み、却下のものだけが対象です。\n <ul>\n <li>\n 初回申請の場合\n <ul><li>申請の作成(POST)</li></ul>\n </li>\n <li>\n 作成済みの申請の申請ステータス変更・更新する場合\n <ul><li>申請の更新(PUT)</li></ul>\n </li>\n <li>\n 申請中、承認済み、却下の申請の承認操作を行う場合\n <ul><li>承認操作の実行(POST)</li></ul>\n </li>\n </ul>\n </li>\n <li>申請の削除(DELETE)が可能なのは申請ステータスが下書き、差戻しの場合のみです</li>\n </ul>\n </li>\n <li>\n 申請経路、承認者の指定として部門役職データ連携を活用し、以下のいずれかを利用している経費申請は本API経由で作成ができません。\n <ul>\n <li>役職指定(申請者の所属部門)</li>\n <li>役職指定(申請時に部門指定)</li>\n <li>部門および役職指定</li>\n </ul>\n </li>\n <li>申請時には、申請タイトル(title)に加え、項目行については金額(amount)、日付(transaction_date)、内容(description)が必須項目となります。申請時の業務効率化のため、API入力をお勧めします。</li>\n <li>本APIは駅すぱあと連携 (出発駅と到着駅から金額を自動入力する機能)には非対応です。駅すぱあと連携を使用した経費申請は作成できません。</li>\n <li>本APIは外貨には非対応です。外貨を利用する経費申請は作成できません。</li>\n <li>本APIはカスタム申請項目には非対応です。カスタム申請項目を使用した経費申請は作成できません。</li>\n <li>本APIは金額計算方法には非対応です。金額計算方法を設定した経費申請は作成できません。</li>\n</ul>",
|
|
3971
|
+
"description": "\n<h2 id=\"_1\">概要</h2>\n\n<p>指定した事業所の経費申請を作成する</p>\n\n<p>経費精算APIの使い方については、<a href=\"https://developer.freee.co.jp/tips/accounting-expense-applications\" target=\"_blank\">freee会計経費精算APIの使い方</a>をご参照ください</p>\n\n<h2 id=\"_2\">注意点</h2>\n<ul>\n <li>\n 申請ステータス(下書き、申請中)の指定と変更、及び承認操作(承認する、却下する、申請者へ差し戻す、特権承認する、承認済み・却下済みを取り消す)は以下を参考にして行ってください。\n <ul>\n <li>\n 承認操作は申請ステータスが申請中、承認済み、却下のものだけが対象です。\n <ul>\n <li>\n 初回申請の場合\n <ul><li>申請の作成(POST)</li></ul>\n </li>\n <li>\n 作成済みの申請の申請ステータス変更・更新する場合\n <ul><li>申請の更新(PUT)</li></ul>\n </li>\n <li>\n 申請中、承認済み、却下の申請の承認操作を行う場合\n <ul><li>承認操作の実行(POST)</li></ul>\n </li>\n </ul>\n </li>\n <li>申請の削除(DELETE)が可能なのは申請ステータスが下書き、差戻しの場合のみです</li>\n </ul>\n </li>\n <li>\n 申請経路、承認者の指定として部門役職データ連携を活用し、以下のいずれかを利用している経費申請は本API経由で作成ができません。\n <ul>\n <li>役職指定(申請者の所属部門)</li>\n <li>役職指定(申請時に部門指定)</li>\n <li>部門および役職指定</li>\n </ul>\n </li>\n <li>申請時には、申請タイトル(title)に加え、項目行については金額(amount)、日付(transaction_date)、内容(description)が必須項目となります。申請時の業務効率化のため、API入力をお勧めします。</li>\n <li>本APIは駅すぱあと連携 (出発駅と到着駅から金額を自動入力する機能)には非対応です。駅すぱあと連携を使用した経費申請は作成できません。</li>\n <li>本APIは外貨には非対応です。外貨を利用する経費申請は作成できません。</li>\n <li>本APIはカスタム申請項目には非対応です。カスタム申請項目を使用した経費申請は作成できません。</li>\n <li>本APIは金額計算方法には非対応です。金額計算方法を設定した経費申請は作成できません。</li>\n <li>本APIは条件分岐を含む申請経路には非対応です。条件分岐を含む申請経路を使用した経費申請は作成できません。</li>\n</ul>",
|
|
3972
3972
|
"hasJsonBody": true
|
|
3973
3973
|
}
|
|
3974
3974
|
},
|
|
@@ -3995,7 +3995,7 @@
|
|
|
3995
3995
|
},
|
|
3996
3996
|
"put": {
|
|
3997
3997
|
"summary": "経費申請の更新",
|
|
3998
|
-
"description": "\n<h2 id=\"\">概要</h2>\n\n<p>指定した事業所の経費申請を更新する</p>\n\n<p>経費精算APIの使い方については、<a href=\"https://developer.freee.co.jp/tips/accounting-expense-applications\" target=\"_blank\">freee会計経費精算APIの使い方</a>をご参照ください</p>\n\n<h2 id=\"_2\">注意点</h2>\n<ul>\n <li>本APIでは、経費申請を更新することができます。</li>\n <li>本APIでは、status(申請ステータス): draft:下書き, feedback:差戻しのみ更新可能です。</li>\n <li>\n 申請ステータス(下書き、申請中)の指定と変更、及び承認操作(承認する、却下する、申請者へ差し戻す、特権承認する、承認済み・却下済みを取り消す)は以下を参考にして行ってください。\n <ul>\n <li>\n 承認操作は申請ステータスが申請中、承認済み、却下のものだけが対象です。\n <ul>\n <li>\n 初回申請の場合\n <ul><li>申請の作成(POST)</li></ul>\n </li>\n <li>\n 作成済みの申請の申請ステータス変更・更新する場合\n <ul><li>申請の更新(PUT)</li></ul>\n </li>\n <li>\n 申請中、承認済み、却下の申請の承認操作を行う場合\n <ul><li>承認操作の実行(POST)</li></ul>\n </li>\n </ul>\n </li>\n <li>申請の削除(DELETE)が可能なのは申請ステータスが下書き、差戻しの場合のみです</li>\n </ul>\n </li>\n <li>\n 申請経路、承認者の指定として部門役職データ連携を活用し、以下のいずれかを利用している経費申請は本API経由で更新ができません。\n <ul>\n <li>役職指定(申請者の所属部門)</li>\n <li>役職指定(申請時に部門指定)</li>\n <li>部門および役職指定</li>\n </ul>\n </li>\n <li>申請時には、申請タイトル(title)に加え、項目行については金額(amount)、日付(transaction_date)、内容(description)が必須項目となります。申請時の業務効率化のため、API入力をお勧めします。</li>\n <li>本APIは駅すぱあと連携 (出発駅と到着駅から金額を自動入力する機能)には非対応です。駅すぱあと連携を使用した経費申請は更新できません。</li>\n <li>本APIは外貨には非対応です。外貨を利用する経費申請は更新できません。</li>\n <li>本APIはカスタム申請項目には非対応です。カスタム申請項目を使用した経費申請は更新できません。</li>\n <li>本APIは金額計算方法には非対応です。金額計算方法を設定した経費申請は更新できません。</li>\n</ul>",
|
|
3998
|
+
"description": "\n<h2 id=\"\">概要</h2>\n\n<p>指定した事業所の経費申請を更新する</p>\n\n<p>経費精算APIの使い方については、<a href=\"https://developer.freee.co.jp/tips/accounting-expense-applications\" target=\"_blank\">freee会計経費精算APIの使い方</a>をご参照ください</p>\n\n<h2 id=\"_2\">注意点</h2>\n<ul>\n <li>本APIでは、経費申請を更新することができます。</li>\n <li>本APIでは、status(申請ステータス): draft:下書き, feedback:差戻しのみ更新可能です。</li>\n <li>\n 申請ステータス(下書き、申請中)の指定と変更、及び承認操作(承認する、却下する、申請者へ差し戻す、特権承認する、承認済み・却下済みを取り消す)は以下を参考にして行ってください。\n <ul>\n <li>\n 承認操作は申請ステータスが申請中、承認済み、却下のものだけが対象です。\n <ul>\n <li>\n 初回申請の場合\n <ul><li>申請の作成(POST)</li></ul>\n </li>\n <li>\n 作成済みの申請の申請ステータス変更・更新する場合\n <ul><li>申請の更新(PUT)</li></ul>\n </li>\n <li>\n 申請中、承認済み、却下の申請の承認操作を行う場合\n <ul><li>承認操作の実行(POST)</li></ul>\n </li>\n </ul>\n </li>\n <li>申請の削除(DELETE)が可能なのは申請ステータスが下書き、差戻しの場合のみです</li>\n </ul>\n </li>\n <li>\n 申請経路、承認者の指定として部門役職データ連携を活用し、以下のいずれかを利用している経費申請は本API経由で更新ができません。\n <ul>\n <li>役職指定(申請者の所属部門)</li>\n <li>役職指定(申請時に部門指定)</li>\n <li>部門および役職指定</li>\n </ul>\n </li>\n <li>申請時には、申請タイトル(title)に加え、項目行については金額(amount)、日付(transaction_date)、内容(description)が必須項目となります。申請時の業務効率化のため、API入力をお勧めします。</li>\n <li>本APIは駅すぱあと連携 (出発駅と到着駅から金額を自動入力する機能)には非対応です。駅すぱあと連携を使用した経費申請は更新できません。</li>\n <li>本APIは外貨には非対応です。外貨を利用する経費申請は更新できません。</li>\n <li>本APIはカスタム申請項目には非対応です。カスタム申請項目を使用した経費申請は更新できません。</li>\n <li>本APIは金額計算方法には非対応です。金額計算方法を設定した経費申請は更新できません。</li>\n <li>本APIは条件分岐を含む申請経路には非対応です。条件分岐を含む申請経路を使用した経費申請は更新できません。</li>\n</ul>",
|
|
3999
3999
|
"parameters": [
|
|
4000
4000
|
{
|
|
4001
4001
|
"name": "id",
|
|
@@ -4031,7 +4031,7 @@
|
|
|
4031
4031
|
"/api/1/expense_applications/{id}/actions": {
|
|
4032
4032
|
"post": {
|
|
4033
4033
|
"summary": "経費申請の承認操作",
|
|
4034
|
-
"description": "\n<h2 id=\"_1\">概要</h2>\n\n<p>指定した事業所の経費申請の承認操作を行う</p>\n\n<p>経費精算APIの使い方については、<a href=\"https://developer.freee.co.jp/tips/accounting-expense-applications\" target=\"_blank\">freee会計経費精算APIの使い方</a>をご参照ください</p>\n\n<h2 id=\"_2\">注意点</h2>\n<ul>\n <li>本APIでは、経費申請の承認操作(承認する、却下する、申請者へ差し戻す、特権承認する、承認済み・却下済みを取り消す)を行うことができます。</li>\n <li>\n 申請ステータス(下書き、申請中)の指定と変更、及び承認操作(承認する、却下する、申請者へ差し戻す、特権承認する、承認済み・却下済みを取り消す)は以下を参考にして行ってください。\n <ul>\n <li>\n 承認操作は申請ステータスが申請中、承認済み、却下のものだけが対象です。\n <ul>\n <li>\n 初回申請の場合\n <ul><li>申請の作成(POST)</li></ul>\n </li>\n <li>\n 作成済みの申請の申請ステータス変更・更新する場合\n <ul><li>申請の更新(PUT)</li></ul>\n </li>\n <li>\n 申請中、承認済み、却下の申請の承認操作を行う場合\n <ul><li>承認操作の実行(POST)</li></ul>\n </li>\n </ul>\n </li>\n <li>申請の削除(DELETE)が可能なのは申請ステータスが下書き、差戻しの場合のみです</li>\n </ul>\n </li>\n <li>\n 申請経路、承認者の指定として部門役職データ連携を活用し、以下のいずれかを利用している経費申請はAPI経由で承認ステータスの変更ができません。\n <ul>\n <li>役職指定(申請者の所属部門)</li>\n <li>役職指定(申請時に部門指定)</li>\n <li>部門および役職指定</li>\n </ul>\n </li>\n <li>本APIは駅すぱあと連携 (出発駅と到着駅から金額を自動入力する機能)には非対応です。駅すぱあと連携を使用した経費申請は承認操作できません。</li>\n <li>本APIはカスタム申請項目には非対応です。カスタム申請項目を使用した経費申請は承認操作できません。</li>\n <li>本APIは金額計算方法には非対応です。金額計算方法を設定した経費申請は承認操作できません。</li>\n</ul>",
|
|
4034
|
+
"description": "\n<h2 id=\"_1\">概要</h2>\n\n<p>指定した事業所の経費申請の承認操作を行う</p>\n\n<p>経費精算APIの使い方については、<a href=\"https://developer.freee.co.jp/tips/accounting-expense-applications\" target=\"_blank\">freee会計経費精算APIの使い方</a>をご参照ください</p>\n\n<h2 id=\"_2\">注意点</h2>\n<ul>\n <li>本APIでは、経費申請の承認操作(承認する、却下する、申請者へ差し戻す、特権承認する、承認済み・却下済みを取り消す)を行うことができます。</li>\n <li>\n 申請ステータス(下書き、申請中)の指定と変更、及び承認操作(承認する、却下する、申請者へ差し戻す、特権承認する、承認済み・却下済みを取り消す)は以下を参考にして行ってください。\n <ul>\n <li>\n 承認操作は申請ステータスが申請中、承認済み、却下のものだけが対象です。\n <ul>\n <li>\n 初回申請の場合\n <ul><li>申請の作成(POST)</li></ul>\n </li>\n <li>\n 作成済みの申請の申請ステータス変更・更新する場合\n <ul><li>申請の更新(PUT)</li></ul>\n </li>\n <li>\n 申請中、承認済み、却下の申請の承認操作を行う場合\n <ul><li>承認操作の実行(POST)</li></ul>\n </li>\n </ul>\n </li>\n <li>申請の削除(DELETE)が可能なのは申請ステータスが下書き、差戻しの場合のみです</li>\n </ul>\n </li>\n <li>\n 申請経路、承認者の指定として部門役職データ連携を活用し、以下のいずれかを利用している経費申請はAPI経由で承認ステータスの変更ができません。\n <ul>\n <li>役職指定(申請者の所属部門)</li>\n <li>役職指定(申請時に部門指定)</li>\n <li>部門および役職指定</li>\n </ul>\n </li>\n <li>本APIは駅すぱあと連携 (出発駅と到着駅から金額を自動入力する機能)には非対応です。駅すぱあと連携を使用した経費申請は承認操作できません。</li>\n <li>本APIはカスタム申請項目には非対応です。カスタム申請項目を使用した経費申請は承認操作できません。</li>\n <li>本APIは金額計算方法には非対応です。金額計算方法を設定した経費申請は承認操作できません。</li>\n <li>本APIは条件分岐を含む申請経路には非対応です。条件分岐を含む申請経路を使用した経費申請は承認操作できません。</li>\n</ul>",
|
|
4035
4035
|
"parameters": [
|
|
4036
4036
|
{
|
|
4037
4037
|
"name": "id",
|
|
@@ -23,6 +23,19 @@
|
|
|
23
23
|
"description": "案件ID"
|
|
24
24
|
}
|
|
25
25
|
]
|
|
26
|
+
},
|
|
27
|
+
"patch": {
|
|
28
|
+
"summary": "案件更新",
|
|
29
|
+
"description": "<h2 id=\"\">概要</h2>\n<p>指定されたIDの案件を更新します。\n案件の基本情報、受注見込、完了予定日などを部分的に更新できます。\n送信したフィールドのみが更新され、送信しなかったフィールドは変更されません。\n</p>\n<h2 id=\"_2\">定義</h2>\n<h3>更新可能項目</h3>\n<ul>\n<li><p>name : 案件名称</p></li>\n<li><p>code : 案件コード</p></li>\n<li><p>business_date : 案件登録日</p></li>\n<li><p>charge_employee_id : 社内担当者の従業員ID</p></li>\n<li><p>customer_id : 顧客の取引先ID</p></li>\n<li><p>prospect_sales_order : 受注見込</p></li>\n<li><p>sales_progression_id : 受注確度ID</p></li>\n<li><p>scheduled_completion_date : 完了予定日</p></li>\n<li><p>completion_date : 完了日</p></li>\n<li><p>business_phase_id : 案件フェーズID</p></li>\n<li><p>reporting_section_id : 担当部門ID</p></li>\n<li><p>internal_memo : 社内メモ</p></li>\n<li><p>custom_fields : カスタム項目(指定した場合、既存のカスタム項目は全て削除され、新しいカスタム項目に置き換えられます)</p></li>\n</ul>\n<p>※全ての項目は任意です。更新したい項目のみを送信してください。</p>\n<p>※custom_fieldsを指定した場合、リクエストに含まれないカスタム項目はnullとして扱われます。必須のカスタム項目が指定されていない場合はバリデーションエラーになります。</p>",
|
|
30
|
+
"parameters": [
|
|
31
|
+
{
|
|
32
|
+
"name": "id",
|
|
33
|
+
"in": "path",
|
|
34
|
+
"type": "string",
|
|
35
|
+
"required": true,
|
|
36
|
+
"description": "案件ID"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
26
39
|
}
|
|
27
40
|
},
|
|
28
41
|
"/master/business_phases": {
|
|
@@ -118,6 +131,19 @@
|
|
|
118
131
|
"description": "受注ID"
|
|
119
132
|
}
|
|
120
133
|
]
|
|
134
|
+
},
|
|
135
|
+
"patch": {
|
|
136
|
+
"summary": "受注更新",
|
|
137
|
+
"description": "<h2 id=\"\">概要</h2>\n<p>指定されたIDの受注を更新します。\n受注の基本情報、納品・請求・入金情報などを部分的に更新できます。\n送信したフィールドのみが更新され、送信しなかったフィールドは変更されません。\n</p>\n<h2 id=\"_2\">定義</h2>\n<h3>更新可能項目</h3>\n<ul>\n<li>\n<p>sales_order_subject : 受注タイトル</p>\n</li>\n<li>\n<p>sales_order_date : 受注日</p>\n</li>\n<li>\n<p>customer_order_no : 顧客注文No.</p>\n</li>\n<li>\n<p>customer_id : 顧客の取引先ID</p>\n</li>\n<li>\n<p>sales_on : 売上予定日</p>\n</li>\n<li>\n<p>deliveries_on : 納品予定日</p>\n</li>\n<li>\n<p>accepts_on : 検収予定日</p>\n</li>\n<li>\n<p>delivery_template_id : 納品書テンプレートID</p>\n</li>\n<li>\n<p>delivery_subject : 納品書件名</p>\n</li>\n<li>\n<p>delivery_note : 納品書の備考欄に掲載する内容</p>\n</li>\n<li>\n<p>billing_creating_method_type : 請求の管理</p>\n</li>\n<li>\n<p>bills_on : 請求予定日</p>\n</li>\n<li>\n<p>billing_partner_id : 請求先の取引先ID</p>\n</li>\n<li>\n<p>invoice_template_id : 請求書テンプレートID</p>\n</li>\n<li>\n<p>invoice_subject : 請求書件名</p>\n</li>\n<li>\n<p>invoice_note : 請求書の備考欄に掲載する内容</p>\n</li>\n<li>\n<p>collects_on : 入金予定日</p>\n</li>\n<li>\n<p>collecting_partner_id : 入金元の取引先ID</p>\n</li>\n<li>\n<p>collection_method_type : 入金方法</p>\n</li>\n<li>\n<p>charge_employee_id : 社内担当者の従業員ID</p>\n</li>\n<li>\n<p>reporting_section_id : 担当部門ID</p>\n</li>\n<li>\n<p>internal_memo : 社内メモ</p>\n</li>\n<li>\n<p>business_id : 案件ID</p>\n</li>\n<li>\n<p>lines : 明細リスト ※指定した場合、既存の明細は全て削除され、新しい明細に置き換えられます。</p>\n</li>\n</ul>\n<p>※全ての項目は任意です。更新したい項目のみを送信してください。</p>",
|
|
138
|
+
"parameters": [
|
|
139
|
+
{
|
|
140
|
+
"name": "id",
|
|
141
|
+
"in": "path",
|
|
142
|
+
"type": "string",
|
|
143
|
+
"required": true,
|
|
144
|
+
"description": "受注ID"
|
|
145
|
+
}
|
|
146
|
+
]
|
|
121
147
|
}
|
|
122
148
|
}
|
|
123
149
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@him0/freee-mcp",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.7",
|
|
5
5
|
"bin": {
|
|
6
6
|
"freee-mcp": "./bin/cli.js"
|
|
7
7
|
},
|
|
@@ -9,10 +9,22 @@
|
|
|
9
9
|
"license": "ISC",
|
|
10
10
|
"description": "Model Context Protocol (MCP) server for freee API integration",
|
|
11
11
|
"devDependencies": {
|
|
12
|
+
"@changesets/cli": "^2.29.8",
|
|
12
13
|
"@modelcontextprotocol/inspector": "^0.17.2",
|
|
14
|
+
"@types/node": "^24.10.1",
|
|
15
|
+
"@types/prompts": "^2.4.9",
|
|
16
|
+
"@typescript-eslint/eslint-plugin": "^8.48.1",
|
|
17
|
+
"@typescript-eslint/parser": "^8.48.1",
|
|
13
18
|
"@vitest/coverage-v8": "^3.2.4",
|
|
14
19
|
"@vitest/ui": "^3.2.4",
|
|
20
|
+
"esbuild": "^0.25.12",
|
|
21
|
+
"eslint": "^9.39.1",
|
|
22
|
+
"eslint-config-prettier": "^10.1.8",
|
|
15
23
|
"knip": "^5.78.0",
|
|
24
|
+
"prettier": "^3.7.4",
|
|
25
|
+
"tsx": "^4.21.0",
|
|
26
|
+
"typescript": "^5.9.3",
|
|
27
|
+
"vitest": "^3.2.4",
|
|
16
28
|
"yaml": "^2.8.2"
|
|
17
29
|
},
|
|
18
30
|
"dependencies": {
|
|
@@ -31,8 +43,7 @@
|
|
|
31
43
|
],
|
|
32
44
|
"repository": {
|
|
33
45
|
"type": "git",
|
|
34
|
-
"url": "https://github.com/him0/freee-mcp.git"
|
|
35
|
-
"directory": "packages/freee-mcp"
|
|
46
|
+
"url": "https://github.com/him0/freee-mcp.git"
|
|
36
47
|
},
|
|
37
48
|
"files": [
|
|
38
49
|
"dist/",
|
|
@@ -40,6 +51,7 @@
|
|
|
40
51
|
"README.md"
|
|
41
52
|
],
|
|
42
53
|
"scripts": {
|
|
54
|
+
"clean": "rm -rf dist",
|
|
43
55
|
"build": "tsx build.ts",
|
|
44
56
|
"start": "tsx src/index.ts",
|
|
45
57
|
"dev": "tsx watch src/index.ts",
|
|
@@ -54,6 +66,9 @@
|
|
|
54
66
|
"test:coverage": "vitest run --coverage",
|
|
55
67
|
"test:ui": "vitest --ui",
|
|
56
68
|
"generate:references": "tsx scripts/generate-references.ts",
|
|
57
|
-
"fetch:schemas": "tsx scripts/fetch-schemas.ts"
|
|
69
|
+
"fetch:schemas": "tsx scripts/fetch-schemas.ts",
|
|
70
|
+
"changeset": "changeset",
|
|
71
|
+
"version": "changeset version",
|
|
72
|
+
"release": "pnpm build && changeset publish"
|
|
58
73
|
}
|
|
59
74
|
}
|