
Slackbot MCP Client를 재검증했더니 사이콜 MCP를 Slackbot에서 호출할 수 있었다
요약
Slackbot MCP Client를 사용하여 MCP(Model Context Protocol) 서버를 호출하는 과정을 재검증한 기술 리뷰입니다. 공식 샘플을 활용해 인증 없는 Dice MCP 서버를 Slackbot에서 성공적으로 호출하고, 도구 호출 시 인자 전달 방식의 특이사항을 확인했습니다.
핵심 포인트
- Slackbot MCP Client를 통한 MCP 서버 호출 성공
- no-auth 방식의 Dice MCP 서버 연동 검증
- 자연어 입력 시 MCP 도구 인자 전달의 한계 확인
- Rich Responses를 활용한 UI 리소스 반환 가능성 확인
지난번에 멈췄던 부분
지난번에 Slackbot MCP Client를 시도해 본 기사를 작성했습니다.
2026년 6월 19일 시점에서는, 제 환경에서 mcp:connect를 manifest에 넣으면 illegal_bot_scopes로 인해 멈췄습니다. App Settings에 MCP Servers도 나타나지 않았고, Slackbot으로부터 MCP tool을 호출하는 단계까지는 진행할 수 없었습니다.
그로부터 시간이 조금 흐른 뒤, Slack의 공식 샘플에 Slackbot MCP Client 예시가 추가되어 있었습니다.
이번에는 그 공식 샘플을 보며 재검증을 진행했습니다. 제 환경에서는 2026년 6월 30일 시점에 mcp:connect가 포함된 manifest validate를 통과하였고, no-auth 방식의 사이콜 (Dice) MCP server를 Slackbot에서 호출하는 단계까지 갈 수 있었습니다.
지난번에는 Slack 측의 입구에서 막혔습니다. 이번에는 Slackbot의 App 메뉴에 Dice Game이 나타났고, tool call 전의 허가 카드도 나타났으며, App Settings의 Logs에 connection_success와 tool_call_success가 남았습니다.

다만, 모든 것이 깔끔하게 작동한 것은 아닙니다. Roll 2d20이라는 자연문일 경우, Slackbot이 tool에 전달하는 인수는 기본값인 1d6이 되었습니다. count=2 and sides=20이라고 인수 이름을 직접 작성하면 2d20으로 작동했습니다. 이 부분은 실제로 접속해 보고 나서야 알게 된 점입니다.
Notion은 나중에
공식 샘플에는 Notion MCP server에 Dynamic Client Registration (DCR)으로 접속하는 예시가 있습니다.
하지만 저는 Notion을 사용하지 않기 때문에, 이번에는 이 부분을 주제로 다루지 않겠습니다.
이번에 살펴볼 것은 다음 두 가지입니다.
no-auth: 직접 만든 MCP server에 인증 없이 접속하는 dice 샘플rich-responses/mcp-apps: MCP Apps를 사용하여 텍스트뿐만 아니라 UI resource도 반환하는 dice 샘플
둘 다 Notion이나 GitHub의 외부 계정 연동을 사용하지 않고 읽을 수 있으므로, Slackbot MCP Client의 입구로서 나쁘지 않습니다.
검증 환경
- 검증일: 2026년 6월 30일
- OS: macOS / darwin arm64
- Slack CLI: v4.3.0
- Node.js: v25.5.0
- npm: 11.8.0
- HTTPS tunnel: cloudflared 2026.6.1
- 대상 리포지토리:
slack-samples/bolt-js-examples - 대상 디렉토리:
ai/slackbot-mcp-client
Slack CLI는 지난번과 동일하게 v4.3.0입니다.
$ slack version
Using slack v4.3.0
GitHub Releases 상에서도 확인 시점의 latest는 v4.3.0이었습니다.
v4.3.0
2026-06-17T23:28:41Z
공식 샘플의 구성
ai/slackbot-mcp-client에는 인증 방식이나 반환 방식에 따라 샘플이 나누어져 있습니다.
README.md
dynamic-client-registration/
external-auth/
...
표로 정리하면 다음과 같은 위치를 가집니다.
| 디렉토리 | 내용 |
|---|---|
dynamic-client-registration | Notion MCP server에 DCR로 접속하는 manifest 예시 |
external-auth | GitHub MCP server에 manual OAuth로 접속하는 manifest 예시 |
no-auth | 인증 없는 자작 MCP server를 구축하는 예시 |
slack-identity | Slack의 user/team identity를 사용하여 자작 MCP server를 호출하는 예시 |
rich-responses/mcp-apps | MCP Apps로 인터랙티브 UI를 반환하는 예시 |
이전 기사에서 작성한 "Slackbot이 MCP Client가 되어 외부 MCP server를 호출한다"는 방향성은 변하지 않습니다. Slack MCP Server와는 방향이 반대입니다.
mcp:connect가 포함된 manifest의 validate
지난번에는 여기서 멈췄습니다.
Illegal bot scopes found `mcp:connect` (illegal_bot_scopes)
이번에는 공식 샘플의 manifest를 그대로 사용하여 slack manifest validate를 실행했습니다.
대상은 다음 4가지입니다.
no-auth
dynamic-client-registration
external-auth
rich-responses/mcp-apps
명령어는 다음과 같은 형태입니다. 공개된 기사이므로 team 값은 생략합니다.
$ slack manifest validate --source manifest.json --team ...
no-auth는 valid(유효)하게 되었습니다.
App Manifest Validation Result: Valid
dynamic-client-registration도 valid입니다.
App Manifest Validation Result: Valid
rich-responses/mcp-apps도 valid입니다.
App Manifest Validation Result: Valid
external-auth는 warning(경고)이 포함되어 있었습니다.
The following warnings were raised during manifest validation
Your app has oauth2 providers. Make sure to add a client secret for each provider via `slack external-auth add-secret` (oauth2_provider_warning)
이는 GitHub OAuth provider의 client secret을 아직 등록하지 않았다는 경고입니다. manifest 자체가 mcp:connect 때문에 실패한 것은 아닙니다.
지난번에는 여기서 멈췄기 때문에, validate가 통과된 것만으로도 상황은 달라졌습니다. 적어도 제 환경에서는 2026년 6월 30일 시점에 mcp:connect가 포함된 manifest의 validate까지 진행할 수 있게 되었습니다.
no-auth의 서명 검증
먼저 no-auth를 살펴보았습니다.
이 샘플은 Bolt app의 custom route로서 /mcp를 생성하고, MCP server를 Streamable HTTP로 받는 구성입니다. tool은 roll_dice입니다.
roll_dice의 schema에서는 sides의 default(기본값)가 6이고, count의 default가 1입니다. 나중에 나올 Roll 2d20이 1d6이 되었던 이야기에서는, 이 default 값이 작용했을 가능성이 있습니다. Slackbot 측의 인자 생성만이 원인이라고는 아직 단정 지을 수 없습니다.
manifest.json에서는 bot scope에 mcp:connect만 들어가 있습니다.

{
"oauth_config": {
"scopes": {
...
no_auth는 MCP server 측에서 추가적인 외부 인증을 하지 않는다는 의미입니다. Slack으로부터 온 request인지 확인하는 서명 검증은 별개입니다.
src/app.js에서는 isValidSlackRequest를 통해 Slack으로부터 온 request인지 확인하며, 서명이 없으면 401을 반환합니다.
로컬 테스트도 그 부분을 확인하고 있습니다.
$ npm test
> test
> node --test
...
테스트에서 확인하고 있는 것은 다음 두 가지입니다.
- Slack 서명이 포함된 MCP request라면
roll_dice의 tool call이 반환된다. - 서명이 없는 request는 401이 된다.
타입 체크 (Type check)도 통과했습니다.
$ npm run check
> check
> npx tsc --checkJs --noEmit app.js
Lint 에러도 없습니다. 다만 Biome 설정에서 deprecated warning이 발생하고 있습니다.
$ npm run lint
> lint
> npx @biomejs/biome check .
...
npm install
시점에서는 high severity vulnerability가 2건 나타났습니다. 이번에는 샘플 검증이므로 깊게 파고들지는 않았지만, 그대로 프로덕션(production)용으로 사용할 것은 아니라는 관점으로 보고 있습니다.
Slackbot에서 Dice Game 호출하기
여기서부터가 이번의 본론입니다.
공식 README는 ngrok http 3000을 사용하고 있지만, 제 로컬 환경에는 ngrok이 설치되어 있지 않았습니다. 이번에는 cloudflared tunnel --url http://localhost:3000을 사용하여 일시적인 HTTPS URL을 만들었습니다.
$ cloudflared tunnel --url http://localhost:3000
발행된 URL은 일시적인 것이므로 기사에는 기재하지 않겠습니다. manifest의 mcp_servers.Dice Game.url을 해당 URL의 /mcp로 교체했습니다.
{
"mcp_servers": {
"Dice Game": {
...
교체 후 validate를 통과했습니다.
$ slack manifest validate --source manifest.json --team ...
App Manifest Validation Result: Valid
그 후, local app으로 install 했습니다.
$ slack install --team ... --environment local --org-workspace-grant all --force
Organization app 형태로 설치되었으며, 여러 개의 workspace grant가 표시되었습니다. 이 부분은 Enterprise Grid / sandbox 환경 구축 방식에 따라 달라질 것 같습니다.
다음으로 App Settings에서 Signing Secret을 확인하여 .env에 설정합니다. 샘플은 no_auth 상태에서도 Slack 서명 검증을 수행하므로, 이 값을 입력하지 않으면 /mcp에서 401 에러가 발생합니다.
SLACK_SIGNING_SECRET=...
PORT=3000
slack run으로 실행하면, Bolt app이 3000번 포트에서 구동되었습니다.
$ slack run --team ... --app local --org-workspace-grant all --activity-level debug
[INFO] bolt-app ⚡️ Bolt app is running!
이번에는 slack run으로 실행했기 때문에, local app 실행에 필요한 값들이 Slack CLI 측으로부터도 전달됩니다. npm start로 직접 실행하는 경우에는 샘플의 .env.example에 따라 토큰(token)류도 확인해야 합니다.
공개 URL을 통해 서명이 포함된 MCP initialize를 보내자 200 응답이 돌아왔습니다. Slackbot 이전에 터널(tunnel), 서명 검증, MCP transport가 정상적으로 작동하고 있음을 확인할 수 있습니다.
{
"status": 200,
"contentType": "text/event-stream",
...
App Settings의 MCP Servers 화면에는 Dice Game이 표시되었습니다. 일시적인 URL은 숨겼지만, Endpoint URL의 끝은 /mcp로 설정했습니다.

Logs에는 Slackbot 측에서 App을 활성화한 시점에 connection_success가 나타났습니다.
2026-06-30 16:37:12 INFO Dice Game connection_success
Slackbot DM의 입력창에는 App
버튼이 있고, 그곳에 Dice Game이 나타났습니다. 이를 활성화하면 Slackbot에서 이 MCP server를 사용할 수 있는 상태가 됩니다.

처음에 Roll 2d20이라고 보내자, Slackbot은 주사위 결과를 반환했습니다.
2d20 Results:
Die 1: 14
Die 2: 7
...
다만, 이 첫 번째 응답은 조금 까다롭습니다. 이 시점의 App Settings Logs에는 tool_call_success가 남아있지 않아, MCP tool을 사용한 결과라고 단정 지을 수 없었습니다. Slackbot이 자체적으로 주사위를 굴렸을 가능성이 있습니다.
그래서 검증을 위해 roll_dice 내부에서 인자(argument)와 결과를 console.log 하도록 설정했습니다.
console.log(
JSON.stringify({
code: "tool_call",
...
다시 한번 Roll 2d20 again이라고 보내자, Slackbot은 tool call 전에 허가 카드(permission card)를 띄웠습니다.
Dice Game MCP로부터의 Roll Dice를 사용하고 있습니다
한 번만 허가하기
허가하지 않기
...
한 번만 허가하기를 누르자, tool call은 성공했습니다. 하지만 로컬 로그를 확인해보니 2d20이 아니었습니다.
{"code":"tool_call","tool":"roll_dice","sides":6,"count":1,"rolls":[5],"total":5}
2026-06-30 16:43:11 [info] {"code":"tool_call_success","server_name":"Dice Game","tool_name":"roll_dice"}
Slackbot 측도 이 결과를 보고 "1d6이 반환되었다"라고 판단했습니다. 즉, tool call 자체는 성공했지만, Roll 2d20이라는 자연어(natural language)로부터 sides=20, count=2로 변환하는 과정은 이 시점에서 제대로 이루어지지 않았습니다.
그래서 인자 이름을 직접 작성했습니다.
Use the Dice Game Roll Dice tool with count=2 and sides=20.
이것 역시 허가 카드가 나타납니다. 허가하자, 이번에는 기대했던 대로 2d20이 되었습니다.
{"code":"tool_call","tool":"roll_dice","sides":20,"count":2,"rolls":[14,1],"total":15}
2026-06-30 16:45:27 [info] {"code":"tool_call_success","server_name":"Dice Game","tool_name":"roll_dice"}
Slackbot의 답변도 2개의 20면체 주사위 결과로 돌아왔습니다.
Here are your 2d20 rolls!
Die 1: 14
Die 2: 1
...
Slackbot 측의 표시에도 Dice Game MCP의 Roll Dice를 사용한 기록이 남았습니다. 이 스크린샷에서는 한 번만 허가로 실행한 것도 확인할 수 있습니다.

App Settings의 Logs에서도 tool_call_success를 확인할 수 있었습니다.
2026-06-30 16:45:27 INFO Dice Game tool_call_success
2026-06-30 16:44:29 INFO Dice Game tool_call_success
2026-06-30 16:43:11 INFO Dice Game tool_call_success
...
다시 찍은 Logs에서도 최신 tool_call_success가 남아있습니다.

제 검증 결과, Slackbot MCP Client의 최소한의 실제 연결은 여기서 확인할 수 있었습니다.
rich responses는 단체 테스트까지
다음으로 rich-responses/mcp-apps를 살펴보았습니다.
이것 또한 dice 샘플이지만, no-auth보다 한 단계 더 나아가 있습니다.
roll_dice의 반환값(return value)에 structuredContent
가 있으며, 나아가 MCP Apps의 UI resource로서 dice.html을 등록하고 있습니다.
이 샘플은 Slackbot에 텍스트로 결과를 반환할 뿐만 아니라, 인터랙티브 UI (Interactive UI)도 함께 반환하기 위한 것입니다.
로컬 테스트는 3건 통과했습니다.
$ npm test
> test
> node --test
...
no-auth와 마찬가지로, 타입 체크 (Type check)도 통과했습니다.
$ npm run check
> check
> npx tsc --checkJs --noEmit app.js
lint 에도 에러는 없습니다. 이 역시 Biome의 deprecated warning은 발생하고 있습니다.
$ npm run lint
> lint
> npx @biomejs/biome check .
...
Slackbot에서 UI가 어떻게 보이는지까지는 아직 확인하지 못했습니다. 다만, MCP server 단독으로서 tool call과 UI resource를 반환할 수 있다는 점은 샘플 테스트를 통해 확인할 수 있었습니다.
실제 접속을 통해 확인한 것
로그에 남은 내용은 다음과 같습니다.
먼저, App Settings에 MCP Servers가 나타났습니다. 지난번에는 이 부분이 보이지 않았습니다.
다음으로, mcp:connect가 포함된 manifest는 검증 (validate)할 수 있었고, 로컬 앱 (local app)으로서 설치할 수 있었습니다. MCP Servers 화면에도 Dice Game이 표시됩니다.
Slackbot 측에서는 App 버튼을 통해 Dice Game을 선택할 수 있었습니다. tool call 전에는 사용자에게 허가를 요청하는 카드가 나타납니다. 선택지는 한 번만 허가하기, 허가하지 않기, 항상 허가하기였습니다.
tool call 이후에는 App Settings Logs에 tool_call_success가 남았습니다. 로컬 서버 측에도 실제로 전달된 인자 (argument)와 결과를 출력할 수 있었습니다.
반면, 자연어 인자 해석 (natural language argument interpretation)은 아직 완전히 신뢰하기 어렵습니다. Roll 2d20만 입력했을 때는 count=2, sides=20이 되지 않고, 기본값인 count=1, sides=6으로 호출되었습니다. count=2 and sides=20과 같이 인자 이름을 명시하면 통과했습니다.
이 부분은 샘플의 tool schema와 Slackbot 측의 tool argument generation 중 어느 쪽에 치우쳐 있는지 아직 분리하여 파악하지 못했습니다. 적어도 제 검증 결과로는 "Slackbot이 자연어로부터 항상 기대하는 대로의 인자를 생성한다"라고는 말할 수 없습니다.
미확인 상태로 남겨둔 것
이번에는 no-auth의 dice뿐입니다. 다음에 살펴본다면 다음과 같은 부분들입니다.
rich-responses/mcp-apps를 Slackbot에서 호출했을 때, UI resource가 어떻게 표시되는지slack_identity_auth를 통해 Slack user/team identity가 MCP server에 어떻게 전달되는지- Organization install 한 앱이 다른 workspace / 다른 사용자의 Slackbot App 메뉴에 어떻게 보이는지
Always allow를 선택하면 다음번 이후의 tool call 허가가 어떻게 변하는지- tool schema를 조금 변경하면
2d20의 인자 해석이 개선되는지
Notion DCR은 이번에 보지 않습니다. Notion을 사용하고 있지 않으므로, 제 검증 측면에서는 우선순위가 낮습니다.
이번의 도달점
이번의 도달점은 Slackbot에서 직접 만든 MCP server의 roll_dice를 실제로 호출할 수 있게 된 단계까지입니다.
- 공식 샘플의
mcp:connect가 포함된 manifest는 검증할 수 있었다 no-auth의 MCP server 단독 테스트를 통과했다rich-responses/mcp-apps의 MCP server 단독 테스트도 통과했다external-auth는 OAuth secret 미설정 warning은 발생하지만,mcp:connect에서 실패하지는 않는다cloudflared로 로컬/mcp를 공개하고, Slackbot에서Dice Game
를 추가할 수 있었다 - Slackbot의 tool call 전에 허가 카드가 나타났다
roll_dice가 실제로 호출되었고, App Settings Logs에tool_call_success가 남았다count=2 and sides=20이라고 명시하면2d20으로서 동작했다
지난번에는 Slack 측 입구에서 멈췄습니다. 이번에는 Slackbot으로부터 자작 MCP server의 tool call까지 도달했습니다.
다만, 자연어로부터 tool 인수를 어떻게 생성할지는 아직 까다로운 부분이 있습니다. Roll 2d20이라고 쓰는 것만으로는 2d20이 되지 않았고, 인수 이름을 명시하니 통과되었습니다.
다음은 rich-responses/mcp-apps입니다. Slackbot에서 MCP Apps의 UI가 어떻게 보이는지 확인하겠습니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기