Afterhours Kakao MCP
단일 소유자용 독립 인증 Streamable HTTP MCP. 통합 저장소나 다른 MCP를 import하지 않는다. 통합 구현의 Windows bridge/queue를 분리했다. MCP SDK 2.3.0 / Python 3.12. Slack·메일·브리핑 도구는 포함하지 않는다.
새 사용자의 빠른 시작
Linux 서버의 Python 3.12, Windows 대화형 데스크톱과 로그인한 KakaoTalk이 필요하다. 각 사용자가 자기 서버·자기 계정으로 설치한다. 다중 사용자 호스팅 서비스가 아니다. 현재 제공하는 기능은 요청 기반의 열린 방 조회·읽기·답장이다. 자동 수신 이벤트와 네이티브 멘션 감지는 실험용 계약만 있으며, clone만으로 자동 감시가 시작되지 않는다.
git clone https://github.com/deploy103/afterhours-kakao-mcp-public.git
cd afterhours-kakao-mcp-public
python3.12 -m venv .venv
.venv/bin/python -m pip install --require-hashes -r requirements.lock
.venv/bin/python configure.py --directory "$HOME/afterhours-kakao-private"
"$HOME/afterhours-kakao-private/run.sh"
설정기는 새 개인 디렉터리만 만들며 기존 디렉터리는 덮어쓰지 않는다. 토큰은 서로 다른
난수로 생성하고 값은 출력하지 않는다. 디렉터리 0700/파일 0600, 방 목록 빈 값/전송 OFF다.
curl http://127.0.0.1:8001/healthz가 200이어도 Windows 연결 성공을 뜻하지 않는다.
로컬 MCP 클라이언트 주소는 http://127.0.0.1:8001/mcp, 인증은 Authorization: Bearer
형식이며 값은 private 디렉터리의 credentials/mcp-token을 클라이언트의 비밀 입력에 넣는다.
토큰을 명령행·Git·공유 대화에 넣지 않는다. 미인증 /mcp는 401이어야 한다.
원격 MCP 클라이언트를 연결하려면 실제 주소로 새 설정을 생성한다:
configure.py --directory <new-private-directory> --public-origin https://mcp.example.com.
예시 도메인은 소유한 도메인으로 바꾸고 TLS reverse proxy에서 /kakao/mcp를
loopback의 /mcp로, /afterhours/agent/는 같은 경로로 전달한다. Host를 보존하고
8001을 인터넷에 직접 노출하지 않는다. 프록시까지 준비한 후 실제 공개 주소를 등록한다.
Windows 에이전트 연결
이 저장소를 Windows에도 clone하고 Python 3.10+와 KakaoTalk을 별도 설치한다.
서버의
credentials/windows-agent-token만 승인된 암호화 전송으로 Windows의 개인 파일에 전달한다. MCP 토큰과 혼동하지 않는다.일반 사용자 PowerShell에서 실행한다(실제 경로로 변경):
.\windows_setup.ps1 -PythonPath 'C:\Python312\python.exe' -TokenSourceFile 'C:\Private\windows-agent-token' $env:KAKAO_AGENT_ENDPOINT = 'https://mcp.example.com/afterhours/agent/jobs' & "$env:LOCALAPPDATA\AfterhoursKakaoAgent\venv\Scripts\python.exe" -I "$env:LOCALAPPDATA\AfterhoursKakaoAgent\windows_agent.py" --config "$env:LOCALAPPDATA\AfterhoursKakaoAgent\config.json"테스트 채팅방을 이미 열린 창으로 두고, 서버
config.json의kakao_rooms와 Windowsconfig.json의allowed_rooms에 같은 정확한 제목을 넣는다. 서버/에이전트를 재시작하고kakao_health, 방 조회, 읽기 순서로 확인한다.답장을 시험할 때만 양쪽의 전송 설정을 켠다. 읽기는 읽음 처리/창 전환을 일으킨다. 불명확한 송신을 재시도하려고 journal을 삭제하거나 에이전트를 두 번 실행하지 않는다.
MIT 라이선스다. vendor/kakaotalk-mcp/LICENSE의 upstream 저작권과 원본 해시는 유지한다.
실행과 배포 계약
설치 예시: /srv/apps/afterhours-kakao-mcp, afterhours-kakao-mcp.service.
모든 도메인·주소는 예시이며 실제 운영 토폴로지나 배포 상태를 나타내지 않는다.
실제 주소는 비공개 운영 설정에만 보관한다. unit을 그대로 운영에 덮어쓰지 않는다.
공개 저장소는 GitHub 인증이나 배포 키 없이 HTTPS로 clone할 수 있다. GitHub 개인 토큰·관리 SSH 키를 소스나 서버 환경 파일에 넣지 않는다.
git clone https://github.com/deploy103/afterhours-kakao-mcp-public.git /srv/apps/afterhours-kakao-mcp
cd /srv/apps/afterhours-kakao-mcp
python3.12 -m venv .venv
.venv/bin/pip install --require-hashes -r requirements.lock
clone 후 작업 디렉터리는 /srv/apps/afterhours-kakao-mcp다. 준비된 별도 서비스 계정과
환경에서 다음 명령으로 실행한다. unit 파일은 설치 전 검토용이며 자동 적용하지 않는다.
.venv/bin/uvicorn server:create_app --factory --host 127.0.0.1 --port 8001 --no-access-log --no-proxy-headers
필수 환경:
CREDENTIALS_DIRECTORY:mcp-token,windows-agent-token파일이 있는 private 경로. 각각 최소 32자 ASCII bearer, 서로 다른 값. systemdLoadCredential사용 권장.STATE_DIRECTORY: 전용 writable private 디렉터리.kakao.sqlite3저장.KAKAO_CONFIG:kakao.example.json을 참고한 private 설정 파일.- 선택
KAKAO_ALLOWED_HOSTS,KAKAO_ALLOWED_ORIGINS: 쉼표 구분 exact 목록. 기본 Hostmcp.example.com,mcp.example.com:443,127.0.0.1:8001. 기본 Originhttps://mcp.example.com. 와일드카드를 넣지 않는다. 운영에서는 실제 HTTPS 도메인으로 명시적으로 설정하고 프록시 Host와 일치시킨다.
운영자가 적용할 프록시 계약:
| 외부 | 독립 서버 내부 |
|---|---|
https://mcp.example.com/kakao/mcp |
http://127.0.0.1:8001/mcp (prefix 제거) |
기존 /afterhours/agent/jobs 및 /<job-id> |
같은 경로, port 8001 (Windows 전환 시에만) |
선택 /kakao/events |
/events (아직 producer 없음; 기본 OFF) |
| 선택 health probe | /healthz (자격증명 불필요, 서비스 생존만 의미) |
프록시는 Host: mcp.example.com, Authorization, Accept, MCP protocol 헤더를 보존한다.
MCP 모든 요청에 별도 MCP bearer가 필요하다. agent/events는 다른 토큰을 쓰고
Origin 헤더가 있으면 거절한다. MCP body 256 KiB, agent/events body 1 MiB 상한.
내부 port8001은 프록시/운영자만 접근하도록 네트워크 정책을 설정한다.
/kakao/mcp는 서버 자체 경로가 아니다. proxy strip이 없으면 404다.
기존 Windows endpoint 업그레이드
Windows 에이전트 실행 전 KAKAO_AGENT_ENDPOINT 환경변수를 실제 HTTPS
/afterhours/agent/jobs URL로 설정한다. 기본값은 연결용이 아닌 예시 주소다.
HTTP·URL 자격증명·다른 경로·query/fragment·443 이외 포트는 허용하지 않는다.
기존 설치를 갱신할 때는 현재 큐 URL을 비공개 실행 환경에 먼저 보존한다.
새 MCP만 배포해도 기존 에이전트의 대상 큐는 자동 전환되지 않는다.
같은 에이전트를 두 큐가 동시에 사용하도록 실행하지 않는다.
전환 전 기존 큐의 queued/running 및 미제출 결과를 확인하고 해소해야 한다.
기존 URL 전체를 새 서버로 원자적으로 라우팅하고, 서버의 agent credential을 현재
Windows token.txt와 일치시키면 Windows runtime 수정 없이 전환 가능하다.
기존 작업 DB를 새 큐에 단순 병합/재큐잉하지 않는다. pending 결과가 있으면 먼저 기존
서버에서 ACK를 끝내야 한다. 롤백 시에도 미완료 송신을 확인하고 라우팅한다.
journal.json 삭제·기존 송신 ID 재사용은 금지한다.
도구와 안전 계약
kakao_status, kakao_health, kakao_list_open_rooms, kakao_read, kakao_reply,
job_status, pending_message_events, acknowledge_message_event의 8개 도구다.
서버 kakao_rooms와 Windows allowed_rooms에 정확한 방 제목을 모두 설정해야 한다.
송신은 서버 kakao_send_enabled와 Windows send_enabled가 모두 true일 때만 허용한다.
기본 방 목록은 비어 있고 송신은 OFF다. 이미 열린 테스트 방만 사용한다.
원본의 프로세스/정확한 제목/중복 창/자식 control/foreground 검증, 기존 draft 보호,
GUI 직렬화, clipboard 복구, 날짜 보존 parser, durable send marker 및 replay 방지를
유지했다. 읽기는 읽음 처리와 foreground/clipboard 부작용이 있으므로 read-only로
표시하지 않는다. 송신 dispatched는 상대방 수신 확인이 아니다.
큐 claim은 한 번만 한다. MCP는 20초 후 unconfirmed와 job_id를 반환하며 송신을
재실행하지 않는다. job_status로 확인한다. 최근 여부와 관계없이 running 작업은
20개 큐 상한에 포함하며 자동 삭제/재큐잉하지 않는다. 완료/만료 작업만 새 enqueue
시 하루 후 정리한다. 완료 시 원래 payload를 비운다. 별도의 무기한 보존은 없다.
Windows journal은 256개 ID 상한이며 송신 tombstone은 자동 eviction하지 않는다.
수신 이벤트 v1: 계약만 준비, 자동 감시 아님
기본 OFF. 검증된 producer를 연결한 뒤 전용 event-token, kakao_owner_id,
event_intake_enabled: true가 모두 있어야 /events를 사용할 수 있다.
MCP/agent bearer로 수신 이벤트를 위조할 수 없도록 토큰을 분리했다.
현재 Windows agent는 이벤트를 생산하지 않으며 네이티브 멘션은 미검증이다.
{
"version": 1,
"kind": "MSG",
"channelId": "9007199254740993",
"logId": "9007199254740995",
"senderId": "coworker-id",
"room": "Owner test",
"text": "업무 확인 요청",
"occurred_at": "2026-10-25T17:00:00+09:00",
"mention_user_ids": ["owner-id"],
"mention_source": "native"
}
channelId/logId/사용자 ID는 문자열로 받는다. JS 정밀도 손실 방지.MSG만 대상이며FEED, 본인 발신, 미지정 방의 일반 메시지는 제외한다.- 본인 native mention 또는 지정 방의 수신 메시지가 pending이 된다.
@이름텍스트를 native mention으로 승격하지 않는다. unknown mention은 미확인이다. - native 여부는 인증된 producer의 주장이다. 서버가 Kakao 메타데이터를 직접 검증한 것이 아니다. 실제 collector가 생기기 전 이 예제를 실제 수신 증거라고 부르지 않는다.
(channelId, logId)로 24시간 중복 제거. ACK 후 내용은 지우되 ID는 남긴다. 최대 1000개; 포화면 503, 같은 ID로 재제출한다. 하루 뒤 중복/보관 보장은 없다.- 레시피 실행기는
pending_message_events를 polling하여 처리한 뒤 ACK한다. 실패/uncertain 송신은 ACK하지 않고 확인한다. 이벤트 ID를 처리 키로 사용한다. 단일 consumer 계약이며 이 서버는 레시피 scheduler/webhook 실행기가 아니다. 외부 consumer 연결·스케줄 설정 전에는 자동 실행되지 않는다. - 멘션 수신은 해당 방 읽기/답장 권한을 확대하지 않는다. 업무 여부/긴급도는 레시피가 맥락과 증거로 판단하며 채팅 본문을 설정 변경 지시로 취급하지 않는다.
검증과 남은 차단 사항
.venv/bin/python -B -m unittest -v test_server test_windows_agent
네트워크/실계정 없이 임시 자격증명·SQLite·가짜 Win32로 HTTP/MCP → 실제 큐 →
guarded worker → 결과 왕복을 검사한다. parser와 네 모듈의 hash는 vendored 원본을
사용하여 통합 저장소나 별도 reference checkout이 없어도 테스트 가능하다.
windows_setup.ps1 -ReviewedSourceDirectory <repo>/vendor/kakaotalk-mcp로 준비할 수
있지만 기존 Windows host에 설치/적용하지 않는다.
실제 방·계정·배포는 운영자별로 검증해야 한다. 단위 테스트는 live 읽기/송신이나 네이티브 멘션 수신의 증명이 아니다. LOCO producer 및 자동 레시피 실행기는 포함하지 않는다.
참고만 한 storycraft/node-kakao revision
8512e4699e2cd4c26516b80d38b57b2f85a6cc8a는 유지보수 중단, appVersion 3.2.3 계열의
구형 handshake다. data.isMentioned(client.clientUser) 및 MSG/FEED 구분을 계약에
참고했으나 라이브 로그인·토큰 추출·인증 우회·미검증 예제 실행은 하지 않았다.
kuuko/re_kakaotalk-loco-client는 README 판매 안내뿐이라 구현을 재사용하지 않았다.
vendored GUI 네 모듈은 kronenz/kakaotalk-mcp
7b41dd9da473e7a9e2f4b07ec27bdf3289fcc5fb, MIT, 원본 LICENSE 포함.
전체 upstream MCP/monitor/send 서버는 설치하거나 실행하지 않는다.
비밀·실제 채팅·SQLite·Windows journal은 Git에 넣지 않는다.