[opencode 시즌 2 심화 — 나만의 도메인 특화 에이전트 만들기] 10/12화: opencode MCP 서버 연동 4가지 실전 패턴 2026
이 글은 「opencode 시즌 2 심화」 10일차입니다
시즌 1에서 opencode의 기본 도구들 — 파일 읽기, 터미널 실행, 웹 검색 — 을 다뤘던 걸 기억하시나요? 그때는 opencode가 기본 제공하는 도구만으로도 충분히 강력하다고 느꼈을 겁니다. 오늘은 그 도구의 경계를 완전히 허물어, 여러분의 사내 시스템 자체가 에이전트의 도구가 되는 세계로 들어갑니다.
어제 9일차에서 플러그인을 직접 만들어 opencode의 네이티브 기능을 확장하는 법을 익혔습니다. 플러그인이 opencode 내부를 확장하는 방법이라면, 오늘 다룰 opencode MCP 서버 연동은 opencode 외부의 모든 시스템을 에이전트의 손과 눈으로 연결하는 방법입니다. MCP(Model Context Protocol)는 에이전트가 외부 세계와 소통하는 표준 프로토콜이며, 이것을 통해 사내 데이터베이스, REST API, 레거시 시스템까지 에이전트의 도구로 노출할 수 있습니다.
오늘의 핵심 3가지:
- opencode.json의
mcp키로 MCP 서버를 등록하고 에이전트에 도구를 공급하는 구조 - 사내 API를 감싸는 MCP 서버를 직접 만들어 에이전트 도구로 노출하는 실전 코드
.well-known/opencode로 조직 전체에 MCP 설정을 원격 배포하는 패턴

MCP가 에이전트의 손과 눈이 되는 순간
MCP(Model Context Protocol)는 Anthropic이 2024년 말 공개한 개방형 프로토콜로, AI 에이전트가 외부 도구·데이터 소스와 표준화된 방식으로 소통할 수 있게 합니다. opencode는 이 프로토콜을 네이티브로 지원하기 때문에, MCP 서버만 하나 만들면 어떤 시스템이든 에이전트의 도구로 변환할 수 있습니다.
핵심 개념을 정리하면 이렇습니다:
- MCP 서버: 도구(tools), 리소스(resources), 프롬프트(prompts)를 제공하는 프로세스. 여러분이 만드는 쪽입니다.
- MCP 클라이언트: MCP 서버에 접속해 도구를 호출하는 쪽. opencode가 이 역할을 합니다.
- 전송 방식: stdio(로컬 프로세스)와 SSE/Streamable HTTP(원격 서버) 두 가지.
시즌 2에서 지금까지 쌓아온 에이전트 아키텍처 위에 MCP를 얹으면, 에이전트가 코드만 읽고 쓰는 존재에서 사내 시스템 전체를 조회하고 조작할 수 있는 존재로 진화합니다.
opencode에서 MCP 서버 등록하기 — 설정 구조
opencode MCP 서버 등록은 opencode.json의 mcp 키 하나로 끝납니다. 4일차에서 다룬 권한 체계, 5일차의 모델 라우팅과 같은 레벨의 최상위 설정입니다.
stdio 방식 — 로컬 MCP 서버
가장 간단한 형태입니다. opencode가 직접 프로세스를 띄우고 stdin/stdout으로 통신합니다.
{
"mcp": {
"deploy-status": {
"command": "node",
"args": ["./mcp-servers/deploy-status/dist/index.js"],
"env": {
"DEPLOY_API_URL": "https://deploy.internal.example.com/api",
"DEPLOY_API_TOKEN": "${DEPLOY_TOKEN}"
}
},
"jira-reader": {
"command": "npx",
"args": ["-y", "@example/jira-mcp-server"],
"env": {
"JIRA_BASE_URL": "https://jira.internal.example.com",
"JIRA_TOKEN": "${JIRA_TOKEN}"
}
}
}
}
각 필드의 의미:
- 키 이름 (
deploy-status,jira-reader): 에이전트가 도구를 식별할 때 사용하는 서버 이름 - command: 실행할 바이너리
- args: 명령줄 인자 배열
- env: 환경변수.
${VAR_NAME}형식으로 시스템 환경변수를 참조할 수 있어, 토큰을 설정 파일에 직접 노출하지 않습니다
SSE/HTTP 방식 — 원격 MCP 서버
팀 공용 서버나 사내 인프라에 MCP 서버를 띄워 두고 원격 접속하는 방식입니다.
{
"mcp": {
"compliance-checker": {
"url": "https://mcp.internal.example.com/compliance/sse",
"headers": {
"Authorization": "Bearer ${COMPLIANCE_TOKEN}"
}
}
}
}
url 필드가 있으면 opencode는 HTTP 기반 전송을 사용합니다. 망분리 환경에서는 사내망 내부 URL을 지정하면 됩니다.
에이전트별 MCP 서버 할당
6일차에서 서브에이전트 오케스트레이션을 다뤘는데, 각 에이전트에 서로 다른 MCP 서버를 할당할 수 있습니다. .opencode/agents/ 의 Markdown 에이전트 정의에서 YAML frontmatter로 지정합니다.
---
name: DeployAgent
description: 배포 상태 조회 및 배포 트리거를 담당하는 전문 에이전트
model: anthropic/claude-sonnet-4-10
mcp:
- deploy-status
- slack-notifier
---
당신은 배포 전문 에이전트입니다.
deploy-status MCP 서버의 도구를 사용하여 배포 상태를 조회하고,
slack-notifier로 결과를 팀 채널에 알립니다.
## 규칙
- 배포 트리거 전 반드시 현재 상태를 먼저 조회할 것
- production 환경 배포는 항상 사용자 확인을 요청할 것
이렇게 하면 DeployAgent는 deploy-status와 slack-notifier MCP 서버의 도구만 사용할 수 있고, 다른 에이전트는 이 도구에 접근하지 못합니다. 4일차에서 배운 최소 권한 원칙이 MCP 레벨에서도 적용되는 셈입니다.

사내 API를 MCP 서버로 감싸기 — 실전 코드
이론은 충분합니다. 실제로 사내 배포 시스템의 REST API를 MCP 서버로 감싸는 코드를 작성해 보겠습니다. MCP TypeScript SDK를 사용합니다.
프로젝트 구조
.opencode/
mcp-servers/
deploy-status/
package.json
tsconfig.json
src/
index.ts # MCP 서버 진입점
deploy-api.ts # 사내 API 클라이언트
package.json
{
"name": "deploy-status-mcp",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.12.0"
},
"devDependencies": {
"typescript": "^5.7.0",
"@types/node": "^22.0.0"
}
}
deploy-api.ts — 사내 API 클라이언트
// 사내 배포 시스템 REST API를 호출하는 클라이언트
// 실제 환경에서는 사내 API 스펙에 맞춰 수정하세요
export interface DeployInfo {
service: string;
version: string;
environment: string;
status: "running" | "deploying" | "failed" | "rolled_back";
deployedAt: string;
deployedBy: string;
}
export interface DeployHistory {
entries: Array<{
version: string;
status: string;
timestamp: string;
author: string;
commitHash: string;
}>;
}
export class DeployApiClient {
private baseUrl: string;
private token: string;
constructor() {
this.baseUrl = process.env.DEPLOY_API_URL ?? "http://localhost:9000/api";
this.token = process.env.DEPLOY_API_TOKEN ?? "";
}
private async request<T>(path: string): Promise<T> {
const res = await fetch(`${this.baseUrl}${path}`, {
headers: {
"Authorization": `Bearer ${this.token}`,
"Content-Type": "application/json",
},
});
if (!res.ok) {
throw new Error(`Deploy API error: ${res.status} ${res.statusText}`);
}
return res.json() as Promise<T>;
}
async getStatus(service: string, env: string): Promise<DeployInfo> {
return this.request<DeployInfo>(`/services/${service}/env/${env}/status`);
}
async getHistory(service: string, env: string, limit = 10): Promise<DeployHistory> {
return this.request<DeployHistory>(
`/services/${service}/env/${env}/history?limit=${limit}`
);
}
async listServices(): Promise<string[]> {
return this.request<string[]>("/services");
}
}
index.ts — MCP 서버 본체
이것이 오늘의 핵심 코드입니다. MCP SDK로 도구 3개를 정의하고, opencode가 호출할 수 있게 stdio 전송으로 노출합니다.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { DeployApiClient } from "./deploy-api.js";
const client = new DeployApiClient();
const server = new McpServer({
name: "deploy-status",
version: "1.0.0",
});
// 도구 1: 서비스 목록 조회
server.tool(
"list_services",
"배포 시스템에 등록된 전체 서비스 목록을 반환합니다",
{},
async () => {
const services = await client.listServices();
return {
content: [
{
type: "text",
text: JSON.stringify(services, null, 2),
},
],
};
}
);
// 도구 2: 특정 서비스의 현재 배포 상태 조회
server.tool(
"get_deploy_status",
"특정 서비스의 현재 배포 상태를 조회합니다. 버전, 배포자, 상태를 확인할 수 있습니다",
{
service: z.string().describe("서비스 이름 (예: payment-api, user-service)"),
environment: z
.enum(["dev", "staging", "production"])
.describe("환경 (dev, staging, production)"),
},
async ({ service, environment }) => {
const status = await client.getStatus(service, environment);
return {
content: [
{
type: "text",
text: [
`서비스: ${status.service}`,
`환경: ${status.environment}`,
`버전: ${status.version}`,
`상태: ${status.status}`,
`배포 시각: ${status.deployedAt}`,
`배포자: ${status.deployedBy}`,
].join("\n"),
},
],
};
}
);
// 도구 3: 배포 이력 조회
server.tool(
"get_deploy_history",
"특정 서비스의 최근 배포 이력을 조회합니다. 롤백 판단이나 변경 추적에 활용합니다",
{
service: z.string().describe("서비스 이름"),
environment: z
.enum(["dev", "staging", "production"])
.describe("환경"),
limit: z
.number()
.min(1)
.max(50)
.default(10)
.describe("조회할 이력 수 (기본 10)"),
},
async ({ service, environment, limit }) => {
const history = await client.getHistory(service, environment, limit);
const formatted = history.entries
.map(
(e, i) =>
`${i + 1}. [${e.status}] v${e.version} — ${e.author} (${e.timestamp})\n commit: ${e.commitHash}`
)
.join("\n");
return {
content: [
{
type: "text",
text: `${service} (${environment}) 최근 ${limit}건 배포 이력:\n\n${formatted}`,
},
],
};
}
);
// stdio 전송으로 실행
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("deploy-status MCP server started");
opencode.json에 등록
빌드 후 opencode.json에 한 줄 추가하면 끝입니다.
{
"mcp": {
"deploy-status": {
"command": "node",
"args": ["./.opencode/mcp-servers/deploy-status/dist/index.js"],
"env": {
"DEPLOY_API_URL": "${DEPLOY_API_URL}",
"DEPLOY_API_TOKEN": "${DEPLOY_API_TOKEN}"
}
}
}
}
이제 opencode를 실행하면 에이전트가 list_services, get_deploy_status, get_deploy_history 세 가지 도구를 자유롭게 사용합니다. “payment-api의 production 배포 상태 알려줘”라고 말하면 에이전트가 알아서 get_deploy_status 도구를 호출합니다.
opencode MCP 서버 — 에이전트 결합 패턴 4가지
MCP 서버를 만드는 건 시작일 뿐입니다. 에이전트와 어떻게 결합하느냐에 따라 활용도가 완전히 달라집니다. 실전에서 가장 많이 쓰이는 4가지 패턴을 정리합니다.
패턴 1 — 읽기 전용 조회 에이전트
가장 안전하고 도입 허들이 낮은 패턴입니다. MCP 서버가 조회(GET) API만 노출하고, 에이전트는 데이터를 읽어서 분석·요약·보고만 합니다.
---
name: StatusReporter
description: 시스템 상태를 조회하고 요약 리포트를 생성하는 읽기 전용 에이전트
model: anthropic/claude-sonnet-4-10
mcp:
- deploy-status
- monitoring-readonly
permission:
deny:
- Bash
- Edit
- Write
---
당신은 시스템 상태 리포터입니다.
## 역할
- deploy-status와 monitoring-readonly MCP 도구를 사용하여 현재 시스템 상태를 수집합니다
- 수집된 데이터를 표 형태로 정리하여 사용자에게 보고합니다
- 이상 징후가 발견되면 즉시 하이라이트합니다
## 제약
- 파일 수정, 명령 실행 등 쓰기 작업을 일체 수행하지 않습니다
- MCP 도구 호출 결과만을 근거로 보고합니다
- 추측이나 가정으로 보고하지 않습니다
핵심은 permission.deny로 Bash, Edit, Write를 모두 차단한 것입니다. 이 에이전트는 MCP 도구로 데이터를 읽을 수만 있고, 로컬 시스템에는 어떤 변경도 가할 수 없습니다. 규제 산업에서 첫 도입으로 적합한 패턴입니다.
패턴 2 — 승인 기반 쓰기 에이전트
MCP 서버가 쓰기 API(POST, PUT)도 노출하되, 에이전트가 실행 전 반드시 사용자 승인을 받는 패턴입니다.
// MCP 서버에서 도구의 description에 승인 필요성을 명시
server.tool(
"trigger_deploy",
"[승인 필요] 특정 서비스를 지정 버전으로 배포합니다. 이 도구는 실제 배포를 트리거하므로 반드시 사용자 확인 후 호출하세요",
{
service: z.string(),
version: z.string(),
environment: z.enum(["dev", "staging", "production"]),
},
async ({ service, version, environment }) => {
// 실제 배포 API 호출
const result = await client.triggerDeploy(service, version, environment);
return {
content: [{ type: "text", text: `배포 시작됨: ${JSON.stringify(result)}` }],
};
}
);
에이전트의 시스템 프롬프트에도 이중 확인 규칙을 넣습니다:
## 쓰기 도구 호출 규칙
1. trigger_deploy 호출 전, 반드시 get_deploy_status로 현재 상태를 먼저 조회
2. production 환경은 사용자에게 "정말 배포하시겠습니까? 서비스: X, 버전: Y" 확인 요청
3. 배포 후 30초 대기, 다시 get_deploy_status로 결과 확인
opencode의 권한 체계(ask 모드)와 시스템 프롬프트의 규칙이 이중 안전장치로 작동합니다.
패턴 3 — RAG 파이프라인 연결
MCP의 리소스(resources) 기능을 활용하여, 사내 문서·위키·규정집을 에이전트의 컨텍스트에 주입하는 패턴입니다.
// MCP 서버에서 리소스 정의
server.resource(
"coding-standards",
"company://docs/coding-standards",
async (uri) => {
// 사내 위키 API에서 코딩 표준 문서 조회
const doc = await wikiClient.getPage("coding-standards");
return {
contents: [
{
uri: uri.href,
mimeType: "text/markdown",
text: doc.content,
},
],
};
}
);
// 동적 리소스 — 특정 규정 문서 조회
server.resource(
"regulation",
new ResourceTemplate("company://regulations/{category}/{id}", "사내 규정 문서"),
async (uri, { category, id }) => {
const doc = await complianceClient.getRegulation(category, id);
return {
contents: [
{
uri: uri.href,
mimeType: "text/markdown",
text: doc.content,
},
],
};
}
);
에이전트가 코드 리뷰를 수행할 때 자동으로 관련 코딩 표준과 규정을 참조할 수 있게 됩니다. 7일차에서 배운 컨텍스트 엔지니어링의 MCP 버전이라고 보면 됩니다.
패턴 4 — 멀티 MCP 오케스트레이션
6일차의 서브에이전트 오케스트레이션과 MCP를 결합한 최상위 패턴입니다. Primary 에이전트가 여러 전문 서브에이전트에게 작업을 위임하고, 각 서브에이전트가 자신만의 MCP 서버를 사용합니다.
{
"mcp": {
"deploy-status": {
"command": "node",
"args": ["./.opencode/mcp-servers/deploy-status/dist/index.js"],
"env": { "DEPLOY_API_URL": "${DEPLOY_API_URL}", "DEPLOY_API_TOKEN": "${DEPLOY_API_TOKEN}" }
},
"jira-reader": {
"command": "node",
"args": ["./.opencode/mcp-servers/jira-reader/dist/index.js"],
"env": { "JIRA_BASE_URL": "${JIRA_URL}", "JIRA_TOKEN": "${JIRA_TOKEN}" }
},
"sonarqube": {
"command": "node",
"args": ["./.opencode/mcp-servers/sonarqube/dist/index.js"],
"env": { "SONAR_URL": "${SONAR_URL}", "SONAR_TOKEN": "${SONAR_TOKEN}" }
},
"slack-notifier": {
"command": "node",
"args": ["./.opencode/mcp-servers/slack-notifier/dist/index.js"],
"env": { "SLACK_WEBHOOK": "${SLACK_WEBHOOK_URL}" }
}
}
}
그리고 각 에이전트가 필요한 MCP 서버만 사용합니다:
# .opencode/agents/deploy-agent.md → mcp: [deploy-status, slack-notifier]
# .opencode/agents/review-agent.md → mcp: [sonarqube, jira-reader]
# .opencode/agents/report-agent.md → mcp: [deploy-status, jira-reader, sonarqube]
이렇게 하면 각 에이전트가 자기 역할에 필요한 도구만 갖게 되어, 최소 권한 원칙을 자연스럽게 유지하면서도 시스템 전체가 유기적으로 연결됩니다.

.well-known/opencode로 조직 전체에 배포하기
MCP 서버 설정을 팀원 각자의 opencode.json에 복사·붙여넣기하는 건 비효율적입니다. .well-known/opencode를 사용하면 조직 차원에서 MCP 설정을 중앙 관리하고 원격 배포할 수 있습니다.
원격 설정 파일 구조
사내 도메인의 https://dev-portal.internal.example.com/.well-known/opencode에 다음 JSON을 배포합니다:
{
"$schema": "https://opencode.ai/schema/well-known.json",
"version": "1.0",
"organization": "Example Financial Corp",
"mcp": {
"compliance-checker": {
"url": "https://mcp.internal.example.com/compliance/sse",
"headers": {
"Authorization": "Bearer ${COMPLIANCE_TOKEN}"
}
},
"deploy-status": {
"url": "https://mcp.internal.example.com/deploy/sse",
"headers": {
"Authorization": "Bearer ${DEPLOY_TOKEN}"
}
}
},
"agents": {
"compliance-reviewer": {
"source": "https://dev-portal.internal.example.com/opencode/agents/compliance-reviewer.md"
}
},
"permission": {
"deny": [
{ "tool": "Bash", "command": "rm -rf *" },
{ "tool": "Bash", "command": "curl * | bash" }
]
}
}
프로젝트에서 원격 설정 연결
각 프로젝트의 opencode.json에서 원격 설정을 참조합니다:
{
"remote": "https://dev-portal.internal.example.com/.well-known/opencode",
"mcp": {
"local-db": {
"command": "node",
"args": ["./mcp-servers/local-db/dist/index.js"]
}
}
}
원격 설정의 MCP 서버와 로컬 설정의 MCP 서버가 병합됩니다. 이렇게 하면:
- 조직 표준 도구(규정 검사, 배포 상태)는 중앙에서 일괄 관리
- 프로젝트 고유 도구(로컬 DB 조회)는 각 프로젝트에서 개별 관리
- MCP 서버 URL이나 인증 방식이 바뀌면 중앙 한 곳만 수정
망분리 환경에서는 .well-known/opencode를 사내망 전용 웹 서버에 배포하면 됩니다. 외부 인터넷이 필요하지 않습니다.
금융IT 익명 사례 — 규제 점검 에이전트
어떤 금융사의 IT팀에서는 코드 변경 시마다 내부 규정 준수 여부를 수동으로 검토하는 절차가 있었습니다. 규정 체크리스트가 수십 항목이고, 담당자가 매번 스프레드시트와 코드를 대조하는 데 건당 30분 이상 걸렸습니다.
이 팀은 MCP 서버 두 개를 만들어 에이전트와 결합했습니다:
- regulation-reader: 사내 규정 관리 시스템에서 최신 규정 텍스트를 조회하는 MCP 서버 (리소스 기반)
- checklist-writer: 검토 결과를 사내 체크리스트 시스템에 기록하는 MCP 서버 (도구 기반, 승인 모드)
에이전트 구성:
---
name: ComplianceReviewer
description: 코드 변경사항의 내부 규정 준수 여부를 점검하는 에이전트
model: anthropic/claude-sonnet-4-10
mcp:
- regulation-reader
- checklist-writer
permission:
allow:
- Read
- Glob
- Grep
deny:
- Bash
- Edit
- Write
---
당신은 금융 규정 준수 코드 리뷰어입니다.
## 점검 절차
1. 변경된 파일 목록을 확인합니다
2. regulation-reader에서 관련 규정을 조회합니다
3. 각 변경 파일이 규정을 준수하는지 항목별로 검토합니다
4. 위반 사항이 있으면 구체적 규정 조항과 함께 보고합니다
5. checklist-writer로 검토 결과를 기록합니다 (반드시 사용자 확인 후)
## 점검 항목 예시
- 개인정보 처리: 로깅에 민감 정보가 포함되지 않는가
- 암호화 기준: 저장/전송 데이터 암호화 규정 준수
- 접근 제어: 인증/인가 로직의 적정성
- 감사 추적: 주요 행위의 로깅 구현 여부
이 구성의 핵심은 세 가지입니다:
- 에이전트는 코드를 읽기만 합니다 — Edit/Write/Bash가 deny이므로 코드를 수정하지 않습니다
- 규정은 항상 최신 버전 — MCP 서버가 규정 시스템에서 실시간 조회하므로, 규정이 바뀌면 에이전트도 즉시 반영합니다
- 감사 추적 가능 — checklist-writer가 기록을 남기므로, 누가 언제 어떤 코드를 검토했는지 추적할 수 있습니다
결과적으로 건당 30분 걸리던 규정 검토가 2~3분으로 줄었고, 검토 누락률도 크게 감소했다고 합니다. 물론 최종 판단은 여전히 사람이 하지만, 에이전트가 1차 스크리닝을 해주는 것만으로도 효과가 상당했습니다.
한 가지 중요한 점 — 이 팀은 MCP 서버를 사내망 안에서만 운영했습니다. 규정 텍스트, 코드 변경 내역, 체크리스트 기록 모두 사내망 밖으로 나가지 않습니다. LLM 추론만 외부 API(또는 BYOK 키)를 사용하되, MCP 서버가 다루는 데이터는 완전히 사내에 머물렀습니다. 망분리 환경에서 MCP를 활용하는 현실적인 모범 패턴이라 할 수 있습니다.

MCP 서버 보안 설계 체크리스트
사내 시스템을 에이전트에 연결한다는 건 보안 관점에서 새로운 공격 면(attack surface)을 여는 것이기도 합니다. 다음 체크리스트를 MCP 서버 개발 시 반드시 점검하세요:
- 최소 권한 API: MCP 서버가 사용하는 사내 API 계정은 필요한 최소한의 권한만 부여합니다. 배포 상태 조회 서버에 배포 트리거 권한을 주지 마세요
- 입력 검증: 에이전트가 전달하는 파라미터를 Zod 스키마로 엄격히 검증합니다. 특히 서비스 이름, 환경 이름처럼 enum으로 제한할 수 있는 값은 반드시 제한하세요
- 토큰 관리: API 토큰은
${ENV_VAR}참조로만 전달합니다.opencode.json이나 MCP 서버 코드에 토큰을 하드코딩하지 마세요..gitignore에.env패턴을 추가하는 것도 잊지 마세요 - 네트워크 격리: SSE/HTTP 방식 MCP 서버는 사내망 안에서만 접근 가능하게 네트워크를 구성합니다
- 로깅: MCP 서버의 도구 호출 로그를 남기되, 민감 데이터(계좌번호, 개인정보 등)는 마스킹합니다
- 타임아웃: 사내 API 응답이 느릴 수 있으므로, MCP 서버에 적절한 타임아웃을 설정합니다. 무한 대기는 에이전트 세션 전체를 블록합니다
MCP 서버 디버깅 실전 팁
MCP 서버를 개발하다 보면 “도구가 등록은 됐는데 에이전트가 호출하지 않는다”거나 “호출은 하는데 에러가 난다” 같은 상황을 자주 만납니다.
디버깅 단계
- 서버 단독 실행 확인: MCP 서버를 터미널에서 직접 실행하고 stdin으로 JSON-RPC 메시지를 보내 응답을 확인합니다
- 도구 목록 확인: opencode 실행 후
/tools명령으로 등록된 MCP 도구 목록이 보이는지 확인합니다 - description 점검: 에이전트가 도구를 호출하지 않는다면,
description이 모호할 가능성이 높습니다. 도구가 무엇을 하는지, 언제 써야 하는지를 구체적으로 적어야 에이전트가 올바른 타이밍에 호출합니다 - stderr 로그 확인: stdio 전송에서 MCP 서버의
console.error()출력은 opencode 로그에 나타납니다. 디버그 정보는 반드시 stderr로 출력하세요 — stdout은 MCP 프로토콜 통신 채널입니다
// 디버그 로깅은 항상 stderr로
console.error("[deploy-status] tool called:", toolName, params);
// 절대 이렇게 하지 마세요 — stdout은 MCP 프로토콜 채널!
// console.log("debug:", something); ← 프로토콜 파싱 깨짐
Gotcha 미니 코너 — description이 에이전트의 눈이다
함정: MCP 도구의 description을 대충 적으면 에이전트가 도구를 아예 호출하지 않거나, 엉뚱한 타이밍에 호출합니다.
나쁜 예:
server.tool(
"check",
"체크합니다", // 뭘 체크하는지, 언제 쓰는지 전혀 모름
{ id: z.string() },
async ({ id }) => { /* ... */ }
);
좋은 예:
server.tool(
"check_compliance_status",
"특정 PR의 규정 준수 점검 상태를 조회합니다. PR 번호를 입력하면 점검 완료 여부, 위반 항목 수, 마지막 점검 시각을 반환합니다. 코드 리뷰 시 규정 준수 여부를 확인할 때 사용하세요",
{ prNumber: z.number().describe("GitHub PR 번호") },
async ({ prNumber }) => { /* ... */ }
);
에이전트는 도구의 description을 읽고 “이 도구를 지금 호출해야 하나?”를 판단합니다. description이 구체적일수록 에이전트의 도구 선택 정확도가 올라갑니다. 3일차에서 시스템 프롬프트의 구체성을 강조했는데, MCP 도구의 description도 정확히 같은 원리입니다. 도구 이름, 파라미터 설명(.describe())까지 전부 에이전트에게 보이는 정보이므로 모두 신경 써야 합니다.
전체 구성 통합 — 오늘의 완성 설정
오늘 다룬 내용을 하나의 opencode.json으로 통합한 전체 설정입니다:
{
"remote": "https://dev-portal.internal.example.com/.well-known/opencode",
"mcp": {
"deploy-status": {
"command": "node",
"args": ["./.opencode/mcp-servers/deploy-status/dist/index.js"],
"env": {
"DEPLOY_API_URL": "${DEPLOY_API_URL}",
"DEPLOY_API_TOKEN": "${DEPLOY_API_TOKEN}"
}
}
},
"agent": {
"DeployAgent": {
"description": "배포 상태 조회 및 리포트 전문 에이전트",
"model": "anthropic/claude-sonnet-4-10",
"mcp": ["deploy-status"],
"permission": {
"deny": ["Bash", "Edit", "Write"]
}
}
}
}
이 설정 하나로 원격 조직 설정(remote)에서 공용 MCP 서버를 가져오고, 로컬 MCP 서버(deploy-status)를 추가하며, 전용 에이전트(DeployAgent)까지 결합됩니다. 시즌 2에서 1일차부터 쌓아온 에이전트 아키텍처·권한·모델 라우팅·오케스트레이션이 MCP라는 외부 연결 고리를 통해 비로소 실세계와 만나는 순간입니다.
내일 예고
오늘 MCP로 에이전트에 외부 시스템을 연결했다면, 내일 11일차에서는 여러 에이전트가 동시에 병렬로 작업하면서 서로의 결과를 공유하는 멀티 에이전트 시스템을 구축합니다. 병렬 백그라운드 실행, 에이전트 간 공유 메모리(hive mind 패턴), fallback chain으로 복원력 있는 시스템을 설계합니다 — MCP로 연결한 도구들이 멀티 에이전트 위에서 풀가동되는 모습을 보게 될 겁니다.
Photo by David McElwee on Pexels
자주 묻는 질문
opencode에서 MCP 서버를 등록하려면 어떻게 설정하나요?
opencode.json 파일의 mcp 키에 서버 이름, 실행 명령어(command), 인자(args), 환경변수(env)를 지정하면 됩니다. 로컬 프로세스는 stdio 방식으로 opencode가 직접 프로세스를 띄워 stdin/stdout으로 통신하고, 원격 서버는 SSE/Streamable HTTP 방식으로 연결할 수 있습니다.
opencode MCP 연동에서 stdio 방식과 SSE/HTTP 방식의 차이점은 무엇인가요?
stdio 방식은 opencode가 로컬에서 직접 프로세스를 실행하고 stdin/stdout으로 통신하는 가장 간단한 형태입니다. SSE/Streamable HTTP 방식은 팀 공용 서버나 사내 인프라처럼 원격에 있는 MCP 서버에 네트워크를 통해 접속하는 방식으로, 조직 전체가 공유하는 도구 서버에 적합합니다.
opencode MCP 서버 설정에서 API 토큰 같은 민감 정보는 어떻게 관리하나요?
env 필드에서 ${VAR_NAME} 형식으로 시스템 환경변수를 참조할 수 있어, 토큰을 설정 파일에 직접 노출하지 않고 안전하게 주입할 수 있습니다. 예를 들어 DEPLOY_API_TOKEN에 ${DEPLOY_TOKEN}처럼 지정하면 시스템 환경변수에서 값을 가져옵니다.
[…] 시즌 2 심화 — 나만의 도메인 특화 에이전트 만들기 (총 12화 중 11화)◀ 이전 10화 (다음 차수는 아직 게시되지 […]
[…] 📚 시리즈: opencode 시즌 2 심화 — 나만의 도메인 특화 에이전트 만들기 (총 12화 중 9화)◀ 이전 8화 다음 10화 ▶ MCP 연동 심화 — 사내 시스템과 에이전트 결합 […]