After-hours Vooy MCP
by deploy103
README.md
# After-hours Vooy MCP
기존 인증 MCP에 퇴근 후 처리 도구를 추가하는 통합 구현이다. 단일 소유자용이며
익명 접근이나 다중 사용자 데이터 분리는 제공하지 않는다.
주소·경로·unit은 일반 예시이며 실제 운영 토폴로지가 아니다. 실제 HTTPS origin은
Git 밖의 `MCP_PUBLIC_ORIGIN`으로 공급하고 프록시 Host와 일치시킨다.
기본은 `https://mcp.example.com`이며 경로·URL 자격증명 없이 Host/Origin을 exact 검증한다.
예제 loopback bind·credential/state 경로를 기존 운영 설정에 그대로 덮어쓰지 않는다.
## 새 사용자의 빠른 시작
Linux/Python 3.11+용 통합 도구와 레시피 예제다. 시스템 IANA 시간대 데이터베이스
(`tzdata`, `Asia/Seoul` 포함)가 필요하다. 최소 컨테이너 이미지에는 별도로 설치한다. 새 연결은 개별
[Slack](https://github.com/deploy103/afterhours-slack-mcp-public),
[Kakao](https://github.com/deploy103/afterhours-kakao-mcp-public),
[Outlook](https://github.com/deploy103/afterhours-outlook-mcp-public),
[Telegram](https://github.com/deploy103/afterhours-telegram-mcp-public) 서버를 권장한다.
이 통합 서버의 Outlook은 자동 갱신 없는 기존 access-token 방식이다.
Gmail은 Vooy의 기존 연결을 사용한다. 어느 서버도 계정 인증이나 자동 감시를 대신
완료해 주지는 않으며, 다중 사용자 공용 서버로 사용하면 안 된다.
```sh
git clone https://github.com/deploy103/afterhours-mcp-public.git
cd afterhours-mcp-public
python3 -m venv .venv
.venv/bin/python -m pip install --require-hashes -r requirements.lock
.venv/bin/python configure.py --directory "$HOME/afterhours-private"
"$HOME/afterhours-private/run.sh"
```
설정기는 소스 밖의 **새 디렉터리**에만 0700 디렉터리/0600 토큰·설정을 생성한다.
기존 경로를 덮어쓰지 않고 전송은 OFF, 방 목록은 빈 값이다. `run.sh`는 localhost8000만
수신한다. 로컬 MCP 주소는 `http://127.0.0.1:8000/mcp`이며 클라이언트의 비밀 입력에
private 디렉터리의 `credentials/mcp-token`을 `Authorization: Bearer`로 설정한다.
토큰을 Git·명령행·공유 채팅에 넣지 않는다. `curl http://127.0.0.1:8000/healthz`는
프로세스 생존 확인뿐이며 메신저 연결 검사가 아니다. 미인증 `/mcp`는 401이어야 한다.
원격 사용은 `--public-origin https://mcp.example.com`의 예시를 자기 도메인으로 바꾸고
새 private 디렉터리를 생성한다. TLS reverse proxy에서 `/mcp` 및 `/afterhours/`를
loopback8000의 같은 경로로 전달하고 Host와 인증 헤더를 보존한다. 내부 포트를 직접
인터넷에 노출하지 않는다. 서버 설정은 private `config.json`에서 관리하고 재시작한다.
Slack 앱/허용방 설정은 아래를, Windows 에이전트의 설치·TLS·테스트방 절차는
[Kakao 서버 안내](https://github.com/deploy103/afterhours-kakao-mcp-public#새-사용자의-빠른-시작)를
따른다. 이 저장소에도 동일한 `windows_setup.ps1`과 검토된 `vendor/` 소스가 포함된다.
미연결 제공자는 실패/미설정으로 표시되며 다른 사용자의 인증정보를 공유하지 않는다.
자체 코드는 MIT 라이선스이며 vendored 원본은 `vendor/kakaotalk-mcp/LICENSE`를 유지한다.
## 구성
- `afterhours.py`: Slack Web API 읽기/답장, Windows 작업 큐, 근거 기반 분류 기록,
약속 저장/완료 및 출근 브리핑. SQLite는 systemd StateDirectory에 저장한다.
- `afterhours_app.py`: 기존 MCP와 별도 토큰으로 Windows 에이전트 REST를 제공한다.
- `outlook.py`: Microsoft Graph의 본인 받은편지함·기본 캘린더 조회 전용 도구.
- `triggers.py`: 서명된 Slack 이벤트 중 본인 멘션 또는 지정 방의 수신 메시지를
중복 없이 보관한다. Vooy 레시피를 자동 실행하는 기능은 아니다.
- `windows_agent.py`, `windows_setup.ps1`: Windows host 대화형 세션에서 실행하는 제한된
KakaoTalk 어댑터. 임의 프로그램 실행·파일 조회·방 검색·일괄 전송은 없다.
- `RECIPE.md`: Vooy 레시피 지침과 Gmail 조회 전용·캘린더/태스크 확인 후 등록 계획.
## 자격증명과 설정
MCP 토큰과 Windows 에이전트 토큰은 별도다. 로컬 시작은 설정기가 만든 private 파일을
사용한다. systemd 배포는 `LoadCredential`로 전달하며 `/etc/afterhours-mcp/mcp-token`,
`/etc/afterhours-mcp/windows-agent-token` 원본 파일을 root 전용으로 보호한다.
Windows 토큰은 에이전트 설치 폴더 `token.txt`로만 전달하며 채팅·URL·Git에 넣지 않는다.
Slack 연결 시 OAuth 앱 토큰을 별도 `slack-token` credential로 설정해야 한다.
브라우저 세션 쿠키를 복사하지 않는다.
Slack Events URL은 `https://mcp.example.com/afterhours/slack/events`다.
앱의 `slack-signing-secret` credential과 `slack_team_id`, `slack_owner_id`를 설정한다.
앱이 참여한 채널의 message 이벤트를 구독한다. 본인 메시지·봇·수정 이벤트는 제외한다.
`pending_message_events` 조회 후 실제 처리가 끝나면 `acknowledge_message_event`로 표시한다.
멘션으로 수집된 미허용 방은 읽기·답장 권한이 자동 확대되지 않는다. 소유자가 방을
허용해야 대화 맥락을 더 읽거나 답장할 수 있다. KakaoTalk 전체 방의 네이티브 멘션
감지는 아직 제공하지 않는다.
Outlook은 소유자 자신의 Entra 앱에서 위임된 `Mail.Read`, `Calendars.ReadBasic`을
승인한 `outlook-access-token` credential이 필요하다. 브라우저 토큰 추출이나 타사
앱 ID를 사용하지 않는다. 현재 자동 갱신은 없으며 만료/취소 시 재연결해야 한다.
메일 발송·답장·일정 생성 도구는 없다. 받은편지함/캘린더는 제한된 한 페이지이며
전체 메일 스캔이나 완전한 가용시간 보고서가 아니다. Gmail은 Vooy 기존 연결을 쓴다.
선택 credential은 준비된 경우에만 systemd `LoadCredential`로 추가한다.
`/etc/afterhours-mcp/afterhours.json`은 `afterhours.example.json` 형식이다. 대화방은
기본 빈 목록, 전송은 OFF다. Slack 채널 ID와 KakaoTalk 정확한 방 제목을 소유자가
정한 뒤 서버 및 Windows 양쪽에서 허용해야 한다. 서버 설정 변경 후 해당 서비스만
재시작한다. Windows는 `allowed_rooms`, `send_enabled` 필드를 사용한다. 에이전트 시작 전
`KAKAO_AGENT_ENDPOINT`에 실제 HTTPS `/afterhours/agent/jobs` URL을 비공개 실행 환경으로
설정한다. 기본 주소는 예시이며 HTTP·URL 자격증명·다른 경로·query/fragment는 거부한다.
Slack 공식 MCP도 있으나 고정 Slack 앱 ID와 OAuth가 필요하며 Dynamic Client
Registration을 지원하지 않는다. 현재 Vooy 테스트에는 읽기/답장 두 기능만 가진
Slack OAuth 앱을 사용한다. 필요한 bot scopes: `channels:history`, `groups:history`,
`im:history`, `mpim:history`, `chat:write` 중 테스트에 실제 필요한 범위만 선택한다.
봇이 참여한 테스트 채널에만 접근할 수 있으며 사용자 본인으로 위장하지 않는다.
Slack 워크스페이스 가입/초대 링크와 앱 설치는 계정 인증 후 완료해야 한다.
## 검증
development VM에서 `.venv/bin/python -m unittest -v test_afterhours test_windows_agent test_outlook`.
서버 설치: Python 3.11+ 가상환경에서 `pip install --require-hashes -r requirements.lock`.
Windows 테스트는 포함된 `vendor/kakaotalk-mcp`의 hash 고정된 원본을 사용한다.
옆 디렉터리나 비공개 reference checkout은 필요하지 않다.
모든 데이터와 자격증명은 폐기 가능한 임시 값이다. 실제 Slack/KakaoTalk 송수신은
계정 연결과 허용 테스트 방을 준비한 후 별도로 검증한다. HTTP/단위 테스트 통과는
네이티브 GUI·메신저·긴급 알림 성공을 뜻하지 않는다.
## 운영 제약
서버 자체는 분류용 모델이나 상시 메시지 감시 스케줄러가 아니다. Vooy가 도구를
호출할 때 실행되며, 자동화하려면 Vooy 트리거 및 본인 알림 수단을 별도로 설정한다.
기본 근무표는 월~금 09~18시, Asia/Seoul이고 공휴일·휴가는 미지원이다.
긴급 분류는 모델이 근거를 읽고 선택하며 서버는 영향/기한 조건만 검사한다.
Gmail 조회 전용 및 Calendar/Tasks 확인 요구는 레시피 지침이다. 기존 Google
연결 자체의 OAuth 권한을 축소한 것은 아니다.
전송 성공은 Windows UI 입력 완료일 수 있으며 상대방 수신 확인이 아니다.
불명확한 전송은 자동 재시도하지 않는다. Windows `journal.json`은 임의 삭제하지
않는다. 서버 작업 기록의 오래된 데이터는 새 작업 생성 시 정리한다. 약속/분류는
소유자가 관리할 영속 기록이다.
복구: 새 systemd drop-in `afterhours.conf`만 제거하고 daemon-reload 후 서비스를
재시작하면 원래 두 도구로 돌아간다. SQLite나 토큰은 삭제하지 않는다. 이는 복구
절차이며 별도 운영 변경을 자동으로 승인하지 않는다.
참고: https://github.com/kronenz/kakaotalk-mcp (MIT, 검토한 버전은 setup에 고정),
https://github.com/silver-flight-group/kakaocli (macOS 전용),
https://docs.slack.dev/ai/slack-mcp-server . 기존 구현이 있어 REA 분석은 하지 않았다.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues