portfolio-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@portfolio-mcphow was TTFB optimization achieved?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
portfolio-mcp
내 포트폴리오를 MCP(Model Context Protocol) 서버로 만들었다. Claude Desktop이나 Claude Code에 붙이면 AI가 내 경력, 프로젝트, 논문을 도구로 직접 조회한다. "이 사람 TTFB 최적화 어떻게 했어?" 같은 질문이 가능해진다.
데모는 demo_session.py로 실행한 실제 세션 출력을 그대로 옮긴 것이다.
구성
MCP Client (Claude 등)
│ stdio
▼
portfolio_mcp ── BM25 검색 ─── data/docs/*.md 기술문서 5편
├─────────── 구조화 조회 ── data/profile.json 경력 사실
├─────────── 리소스 노출 ── portfolio://… 문서 전문 읽기
├─────────── 프롬프트 2개 ─ 브리핑·딥다이브 도구 사용 안내 템플릿
└─────────── 실시간 조회 ── GitHub API·RSS 오늘 자 활동 데이터도구 | 하는 일 |
| 경력 회사·기간·직급, 학력, 기술 스택, 링크 |
| 프로젝트 17개 목록. 회사명 필터 지원(구 사명도 인식) |
| 논문 7편(제1저자), 특허 2건(제1발명자), 수상 |
| 기술문서 BM25 검색. 트러블슈팅 과정 같은 세부 내용용 |
| GitHub 공개 저장소 실시간 조회 (최근 푸시 순 10개) |
| 기술 블로그 최신 글 RSS 실시간 조회 (5건) |
| 재직 회사의 기간·직급·검증된 공식 홈페이지 |
도구 7개는 텍스트 JSON과 함께 structuredContent도 내려주고, 반환 타입에서 생성한 outputSchema를 클라이언트에 공개한다 — 클라이언트가 파싱 없이 스키마가 보장된 결과를 바로 쓸 수 있다.
도구 외에 MCP 리소스도 6개 노출한다 — 기술문서 전문
portfolio://docs/<파일명> 5개와 구조화 프로필 portfolio://profile.
역할 분담은 이렇다: 검색은 관련 조각을 찾는 입구고, 리소스는 문서 전문을
읽는 경로다. 검색 결과가 길어서 …(이하 생략)으로 잘려 있으면 클라이언트가
해당 문서 리소스를 열어 이어 읽으면 된다. 리소스도 검색 인덱스와 같은
정제본을 내보내므로 두 경로의 내용이 항상 일치한다.
MCP 프롬프트도 2개 제공한다 — candidate_briefing(채용 담당자용 브리핑,
focus 인자로 영역 지정)과 tech_deep_dive(주제별 기술 딥다이브, 재검색과
리소스 이어읽기 절차 포함). 프롬프트는 어떤 도구를 어떤 순서로 쓸지 안내하는
템플릿이라, 서버가 자기 도구의 올바른 사용법을 함께 배포하는 셈이다. 이로써
MCP 3대 프리미티브(도구·리소스·프롬프트)를 모두 구현한다.
여기에 두 가지가 더 붙는다. 연결(initialize) 시 instructions로 도구 사용 순서 요약이 클라이언트 LLM에 자동 전달되고, 프롬프트 인자(topic·focus) 입력 중에는 **자동완성(completions)**이 내려간다 — 제안 목록을 profile.json의 프로젝트명·기술 스택에서 뽑으므로 데이터가 바뀌면 자동완성도 따라온다.
전부 read-only다.
Related MCP server: whoami-mcp
설계하면서 정한 것들
mcpSDK 1.x와 2.x 양쪽에서 동작한다. 2.0이FastMCP를MCPServer로 개명하고 결과 필드를 snake_case로 바꿨는데, import 폴백과 필드 접근 헬퍼로 흡수했다 — CI는 항상 최신을 설치하므로 2.0 출시 당일 빨간불이 됐던 것을 이렇게 고쳤다. 이후 CI를 매트릭스로 바꿔 커밋마다 두 메이저를 모두 돌린다. 호환은 주장이 아니라 매 커밋 검증되는 사실이어야 한다.의존성은
mcpSDK와rank_bm25둘뿐이다. 처음엔 임베딩 검색도 고려했는데 문서 5편에 청크 수십 개 규모에서 벡터 검색은 과하다. BM25면 충분하고, 덕분에 GPU도 외부 API도 없이 clone 후 바로 돈다.추론은 클라이언트 LLM의 몫이다. 서버는 데이터만 정확하게 내려주면 된다.
확정된 사실(경력, 논문, 수치)은
profile.json으로, 서술형 내용은 문서 검색으로 분리했다. 숫자가 검색 랭킹에 따라 흔들리면 안 되기 때문이다.검색 결과가 비면 "다른 키워드로 재검색하거나 목록부터 보라"는 힌트를 응답에 같이 넣는다. 에러 메시지가 다음 행동을 알려줘야 agent가 헤매지 않는다.
실시간 도구(GitHub·블로그)는 표준 라이브러리
urllib만 쓴다 — 의존성은 여전히 2개다. "요즘도 활동하나?"는 정적 파일이 답할 수 없는 질문이라 웹 조회를 넣었지만, 일반 웹 검색은 넣지 않았다. 클라이언트 LLM이 이미 갖고 있고, 이 서버의 역할은 이윤선 데이터를 정확하게 내려주는 것까지다. 조회 실패 시 에러 대신 "정적 데이터로 답하라"는 힌트를 반환하므로 오프라인 CI에서도 깨지지 않고, TTL 10분 캐시로 GitHub 무인증 rate limit을 지킨다. 회사 홈페이지 URL은 공식 사이트를 직접 확인한 것만 넣었다 — 큐헷지는 확인하지 못해 비워 뒀다(추측으로 채우지 않는다).
뒤늦게 고친 것 일곱 가지
1. 원문 정제를 아예 안 하고 있었다. data/docs/*.md는 노션에서 내보낸
그대로였고, 서버는 read_text()로 읽어 바로 청킹하고 있었다. 문제는 노션이
내부 페이지 링크를 퍼센트 인코딩된 한글 파일명으로 내보낸다는 것이다
([Experience](%EC%9D%B4%EC%9C%A4%EC%84%A0%20...)). BM25 토크나이저는 이걸
ec, d, b, 9 같은 쓰레기 토큰으로 쪼갠다. 토큰이 늘면 BM25의 문서 길이
정규화가 해당 청크에 페널티를 줘서 순위가 실제로 나빠지고, 도구가 돌려주는
본문에도 그대로 섞여 클라이언트 LLM의 컨텍스트를 낭비한다.
정제를 넣으니 BM25 토큰 7,653개 → 6,310개(18% 감소), 청크 32 → 26개. 줄어든 1,343개는 전부 순위를 왜곡하던 쓰레기다. (이 수치는 당시 토크나이저·청커 기준이다. 이후 bigram 토크나이저와 청킹 교정이 들어가 절대값이 달라졌으니, 지금 코드로 다시 재면 토큰 12,773 → 11,942(7% 감소)·청크 64 → 55다.) 외부 http(s) URL은 "깃허브 주소" 같은 질문에 답해야 하므로 그대로 남긴다. (같은 문제를 portfolio-rag-agent에서 검수 스크립트로 찾았고, 규칙을 이쪽에도 옮겼다. 이 서버는 의존성 2개로 단독 실행되는 게 목표라 공용 모듈로 빼지 않고 복제했다.)
2. 스모크 테스트가 실패할 수 없는 테스트였다. test_client.py는 응답
텍스트가 비어 있지 않은지만 봤다. 그래서 도구가 "결과 없음 + 힌트"를 돌려줘도
[OK]로 통과했다 — 실제로 portfolio_list_projects(company="에이아이세스")가
빈 배열을 반환하고 있었는데 테스트는 계속 초록색이었다.
원인은 표기 불일치였다. career에는 MiCo AI (구 에이아이세스)로, projects에는
MiCo AI로 적혀 있어서 구 사명으로 물으면 아무것도 안 나왔다. 현 직장인데도.
career를 별칭 사전처럼 써서 두 표기를 잇고, 테스트는 응답이 왔는지가 아니라
내용이 맞는지(프로젝트가 1개 이상인지, 특허가 2건인지)를 검사하도록 바꿨다.
3. 조사가 붙은 질의에서 검색이 죽어 있었다. 토크나이저가 단어 단위라 '쿠버네티스로'와 '쿠버네티스'가 서로 다른 토큰이었다. 실측하면:
질의 | 이전 | 이후 |
| 0건 | portfolio.md (14.16) |
| 0건 | resume.md (4.28) |
| tts-deepdive.md 1위 | tts-deepdive.md 1위 (유지) |
한국어는 교착어라 실제 질문에는 거의 항상 조사가 붙는데, 정확히 그 형태에서
0건이 나오고 있었다. 한글 런(run)에 문자 bigram을 추가해 해결했다 —
'쿠버네티스로'와 '쿠버네티스'가 bigram(쿠버, 버네, …)으로 겹치게 된다.
같은 문제를 portfolio-rag-agent에서
검색 평가로 먼저 찾았고(recall@1 80%), 그쪽의 bm25_tokenize 규칙을 이식했다.
기존에 잘 되던 질의의 1위 문서는 바뀌지 않는 것을 확인했다.
4. 청킹이 사실상 동작하지 않고 있었다. CHUNK_SIZE = 800으로 설정해
뒀는데, 실제 최대 청크는 16,618자였다. 청커가 빈 줄(\n\n)로만 자르는데
노션은 중첩 리스트를 들여쓰기 + 단일 개행으로 내보낸다. 그래서
portfolio.md의 본문 대부분이 '단락 하나'로 붙어 통째로 청크 1개가 됐다.
결과는 두 겹으로 나빴다. BM25는 문서 길이로 점수를 정규화하므로 그 거대 청크는 어떤 질의에도 상위로 못 올라오고, 어쩌다 올라와도 도구는 앞부분만 잘라서 돌려준다 — 25,404자가 검색으로 도달 불가능한 상태였다.
거대 단락을 줄 단위로 다시 쪼개고, 아스키 다이어그램의 박스 그리기 문자를 정제 대상에 추가했다. 박스 문자는 노션 인코딩 노이즈와 같은 계열인데 방향이 반대다 — 토큰화되지 않으므로 글자 수만 부풀리고 토큰 수는 그대로여서, 다이어그램 청크가 '아주 짧은 문서'로 취급돼 길이 정규화에서 부당하게 유리해진다(실측: 732자에 토큰 42개, 같은 길이 산문은 토큰 171개).
문서에 실제로 있는 사실 12개로 질의를 만들어, 도구가 반환하는 본문(잘림 포함)에 정답 키워드가 들어 있는지로 채점했다.
청크 수 | 최대 청크 | top1 정답 | top4 정답 | |
변경 전 | 26개 | 14,664자 | 6/12 | 8/12 |
변경 후 | 56개 | 797자 | 11/12 | 12/12 |
이 표는 python eval_search.py로 재현된다. 처음엔 이 평가를 임시 스크립트로만
돌렸는데, 그 사이 README의 청크 수가 실제 코드와 어긋났다 — 재현할 수 없는
수치는 그냥 주장이 된다. 그래서 하네스를 저장소에 넣고 CI에서 돌린다.
남은 1건은 TTFB 최적화다. 1위가 TTS 아키텍처 다이어그램 청크로, 정작
TTFB를 서술한 청크는 2위로 밀린다. 원인은 토크나이저의 구조적 트레이드오프다 —
한글 런은 bigram까지 더해 토큰 3개('최적화', '최적', '적화')가 되는데 영문
약어는 1개('ttfb')라, 섞인 질의에서 한글 쪽 가중치가 커진다. 정답은 top4
안에 있고, 이걸 건드리면 조사 대응(개선 3)이 깨질 위험이 커서 두었다.
5. 회사 영문 사명이 오타였다. 인피닉 (INFINIIC)로 적혀 있었는데 공식
사명은 INFINIQ다(회사 홈페이지 저작권 표기로 확인). profile.json과
resume.md 양쪽을 고쳤다. 채용 담당자가 보는 데이터라 사명 오타는
수치 오류만큼 나쁘다.
6. "못 찾았다" 힌트가 사실상 죽어 있었다. 설계 원칙으로 "검색 결과가 비면
다음 행동을 알려준다"를 내걸었는데, bigram 토크나이저를 넣은 뒤로는 결과가
비는 일이 거의 없었다. 아무 한국어 질의나 bigram이 조금씩 걸려서,
양자컴퓨팅 큐비트 결맞음 같은 무관한 질문에도 점수 5점대 청크가 나왔다.
힌트 대신 쓸모없는 본문이 클라이언트 컨텍스트를 채우고 있었던 셈이다.
처음엔 절대 점수 임계값을 두려 했는데 실측이 그 방법을 반박했다. 정상
단일어 질의가 오히려 더 낮게 나온다 — MetalLB 4.6, OCR 4.4, Redis 1.8인데
무의미 질의가 5.5다. BM25 점수는 질의 토큰 수에 비례해 커지기 때문이다.
질의 토큰 수로 나누면 순서가 뒤집힌다.
정규화 점수(1위 점수 ÷ 질의 토큰 수) | |
무의미 질의 6개 | 0.00 ~ 0.55 |
정상 질의 24개 | 1.82 ~ 4.87 |
양쪽에 여유를 두고 MIN_SCORE_PER_TOKEN = 1.0으로 잘랐다. 무의미 질의는
이제 힌트를 받고, 정상 질의의 검색 품질은 그대로다(top1 11/12·top4 12/12 유지).
7. 같은 수치를 도구마다 다르게 답하고 있었다. portfolio_list_projects는
Throughput을 3.09 rps로, portfolio_search는 같은 지표를 ~1.7 rps로 돌려줬다.
profile.json은 2026-08-04 재측정값으로 갱신됐는데 resume.md가 낡은 채로
남아 있었던 것이다. 동시 처리 수치도 24채널 대 36스트림으로 어긋났다.
채용 담당자가 어느 도구를 먼저 부르냐에 따라 다른 답을 듣는 상태였다.
포트폴리오 저장소의 실측 기록을 기준으로 resume.md를 맞췄고, 두 소스가
같은 값을 말하는지 검사하는 테스트를 넣었다. 문서와 구조화 데이터를 나눠
두는 설계에는 이런 동기화 테스트가 따라와야 한다.
실행
python -m venv .venv && .venv\Scripts\activate
pip install -r requirements.txt
python test_client.py # 서버 기동 + 도구·리소스·프롬프트 전부 실제 호출하는 스모크 테스트
python eval_search.py # 검색 품질 평가 — 정답 포함률과 무의미 질의 거부율Claude에 연결
Claude Code:
claude mcp add portfolio /path/to/.venv/Scripts/python.exe /path/to/portfolio-mcp/server.pyClaude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"portfolio": {
"command": "C:/path/to/portfolio-mcp/.venv/Scripts/python.exe",
"args": ["C:/path/to/portfolio-mcp/server.py"]
}
}
}연결하고 이런 걸 물어보면 된다.
이윤선의 TTS 프로젝트에서 스트리밍 팝 노이즈를 어떻게 해결했는지 찾아줘
인피닉에서 한 프로젝트 목록 보여줘
특허 등록번호 알려줘
Python / MCP SDK (FastMCP) / rank_bm25 / stdio
This server cannot be installed
Maintenance
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
- Alicense-qualityBmaintenanceExposes a public, read-only professional profile with tools to search resume evidence, fetch curated links, and generate career briefs for LLM agents.MIT
- Alicense-qualityCmaintenanceExposes a person's structured professional profile as MCP tools, enabling Claude and other MCP clients to answer questions about that person based on real data.111MIT
- Alicense-qualityAmaintenanceAggregates your digital footprint (GitHub, blogs, resume) into a single AI-readable profile and exposes it via MCP tools so AI agents can query your context live.1MIT
- Alicense-qualityDmaintenanceEnables AI tools to maintain a personal knowledge wiki via MCP, allowing users to add sources and ask questions grounded in their research.6MIT
Related MCP Connectors
Your memory, everywhere AI goes. Build knowledge once, access it via MCP anywhere.
The personal context layer for AI: your profile and files, read by any MCP client over OAuth.
Real-time Amazon, WIPO & PACER data for AI agents — 19 tools via the MCP protocol.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/ckc5800/portfolio-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server