CodeGym /행동 /ChatGPT Apps /첫 MCP‑서버: SDK부터 동작하는 tools/resources/prompts까지

첫 MCP‑서버: SDK부터 동작하는 tools/resources/prompts까지

ChatGPT Apps
레벨 6 , 레슨 3
사용 가능

1. 오늘 무엇을 만들고 앱에 어떻게 녹여 넣을 것인가

우리의 학습용 애플리케이션을 떠올려 봅시다: 우리는 선물 선택 어시스턴트를 만들고 있습니다. 이전 모듈에서는 이미 다음을 갖췄습니다:

  • ChatGPT 안의 위젯(Next.js 16 + Apps SDK) — UI와 상태를 보여주고 callTool을 호출할 수 있음;
  • 간단한 백엔드(Apps SDK / Next.js 라우트) — 선물 스텁을 반환함.

이제 어시스턴트의 “두뇌”를 별도의 MCP‑서버로 분리하려 합니다. 최종 구조는 다음과 같습니다:

flowchart TD
  subgraph ChatGPT
    U[사용자
채팅에서] W["앱 위젯
(Apps SDK)"] end subgraph MCP 클라이언트 C[ChatGPT MCP client] end subgraph OurServer[우리 MCP 서버] T1[Tool: suggest_gifts] R1[Resource: gift_catalog] P1[Prompt: birthday_template] end U --> W W -- callTool --> C C <-- JSON-RPC / HTTP --> OurServer OurServer --> C C --> W

즉, 이제 다음과 같습니다:

  • ChatGPT 내부의 모델이 우리의 MCP‑서버를 표준 tools/resources/prompts 집합으로 인식합니다;
  • 위젯의 callTool 호출은 논리적으로 내부 MCP 호출로 변환됩니다;
  • 우리 서버는 계약(스키마, 설명)을 정의하고 비즈니스 로직을 구현합니다.

이 강의가 끝날 때쯤에는 MCP‑서버가 포함된 별도의 Node/TypeScript 프로젝트가 생기며, 이 서버는 다음을 수행합니다:

  • 한 번의 명령으로 로컬에서 기동된다;
  • 적어도 하나의 도구와 하나의 리소스를 등록한다;
  • 의미 있는 데이터를 반환한다(단순한 목이어도 됨);
  • 향후 확장 가능한 구조를 갖춘다.

이때 기존의 Apps SDK/Next.js 백엔드는 지금은 수정하지 않습니다. 그대로 두고, MCP‑서버를 옆에 별도 서비스로 올립니다. 이후 ChatGPT App에 이를 “연결”하고, 기존 스텁 대신 선물 로직을 점진적으로 MCP로 이전할 수 있습니다.

2. 스택: TypeScript + MCP SDK + HTTP‑트랜스포트

우리는 Node.js 기반 TypeScriptMCP‑서버를 작성합니다. MCP용 공식 JS/TS SDK는 @modelcontextprotocol/sdk 패키지에 있습니다. 이 SDK는 JSON‑RPC, 검증, 스키마 변환 같은 반복 작업을 대신해 줍니다: 인자를 Zod 스키마로 기술하면, SDK가 이를 모델이 이해하는 JSON Schema로 자동 변환합니다.

트랜스포트는 HTTP가 필요합니다. ChatGPT는 원격 MCP‑서버와 네트워크로 통신하며, stdio/로컬이 아닙니다. MCP 사양은 표준 “스트리머블 HTTP” 형식을 설명합니다 — 사실상 오래된 HTTP+SSE 스킴의 진화판입니다. 실무에서는 하나의 HTTP endpoint가 요청(POST/GET)을 처리하고 필요하면 응답을 스트리밍합니다. TypeScript SDK에는 보통 이런 형식에 맞는 트랜스포트가 준비되어 있어 Express나 Hono에 붙일 수 있습니다.

논점을 좁히기 위해 다음이 있다고 가정하겠습니다:

  • 서버 객체 McpServer (@modelcontextprotocol/sdk 제공);
  • HTTP 트랜스포트(예: StreamableHttpServerTransport 또는 유사체) — Express와 연동 가능.

클래스의 정확한 이름은 SDK 버전에 따라 조금 다를 수 있지만, 아키텍처는 늘 같습니다:

  1. MCP‑서버 객체를 생성한다;
  2. 그 위에 tools/resources/prompts를 등록한다;
  3. HTTP 애플리케이션에 트랜스포트를 연결한다.

3. 프로젝트 구조와 준비

MCP‑서버용 폴더를 따로 만듭니다. 프런트엔드 앱과 나란히 두되, 별도의 Node 프로젝트로 두는 것이 좋습니다:

chatgpt-gift-app/
  app/              ← Next.js + Apps SDK (위젯)
  mcp-server/       ← 우리 MCP 서버

내부 mcp-server:

mcp-server/
  src/
    server.ts       ← MCP 서버 진입점
    gifts.ts        ← 선물 추천 비즈니스 로직
  package.json
  tsconfig.json

간단한 gifts.ts 예시는 조금 뒤에 만들고, 지금은 server.ts에 집중하겠습니다.

프로젝트를 이미 초기화했다고 가정합시다:

mkdir mcp-server
cd mcp-server
npm init -y
npm install typescript ts-node-dev zod express @modelcontextprotocol/sdk

tsconfig.json — 일반적인 설정입니다(esnext modules, target node, strict). 기존 TS 프로젝트의 것을 그대로 사용해도 됩니다.

4. 비즈니스 로직을 별도 모듈로 분리하기

바로 server.registerTool(..., async () => {...})에 모든 로직을 넣고 싶겠지만, 처음부터 다음처럼 분리하는 게 좋습니다:

  • MCP나 JSON‑RPC 등에 대해 아무것도 모르는 모듈;
  • MCP만 아는 모듈(비즈니스 로직은 거의 모름).

src/gifts.ts에 간단한 선물 추천 함수를 정의해 보겠습니다:

// src/gifts.ts

export type GiftIdea = {
  id: string;
  title: string;
  price: number;
  occasion: string;
};

export type SuggestGiftsInput = {
  age: number;
  relationship: "friend" | "partner" | "child" | "coworker";
  budget: number;
};

export function suggestGifts(input: SuggestGiftsInput): GiftIdea[] {
  // 지금은 간단한 목 데이터
  return [
    {
      id: "book-1",
      title: "좋아하는 취미 관련 책",
      price: Math.min(input.budget, 30),
      occasion: "generic",
    },
    {
      id: "game-1",
      title: "모임에서 즐길 수 있는 보드게임",
      price: Math.min(input.budget, 50),
      occasion: "party",
    },
  ];
}

이 함수는 순수합니다. 입력에 파라미터를 받아 출력으로 아이디어 배열을 반환합니다. 유닛 테스트가 가능하고 다른 곳에서도 재사용할 수 있으며, MCP에 전혀 의존하지 않습니다. 이렇게 “서버 래핑”과 비즈니스 함수는 분리하는 것이 권장됩니다.

5. MCP‑서버를 만들고 HTTP‑트랜스포트를 연결하기

이제 진입점 src/server.ts입니다. 개략적으로 필요한 작업:

  1. MCP‑서버 인스턴스를 생성한다;
  2. 그 위에 도구, 리소스, 프롬프트를 등록한다;
  3. HTTP 서버(예: Express)를 띄우고 그 위에 MCP‑트랜스포트를 붙인다.

골격부터 시작해 봅시다:

// src/server.ts
import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server";
import { StreamableHttpServerTransport } from "@modelcontextprotocol/sdk/transport/streamable-http";

const app = express();

// 1. MCP 서버 생성
const mcpServer = new McpServer({
  name: "gift-assistant-mcp",
  version: "0.1.0",
});

// 2. 여기에서 나중에 tools/resources/prompts를 등록

// 3. HTTP 위에 트랜스포트를 설정
const transport = new StreamableHttpServerTransport({
  path: "/mcp", // 단일 MCP endpoint
  app,          // Express 애플리케이션에 통합
});

transport.attach(mcpServer);

const PORT = process.env.PORT ?? 4000;
app.listen(PORT, () => {
  console.log(`MCP server listening on http://localhost:${PORT}/mcp`);
});

트랜스포트 클래스의 정확한 이름은 다를 수 있지만, 패턴은 같습니다. 하나의 HTTP endpoint를 만들고, 그 위에 MCP‑서버를 JSON‑RPC over HTTP/stream 처리기로 연결합니다.

이 단계에서 서버는 아직 유용한 일을 하진 않지만, 이미 다음을 수행할 수 있습니다:

  • MCP‑핸드셰이크를 통과한다;
  • 기본 discovery 요청에 응답한다(도구/리소스/프롬프트 목록 — 현재는 비어 있음).

다음 단계는 첫 번째 도구를 등록하는 것입니다.

6. MCP SDK로 tool suggest_gifts 등록

공식 Apps SDK와 MCP 문서는 도구 등록에 동일한 패턴을 보여 줍니다. 즉, registerTool 메서드에 이름, 설명자(제목, 설명, 인자 스키마), 그리고 핸들러를 전달합니다.

우리는 이미 gifts.ts에서 타입 SuggestGiftsInput을 정의했습니다. 이제 Zod 스키마를 추가해 서버가 입력 인자를 검증하고, LLM에 올바른 JSON Schema를 자동으로 제공하도록 하겠습니다.

// src/server.ts (일부)
import { z } from "zod";
import { suggestGifts } from "./gifts";

const suggestGiftsInputSchema = z.object({
  age: z.number().int().min(0).max(120),
  relationship: z.enum(["friend", "partner", "child", "coworker"]),
  budget: z.number().min(0),
});

이제 도구를 등록합니다:

// 여전히 server.ts

mcpServer.registerTool(
  "suggest_gifts",
  {
    title: "Suggest gift ideas",
    description:
      "나이, 관계 유형, 예산을 기준으로 선물 아이디어를 제안합니다.",
    // SDK가 Zod 스키마를 JSON Schema로 변환해 모델에 제공합니다
    inputSchema: suggestGiftsInputSchema,
  },
  async ({ input }) => {
    const ideas = suggestGifts(input);

    const text = ideas
      .map(
        (g) =>
          `• ${g.title} — ~${g.price} USD (occasion: ${g.occasion}, id: ${g.id})`
      )
      .join("\n");

    return {
      content: [
        {
          type: "text",
          text,
        },
      ],
      // structuredContent는 위젯에서 사용할 수 있습니다
      structuredContent: {
        ideas,
      },
    };
  }
);

핵심 포인트:

  • inputSchema는 Zod 스키마입니다. TS용 SDK는 이를 JSON Schema로 변환하여 도구를 모델에 자동으로 기술합니다.
  • 핸들러는 input이 포함된 객체를 받습니다(타입은 스키마에서 유추). 그 내부에서 비즈니스 함수를 호출하면 됩니다.
  • result에는 모델이 결과로 볼 content 텍스트를 반환하고, 원한다면 위젯이 소비할 수 있는 structuredContent JSON 구조도 함께 반환합니다.

이전 모듈에서 Apps SDK로 도구를 만든 경험이 있다면, 이 코드는 매우 익숙하게 느껴질 것입니다. 패턴은 동일하지만, 이제 별도의 MCP‑서버 안에 존재한다는 점만 다릅니다.

7. 데이터용 리소스 gift_catalog 추가

도구는 “행동”입니다. 때로는 모델이 읽거나 검색할 수 있도록 데이터를 리소스로 제공하고 싶을 때가 있습니다. 혹은 위젯이 템플릿, 컴포넌트를 불러오기 위해서도요. MCP는 URI, MIME 타입, 콘텐츠를 갖는 리소스 개념을 별도로 정의합니다.

리소스 gift_catalog를 만들어서 사용 가능한 선물 리스트를 반환해 봅시다. 지금은 목 데이터지만, 실제론 DB 덤프나 product feed가 될 수 있습니다.

먼저 카탈로그 자체입니다:

// src/gifts.ts (추가)
export const giftCatalog: GiftIdea[] = [
  {
    id: "book-1",
    title: "프로그래밍 책",
    price: 25,
    occasion: "learning",
  },
  {
    id: "lego-1",
    title: "LEGO 세트",
    price: 60,
    occasion: "fun",
  },
];

이제 서버에 리소스를 등록합니다:

// src/server.ts (일부)
import { giftCatalog } from "./gifts";

mcpServer.registerResource(
  "gift_catalog",
  {
    title: "Gift catalog",
    description: "데모와 디버깅을 위한 간단한 선물 카탈로그.",
    mimeType: "application/json",
  },
  async () => {
    return {
      contents: [
        {
          uri: "mcp://gift-catalog",
          mimeType: "application/json",
          text: JSON.stringify(giftCatalog, null, 2),
        },
      ],
    };
  }
);

여기서 논리적으로 일어나는 일:

  • 리소스 이름 gift_catalog는 discovery 시 클라이언트에 보입니다(나중에 MCP 인스펙터에서 리소스 목록에 나타납니다);
  • 설명자에는 사람이 읽기 쉬운 설명과 MIME 타입이 포함됩니다;
  • 핸들러는 URI와 텍스트가 포함된 contents 배열을 반환합니다 — 이는 MCP 리소스의 표준 형식입니다.

이후 다음과 같은 일을 할 수 있습니다:

  • 클라이언트(예: 에이전트나 인스펙터)에서 이 리소스를 읽는다;
  • UI 템플릿/데이터로 활용한다;
  • 모델이 준비된 카탈로그를 활용해 사용자에게 옵션을 설명하는 방식을 실험한다.

8. 간단한 prompt 등록

세 번째 MCP 엔터티는 프롬프트입니다. 미리 준비된 지시문으로, 긴 시스템/사용자 프롬프트를 반복하지 않고 서버에 이름으로 저장해 둘 수 있게 해 줍니다.

미니 예제로 birthday_gift 프롬프트를 만들겠습니다. “생일 선물 대화의 미리 채워진 템플릿”처럼 호출할 수 있습니다.

// src/server.ts (일부)

mcpServer.registerPrompt("birthday_gift", {
  title: "Birthday gift helper",
  description: "생일 선물 선정을 위한 요청 템플릿.",
  messages: [
    {
      role: "system",
      content:
        "당신은 선물 찾기 어시스턴트입니다. 구체화 질문을 하고 여러 가지 옵션을 제안하세요.",
    },
    {
      role: "user",
      content:
        "생일 선물이 필요해요. 필요한 질문을 하고 선택을 도와주세요.",
    },
  ],
});

내부적으로 MCP는 클라이언트가 다음을 할 수 있게 합니다:

  • 프롬프트 목록을 가져온다(인스펙터에서 birthday_gift를 볼 수 있습니다);
  • 그 내용을 요청해 모델의 기본 지시문으로 사용한다.

시스템 프롬프트와 지시문의 조합에 대해서는 다른 모듈에서 자세히 다룹니다. 여기서는 그것들이 MCP‑서버의 일부임을 “보는” 것이 중요합니다.

9. 런타임에서 이것이 동작하는 방식

전체 그림을 모아 봅시다.

클라이언트(예: MCP Inspector 또는 ChatGPT)가 HTTP endpoint /mcp에 연결하면:

  1. 핸드셰이크가 진행됩니다: 클라이언트와 서버가 지원 기능(tools/resources/prompts 등)에 대한 정보를 교환합니다;
  2. 클라이언트가 discovery 메서드를 호출하여 도구, 리소스, 프롬프트 목록과 그 설명/스키마를 가져옵니다;
  3. 모델이 도구 호출이 필요하다고 판단하면, tools/call과 같은 메서드를 가진 JSON‑RPC 요청을 구성합니다 — 서버 측 SDK는 이를 내부 registerTool 핸들러 호출로 변환합니다;
  4. 핸들러는 비즈니스 로직을 수행합니다(우리 예시에서는 suggestGifts 또는 giftCatalog 제공) 그리고 표준화된 형식으로 결과를 반환합니다;
  5. SDK는 응답을 다시 JSON‑RPC로 직렬화하고 동일한 HTTP/스트림 트랜스포트를 통해 클라이언트에 전송합니다.

JSON‑RPC의 모든 세부(예: id 생성, 메서드 라우팅 등)는 @modelcontextprotocol/sdk 내부에 캡슐화되어 있습니다. 여러분이 다루는 인터페이스는 Apps SDK와 매우 유사합니다. 즉, registerTool/registerResource/registerPrompt 및 핸들러로 작업하며, 프로토콜 자체는 신경 쓰지 않아도 됩니다.

10. 로컬 실행과 첫 간단한 테스트

앞서 설명한 내용을 모두 추가했다고 가정해 봅시다. 이제 실행만 남았습니다.

package.json에 스크립트를 추가합니다:

{
  "scripts": {
    "dev": "ts-node-dev src/server.ts"
  }
}

실행:

npm run dev

콘솔에는 대략 다음이 표시됩니다:

MCP server listening on http://localhost:4000/mcp

본격적인 인스펙션과 수동 도구 호출은 다음 강의에서 MCP Inspector / MCP Jam으로 진행합니다. 하지만 지금도 curl을 이용해 매우 간단한 스모크 테스트를 해볼 수 있습니다:

curl -X POST http://localhost:4000/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

curl은 “날것”의 JSON 응답을 보고 싶은 분들을 위한 선택적 스모크 테스트입니다. 실제 개발에서는 거의 항상 SDK를 통해 MCP‑서버와 통신하며, 손으로 JSON‑RPC 요청을 조립하지 않습니다.

정확한 메서드 이름은 프로토콜/SDK 버전에 따라 달라질 수 있지만, 핵심은 tools 목록에 suggest_gifts가 보인다는 점입니다. 메서드 이름이 다르더라도 괜찮습니다. 이 강의의 목표는 모든 이름을 암기하는 것이 아니라, JSON 응답을 두려워하지 않고 이전 강의 덕분에 그 구조를 이해하는 것입니다.

11. 우리 ChatGPT App과의 연동 및 이후 발전

지금은 MCP‑서버가 독립적으로 동작합니다. 다음 모듈에서 여러분은:

  • MCP Inspector에 연결해 tools/resources/prompts를 ChatGPT와 별개로 디버깅하는 법을 익힌다;
  • ChatGPT App이 이 MCP‑서버를 도구의 소스로 인식하도록 설정한다;
  • 이전에는 Apps SDK 내부(예: 내장 tools)에서 구현했던 일부 로직을 MCP 레이어로 옮긴다;
  • 인증, 로깅, 스트리밍 시나리오를 이미 준비된 골격 위에서 추가한다.

현재 중요한 점:

  • 앱의 “능력”과 “데이터”를 담당하는 별도 서비스가 있다;
  • 이 서비스는 표준 MCP로 클라이언트와 대화하며, 커스텀 REST가 아니다;
  • 프로토콜을 두려워하지 않고 도구, 리소스, 프롬프트를 직접 등록할 수 있다.

12. 코드 구조와 모범 사례

이렇게 작은 예제에서도 좋은 습관을 심을 수 있습니다.

첫째, 서버 설정을 분리하세요. 이름, 버전, 로깅, 트랜스포트 설정(포트, 경로 /mcp) 등은 작은 config.ts 모듈로 쉽게 뺄 수 있습니다. 나중에 Vercel 배포나 MCP‑gateway 뒤에 둘 때 환경 변수를 추가해야 하니, 지금부터 분리해 두면 좋습니다.

둘째, registerTool/registerResource/registerPrompt의 메서드를 최대한 “얇게” 유지하세요. 스키마와 텍스트 설명, 비즈니스 로직은 별도 파일에서 관리하는 게 좋습니다:

  • gifts.ts — 선물 선택 함수;
  • catalog.ts — 상품 카탈로그 처리;
  • prompts.ts — 프롬프트 모음.

이렇게 하면 server.ts는 모든 것을 하나로 “접합하는” MCP 제공자에 가깝게 바뀝니다.

셋째, MCP‑서버는 본질적으로 리액티브합니다. 클라이언트 연결과 요청을 기다립니다. 따라서 도구 내부의 블로킹 작업이나 과도하게 긴 작업은 ChatGPT UX에 직접 영향을 줍니다. 다음 모듈에서 타임아웃, 비동기 작업, 스트리밍 응답을 다루겠지만, 지금도 어떤 작업을 백그라운드로 분리하고, 어떤 작업은 빠르게 응답해야 하는지 고민해 보세요.

Insight: ChatGPT는 MCP의 일부만 지원합니다

중요한 사실: ChatGPT Apps는 MCP를 전송과 형식으로 사용하지만, 완전한 MCP 클라이언트는 아닙니다. 프로토콜만 보고 런타임 동작을 잘못 예상하기 쉽습니다.

“순수한” MCP가 약속하는 것:

  • 리소스(resources)는 클라이언트가 필요할 때마다 동적으로 읽을 수 있으며, 한 번만 고정되는 것이 아니다;
  • 서버는 resourceChanged/toolChanged 알림을 보내 재시작 없이 업데이트를 “푸시”할 수 있다;
  • tools/resources/prompts 집합을 설정이나 외부 상태로 유연하게 관리할 수 있다.

ChatGPT Apps 맥락에서는 그렇지 않습니다. 앱 입장에서는 훨씬 더 정적입니다:

  • App 등록 시 ChatGPT가 모든 tools와 resources 설명을 한 번 읽는다;
  • 그 후 이 구성은 사실상 앱 버전의 일부로 캐시된다;
  • MCP 알림을 통한 동적 업데이트는 지원되지 않는다 — 플랫폼이 이를 무시합니다.

13. 첫 MCP‑서버 작성 시 흔한 실수

오류 1: 모든 비즈니스 로직을 registerTool 안에 몰아넣기.
교육용 예제에서 특히 “핸들러 안에 빠르게 다 써 버리기”가 유혹적입니다. 그러나 나중에 검증, DB 작업, 응답 포맷팅이 뒤섞인 읽기 어려운 덩어리가 됩니다. 초기에 비즈니스 함수(suggestGifts, 카탈로그 처리)를 별도 모듈로 빼고, 핸들러에서는 “접합”만 하세요.

오류 2: MCP의 특정 JSON 메서드 이름에 하드코딩으로 의존하기.
가끔 학생들이 if (method === "tools/list") 같은 코드를 작성해 수동으로 JSON을 파싱하려 합니다. 그럴 필요가 없습니다. 그건 SDK의 역할입니다. MCP 사양과 메서드 이름은 진화할 수 있으며, SDK가 그 부담을 대신 집니다. registerTool, registerResource, registerPrompt를 사용하고, JSON‑RPC의 구체 형상은 라이브러리에 맡기세요.

오류 3: 트랜스포트를 고려하지 않고 ChatGPT에 stdio‑서버를 붙이려 하기.
Stdio 트랜스포트는 데스크톱 환경 등 로컬 클라이언트가 서버를 하위 프로세스로 실행할 수 있을 때 이상적입니다. 하지만 ChatGPT는 HTTPS로 통신하며 HTTP/스트림 endpoint가 필요합니다. “어떻게든 stdio를 터널링”하려는 시도는 고통을 야기합니다. ChatGPT App용이라면 처음부터 HTTP 트랜스포트(Streamable HTTP)로 만드세요.

오류 4: MIME 타입과 리소스 구조를 무시하기.
리소스에는 콘텐츠뿐 아니라 mimeType과 URI가 중요합니다. 어디에나 text/plain만 쓰고 JSON 문자열을 아무렇게나 던지면 클라이언트(와 인스펙터)가 데이터를 이해하기가 어려워집니다. 가능한 올바른 MIME 타입(application/json, UI 템플릿에는 text/html 등)과 안정적인 URI를 표기하세요.

오류 5: MCP‑서버를 “임의의 HTTP‑API”로 사용하기.
“이미 Express가 있으니 /api/whatever도 달고 그쪽으로 직접 호출하자”는 유혹이 생깁니다. 하지만 MCP endpoint와 임의의 REST를 섞는 것은 구성, 라우팅, 보안을 복잡하게 만듭니다. /mcpMCP용, 다른 용도는 별도 경로 또는 별도 서비스로 분리하는 명확한 계약이 좋습니다. 프로덕션에서는 게이트웨이와 인증 설정을 위해 특히 중요합니다. 즉, MCP‑서버를 MCP‑계약과 무관한 ‘임의의 HTTP‑API’로 만들지 마세요.

오류 6: 들어오고 나가는 MCP 메시지를 로깅하지 않기.
로그가 없으면 MCP‑서버는 블랙박스가 됩니다. “무언가 안 되는데, 왜인지 모르겠다.” 최소한 stderr에라도 간결한 구조 로그(도구 메서드, 상태, 실행 시간)를 남기세요. 단, 민감한 데이터와 토큰은 로깅하지 마십시오. 보안은 뒤에서 별도로 다룹니다.

오류 7: 인스펙터 없이 ChatGPT만으로 모든 것을 디버깅하려 하기.
흔한 상황: 학생이 MCP‑서버를 작성하자마자 ChatGPT App에 연결하고, “무언가 막힌다”고 합니다. 그런데 인스펙터는 한 번도 실행하지 않았습니다. 결과적으로 문제가 프로토콜인지, 서버인지, Apps SDK인지, 모델 동작인지 분간하기 어렵습니다. 올바른 순서는 먼저 MCP‑서버가 고립된 환경(MCP Jam / Inspector)에서 정상 동작함을 확인하고, 그다음 애플리케이션에 연결하는 것입니다.

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION