MCP Hub
title: Pioneer emoji: 🔥 colorFrom: purple colorTo: pink sdk: docker app_port: 7860 pinned: false license: mit
MCP Hub
1つのHF Spaceに複数のMCP Serverをマウントし、パスで区別して、それぞれ独立した認証キーを持ちます。
local-mcp-hub のプラグイン方式に合わせて再構築:1つのMCPに1つのpy、main.py が自動で発見・組み立てを行い、新しいMCPを追加してもメインファイルを変更する必要はありません。
Related MCP server: MCP Hub
構造
hub-mcp/
├── main.py ← 插件自动发现 + 鉴权壳 + 路由装配
├── Dockerfile ← ⚠️ GitHub 侧完整构建定义,与 HF 侧那份内容不同,见「构建部署链路」
├── requirements.txt
├── .github/workflows/build.yml ← GHCR 镜像构建(含防套娃闸门)
├── duck-mcp/ ← duck-mcp TS 原版完整项目(npm install + tsc build 出 dist/)
└── mcps/
├── _ddg.py ← 库:DDG 搜索/抓取实现(下划线开头,不加载为插件)
├── _stdio_bridge.py ← 库:stdio 子进程桥公共实现(duck / academic 共用,见「踩坑档案 #1」)
├── doubao-mcp.py → /doubao/sse web_search
├── zhihu-mcp.py → /zhihu/sse zhihu_search / global_search / zhihu_ask / zhihu_trending
├── ddg-mcp.py → /ddg/sse search / scrape(旧版,已被 /duck 取代)
│ + REST: POST /ddg/search、/ddg/scrape(给 rikkahub 安卓端)
├── duck-mcp.py → /duck/sse 桥:bash -c 'cd duck-mcp && node dist/index.js'
└── academic-mcp.py → /academic/sse 桥:/opt/academic-venv/bin/academic-mcp子プロセスブリッジ(duck / academic)
これらは自前実装ではなく、上流のオリジナルMCPサーバーを子プロセスとして起動し、stdioで通信します。
ハブはプロトコル転送のみ行います(tools/list、tools/call をそのまま透過):
duck:上流はTSプロジェクト(VMサンドボックスでanti-botチャレンジを解決 + Chrome134 TLSフィンガープリント)。 Pythonへの移植コストが高すぎるため、プロジェクト全体を
duck-mcp/に格納し、イメージ内でnode 22を使ってdist/index.jsを実行します。academic:純Pythonですが、依存関係(fastmcp)がハブの
mcp==1.2.0と競合するため、独立したvenv/opt/academic-venvに分離してインストールします。
共通実装は mcps/_stdio_bridge.py にあり、呼び出しのたびに独立したセッションを起動し、使い終わったら閉じます。
これは手抜きではなく、anyioに強制された結果です。理由は「ハマりどころアーカイブ #1」を参照。セッションキャッシュを追加しないでください。
エンドポイント
MCP | SSEエンドポイント | ツール |
豆包検索 |
|
|
知乎 |
|
|
DuckDuckGo |
|
|
DuckDuckGo(オリジナルTSブリッジ) |
|
|
学術論文 |
|
|
エンドポイントの実測ステータス(2026-08-20)
エンドポイント | tools/list | 実際の呼び出し | 備考 |
| ✅ | ✅ | 無料枠はCustom+Globalで合計500回/月、使い切らないように注意 |
| ✅ | ✅ | |
| ✅ 3ツール | ✅ 実際の論文を返す | arXivは成功、キー不足のソース(Scopus/WOS/CORE/IEEE…)は警告のみで影響なし |
| ✅ 9ツール | ⚠️ ブリッジは接続、上流がブロック | DDGがHFデータセンターのIPにanti-botチャレンジを返す、コードの問題ではない、IP変更/プロキシが必要 |
| ✅ | ⚠️ | 旧版、反スクレイピングで死にやすい、REST用に残してある、削除を検討してもよい |
academicのパラメータの落とし穴
paper_search / paper_download は query_list オブジェクトの配列を受け取ります。文字列ではありません:
{"query_list": [{"query": "quantum computing", "searcher": "arxiv", "max_results": 2}]}searcher を省略 = 全ソースを検索(遅い)。paper_read は {"searcher": ..., "paper_id": ...} です。
RESTエンドポイント(rikkahub Android用、MCPではない)
メソッド | パス | body | 戻り値 |
POST |
|
|
|
POST |
|
|
|
戻り値はrikkahubの SearchResult / ScrapedResult と完全に同型なので、クライアントはそのままデシリアライズできます。
認証も Authorization: Bearer <DDG_KEY> で、エラー時は {"detail": "..."} を返します。
認証
各MCPは独立したBearerキー(Authorization: Bearer <key>)を持ちます:
MCP | key env | デフォルト値 |
doubao |
|
|
zhihu |
|
|
ddg |
|
|
duck |
|
|
academic |
|
|
envが設定されていればenvの値を使い、なければデフォルトを使います。GET / のホームページで各エンドポイントの認証設定状況や上流のシークレットが配置されているかを確認できます。
上流のシークレット(HF Space Settings → Secrets に置く、リポジトリには書かない)
env | 用途 |
| 火山方舟豆包検索 Custom 版 API キー(必須、Global 版未設定時はこれにフォールバック) |
| 豆包検索 Global 版専用キー(任意、「APIキー管理-従量課金」で作成、未設定だと Global 版は ARK キーを使い、高確率で 700901 エラー) |
| 知乎オープンプラットフォームの Access Secret |
新しいMCPを追加する
mcps/ にpyファイルを置くだけ、main.py の変更は不要:
"""第一行 docstring 会显示在 / 首页 about 里。"""
import os
from mcp import types
from mcp.server import Server
MOUNT = "myname" # 可选,默认用文件名(去掉 .py)
KEY_ENV = "MY_KEY" # 可选,Bearer 鉴权 env 名
DEFAULT_KEY = "" # 可选,默认 key(env 没配时用)
# ENABLED = False # 可选,临时停用
server = Server("My Server")
@server.list_tools()
async def list_tools() -> list[types.Tool]:
...
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
...書かない
if __name__ == "__main__": server.run(...)—— ポートとルーティングはハブが管理します。1つのpyで複数のエンドポイントをマウント:
MOUNTS = {"path1": srv1, "path2": srv2}。追加のRESTルートを持たせたい場合(単一マウント):
ROUTES = [starlette.Route("/xxx", endpoint=..., methods=["POST"])]、このプラグインのパスの下にマウントされます。同じディレクトリのライブラリファイルを参照したい場合:
import _xxx(アンダースコアで始まるファイルはプラグインとして読み込まれません)。単一のプラグインのimport失敗は
/のbrokenにエラーが表示されるだけで、他のプラグインには影響しません。
ローカルで実行
pip install -r requirements.txt
uvicorn main:app --port 7860ビルド・デプロイの流れ
HF Space のビルド環境は制限が多い(bun がインストールできない、curl もない)ため、HF 内ではビルドしません:
改代码 → push GitHub(fuwei99/hub-mcp) → Actions 构建镜像 → 推 GHCR
↓
HF 的 Dockerfile 只 FROM 拉现成镜像両側の Dockerfile は内容が異なり、それぞれ役割が違います:
場所 | 内容 | 役割 |
GitHub |
| 実際にイメージをビルド |
HF |
| 既存のイメージを引っ張って実行するだけ |
🚨 鉄の掟
1. HF の Dockerfile を GitHub に同期してはいけない。 そうしないと Actions が「入れ子ビルド」を起こします:前のバージョンのイメージをその場で転用して再プッシュし、
COPYステップが一切実行されず、 イメージは常に古いコードのまま、ビルドは success と表示される。すでに2回失敗しています(ハマりどころ #2 参照)。 具体的な地雷:ローカルリポジトリの remote が HF を指している場合、Dockerfile に対してgit checkout origin/main -- Dockerfileを実行しないでください。HF 版をローカルに取り込んで、一緒に GitHub にプッシュしてしまいます。2. HF 側は digest を固定し、
:latestを使わない。 HF のビルドは latest の古い digest をキャッシュするため、タグが変わらなければレイヤーを再取得しません → コードを変更しても本番は古いまま。3. デプロイ前に検証する。盲目的に "success" を信じない。 GHCR からコードレイヤーを取り出してファイルが正しいか確認する(方法は下記)方が、本番ログで何度も壁にぶつかるより速い。
イメージを変更する標準的な流れ
# 1. 改代码,只推 GitHub(注意:Dockerfile 必须是完整构建版)
git push --force https://github.com/fuwei99/hub-mcp.git main:main
# 2. 等 Actions(workflow 已带防呆闸门,套娃/缺 COPY 会直接 fail)
curl -H "Authorization: Bearer $GITHUB_TOKEN_FUWEI" \
"https://api.github.com/repos/fuwei99/hub-mcp/actions/runs?per_page=1"
# 3. 取新 digest
tok=$(curl -s "https://ghcr.io/token?scope=repository:fuwei99/hub-mcp:pull" | jq -r .token)
curl -sI -H "Authorization: Bearer $tok" \
-H "Accept: application/vnd.oci.image.index.v1+json" \
"https://ghcr.io/v2/fuwei99/hub-mcp/manifests/latest" | grep -i docker-content-digest
# 4. 改 HF 的 Dockerfile FROM 行为该 digest,推 HF
# 5. 验证线上真的换了代码(找个只有新版才有的字符串)
curl -s https://fluidgender159-hub-mcp.hf.space/ | jq .about検証:GHCR からファイルを取り出して確認
docker を使わず、curl だけでイメージレイヤーを開けます(ビルドが実際に有効かどうかを判断する究極の手段):
tok=$(curl -s "https://ghcr.io/token?scope=repository:fuwei99/hub-mcp:pull" | jq -r .token)
A="Accept: application/vnd.oci.image.index.v1+json, application/vnd.oci.image.manifest.v1+json"
# index → amd64 manifest → 找几 KB 的小层(就是 COPY mcps/ 那层)→ 拉 blob 解 tar
curl -s -H "Authorization: Bearer $tok" -H "$A" \
"https://ghcr.io/v2/fuwei99/hub-mcp/manifests/latest" -o idx.json
# ...取 amd64 digest、取 layers 里 size < 20000 的、curl blobs/<digest> | tar tzActions のログでも入れ子ビルドは一目でわかります:正常なビルドには COPY があり、1〜2分かかります。
入れ子ビルドは resolve ghcr.io/... done + exporting layers だけで、2秒で終わります。
ハマりどころアーカイブ
#1 ⭐ anyio cancel scope はタスクをまたげない(子プロセスブリッジがハングする真の原因)
現象:/duck/sse、/academic/sse には接続でき、initialize は即座に返るが、tools/list
永久に沈黙 —— エラーも出ず、タイムアウトもせず、SSE には ping だけが流れる。どの MCP クライアントでも「ハング」します。
誤って判断した方向(どれも原因ではない):SSE 長接続、テストスクリプト、node が起動しない、 banner が stdout を汚す(banner は stderr に出力されるので、stdout はクリーン)。
真の原因:stdio_client() と ClientSession() はどちらも task-bound の anyio コンテキストです。
ブリッジは当初、オーバーヘッドを減らすために、A リクエストのタスク内で __aenter__ した後、セッションをグローバルにキャッシュして B リクエストで再利用していました。
しかし、ハブでは各 SSE 接続が独立したタスクなので、次のようになります:
RuntimeError: Attempted to exit cancel scope in a different task than it was entered inその挙動は非常に陰険です:initialize が返るのは、ブリッジシェルが自分で答えただけで、子プロセスには触れていないからです。
tools/list で実際の子プロセス転送が必要になると、タスクをまたいだ cancel scope で死にます。
再現(30行のローカルスクリプト、デプロイ不要):
async def task_a():
cm = stdio_client(params); read, write = await cm.__aenter__()
scm = ClientSession(read, write); s = await scm.__aenter__()
await s.initialize(); state["s"] = s # 缓存给别的 task
async def task_b():
await state["s"].list_tools() # 💥 死这儿
await asyncio.create_task(task_a())
await asyncio.create_task(task_b())修正:mcps/_stdio_bridge.py —— 毎回 list_tools/call_tool を現在のタスク内で
子プロセスを起動し、async with で閉じ、使い終わったら即閉じます。ツールの説明(純データ、タスクをまたげる)だけをキャッシュします。
async with stdio_client(self._params_factory()) as (read, write):
async with ClientSession(read, write) as session:
await asyncio.wait_for(session.initialize(), timeout=self._timeout)
return await asyncio.wait_for(fn(session), timeout=self._timeout)共有セッションに「最適化」しないでください。本当に高速化したいなら、専用の常駐ワーカータスク + キューを起動し、すべてのIOをそのタスク内で行うのが正しい方法です。コンテキストオブジェクトをタスク間で渡すのはやめてください。
ついでの教訓:ClientSession(read, write) を new するだけで __aenter__ しないとハングします ——
バックグラウンドの「stdout を読む → レスポンスをディスパッチ」タスクは __aenter__ 内で起動されるので、
コンテキストに入らなければ、送信したリクエストの応答を受け取る人がいません。
#2 ⭐ 入れ子ビルド(イメージは常に古いコード、ビルドは success と表示)
現象:コードを変更し、Actions が success、HF が RUNNING で再構築されたが、本番の挙動はまったく変わらない。 HF キャッシュ、GHCR キャッシュ、レイヤーキャッシュを疑ったが、どれも違った。
特定手段:GHCR から COPY mcps/ のレイヤーを取り出して tar tzf で確認 —— 新しく追加した
_stdio_bridge.py がイメージに存在しないのに、GitHub にはある。Actions のログを見ると:
#1 transferring dockerfile: 647B ← 完整版有 2.7KB
#5 resolve ghcr.io/fuwei99/hub-mcp@sha256:0799864b... done
#7 exporting layers done ← 全程 2 秒,零 COPY真の原因:GitHub リポジトリの Dockerfile が HF 版の
FROM ghcr.io/fuwei99/hub-mcp@sha256:... になっていた —— Actions が古いイメージをその場で転用して再プッシュしていた。
どうやって混入したか:ローカルリポジトリの remote が HF で、git checkout origin/main -- Dockerfile を実行して HF 版を作業領域に取り込み、その後 GitHub にプッシュする際に一緒に送ってしまった。
防止策(.github/workflows/build.yml に追加済み、再発したらその場でビルド失敗):
- name: 拒绝套娃构建
run: |
if grep -qE '^FROM +ghcr\.io/fuwei99/hub-mcp' Dockerfile; then
echo "::error::Dockerfile 是 HF 版,会套娃构建"; exit 1
fi
grep -q 'COPY mcps/' Dockerfile || { echo "::error::缺少 COPY mcps/"; exit 1; }さらに Dockerfile にもビルド時自己チェックを追加:test -f mcps/_stdio_bridge.py || exit 1。
#3 academic-mcp の依存関係地獄
上流の academic-mcp==0.1.7 は依存関係の上限を固定しておらず、インストールされた組み合わせが壊れています。3連続のエラー:
エラー | 原因 |
| fastmcp が必要だが、academic-mcp が宣言していない |
|
|
| 2段階で |
修正:1つのコマンドでまとめてインストールし、上限を明示的に固定し、ビルド時に import 自己チェックを実行:
RUN python3 -m venv /opt/academic-venv \
&& /opt/academic-venv/bin/pip install --no-cache-dir \
academic-mcp==0.1.7 pydantic-settings "mcp<2.0" \
&& /opt/academic-venv/bin/python -c "from fastmcp import FastMCP; \
from academic_mcp.__main__ import main; print('academic-mcp import OK')"実測で使える組み合わせ:academic-mcp 0.1.7 + fastmcp 3.4.7(または 2.14.1)+ mcp 1.29.0
pydantic-settings 2.15.0。
教訓:依存関係のアップグレードは2段階の pip install をせず、1回でまとめてインストールしてパーサーに統一判断させる。
依存関係の組み合わせは必ずローカル venv で実測してから Dockerfile に書き込み、import 自己チェックをビルド時に組み込む ——
インストールが間違っていればビルドが失敗し、本番ログでエラーを待つ必要がない。
#4 その他
現象 | 原因 | 修正 |
bun ダウンロード exit 127 |
| 先に |
bun | HF ビルド環境の制限 | Node 22 公式 tarball に変更、 |
|
| 旧シグネチャに合わせて単一引数を渡す |
academic が |
| 純粋にローカル環境の制限、HF/docker では正常。ダウンロードディレクトリは別途 |
ローカル/HF リモートが分岐 | 両側で並行プッシュ | rebase 後に強制プッシュ;またはデプロイ専用にクリーンな clone を1つ作成 |
トラブルシューティングの方法論(時間を節約する部分)
Python MCP クライアントでハング問題をデバッグしない —— それ自体もハングし、どこで詰まっているかわからない。 curl でプロトコルを手打ちし、フレームごとに誰が応答しないかを確認:
curl -sN -H "Authorization: Bearer wei123.." "$BASE/duck/sse" > sse.log & SID=$(grep -o 'session_id=[a-f0-9]*' sse.log | head -1 | cut -d= -f2) P="$BASE/duck/messages/?session_id=$SID" curl -X POST "$P" -d '{"jsonrpc":"2.0","id":1,"method":"initialize",...}' curl -X POST "$P" -d '{"jsonrpc":"2.0","method":"notifications/initialized"}' curl -X POST "$P" -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' # 盯 sse.log:initialize 回了但 id:2 不回 → 问题在桥拉子进程那一步レイヤーごとに特定:ブリッジシェル → 子プロセスが単独で実行できるか → 子プロセスのライブラリ関数を直接呼ぶ。 この例では
ArxivSearcher().search()を直接呼ぶと正常で、検索本体は問題ないことがわかる。問題はラッパーレイヤーにある。本番ログで壁に当たるのが最もコストが高い。ローカルでハブを起動して再現できるなら、本番にプッシュしない。 依存関係の問題はビルド時自己チェックに入れて、Actions で爆発させる。
serverInfo.versionはコードバージョンではない(それは mcp ライブラリのバージョン)。本番が新しいコードかどうかを判断するには、 新しいバージョンにしかない文字列を探す。例えばGET /が返す about のテキスト。
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceAn MCP server that provides Hugging Face Hub API and Search endpoints through multiple transport protocols (STDIO, SSE, StreamableHTTP, and StreamableHTTPJson), enabling integration with AI model capabilities.276MIT
- FlicenseNot gradedqualityNot gradedmaintenanceMCP Hub aggregates and proxies multiple Model Context Protocol servers into a unified Streamable HTTP interface. It allows users to combine diverse stdio, SSE, and HTTP-based servers while providing tool namespacing, health monitoring, and secure authentication.781
- AlicenseNot gradedqualityAmaintenanceZero-auth multi-source research MCP server that enables web search, reading URLs, PDFs, GitHub repos, and querying Hacker News, Stack Overflow, Semantic Scholar, and YouTube transcripts without API keys.10Apache 2.0
- FlicenseNot gradedqualityBmaintenanceAn extensible MCP hub that exposes internal services (chat, observability, RAG) as namespaced tools via FastMCP, with OpenAPI auto-generation, auth, and resilient error handling.
Related MCP Connectors
Hosted MCP server for LLM cost estimation, model comparison, and budget-aware routing.
MCP Hub: AI service discovery, per-user OAuth, and multi-service workflow orchestration
MCP server for secureFlows: token-free URL builders and integration-linting tools for AI agents.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/fuwei99/hub-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server