MCP Servers

์›๋ฌธ: https://platform.openai.com/docs/mcp

Tldr

์‚ฌ์šฉ์ž๋Š” ํšŒ์‚ฌ ์ง€์‹์„ ํ™œ์šฉํ•˜์—ฌ ChatGPT๋ฅผ ๋งž์ถคํ™”ํ•˜๊ธฐ ์œ„ํ•ด OpenAI์˜ ๊ฐœ๋ฐฉํ˜• ํ”„๋กœํ† ์ฝœ์ธ ๋ชจ๋ธ ์ปจํ…์ŠคํŠธ ํ”„๋กœํ† ์ฝœ(MCP) ๊ธฐ๋ฐ˜์˜ ์›๊ฒฉ ์„œ๋ฒ„๋ฅผ ์ง์ ‘ ๊ตฌ์ถ•ํ•˜๊ณ  ChatGPT์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ๊ณผ์ •์—์„œ ์•ˆ์ „ ๋ฐ ๋ณด๊ฒฝ ๊ณ ๋ ค์‚ฌํ•ญ์„ ๋ฐ˜๋“œ์‹œ ์ดํ•ดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • MCP ์„œ๋ฒ„๋Š”ย ๊ฒ€์ƒ‰ย ๋ฐย ๊ฐ€์ ธ์˜ค๊ธฐย ๋„๊ตฌ๋ฅผ ํ†ตํ•ด ChatGPT์˜ ์‹ฌ์ธต ์—ฐ๊ตฌ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค.
  • ์„œ๋ฒ„ ๊ตฌ์ถ• ์‹œ ==OAuth ๋ฐ ๋™์  ํด๋ผ์ด์–ธํŠธ ๋“ฑ๋ก์„ ํ†ตํ•œ ์ธ์ฆ, ๊ทธ๋ฆฌ๊ณ  ์ธํŠธ๋ผ๋„ท ํ™˜๊ฒฝ์—์„œ๋Š” ํ„ฐ๋„๋ง(์˜ˆ: ngrok)==์ด ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.
  • ์‹ ๋ขฐํ•  ์ˆ˜ ์—†๋Š” MCP ์„œ๋ฒ„ ์—ฐ๊ฒฐ์€ ์ˆจ๊ฒจ์ง„ ์ง€์นจ(ํ”„๋กฌํ”„ํŠธ ์ธ์ ์…˜)์ด๋‚˜ ์•…์˜์ ์ธ ๋™์ž‘์„ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์—ฐ๊ฒฐ ์ „ ์ฒ ์ €ํ•œ ์‹ค์‚ฌ์™€ ๋ฐ์ดํ„ฐ ๊ณต์œ  ์œ„ํ—˜ ๊ฒ€ํ† ๊ฐ€ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

MCP ์„œ๋ฒ„

ํšŒ์‚ฌ ์ง€์‹์„ ํ™œ์šฉํ•˜์—ฌ ChatGPT๋ฅผ ๋งž์ถคํ™”ํ•˜๋ ค๋ฉด ์‚ฌ์šฉ์ž ์ง€์ • ์›๊ฒฉ MCP ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์—ฐ๊ฒฐํ•˜์„ธ์š”.

๋ชจ๋ธ ์ปจํ…์ŠคํŠธ ํ”„๋กœํ† ์ฝœ(MCP)์€ AI ๋ชจ๋ธ์„ ์ถ”๊ฐ€ ๋„๊ตฌ ๋ฐ ์ง€์‹์œผ๋กœ ํ™•์žฅํ•˜๋Š” ๋ฐ ์—…๊ณ„ ํ‘œ์ค€์ด ๋˜๊ณ  ์žˆ๋Š” ๊ฐœ๋ฐฉํ˜• ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ๋ฐ์ดํ„ฐ ์†Œ์Šค ์œ„์— MCP ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฐ€์ด๋“œ์—์„œ๋Š” ChatGPT์—์„œ ์‚ฌ์šฉํ•  ๊ธฐ๋ณธ์ ์ธ ์›๊ฒฉ MCP ์„œ๋ฒ„๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

์ž‘๋™ ๋ฐฉ์‹

๋…์  ์‹œ์Šคํ…œ์„ ํฌํ•จํ•œ ๋ชจ๋“  ๋„๊ตฌ๋ฅผ ChatGPT์˜ ์‹ฌ์ธต ์—ฐ๊ตฌ ๊ธฐ๋Šฅ์— ์—ฐ๊ฒฐํ•˜์—ฌ, ์ง์›๋“ค์ด ChatGPT์—์„œ ํšŒ์‚ฌ ์ง€์‹์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ์ ˆ์ฐจ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. ๊ฒ€์ƒ‰(search) ๋„๊ตฌ์™€ ๊ฐ€์ ธ์˜ค๊ธฐ(fetch) ๋„๊ตฌ๋ฅผ ๋…ธ์ถœํ•˜์—ฌ ์‹ฌ์ธต ์—ฐ๊ตฌ์— ์ตœ์ ํ™”๋œ MCP ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค.
  2. ChatGPT์—์„œ ์‚ฌ์šฉ์ž ์ง€์ • ์‹ฌ์ธต ์—ฐ๊ตฌ ์ปค๋„ฅํ„ฐ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  3. ChatGPT๊ฐ€ ์„œ๋น„์Šค์™€ ํšจ๊ณผ์ ์œผ๋กœ ์ƒํ˜ธ ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ปค๋„ฅํ„ฐ ์„ค์ •์— ์ž์„ธํ•œ ์‚ฌ์šฉ ์ง€์นจ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.
  4. ChatGPT์—์„œ ์ง์ ‘ ์ปค๋„ฅํ„ฐ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ณ  ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค.
  5. ์„ ํƒ์ ์œผ๋กœ (ChatGPT Enterprise, Edu ๋˜๋Š” Team ๊ด€๋ฆฌ์ž์˜ ๊ฒฝ์šฐ), ์ปค๋„ฅํ„ฐ๋ฅผ ์ „์ฒด ์ž‘์—… ๊ณต๊ฐ„์— ๊ฒŒ์‹œํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์‹ฌ์ธต ์—ฐ๊ตฌ(deep research)์—์„œ ์ถ”๊ฐ€์ ์ธ ์ง€์‹ ์†Œ์Šค๋กœ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.
์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ MCP ์›๊ฒฉ ์„œ๋ฒ„์— ์ ‘๊ทผํ•˜๊ณ  ์‹ถ์œผ์‹ ๊ฐ€์š”?

์ง€๊ธˆ ์ฝ๊ณ  ๊ณ„์‹  ๊ฐ€์ด๋“œ๋Š” ChatGPT์— ์—ฐ๊ฒฐํ•  ์›๊ฒฉ MCP ์„œ๋ฒ„๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. LLM ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋„๊ตฌ ๋„คํŠธ์›Œํฌ์— API ์ ‘๊ทผ์„ ํ•˜๋ ค๋ฉด, ๋ชจ๋ธ์ด MCP ์›๊ฒฉ ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์„ธ์š”.

MCP ์ƒํƒœ๊ณ„

MCP ์ƒํƒœ๊ณ„๋Š” ์•„์ง ์ดˆ๊ธฐ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ์ธ๊ธฐ ์žˆ๋Š” ์›๊ฒฉ MCP ์„œ๋ฒ„๋กœ๋Š” Cloudflare, HubSpot, Intercom, PayPal, Pipedream, Plaid, Shopify, Stripe, Square, Twilio, Zapier ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์•ž์œผ๋กœ ๋” ๋งŽ์€ ์„œ๋ฒ„์™€ ์ด๋Ÿฌํ•œ ์„œ๋ฒ„๋ฅผ ์‰ฝ๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š” ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋“ค์ด ์ถœ์‹œ๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋ฉ๋‹ˆ๋‹ค. MCP ํ”„๋กœํ† ์ฝœ ์ž์ฒด๋„ ์ดˆ๊ธฐ ๋‹จ๊ณ„์ด๋ฉฐ, ํ”„๋กœํ† ์ฝœ์ด ๋ฐœ์ „ํ•จ์— ๋”ฐ๋ผ MCP ๋„๊ตฌ์— ๋” ๋งŽ์€ ์—…๋ฐ์ดํŠธ๋ฅผ ์ถ”๊ฐ€ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ง€์ • ์›๊ฒฉ MCP ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ•˜๊ธฐ ์ „์— ์œ„ํ—˜ ๋ฐ ์•ˆ์ „ ์ •๋ณด๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

MCP ์„œ๋ฒ„ ๊ตฌ์ถ•

์•„์ง MCP์— ์ต์ˆ™ํ•˜์ง€ ์•Š๋‹ค๋ฉด, MCP ์†Œ๊ฐœ๋ฅผ ์ฝ์–ด๋ณด์„ธ์š”. ์„ ํ˜ธํ•˜๋Š” ๋„๊ตฌ ๋ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๊ฐ„๋‹จํ•œ ์„œ๋ฒ„ ์ง€์นจ์„ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ๋ช‡ ๊ฐ€์ง€ ์ž๋ฃŒ์ž…๋‹ˆ๋‹ค:

๊ธฐ๋ณธ์ ์ธ ์›๊ฒฉ ์„œ๋ฒ„ ์„ค์ •

์‹œ์ž‘์ ์œผ๋กœ, GitHub์˜ ์‹ฌ์ธต ์—ฐ๊ตฌ MCP ์„œ๋ฒ„ ์ƒ˜ํ”Œ ์•ฑ์„ ์‚ฌ์šฉํ•˜์„ธ์š”. ์ด ์ตœ์†Œํ•œ์˜ ์˜ˆ์ œ๋Š” ์ปต์ผ€์ดํฌ ์ฃผ๋ฌธ์„ ๊ฒ€์ƒ‰ํ•˜๊ณ  ๊ฐ€์ ธ์˜ค๋Š” ์›๊ฒฉ MCP ์„œ๋ฒ„๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

  1. ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ํด๋ก ํ•˜๊ฑฐ๋‚˜ ํŒŒ์ผ์„ ๊ธฐ์กด ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.
  2. ์„œ๋ฒ„๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. Python์—์„œ๋Š” ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
    python -m venv env
    source env/bin/activate
    pip install -r requirements.txt
    
  3. ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. SSE ์ „์†ก์„ ์‚ฌ์šฉํ•˜์—ฌ http://127.0.0.1:8000์—์„œ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.
    python sample_mcp.py
    
  4. ํ•„์š”ํ•œ ์‚ฌ์šฉ์ž ์ง€์ • ์‚ฌํ•ญ์œผ๋กœ ์ƒ˜ํ”Œ ํŒŒ์ผ์„ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค:
    • sample_mcp.py๋Š” ๋ฉ”์ธ ์„œ๋ฒ„ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.
    • records.json์€ ์ปต์ผ€์ดํฌ ์ฃผ๋ฌธ ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค (๋™์ผํ•œ ๋””๋ ‰ํ† ๋ฆฌ์— ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค).

MCP ์„œ๋ฒ„๋Š” ์—ฌ๋Ÿฌ ๋„๊ตฌ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ChatGPT์—์„œ MCP ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์‹ฌ์ธต ์—ฐ๊ตฌ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๊ท€ํ•˜์˜ MCP ์›๊ฒฉ ์„œ๋ฒ„๊ฐ€ ๊ฒ€์ƒ‰ ๋ฐ ๋ฌธ์„œ ๊ฒ€์ƒ‰ ๋„๊ตฌ๋ฅผ ๊ฐ–์ถ˜ ๊ฒ€์ƒ‰ ์—”์ง„๊ณผ ์œ ์‚ฌํ•ด์•ผ ํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๊ฒ€์ƒ‰ ์„ค์ •

๊ฒ€์ƒ‰ ๋„๊ตฌ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ ์ปต์ผ€์ดํฌ ์ฃผ๋ฌธ ์˜ˆ์ œ์—์„œ๋Š” ์ฝ”๋“œ๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

def create_server():
    mcp = FastMCP(name="Cupcake MCP", instructions="Search cupcake orders")

    @mcp.tool()
    async def search(query: str):
        """
        Search for cupcake orders โ€“ keyword match.
        """
        toks = query.lower().split()
        ids = []
        for r in RECORDS:
            hay = " ".join(
                [
                    r.get("title", ""),
                    r.get("text", ""),
                    " ".join(r.get("metadata", {}).values()),
                ]
            ).lower()
            if any(t in hay for t in toks):
                ids.append(r["id"])
        return {"ids": ids}

๊ฒ€์ƒ‰ ์˜๋ฏธ๋ก 

์ด ๋„๊ตฌ๋ฅผ ์ •์˜ํ•˜๊ณ  MCP ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด ๋…ธ์ถœํ•˜๊ธฐ ์œ„ํ•œ ๊ฒ€์ƒ‰ ์˜๋ฏธ๋ก ์€ ์ต์ˆ™ํ•œ ๋ฐฉ์‹๊ณผ ์•ฝ๊ฐ„ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ์ „์ฒด ์‚ฌ์–‘์ž…๋‹ˆ๋‹ค:

{
  "tools": [
    {
      "name": "search",
      "description": "Searches for resources using the provided query string and returns matching results.",
      "input_schema": {
        "type": "object",
        "properties": {
          "query": {"type": "string", "description": "Search query."}
        },
        "required": ["query"]
      },
      "output_schema": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {"type": "string", "description": "ID of the resource."},
                "title": {"type": "string", "description": "Title or headline of the resource."},
                "text": {"type": "string", "description": "Text snippet or summary from the resource."},
                "url": {"type": ["string", "null"], "description": "URL of the resource. Optional but needed for citations to work."},
              },
              "required": ["id", "title", "text"]
            }
          }
        },
        "required": ["results"]
      }
    }
  ]
}

๋ชจ๋ธ์— ์œ ํšจํ•œ ์ฟผ๋ฆฌ ๊ตฌ์„ฑ ๋ฐฉ๋ฒ• ๊ต์œก

์œ„ ์‚ฌ์–‘์—์„œ description ํ•„๋“œ๋Š” ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•„๋“œ์—์„œ ์‹ฌ์ธต ์—ฐ๊ตฌ ๋ชจ๋ธ์—๊ฒŒ ์œ ํšจํ•œ ๊ฒ€์ƒ‰ ์ฟผ๋ฆฌ๋ฅผ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์ด ๋„๊ตฌ๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”์ง€ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด, ์›๊ฒฉ MCP ์„œ๋ฒ„๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•˜๋‚˜์˜ ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด๋กœ ํ‘œํ˜„๋˜๋Š” ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ์˜ ๋ณต์žกํ•œ ์ฟผ๋ฆฌ ๊ตฌ๋ฌธ์„ ์ง€์›ํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

type:deals amount:gt:1000

์ด๋Ÿฌํ•œ ์ข…๋ฅ˜์˜ ์ฟผ๋ฆฌ ๊ตฌ๋ฌธ์„ ํ™œ์„ฑํ™”ํ•˜๋ ค๋ฉด descriptionใ€‹ํ•„๋“œ๊ฐ€ ๋ชจ๋ธ์—๊ฒŒ ์ด ์‚ฌ์šฉ์ž ์ž…๋ ฅ์—์„œ ์œ ํšจํ•œ ์ฟผ๋ฆฌ๋ฅผ ํ˜•์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. OpenAI๋Š” ์‹ฌ์ธต ์—ฐ๊ตฌ ๋ชจ๋ธ์ด ์ด ๊ฒ€์ƒ‰ ๋„๊ตฌ๋ฅผ ํ˜ธ์ถœํ•  ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด์„ ํ˜•์„ฑํ•˜๋„๋ก ํ”„๋กฌํ”„ํŠธํ•˜๋Š” ๋ฐ descriptionใ€‹ํ•„๋“œ์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค.
์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ‘œํ˜„๋ ฅ ์žˆ๋Š” ์ฟผ๋ฆฌ๋ฅผ ๊ตฌ์ถ•ํ•˜์„ธ์š”. ์‹ค์ œ ์˜ˆ์‹œ๋กœ, ๋‹ค์Œ์€ HubSpot์˜ ๊ฒ€์ƒ‰ ๋„๊ตฌ ์„ค๋ช…์ž…๋‹ˆ๋‹ค.

Purpose:\\n  1. Search for resources in the HubSpot CRM of a specific object type (only contacts, deals, companies, tickets are supported).\\n  2. Note that only a subset of the properties will be returned.\\n  3. For the complete set of properties, use the Fetch tool.\\n\\nUsage:\\n  1. List a few objects to understand the data model of a specific object type.\\n  2. Search for objects of a specific object type using filters.\\n  3. Make sure to use the offset from the prior search result to call the tool again to paginate through the entire list.\\n  4. Search for associated objects.\\n\\nSearch Tool Response:\\n  1. The Search Tools response contains an idfor each attribute. Note that thisidis not the same as thehs_object_idof the object.\\n  2. Theidcan ONLY be used to fetch the object metadata using the Fetch tool. Use thehs_object_id when creating search queries involving specific objects.\\n', is_consequential=True, params={'type': 'object', 'properties': {'query': {'type': 'string', 'description': 'HubSpot Search Query DSL specification.\\n\\n โ€ข Tokens separated by spaces\\n โ€ข Each token: key[:op]:value\\n   โ€“ key โˆˆ { object_type, q|query, any HubSpot CRM property name, associated_{object_type} , limit, offset, sort }\\n   โ€“ op (optional; default โ€œeqโ€) โˆˆ { eq, neq, gt, gte, lt, lte, in, not_in, contains_token, not_contains_token, has_property, not_has_property }\\n   โ€“ value:\\n     โ€ข unquoted (alphanumeric, no spaces)\\n     โ€ข or quoted in single/double quotes (to include spaces)\\n     โ€ข for in/not_in: comma-separated list (no spaces)\\n โ€ข Semantics:\\n   โ€“ object_type (mandatory) โ†’ which object to search (only contacts, deals, companies & tickets supported)\\n   โ€“ q/query โ†’ free-text search on a few key fields unique for each object type.\\n   โ€“ propertyName[:op]:value โ†’ filters, all ANDโ€™d in one group\\n   โ€“ associated_{object_type}:id โ†’ search for objects associated with the specified object type (e.g. associated_contacts:<hs_object_id of contact>).\\n      You can also use the associated_{object_type} key with in, not_in, eq and neq operators. For example, associated_contacts:in:123,456. No other format is supported.\\n   โ€“ limit, offset โ†’ integers. offset can help paginate through results, and will be returned in the search API response if more items exist.\\n   โ€“ sort:property[:asc|:desc] (default asc)\\n\\n โ€ข Searching for associated objects to a given object requires knowing its hs_object_id. If the given object\\'s hs_object_id is not known,\\\\n   then search must be done in two steps:\\n     1. Search for the given object to get its hs_object_id (e.g. for contact, use a query like: object_type:contacts email:someone@example.com )\\n     2. Search for the associated objects using that hs_object_id ( e.g. for the contact with hs_object_id 123, use the filter: associated_contacts:123 )\\n\\n โ€ข The q/query attribute searches across a few key fields unique for each object type:\\n  โ€“ Contacts: firstname, lastname, email, phone, company, hs_additional_emails, fax\\n  โ€“ Companies: name, website, domain, phone\\n  โ€“ Deals: dealname, pipeline, dealstage, description, dealtype\\n  โ€“ Tickets: subject, content, hs_pipeline_stage, hs_ticket_category, hs_ticket_id\\n\\n EXAMPLES:\\n\\n user: show me my deals\\n query: object_type:deals\\n\\n user: show me all contacts containing \\'John Doe\\'\\n query: object_type:contacts q:\\'John Doe\\'\\n\\n user: Show me the 5 most recently modified contacts.\\n query: object_type:contacts limit:5 offset:0 sort:lastmodifieddate:desc\\n\\n user: Find contacts whose email contains โ€œ@example.comโ€, sorted by the last contacted date.\\n query: object_type:contacts email:contains_token:\"@example.com\" limit:20 offset:0 sort:lastcontacted:desc\\n\\n user: List marketing qualified and sales qualified leads in the U.S.\\n query: object_type:contacts lifecyclestage:in:marketingqualifiedlead,salesqualifiedlead country:US\\n\\n user: Retrieve deals for owner 12345 in Q1 2025 but exclude โ€œClosed Lostโ€.\\n query: object_type:deals hubspot_owner_id:12345 dealstage:not_in:closedlost closedate:gte:2025-01-01 closedate:lte:2025-03-31 limit:100 offset:0 sort:closedate:asc\\n\\n user: Find tech companies with annual revenue between $1,000,000 and $10,000,000.\\n query: object_type:companies industry:Technology annualrevenue:gte:1000000 annualrevenue:lte:10000000 limit:100 offset:0 sort:annualrevenue:desc\\n\\n user: List contacts that have a phone number defined but no website.\\n query: object_type:contacts phone:has_property website:not_has_property limit:100 offset:0 sort:createdate:asc\\n\\n user: Find companies whose description does not contain โ€œstartupโ€\\n query: object_type:companies description:not_contains_token:\"startup\"\\n\\n user: Find tickets mentioning \\'refund\\'\\n query: object_type:tickets q:\\'refund\\'\\n\\n user: Find all contacts associated with company hs_object_id 12345.\\n query: object_type:contacts associated_companies:12345 limit:100 offset:0 sort:createdate:desc\\n\\n user: Show me deals not associated with contact hs_object_id 54321.\\n query: object_type:deals associated_contacts:neq:54321 limit:100 offset:0 sort:amount:desc\\n\\n user: Find contacts associated with deals with hs_object_ids in 24680,24681 who are in California.\\n query: object_type:contacts associated_deals:in:24680,24681 state:CA limit:100 offset:0\\n\\n Unsupported features:\\n   โ€ข Boolean logic: NO OR, NOT, AND keywords or parenthesis grouping\\n   โ€ข Relative dates: NO โ€œnowโ€“7dโ€, โ€œtodayโ€, โ€œlast weekโ€ syntax\\n   โ€ข Aggregations / metrics: NO count, sum, facet, groupโ€by\\n   โ€ข Fuzzy or proximity: NO โ€œ~2โ€ fuzzy match operators\\n   โ€ข Nested expressions or sub-queries\\n   โ€ข Custom functions or scripts\\n   โ€ข Escaping beyond simple single/double quotes\\n   โ€ข No support for searching for objects without associations directly. You must list all objects and see if they have associations from contacts, companies, or deals.'}}, 'required': ['query'], 'additionalProperties': False}, return_type={'$defs': {'Annotations': {'additionalProperties': True, 'properties': {'audience': {'anyOf': [{'items': {'enum': ['user', 'assistant'], 'type': 'string'}, 'type': 'array'}, {'type': 'null'}], 'default': None, 'title': 'Audience'}, 'priority': {'anyOf': [{'maximum': 1.0, 'minimum': 0.0, 'type': 'number'}, {'type': 'null'}], 'default': None, 'title': 'Priority'}}, 'title': 'Annotations', 'type': 'object'}, 'BlobResourceContents': {'additionalProperties': True, 'description': 'Binary contents of a resource.', 'properties': {'uri': {'format': 'uri', 'minLength': 1, 'title': 'Uri', 'type': 'string'}, 'mimeType': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'default': None, 'title': 'Mimetype'}, 'blob': {'title': 'Blob', 'type': 'string'}}, 'required': ['uri', 'blob'], 'title': 'BlobResourceContents', 'type': 'object'}, 'EmbeddedResource': {'additionalProperties': True, 'description': 'The contents of a resource, embedded into a prompt or tool call result.\\n\\nIt is up to the client how best to render embedded resources for the benefit\\nof the LLM and/or the user.', 'properties': {'type': {'const': 'resource', 'title': 'Type', 'type': 'string'}, 'resource': {'anyOf': [{'$ref': '#/$defs/TextResourceContents'}, {'$ref': '#/$defs/BlobResourceContents'}], 'title': 'Resource'}, 'annotations': {'anyOf': [{'$ref': '#/$defs/Annotations'}, {'type': 'null'}], 'default': None}}, 'required': ['type', 'resource'], 'title': 'EmbeddedResource', 'type': 'object'}, 'ImageContent': {'additionalProperties': True, 'description': 'Image content for a message.', 'properties': {'type': {'const': 'image', 'title': 'Type', 'type': 'string'}, 'data': {'title': 'Data', 'type': 'string'}, 'mimeType': {'title': 'Mimetype', 'type': 'string'}, 'annotations': {'anyOf': [{'$ref': '#/$defs/Annotations'}, {'type': 'null'}], 'default': None}}, 'required': ['type', 'data', 'mimeType'], 'title': 'ImageContent', 'type': 'object'}, 'TextContent': {'additionalProperties': True, 'description': 'Text content for a message.', 'properties': {'type': {'const': 'text', 'title': 'Type', 'type': 'string'}, 'text': {'title': 'Text', 'type': 'string', 'title': 'Text', 'type': 'string'}, 'annotations': {'anyOf': [{'$ref': '#/$defs/Annotations'}, {'type': 'null'}], 'default': None}}, 'required': ['type', 'text'], 'title': 'TextContent', 'type': 'object'}, 'TextResourceContents': {'additionalProperties': True, 'description': 'Text contents of a resource.', 'properties': {'uri': {'format': 'uri', 'minLength': 1, 'title': 'Uri', 'type': 'string'}, 'mimeType': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'default': None, 'title': 'Mimetype'}, 'text': {'title': 'Text', 'type': 'string'}}, 'required': ['uri', 'text'], 'title': 'TextResourceContents', 'type': 'object'}}, 'additionalProperties': True, 'description': "The server's response to a tool call.", 'properties': {'_meta': {'anyOf': [{'additionalProperties': True, 'type': 'object', 'default': None, 'title': 'Meta'}, 'content': {'items': {'anyOf': [{'$ref': '#/$defs/TextContent'}, {'$ref': '#/$defs/ImageContent'}, {'$ref': '#/$defs/EmbeddedResource'}]}, 'title': 'Content', 'type': 'array'}, 'isError': {'default': False, 'title': 'Iserror', 'type': 'boolean'}}, 'required': ['content'], 'title': 'CallToolResult', 'type': 'object'}


### ๋ฌธ์„œ ๊ฒ€์ƒ‰ ์„ค์ •
๋ฌธ์„œ ๊ฒ€์ƒ‰ ๋„๊ตฌ๋Š” ์ธ์šฉ์„ ํ™œ์„ฑํ™”ํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค. ์ปต์ผ€์ดํฌ ์ฃผ๋ฌธ ์˜ˆ์ œ์—์„œ ๋ฌธ์„œ ๊ฒ€์ƒ‰ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

```python
@mcp.tool()
async def fetch(id: str):
    """
    Fetch a cupcake order by ID.
    """
    if id not in LOOKUP:
        raise ValueError("unknown id")
    return LOOKUP[id]

return mcp

๋‹ค์Œ์€ ์ „์ฒด ์‚ฌ์–‘์ž…๋‹ˆ๋‹ค:

{
  "tools": [
    {
      "name": "fetch",
      "description": "Retrieves detailed content for a specific resource identified by the given ID.",
      "input_schema": {
        "type": "object",
        "properties": {
          "id": {"type": "string", "description": "ID of the resource to fetch."}
        },
        "required": ["id"]
      },
      "output_schema": {
        "type": "object",
        "properties": {
          "id": {"type": "string", "description": "ID of the resource."},
          "title": {"type": "string", "description": "Title or headline of the fetched resource."},
          "text": {"type": "string", "description": "Complete textual content of the resource."},
          "url": {"type": ["string", "null"], "description": "URL of the resource. Optional but needed for citations to work."},
          "metadata": {
            "type": ["object", "null"],
            "additionalProperties": {"type": "string"},
            "description": "Optional metadata providing additional context."
          }
        },
        "required": ["id", "title", "text"]
      }
    }
  ]
}

์ธ์ฆ ์ฒ˜๋ฆฌ

์‚ฌ์šฉ์ž ์ง€์ • ์›๊ฒฉ MCP ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ์‚ฌ๋žŒ์œผ๋กœ์„œ, ๊ถŒํ•œ ๋ถ€์—ฌ์™€ ์ธ์ฆ์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณดํ˜ธํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. OAuth์™€ ๋™์  ํด๋ผ์ด์–ธํŠธ ๋“ฑ๋ก์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœํ† ์ฝœ์˜ ์ธ์ฆ์— ๋Œ€ํ•ด ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณด๋ ค๋ฉด MCP ์‚ฌ์šฉ์ž ๊ฐ€์ด๋“œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ๊ถŒํ•œ ๋ถ€์—ฌ ์‚ฌ์–‘์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
ChatGPT์—์„œ ์‚ฌ์šฉ์ž ์ง€์ • ์›๊ฒฉ MCP ์„œ๋ฒ„๋ฅผ ์—ฐ๊ฒฐํ•˜๋ฉด, ์ž‘์—… ๊ณต๊ฐ„์˜ ์‚ฌ์šฉ์ž๋Š” ๊ท€ํ•˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ OAuth ํ๋ฆ„์„ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ „์†ก ๋ฐ ํ„ฐ๋„๋ง

์›๊ฒฉ MCP ์„œ๋ฒ„๋Š” ์ธํ„ฐ๋„ท ์ฃผ์†Œ ์ง€์ •์ด ๊ฐ€๋Šฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์„œ๋ฒ„๊ฐ€ ์ธํŠธ๋ผ๋„ท์— ํ˜ธ์ŠคํŒ…๋˜์–ด ์žˆ๋‹ค๋ฉด ์–ด๋–ค ํ˜•ํƒœ์˜ ํ„ฐ๋„๋ง์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ngrok์€ ์ด๋ฅผ ์œ„ํ•œ ํŽธ๋ฆฌํ•œ ๋„๊ตฌ ์ค‘ ํ•˜๋‚˜์ด์ง€๋งŒ, Cloudflare์™€ ๊ฐ™์€ ๋‹ค๋ฅธ ํ„ฐ๋„๋ง ์†”๋ฃจ์…˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ๋ฐ ๋””๋ฒ„๊น…

MCP ์„œ๋ฒ„๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋ ค๋ฉด, API ํ”Œ๋ ˆ์ด๊ทธ๋ผ์šด๋“œ์„ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋ฒ„์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ์ง€, ๋„๊ตฌ ๋ชฉ๋ก์ด ์˜ˆ์ƒ๋Œ€๋กœ ํ•ด๊ฒฐ๋˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”. ํ”Œ๋ ˆ์ด๊ทธ๋ผ์šด๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹ฌ์ธต ์—ฐ๊ตฌ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ์„œ๋ฒ„๊ฐ€ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋Šฅ๋ ฅ์„ ๋ถ€๋ถ„์ ์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ฌ์ธต ์—ฐ๊ตฌ๋Š” ๋ฐ˜๋ณต์ ์ธ ๊ฐœ์„  (์˜ˆ: ๊ฒ€์ƒ‰ ๋„๊ตฌ ์„ค๋ช…์„ ๊ฐœ์„ ํ•˜๋Š” ๋™์•ˆ)์— ๋” ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค.

์ตœ์ƒ์˜ ๊ฒฐ๊ณผ๋ฅผ ์œ„ํ•ด, ํ”Œ๋ ˆ์ด๊ทธ๋ผ์šด๋“œ์—์„œ OpenAI o3 ๋˜๋Š” o3 mini๋กœ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

์›๊ฒฉ MCP ์„œ๋ฒ„ ์—ฐ๊ฒฐ

  1. ChatGPT ์„ค์ •์—์„œ ์›๊ฒฉ MCP ์„œ๋ฒ„๋ฅผ ์ง์ ‘ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
  2. ์ปค๋„ฅํ„ฐ ํƒญ์—์„œ ์„œ๋ฒ„๋ฅผ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ ์ปดํฌ์ € > ์‹ฌ์ธต ์—ฐ๊ตฌ ๋„๊ตฌ์—์„œ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„œ๋ฒ„๋ฅผ ์†Œ์Šค๋กœ ์ถ”๊ฐ€ํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
  3. ๋ช‡ ๊ฐ€์ง€ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ์„œ๋ฒ„๋ฅผ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค.

์œ„ํ—˜ ๋ฐ ์•ˆ์ „

์‚ฌ์šฉ์ž ์ง€์ • MCP ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ChatGPT ์ž‘์—… ๊ณต๊ฐ„์„ ์™ธ๋ถ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ChatGPT๋Š” ์ด๋Ÿฌํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ์†ก์ˆ˜์‹ ํ•˜๋ฉฐ, ์กฐ์น˜๋ฅผ ์ทจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ์ง€์ • MCP ์„œ๋ฒ„๋Š” OpenAI์—์„œ ๊ฐœ๋ฐœํ•˜๊ฑฐ๋‚˜ ๊ฒ€์ฆํ•œ ๊ฒƒ์ด ์•„๋‹ˆ๋ฉฐ, ์ž์ฒด ์ด์šฉ ์•ฝ๊ด€์ด ์ ์šฉ๋˜๋Š” ํƒ€์‚ฌ ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค.

์•…์˜์ ์ธ MCP ์„œ๋ฒ„๋ฅผ ๋ฐœ๊ฒฌํ•˜๋ฉด security@openai.com์œผ๋กœ ์‹ ๊ณ ํ•ด ์ฃผ์‹ญ์‹œ์˜ค.

์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์„œ๋ฒ„ ์—ฐ๊ฒฐ

ChatGPT ์ž‘์—… ๊ณต๊ฐ„์— ์ถ”๊ฐ€ํ•˜๋Š” ์‚ฌ์šฉ์ž ์ง€์ • MCP ์„œ๋ฒ„์— ์ฃผ์˜ํ•˜์‹ญ์‹œ์˜ค. ๊ธฐ๋ฐ˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์•Œ๊ณ  ์‹ ๋ขฐํ•˜์ง€ ์•Š๋Š” ํ•œ ์‚ฌ์šฉ์ž ์ง€์ • MCP ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ==์•…์˜์ ์ธ MCP ์„œ๋ฒ„์—๋Š” ChatGPT๊ฐ€ ์˜ˆ๊ธฐ์น˜ ์•Š๊ฒŒ ๋™์ž‘ํ•˜๋„๋ก ์„ค๊ณ„๋œ ์ˆจ๊ฒจ์ง„ ์ง€์นจ(ํ”„๋กฌํ”„ํŠธ ์ธ์ ์…˜)==์ด ํฌํ•จ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์–ด๋–ค MCP ์„œ๋ฒ„์—๋“  ์—ฐ๊ฒฐํ•˜๊ธฐ ์ „์— ๊ณต์œ ๋  ๋ฐ์ดํ„ฐ ์œ ํ˜•์„ ์‹ ์ค‘ํ•˜๊ณ  ์ฒ ์ €ํ•˜๊ฒŒ ๊ฒ€ํ† ํ•˜์‹ญ์‹œ์˜ค.

์„œ๋ฒ„ ๊ตฌ์ถ•

์ ‘๊ทผ์„ ํ—ˆ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ์— ์ฃผ์˜ํ•˜์‹ญ์‹œ์˜ค. ๋„๊ตฌ์˜ JSON์— ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ๋„ฃ์ง€ ๋งˆ์‹ญ์‹œ์˜ค. ๋˜ํ•œ ์›๊ฒฉ MCP ์„œ๋ฒ„์— ์ ‘๊ทผํ•˜๋Š” ChatGPT ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค.

MCP ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ์‚ฌ๋žŒ์œผ๋กœ์„œ, ๋„๊ตฌ ์ •์˜์— ์•…์˜์ ์ธ ๋‚ด์šฉ์„ ๋„ฃ์ง€ ๋งˆ์‹ญ์‹œ์˜ค. ํ˜„์žฌ๋กœ์„œ๋Š” ๊ฒ€์ƒ‰ ๋ฐ ๋ฌธ์„œ ๊ฒ€์ƒ‰๋งŒ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•œ MCP ์„œ๋ฒ„ ๋ฐฐํฌ

๋Œ€๊ธฐ์—…์€ ChatGPT์˜ ์‹ฌ์ธต ์—ฐ๊ตฌ ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ํšŒ์‚ฌ ์ง€์‹์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก MCP ์„œ๋ฒ„๋ฅผ ๋ฐฐํฌํ•˜๊ธฐ๋ฅผ ์›ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. MCP ์„œ๋ฒ„๋ฅผ ๋ฐฐํฌํ•˜๋ ค๋ฉด ๊ด€๋ฆฌ์ž์™€ ํ˜‘๋ ฅํ•˜์‹ญ์‹œ์˜ค.