채널 레퍼런스
MCP 기반 채널 서버 구축 레퍼런스: capability 선언, 알림 포맷, 응답 도구, 보안 설정
채널 레퍼런스
웹훅, 알림, 채팅 메시지를 Claude Code 세션에 푸시하는 MCP 서버를 구축합니다. 채널 규약에 대한 레퍼런스: capability 선언, 알림 이벤트, 응답 도구, 발신자 제어.
참고: 채널은 리서치 프리뷰 단계이며 Claude Code v2.1.80 이상이 필요합니다. claude.ai 로그인이 필요하며, Console 및 API 키 인증은 지원되지 않습니다. Team 및 Enterprise 조직은 명시적으로 활성화해야 합니다.
채널은 Claude Code 세션에 이벤트를 푸시하는 MCP 서버로, 터미널 외부에서 발생하는 일에 Claude가 반응할 수 있게 해줍니다.
단방향 또는 양방향 채널을 구축할 수 있습니다. 단방향 채널은 알림, 웹훅, 모니터링 이벤트를 Claude가 처리하도록 전달합니다. 채팅 브리지와 같은 양방향 채널은 응답 도구를 노출하여 Claude가 메시지를 되돌려 보낼 수 있게 합니다.
이 페이지에서 다루는 내용:
- 개요: 채널 작동 방식
- 필요한 것: 요구 사항 및 일반 단계
- 예제: 웹훅 수신기 구축: 최소한의 단방향 워크스루
- 서버 옵션: 생성자 필드
- 알림 포맷: 이벤트 페이로드
- 응답 도구 노출: Claude가 메시지를 되돌려 보낼 수 있게 하기
- 인바운드 메시지 제어: 프롬프트 인젝션을 방지하기 위한 발신자 검사
기존 채널을 사용하려면(구축하지 않고) 채널을 참고하세요. Telegram, Discord, fakechat이 리서치 프리뷰에 포함되어 있습니다.
개요
채널은 Claude Code와 같은 머신에서 실행되는 MCP 서버입니다. Claude Code가 이를 서브프로세스로 생성하고 stdio를 통해 통신합니다. 채널 서버는 외부 시스템과 Claude Code 세션 사이의 브리지입니다:
- 채팅 플랫폼 (Telegram, Discord): 플러그인이 로컬에서 실행되며 플랫폼의 API를 폴링하여 새 메시지를 확인합니다. 누군가 봇에 DM을 보내면, 플러그인이 메시지를 받아 Claude에게 전달합니다. 노출할 URL이 필요 없습니다.
- 웹훅 (CI, 모니터링): 서버가 로컬 HTTP 포트에서 리스닝합니다. 외부 시스템이 해당 포트로 POST를 보내고, 서버가 페이로드를 Claude에게 푸시합니다.
필요한 것
유일한 필수 요구 사항은 @modelcontextprotocol/sdk 패키지와 Node.js 호환 런타임입니다. Bun, Node, Deno 모두 사용 가능합니다. 리서치 프리뷰의 사전 빌드된 플러그인은 Bun을 사용하지만, 직접 만드는 채널에서 반드시 사용할 필요는 없습니다.
서버는 다음을 수행해야 합니다:
- Claude Code가 알림 리스너를 등록하도록
claude/channelcapability를 선언 - 무언가 발생하면
notifications/claude/channel이벤트를 발행 - stdio transport를 통해 연결 (Claude Code가 서버를 서브프로세스로 생성)
서버 옵션과 알림 포맷 섹션에서 각각에 대해 자세히 다룹니다. 전체 워크스루는 예제: 웹훅 수신기 구축을 참고하세요.
리서치 프리뷰 기간 동안 커스텀 채널은 승인된 허용 목록에 포함되지 않습니다. 로컬 테스트에는 --dangerously-load-development-channels를 사용하세요. 자세한 내용은 리서치 프리뷰 중 테스트를 참고하세요.
예제: 웹훅 수신기 구축
이 워크스루에서는 HTTP 요청을 수신하여 Claude Code 세션으로 전달하는 단일 파일 서버를 구축합니다. 완료되면 CI 파이프라인, 모니터링 알림, curl 명령 등 HTTP POST를 보낼 수 있는 모든 것이 Claude에 이벤트를 푸시할 수 있습니다.
이 예제에서는 내장 HTTP 서버와 TypeScript 지원을 위해 Bun을 런타임으로 사용합니다. Node나 Deno를 대신 사용할 수 있으며, 유일한 요구 사항은 MCP SDK입니다.
1단계: 프로젝트 생성
새 디렉토리를 만들고 MCP SDK를 설치합니다:
mkdir webhook-channel && cd webhook-channel
bun add @modelcontextprotocol/sdk
2단계: 채널 서버 작성
webhook.ts라는 파일을 만듭니다. 이것이 전체 채널 서버입니다: stdio를 통해 Claude Code에 연결하고, 포트 8788에서 HTTP POST를 수신합니다. 요청이 도착하면 본문을 채널 이벤트로 Claude에 푸시합니다.
#!/usr/bin/env bun
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
// MCP 서버를 생성하고 채널로 선언
const mcp = new Server(
{ name: 'webhook', version: '0.0.1' },
{
// 이 키가 채널로 만드는 것 — Claude Code가 이에 대한 리스너를 등록
capabilities: { experimental: { 'claude/channel': {} } },
// Claude의 시스템 프롬프트에 추가되어 이 이벤트를 처리하는 방법을 알려줌
instructions: 'Events from the webhook channel arrive as <channel source="webhook" ...>. They are one-way: read them and act, no reply expected.',
},
)
// stdio를 통해 Claude Code에 연결 (Claude Code가 이 프로세스를 생성)
await mcp.connect(new StdioServerTransport())
// 모든 POST를 Claude에 전달하는 HTTP 서버 시작
Bun.serve({
port: 8788, // 열린 포트면 무엇이든 가능
// localhost 전용: 이 머신 외부에서는 POST 불가
hostname: '127.0.0.1',
async fetch(req) {
const body = await req.text()
await mcp.notification({
method: 'notifications/claude/channel',
params: {
content: body, // <channel> 태그의 본문이 됨
// 각 키는 태그 속성이 됨, 예: <channel path="/" method="POST">
meta: { path: new URL(req.url).pathname, method: req.method },
},
})
return new Response('ok')
},
})
이 파일은 순서대로 세 가지를 수행합니다:
- 서버 구성: capabilities에
claude/channel을 포함하여 MCP 서버를 생성합니다. 이것이 Claude Code에게 이것이 채널임을 알려주는 것입니다.instructions문자열은 Claude의 시스템 프롬프트에 추가됩니다: 어떤 이벤트를 예상하고, 응답해야 하는지, 응답해야 한다면 어떻게 라우팅할지 Claude에게 알려줍니다. - Stdio 연결: stdin/stdout을 통해 Claude Code에 연결합니다. 이것은 모든 MCP 서버에 대한 표준입니다: Claude Code가 서브프로세스로 생성합니다.
- HTTP 리스너: 포트 8788에서 로컬 웹 서버를 시작합니다. 모든 POST 본문은
mcp.notification()을 통해 채널 이벤트로 Claude에 전달됩니다.content는 이벤트 본문이 되고, 각meta항목은<channel>태그의 속성이 됩니다. 리스너는mcp인스턴스에 접근해야 하므로 같은 프로세스에서 실행됩니다. 더 큰 프로젝트에서는 별도 모듈로 분리할 수 있습니다.
3단계: Claude Code에 서버 등록
Claude Code가 서버를 시작하는 방법을 알 수 있도록 .mcp.json에 서버를 추가합니다. 같은 디렉토리의 프로젝트 수준 .mcp.json에 추가하는 경우 상대 경로를 사용합니다. 사용자 수준 ~/.mcp.json에 추가하는 경우 전체 절대 경로를 사용합니다:
{
"mcpServers": {
"webhook": { "command": "bun", "args": ["./webhook.ts"] }
}
}
Claude Code는 시작 시 .mcp.json을 읽고 각 서버를 서브프로세스로 생성합니다.
4단계: 테스트
리서치 프리뷰 기간 동안 커스텀 채널은 허용 목록에 없으므로, 개발 플래그와 함께 Claude Code를 시작합니다:
claude --dangerously-load-development-channels server:webhook
Claude Code가 시작되면 .mcp.json을 읽고, webhook.ts를 서브프로세스로 생성하며, HTTP 리스너가 구성한 포트(이 예에서는 8788)에서 자동으로 시작됩니다. 서버를 직접 실행할 필요가 없습니다.
"blocked by org policy"가 표시되면, Team 또는 Enterprise 관리자가 먼저 채널을 활성화해야 합니다.
별도의 터미널에서, 서버에 메시지가 포함된 HTTP POST를 보내 웹훅을 시뮬레이션합니다. 이 예에서는 포트 8788(또는 구성한 포트)로 CI 실패 알림을 보냅니다:
curl -X POST localhost:8788 -d "build failed on main: https://ci.example.com/run/1234"
페이로드가 <channel> 태그로 Claude Code 세션에 도착합니다:
<channel source="webhook" path="/" method="POST">build failed on main: https://ci.example.com/run/1234</channel>
Claude Code 터미널에서 Claude가 메시지를 수신하고 응답을 시작하는 것을 볼 수 있습니다: 파일 읽기, 명령 실행 등 메시지가 요구하는 작업을 수행합니다. 이것은 단방향 채널이므로, Claude는 세션에서 동작하지만 웹훅을 통해 아무것도 되돌려 보내지 않습니다. 응답을 추가하려면 응답 도구 노출을 참고하세요.
fakechat 서버는 이 패턴을 웹 UI, 파일 첨부, 양방향 채팅을 위한 응답 도구로 확장합니다.
리서치 프리뷰 중 테스트
리서치 프리뷰 기간 동안 모든 채널은 등록하려면 승인된 허용 목록에 있어야 합니다. 개발 플래그는 확인 프롬프트 후 특정 항목에 대해 허용 목록을 우회합니다. 이 예에서는 두 가지 항목 유형을 보여줍니다:
# 개발 중인 플러그인 테스트
claude --dangerously-load-development-channels plugin:yourplugin@yourmarketplace
# 베어 .mcp.json 서버 테스트 (아직 플러그인 래퍼 없음)
claude --dangerously-load-development-channels server:webhook
우회는 항목별로 적용됩니다. 이 플래그를 --channels와 결합해도 --channels 항목에는 우회가 확장되지 않습니다. 리서치 프리뷰 기간 동안 승인된 허용 목록은 Anthropic이 관리하므로, 구축 및 테스트하는 동안 채널은 개발 플래그에 머무릅니다.
참고: 이 플래그는 허용 목록만 건너뜁니다.
channelsEnabled조직 정책은 여전히 적용됩니다. 신뢰할 수 없는 소스의 채널을 실행하는 데 사용하지 마세요.
서버 옵션
채널은 Server 생성자에서 다음 옵션을 설정합니다. instructions와 capabilities.tools 필드는 표준 MCP이며, capabilities.experimental['claude/channel']이 채널 전용 추가 사항입니다:
| 필드 | 타입 | 설명 |
|---|---|---|
capabilities.experimental['claude/channel'] | object | 필수. 항상 {}. 존재 자체가 알림 리스너를 등록합니다. |
capabilities.tools | object | 양방향 전용. 항상 {}. 표준 MCP 도구 capability. 응답 도구 노출을 참고하세요. |
instructions | string | 권장. Claude의 시스템 프롬프트에 추가됩니다. 어떤 이벤트를 예상하고, <channel> 태그 속성이 무엇을 의미하고, 응답해야 하는지, 응답한다면 어떤 도구를 사용하고 어떤 속성(chat_id 같은)을 되돌려 전달해야 하는지 Claude에게 알려줍니다. |
단방향 채널을 만들려면 capabilities.tools를 생략합니다. 이 예에서는 세 가지 옵션이 모두 설정된 양방향 설정을 보여줍니다:
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
const mcp = new Server(
{ name: 'your-channel', version: '0.0.1' },
{
capabilities: {
experimental: { 'claude/channel': {} }, // 채널 리스너 등록
tools: {}, // 단방향 채널에서는 생략
},
// Claude의 시스템 프롬프트에 추가되어 이벤트 처리 방법을 알려줌
instructions: 'Messages arrive as <channel source="your-channel" ...>. Reply with the reply tool.',
},
)
이벤트를 푸시하려면 method notifications/claude/channel로 mcp.notification()을 호출합니다. 파라미터는 다음 섹션에 있습니다.
알림 포맷
서버는 두 개의 파라미터와 함께 notifications/claude/channel을 발행합니다:
| 필드 | 타입 | 설명 |
|---|---|---|
content | string | 이벤트 본문. <channel> 태그의 본문으로 전달됩니다. |
meta | Record<string, string> | 선택 사항. 각 항목은 채팅 ID, 발신자 이름, 알림 심각도 등 라우팅 컨텍스트를 위한 <channel> 태그의 속성이 됩니다. 키는 식별자여야 합니다: 문자, 숫자, 밑줄만 가능합니다. 하이픈이나 다른 문자를 포함하는 키는 자동으로 무시됩니다. |
서버는 Server 인스턴스에서 mcp.notification()을 호출하여 이벤트를 푸시합니다. 이 예에서는 두 개의 meta 키와 함께 CI 실패 알림을 푸시합니다:
await mcp.notification({
method: 'notifications/claude/channel',
params: {
content: 'build failed on main: https://ci.example.com/run/1234',
meta: { severity: 'high', run_id: '1234' },
},
})
이벤트는 <channel> 태그로 래핑되어 Claude의 컨텍스트에 도착합니다. source 속성은 서버의 구성된 이름에서 자동으로 설정됩니다:
<channel source="your-channel" severity="high" run_id="1234">
build failed on main: https://ci.example.com/run/1234
</channel>
응답 도구 노출
채널이 양방향인 경우(알림 전달기가 아닌 채팅 브리지와 같은), Claude가 메시지를 되돌려 보내기 위해 호출할 수 있는 표준 MCP 도구를 노출합니다. 도구 등록에 채널 전용 요소는 없습니다. 응답 도구에는 세 가지 구성 요소가 있습니다:
- Claude Code가 도구를 검색하도록
Server생성자 capabilities에tools: {}항목 - 도구의 스키마를 정의하고 전송 로직을 구현하는 도구 핸들러
- Claude에게 도구를 언제 어떻게 호출할지 알려주는
Server생성자의instructions문자열
위의 웹훅 수신기에 이를 추가하려면:
1단계: 도구 검색 활성화
webhook.ts의 Server 생성자에서, Claude Code가 서버에 도구가 있음을 알 수 있도록 capabilities에 tools: {}를 추가합니다:
capabilities: {
experimental: { 'claude/channel': {} },
tools: {}, // 도구 검색 활성화
},
2단계: 응답 도구 등록
webhook.ts에 다음을 추가합니다. import는 파일 상단의 다른 import와 함께, 두 핸들러는 Server 생성자와 mcp.connect() 사이에 배치합니다. chat_id와 text로 Claude가 호출할 수 있는 reply 도구를 등록합니다:
// webhook.ts 상단에 이 import 추가
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js'
// Claude가 시작 시 서버가 제공하는 도구를 검색하기 위해 쿼리
mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{
name: 'reply',
description: 'Send a message back over this channel',
// inputSchema는 Claude에게 전달할 인수를 알려줌
inputSchema: {
type: 'object',
properties: {
chat_id: { type: 'string', description: 'The conversation to reply in' },
text: { type: 'string', description: 'The message to send' },
},
required: ['chat_id', 'text'],
},
}],
}))
// Claude가 도구를 호출하려 할 때 실행
mcp.setRequestHandler(CallToolRequestSchema, async req => {
if (req.params.name === 'reply') {
const { chat_id, text } = req.params.arguments as { chat_id: string; text: string }
// 플랫폼의 전송 API
await yourPlatform.send(chat_id, text)
return { content: [{ type: 'text', text: 'sent' }] }
}
throw new Error(`unknown tool: ${req.params.name}`)
})
3단계: instructions 업데이트
Claude가 도구를 통해 응답을 라우팅하도록 Server 생성자의 instructions 문자열을 업데이트합니다. 이 예에서는 인바운드 태그에서 chat_id를 전달하도록 Claude에게 알려줍니다:
instructions: 'Messages arrive as <channel source="webhook" chat_id="...">. Reply with the reply tool, passing the chat_id from the tag.'
다음은 단방향 수신기 워크스루와 응답 도구 추가를 결합한 완전한 webhook.ts입니다:
#!/usr/bin/env bun
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js'
const mcp = new Server(
{ name: 'webhook', version: '0.0.1' },
{
capabilities: {
experimental: { 'claude/channel': {} },
tools: {},
},
instructions: 'Messages arrive as <channel source="webhook" chat_id="...">. Reply with the reply tool, passing the chat_id from the tag.',
},
)
mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{
name: 'reply',
description: 'Send a message back over this channel',
inputSchema: {
type: 'object',
properties: {
chat_id: { type: 'string', description: 'The conversation to reply in' },
text: { type: 'string', description: 'The message to send' },
},
required: ['chat_id', 'text'],
},
}],
}))
mcp.setRequestHandler(CallToolRequestSchema, async req => {
if (req.params.name === 'reply') {
const { chat_id, text } = req.params.arguments as { chat_id: string; text: string }
// 플랫폼의 전송 API — 실제 통합으로 교체
console.error(`Reply to ${chat_id}: ${text}`)
return { content: [{ type: 'text', text: 'sent' }] }
}
throw new Error(`unknown tool: ${req.params.name}`)
})
await mcp.connect(new StdioServerTransport())
let nextId = 1
Bun.serve({
port: 8788,
hostname: '127.0.0.1',
async fetch(req) {
const body = await req.text()
const chat_id = String(nextId++)
await mcp.notification({
method: 'notifications/claude/channel',
params: {
content: body,
meta: { chat_id, path: new URL(req.url).pathname, method: req.method },
},
})
return new Response('ok')
},
})
fakechat 서버에서 파일 첨부와 메시지 편집이 포함된 보다 완전한 예제를 확인할 수 있습니다.
인바운드 메시지 제어
제어되지 않는 채널은 프롬프트 인젝션 벡터입니다. 엔드포인트에 접근할 수 있는 누구나 Claude 앞에 텍스트를 넣을 수 있습니다. 채팅 플랫폼이나 공개 엔드포인트를 수신하는 채널에는 이벤트를 발행하기 전에 실제 발신자 검사가 필요합니다.
mcp.notification()을 호출하기 전에 허용 목록과 발신자를 대조합니다. 이 예에서는 세트에 없는 발신자의 메시지를 무시합니다:
const allowed = new Set(loadAllowlist()) // access.json 또는 이에 상응하는 파일에서
// 메시지 핸들러 내부에서 발행 전:
if (!allowed.has(message.from.id)) { // 방이 아닌 발신자
return // 자동으로 무시
}
await mcp.notification({ ... })
방(room) ID가 아닌 발신자의 ID로 제어합니다: 예에서 message.chat.id가 아닌 message.from.id입니다. 그룹 채팅에서는 이 둘이 다르며, 방 기준으로 제어하면 허용된 그룹의 누구나 세션에 메시지를 주입할 수 있게 됩니다.
Telegram과 Discord 채널은 같은 방식으로 발신자 허용 목록으로 제어합니다. 페어링을 통해 목록을 초기화합니다: 사용자가 봇에 DM을 보내고, 봇이 페어링 코드로 응답하고, 사용자가 Claude Code 세션에서 승인하면 플랫폼 ID가 추가됩니다. 전체 페어링 흐름은 각 구현을 참고하세요.
플러그인으로 패키징
채널을 설치 및 공유 가능하게 만들려면, 플러그인으로 래핑하고 마켓플레이스에 게시합니다. 사용자는 /plugin install로 설치한 다음, --channels plugin:<name>@<marketplace>로 세션별로 활성화합니다.
자체 마켓플레이스에 게시된 채널은 승인된 허용 목록에 없으므로 여전히 --dangerously-load-development-channels가 필요합니다. 목록에 추가하려면 공식 마켓플레이스에 제출하세요. 채널 플러그인은 승인 전에 보안 검토를 거칩니다.
관련 참고
- 채널: Telegram, Discord, fakechat 데모를 설치하고 사용하며, Team 또는 Enterprise 조직에서 채널을 활성화하기
- 작동하는 채널 구현: 페어링 흐름, 응답 도구, 파일 첨부가 포함된 완전한 서버 코드
- MCP: 채널 서버가 구현하는 기반 프로토콜
- 플러그인: 사용자가
/plugin install로 설치할 수 있도록 채널을 패키징하기