OpenAPI 스펙에서 MCP

OpenAPI 스펙에서 MCP (MCP from OpenAPI Specs)

LiteLLM은 사용자 지정 MCP 서버 코드 없이도 어떤 OpenAPI/Swagger 스펙이든 MCP 서버로 변환할 수 있어요.

출처: 문서

본문

1단계: MCP 서버 추가 (Add the MCP Server)

config.yaml에서 OpenAPI 기반 서버를 추가해 주세요:

config.yaml:

mcp_servers:
  petstore_mcp:
    url: "https://petstore.swagger.io/v2"
    spec_path: "/path/to/openapi.json"
    auth_type: "none"

  my_api_mcp:
    url: "http://0.0.0.0:8090"
    spec_path: "/path/to/openapi.json"
    auth_type: "api_key"
    auth_value: "your-api-key-here"

  secured_api_mcp:
    url: "https://api.example.com"
    spec_path: "/path/to/openapi.json"
    auth_type: "bearer_token"
    auth_value: "your-bearer-token"

또는 UI에서: MCP Servers → Add New MCP Server로 가서 URL과 스펙 경로를 입력하면 LiteLLM이 스펙을 가져와 모든 엔드포인트를 도구로 로드해요.

구성 파라미터 (Configuration parameters):

파라미터 필수 설명
url API의 기본 URL
spec_path OpenAPI 스펙의 경로 또는 URL(JSON 또는 YAML)
auth_type 아니오 none, api_key, bearer_token, basic, authorization, oauth2
auth_value 아니오 인증 값(auth_type 설정 시 필수)
description 아니오 선택적 설명
allowed_tools 아니오 특정 도구의 허용 목록
disallowed_tools 아니오 특정 도구의 차단 목록

지원 스펙 버전: OpenAPI 3.0.x, 3.1.x, Swagger 2.0. 각 작업의 operationId가 도구 이름이 되므로 고유해야 해요.

내부 스펙 URL (SSRF) (Internal spec URLs (SSRF))

spec_pathhttp:// 또는 https:// URL이면 LiteLLM 프록시는 기본적으로 SSRF 보호를 활성화해 스펙을 가져와요. 호스트명이 해석되고, 해석된 주소 중 하나라도 전역 라우팅 가능하지 않으면(예: 10.x, 192.168.x, 127.0.0.1) 요청이 거부돼요. 단, URL의 호스트명(해석된 IP가 아니라)을 허용 목록에 추가하면 예외예요.

일반적인 경우:

  • 스펙 URL이 https://api.example.com/...인데 네트워크 내부 DNS가 사설 IP를 반환한다면, api.example.com을 허용 목록에 추가(포트를 고정했다면 api.example.com:443).
  • 스펙 URL이 http://127.0.0.1:8080/openapi.json이면 127.0.0.1 또는 127.0.0.1:8080을 추가.

프록시 config.yamllitellm_settings 아래에 구성하세요(general_settings에서 읽지 않음):

config.yaml:

litellm_settings:
  user_url_validation: true # default; set false only if you fully trust URL sources
  user_url_allowed_hosts:
    - "api.example.com"
    - "127.0.0.1"
    - "127.0.0.1:8080"

이 필드들의 전체 참조는 config settings: litellm_settings를 참고해 주세요.

2단계: 도구 이름·설명 재정의 (선택) (Optionally Override Tool Names and Descriptions)

기본적으로 도구 이름과 설명은 스펙의 operationId와 description 필드에서 가져와요. 업스트림 스펙을 건드리지 않고 MCP 클라이언트가 더 깔끔한 것을 보도록 이름을 바꾸거나 다시 쓸 수 있어요.

UI에서 (From the UI)

각 도구 카드에는 연필 아이콘이 있어요. 클릭해 인라인 편집기를 엽니다.

  • Display Name — MCP 클라이언트가 보는 이름을 재정의
  • Description — MCP 클라이언트가 보는 설명을 재정의
  • 필드를 비워두면 스펙의 원본 유지

재정의를 설정하면 도구 카드에 보라색 Custom name 배지가 나타나요.

API에서 (From the API)

생성 또는 업데이트 요청에 tool_name_to_display_nametool_name_to_description을 전달해 주세요:

도구 이름 재정의로 서버 생성:

curl -X POST http://localhost:4000/v1/mcp/server \
  -H "Authorization: Bearer $LITEL..._KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "petstore_mcp",
    "url": "https://petstore.swagger.io/v2",
    "spec_path": "/path/to/openapi.json",
    "tool_name_to_display_name": {
      "getPetById": "Get Pet",
      "findPetsByStatus": "List Available Pets"
    },
    "tool_name_to_description": {
      "getPetById": "Look up a pet by its ID",
      "findPetsByStatus": "Returns all pets matching a given status (available, pending, sold)"
    }
  }'

기존 서버의 재정의 업데이트:

curl -X PUT http://localhost:4000/v1/mcp/server \
  -H "Authorization: Bearer $LITEL..._KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "server_id": "<server-id>",
    "tool_name_to_display_name": {
      "getPetById": "Get Pet"
    },
    "tool_name_to_description": {
      "getPetById": "Look up a pet by its ID"
    }
  }'

맵 키는 접두사가 붙지 않은 스펙의 **원본 operationId**예요. LiteLLM은 조회 전에 서버 접두사를 제거해요.

예를 들어 서버가 petstore_mcp이면 도구는 petstore_mcp-getPetById로 노출돼요. 맵 키는 여전히 getPetById예요.

재정의 전후 (Before and after):

# Without overrides
Tool: "petstore_mcp-getPetById"
Description: "Returns a single pet"

Tool: "petstore_mcp-findPetsByStatus"
Description: "Finds Pets by status"

# After overrides
Tool: "Get Pet"
Description: "Look up a pet by its ID"

Tool: "List Available Pets"
Description: "Returns all pets matching a given status (available, pending, sold)"

서버 사용 (Using the Server)

Python FastMCP:

from fastmcp import Client
import asyncio

config = {
    "mcpServers": {
        "petstore": {
            "url": "http://localhost:4000/petstore_mcp/mcp",
            "headers": {
                "x-litellm-api-key": "Bearer sk-<your-litellm-api-key>"
            }
        }
    }
}

client = Client(config)

async def main():
    async with client:
        tools = await client.list_tools()
        print(f"Available tools: {[tool.name for tool in tools]}")

        response = await client.call_tool(
            name="Get Pet",        # overridden name
            arguments={"petId": "1"}
        )
        print(f"Response: {response}")

if __name__ == "__main__":
    asyncio.run(main())

Cursor IDE:

{
  "mcpServers": {
    "Petstore": {
      "url": "http://localhost:4000/petstore_mcp/mcp",
      "headers": {
        "x-litellm-api-key": "Bearer sk-<your-litellm-api-key>"
      }
    }
  }
}

OpenAI Responses API:

curl --location 'https://api.openai.com/v1/responses' \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ***" \
--data '{
    "model": "gpt-5.6-terra",
    "tools": [
        {
            "type": "mcp",
            "server_label": "petstore",
            "server_url": "http://localhost:4000/petstore_mcp/mcp",
            "require_approval": "never",
            "headers": {
                "x-litellm-api-key": "Bearer YOUR_LITELLM_API_KEY"
            }
        }
    ],
    "input": "Find all available pets",
    "tool_choice": "required"
}'

더 알아보기 (Learn more)