Content
# Tool List
Based on the **official Python MCP SDK (FastMCP)** local MCP Server:
Automatically generates resume "project experience" bullet points from GitHub repository **objective signals**, merges them into existing PDF resume structured data, and exports new PDFs using HTML templates and WeasyPrint.
> **Design Principles**
> 1. Tool logic and MCP protocol layer separation (core functions can be tested independently of MCP)
> 2. Self-contained dependencies: directly use `requests` to access GitHub REST, without relying on third-party GitHub MCP
> 3. Only write verifiable facts, prohibit fabricating metrics like "50% performance improvement"
> 4. No persistent user resume/token storage; temporary files can be cleaned up after expiration
---
## Function List
| # | Tool | Description |
|---|------|------|
| 1 | `parse_resume` | PDF → structured `ResumeData` |
| 2 | `fetch_repo_info` | GitHub REST → `RepoInfo` (including `topics`, `contributor_stats`) |
| 3 | `analyze_repo_for_resume` | Objective technical points + `contribution_level` + `absolute_contribution_signal` |
| 4 | `generate_bullet_points` | Converge wording based on contribution level/absolute submission volume; negligible can skip entire project section |
| 5 | `merge_into_resume` | Insert/overwrite project (conflict → `project_already_exists`) |
| 6 | `diff_resume_versions` | Project-level human-readable diff |
| 7 | `render_pdf` | Jinja2 template + WeasyPrint → PDF (`modern` / `compact`) |
Recommended call order:
```
parse_resume
→ fetch_repo_info(github_username?)
→ analyze_repo_for_resume(github_username?, target_role?)
→ generate_bullet_points # If skip_project_entry, don't merge
→ merge_into_resume
→ diff_resume_versions
→ render_pdf(template=modern|compact)
```
---
## Architecture
```text
┌─────────────────────────────────────┐
│ MCP Client (Claude Desktop / │
│ Inspector / other LLM Host) │
└─────────────────┬───────────────────┘
│ stdio
┌─────────────────▼───────────────────┐
│ server.py (FastMCP Tool registration) │
│ Only parameter validation + envelope serialization │
└─────────────────┬───────────────────┘
│ Call pure Python functions
┌──────────────┬──────────────┼──────────────┬──────────────┐
▼ ▼ ▼ ▼ ▼
resume/ github/ generation/ cleanup.py config.py
parser fetcher bullet_writer TTL / rmtree GITHUB_TOKEN
merge analyzer
diff
renderer ──► templates/{modern,compact}.html ──► WeasyPrint PDF
```
**Layered benefits**: Business modules without `mcp` import → `pytest` can be used directly; switching transports (future HTTP) doesn't require changing analysis/rendering logic.
---
## Project Structure
```
resume-repo-sync/
├── src/resume_repo_sync/
│ ├── server.py # FastMCP + Tool registration (stdio)
│ ├── config.py # Environment variables / TTL
│ ├── cleanup.py # Temporary file cleanup
│ ├── resume/
│ │ ├── schema.py # Pydantic model + ToolErrorCode
│ │ ├── parser.py # pdfplumber parsing
│ │ ├── merge.py / diff.py
│ │ └── renderer.py # Jinja2 + WeasyPrint
│ ├── github/
│ │ ├── fetcher.py # REST: meta / languages / README /
│ │ │ # commits / contributors / deps
│ │ └── analyzer.py # Objective analysis + contribution grading
│ └── generation/
│ └── bullet_writer.py # bullet wording (including negligible skip)
├── templates/
│ ├── modern.html # Single-column relaxed
│ └── compact.html # Two-column compact
├── scripts/e2e_demo.py
├── tests/
├── pyproject.toml
├── .env.example
└── README.md
```
---
## Quick Start
### 1. Installation
Requires Python ≥ 3.11, recommended [uv](https://github.com/astral-sh/uv):
```bash
cd resume-repo-sync
uv sync --extra dev
```
#### WeasyPrint (Windows)
```bash
winget install --id tschoonj.GTKForWindows --accept-package-agreements
# bin is usually at: C:\Program Files\GTK3-Runtime Win64\bin
# renderer will automatically detect; can also set WEASYPRINT_GTK_BIN
```
Chinese font stack (shared by both templates): `Microsoft YaHei` / `Noto Sans SC` / `SimSun`.
Local verification: PDF embedded **Microsoft YaHei**, `pdfplumber` can extract "Zhang San" "Tsinghua University" and other Chinese text.
### 2. Configuration
```bash
cp .env.example .env
```
| Variable | Description |
|------|------|
| `GITHUB_TOKEN` | Private repository / increase API quota (optional) |
| `RESUME_TMP_DIR` | Managed temporary PDF root directory |
| `PDF_TMP_TTL_HOURS` | Managed PDF expiration hours (default 24; `0`=clear every time) |
| `WEASYPRINT_GTK_BIN` | Windows GTK `bin` override |
| `LOG_LEVEL` | Default `INFO` (only operation status, no resume content/token) |
### 3. Testing / Demo / Launch
```bash
uv run pytest -q
# End-to-end (built-in sample resume + real public repository)
uv run python scripts/e2e_demo.py
# MCP stdio
uv run resume-repo-sync
# Inspector
uv run mcp dev src/resume_repo_sync/server.py
```
### 4. Claude Desktop (optional)
`%APPDATA%\Claude\claude_desktop_config.json`:
```json
{
"mcpServers": {
"resume-repo-sync": {
"command": "uv",
"args": [
"--directory",
"C:\\Users\\admin\\Desktop\\resume-repo-sync",
"run",
"resume-repo-sync"
],
"env": { "GITHUB_TOKEN": "ghp_xxxxxxxx" }
}
}
}
```
---
## PDF Template Comparison
Both templates are **Jinja2 + Chinese font stack**, switched using `render_pdf(..., template="...")`;
Unknown template name returns **`template_not_found`** (lists available templates), **does not** silently fall back to default template.
| Dimension | `modern` (default) | `compact` |
|------|------------------|-----------|
| Layout | **Single-column** vertical flow | **Two-column**: left track (contact/skill/education) + right main column (work/project) |
| Margins | About 1.6–1.8cm, more whitespace | About 1.0–1.1cm, higher information density |
| Font size levels | Name ~20pt, section ~12pt, body ~10.5pt | Name ~16pt, section small title ~8.5pt, body ~8.5–9pt |
| Section style | Bold underline `h2` partition | Capital fine line `block-title`, more compact items |
| Project display | Each bullet point has a loose interval, tech stack on a separate line | `ul.dense` compact list, tech stack in italics on one line |
| Suitable scenario | Few projects on one page, hoping for comfortable reading | More content, hoping to fit one page / ATS-friendly dense version |
Call example:
```text
render_pdf(resume_data, template="modern")
render_pdf(resume_data, template="compact")
render_pdf(resume_data, template="nope") → error_code=template_not_found
```
New `*.html` templates can be added in `templates/`; `list_templates()` / error details will automatically discover.
---
## End-to-End Example
```text
1) parse_resume(pdf_path)
2) fetch_repo_info(repo_url, github_username?)
3) analyze_repo_for_resume(repo_info, github_username?, target_role?)
→ contribution_level + absolute_contribution_signal
4) generate_bullet_points(analysis)
→ If skip_project_entry=true: don't merge, follow recommendation
5) merge_into_resume(...)
6) diff_resume_versions(old, new) # Human confirmation
7) render_pdf(..., template="compact") # or modern
```
Actual test (sample resume + `modelcontextprotocol/python-sdk`) will see similar output in diff:
```text
+ New project [0] python-sdk
• Participated… / used Python… / project obtained N GitHub Stars
```
---
## Contribution Grading (Resume Integrity)
**Proportion** → `contribution_level`:
| level | Condition |
|-------|------|
| `primary` | ≥ 50% listed contributions |
| `contributor` | 20% – <50% |
| `minor` | < 20% |
| `unknown` | No data |
**Absolute count** → `absolute_contribution_signal` (used to refine **minor** wording):
| signal | Absolute listed contributions | minor bullet |
|--------|---------------------------|-----------------|
| `negligible` | < 3 | **No complete project entry generated**; `skip_project_entry` + recommendation |
| `moderate` | 3–19 | "Participated in contribution / assisted" |
| `substantial` | ≥ 20 | Specify exact submission count; still prohibit "led/independently completed" |
| `unknown` | No data | Neutral wording |
Large repositories with "low proportion but many submissions" and "almost no contributions" are no longer mixed.
---
## Privacy and Data Retention
| Data | Saved? | Retention |
|------|--------|------|
| User resume PDF | Read-only, no copying | User-managed |
| Parsed workdir | System temp private directory | **Deleted before returning** (including abnormal `finally`) |
| `ResumeData` / `RepoInfo` | Only in-memory / MCP response | Disappears after process ends |
| `render_pdf` default output | `{system_temp}/resume-repo-sync/resume-*.pdf` | TTL (default 24h) automatic deletion |
| Explicit `output_path` | Caller-specified path | **Never automatically deleted** |
| `GITHUB_TOKEN` | Environment variable / `.env` | Not written to logs |
Logs only record ok/fail, basename, count – **not** resume content, bullet points, or tokens.
`.gitignore` already shields `.env`, `*.pdf`, `uploads/`, `output/`, `tmp/`, etc.
---
## Design Decisions (Display / Interview)
1. **Self-contained GitHub REST, not nested GitHub MCP**
Controllable error semantics (private repository / rate limit / 404 → stable `ToolErrorCode`), fewer deployed processes.
2. **Business logic not in `@mcp.tool`**
Ordinary functions can be tested with `pytest`; MCP layer only handles IO envelope.
3. **Parsing uses heuristics, no LLM calls**
Predictable behavior, zero additional API cost; extreme layouts use `parse_warnings` / `raw_text` for upper-layer model fallback.
4. **WeasyPrint + Jinja2**
Layout and logic separation; adding templates = adding HTML files, no need to modify Python branches.
5. **Contribution grading drives wording**
Avoid writing "contributed several PRs in a large repository" as "independently led", ensuring verifiability and integrity.
---
## MCP Inspector Quick Reference
```bash
uv run mcp dev src/resume_repo_sync/server.py
```
- **Template switching**: try `render_pdf` with `template` set to `modern`, `compact`, and then `does-not-exist` → `template_not_found`.
- **negligible**: public large repository + non-existent `github_username` → `skip_project_entry=true`.
- **Conflict**: `merge_into_resume` with same name and `overwrite=false` → `project_already_exists`.
---
## License
MIT
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available