https://demo.makemyproject.net/marketplaceAuthorization: Bearer <토큰> 헤더를 보냅니다. UI에서는 우상단 🔑 토큰에서 확인/복사.개념① 서버 등록(공급자) ② 게이트웨이로 사용(소비자)③ 상태/장애 에러 코드
마켓플레이스는 등록 서버(Registry)이자 프록시 게이트웨이(Gateway)입니다.
서버id__도구명으로 네임스페이스됩니다.| 형태 | 주소 | 도구 이름 | 언제 |
|---|---|---|---|
| 통합 | https://demo.makemyproject.net/marketplace/mcp/consumers/<컨슈머id> |
서버id__도구명 | 여러 서버를 한 에이전트로 묶을 때 (보통 이걸 씀) |
| 개별 | https://demo.makemyproject.net/marketplace/mcp/servers/<namespace>/<서버id>(namespace 생략한 /mcp/servers/<서버id> 도 동작) |
원래 이름 그대로 | 특정 서버 하나만 쓸 때 |
먼저 평범한 MCP 서버를 만듭니다. (공식 mcp SDK의 FastMCP 예시)
# my_server.py — pip install mcp / 실행: python my_server.py
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("My MCP", host="0.0.0.0", port=8101)
@mcp.tool()
def greet(name: str) -> str:
"""이름을 받아 인사한다."""
return f"안녕하세요, {name}님!"
if __name__ == "__main__":
mcp.run(transport="streamable-http") # → http://<내주소>:8101/mcp
그다음 마켓플레이스에 등록합니다. 🖥️ MCP 서버 페이지의 “새 MCP 서버 등록” 폼을 쓰거나, API로:
curl -X POST https://demo.makemyproject.net/marketplace/api/servers \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <공유토큰>" \
-d '{
"id": "myteam-greet",
"name": "인사 서버",
"owner": "4조",
"namespace": "myteam",
"endpoint": "http://my-public-host:8101/mcp",
"description": "이름을 받아 인사"
}'
등록하면 마켓플레이스가 즉시 list_tools를 호출해 도구·스키마를 자동 수집합니다.
도구를 바꿨다면 UI의 “도구 새로고침” 또는 POST /api/servers/<id>/refresh.
👤 컨슈머 페이지에서 컨슈머를 만들고, 쓰고 싶은 서버들을 구독 체크하세요. 그러면 아래 한 줄이면 끝입니다.
# pip install mcp langchain langchain-openai langchain-mcp-adapters langgraph requests
# export OPENAI_API_KEY=sk-... ← 본인 OpenAI 키
import asyncio, requests
from langchain_openai import ChatOpenAI
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent
MARKET = "https://demo.makemyproject.net/marketplace"
CONSUMER = "my-agent" # 내 컨슈머 id (원하는 이름)
TOKEN = "<공유토큰>" # 컨슈머 생성/구독(관리)에만 필요. 게이트웨이 접속엔 불필요.
H = {"Authorization": f"Bearer {TOKEN}"}
def setup():
"""컨슈머가 없으면 만들고, 구독이 비어 있으면 등록된 서버 전체를 구독한다(관리=토큰 필요)."""
s = requests.get(f"{MARKET}/api/consumers/{CONSUMER}/subscriptions")
if s.status_code == 404:
requests.post(f"{MARKET}/api/consumers", json={"id": CONSUMER, "name": CONSUMER}, headers=H)
s = requests.get(f"{MARKET}/api/consumers/{CONSUMER}/subscriptions")
if not s.json():
ids = [x["id"] for x in requests.get(f"{MARKET}/api/servers").json()]
requests.put(f"{MARKET}/api/consumers/{CONSUMER}/subscriptions",
json={"server_ids": ids}, headers=H)
async def main():
setup()
# 게이트웨이는 토큰 불필요 — 그냥 붙는다
client = MultiServerMCPClient({"market": {
"url": f"{MARKET}/mcp/consumers/{CONSUMER}", "transport": "streamable_http"}})
tools = await client.get_tools() # 구독한 모든 서버의 도구 (서버id__도구명)
print("쓸 수 있는 도구:", [t.name for t in tools])
agent = create_agent(ChatOpenAI(model="gpt-4o-mini"), tools)
res = await agent.ainvoke({"messages": [("user", "도쿄 여행 상품 검색해줘")]})
print(res["messages"][-1].content)
asyncio.run(main())
구독은 서버 단위입니다(도구 단위 선택은 ④처럼 클라이언트에서 거릅니다).
아래 네 토막은 모두 같은 변수(MARKET·CONSUMER·H)를 앞에 둔다고 가정합니다.
# 공통 — 아래 네 예제 앞에 한 번만 둔다. pip install requests
import requests
MARKET = "https://demo.makemyproject.net/marketplace"
CONSUMER = "my-agent" # 내 컨슈머 id (원하는 이름)
TOKEN = "<공유토큰>" # 등록/구독(관리)에만 필요. 게이트웨이 접속엔 불필요.
H = {"Authorization": f"Bearer {TOKEN}"}
SUBS = f"{MARKET}/api/consumers/{CONSUMER}/subscriptions"
① 내 앱(서버)을 등록 — 켜둔 MCP 서버 주소를 마켓에 알린다. 등록 즉시 도구가 자동 수집된다.
requests.post(f"{MARKET}/api/servers", headers=H, json={
"id": "myteam-greet", "name": "인사 서버", "owner": "4조",
"namespace": "myteam", "endpoint": "http://my-public-host:8101/mcp"})
# 도구를 바꾼 뒤 다시 수집: POST {MARKET}/api/servers/myteam-greet/refresh
② 모두 구독 — 등록된 서버 전부를 구독한다.
requests.post(f"{MARKET}/api/consumers", json={"id": CONSUMER, "name": CONSUMER}, headers=H) # 없으면 생성(있어도 안전)
ids = [s["id"] for s in requests.get(f"{MARKET}/api/servers").json()]
requests.put(SUBS, json={"server_ids": ids}, headers=H)
③ 원하는 서버만 구독 — id 목록으로 구독을 통째 교체한다.
requests.post(f"{MARKET}/api/consumers", json={"id": CONSUMER, "name": CONSUMER}, headers=H)
requests.put(SUBS, json={"server_ids": ["weather", "travel"]}, headers=H) # ← 원하는 id 만
④ 원하는 서버 안에서 원하는 도구만 — 구독은 서버 단위라, 도구는 클라이언트에서 거른다.
게이트웨이는 도구 이름을 서버id__도구명 으로 준다.
# ③으로 weather·travel 을 구독해 둔 상태라고 하자. (async 함수 안에서)
from langchain_mcp_adapters.client import MultiServerMCPClient
client = MultiServerMCPClient({"market": {
"url": f"{MARKET}/mcp/consumers/{CONSUMER}", "transport": "streamable_http"}})
all_tools = await client.get_tools() # 구독한 서버의 모든 도구
WANT = {"weather__get_weather", "travel__search_trips"} # 실제로 쓸 도구만
tools = [t for t in all_tools if t.name in WANT]
# 이 tools 만 에이전트에 넘기면 나머지 도구는 LLM 에게 보이지 않는다.
GET …/subscriptions) 응답은 서버 객체 목록이라 id 만 뽑아 씁니다([s["id"] for s in …]).
구독을 추가만 하려면 현재 id 에 합쳐 보내고
(cur = requests.get(SUBS).json() → server_ids = list({s["id"] for s in cur} | {"shopping"})),
server_ids: [] 는 전체 해지입니다. ④의 도구 단위 선택은 서버에 저장되지 않고 매번 클라이언트가 거릅니다.# pip install mcp requests
import asyncio, requests
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
MARKET = "https://demo.makemyproject.net/marketplace"
CONSUMER = "my-agent"
TOKEN = "<공유토큰>" # 컨슈머 생성/구독(관리)에만 필요 (🔑 토큰에서 복사)
def setup():
"""컨슈머가 없으면 만들고, 구독이 비었으면 등록된 서버 전체를 구독한다."""
H = {"Authorization": f"Bearer {TOKEN}"}
s = requests.get(f"{MARKET}/api/consumers/{CONSUMER}/subscriptions")
if s.status_code == 404:
requests.post(f"{MARKET}/api/consumers", json={"id": CONSUMER, "name": CONSUMER}, headers=H)
s = requests.get(f"{MARKET}/api/consumers/{CONSUMER}/subscriptions")
if not s.json():
ids = [x["id"] for x in requests.get(f"{MARKET}/api/servers").json()]
requests.put(f"{MARKET}/api/consumers/{CONSUMER}/subscriptions",
json={"server_ids": ids}, headers=H)
async def main():
setup() # ← 이게 없으면 게이트웨이가 404(=Session terminated)
async with streamablehttp_client(f"{MARKET}/mcp/consumers/{CONSUMER}") as (r, w, _):
async with ClientSession(r, w) as s: # 게이트웨이 접속 — 토큰 불필요
await s.initialize()
tools = (await s.list_tools()).tools
print("도구:", [t.name for t in tools])
out = await s.call_tool("weather__get_weather", {"city": "서울"}) # 데모 도구 예시
print(out.content[0].text)
asyncio.run(main())
https://demo.makemyproject.net/marketplace/mcp/servers/<서버id>에 붙고,
도구 이름은 네임스페이스 없이 greet 처럼 원래 이름 그대로입니다.마켓플레이스는 등록된 서버를 주기적으로 폴링합니다. 서버가 죽어도 등록 정보는 지우지 않고 상태만 바꿉니다.
| 상태 | 의미 |
|---|---|
ONLINE | 최근 응답함 — 정상 중계 |
UNHEALTHY | 잠시 응답이 늦음 — 아직 중계 시도 |
OFFLINE | 오래 응답 없음 — 호출 시 즉시 SERVER_OFFLINE 반환(타임아웃 X) |
ARCHIVED | 아주 오래 죽어 있음 — 목록에서 흐리게 표시, 메타는 보존 |
폴링과 별개로, 서버가 직접 생존 신고를 할 수도 있습니다(선택):
curl -X POST https://demo.makemyproject.net/marketplace/heartbeat \
-H "Content-Type: application/json" -H "Authorization: Bearer <공유토큰>" \
-d '{"server_id": "myteam-greet"}'
호출이 실패하면 게이트웨이는 도구 결과 텍스트로 JSON 에러를 돌려줍니다.
| error | 의미 |
|---|---|
NOT_SUBSCRIBED | 그 컨슈머가 해당 서버를 구독하지 않음 → UI에서 구독 체크 |
SERVER_OFFLINE | 대상 서버가 OFFLINE/ARCHIVED 상태 |
UPSTREAM_ERROR | 서버는 살아있다고 보였으나 실제 호출이 실패 |
UNKNOWN_SERVER / BAD_TOOL_NAME | 없는 서버 / 잘못된 도구 이름 형식 |
그 외 HTTP 레벨: 401 UNAUTHORIZED(토큰 없음/오류),
400 ENDPOINT_REJECTED(SSRF 차단된 endpoint).