Spaces:
Runtime error
Runtime error
ryoshimu
commited on
Commit
·
ad95a4d
1
Parent(s):
87173b8
commit
Browse files- Dockerfile +15 -0
- README1.md +21 -0
- app.py +21 -0
- clinet.py +43 -0
- requirements.txt +2 -0
Dockerfile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.10-slim
|
| 2 |
+
|
| 3 |
+
ENV PYTHONUNBUFFERED=1 \
|
| 4 |
+
PORT=7860
|
| 5 |
+
|
| 6 |
+
WORKDIR /app
|
| 7 |
+
|
| 8 |
+
COPY requirements.txt .
|
| 9 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 10 |
+
|
| 11 |
+
COPY . .
|
| 12 |
+
|
| 13 |
+
EXPOSE 7860
|
| 14 |
+
|
| 15 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
README1.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# FastMCP サンプル MCP サーバー (Hugging Face Spaces デプロイ用)
|
| 2 |
+
FastMCP 2.12.4 を使った MCP サーバーのサンプルです。`app.py` では `Sample MCP Server` (version 0.1.0) を定義し、`/mcp` エンドポイントを Streamable HTTP で公開しています。`@mcp.tool` で登録した `echo` ツールを呼び出すと、受け取ったテキストをそのまま返します。Hugging Face Spaces に配置することで、ChatGPT などの MCP 対応クライアントから簡単に利用できます。
|
| 3 |
+
## セットアップ
|
| 4 |
+
- Python 3.10 以降を想定しています。
|
| 5 |
+
- 仮想環境を作成したら、`pip install -r requirements.txt` を実行して FastMCP と Uvicorn をインストールしてください。
|
| 6 |
+
## ローカル動作確認
|
| 7 |
+
1. `python app.py` を実行すると、`http://localhost:7860` にサーバーが立ち上がります。`/` でヘルスチェック (`{"status": "ok", ...}`) を確認できます。
|
| 8 |
+
2. クライアント検証には `python client.py --url http://localhost:7860/mcp` を使用します。`ping → list_tools → call_tool("echo")` の順に呼び出し、最初の `TextContent` を標準出力に表示します。
|
| 9 |
+
## Hugging Face Spaces へのデプロイ
|
| 10 |
+
1. Space を作成し、SDK を「Docker」、ランタイムを「Streamable HTTP」に設定します。
|
| 11 |
+
2. このリポジトリをそのままアップロードします (既存の `Dockerfile` で `uvicorn app:app --host 0.0.0.0 --port 7860` が実行される前提です)。
|
| 12 |
+
3. ビルド後、Space の URL `https://<your-space>.hf.space` が発行されます。Streamable HTTP の `/mcp` エンドポイントが外部に公開され、MCP クライアントからアクセス可能になります。
|
| 13 |
+
必要に応じて README に上記 Streamable HTTP 前提であること、`Dockerfile` のコマンド (`uvicorn app:app`) が FastMCP サーバーに対応していることを追記してください。
|
| 14 |
+
## サーバー仕様メモ
|
| 15 |
+
- `/mcp` は HTTP ベースの MCP エンドポイントです (WebSocket ではありません)。
|
| 16 |
+
- `@mcp.tool` を追加することで、任意のツールを拡張できます。例: `echo` ツールは `{"text": "..."}` を受け取り同じ内容を返します。
|
| 17 |
+
- `/` は Starlette ベースのヘルスチェックを返し、Spaces の稼働監視に利用できます。
|
| 18 |
+
## ChatGPT からの接続手順
|
| 19 |
+
1. ChatGPT アプリの MCP サーバー設定に、新規エンドポイントとして `https://<your-space>.hf.space/mcp` を登録します。
|
| 20 |
+
2. FastMCP のクライアント (例: `client.py`) で事前に疎通確認すると安心です。
|
| 21 |
+
3. ChatGPT 側で接続後、`echo` ツールを呼び出すと、入力テキストがそのままレスポンスとして返ります。
|
app.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""FastMCP sample MCP server implementation."""
|
| 2 |
+
from fastmcp import FastMCP
|
| 3 |
+
from starlette.responses import JSONResponse
|
| 4 |
+
import uvicorn
|
| 5 |
+
mcp = FastMCP(name="Sample MCP Server", version="0.1.0")
|
| 6 |
+
@mcp.tool(name="echo")
|
| 7 |
+
async def echo(text: str) -> str:
|
| 8 |
+
"""Return the provided text unchanged."""
|
| 9 |
+
return text
|
| 10 |
+
@mcp.custom_route("/", methods=["GET"])
|
| 11 |
+
async def health_check(_request) -> JSONResponse:
|
| 12 |
+
"""Basic health-check endpoint for Hugging Face Spaces."""
|
| 13 |
+
return JSONResponse(
|
| 14 |
+
{
|
| 15 |
+
"status": "ok",
|
| 16 |
+
"message": "Sample MCP server is running with FastMCP.",
|
| 17 |
+
}
|
| 18 |
+
)
|
| 19 |
+
app = mcp.http_app()
|
| 20 |
+
if __name__ == "__main__":
|
| 21 |
+
uvicorn.run("app:app", host="0.0.0.0", port=7860, reload=False)
|
clinet.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Utility client for validating the FastMCP sample server."""
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
import argparse
|
| 4 |
+
import asyncio
|
| 5 |
+
from typing import Any, Iterable, Optional
|
| 6 |
+
from fastmcp import Client
|
| 7 |
+
from fastmcp.types import TextContent
|
| 8 |
+
def extract_first_text_block(content_blocks: Optional[Iterable[Any]]) -> Optional[str]:
|
| 9 |
+
"""Return the first text block contained in the MCP tool response."""
|
| 10 |
+
if not content_blocks:
|
| 11 |
+
return None
|
| 12 |
+
for block in content_blocks:
|
| 13 |
+
if isinstance(block, TextContent):
|
| 14 |
+
return block.text
|
| 15 |
+
return None
|
| 16 |
+
async def run(url: str) -> None:
|
| 17 |
+
"""Execute a simple validation workflow against the MCP server."""
|
| 18 |
+
client = Client(url=url)
|
| 19 |
+
async with client:
|
| 20 |
+
ping_result = await client.ping()
|
| 21 |
+
print("Ping:", ping_result)
|
| 22 |
+
tools_response = await client.list_tools()
|
| 23 |
+
print("Tools:", tools_response)
|
| 24 |
+
tool_call = await client.call_tool("echo", {"text": "Hello MCP"})
|
| 25 |
+
text = extract_first_text_block(getattr(tool_call, "content", None))
|
| 26 |
+
if text is not None:
|
| 27 |
+
print("Echo response:", text)
|
| 28 |
+
else:
|
| 29 |
+
print("Echo response contains no TextContent blocks:", tool_call)
|
| 30 |
+
def parse_args() -> argparse.Namespace:
|
| 31 |
+
parser = argparse.ArgumentParser(description="FastMCP sample client")
|
| 32 |
+
parser.add_argument(
|
| 33 |
+
"--url",
|
| 34 |
+
default="http://localhost:7860/mcp",
|
| 35 |
+
help="MCP server URL (default: %(default)s)",
|
| 36 |
+
)
|
| 37 |
+
return parser.parse_args()
|
| 38 |
+
def main() -> None:
|
| 39 |
+
args = parse_args()
|
| 40 |
+
asyncio.run(run(args.url))
|
| 41 |
+
if __name__ == "__main__":
|
| 42 |
+
main()
|
| 43 |
+
|
requirements.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastmcp==2.12.4
|
| 2 |
+
uvicorn[standard]==0.30.1
|