Skip to main content
Glama
223n
by 223n

mcp-server

ローカルのOllamaをMCPサーバーとして公開し、Claudeから作業を任せられるようにするNode.jsのサーバーです。 公式のMCP TypeScript SDK v2で作っています。

入口は次の2つです。 どちらもMCPの2026-07-28版(server/discover)と2025年版(initialize)の両方に応答します。

入口

用途

ファイルの読み込み

出力の保存

stdio(docker exec -i ollama-mcp node stdio.ts

同じPCのClaude CodeとClaude Desktopから使います。こちらを勧めます

使えます(FILE_ROOTSの配下だけ)

OUTPUT_DIRを設定したときだけ使えます

HTTP(POST /mcp、ポート3000)

Cloudflare Tunnelを通して、claude.aiなどから使います

HTTP_ALLOW_FILES=trueと認証を両方設定したときだけ使えます

HTTP_ALLOW_WRITES=trueと認証とOUTPUT_DIRが要ります

構成

[ローカル]
Claude Code / Claude Desktop ──stdio──> docker exec ollama-mcp node stdio.ts ──> Ollama(ホストの11434番)

[リモート]
claude.ai ──HTTPS──> Cloudflare Access ──> Cloudflare Tunnel ──> 127.0.0.1:3000/mcp ──> Ollama

claude.aiとClaude Desktopのカスタムコネクタは、このPCではなくAnthropicのクラウドから接続します。 同じPCで使うだけなら、ローカル(stdio)の接続が確実です。 トンネルとAccessは要りません。

Related MCP server: Claude Sidekick

MCPのツール

ツール

内容

既定のモデル

ollama_chat

下書き、要約、翻訳などの作業を任せます。profileでphp、docker、git、code_reviewの定型の指示を選べます

DEFAULT_MODEL

ollama_review_code

コードを確かめます。行番号付きで「重大度、行、問題、改善案」を返します

DEEP_MODEL

ollama_explain_error

エラーやログの原因の候補と対処を返します

DEEP_MODEL

ollama_list_models

入っているモデルの一覧を返します

-

ollama_health

Ollamaが動いているかと、サーバーの設定を返します

-

list_files

許可ルートの中のファイルとディレクトリを一覧します。filesに渡すパスを探すときに使います。ファイルを扱えるときだけ出ます

-

read_file

1つのファイルを、ローカルのモデルに渡さずにそのまま読みます。save_outputで書いた結果を読み返すときに使います。ファイルを扱えるときだけ出ます

-

git_clone

GitHubのリポジトリをCLONE_ROOTの配下に取得します。owner/repoだけを受け、URLは受けません。CLONE_ROOTを設定したときだけ出ます

-

git_read

取得したリポジトリの状態を読みます。statuslogdiffshowbranchesremotesです

-

git_write

ブランチの作成、staging、commit、pushです。GIT_ALLOW_WRITE=trueにしたstdioでだけ出ます

-

github_read

Pull RequestとIssueと差分とコメントとチェックを読みます。GITHUB_MCP_TOKENを設定したときだけ出ます

-

github_write

Pull Requestの作成とコメントです。GITHUB_ALLOW_WRITE=trueにしたstdioでだけ出ます

-

  • ファイルを扱えるとき(stdioと、設定したHTTP)は、files引数にWindowsの絶対パスを渡すと、サーバーがファイルを読み込みます。Claudeはファイルの中身を引数として書き出さずに済むため、トークンを節約できます

  • filesは、1つのパスのほかにグロブと行範囲も取ります

    • グロブはC:\dev\app\src\**\*.phpのように書きます。list_filesで探してから渡す往復を省けます

    • 行範囲はC:\dev\app\src\Main.php#L10-200のように末尾に付けます。GitHubの永続リンクと同じ書き方です。#L10は1行、#L10-は末尾までです

    • ディレクトリをそのまま渡すことはできません。グロブの書き方を添えて拒みます

  • inline_filesには、サーバーが読めないファイルの中身を{"name": ..., "content": ...}の形で渡します。許可ルートの外にあるファイルや、Claudeが別の環境で開いているファイルに使います

    • これはトークンを節約しません。contentの分はどちらにせよ払います。サーバーが読めるパスなら必ずfilesを使います

  • 渡せる量の上限は、文字数ではなくトークン数の目安で測ります。日本語のコメントが多いコードは1文字がほぼ1トークンになるためです

    • 上限を超えた分は丸ごと落とし、落としたファイル名を応答とプロンプトの両方に書きます。黙って切りません

  • list_filespatternにグロブを取ります。大文字と小文字は区別しません

    • *は直下、**/*.phpは下の階層のPHPのファイル、*.{js,ts}は選択肢、末尾の/はディレクトリだけです

    • **でたどるのはpathから8階層までです。それより深いときは、そのことを結果に書き添えます

    • 秘密のファイルと、node_modulesvendor.gitは出しません。シンボリックリンクはたどりません

  • 応答の末尾に[ollama] model=... prompt_tokens=... output_tokens=... done_reason=... elapsed=...が付きます。done_reason=lengthdone_reason=timeoutのときは、出力が途中で切れています

  • 小さいモデルは同じ内容を繰り返し続けることがあるため、出力のトークン数に上限を設けています。ollama_chatは4096、ほかの2つは1536で、max_tokensで変えられます

  • ローカルのモデルの出力は誤りを含みます。Claudeの側で確かめてから使います

セットアップ

コンテナーを起動する

docker compose up -d --build

restart: unless-stoppedのため、Docker Desktopを起動すると一緒に立ち上がります。

Claude Desktopに登録する

%APPDATA%\Claude\claude_desktop_config.jsonmcpServersに次を足し、Claude Desktopを終了してから起動し直します。 チャットとCodeタブの両方で使えます。

{
  "mcpServers": {
    "ollama": {
      "command": "docker",
      "args": ["exec", "-i", "ollama-mcp", "node", "stdio.ts"]
    }
  }
}

Claude Codeだけで使う場合は、次のコマンドでも登録できます。

claude mcp add --scope user ollama -- docker exec -i ollama-mcp node stdio.ts

サブエージェントを入れる

claude/agents/ollama-worker.md%USERPROFILE%\.claude\agents\に写すと、Claude Codeからollama-workerのサブエージェントとして呼べます。 Ollamaに作業を任せ、その結果をClaudeが確かめてから返すための指示です。

ツールを呼ぶたびの確認を省く場合は、%USERPROFILE%\.claude\settings.jsonpermissions.allowmcp__ollama__*を足します。 OUTPUT_DIRを設定しないかぎり、どのツールもファイルを書き換えません。 設定したときに書くのはOUTPUT_DIRの配下だけで、読み込みのツールは何も書き換えません。

環境変数

.envに書きます。 docker-compose.yml.envの値を差し込むことにだけ使い、次の変数だけをコンテナーに渡します。

変数

既定値

説明

OLLAMA_URL

http://host.docker.internal:11434

OllamaのURLです

DEFAULT_MODEL

nucbox-fast:latest

ollama_chatの既定のモデルです

DEEP_MODEL

qwen2.5-coder:14b

コードの確認とエラーの解析の既定のモデルです

OLLAMA_TIMEOUT

300000

Ollamaから何も届かない状態の上限(ミリ秒)です。キューの待ち、モデルの読み込み、プロンプトの評価も含みます

OLLAMA_MAX_DURATION

3000000

1回の生成全体の上限(ミリ秒)です。HTTPのrequestTimeoutもこの値+60秒に合わせます

OLLAMA_MAX_CONCURRENCY

2

同時に走らせる生成の数です。OllamaはGPUを1つずつ使うため、並べても全体は速くなりません

OLLAMA_MAX_QUEUE

8

待ち行列の長さの上限です。ここも一杯なら、待たせずにその場で断ります

ALLOWED_HOSTS

localhost,127.0.0.1,[::1],host.docker.internal,mcp.223n.tech

HTTPで受け付けるHostOriginです

HTTP_ALLOW_FILES

false

HTTPでもファイルの読み込みを許すかどうかです。認証(MCP_AUTH_TOKENCF_ACCESS_*)がないときは無視します

MCP_AUTH_TOKEN

なし

設定すると、HTTPにAuthorization: Bearer <値>を求めます

CF_ACCESS_TEAM_DOMAINCF_ACCESS_AUD

なし

設定すると、HTTPにCloudflare AccessのJWT(Cf-Access-Jwt-Assertion)を求めます

CF_ACCESS_ALLOWED_EMAILS

なし

設定すると、AccessのJWTのemailがこの一覧にある人だけを通します。カンマで区切って並べます。サービストークンのJWTにはemailがないため、拒まれます

FILE_ROOTS

docker-compose.ymlで設定

ホストのパス=コンテナのパス;で区切って並べます

OUTPUT_DIR

なし

ローカルのモデルの出力を書き出す先です。ホストのパス=コンテナのパスを1件だけ書きます。設定したときだけsave_outputが出ます

HTTP_ALLOW_WRITES

false

HTTPでも書き出しを許すかどうかです。HTTP_ALLOW_FILESとは別に持ちます。認証がないときは無視します

CLONE_ROOT

docker-compose.ymlで設定

リポジトリを取得する先です。ホストのパス=コンテナのパスを1件だけ書きます。サーバーが書き換えてよいのはここの配下だけです

GIT_ALLOWED_OWNERS

docker-compose.ymlで設定

取得してよいGitHubのownerです。カンマで区切って並べます。空なら取得そのものを拒みます

GIT_ALLOW_WRITE

false

commitとpushを許すかどうかです。stdioでだけ効き、HTTPでは常に無効です

GIT_TIMEOUTGIT_MAX_DURATION

120000600000

gitから何も届かない状態の上限と、1回の操作全体の上限です(ミリ秒)

GIT_USER_NAMEGIT_USER_EMAIL

なし

commitに使う名前とメールアドレスです。commitするなら両方とも要ります

GITHUB_MCP_TOKEN

なし

GitHubのAPIに使うトークンです。fine-grainedを使い、対象のリポジトリを列挙します

GITHUB_ALLOW_WRITE

false

Pull Requestの作成とコメントを許すかどうかです。stdioでだけ効き、HTTPでは常に無効です

  • タイムアウトしても、それまでに生成された部分はdone_reason=timeoutと警告を付けて返します

  • NodeのrequestTimeoutは既定で300秒です。これを上げないと、OLLAMA_MAX_DURATIONをいくら大きくしてもHTTPは300秒で切ります。サーバーはOLLAMA_MAX_DURATION+60秒に合わせ、起動時に[http] request timeoutとして出します

    • 無通信の上限(OLLAMA_TIMEOUT)は300秒のままです。全体の上限だけを延ばし、Ollamaが固まったときは早く気付けるようにしています

    • 3000秒まで使えるのはstdioと、同じPCから直にHTTPを叩くときです。claude.aiのコネクタは約240秒、Cloudflareは無通信が約100秒で打ち切ります

  • MCP_AUTH_TOKENCF_ACCESS_*の両方を設定したときは、どちらかを満たせば通します

  • どちらも設定しないと、このPCのほかのコンテナーからもhost.docker.internal:3000を通してHTTPを呼べます

動作を確かめる

curl.exe http://127.0.0.1:3000/healthz
docker logs --tail 20 ollama-mcp

HTTPのアクセスログには、メソッド、パス、ステータス、Host、JSON-RPCのメソッドが出ます。 本文は記録しません。 トンネルを通したリクエストがサーバーまで届いているかを確かめるときに使います。

リモートで使う

claude.aiから使うときは、Cloudflare TunnelとCloudflare Accessを前に置きます。

  1. Cloudflare Tunnelで、公開するホスト名(例: mcp.223n.tech)をhttp://127.0.0.1:3000に向けます

  2. Cloudflare Zero Trustで、そのホスト名に「Self-hosted」のAccessのアプリを1つだけ作ります

  3. アプリに「Allow」のポリシーを足し、使う人のメールアドレスを入れます

  4. アプリの「Managed OAuth」を有効にし、「Allowed redirect URIs」にhttps://claude.ai/api/mcp/auth_callbackを足します

  5. .envCF_ACCESS_TEAM_DOMAINとアプリのCF_ACCESS_AUDを書き、コンテナーを作り直します

  6. claude.aiの「設定」の「コネクタ」で、https://<ホスト名>/mcpをカスタムコネクタとして足します

  • claude.aiとClaude Desktopのリモートのコネクタは、1回の呼び出しを約240秒で打ち切ります。Cloudflareは応答が約100秒途切れると打ち切ります。長い生成はローカルで行います

  • うまくつながらないときはdocs/troubleshooting.mdを見てください

リモートでファイルを読む

HTTPでも、files引数とlist_filesを使えます。 認証がないまま有効にすると誰でもファイルを読めてしまうため、認証を設定したときだけ有効になります。

  1. 前の手順でCF_ACCESS_TEAM_DOMAINCF_ACCESS_AUDを設定しておきます

  2. .envHTTP_ALLOW_FILES=trueを足します

  3. 使う人をさらに絞るときは、.envCF_ACCESS_ALLOWED_EMAILSにメールアドレスを書きます

  4. docker compose up -dでコンテナーを作り直します。ログにFile tools are enabled over HTTPと出れば有効です。CF_ACCESS_ALLOWED_EMAILSを書いたときは、ログの[auth]の行に登録した件数が出ます

  5. claude.aiの「設定」の「コネクタ」で、このコネクタのツールリストを更新します。list_filesが加わり、ollama_chatなどにfiles引数が付きます

  • 読めるのはFILE_ROOTSの配下だけで、秘密のファイルを拒むのはstdioと同じです

  • ファイルの中身はこのPCのOllamaにだけ渡ります。ただし、Ollamaの出力はclaude.aiに返るため、Anthropicのサービスを通ります

  • HTTP_ALLOW_FILESを外したときも、ツールリストを更新します。更新しないと、Claudeがなくなったツールや引数を呼んで失敗します

  • 大きなファイルを14Bのモデルに読ませると、1回の呼び出しの上限(約240秒)を超えることがあります。そのときはDEFAULT_MODELの7Bのモデルを使うか、ファイルを分けます

  • コネクタが約240秒で打ち切ったときは、途中まで生成された部分も返りません。リモートで主に使うなら、OLLAMA_MAX_DURATION200000ほどに下げると、打ち切られる前に途中までの結果を返せます。ただし、同じコンテナーのstdioにも効きます

出力をファイルに保存する

長い下書きや翻訳は、save_outputでファイルに書き出せます。 応答にはパスと先頭と末尾の抜粋だけが返るため、Claudeが全文を読まずに済みます。

  1. ホスト側に書き出し先のディレクトリを作ります。コンテナーのnodeユーザーが書ける権限にします

  2. .envOUTPUT_DIR=C:\dev\ollama-out=/work/dev/ollama-outのように書きます

  3. HTTPでも使うときは、認証を設定したうえでHTTP_ALLOW_WRITES=trueを足します

  4. docker compose up -dでコンテナーを作り直します。ollama_healthoutput savingenabledになれば有効です

  • ファイル名はoutput_nameで指定します。使えるのは英数字と_-だけで、.とパス区切りは拒みます

  • 拡張子はサーバーが.mdに決めます。.php.jsをサーバーに書かせないためです

  • すでにあるファイルは上書きせず、-2-3と後ろに足して新しく作ります

  • 書き出したファイルの先頭には、ローカルのモデルが書いたものだという断りが入ります

  • 書き出したファイルはread_fileで読み返せます。#L120-200を付けると一部だけ読めます

  • ローカルのモデルは同じ行を繰り返して終わることがあります。末尾の重複を数え、疑わしいときは警告を付けます

MCPのリソースとして読む

ファイルを扱えるときは、MCPのresourcesとしても同じファイルを公開します。 resources/listは許可ルートだけを返し、resources/readはディレクトリなら一覧を、ファイルなら中身を返します。

  • URIはfile:///C:/dev/app/src/Main.phpの形です。#L10-200を付けると行範囲になります

  • 防御はfiles引数とまったく同じ経路を通ります。許可ルートの外、..、秘密のファイル、シンボリックリンクは同じように拒みます

  • resources/readにはツール名がないため、mcp__ollama__*の許可の対象になりません。そのぶん、ファイルのツールと完全に同じ条件でだけ公開します

  • resources/subscribeとページングには対応していません。一覧は許可ルートだけに絞っています

リポジトリを取得してgitとGitHubを操作する

CLONE_ROOTを設定すると、GitHubのリポジトリを取得して、そのままローカルのモデルにレビューさせられます。

  1. ホスト側に取得先のディレクトリを作ります(例:C:\dev\claude

  2. .envCLONE_ROOTGIT_ALLOWED_OWNERSを書きます

  3. privateのリポジトリを扱うときは、fine-grainedのトークンをGITHUB_MCP_TOKENに書きます。対象のリポジトリは列挙して絞ります

  4. commitとpushまで任せるときは、GIT_ALLOW_WRITE=trueGIT_USER_NAMEGIT_USER_EMAILを足します

  5. Pull Requestの作成まで任せるときは、GITHUB_ALLOW_WRITE=trueを足します

  6. docker compose up -d --buildでコンテナーを作り直します。ollama_healthで状態を確かめられます

privateリポジトリを取得する

SSHの鍵は要りません。 GITHUB_MCP_TOKENにfine-grainedのトークンを設定すると、HTTPS経由でそのまま取得できます。 サーバーはトークンをx-access-tokenのBasic認証としてgitに渡します。 子プロセスの環境変数だけで渡すため、argvに現れず、.git/configにも残りません。

  1. GitHubの「Settings」→「Developer settings」→「Personal access tokens」→「Fine-grained tokens」で発行します

  2. 「Resource owner」に、対象のリポジトリを持つ利用者か組織を選びます

  3. 「Repository access」は「Only select repositories」にして、使うリポジトリだけを選びます

  4. 「Repository permissions」を次のように設定します

    権限

    必要な場面

    Metadata: Read

    必須です。ほかの権限を選ぶと自動で付きます

    Contents: Read

    git_cloneです。pushもするならRead and write

    Pull requests: Read

    pr_listpr_viewpr_diffpr_commentsです。PRを作るならRead and write

    Issues: Read

    issue_listissue_viewです。コメントするならRead and write

    Checks: Read

    pr_checksです

  5. .envGITHUB_MCP_TOKEN=github_pat_...と書き、docker compose up -dで作り直します

  6. ollama_healthgithub apienabledになれば有効です

トークンを設定していないと、privateリポジトリの取得はRepository not foundで失敗します。 GitHubが認証のない要求に404を返すためで、名前の打ち間違いと見分けが付きません。 サーバーはこのとき、トークンが未設定であることを書き添えます。

  • 取得先はCLONE_ROOT/owner/repoです。パスはownerrepoから組み立てるため、渡した文字列がパスの区切りとして働く余地がありません

  • URLは受け取りません。owner/repoだけを受け、https://github.com/owner/repo.gitはサーバーが組み立てます

  • 取得したリポジトリはlist_filesfilesread_fileから読めます。ローカルのモデルにレビューさせる目的なので、これは意図した動きです

  • 書き込みはstdioでだけ有効です。 GIT_ALLOW_WRITEGITHUB_ALLOW_WRITEをtrueにしても、HTTP経由ではgit_writegithub_writeが出ません

  • mainmasterdevelopへの直pushは、設定にかかわらず拒みます。それらをheadにしたPull Requestの作成も拒みます

  • ghコマンドは入れていません。GitHubのRESTのAPIを直に呼ぶため、gh apigh aliasのような別の実行経路がそもそもありません

監査ログ

サーバーはファイルを書き、リポジトリを取得し、pushし、Pull Requestを作れます。 何が行われたかを後から言えるよう、ツールの呼び出しを1行1JSONで記録します。

{"ts":"2026-09-22T12:00:00.000Z","identity":"you@example.com","kind":"tool","tool":"git_write","ok":true,"ms":842,"args":{"repo":"223n/mcp-server","op":"push","branch":"feature/x"}}
  • 出力先は標準エラーです。stdioのとき標準出力はMCPの通信路なので、そちらには出しません

  • identityは、Cloudflare AccessのJWTのemail、静的なトークンならtoken、stdioならstdioです。認証がない構成ではanonymousになります

  • argsには記録してよい鍵だけを残します。promptcodesystemcontextmessagebodyinline_filesの中身は出しません

    • 渡したファイルのパス(filespaths)は残します。何をローカルのモデルに渡したかは、監査でいちばん知りたいことだからです

    • inline_filesは件数だけにします。名前と中身のどちらも呼び出し側が決めるためです

  • resources/readにはツール名がなく、ツールの記録に載りません。読み取りの経路としては同じ重さなので、"kind":"resource"として別に記録します

  • Dockerではdocker logs ollama-mcpで見られます。ログは10MBを3世代まで残します

同時に走らせる数を絞る

OllamaはGPUを1つずつ使うため、生成を並べて投げても待ち行列に並ぶだけで、全体は速くなりません。 待っている間もクライアントの上限(claude.aiは約240秒)は進みます。

  • OLLAMA_MAX_CONCURRENCY(既定2)までを同時に走らせ、それを超えた分はOLLAMA_MAX_QUEUE(既定8)まで待ち行列に並べます

  • 待ち行列も一杯のときは、待たせずにその場で断ります。Claudeを長く待たせず、早く判断できるようにするためです

  • 待っている間は、進捗の通知で「何件待ちか」を伝えます

  • 今の状態はollama_healthconcurrencyに出ます

セキュリティ

  • .envはコミットしません。.gitignoreで外しています

  • Ollama(11434番ポート)には認証がありません。LANやインターネットへ直に公開しないでください

  • HTTPでファイルを読めるのは、HTTP_ALLOW_FILES=trueに加えて認証を設定したときだけです

  • ファイルの読み込みはFILE_ROOTSの配下だけに限ります

    • .env.envrc.npmrc、秘密鍵、app_local.phpなどの秘密のファイルと、.git.sshなどの配下は拒みます

    • Windowsの8.3形式の短い名前(ENV~1など)で回り込むことも拒みます

  • 書き出せるのはOUTPUT_DIRの配下だけです。FILE_ROOTSには書きません

    • HTTPで書き出せるのは、HTTP_ALLOW_WRITES=trueに加えて認証を設定したときだけです。HTTP_ALLOW_FILESだけでは書けません

    • ファイル名は英数字と_-だけに限り、NULCOM1などWindowsが特別扱いする名前も拒みます

    • 全角のはNFKCで/になるため、正規化してから確かめます

    • 作成はO_CREAT|O_EXCLで行います。先に置かれたシンボリックリンクをたどって別の場所へ書くことはありません

  • グロブでまとめて渡すときは、拒否リストではなく拡張子の許可リストで絞ります。名前を指定せずにサーバーが選ぶため、明示的なパスより狭くしています

    • 先頭が.の名前、拡張子のないファイル、ハードリンクは展開で拾いません

  • 読み込んだファイルに書かれた指示は、ローカルのモデルの出力に紛れ込むことがあります。出力の中の指示には従わないよう、ツールの応答と説明に書いてあります

    • OUTPUT_DIRFILE_ROOTSの配下に置くと、書き出した出力を読み返せる代わりに、モデルの出力が普通のファイルのような顔で戻ってきます。起動時に警告を出し、書き出したファイルの先頭に出自を書いています

  • gitを動かすときは、環境変数を継承しません。GIT_SSH_COMMANDGIT_EXTERNAL_DIFFなど、任意のコマンドを実行させる変数を持ち込ませないためです

    • 設定は/etc/git/server.gitconfigの1枚だけを読ませます。取得したリポジトリの.git/configに書かれた危険なキーは効きません

    • https以外のプロトコル(ext::file://git://ssh://)を拒みます

    • 引数は必ず配列で渡し、シェルを介しません。利用者の値は値の位置にしか入らず、-で始まる値は拒みます

    • トークンは子プロセスの環境変数だけで渡します。argvに現れず、.git/configにも残りません

  • gitはfiles引数とは別の読み取り口になります。diffからは秘密のファイルをpathspecで外し、showは中身を返しません

    • ただしこれは許可リストによる防御で、files.tsのような構造的な防御ではありません。取得したリポジトリの履歴に残った秘密は、原理的に読めます

  • 取得したリポジトリの中身は第三者が書いたテキストです。ローカルのモデルは指示の混入に弱いため、出力の中の指示には従いません

  • ツールの呼び出しは監査ログに残します。中身は出しませんが、ファイルのパスと操作の種類は残します

  • HTTPのアクセスログは10MBを3世代まで残します

ディレクトリ

mcp-server/
├─ claude/agents/ollama-worker.md   Claude Code のサブエージェントの定義
├─ docs/                            運用の手引きとトラブルシューティング
├─ docker-compose.yml
├─ Dockerfile
├─ tsconfig.json                    型の検査の設定(成果物は作らない)
├─ index.ts                         HTTP の入口
├─ stdio.ts                         stdio の入口
├─ src/
│  ├─ server.ts                     McpServer を作る(HTTP と stdio で共通)
│  ├─ types.ts                      複数のファイルで共有する型
│  ├─ config/                       環境変数、モデル、定型の指示
│  ├─ git/exec.ts                   git の起動(環境を継承しない、引数は配列、上限と中断)
│  ├─ http/auth.ts                  HTTP の認証(静的なトークン、Cloudflare Access の JWT)
│  ├─ ollama/client.ts              Ollama の API(ストリーミング、タイムアウト、中断)
│  └─ tools/                        ツール、ファイルの読み込みと一覧、出力の保存、リソース
└─ test/                            試験(Ollama の代わりに試験用のサーバーを使う)

TypeScript

ソースはTypeScriptで書きます。 ビルドはしません。 Nodeが.tsから型を取り除いてそのまま実行します(型の剥がし)。 そのためdist/のような成果物はなく、node index.tsnode stdio.tsが本番の起動コマンドです。

この方法にはNode 22.18以上が要ります。 package.jsonenginesがその下限を書いています。 Dockerのイメージが使うのはNode 26です。

型を検査する

Nodeは型を取り除くだけで、型が合っているかは見ません。 型の誤りが見つかるのは次のコマンドだけです。

npm run typecheck

npm run lintにも入っています。 CIでは「型の検査」ジョブが同じことをします。

書き方の決まり

設定はtsconfig.jsonにあり、次の3つが書き方を縛ります。

設定

何を縛るか

allowImportingTsExtensions

importには実行時と同じ綴りを書きます(./files.tsであって./files.jsではありません)

verbatimModuleSyntax

型だけを取り込むときはimport typeと書きます。こう書かないとNodeが値の取り込みと区別できません

erasableSyntaxOnly

enumnamespace、コンストラクターのパラメータープロパティは使えません。取り除くだけでは消えないためです

strictnoUncheckedIndexedAccessを有効にしています。 arr[0]obj[key]の型にはundefinedが入ります。 取り出した値は、そのまま使わずに確かめてください。

複数のファイルで使う型はsrc/types.tsに置きます。 MCPの通信で使う形は写さず、SDKの型(@modelcontextprotocol/server)をそのまま使います。

試験

npm testで試験します。 Ollamaの代わりに試験用のサーバー(test/helpers/mock-ollama.ts)を使うため、GPUとOllamaは要りません。

npm install
npm test

次のことを確かめます。

  • HTTPとstdioで、MCPの2025年版と2026-07-28版の両方につながること

  • ファイルの読み込みの防御(許可ルートの外、..、シンボリックリンク、秘密のファイル、大きさの上限)と、list_filesの絞り込み

  • 同じ防御が、グロブの展開とresources/readでも働くこと

  • 行範囲の切り出しで、行番号が元のファイルのまま振られること

  • inline_filesの名前で、見出しやフェンスを偽装できないこと

  • 書き出しの防御(パス区切り、..、二重の拡張子、Windowsの装置名、全角の区切り、置かれたシンボリックリンク)

  • 認証のないHTTPで、resourcesも書き出しの引数も出ないこと

  • owner/repoの検証(..、パスの区切り、Windowsの装置名、.gitで終わる名前、URL)

  • -で始まる値をgitの引数として拒むこと

  • 守るブランチへのpushと、それらをheadにしたPull Requestの作成を拒むこと

  • diffから秘密のファイルが外れること

  • HTTPではgit_writegithub_writeを出さないこと

  • 監査ログにpromptcodeの中身が出ず、識別子とファイルのパスは出ること

  • 同時に走らせる数の上限と、待ち行列が一杯のときに断ること

  • HTTPの認証(静的なトークン、Cloudflare AccessのJWT、メールアドレスの絞り込み)と、エラーの形

  • クライアントからの中断と、stdioのstdinが閉じたときに、Ollamaへの呼び出しが止まること

  • OLLAMA_MAX_DURATIONを超えたときに、途中までの出力を警告付きで返し、Ollamaへの呼び出しも止まること

  • list_filesのグロブが、*を並べた意地の悪いパターンでもすぐ終わること(ReDoSを防ぐ)

  • サーバーが読む環境変数を、docker-compose.ymlがすべてコンテナーに渡していること

  • 環境変数の不正な値で、起動時に止まること

CIは、Node 22と26で試験し、Dockerのイメージを作って起動したうえでHTTPとstdioの応答を確かめます。 あわせてTrivyでイメージの脆弱性を見ます。 apkで入れたパッケージの版はDependabotが追わないため、ここで拾います。 直せるもの(上流に修正がある高・重大)が見つかると失敗し、直せないものは記録に残すだけにします。

リポジトリの運用

ブランチの運用、リリース、ラベル、ワークフローはdocs/repository-operations.mdにあります。 変更の進め方はCONTRIBUTING.mdにあります。

変更したらnpm run lintを通します。 型の検査(tsc --noEmit)、Markdownの書式、日本語の書き方をまとめて確かめます。 文書だけを変えたときも型の検査が先に走ります。 ここで落ちたらnpm run typecheckを単体で実行し、どちらの検査が落ちたかを切り分けてください。

npm install
npm run lint

ライセンス

Apache License 2.0です。 LICENSEを見てください。

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    Not graded
    maintenance
    A minimal Python MCP server that enables Claude Code to call local Ollama models (e.g., gemma3) as a tool, routing low-stakes work off the API and onto a homelab.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    A small MCP server that turns a shared Ollama box into a team resource for Claude Code, providing typed tools and delegated read-only repo exploration using local models.
    MIT