Skip to main content
Glama
kentaroajisaka

mf-full-mcp

README.md
# mf-full-mcp

マネーフォワード クラウド会計の**非公式**MCPサーバーです。公式beta MCPと同名のツール(`mfc_ca_*`)を提供しつつ、次の3点を足しています。

- **証憑の添付** — レシートや請求書をアップロードして仕訳に紐づける。**公式のリモートMCPには2026年8月時点でこの機能がありません**
- **複数事業者への同時接続** — 複数法人分のトークンを保持し、再認証なしで、ウィンドウ/セッションごとに別々の事業者へ接続できる
- **仕訳の完全削除** — 公式MCPは登録・更新のみで、削除に対応していません

会計事務所のように、日常的に何十社もの帳簿を行き来する使い方を想定しています。

> **非公式プロジェクトです。** 株式会社マネーフォワードとは一切関係がありません。同社のサポート対象外です。

---

## なぜ作ったか

MFのアクセストークンは、**認可画面で選んだ1事業者に紐づきます**(`/api/v3/offices` が配列ではなく単一オブジェクトを返すのがその証拠です)。複数法人を扱うには、その数だけトークンを持つ必要があります。

公式のリモートMCPには2つのエンドポイントがあり、それぞれ事情が異なります。

| | 公式 beta | 公式 alpha | 本サーバー |
|---|---|---|---|
| 複数法人の同時保持 | 不可(切替時に再認証) | 可能 | 可能 |
| トークンの寿命 | 長時間 | 約1時間 | 自動リフレッシュ(実測で1か月以上放置可) |
| 認証の操作 | 自動 | 認証コードを手動でコピペ | 自動 |
| 証憑の添付 | 不可 | 不可 | 可能 |

beta版は認証が楽な代わりに1事業者しか保持できず、alpha版は複数保持できる代わりにトークンが約1時間で切れて手動での再認証が必要になります。

**「複数法人を、再認証に煩わされず、ウィンドウごとに別々に触る」という状態は、公式のどちらでも成立しません。**本サーバーはOAuthを自前で持つことでここを埋めています。

---

## 設計上のポイント

### 1. OAuthを自前で持つ(動的クライアント登録)

RFC 7591の動的クライアント登録で、認証のたびに公開クライアントを登録します(`token_endpoint_auth_method: "none"` + PKCE)。

そのため **事前のアプリ登録が不要**で、**client_secretがそもそも存在しません**。このリポジトリにも、利用者の環境にも、秘密鍵の類は一切置かれません。

### 2. 事業者ごとにトークンをラベル付きで保存

`~/.mf-full-mcp/tokens.json`(ディレクトリ`0700`/ファイル`0600`)に、ラベルをキーとして複数事業者分のトークンを保持します。トークン1本=1社なら、必要な数だけ持てばよい、という考え方です。

期限切れは自動でリフレッシュされます(期限60秒前の先回り+401時の1回リトライ)。実測ではリフレッシュトークンは1か月以上有効で、一度認証すればしばらく放置できます。

### 3. アクティブな事業者は「セッションごと」に独立

**このプロジェクトの核心です。**

`use_office` で切り替わるのはプロセス内のメモリだけで、共有ファイルの `active` は書き換えません。共有ファイルの値は、まだ一度も切り替えていない新規プロセスの初期値としてのみ読まれます。

結果として、ウィンドウAで甲社、ウィンドウBで乙社を**並行して**操作できます。

もしここで共有ファイルを書き換えていたら、一方が事業者を切り替えた瞬間に、もう一方が**別法人のデータを取得してしまいます**。帳簿を扱う以上、これは起きてはならない事故です。「複数社同時接続」の実体は、トークンを複数持てることよりも、**アクティブ状態を共有しないこと**のほうにあります。

起動時に固定したい場合は環境変数 `MF_FULL_OFFICE` を使ってください。

### 4. コールバックポートは自動割当

`server.listen(0)` でOSに空きポートを割り当てさせ、**確定したポートでredirect_uriを組み立ててから**クライアント登録します(RFC 8252のloopback redirect)。

固定ポートだと、2つ目のウィンドウでの認証が `EADDRINUSE` で失敗します。「listen → ポート確定 → register → authorize」という順序が、3の設計を実際に機能させるために必要でした。

後方互換のため、`MF_FULL_CALLBACK_PORT` を明示したときだけ固定ポートを使います。

### 5. ブラウザがコールバックに戻れない環境でも認証できる

loopback redirectは「ブラウザと本サーバーが同じ機械にいる」ことが前提です。スマホのブラウザで許可した場合や、本サーバーをクラウドのコンテナで動かしてSlack越しに操作する場合、ブラウザは `http://localhost:PORT/callback` に到達できず、接続エラーの画面で止まります。

これは異常ではありません。**その画面のアドレスバーにはすでに `code` と `state` が入っています。** そのURLをそのまま `auth_paste_redirect` に渡すと、本サーバーが同じ `redirect_uri` でトークン交換を行い、認証が完了します(MFの認可サーバーはパラメータの一致だけを見るため、実際にそのURLへ到達できる必要はありません)。

さらに、接続元のクライアントがMCPの elicitation(form)に対応していれば、`authenticate` は認可URLと「エラー画面が出たらそのURLを貼ってください」という案内をクライアント経由で直接ユーザーに提示し、貼り付けを待って完了まで進みます。対応していない、または応答が返らない場合でも、`authUrl` と案内文は必ず返り値に含めます(呼び出し側のAIがそれをそのままユーザーに伝えてください)。

---

## 証憑の添付

公式のリモートMCPが提供するツールは、2026年8月時点で仕訳・帳票・明細・マスタの操作までで、**証憑(レシート・請求書)を扱うツールは含まれていません**。証憑を仕訳に紐づけたい場合、ブラウザで手作業するしかありませんでした。

本サーバーはこれをツールとして提供します。ローカルファイルの絶対パスを渡すだけで、base64化からアップロード、仕訳への添付までを行います。

```
mfc_ca_postVouchers(journal_id: "<仕訳ID>", file_paths: ["/path/to/receipt.pdf"])
```

紐付けを解除したい場合は `mfc_ca_deleteVouchers` を使います。

「仕訳を起こして、対応するレシートを添付する」までを1つの流れで完結できることが、このサーバーを作った動機のひとつです。

> **注意** — `journal_id` を省略するとどの仕訳にも紐づかない孤立証憑になり、**後から仕訳に紐づける手段がありません**。原則として必ず指定してください。

---

## 必要環境

- Node.js 20 以上
- マネーフォワード クラウド会計のアカウント

## インストール

```bash
git clone https://github.com/kentaroajisaka/mf-full-mcp.git
cd mf-full-mcp
npm install
npm run build
```

MCPクライアントに登録します(Claude Codeの場合、`~/.claude.json`)。

```json
{
  "mcpServers": {
    "mf-full": {
      "type": "stdio",
      "command": "node",
      "args": ["/absolute/path/to/mf-full-mcp/dist/index.js"],
      "env": {}
    }
  }
}
```

## 使い方

### 1社目を認証する

```
authenticate(label: "company-a")
```

返ってきた `authUrl` をブラウザで開き、事業者を選んで許可します。完了したら `auth_status` で確認します。

### ブラウザが別の端末にあるとき(スマホ・クラウド実行・Slack越し)

許可のあとに「接続できません」というエラー画面になったら、その画面のアドレスバーのURLをコピーして渡します。

```
auth_paste_redirect(callback_url: "http://localhost:60855/callback?state=...&code=...")
```

URL全体でも、`code=...&state=...` の部分だけでも受け付けます。`status: done` が返れば完了です。

### 2社目以降を追加する

同じ手順を別のラベルで繰り返すだけです。

```
authenticate(label: "company-b")
```

### 接続先を切り替える

```
list_offices          保存済み事業者の一覧
use_office            このセッションの接続先を切り替える
mfc_ca_currentOffice  いまどの事業者に繋がっているか確認する
```

`use_office` は他のセッションに影響しません。データを取得する前に `mfc_ca_currentOffice` で対象法人を確認する運用をおすすめします。

## ツール一覧

### 認証・事業者管理

| ツール | 説明 |
|---|---|
| `authenticate` | OAuth認証を開始する(`extra_scopes` で給与等の追加スコープも要求可)。クライアントが elicitation 対応なら案内を直接提示して完了まで待つ |
| `auth_paste_redirect` | ブラウザが最終的に表示したURL(接続エラー画面でよい)を渡して認証を完了する。`code=...&state=...` だけでも可 |
| `auth_status` | 進行中の認証フローの状態を確認する |
| `list_offices` | 保存済み事業者と現在の接続先を表示する |
| `use_office` | このセッションの接続先を切り替える |
| `remove_office` | 保存済みトークンを削除する |
| `mf_full_info` | 要求スコープ・コールバックポート等の設定を表示する |

### 公式MCP互換(`mfc_ca_*`)

事業者・会計期間、勘定科目、補助科目、部門、税区分、取引先、連携サービス、仕訳の取得/登録/更新、試算表(BS・PL)、推移表(BS・PL)、明細の取得/作成/仕訳化。

### 公式MCPが提供していないもの

| ツール | 説明 |
|---|---|
| `mfc_ca_postVouchers` | 証憑をアップロードして仕訳に添付する(ローカルファイルのパスを渡せば自動でbase64化) |
| `mfc_ca_deleteVouchers` | 仕訳と証憑の紐付けを解除する(証憑自体は孤立して残る) |
| `mfc_ca_deleteJournals` | 仕訳を完全削除する |

## エージェントから複数事業者を扱う(1事業者=1プロセス)

人が対話で使うときは、1プロセスの中で `use_office` を切り替えれば足ります。
しかし **無人のエージェント(cron・複数の担当者と同時にやりとりする Slack ボット等)** が複数事業者に書き込む場合、
1プロセスを共有すると次の事故が起きえます。

- アクティブ事業者はプロセス内のメモリに1つだけ。A社の承認を処理している最中に B社向けの `use_office` が走ると、A社の仕訳が B社に入る
- `~/.mf-full-mcp/tokens.json` は保存のたびにファイル全体を書き直す。複数プロセスが同じファイルを持つと、片方のリフレッシュ結果がもう片方に上書きされて消える
- 認証の途中状態(PKCE の verifier と state)はプロセスのメモリにある。`authenticate` と `auth_paste_redirect` は同じプロセスで呼ばないと完了しない

**推奨: 事業者ごとに別プロセスを立て、`HOME` と `MF_FULL_OFFICE` で固定する。**

```yaml
# 例(Hermes Agent の mcp_servers)。Claude Code の mcpServers でも同じ考え方
mf-office-a:
  command: node
  args: ["/opt/data/mf-full-mcp/dist/index.js"]
  env:
    HOME: /opt/data/mf/office-a          # トークン置き場をこのプロセス専用にする(os.homedir() が HOME を見る)
    MF_FULL_OFFICE: office-a        # 起動時の接続先を固定
mf-office-b:
  command: node
  args: ["/opt/data/mf-full-mcp/dist/index.js"]
  env:
    HOME: /opt/data/mf/office-b
    MF_FULL_OFFICE: office-b
```

こうすると各プロセスの tokens.json には自社のトークンしか無いので、誤って `use_office("office-b")` を呼んでも
「保存済みトークンがありません」で落ちます。固定が仕組みになります。

運用の決まり(エージェント側の指示に入れるもの):

1. 各事業者の処理は、その事業者専用のサーバー以外で行わない。`use_office` は使わない
2. 書き込みの直前に `mfc_ca_currentOffice` を呼び、事業者名が処理対象と一致することを確認する。違えば書かない
3. 1件ずつ完結させる: 登録 → 返ってきた `journal_id` に `postVouchers` で添付 → 次へ。承認が続けて来ても混ぜない
4. `postTransactionJournalize` の前に、その明細の `journalizing_status` がまだ `none` か確認する(二重登録の防止)
5. API 経由で登録した仕訳は `entered_by` が `JOURNAL_TYPE_EXTERNAL` になる(画面からの入力は `JOURNAL_TYPE_NORMAL`)。
   `getJournals` の結果を `entered_by` で絞れば、エージェントが登録した仕訳だけを後から抽出できる。
   ただし「どのエージェント/どの承認で」までは分からない。それが必要なら `memo` や `tags` に残すか、承認の会話履歴(Slack 等)で追う
6. `POST /journals` で登録した仕訳は、`memo` を渡さなくても **MF が伝票メモに OAuth クライアント名を書き込む**(本サーバーなら `mf-full-mcp`、
   `MF_FULL_CLIENT_NAME` で変えられる)。明細の仕訳化(`/transactions/{id}/journalize`)では入らない。消したいときは `putJournals` で全置換する

認証は事業者ごとに1回。ブラウザが localhost に戻れない環境では、認可後のエラー画面の URL を `auth_paste_redirect` に渡します
(前述)。**必ずその事業者のサーバーで** `authenticate` から `auth_paste_redirect` まで行ってください。

## 環境変数

| 変数 | 既定値 | 説明 |
|---|---|---|
| `MF_FULL_OFFICE` | なし | 起動時にセッションの接続先事業者を固定する |
| `MF_FULL_CALLBACK_PORT` | 自動割当 | OAuthコールバックのポートを固定する |
| `MF_FULL_SCOPES` | 会計16スコープ | 要求スコープを上書きする |
| `MF_FULL_CLIENT_NAME` | `mf-full-mcp` | 動的クライアント登録時のクライアント名 |

---

## 既知の注意点

MFのAPIには、いくつか踏みやすい落とし穴があります。

- **IDのエンコード** — MFのIDはBase64由来で `%2F` 等を含みます。URLパスに埋める際に再エンコードしないとパスが壊れて403になります。本サーバーは対策済みですが、返ってきたIDは**加工せずそのまま**渡してください。
- **`putJournals` は全置換** — 部分更新はできません。更新前に元の仕訳を控えてください。
- **ラベルは保存場所であって、事業者の予約ではない** — `authenticate` に既存のラベルを渡すと、ブラウザ側でどの事業者を選んでも**そのラベルのトークンが上書き**されます。事業者ごとに一意なラベルを付け、使い回さないでください。
- **書き込み系ツールの扱い** — 仕訳の登録・更新、証憑の添付・解除、明細の仕訳化は帳簿を書き換えます。AIエージェントから使う場合は、実行前に必ず人間の承認を挟む運用にしてください。

## セキュリティ

### トークンをどこに置くか

公式のリモートMCPは、認証がサーバー側で完結します。認可とトークン交換がMCPツールとして提供されていて、利用者が `access_token` を直接扱う場面がありません。つまり **MFのトークンが利用者の端末に置かれることはありません**。

本サーバーは逆に、**トークンを自分の端末に持ちます**。`~/.mf-full-mcp/tokens.json` に平文で保存されます(ディレクトリ `0700` / ファイル `0600`)。

これはトレードオフです。複数法人ぶんを再認証なしで保持できるのも、期限切れを自動更新してしばらく放置できるのも、リフレッシュトークンが手元にあるからこそ成立しています。裏を返せば、**このファイルが漏れれば、保存したすべての事業者の帳簿にアクセスされます。**

そのうえで、次の運用を前提としています。

- ディスクが暗号化された個人端末で使うこと。共用端末では使わないこと
- 不要になった事業者は `remove_office` で削除すること
- OSのキーチェーンへの保存は今後の課題です

### client_secret は存在しません

動的クライアント登録により、秘密鍵を保管する必要がない設計です。このリポジトリにも、利用者の環境にも、client_secret は置かれません。

## 免責

本ソフトウェアは非公式であり、株式会社マネーフォワードとは関係がありません。

本サーバーが要求するスコープの一部は、**MF側の仕様変更によって予告なく利用できなくなる可能性があります。**その場合、証憑まわりなど一部の機能が動作しなくなります。

会計帳簿という性質上、利用にあたっては次の点にご留意ください。

- 書き込み操作の結果について、作者はいかなる責任も負いません
- 本番の帳簿で使う前に、テスト事業者で動作を確認してください
- 利用にあたってはマネーフォワードの利用規約を各自でご確認ください

MITライセンスで提供されます。詳細は [LICENSE](LICENSE) をご覧ください。

## 関連

- [unofficial-official-mf-mcp-skill](https://github.com/kentaroajisaka/unofficial-official-mf-mcp-skill) — 公式MCPの使い方・APIのクセをAIに教える非公式スキル
- [mfc-journal-analyst-pro](https://github.com/kentaroajisaka/mfc-journal-analyst-pro) — 仕訳分析・引き継ぎ資料生成の非公式スキル

TDQS

B3/5.0

Scored across 29 tools

Disambiguation4/5

Most tools have clear resource/action separation: journals, transactions, reports, master data, auth, and office management each have distinct targets. A few pairs like currentOffice vs list_offices or postJournals vs postTransactionJournalize have some boundary overlap, but the descriptions are precise enough to resolve most ambiguity.

Naming Consistency3/5

The dominant mfc_ca_get/post/put/delete + Resource pattern is readable and consistent within the core accounting tools. However, the server mixes styles: verbless mfc_ca_currentOffice, un-prefixed snake_case names like remove_office and list_offices, and bare verbs like authenticate.

Tool Count2/5

29 tools is a heavy surface and exceeds the 25+ threshold. Although the tools are grouped into clear subdomains such as journals, reports, master data, and auth, the high number of parallel getters and report variants creates meaningful selection overhead for agents.

Completeness3/5

Core workflows are well covered: authentication, office switching, journal CRUD, report retrieval, and master data reads are all present. Obvious gaps remain, however: trade partners have no update/delete, transactions have no update/delete, and vouchers lack list/get or an attach-orphan recovery path.

Maintenance

ActivityMaintained
ResponsivenessNo issues