{"_id":"@doyeonjeong/resume-mcp","_rev":"2-7489443eaf772ec787dfbc1c5ab6e7dc","name":"@doyeonjeong/resume-mcp","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.1":{"name":"@doyeonjeong/resume-mcp","version":"0.1.1","keywords":["mcp","model-context-protocol","resume","cover-letter","job-description","ats","ollama","local-llm","claude","cursor"],"author":{"name":"doyeonjeong"},"license":"MIT","_id":"@doyeonjeong/resume-mcp@0.1.1","maintainers":[{"name":"doyeonjeong","email":"dev.doyeonjeong@gmail.com"}],"homepage":"https://github.com/doyeonjeong/IT-Resume-MCP","bugs":{"url":"https://github.com/doyeonjeong/IT-Resume-MCP/issues"},"bin":{"resume-mcp":"dist/mcp-server.js"},"dist":{"shasum":"8696044139bc0dc7c33b33c5f9d79afd9d6eb4fd","tarball":"https://registry.npmjs.org/@doyeonjeong/resume-mcp/-/resume-mcp-0.1.1.tgz","fileCount":102,"integrity":"sha512-WeZBKcKzrsAU/YkIcwr1U2csQCbJWTyL9+czhrnkJmAfNMX2I4T6lgJydmNeAvEIuN/QfDxv8YmSxw44xVrplQ==","signatures":[{"sig":"MEYCIQDLoBC3tlz79DIY/1AHx+yt+zRFgLCOoYBfQP73ykL1EQIhALUgIGQ1QqmgyOxi3rxWsNopoz32qZmyFiVNPpV/uAvK","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":449329},"jest":{"rootDir":"src","testRegex":".*\\.spec\\.ts$","transform":{"^.+\\.(t|j)s$":"ts-jest"},"testEnvironment":"node","coverageDirectory":"../coverage","collectCoverageFrom":["**/*.(t|j)s"],"moduleFileExtensions":["js","json","ts"]},"engines":{"node":">=20"},"gitHead":"9d0581d4925d590f0eb3b894df041a491f2a8085","scripts":{"mcp":"tsx src/mcp-server.ts","lint":"eslint \"{src,apps,libs,test}/**/*.ts\" --fix","test":"jest","build":"nest build && chmod +x dist/mcp-server.js","smoke":"npm run build && node scripts/smoke.mjs","start":"nest start","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","inspect":"npm run build && npx @modelcontextprotocol/inspector node dist/mcp-server.js","test:cov":"jest --coverage","test:e2e":"jest --config ./test/jest-e2e.json","pack:test":"npm run build && npm pack && echo '\\n→ test the packed tarball with: npx -y ./'$(ls -1t doyeonjeong-resume-mcp-*.tgz | head -1)","start:dev":"nest start --watch","start:prod":"node dist/main","test:debug":"node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand","test:watch":"jest --watch","start:debug":"nest start --debug --watch","prepublishOnly":"npm run build"},"_npmUser":{"name":"doyeonjeong","email":"dev.doyeonjeong@gmail.com"},"repository":{"url":"git+https://github.com/doyeonjeong/IT-Resume-MCP.git","type":"git"},"_npmVersion":"11.9.0","description":"Local-only MCP server that turns a job description into a tailored resume from your private profile. Profile data stays on your machine.","directories":{},"_nodeVersion":"24.14.0","dependencies":{"zod":"^4.1.13","rxjs":"^7.8.1","dotenv":"^17.2.3","marked":"^17.0.1","openai":"^6.9.1","postgres":"^3.4.7","@nestjs/core":"^11.0.1","@google/genai":"^1.31.0","@types/marked":"^5.0.2","@nestjs/config":"^4.0.2","class-validator":"^0.14.3","reflect-metadata":"^0.2.2","@anthropic-ai/sdk":"^0.71.0","class-transformer":"^0.5.1","@supabase/supabase-js":"^2.86.2","@nestjs/platform-express":"^11.0.1","@modelcontextprotocol/sdk":"^1.24.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","jest":"^30.2.0","eslint":"^9.18.0","globals":"^16.0.0","ts-jest":"^29.4.6","ts-node":"^10.9.2","prettier":"^3.4.2","supertest":"^7.0.0","ts-loader":"^9.5.2","@eslint/js":"^9.18.0","typescript":"^5.7.3","@nestjs/cli":"^11.0.0","@types/jest":"^30.0.0","@types/node":"^22.10.7","@nestjs/common":"^11.1.9","@types/express":"^5.0.0","tsconfig-paths":"^4.2.0","@nestjs/testing":"^11.1.9","@eslint/eslintrc":"^3.2.0","@types/supertest":"^6.0.2","typescript-eslint":"^8.20.0","@nestjs/schematics":"^11.0.0","source-map-support":"^0.5.21","@jest/test-sequencer":"^30.2.0","eslint-config-prettier":"^10.0.1","eslint-plugin-prettier":"^5.2.2"},"_npmOperationalInternal":{"tmp":"tmp/resume-mcp_0.1.1_1777738700095_0.4575246235002508","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@doyeonjeong/resume-mcp","version":"0.1.2","description":"Local-only MCP server that turns a job description into a tailored resume from your private profile. Profile data stays on your machine.","author":{"name":"doyeonjeong"},"license":"MIT","homepage":"https://github.com/doyeonjeong/IT-Resume-MCP","repository":{"type":"git","url":"git+https://github.com/doyeonjeong/IT-Resume-MCP.git"},"bugs":{"url":"https://github.com/doyeonjeong/IT-Resume-MCP/issues"},"keywords":["mcp","model-context-protocol","resume","cover-letter","job-description","ats","ollama","local-llm","claude","cursor"],"publishConfig":{"access":"public"},"bin":{"resume-mcp":"dist/mcp-server.js"},"engines":{"node":">=20"},"scripts":{"build":"nest build && chmod +x dist/mcp-server.js","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","start":"nest start","start:dev":"nest start --watch","start:debug":"nest start --debug --watch","start:prod":"node dist/main","mcp":"tsx src/mcp-server.ts","smoke":"npm run build && node scripts/smoke.mjs","inspect":"npm run build && npx @modelcontextprotocol/inspector node dist/mcp-server.js","pack:test":"npm run build && npm pack && echo '\\n→ test the packed tarball with: npx -y ./'$(ls -1t doyeonjeong-resume-mcp-*.tgz | head -1)","lint":"eslint \"{src,apps,libs,test}/**/*.ts\" --fix","test":"jest","test:watch":"jest --watch","test:cov":"jest --coverage","test:debug":"node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand","test:e2e":"jest --config ./test/jest-e2e.json","prepublishOnly":"npm run build"},"dependencies":{"@anthropic-ai/sdk":"^0.71.0","@google/genai":"^1.31.0","@modelcontextprotocol/sdk":"^1.29.0","@nestjs/config":"^4.0.2","@nestjs/core":"^11.0.1","@nestjs/platform-express":"^11.0.1","@supabase/supabase-js":"^2.86.2","@types/marked":"^5.0.2","class-transformer":"^0.5.1","class-validator":"^0.14.3","dotenv":"^17.2.3","marked":"^17.0.1","openai":"^6.9.1","postgres":"^3.4.7","reflect-metadata":"^0.2.2","rxjs":"^7.8.1","zod":"^4.1.13"},"devDependencies":{"@eslint/eslintrc":"^3.2.0","@eslint/js":"^9.18.0","@jest/test-sequencer":"^30.2.0","@nestjs/cli":"^11.0.0","@nestjs/common":"^11.1.9","@nestjs/schematics":"^11.0.0","@nestjs/testing":"^11.1.9","@types/express":"^5.0.0","@types/jest":"^30.0.0","@types/node":"^22.10.7","@types/supertest":"^6.0.2","eslint":"^9.18.0","eslint-config-prettier":"^10.0.1","eslint-plugin-prettier":"^5.2.2","globals":"^16.0.0","jest":"^30.2.0","prettier":"^3.4.2","source-map-support":"^0.5.21","supertest":"^7.0.0","ts-jest":"^29.4.6","ts-loader":"^9.5.2","ts-node":"^10.9.2","tsconfig-paths":"^4.2.0","tsx":"^4.21.0","typescript":"^5.7.3","typescript-eslint":"^8.20.0"},"jest":{"moduleFileExtensions":["js","json","ts"],"rootDir":"src","testRegex":".*\\.spec\\.ts$","transform":{"^.+\\.(t|j)s$":"ts-jest"},"collectCoverageFrom":["**/*.(t|j)s"],"coverageDirectory":"../coverage","testEnvironment":"node"},"gitHead":"9e364bccf6c61627118706a5289b82acab271043","_id":"@doyeonjeong/resume-mcp@0.1.2","_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-I8lJqT+60vDIAPTWkqpgm4IO2xjzzDl0D5FQQ3eKCzKSpCJsGPsyjsYIjlutWDfJHZfhwpmNvvXduOHtVf8rJQ==","shasum":"fe220b4e27099a720e455a10bdba14afb6c36aa5","tarball":"https://registry.npmjs.org/@doyeonjeong/resume-mcp/-/resume-mcp-0.1.2.tgz","fileCount":102,"unpackedSize":449522,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCID041h+vjhhQAoIkiF/5kzfiGll8dx7RluCDCRD4bhcJAiEAyyNDzRsHiyp+NuP8mmXRcAnu6v/8LYdUOxgKjLHmL30="}]},"_npmUser":{"name":"doyeonjeong","email":"dev.doyeonjeong@gmail.com"},"directories":{},"maintainers":[{"name":"doyeonjeong","email":"dev.doyeonjeong@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/resume-mcp_0.1.2_1777798285415_0.7327409284637416"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-02T16:18:20.016Z","modified":"2026-05-03T08:51:25.688Z","0.1.1":"2026-05-02T16:18:20.244Z","0.1.2":"2026-05-03T08:51:25.568Z"},"bugs":{"url":"https://github.com/doyeonjeong/IT-Resume-MCP/issues"},"author":{"name":"doyeonjeong"},"license":"MIT","homepage":"https://github.com/doyeonjeong/IT-Resume-MCP","keywords":["mcp","model-context-protocol","resume","cover-letter","job-description","ats","ollama","local-llm","claude","cursor"],"repository":{"type":"git","url":"git+https://github.com/doyeonjeong/IT-Resume-MCP.git"},"description":"Local-only MCP server that turns a job description into a tailored resume from your private profile. Profile data stays on your machine.","maintainers":[{"name":"doyeonjeong","email":"dev.doyeonjeong@gmail.com"}],"readme":"# Resume MCP\n\n> **로컬 전용 MCP 서버.** 채용 공고(JD)를 입력하면 본인 프로필을 바탕으로 맞춤 이력서를 생성합니다.\n> 프로필과 생성된 문서는 **사용자 머신을 떠나지 않습니다.** LLM 호출만 본인 API 키로 외부에 나가고, Local LLM(Ollama)을 쓰면 완전 오프라인입니다.\n\n[![npm](https://img.shields.io/npm/v/@doyeonjeong/resume-mcp.svg)](https://www.npmjs.com/package/@doyeonjeong/resume-mcp)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\n대부분의 이력서 AI 도구는 본인 경력 데이터를 자기네 서버에 업로드합니다. 이 도구는 그러지 않습니다. 프로필은 사용자 디스크의 `~/.resume-mcp/profile.json`에 그대로 있고, MCP 서버는 Cursor / Claude Code / Antigravity 등 이미 쓰고 있는 클라이언트 안에서 로컬 프로세스로 돕니다 — 제3자 SaaS가 데이터를 들고 있을 일이 없습니다.\n\n## 왜 MCP고, 왜 로컬인가\n\n- **Privacy by design** — 프로필 JSON은 사용자 파일시스템에만 존재. DB 없음, 클라우드 동기화 없음, 텔레메트리 없음.\n- **재사용 가능** — 경력 데이터를 한 번 작성하면 수십 번의 맞춤 이력서로 재활용.\n- **조립형 파이프라인** — `analyze_jd → match_profile_to_jd → generate_resume_bullets → generate_resume_markdown` 으로 단계 분리. 중간에 사용자가 개입 가능.\n- **AI IDE 네이티브** — Cursor, Claude Code, Cline, Antigravity 등 MCP 호환 클라이언트에서 동작.\n\n## 설치 (60초)\n\nMCP 클라이언트 설정 (예: `~/.cursor/mcp.json`, `~/.claude/mcp_config.json`)에 한 줄 등록:\n\n```json\n{\n  \"mcpServers\": {\n    \"resume-mcp\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@doyeonjeong/resume-mcp\"],\n      \"env\": {\n        \"LLM_PROVIDER\": \"gemini\",\n        \"GOOGLE_API_KEY\": \"your_api_key_here\"\n      }\n    }\n  }\n}\n```\n\n끝. 클라이언트를 재시작하고 *\"call get_profile\"* 이라고 입력하세요. 첫 호출 시 `~/.resume-mcp/profile.json`이 템플릿으로 자동 생성됩니다.\n\n### LLM Provider 선택\n\nCloud LLM은 한 방 생성 tool에 사용됩니다. 가지고 있는 키 중 아무거나 선택:\n\n```json\n\"env\": {\n  \"LLM_PROVIDER\": \"claude\",     // 또는 \"openai\" | \"gemini\"\n  \"ANTHROPIC_API_KEY\": \"...\",\n  \"OPENAI_API_KEY\": \"...\",\n  \"GOOGLE_API_KEY\": \"...\"\n}\n```\n\n단계형 파이프라인(`analyze_jd`, `match_profile_to_jd` 등)은 기본적으로 **Local LLM**을 사용합니다. Ollama(권장) 또는 OpenAI 호환 endpoint:\n\n```json\n\"env\": {\n  \"LOCAL_LLM_PROVIDER\": \"ollama\",\n  \"LOCAL_LLM_MODEL\": \"gemma4:26b\"\n}\n```\n\n## 프로필 세팅\n\n`~/.resume-mcp/profile.json`을 본인 경력 데이터로 채우세요:\n\n```json\n{\n  \"name\": \"정도연\",\n  \"title\": \"Backend Developer\",\n  \"summary\": \"...\",\n  \"skills\": [\"NestJS\", \"TypeScript\", \"...\"],\n  \"projects\": [\n    {\n      \"title\": \"프로젝트명\",\n      \"period\": \"2025.01 ~ 2025.06\",\n      \"role\": \"백엔드 리드\",\n      \"description\": \"본인이 실제로 한 일\",\n      \"techStack\": [\"...\"],\n      \"achievements\": \"구체적이고 사실 기반 성과\",\n      \"githubUrl\": \"https://github.com/you/project\"\n    }\n  ]\n}\n```\n\n또는 AI에게 대화로 채우게 시키기:\n\n> \"내 프로필 업데이트해줘. 스킬은 NestJS, TypeScript, Python, MCP SDK\"\n\n→ AI가 `update_profile` 호출 → 파일이 부분 업데이트됨.\n\nplaceholder 값(`홍길동`, `프로젝트명`, `username/project`)이 남아있으면 서버가 **생성을 거부**합니다 — 보일러플레이트 이력서가 잘못 나가는 사고를 막아줍니다.\n\n### 프로필 위치 변경\n\n```json\n\"env\": {\n  \"RESUME_MCP_PROFILE_PATH\": \"/path/to/your/profile.json\"\n}\n```\n\n## 사용 시나리오\n\n### 시나리오 1 — JD 입력, 이력서 한 방\n\n채팅에 JD 본문 붙여넣기:\n\n> \"이 JD로 이력서 만들어줘. 회사: ROSAIC, 직무: LLM 엔지니어.\\n\\n<JD 본문>\"\n\n→ AI가 `generate_resume({ jdText, companyName, position, language: \"ko\" })` 호출 → markdown 이력서 10~20초 내.\n\n### 시나리오 2 — 단계별 파이프라인 (정확도 ↑, 권장)\n\n4개 tool, 각 단계마다 사용자 개입 가능:\n\n| 단계 | Tool | 출력 |\n|---|---|---|\n| 1 | `analyze_jd` | 구조화 JSON: 필수/우대 스킬, ATS 키워드, risk factors |\n| 2 | `match_profile_to_jd` | strong matches, partial matches, **과장하면 안 되는 항목** |\n| 3 | `generate_resume_bullets` | ATS 친화 bullet, summary, 자소서 hooks |\n| 4 | `generate_resume_markdown` | 최종 markdown, 템플릿 선택 (`general` / `backend` / `fullstack` / `ios` / `ai-agent`) |\n\n총 ~90초 (Local LLM 기준). 핵심은 2단계 — *\"이건 과장이다\"*를 사전에 잡아주는 안전장치.\n\n### 시나리오 3 — 자기소개서 한 방\n\n> \"이 JD로 자기소개서 써줘\"\n\n→ `generate_cover_letter({ jdText, companyName, position, language: \"ko\" })`\n\n### 시나리오 4 — 포트폴리오 요약본\n\n> \"내 프로필을 포트폴리오 한 장으로 정리해줘\"\n\n→ `generate_portfolio({ language: \"ko\" })`\n\n## MCP Tools\n\n| Tool | 용도 |\n|---|---|\n| `get_profile` | 현재 프로필 조회 |\n| `update_profile` | 프로필 JSON 부분 업데이트 |\n| `generate_resume` | JD 맞춤 이력서 한 방 생성 (Cloud LLM) |\n| `generate_portfolio` | 포트폴리오 요약본 (Cloud LLM) |\n| `generate_cover_letter` | 자기소개서 (Cloud LLM) |\n| `analyze_jd` | JD를 구조화 JSON으로 분석 (Local LLM) |\n| `match_profile_to_jd` | JD 분석 × 프로필 → 매칭 리포트 (Local LLM) |\n| `generate_resume_bullets` | ATS 친화 bullet (Local LLM) |\n| `generate_resume_markdown` | 최종 markdown 이력서 (Local LLM) |\n\n## 환경변수\n\n| 변수 | 기본값 | 설명 |\n|---|---|---|\n| `LLM_PROVIDER` | `gemini` | `claude` / `openai` / `gemini` |\n| `ANTHROPIC_API_KEY` | — | `LLM_PROVIDER=claude`일 때 필수 |\n| `OPENAI_API_KEY` | — | `LLM_PROVIDER=openai`일 때 필수 |\n| `GOOGLE_API_KEY` | — | `LLM_PROVIDER=gemini`일 때 필수 |\n| `LOCAL_LLM_PROVIDER` | `ollama` | `ollama` / `openai-compatible` |\n| `LOCAL_LLM_BASE_URL` | provider 기본값 | Ollama: `http://localhost:11434` |\n| `LOCAL_LLM_MODEL` | provider 기본값 | 예: `gemma4:26b` |\n| `LOCAL_LLM_API_KEY` | — | OpenAI 호환 endpoint(HF Router 등) 토큰 |\n| `LLM_TIMEOUT_MS` | `120000` | per-call timeout |\n| `RESUME_MCP_PROFILE_PATH` | `~/.resume-mcp/profile.json` | 프로필 경로 override |\n\n## Local LLM 셋업\n\n### 옵션 A — Ollama (권장)\n\n```bash\nbrew install ollama          # 또는 https://ollama.com/download\nollama pull gemma4:26b\nollama serve\n```\n\nMCP 환경변수:\n```json\n\"LOCAL_LLM_PROVIDER\": \"ollama\",\n\"LOCAL_LLM_MODEL\": \"gemma4:26b\"\n```\n\n### 옵션 B — OpenAI 호환 endpoint (MLX, vLLM, LM Studio, HF Router…)\n\n```json\n\"LOCAL_LLM_PROVIDER\": \"openai-compatible\",\n\"LOCAL_LLM_BASE_URL\": \"http://localhost:8080/v1\",\n\"LOCAL_LLM_MODEL\": \"mlx-community/gemma-4-26b-a4b-it-4bit\",\n\"LOCAL_LLM_API_KEY\": \"any-non-empty-string\"\n```\n\n## 트러블슈팅\n\n| 에러 | 해결 |\n|---|---|\n| `PROFILE_INCOMPLETE` | `~/.resume-mcp/profile.json`에 placeholder가 남아있음. `홍길동`, `프로젝트명` 등을 실제 값으로 교체 |\n| `OLLAMA_CONNECTION_FAILED` | `ollama serve` 실행 (기본 포트 11434) |\n| `LOCAL_LLM_CONNECTION_FAILED` | OpenAI 호환 endpoint 미접속. `LOCAL_LLM_BASE_URL` 확인 |\n| `LOCAL_LLM_AUTH_FAILED` | `LOCAL_LLM_API_KEY` 누락/잘못됨. HF Router는 토큰 권한 확인 |\n| `LOCAL_LLM_MODEL_UNAVAILABLE` | 모델이 서빙 안 됨. `ollama pull <model>` 또는 다른 모델 선택 |\n| `No LLM API key found` | Cloud tool 사용 시 `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_API_KEY` 중 최소 하나 필요 |\n\n## 로컬 개발\n\n```bash\ngit clone https://github.com/doyeonjeong/IT-Resume-MCP.git\ncd IT-Resume-MCP\nnpm install\ncp env.example .env\n```\n\n### 한 줄 명령어 3종\n\n```bash\nnpm run smoke       # 5초 — 서버 부팅 + 9개 tool 등록 확인 (publish 전 헬스체크)\nnpm run inspect     # 브라우저 GUI에서 모든 tool 인터랙티브 호출 (MCP Inspector)\nnpm run pack:test   # 진짜 npm tarball 만들어서 publish 시뮬레이션\n```\n\n자세한 IDE 연결 방법(Antigravity, Cursor, Claude Code, Cline 등)과 5분 검증 시나리오는 [TESTING.md](./TESTING.md) 참고.\n\n### 기본 명령어\n\n```bash\nnpm test            # 단위 테스트 (53개)\nnpm run build       # NestJS 빌드 + chmod +x\nnpm run mcp         # tsx로 dev 모드 실행\n```\n\n로컬 dev 빌드를 MCP 클라이언트에 등록:\n\n```json\n{\n  \"mcpServers\": {\n    \"resume-mcp-dev\": {\n      \"command\": \"node\",\n      \"args\": [\"/absolute/path/to/IT-Resume-MCP/dist/mcp-server.js\"]\n    }\n  }\n}\n```\n\n## 기술 스택\n\nNestJS 11 · TypeScript · MCP SDK · Ollama · OpenAI / Anthropic / Gemini SDKs · Zod\n\n## License\n\nMIT — [LICENSE](./LICENSE) 참고.\n","readmeFilename":"README.md"}