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.
검색은 관련 조각을 찾는 입구, 리소스는 문서 전문을 읽는 경로.
검색 결과에 그 조각이 나온 절 제목(section)과 문서 전문 주소(resource)가 함께 실려 오므로, 조각만으로 부족하면 바로 리소스를 열면 됨.
리소스도 검색 인덱스와 같은 정제본을 내보내므로 두 경로의 내용이 항상 일치함.
MCP 프롬프트도 2개 제공함.
candidate_briefing은 채용 담당자용 브리핑을, tech_deep_dive는 주제별 기술 딥다이브를 시킴.
둘 다 어떤 도구를 어떤 순서로 쓸지 안내하는 템플릿이라, 서버가 자기 도구의 사용법을 함께 배포하는 셈.
이로써 MCP 3대 프리미티브인 도구, 리소스, 프롬프트를 모두 구현함.
두 가지가 더 있음.
연결(initialize) 시 instructions로 도구 사용 순서 요약이 클라이언트 LLM에 자동 전달됨.
프롬프트 인자(topic·focus)를 입력하는 중에는 자동완성이 내려가는데, 제안 목록을 profile.json의 프로젝트명·기술 스택과 문서 제목·본문 용어에서 뽑으므로 데이터가 바뀌면 자동완성도 따라옴.
전부 read-only.
Related MCP server: MyMem
설계하면서 정한 것들
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 토크나이저와 청킹 교정이 들어간 지금은 절대값이 다름. 외부 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로 재현됨(현재는 질의 14문항에 top1 14/14. 개선 8·9 반영).
처음엔 임시 스크립트로만 돌렸는데, 그 사이 README의 청크 수가 실제 코드와 어긋남.
재현할 수 없는 수치는 주장에 불과하므로 하네스를 저장소에 넣고 CI에서 돌림.
이 단계에서 남은 1건은 TTFB 최적화였음.
1위가 TTS 아키텍처 다이어그램 청크로, 정작 TTFB를 서술한 청크가 2위로 밀렸음.
한글 런은 bigram까지 더해 토큰 3개('최적화', '최적', '적화')가 되는데 영문 약어는 1개('ttfb')라, 섞인 질의에서 한글 쪽 가중치가 커지는 구조적 트레이드오프.
조사 대응(개선 3)을 깰 위험이 있어 토크나이저는 두고, 개선 8에서 다른 방향으로 해결함.
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은 재측정값으로 갱신됐는데 resume.md가 낡은 채로 남아 있었던 것.
동시 처리 수치도 24채널 대 36스트림으로 어긋남.
채용 담당자가 어느 도구를 먼저 부르냐에 따라 다른 답을 듣는 상태였음.
게다가 그 1.7 rps는 성과가 아니라, 포트폴리오 저장소가 벤치마크로 반증한 사내 '천장' 결론이었음.
반증된 수치가 이력서에 성과로 적혀 있었던 셈.
포트폴리오 저장소의 실측 기록을 기준으로 resume.md를 맞추고, 두 소스가 같은 값을 말하는지 검사하는 테스트를 추가함.
문서와 구조화 데이터를 나눠 두는 설계에는 이런 동기화 테스트가 따라와야 함.
8. 조각이 어느 절에서 나왔는지 알려주지 않았음.
청킹하고 나면 청크의 68%(56개 중 38개)에 제목 줄이 없음.
그래서 도구는 파일명만 붙은 조각을 돌려주고, 클라이언트는 그게 어느 프로젝트 이야기인지 모른 채 읽음.
profile.json에서 서로 다른 TTS 프로젝트 둘이 한 항목으로 섞여 있던 적이 있어서(개선 7의 이웃 문제), 출처를 흐리는 건 실제로 위험함.
각 청크에 소속 절 제목을 달았음. 청크 안에 제목이 있으면 그 첫 제목이 소속이고, 없으면 앞 청크에서 물려받음. 56개 전부에 제목이 붙음.
여기서 부수 효과가 하나 나옴. 제목을 BM25 색인에도 넣으면 제목의 단어가 그 절 전체에 걸려서, 본문이 제목을 다시 말하지 않는 조각도 찾힘.
top1 정답 | top4 정답 | 무의미 질의 거부 | |
제목 색인 안 함 | 11/12 | 12/12 | 6/6 |
제목 색인 포함 | 12/12 | 12/12 | 6/6 |
개선 4에서 트레이드오프로 두고 넘어갔던 TTFB 최적화가 이 변경으로 1위를 되찾음.
토크나이저를 건드리지 않았으므로 조사 대응도 그대로.
함께 고친 것으로, 검색 결과에 resource 주소를 실어 보냄.
전에는 instructions와 프롬프트가 "…(이하 생략)으로 잘려 있으면 리소스를 열어라"로 안내했는데, 청킹 교정 이후 최대 청크가 797자라 800자 한계에 걸리는 청크가 하나도 없었음.
즉 잘림 표식이 한 번도 나타나지 않아 리소스로 가는 길이 사실상 닫혀 있었음.
개선 6의 죽은 힌트와 같은 계열이라, 조건을 없애고 결과에 주소를 직접 넣는 쪽으로 바꿈.
9. 자연스럽게 물을수록 검색이 안 됐음.
vLLM 써봤어요?가 0건이었음. vLLM은 문서에 가득한데도.
원인은 개선 6에서 넣은 문턱의 계산 방식.
문턱을 질의 토큰 '전체' 수에 비례해 잡는데, 한국어 어미가 bigram으로 토큰 수를 부풀림(써봤어요 → 써봤·봤어·어요).
이 조각들은 코퍼스에 없어 점수는 0을 보태면서 문턱만 올림.
질문이 자연스러울수록 통과 기준이 높아지는 구조였음.
문턱을 '코퍼스에 있는 토큰 수'로만 계산해 봤더니 자연어 질의는 살아났지만 무의미 거부가 6/6에서 3/6으로 무너짐. 점수 축에서는 두 집합이 겹쳐서 임계값을 어디에 둬도 한쪽이 깨짐.
질의 | 최고점 / 토큰수 | |
| 1.1 / 5 = 0.23 | 정상 |
| 5.3 / 14 = 0.38 | 무의미 |
| 5.1 / 11 = 0.47 | 무의미 |
| 10.6 / 11 = 0.97 | 정상 |
갈리는 축은 점수가 아니라 어휘였음. 무의미 질의는 온전한 단어(bigram으로 만든 조각 말고)가 코퍼스에 하나도 없고(0/2~0/4), 정상 질의는 자연어라도 반드시 하나는 있음. 그래서 질의에 코퍼스 단어가 있으면 점수 문턱을 걷음.
관문만 쓰면 쿠버네티스로 뭐 했어가 탈락함. 조사가 붙어 온전한 단어가 코퍼스에 없기 때문.
그래서 둘의 합집합으로 둠. 점수 문턱을 넘거나, 코퍼스 단어를 담고 있으면 통과.
평가 질의에 자연어 2문항을 추가해 12문항에서 14문항이 됐고, top1 14/14·무의미 거부 6/6. 관문을 되돌리면 평가가 exit 1로 떨어지는 것까지 확인함.
실행
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 Connectors
Read-only MCP tools for AI agent discovery, structured resources, and NIULAI information.
Your memory, everywhere AI goes. Build knowledge once, access it via MCP anywhere.
Knowledge base MCP for AI agents on iknow.dev. Search, read, and maintain via OAuth.
Zero-setup MCP gateway securely connecting AI to your tools with authentication and workflows
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceAggregates 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
- AlicenseNot gradedqualityBmaintenanceExposes a private social memory archive to AI assistants via MCP, enabling read-only queries about the user's posts, people, and preferences with tools like search, whoami, and timeline.MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI assistants to query a person's CV and portfolio content via MCP tools and resources, returning grounded answers from local markdown data instead of relying on resume parsing.
- FlicenseNot gradedqualityBmaintenanceEnables AI agents to retrieve a personal biography, skills, projects, and contact links through a read-only MCP server.
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