Skip to main content
Glama

Waple 업무일지 MCP 서버

개발자가 하루 동안 수행한 작업을 Git 기록과 Claude Code 세션 로그에서 자동으로 수집하여 업무일지 초안을 만들고, 사용자가 승인한 내용만 HR SaaS 플랫폼(Waple)에 등록하는 MCP 서버입니다.

8주 인턴 팀 프로젝트(4인) 중 MCP 서버 파트를 담당하여 개발하였습니다. 코드 리뷰는 팀원이 수행하였으며, PR #13에서 지적받은 인증 처리 결함을 반영해 구조를 수정하였습니다.


1. 왜 만들었는가

업무일지는 매일 써야 하지만, 하루가 끝날 무렵에는 무엇을 했는지 정확히 기억나지 않습니다. 그 결과 "개발 진행함" 같은 내용 없는 기록이 쌓이고, 인사 데이터로서의 가치를 잃습니다.

이미 커밋 메시지와 변경 파일에 하루의 기록이 남아 있는데도 다시 손으로 쓰고 있다는 점에 주목하였습니다. 그래서 이미 존재하는 근거를 모아 초안을 만들고, 사람은 확인만 하는 방향으로 설계하였습니다.


Related MCP server: Povio Worklog MCP Server

2. 시연

로컬 시연 (3분 51초) — 초안 생성부터 승인·등록까지 전체 흐름

https://github.com/user-attachments/assets/11a2e83e-085b-435b-8d98-cd3bad4a6dc9

등록 결과 (1분 43초) — Waple 웹 화면에서 등록된 업무일지 확인

https://github.com/user-attachments/assets/4606369c-0ba7-4db5-b42f-f2e1ab72ff88

실제 생성된 초안 (2026-07-25)

오늘 한 일
- test_connector.py 의 import 문을 파일 상단으로 정리하였습니다. [커밋 97a822d9]
- 포트폴리오용 시연 자료를 촬영하였습니다. [사용자 메모]
- 오늘 사용한 토큰: 세션 로그에서 자동 집계[Claude Code 세션 로그]

각 항목 끝의 대괄호가 작성 근거입니다. 커밋에서 나온 내용인지, 사용자가 직접 적은 메모인지, 세션 로그에서 집계한 값인지 구분됩니다. 근거가 없는 내용은 지어내지 않고 "사용자 확인 필요" 항목으로 분리합니다.


3. 동작 구조

[Claude Code / Claude 데스크톱 앱]
            │  MCP 프로토콜
            ▼
    ┌───────────────────┐
    │   MCP 서버        │
    │  (server.py)      │
    ├───────────────────┤
    │ 근거 수집          │ ← git log / git diff / 세션 로그(JSONL)
    │ 업무 단위 분류     │
    │ 초안 생성          │
    │ 승인 확인          │ ← 여기서 멈춤. 승인 전에는 등록하지 않음
    │ Waple API 호출     │ ──▶ POST /api/mcp/diary
    └───────────────────┘

이중 transport 구조

방식

용도

인증

stdio

로컬 Claude Code

.env 파일의 키

Streamable HTTP

원격 접속 (nginx + HTTPS)

요청 헤더 x-api-key

HTTP 하나로 통일하지 못한 이유가 있습니다. Git 기록과 세션 로그는 사용자 PC에 있는 자료입니다. 원격 서버는 이 자료에 접근할 수 없으므로, 자동 수집 기능을 유지하려면 로컬 경로가 함께 필요하였습니다.

이 구분이 실제로 얼마나 중요한지는 프로젝트가 끝난 뒤에야 드러났습니다(9절 참고). 원격의 tasklog 는 "아무것도 읽지 못하는" 상태가 아니라 서버 자신의 저장소를 읽는 상태였고, 그래서 지금은 HTTP 모드에서 호출을 차단합니다.


4. 제공 도구

도구

역할

waple_login

API 키 검증

tasklog

Git 기록 기반 초안 생성 (로컬 전용, 원격 호출은 차단)

chat_tasklog

대화 내용 기반 초안 생성 (원격 지원)

submit_worklog

승인된 초안만 Waple에 등록

초안 생성과 등록을 의도적으로 다른 도구로 분리하였습니다. 하나로 합치면 "정리해줘"라는 말에도 등록이 실행될 수 있기 때문입니다.


5. 설계 시 신경 쓴 부분

승인 없이는 등록하지 않는다

정리해줘, 초안 만들어줘, 검토해줘 는 승인으로 보지 않습니다. 등록해, 승인, 이 내용으로 올려 처럼 명확한 표현이 있을 때만 API를 호출합니다.

요청별 키 격리

원격 모드에서는 여러 사용자가 같은 서버 프로세스를 공유합니다. ContextVar 로 요청마다 키를 분리하고, 초안 캐시도 키의 해시값으로 사용자별 스코핑하여 다른 사용자의 초안이 섞이지 않도록 하였습니다.

HTTP 모드에서는 키를 저장하지 않는다

.env 저장 로직은 stdio 모드에서만 동작합니다. 원격 사용자의 키가 서버 파일에 남는 것을 막기 위함입니다.


6. 빠른 시작

git clone https://github.com/psy0635-ctrl/waple-worklog-mcp-portfolio.git
cd waple-worklog-mcp-portfolio

python -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
pip install -r requirements.txt

cp .env.example .env             # 실제 값을 채워 넣습니다

로컬 실행 (Claude Code)

cp .mcp.json.example .mcp.json
claude                           # 실행 후 /mcp 로 연결 확인

원격 실행 (Streamable HTTP)

python server.py --transport streamable-http --port 8010
claude mcp add --transport http waple-remote \
  https://[SERVER_URL]/llm/mcp --header "x-api-key: 발급받은키"

자세한 배포·연동 절차는 docs/integration-manual.md 를 참고하십시오.


7. 테스트

python -m pytest -q
70 passed, 1 skipped

skipped 1건은 로그 폴더 대소문자 혼재 케이스입니다. Windows 파일시스템에서는 대소문자만 다른 폴더를 동시에 만들 수 없어 건너뛰고, 리눅스에서 별도로 실행해 통과를 확인하였습니다.

파일

검증 내용

test_connector.py

원격 커넥터, 사용자별 초안 스코핑, 원격 tasklog 차단

test_cache.py

초안 캐시 동작

test_guideline17.py

승인 절차·필수값 누락·중복 등록 방지

test_login_retry.py

인증 실패 시 재시도 안내

test_token_usage.py

세션 로그 기반 토큰 집계

test_draft_cleanup.py

초안 캐시 누적 방지 (등록 후 삭제, TTL 정리)

test_multiline_input.py

여러 줄 입력 시 불릿·근거 라벨 정규화 (_format_bullet_lines)


8. 검증 현황

사실과 추정을 구분하기 위해 항목마다 검증 수준을 표기하였습니다.

항목

결과

검증 수준

로컬(stdio) 전체 흐름

초안 생성 → 승인 → Waple 등록 성공

실물 검증

원격(HTTP) 연동

Claude Code 에서 호출, 서버 로그에 기록 확인

실물 검증

자동 재시작

프로세스 강제 종료 후 PID 변경 확인

실물 검증

재부팅 후 자동 기동

systemctl --user is-enabled, Linger=yes 확인

설정 확인 (재부팅 미실시)

claude.ai 웹 커넥터

연결·도구 목록·도구 호출은 성공, 인증은 불가 (헤더 지정 수단 없음)

실물 검증 (직접 호출 + 설정 화면 확인)

원격 tasklog 차단

배포 서버에서 호출 → 차단 응답 반환, 서버 로그에 호출 기록

실물 검증 (9절)

웹 커넥터가 되지 않는 이유는 추정이 아니라 서버 로그로 확인하였습니다. 5일간(2026.07.21 ~ 07.25) 누적된 요청을 발신 경로별로 대조한 결과입니다.

발신 경로

도구 목록 조회

도구 호출

claude.ai 웹 커넥터

19건

0건

설정 파일 기반 클라이언트 (Claude Code, 데스크톱 앱)

26건

12건

웹 커넥터 쪽 응답 코드는 200과 202뿐이고 4xx·5xx는 없었습니다. 연결·TLS·프록시·목록 조회까지는 정상이었고, 이 기간에는 도구 호출이 성립하지 않았습니다.

이후 2026-07-28 에 웹 커넥터로 도구를 직접 호출해 서버 응답을 받았습니다. 관찰에서 추론하던 원인을 실물로 확정한 것입니다.

시점

도구 목록 조회

도구 호출

인증

2026.07.21 ~ 07.25

19건

0건

(판단 불가)

2026.07.28

정상 (4개 인식)

성공

실패

07.28 호출 시 서버가 반환한 것은 "요청에 x-api-key 헤더가 없다"는 안내였습니다. 즉 연결과 도구 호출은 되지만 인증 수단이 없어 실사용이 불가능한 상태입니다.

claude.ai 웹 커넥터의 설정 화면에서 제공되는 인증 항목은 고급 설정을 펼쳐도 OAuth 클라이언트 ID·시크릿 두 가지뿐이며, 임의의 요청 헤더를 지정하는 입력란은 제공되지 않습니다(2026-07-28 화면 확인). 이 서버는 x-api-key 헤더로 인증하므로 웹 커넥터 경로로는 인증이 성립하지 않습니다.

설정 파일 기반 클라이언트(Claude Code --header, 데스크톱 앱 mcp-remote 브리지)는 헤더를 지정할 수 있어 정상 동작합니다.

검토했으나 원인이 아니었던 것: FastMCP 의 DNS Rebinding 방어가 Origin 헤더를 검증해 https://claude.ai 를 403 으로 거부할 가능성을 확인하였습니다. 그러나 위 로그에서 웹 커넥터 요청의 응답 코드는 200·202 뿐이고 403 이 한 건도 없었으므로, Origin 차단은 실제로 발생하지 않았습니다. 현재 허용 목록에 해당 Origin 이 포함되어 있으나 이는 버그 수정이 아니라 예방적 조치입니다.

webconnector_0728_evidence_masked.txt 의 139행에 403 이 한 건 있으나 이는 Origin 차단 동작을 확인하기 위해 직접 실행한 curl 요청이며, 웹 커넥터 발신 요청(160.79.106.x)의 응답은 200·202 뿐입니다. 차단 기능은 정상 동작하되 웹 커넥터 실패의 원인은 아니었음을 같은 파일에서 확인할 수 있습니다.

마스킹한 로그 발췌는 두 파일로 나누어 두었습니다.


9. 막혔던 부분

문제

원인

해결

원격 접속 시 421 반환

FastMCP 의 DNS Rebinding 방지로 허용 Host 가 127.0.0.1 로 잠김

우선 nginx 에서 Host 헤더를 재작성해 우회. 이후 transport_security 로 허용 Host·Origin 을 코드에 명시 (아래 참고)

커넥터 연결 실패

HTTPS 인증서 체인에 중간 인증서 누락

브라우저는 자동 보완되지만 서버 간 통신은 실패함을 확인, fullchain 적용

서버 재부팅 후 중단

nohup 프로세스가 재부팅과 함께 소멸

sudo 권한이 제한된 환경이라 사용자 systemd + linger 로 해결

API 키 터미널 노출

키 확인 과정에서 값이 평문 출력됨

키 삭제 후 재발급. 이후 키를 다루는 명령은 값이 표시되지 않는 방식으로 변경

프로젝트 종료 후 개선 (2026-07-28)

421 은 nginx 우회로 이미 해결된 상태였지만, 근본 원인을 다시 확인한 결과 SDK 가 방어를 켜주는 조건이 실행 옵션에 달려 있다는 점을 발견하였습니다.

FastMCP 는 생성자의 host127.0.0.1/localhost/::1 일 때만 DNS Rebinding 방어를 자동 활성화합니다. 그런데 이 서버의 실행 인자 도움말은 "외부 공개 배포 시 0.0.0.0" 을 안내하고 있었습니다. 안내대로 실행하면 보안 기능이 경고 한 줄 없이 꺼지는 구조였습니다.

허용 Host·Origin 을 코드에 직접 명시해 실행 옵션·SDK 버전과 무관하게 동일한 설정이 적용되도록 수정하였습니다. 배포 서버에서 적용 전후를 측정한 결과입니다.

측정 조건

before

after

정상 요청

406

406

위조 Host

421

421 (방어 유지)

도메인 Host

421

406

Origin: https://claude.ai

403

406

외부 HTTPS 경유

406

406

허용 목록을 SDK 자동 기본값의 상위집합으로 구성하여, 기존에 동작하던 경로가 막히지 않도록 하였습니다. 적용 후 MCP 핸드셰이크와 도구 호출까지 실측하였습니다. nginx 의 Host 재작성은 이중 안전망으로 그대로 유지하였습니다.

프로젝트 종료 후 개선 (2026-07-29) — 내가 남긴 기록이 틀렸던 사례

원격 환경에서 tasklog 를 호출하면 어떻게 되는지, 문서에는 이렇게 적혀 있었습니다.

초안 자체는 생성되지만 Git 커밋·변경 파일·토큰 사용량이 모두 비어 있습니다. (2026-07-21 기록)

원격 서버가 사용자 PC 에 접근할 수 없으니 당연히 비어 있으리라 여겼고, 실제로 비어 있는 초안을 한 번 보고 그대로 기록하였습니다. 그러나 실사용 중 다시 확인해 보니 초안에 커밋이 채워져 있었습니다. 제가 그날 하지 않은 작업이었습니다.

원인은 서비스 설정 한 줄에 있었습니다.

WorkingDirectory=%h/2026-uxis-mirae/llm팀

taskloggit rev-parse --show-toplevel 로 저장소 루트를 찾는데, 이 명령이 기준으로 삼는 것은 프로세스가 실행 중인 디렉터리입니다. 원격 모드에서 그 디렉터리는 사용자의 작업 폴더가 아니라 배포 서버가 체크아웃해 둔 저장소였습니다. 즉 원격 사용자의 업무일지에 배포 서버의 커밋 이력이 실리는 구조였습니다. 토큰 집계도 같은 이유로 서버 계정의 ~/.claude 를 읽고 있었습니다.

이 문제가 위험한 이유는 틀린 결과가 정상처럼 보인다는 점입니다. 수집이 비어 있으면 사용자가 이상을 눈치채지만, 그럴듯한 커밋 목록이 채워져 있으면 그대로 승인하게 됩니다. 이 프로젝트가 처음부터 지켜 온 원칙 — 확인되지 않은 내용을 사실처럼 쓰지 않는다 — 이 정면으로 깨지는 경로였습니다.

수정은 수집이 시작되기 이전에 차단하는 방식으로 하였습니다. 요청이 HTTP 인지 판별하는 값(ContextVar)은 사용자별 키 격리를 위해 이미 만들어 둔 것이라, 도구 진입점 한 곳에서 조기 반환하는 것으로 충분하였습니다. 조기 반환이므로 초안 캐시가 오염되지 않아, 등록 도구가 잘못된 초안을 집어갈 경로도 함께 사라집니다.

확인 항목

결과

원인

WorkingDirectory 가 배포 서버 저장소를 가리킴 (설정 실측)

차단 동작

배포 서버에 도구 호출 → 차단 응답 반환, 서버 로그에 호출 기록

초안 캐시

조기 반환으로 미오염 (단위 테스트)

로컬 경로 회귀

기존대로 초안 생성 (단위 테스트)

테스트

3케이스 추가, 22 → 25 passed

함께 정정한 것은 코드만이 아닙니다. "수집 항목이 비어 있다" 는 서술이 README·연동 매뉴얼 여러 곳에 퍼져 있었고, 그 문장들을 모두 찾아 실제 동작으로 고쳤습니다. 잘못된 관찰 하나가 문서 전체로 번지면 나중에는 어디까지가 사실인지 알 수 없게 된다는 것을 확인한 사례입니다.

프로젝트 종료 후 개선 (2026-07-29) — 막다른 길을 알려주던 안내

인증에 실패했을 때 서버가 돌려주던 안내는 이랬습니다.

커넥터 설정 → Request Headers → x-api-key 항목에 Waple API 키를 입력해 주세요.

그런데 8절에 적은 대로, claude.ai 웹 커넥터에는 그 입력란이 없습니다. 인증에 실패한 사용자에게 존재하지 않는 화면을 찾아가라고 안내하고 있었던 것입니다.

실제로 헤더를 지정할 수 있는 세 경로(Claude Code --header, 데스크톱 앱 mcp-remote 브리지, 로컬 stdio)와 웹 커넥터로는 불가능하다는 사실을 함께 안내하도록 교체하였습니다.

고치면서 원인을 하나 더 확인하였습니다. 같은 문구가 waple_loginsubmit_worklog 두 곳에 복사되어 있었습니다. 한쪽만 고치면 두 안내가 서로 다른 말을 하게 되므로, 상수로 분리하고 두 도구가 같은 상수를 참조하는지를 테스트로 고정하였습니다. 문구 자체보다 이 중복이 재발 지점이었습니다.

배포 서버에 키 없이 등록 도구를 호출해 새 안내가 반환되는 것까지 확인하였습니다.

프로젝트 종료 후 개선 (2026-07-30)

tomorrow_plan·memo·activities·created_files에 개행이 포함된 값을 넣으면, "- "와 근거 라벨이 조립된 문자열의 첫 줄 또는 마지막 줄에만 붙고 중간 줄은 아무 표시 없이 남는 문제를 확인하였습니다. tomorrow_plan은 글머리 기호만 깨지지만, memo·activities는 라벨이 사라진 줄이 Git에서 확인된 사실처럼 보이게 되어, 확인되지 않은 내용을 사실처럼 쓰지 않는다는 이 프로젝트의 원칙을 정면으로 어기는 경로였습니다.

원인은 같은 조립 로직(f"- {값} [라벨]")이 두 함수에 복사되어 있었던 것입니다. _format_bullet_lines 헬퍼로 로직을 통합해, 값을 줄 단위로 분해한 뒤 각 줄마다 독립적으로 "- "와 근거 라벨을 붙이도록 수정하였습니다.

수정 전(ad8ce45)과 수정 후(47e38ba) 상태에서 같은 재현 스크립트를 실행한 결과를 한 파일에서 대조할 수 있도록 근거로 남겼습니다 (docs/evidence/multiline_input_evidence.txt). 이 근거 파일을 처음 만드는 과정에서 셸을 혼용해 캡처에 실패한 사고가 있었는데, 그 경위는 docs/development-notes.md 13절 "기록 무결성"에 정리하였습니다.

확인 항목

결과

원인

조립 로직 복붙 (코드 확인)

재현

수정 전(ad8ce45) 상태에서 실측 재현

수정

_format_bullet_lines 헬퍼 통합

테스트

13케이스 추가, 28 → 41 passed

(부수 정정) 이 정리 과정에서 위 7절의 테스트 총계가 실제로는 이미 7/29에 28이었어야 하는데 25로 방치되어 있던 것을 확인하여, 41로 갱신하며 함께 정정하였습니다(test_connector.py import 정리 항목도 이미 완료 상태로 남아 있던 것을 development-notes.md에서 함께 정리하였습니다).


마지막 항목이 가장 뼈아팠습니다. 키를 재발급하기만 해서는 기존 키가 살아 있다는 점을 뒤늦게 알았고, 반드시 기존 키를 삭제해야 무효화된다는 것을 확인하였습니다.

각 사례의 판단 과정은 docs/development-notes.md 에 정리해 두었습니다.


10. 기술 스택

Python · MCP Python SDK (FastMCP) · requests · pytest nginx (리버스 프록시, HTTPS) · systemd (사용자 서비스)


11. 문서

문서

내용

docs/waple-worklog-mcp-상세설명.pdf

프로젝트 전체 과정 상세 설명 (인쇄용)

docs/integration-manual.md

설치·배포·연동 절차

docs/connector-design.md

근거 수집 설계

docs/development-notes.md

개발 기록, 오류 사례, 설계 편차와 근거

deploy/README.md

systemd 운영 가이드

docs/evidence/

웹 커넥터 검증에 사용한 서버 로그 발췌 (마스킹)


12. 남은 과제

  • 원격 모드에서는 Git 기록·토큰 사용량 자동 수집이 원리상 불가능한 구조적 한계 (현재는 차단 후 chat_tasklog 로 안내하며, 대체 수집 경로는 없음)


회사 도메인·서버 주소 등 인프라 정보는 [SERVER_URL] 형태의 플레이스홀더로 대체하였습니다. 서비스 포트는 nginx 설정과 배포 구조를 설명하는 데 필요하여 그대로 두었습니다. 도메인·주소가 가려져 있어 포트만으로는 접근 경로가 성립하지 않습니다.

F
license - not found
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • WHOOP recovery, strain, sleep and workouts in Claude via official WHOOP OAuth. Free, open source.

  • Persistent context for Claude. Your AI always knows your projects and next actions across sessions.

  • Connect Claude to Fathom meeting recordings, transcripts, and summaries

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/psy0635-ctrl/waple-worklog-mcp-portfolio'

If you have feedback or need assistance with the MCP directory API, please join our Discord server