Skip to main content
Glama
yoyojyv

Medium MCP Server

by yoyojyv
README.md
# Medium MCP Server

Medium 블로그 글을 읽어오는 Model Context Protocol (MCP) 서버입니다.

AI 어시스턴트(Claude, ChatGPT 등)가 Medium 글의 내용을 직접 읽고 분석할 수 있도록 해줍니다.

> ⚠️ **Disclaimer / 면책 조항**
>
> 이 도구는 **교육 및 개인 학습 목적**으로만 제공됩니다.
>
> - 이 프로젝트는 Medium 이용약관(ToS)을 위반할 수 있습니다
> - 사용자는 관련 법률 준수에 대한 전적인 책임을 집니다
> - 저작자는 이 도구의 오용에 대해 어떠한 책임도 지지 않습니다
> - 상업적 목적이나 대량 데이터 수집에 사용하지 마세요
>
> **사용에 따른 모든 위험은 사용자 본인에게 있습니다.**

## Features

- Medium 글 URL에서 콘텐츠 추출
- **Author 글 목록**: 특정 Author의 글 목록 조회 (RSS/스크래핑)
- **검색 기능**: Medium 전체 검색 및 Author 내 검색
- Playwright 헤드리스 브라우저로 동적 콘텐츠 처리
- Mozilla Readability로 본문만 깔끔하게 추출
- Markdown 형식으로 변환하여 반환
- **로그인 지원**: Medium 멤버십 전용 콘텐츠 접근 가능

## 빠른 시작 가이드

설치 후 Claude에게 이렇게 말해보세요:

### 📖 글 읽기 & 번역

```
이 글 내용을 한글로 번역해줘: https://medium.com/@username/article-title
```

```
이 Medium 글을 요약해줘: https://towardsdatascience.com/some-article
```

### 🔍 분석 & 질문

```
이 글의 핵심 포인트 3가지를 정리해줘: [Medium URL]
```

```
이 기술 블로그에서 설명하는 개념을 초보자도 이해할 수 있게 설명해줘: [Medium URL]
```

### 📋 Author 글 목록 & 검색

```
@springrod 의 글 목록을 보여줘
```

```
@username 의 글 중에서 "AI agent" 관련 글을 찾아줘
```

```
Medium에서 "React hooks" 관련 글을 검색해줘
```

### 🔐 멤버십 글 읽기 (로그인 필요)

```
Medium에 로그인해줘
→ (브라우저에서 로그인 완료 후)
로그인 세션 저장해줘
→ 이제 멤버십 전용 글도 읽을 수 있어요!
```

### ➕ 새 도메인 추가

```
stackademic.com 도메인 추가해줘
→ 이제 stackademic.com 글도 읽을 수 있어요!
```

> **팁**: URL만 붙여넣고 원하는 작업을 자연스럽게 요청하세요. Claude가 알아서 글을 읽고 처리합니다.

## 지원 도메인

### 기본 도메인

- `medium.com`
- `*.medium.com` (사용자 서브도메인)
- `towardsdatascience.com`
- `betterprogramming.pub`
- `levelup.gitconnected.com`
- `uxdesign.cc`
- `eand.co`
- `betterhumans.pub`
- `writingcooperative.com`

### 커스텀 도메인 추가

새로운 Medium 파트너 도메인(예: `stackademic.com`)을 추가할 수 있습니다:

**방법 1: MCP 도구 사용**
```
도메인 추가해줘: stackademic.com
```

**방법 2: 설정 파일 편집**
```json
// ~/.medium-mcp/config.json
{
  "additionalDomains": ["stackademic.com", "blog.example.com"]
}
```

**방법 3: 환경변수 설정** (MCP 클라이언트 설정에서)
```json
{
  "mcpServers": {
    "medium": {
      "command": "node",
      "args": ["/절대/경로/medium-mcp-server/dist/index.js"],
      "env": {
        "MEDIUM_ADDITIONAL_DOMAINS": "stackademic.com,another.com"
      }
    }
  }
}
```

## 설치

### 1. 저장소 클론

```bash
git clone https://github.com/yoyojyv/medium-mcp-server.git
cd medium-mcp-server
```

### 2. 의존성 설치

```bash
npm install
```

### 3. Playwright 브라우저 설치

```bash
npx playwright install chromium
```

### 4. 빌드

```bash
npm run build
```

## 사용 방법

### Claude Desktop에서 사용

`claude_desktop_config.json` 파일에 다음을 추가합니다:

**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
**Windows**: `%APPDATA%/Claude/claude_desktop_config.json`

```json
{
  "mcpServers": {
    "medium": {
      "command": "node",
      "args": ["/절대/경로/medium-mcp-server/dist/index.js"]
    }
  }
}
```

### Cursor에서 사용

Cursor 설정의 MCP 섹션에 추가:

```json
{
  "mcpServers": {
    "medium": {
      "command": "node",
      "args": ["/절대/경로/medium-mcp-server/dist/index.js"]
    }
  }
}
```

### 직접 테스트

```bash
# tools 목록 확인
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node dist/index.js
```

## 제공 Tools

### `read_article`

Medium 글 URL에서 콘텐츠를 추출합니다.

**입력:**

| 파라미터 | 타입   | 필수 | 설명          |
| -------- | ------ | ---- | ------------- |
| url      | string | O    | Medium 글 URL |

**출력:**
```json
{
  "title": "글 제목",
  "author": "작성자",
  "publishedAt": "2025-12-15T00:00:00Z",
  "content": "# 글 제목\n\n본문 내용 (Markdown)...",
  "excerpt": "글 요약",
  "url": "https://medium.com/..."
}
```

**사용 예시 (Claude에서):**

```
이 Medium 글을 읽고 요약해줘: https://medium.com/@username/article-title-123abc
```

---

### `login`

Medium 로그인을 위한 브라우저 창을 엽니다. 멤버십 전용 콘텐츠에 접근하려면 로그인이 필요합니다.

**사용 예시:**
```
Medium에 로그인해줘
```

> 브라우저 창이 열리면 직접 로그인을 완료하세요.

---

### `save_login`

로그인 완료 후 세션을 저장합니다. `login` 실행 후 사용합니다.

**사용 예시:**
```
로그인 세션을 저장해줘
```

> 세션은 `~/.medium-mcp/auth.json`에 저장됩니다.

---

### `login_status`

현재 로그인 상태를 확인합니다.

**출력:**
```json
{
  "loggedIn": true,
  "storagePath": "/Users/username/.medium-mcp/auth.json",
  "message": "You are logged in. Member-only content should be accessible."
}
```

---

### `logout`

저장된 로그인 세션을 삭제합니다.

**사용 예시:**
```
Medium에서 로그아웃해줘
```

---

### `add_domain`

커스텀 Medium 파트너 도메인을 추가합니다.

**입력:**

| 파라미터 | 타입   | 필수 | 설명                              |
| -------- | ------ | ---- | --------------------------------- |
| domain   | string | O    | 추가할 도메인 (예: stackademic.com) |

**사용 예시:**
```
stackademic.com 도메인 추가해줘
```

> 추가된 도메인은 `~/.medium-mcp/config.json`에 저장됩니다.

---

### `list_domains`

현재 지원되는 모든 도메인 목록을 조회합니다.

**출력:**
```json
{
  "defaultDomains": ["medium.com", "towardsdatascience.com", ...],
  "customDomains": ["stackademic.com"],
  "allDomains": ["medium.com", ..., "stackademic.com"],
  "configPath": "/Users/username/.medium-mcp/config.json"
}
```

**사용 예시:**
```
지원되는 도메인 목록 보여줘
```

---

### `remove_domain`

커스텀 도메인을 제거합니다. 기본 도메인은 제거할 수 없습니다.

**입력:**

| 파라미터 | 타입   | 필수 | 설명              |
| -------- | ------ | ---- | ----------------- |
| domain   | string | O    | 제거할 도메인     |

**사용 예시:**
```
stackademic.com 도메인 제거해줘
```

---

### `list_author_articles`

특정 Author의 글 목록을 가져옵니다.

**입력:**

| 파라미터 | 타입   | 필수 | 설명                                           |
| -------- | ------ | ---- | ---------------------------------------------- |
| username | string | O    | Medium username (@ 없이)                       |
| source   | string | X    | `rss` (기본, 빠름) 또는 `scrape` (더 많은 글)  |
| limit    | number | X    | 최대 글 개수 (scrape 모드에서만 적용, 기본 10) |
| keyword  | string | X    | 키워드 필터링                                  |

**출력:**
```json
{
  "username": "springrod",
  "articleCount": 10,
  "articles": [
    {
      "title": "글 제목",
      "url": "https://medium.com/@springrod/...",
      "publishedAt": "Mon, 22 Dec 2025 08:25:33 GMT",
      "categories": ["genai", "java", "ai-agent"],
      "content": "전체 HTML 콘텐츠 (RSS 모드에서만)"
    }
  ],
  "source": "rss",
  "hasMore": true
}
```

**사용 예시:**
```
@springrod 의 글 목록을 보여줘
```

```
@username 의 최근 글 20개를 스크래핑으로 가져와줘
```

---

### `search_articles`

Medium 전체에서 글을 검색합니다. (Playwright 스크래핑 사용)

**입력:**

| 파라미터 | 타입   | 필수 | 설명                     |
| -------- | ------ | ---- | ------------------------ |
| query    | string | O    | 검색어                   |
| limit    | number | X    | 최대 결과 개수 (기본 10) |

**출력:**
```json
{
  "query": "AI agent java",
  "resultCount": 5,
  "results": [
    {
      "title": "글 제목",
      "url": "https://medium.com/...",
      "author": "작성자",
      "excerpt": "글 요약"
    }
  ],
  "hasMore": true
}
```

**사용 예시:**
```
Medium에서 "React hooks" 관련 글을 검색해줘
```

> **참고**: 스크래핑 방식이므로 봇 탐지로 차단될 수 있습니다.

---

### `search_author_articles`

특정 Author의 글 중에서 키워드로 검색합니다. (RSS 피드 기반)

**입력:**

| 파라미터 | 타입   | 필수 | 설명                     |
| -------- | ------ | ---- | ------------------------ |
| username | string | O    | Medium username (@ 없이) |
| keyword  | string | O    | 검색할 키워드            |

**출력:**
```json
{
  "username": "springrod",
  "keyword": "agent",
  "matchCount": 8,
  "articles": [...],
  "note": "Search is limited to author's ~10 most recent articles from RSS feed"
}
```

**사용 예시:**
```
@springrod 의 글 중에서 "agent" 관련 글을 찾아줘
```

---

## 멤버십 콘텐츠 접근 방법

Medium 멤버십 전용 글을 읽으려면:

1. **로그인 시작**: "Medium에 로그인해줘" 라고 요청
2. **브라우저에서 로그인**: 열린 브라우저 창에서 Medium 계정으로 로그인
3. **세션 저장**: "로그인 세션을 저장해줘" 라고 요청
4. **글 읽기**: 이제 멤버십 전용 글도 읽을 수 있습니다

> **중요**: 로그인 시 **이메일 기반 인증**을 사용하세요.
> Google/Apple/Facebook 등 OAuth 인증은 Playwright 환경에서 정상 작동하지 않을 수 있습니다.

### Claude Desktop 사용 예시

![Claude Desktop에서 Medium MCP 로그인 플로우](docs/images/claude-screenshot.png)

```
# 예시 대화
사용자: Medium에 로그인해줘
Claude: 브라우저가 열렸습니다. 로그인을 완료한 후 알려주세요.

사용자: 로그인 완료했어. 세션 저장해줘
Claude: 로그인 세션이 저장되었습니다.

사용자: 이 글 읽어줘: https://medium.com/membership-only-article
Claude: (멤버십 전용 글 내용 표시)
```

## 개발

### 개발 모드 실행

```bash
npm run dev
```

### 빌드

```bash
npm run build
```

### 프로젝트 구조

```
src/
├── index.ts              # 진입점
├── server.ts             # MCP 서버 설정
├── config/
│   ├── constants.ts      # 도메인 목록, 타임아웃 설정
│   └── settings.ts       # 설정 파일 관리
├── tools/
│   ├── read-article.ts   # read_article Tool
│   ├── auth.ts           # 로그인 관련 Tools
│   ├── domains.ts        # 도메인 관리 Tools
│   ├── author-articles.ts # list_author_articles Tool
│   └── search.ts         # search_articles, search_author_articles Tools
├── services/
│   ├── article-extractor.ts  # Playwright + Readability
│   ├── rss-feed.ts           # RSS 피드 파싱
│   ├── author-scraper.ts     # Author 페이지 스크래핑
│   └── search-scraper.ts     # 검색 페이지 스크래핑
├── types/
│   ├── article.ts            # Article 타입
│   ├── author-article.ts     # AuthorArticle 타입
│   └── search-result.ts      # SearchResult 타입
└── utils/
    ├── errors.ts         # 에러 클래스
    ├── logger.ts         # 로깅
    └── response.ts       # 응답 포맷
```

### 설정 파일 위치

| 파일 | 경로 | 설명 |
|------|------|------|
| 로그인 세션 | `~/.medium-mcp/auth.json` | Playwright 인증 상태 |
| 사용자 설정 | `~/.medium-mcp/config.json` | 커스텀 도메인 등 |

## 기술 스택

- **TypeScript** - 타입 안전성
- **@modelcontextprotocol/sdk** - MCP 서버 구현
- **Playwright** - 헤드리스 브라우저
- **@mozilla/readability** - 콘텐츠 추출
- **Turndown** - HTML → Markdown 변환
- **Zod** - 스키마 검증
- **rss-parser** - RSS 피드 파싱

## 제한사항

- Paywall 콘텐츠는 로그인 후 접근 가능
- 과도한 요청 시 Rate limiting 가능성
- Playwright 브라우저 설치 필요 (~300MB)

## 라이선스

MIT

TDQS

A4.1/5.0

Scored across 11 tools

Disambiguation4/5

Most tools target distinct actions (login, domain management, reading, searching). The only potential confusion is between search_author_articles and list_author_articles, but descriptions clarify search (query within author) versus list (full article list). All others are clearly separated by resource and action.

Naming Consistency4/5

Tool names follow a consistent snake_case verb_noun pattern (e.g., search_articles, add_domain, remove_domain). The only deviation is 'login_status', which uses a noun phrase rather than a verb, but it is still readable and predictable. No mixed casing or chaotic naming.

Tool Count5/5

Eleven tools is well-scoped for a Medium server that handles authentication, article retrieval, author queries, and domain configuration. Each tool covers a distinct feature area without redundancy or bloat.

Completeness4/5

The server provides solid coverage for reading and searching Medium content, including member-only access via login. Missing writing/publishing and comment features, but these appear out of scope. The domain management tools are a bit niche but complete for their purpose.

Maintenance

ActivityInactive
ResponsivenessNo issues