Skip to main content
Glama
amamirugi

Subwatch MCP

by amamirugi
README.md
# Subwatch MCP

Subwatch는 사용자가 요청했을 때만 공개 Reddit 게시물과 댓글을 읽어 오는 개인용 원격 MCP 서버입니다. Claude 커스텀 커넥터와 Open WebUI가 같은 Streamable HTTP 엔드포인트를 사용할 수 있도록 Cloudflare Workers 위에서 동작합니다.

## 현재 범위

- 수동 MCP 호출에만 반응
- 예약 실행 없음
- Reddit 콘텐츠 데이터베이스 없음
- 공개 게시물을 조회하며 Reddit의 `over_18` 표시는 보존하되 서버가 일괄 제외하지 않음
- 작성자 이름은 응답에서 제외
- GitHub OAuth와 숫자 사용자 ID 허용 목록으로 접근 제한
- Reddit 승인 전에는 조작된 테스트 데이터로 동작

## MCP 도구

| 도구 | 용도 |
| --- | --- |
| `get_recent_posts` | 정확한 최근 시간 범위에서 `top`, `hot`, `new`, `comments` 순으로 게시물 조회 |
| `search_recent_posts` | 한 서브레딧 안에서 최근 게시물 키워드 검색 |
| `get_post_details` | 선택한 게시물의 본문과 메타데이터 조회 |
| `get_top_comments` | 선택한 게시물의 상위 댓글 조회 |
| `get_server_status` | 테스트/실제 모드와 저장·예약 실행 여부 확인 |

사용 예시:

> r/LocalLLaMA에서 지난 24시간 동안 에이전트 프레임워크에 관한 인기 게시글을 정리해줘.

호스트 모델은 일반적으로 `get_recent_posts` 또는 `search_recent_posts`를 먼저 호출하고, 선별한 게시물에 대해서만 상세 정보와 댓글을 추가로 가져옵니다.

## 로컬 확인

Node.js 20 이상에서 실행합니다.

```bash
npm install
cp .dev.vars.example .dev.vars
npm run typegen
npm run check
npm run dev
```

실제 비밀값을 `.dev.vars`나 저장소에 커밋하지 마세요. 기본 `REDDIT_MODE`는 `mock`이며 가짜 데이터만 반환합니다.

## 실제 Reddit API 전환

Reddit의 명시적 승인을 받은 뒤에만 진행합니다.

1. Cloudflare Worker Secrets에 `REDDIT_CLIENT_ID`, `REDDIT_CLIENT_SECRET`을 저장합니다.
2. `wrangler.jsonc`의 `REDDIT_USER_AGENT`에서 `u/REPLACE_ME`를 실제 Reddit 사용자 이름으로 바꿉니다.
3. `REDDIT_MODE`를 `live`로 바꿉니다.
4. `npm run typegen && npm run check` 후 다시 배포합니다.

현재 실제 API 구현은 앱 전용 OAuth `client_credentials`와 `read` 범위를 사용합니다. Reddit이 승인 과정에서 다른 앱 유형이나 인증 흐름을 지정하면 그 안내를 우선하여 어댑터를 수정해야 합니다.

## 배포

웹과 Android만 사용하는 경우 [docs/WEB-DEPLOY.md](docs/WEB-DEPLOY.md)를 따르세요. 핵심 순서는 다음과 같습니다.

1. OAuth용 Cloudflare KV 네임스페이스 생성
2. KV ID와 GitHub 숫자 사용자 ID를 `wrangler.jsonc`에 입력
3. GitHub 저장소를 Cloudflare Workers Builds에 연결하여 최초 배포
4. 배포 주소로 GitHub OAuth App 생성
5. Worker Secrets에 GitHub 자격증명과 쿠키 암호화 키 저장
6. `https://<worker>.workers.dev/mcp`를 Claude 또는 Open WebUI에 연결

Reddit 승인 신청 문안은 [docs/REDDIT-APPROVAL.md](docs/REDDIT-APPROVAL.md)에 있습니다.

## 보안 경계

- Reddit·GitHub 비밀값을 소스나 `wrangler.jsonc`에 넣지 않습니다.
- GitHub 로그인 성공만으로는 충분하지 않습니다. `ALLOWED_GITHUB_USER_IDS`와 일치해야 MCP 토큰이 발급됩니다.
- OAuth 동의, CSRF, 일회성 state를 짧은 TTL의 KV에 저장합니다.
- Reddit 응답 크기와 MCP 결과 개수를 제한합니다.
- 외부 URL을 임의로 요청하는 도구를 제공하지 않습니다.
- Reddit 콘텐츠와 GitHub 상위 액세스 토큰을 영구 저장하지 않습니다.

자세한 운영 주의사항은 [SECURITY.md](SECURITY.md)를 참고하세요.

## 개발 명령

```bash
# wrangler.jsonc의 바인딩 또는 .dev.vars의 secret 이름을 바꿨을 때
npm run typegen

# 커밋된 타입으로 정적 검사와 Workers 런타임 테스트 실행
npm run check

npm run typecheck
npm test
npm run deploy -- --dry-run
```

GitHub Actions도 push와 pull request마다 `npm run check`를 실행합니다. `worker-configuration.d.ts`는 `npm run typegen`으로 갱신한 뒤 함께 커밋하세요.

## 라이선스

MIT

Maintenance

ActivitySlowing
ResponsivenessNo issues