튜토리얼

IBM Bob으로 MCP 서버 구축하기

IBM Bob을 사용해 AI 모델을 외부 도구 및 데이터 소스에 연결하는 커스텀 Model Context Protocol(MCP) 서버를 구축하는 방법을 알아보자. 이 실습 튜토리얼에서는 고급 모드, 승인 워크플로우, MCP 설정을 다룬다.

이 튜토리얼에서는 IBM Bob을 사용해 과학 논문 오픈 액세스 콘텐츠 저장소인 arXiv에 대한 읽기 전용 액세스를 제공하는 커스텀 MCP 서버를 구축한다. 선택적으로 서버를 확장해 watsonx Orchestrate AI 에이전트와 통합할 수 있다.

Model Context Protocol(MCP)은 대규모 언어 모델(LLM)이 통합된 클라이언트-서버 아키텍처를 통해 외부 도구, 데이터 소스, 콘텐츠 저장소와 통신할 수 있게 하는 오픈 표준이다. MCP 이전에는 각 AI 어시스턴트가 상호 운용성 없이 함수 호출을 사용해 각 외부 도구에 대한 독자적인 통합이 필요했다. MCP는 대신 모든 MCP 호스트가 모든 MCP 서버에 연결하는 데 사용할 수 있는 단일 JSON-RPC 2.0 프로토콜을 정의한다.

사전 요구 사항

이 튜토리얼은 arXiv API를 쿼리하는 TypeScript MCP 서버를 구축한다. TypeScript나 MCP 통합에 대한 사전 경험이 필요하지 않다.

이 튜토리얼을 완료하려면 다음이 필요하다:

워크스페이스 설정

IBM Bob을 실행하고 MCP 설정 패널을 열어 서버 프로젝트를 위한 작업 디렉터리를 준비한다.

IBM Bob 실행

컴퓨터에서 IBM Bob 애플리케이션을 실행한다.

Bob 채팅 패널 열기

채팅 패널이 아직 열려 있지 않으면 내비게이션 바 옆의 Bob 아이콘을 클릭하거나 단축키 Option + Command + B(Mac) 또는 Ctrl + Alt + B(Windows)를 사용한다.

MCP 설정 패널 열기

채팅 창 오른쪽 상단의 톱니바퀴 아이콘을 클릭한 후 왼쪽 사이드바에서 MCP를 선택한다.

MCP 설정 패널에서는 서버 활성화/비활성화, 특정 도구 자동 승인, MCP SDK를 사용한 커스텀 통합 구축이 가능하다.

  • 전역: mcp_settings.json에 저장되며 모든 워크스페이스에 적용된다.
  • 프로젝트: 프로젝트 루트의 .bob/mcp.json에 저장되며 버전 관리를 통해 팀과 공유할 수 있다. 프로젝트 수준 설정이 전역 설정을 재정의한다.

자동 승인 설정

Bob 채팅에서 채팅 입력 필드 바로 아래의 자동 승인 권한이 "Read"(읽기) 전용으로 설정되어 있는지 확인한다. 이 설정으로 Bob은 파일과 디렉터리 내용을 볼 수 있지만, 각 명령을 실행하기 전에 검토와 승인을 요청한다.

프로젝트 디렉터리 열기

프로젝트에 선호하는 디렉터리가 있으면 IDE에서 연다. 채팅 창에서 Bob에게 요청할 수도 있다.

Python 가상 환경 설정

프로젝트 의존성을 격리하여 서로 다른 프로젝트 간의 충돌을 방지하기 위해 Python 가상 환경을 생성하는 것이 일반적인 관행이다. 터미널 명령을 읽고 쓰고 실행할 수 있는 Agent 모드로 Bob을 전환한 후 환경을 생성한다.

가상 환경 생성 및 활성화

Bob 채팅 패널에 다음 프롬프트를 입력한다:

In this directory, activate a Python virtual environment.

Bob이 일련의 터미널 명령을 실행한다. 프롬프트가 표시될 때마다 각각을 승인한다. 명령이 venv/ 디렉터리에 새 가상 환경을 생성하고 활성화한다.

MCP 서버 빌드 계획 생성

Plan 모드로 전환

채팅 입력 필드 바로 아래의 버튼을 클릭해 인터랙션 모드를 Plan으로 변경한다. 이 모드에서 Bob은 코드를 작성하기 전에 MCP 서버에 대한 구조화된 계획을 생성할 수 있다.

서버 요구 사항 제출

가상 환경이 활성화된 상태에서 다음 프롬프트를 Bob에게 제출한다. 구체적인 요구 사항을 미리 제공하면 Bob이 코드를 작성하기 전에 완전한 계획을 수립할 충분한 컨텍스트를 얻을 수 있다:

Create an MCP server named arxiv-server that provides read-only access to arXiv. The server should:
- Expose one tool, search_papers, for querying arXiv papers by keyword
- Accept a query string and an optional max_results parameter (default 5, max 20)
- Limit results to paper metadata and abstracts (no PDFs)
- Return title, authors, publication date, abstract, and arXiv URL for each result
- Normalize responses into a clean, structured JSON schema
- Handle API errors and empty results gracefully, returning a clear message instead of failing
- Use TypeScript/Node.js with the stdio transport
- Use the arXiv API v2, which requires no authentication

Bob은 프로젝트 스캐폴딩, MCP 서버 구현, 의존성 설치, 서버 설정, 테스트를 포함하는 구조화된 할 일 목록을 생성한다. Bob이 오류 처리와 인증 고려 사항을 자동으로 계획하는 것을 확인할 수 있다. 대상 API(arXiv)가 키를 필요로 하지 않더라도 Bob은 키가 필요한 서버에 대해 자격 증명이 주입될 위치를 표시한다.

Bob이 명확화 질문을 하면 최대한 답하거나 합리적인 가정을 하도록 지시한다.

MCP 서버 구축 및 검토

계획을 검토하고 승인한 후 Agent 모드로 전환해 각 단계를 실행한다. Bob이 실시간으로 응답을 생성하므로 출력과 순서가 다음 예시와 약간 다를 수 있다.

다음 프롬프트로 Bob에게 서버 구축을 시작하도록 지시한다:

Implement the plan.

먼저 Bob이 프로젝트 구조를 스캐폴딩하고 mkdir -p arxiv-server/src를 실행해 프로젝트 디렉터리를 생성한다.

다음으로 Bob이 프로젝트의 메타데이터, 스크립트, 의존성을 선언하는 Node.js 설정 허브인 arxiv-server/package.json을 생성한다:

{
  "name": "arxiv-server",
  "version": "0.1.0",
  "description": "MCP server for read-only access to arXiv papers",
  "type": "module",
  "bin": {
    "arxiv-server": "./build/index.js"
  },
  "scripts": {
    "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
    "prepare": "npm run build",
    "watch": "tsc --watch"
  },
  "keywords": ["mcp", "arxiv", "research", "papers"],
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.4",
    "axios": "^1.7.9",
    "zod": "^3.24.1"
  },
  "devDependencies": {
    "@types/node": "^22.10.5",
    "typescript": "^5.7.3"
  }
}

Bob은 TypeScript 컴파일러를 설정하는 arxiv-server/tsconfig.json도 생성한다:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "declaration": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "build"]
}

다음으로 Bob이 arxiv-server/src/index.ts에 메인 서버 파일을 생성한다. 이 파일은 MCP SDK에 search_arxiv 도구를 등록하고, arXiv API 응답에 대한 XML-to-JSON 파싱을 구현하며, 결과 한도를 적용하고, MCP 호스트와 같은 머신에서 실행되는 서버에 적합한 로컬 저지연 전송 유형인 STDIO 전송으로 서버를 시작한다.

MCP SDK의 server.tool() 호출이 기본 통합 포인트다. 모든 MCP 클라이언트에 도구를 노출한다.

#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import axios from 'axios';

interface ArxivEntry {
  id: string;
  title: string;
  summary: string;
  authors: Array<{ name: string }>;
  published: string;
  updated: string;
  categories: string[];
  primary_category: string;
  links: Array<{ href: string; rel: string; type?: string }>;
}

interface ArxivSearchResult {
  entries: ArxivEntry[];
  totalResults: number;
  startIndex: number;
  itemsPerPage: number;
}

const server = new McpServer({ name: "arxiv-server", version: "0.1.0" });

const arxivApi = axios.create({
  baseURL: 'http://export.arxiv.org/api',
  timeout: 30000,
});

server.tool(
  "search_arxiv",
  {
    query: z.string().describe("Search query (supports arXiv query syntax)"),
    max_results: z.number().min(1).max(50).optional()
      .describe("Maximum results to return (1–50, default: 10)"),
    start: z.number().min(0).optional()
      .describe("Starting index for pagination (default: 0)"),
    sort_by: z.enum(["relevance", "lastUpdatedDate", "submittedDate"]).optional(),
    sort_order: z.enum(["ascending", "descending"]).optional()
  },
  async ({ query, max_results = 10, start = 0, sort_by = "relevance", sort_order = "descending" }) => {
    try {
      const params: Record<string, string | number> = {
        search_query: query,
        start: Math.max(start, 0),
        max_results: Math.min(max_results, 50),
        ...(sort_by && { sortBy: sort_by }),
        ...(sort_order && { sortOrder: sort_order }),
      };
      const response = await arxivApi.get('/query', { params });
      return {
        content: [{ type: "text", text: formatSearchResults(parseArxivXML(response.data)) }],
      };
    } catch (error) {
      if (axios.isAxiosError(error)) {
        return {
          content: [{ type: "text", text: `arXiv API error: ${error.response?.data?.message ?? error.message}` }],
          isError: true,
        };
      }
      throw error;
    }
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);
console.error('arXiv MCP server running on stdio');

catch 블록의 isError: true 플래그는 MCP의 표준 오류 처리 패턴이다. 서버 프로세스를 중단하지 않고 도구 호출이 실패했음을 MCP 클라이언트에 알린다.

다음 단계로 Bob이 cd arxiv-server && npm install을 실행해 arxiv-server 디렉터리 내에 의존성을 설치한다.

프로젝트 범위에서 서버 등록

Bob은 새 서버를 자동으로 등록하지 않는다. Bob에게 명시적으로 추가하도록 지시하고, 설정이 .bob/mcp.json에 저장되어 버전 관리를 통해 팀과 공유할 수 있도록 프로젝트 범위를 지정한다.

Bob에게 서버 등록 지시

Bob 채팅 패널에 다음 프롬프트를 입력한다:

Register the arxiv-server as an MCP server at project scope. Build it first if needed, then add it to .bob/mcp.json.

생성된 설정 검토

Bob이 프로젝트 루트의 .bob/mcp.json에 다음을 작성한다. commandargs 필드는 STDIO 전송을 사용해 서버 프로세스를 시작하는 방법을 MCP 클라이언트에 알려준다.

{
  "mcpServers": {
    "arxiv-server": {
      "command": "node",
      "args": ["${workspaceFolder}/arxiv-server/build/index.js"]
    }
  }
}

서버가 로드되었는지 확인

Bob이 이 파일을 작성한 후 MCP 설정을 자동으로 다시 로드한다. 계속하기 전에 MCP 설정 패널(톱니바퀴 아이콘 > MCP)을 열어 arxiv-server가 목록에 표시되고 활성화되어 있는지 확인한다. 표시되지 않으면 서버 목록 옆의 다시 로드 아이콘을 클릭한다.

Bob 재시작

서버가 실행 중이고 쿼리를 받을 준비가 되었는지 확인하려면 Bob을 재시작한다.

MCP 서버 테스트

서버가 등록되면 Bob이 search_arxiv 도구에 대해 두 가지 유효성 검사 쿼리를 자동으로 실행한다.

첫 번째 테스트는 관련성 순으로 정렬된 양자 컴퓨팅 논문 3편을 쿼리한다. 두 번째는 제출 날짜 내림차순으로 정렬된 머신러닝 논문 2편을 쿼리한다. 두 쿼리 모두 성공적으로 실행되어 도구에 접근 가능하고 서버의 오류 처리가 다양한 매개변수 조합을 올바르게 관리하는 것을 확인한다.

이제 자체 쿼리를 실행해 Bob이 자연어에서 올바른 매개변수를 추출하는지 확인한다. Bob 채팅 패널에 붙여넣을 수 있는 프롬프트 예시:

What are the latest papers on LLM agent tracing?

서버 문서화

오픈 소스 MCP 서버 구현에는 일반적으로 다른 사람들이 빠르게 시작할 수 있도록 문서가 포함된다. Bob에게 생성을 요청한다:

In this directory, create a README.md file to document this MCP server.
Include setup and usage instructions.

Bob이 설치, 여러 MCP 호스트(IBM Bob, Claude Desktop, Cursor, Claude Code) 설정, API 키가 필요한 서버의 인증 가이드, 로컬 파일 액세스 패턴, 트러블슈팅 팁을 포함하는 포괄적인 README.md를 생성한다.

리소스 정리

이 튜토리얼은 로컬 파일과 서버 등록을 생성한다. arXiv MCP 서버를 계속 사용할 계획이 없으면 제거한다.

Bob에서 MCP 설정 패널(톱니바퀴 아이콘 > MCP)을 열고 arxiv-server 항목을 비활성화하거나 삭제한다. 또는 mcp_settings.json(전역 범위) 또는 .bob/mcp.json(프로젝트 범위)에서 직접 arxiv-server 블록을 제거한다.

다음 단계

이 튜토리얼에서는 IBM Bob을 사용해 TypeScript MCP 서버를 구축하고, STDIO 전송으로 설정하고, 모든 것을 자연어 프롬프트를 통해 arXiv 라이브 쿼리로 테스트했다.

동일한 워크플로우가 더 복잡한 MCP 서버 구현에도 적용된다: 데이터베이스, 로컬 파일 또는 기타 외부 데이터 소스에 연결하는 서버. 인증이 필요한 서버는 MCP 설정 JSON의 환경 변수로 자격 증명을 주입해야 한다. 원격 배포의 경우 STDIO 전송을 SSE로 교체한다.

  • 서버 코드를 커밋하기 전에 문제를 발견하는 Bob의 코드 리뷰 기능에 대해 알아보자.
  • Advanced, Code, Ask 및 다른 Bob 페르소나를 언제 사용할지 이해하기 위해 모드에 대해 알아보자.
  • 전역 범위와 프로젝트 범위, 자동 승인 도구, SSE 전송 설정에 대한 자세한 내용은 MCP 설정을 살펴보자.
  • 계속 학습하려면 IBM Bob 입문 튜토리얼 시리즈를 따라가 보자.
이 주제는 어떤가요?