standalone_mcp_server.py•8.09 kB
#!/usr/bin/env python3
"""
Standalone MCP Server for STDIO mode
이 파일은 독립적으로 실행되어 asyncio 충돌 문제를 해결합니다.
"""
import asyncio
import httpx
from fastmcp import FastMCP
from fastmcp.server.openapi import RouteMap, MCPType
# OpenAPI 스펙 정의
SAMPLE_OPENAPI_SPEC = {
"openapi": "3.0.0",
"info": {
"title": "JSONPlaceholder API",
"version": "1.0.0",
"description": "A simple API for testing and prototyping",
},
"servers": [
{
"url": "https://jsonplaceholder.typicode.com",
"description": "JSONPlaceholder server",
}
],
"paths": {
"/posts": {
"get": {
"operationId": "get_posts",
"summary": "Get all posts",
"description": "Retrieve all posts from the blog",
"tags": ["posts"],
"parameters": [
{
"name": "userId",
"in": "query",
"schema": {"type": "integer"},
"description": "Filter posts by user ID",
}
],
"responses": {
"200": {
"description": "List of posts",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"title": {"type": "string"},
"body": {"type": "string"},
"userId": {"type": "integer"},
},
},
}
}
},
}
},
},
"post": {
"operationId": "create_post",
"summary": "Create a post",
"description": "Create a new blog post",
"tags": ["posts"],
"requestBody": {
"required": True,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["title", "body", "userId"],
"properties": {
"title": {"type": "string"},
"body": {"type": "string"},
"userId": {"type": "integer"},
},
}
}
},
},
"responses": {
"201": {
"description": "Created post",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"title": {"type": "string"},
"body": {"type": "string"},
"userId": {"type": "integer"},
},
}
}
},
}
},
},
},
"/posts/{id}": {
"get": {
"operationId": "get_post_by_id",
"summary": "Get post by ID",
"description": "Retrieve a specific post by its ID",
"tags": ["posts"],
"parameters": [
{
"name": "id",
"in": "path",
"required": True,
"schema": {"type": "integer"},
"description": "Post ID",
}
],
"responses": {
"200": {
"description": "Post details",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"title": {"type": "string"},
"body": {"type": "string"},
"userId": {"type": "integer"},
},
}
}
},
}
},
}
},
"/users": {
"get": {
"operationId": "get_users",
"summary": "Get all users",
"description": "Retrieve all users",
"tags": ["users"],
"responses": {
"200": {
"description": "List of users",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"name": {"type": "string"},
"email": {"type": "string"},
"username": {"type": "string"},
},
},
}
}
},
}
},
}
},
},
}
async def main():
"""독립적인 MCP 서버 실행"""
print("🚀 Standalone OpenAPI MCP 서버 시작 중...")
# HTTP 클라이언트 생성
client = httpx.AsyncClient(
base_url="https://jsonplaceholder.typicode.com", timeout=30.0
)
# 커스텀 라우트 매핑 설정
custom_route_maps = [
# GET 요청을 리소스로 매핑
RouteMap(
methods=["GET"],
pattern=r".*\{.*\}.*", # 경로 파라미터가 있는 GET 요청
mcp_type=MCPType.RESOURCE_TEMPLATE,
mcp_tags={"data", "read-only"},
),
RouteMap(
methods=["GET"],
pattern=r".*", # 기타 GET 요청
mcp_type=MCPType.RESOURCE,
mcp_tags={"data", "collection"},
),
# POST, PUT, DELETE 요청을 도구로 매핑
RouteMap(
methods=["POST", "PUT", "DELETE"],
pattern=r".*",
mcp_type=MCPType.TOOL,
mcp_tags={"write-operation", "api-mutation"},
),
]
# OpenAPI 스펙에서 MCP 서버 생성
mcp = FastMCP.from_openapi(
openapi_spec=SAMPLE_OPENAPI_SPEC,
client=client,
name="Standalone JSONPlaceholder MCP Server",
tags={"jsonplaceholder", "demo", "standalone"},
route_maps=custom_route_maps,
timeout=30.0,
)
print("✅ MCP 서버가 성공적으로 생성되었습니다!")
print("🔄 STDIO 모드로 실행 중...")
# STDIO 모드로 실행
await mcp.run()
if __name__ == "__main__":
asyncio.run(main())