Skip to main content
Glama
nht-x
by nht-x

knowledge-mcp

社内ナレッジをGitHubの保管庫に貯め、目次から辿って読むためのMCPサーバー。

ChatGPT や Claude などのMCP対応クライアントから、社内の記録を検索・保存できます。

できること

ツール

種別

内容

get_knowledge_root

読み取り

目次の入口を読む

get_knowledge_index

読み取り

分類ごとの一覧を読む

get_knowledge_record

読み取り

記録を1件読む

save_knowledge

書き込み

記録を1件保存する

この4つだけです。 ファイル削除・Issue作成・リポジトリ操作などの権限は持ちません。

Related MCP server: GitHub MCP Server

探し方の設計

いきなり全文検索せず、目次から辿ります

get_knowledge_root        目次の入口(ROOT.md)
   ↓ 分類を選ぶ
get_knowledge_index       分類ごとの一覧(knowledge/{分類}/INDEX.md)
   ↓ 記録を選ぶ
get_knowledge_record      記録の本文

目次は記録の位置を path: knowledge/policy/abc.md の形で載せています。 get_knowledge_record にはこのうち最後の「/」より後ろだけを渡してください (分類は category へ分けて渡します)。

目次の表記    path: `knowledge/policy/20260805T060019Z-abc.md`
                     └─ category ─┘ └────── filename ──────┘

ディレクトリを含む文字列は拒否されます。.github/workflows/ へ書き込めると 任意のコードが実行できてしまうため、パス区切りは受け付けない設計です。 取り違えたときは、エラーメッセージが渡すべき形を示します。

分類は5つです。

分類

ディレクトリ

内容

Policy

policy

社内ルール・方針

How-to

how-to

作業手順・やり方

Q&A

qa

よくある質問と答え

Reference

reference

参照表・データ

Decision

decision

決定事項とその理由

保存のしくみ

ファイルを直接コミットせず、保管庫のワークフローを起動します

save_knowledge
  → POST /repos/{owner}/{repo}/dispatches
     (event_type: save_knowledge・日本語のまま送る)
        ↓
  保管庫の GitHub Actions が起動
        ↓
  形式変換 → コミット → 目次の再生成 → 個人情報チェック

AIやサーバー側でBase64変換を行いません。 変換はワークフロー側の責務です。

保存は非同期のため、ツールの成功は「受け付けた」ことを意味します。入力に不備があった場合は、保管庫に「保存失敗」として記録されます。

セットアップ

1. 依存のインストール

poetry install

2. 環境変数

.env.example をコピーして .env を作成します。

cp .env.example .env

変数

必須

内容

KNOWLEDGE_REPOSITORY

保管庫のリポジトリ(owner/repo

GITHUB_TOKEN

アクセストークン

KNOWLEDGE_SAVE_RATE_LIMIT_PER_MINUTE

1分あたりの保存上限(既定 10)

MCP_TRANSPORT

stdio(既定)または streamable-http

3. トークンの権限

Fine-grained PAT を推奨します。 権限は必要最小限にしてください。

対象リポジトリ : 保管庫の1つだけ
権限          : Contents — Read and write
付与しないもの : Actions / Issues / Administration / Workflows

4. 起動

# stdio(ローカルのMCPクライアント向け)
poetry run knowledge-mcp

# Streamable HTTP(リモート接続向け)
MCP_TRANSPORT=streamable-http poetry run knowledge-mcp

セキュリティ

AIに書き込み権限を渡すため、AIの判断に依存しない防御を実装しています。

対策

内容

パス検証

分類はホワイトリスト方式。ファイル名は .md のみ許可し、.. / \ と隠しファイルを拒否

最小の権限

ツールは4つのみ。削除・Issue作成・ワークフロー実行の手段を持たない

レート制限

1分あたりの保存回数に上限。プロンプトインジェクションによる大量保存への歯止め

入力の上限

タイトル120字/概要500字/本文10,000字/タグ10個

秘密情報

暗号化せず環境変数から直接読む(鍵を同じ場所に置く二重管理はしない)

タイムアウト

全HTTPリクエストに設定(接続5秒/読み取り30秒)

リダイレクト

追わない。意図しない転送先へトークンが送られることを防ぐ

ログ

本文・トークンを記録しない。エラーの詳細は標準エラーのみに出す

ログ偽装対策

エラーメッセージに含める入力値から制御文字を除去し、長さを切り詰める

パス検証について

.github/workflows/ に書き込めると任意のコードが実行できてしまうため、 分類名はホワイトリスト方式で固定しています。相対参照や絶対パスは、 ホワイトリストに存在しない時点で弾かれます。

ファイル名の正規表現には \A\Z を使っています。 Python の $ は「文字列末尾の直前の改行」にもマッチするため、 ^...$ だと "file.md\n" のような入力が通ってしまいます。

ログ

操作の記録と、障害を追うための情報を1行1JSONで出します。 出力先は常に標準エラーです(stdio 通信では標準出力が MCP プロトコル専用のため)。

何が残るか

種類

レベル

内容

本番(INFO)で残るか

tool.started

DEBUG

呼び出しの開始

tool.completed

INFO

操作ログ。何が実行されたか

tool.denied

WARNING

弾いた入力。検証エラー・レート制限

tool.failed

ERROR

失敗。原因の種別つき

{"timestamp":"2026-09-01T05:12:33.123Z","level":"WARNING","logger":"knowledge_mcp.audit",
 "message":"tool.denied",
 "extra":{"tool":"get_knowledge_index","request_id":"71346ae6020f",
          "category":"../.github/workflows","outcome":"denied",
          "reason":"validation_error","duration_ms":0.1}}

調べ方

1回の呼び出しに request_id が振られ、その間に出た全てのログに同じ値が付きます。

grep '71346ae6020f' server.log
jq 'select(.extra.tool=="save_knowledge")' server.log
jq 'select(.message=="tool.denied")' server.log
jq 'select(.extra.duration_ms > 3000)' server.log

tool.denied が増えていたら中身を見てください。 検証で弾いた記録であり、 AIが仕様を取り違えているか、指示文が誘導に乗せられている可能性があります。 上の例では、保管庫の外(.github/workflows)を読もうとしたことが残っています。

保存の追跡

保存は非同期です。このサーバーのログに残るのは受け付けたところまでで、 実際にコミットされたかは保管庫側の Actions の記録を見ます。 突き合わせられるよう、保存時の request_id を控えておいてください。

残さないもの

記録の本文・タイトル・トークンは出しません。本文は長さ(body_chars)だけを残します。 何が保存されたかは保管庫の履歴で確認してください。ログに業務内容を写すと、 ログの保管場所にも同じ機密性が必要になるためです。

トークンは形式によらず伏せられます(GITHUB_TOKEN= / GITHUB_PAT= のような 環境変数名の形も含む)。

他のMCPサーバーとの統合

register(server) を公開しているため、1台のサーバーに複数のMCPをまとめられます。

from mcp.server import MCPServer
from chatwork_mcp.interfaces.mcp_tools import register as register_chatwork
from knowledge_mcp.interfaces.mcp_tools import register as register_knowledge

server = MCPServer("integration", version="1.0.0")
register_chatwork(server)
register_knowledge(server)
server.run(transport="stdio")

クライアントからは1つのアプリとして見え、サーバーの費用も1台分で済みます。

構成

src/knowledge_mcp/
├── domain/              外部に依存しない。保管庫の構造と検証ルール
│   ├── models.py
│   └── validators.py
├── application/         ユースケース
│   └── usecases.py
├── infrastructure/      外部との接続
│   ├── config.py
│   ├── github_client.py
│   └── rate_limiter.py
├── interfaces/          MCPとの接続
│   └── mcp_tools.py
├── observability/       運用のための記録
│   ├── audit.py         操作の記録と相関ID
│   └── log_config.py    出力形式・秘密情報の伏せ字・保存先
└── mcp_server.py        エントリポイント

依存の向きは常に外側から内側です。MCP SDK の仕様が変わっても domainapplication は影響を受けません。

テスト

poetry run pytest

ライセンス

MIT

Related MCP Connectors

Related MCP Servers