portfolio-mcp
Integrates with GitHub to fetch repository metadata, pinned repositories, and contribution calendar data, enriching portfolio information.
Click on "Deploy 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-mcpShow me Karthikeyan's top skills and experience summary."
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
A production-ready Model Context Protocol server exposing Karthikeyan K's portfolio — profile, skills, experience, education, projects, certifications, and resume — as MCP tools, resources, and prompts, so ChatGPT, Claude, and any other MCP-compatible client can query it directly instead of browsing the website.
It also ships Gemini-powered AI tools for job matching: compare a job description against the profile, estimate an ATS score, generate interview questions, get a learning plan, and draft tailored summaries.
Built with the official @modelcontextprotocol/sdk,
TypeScript, and Zod, deployed on Cloudflare Workers.
Architecture
┌─────────────────────────┐
│ MCP Client │
│ (ChatGPT / Claude / │
│ MCP Inspector) │
└───────────┬─────────────┘
│ JSON-RPC over
│ Streamable HTTP (POST /mcp)
▼
┌───────────────────────────────────────────────────────┐
│ Cloudflare Worker (src/index.ts) │
│ routes: POST /mcp · GET /health · /version · /metrics │
└───────────────────────┬─────────────────────────────────┘
│ per-request
▼
┌───────────────────────────────────────────────────────┐
│ StatelessHttpTransport (src/transport/) │
│ one JSON-RPC message in → one response out │
└───────────────────────┬─────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────┐
│ McpServer (src/server.ts) │
│ ┌───────────┐ ┌────────────┐ ┌──────────────────┐ │
│ │ tools/ │ │ resources/ │ │ prompts/ │ │
│ │ 23 tools │ │ 8 resources│ │ 7 prompts │ │
│ └─────┬─────┘ └─────┬──────┘ └─────────┬──────────┘ │
│ └─────────────┴──────────────────┘ │
│ ▼ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ services/ │ │
│ │ data.ts (JSON → Zod) · gemini.ts · github.ts · │ │
│ │ search.ts (keyword + optional embeddings) · │ │
│ │ cache.ts (in-memory TTL) · metrics.ts │ │
│ └───────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────┘
▲ ▲
│ │
Gemini API (generateContent, GitHub REST + GraphQL
embedContent) — optional, (repo metadata always;
GEMINI_API_KEY pinned repos / contributions
need GITHUB_TOKEN)For local development, src/local.ts connects the same McpServer (from src/server.ts) over stdio
instead of HTTP — no Worker or network required.
Related MCP server: mcp-me
Folder structure
portfolio-mcp/
src/
index.ts # Cloudflare Worker fetch entry (HTTP routes)
local.ts # stdio entry point for local MCP clients
server.ts # createMcpServer(env) — registers everything
transport/
statelessHttpTransport.ts # MCP Transport adapter for one-shot HTTP
tools/ # one module per domain, one per tool group
portfolio.ts skills.ts experience.ts education.ts
projects.ts certifications.ts resume.ts ai.ts
resources/index.ts # 8 resources reading from data/*.json
prompts/index.ts # 7 reusable prompt templates
services/
data.ts # loads + Zod-validates data/*.json, builds resume markdown
gemini.ts # Gemini REST client (generate, embed)
github.ts # GitHub REST + GraphQL client (repo/pinned/contributions)
search.ts # keyword scoring + optional embedding blend
cache.ts # in-memory TTL cache
metrics.ts # per-isolate tool call counters
utils/
logger.ts errors.ts sanitize.ts pagination.ts config.ts
types/
env.ts portfolio.ts # Zod schemas + inferred types
data/ # the actual portfolio content (edit these to update)
profile.json contact.json skills.json experience.json
education.json projects.json certifications.json resume.md
tests/
services/ utils/ integration/
.github/workflows/deploy.yml
wrangler.jsonc package.json tsconfig.json eslint.config.js .prettierrcInstallation
Requires Node.js 22+.
npm install
cp .env.example .env # for reference; wrangler dev uses .dev.vars instead (see below)Development
Local stdio (Claude Desktop, MCP Inspector)
npm run dev:stdioThis runs src/local.ts directly with tsx, connecting the server over stdio. Point the
MCP Inspector at it:
npx @modelcontextprotocol/inspector npm run dev:stdioTo use it from Claude Desktop, add to claude_desktop_config.json:
{
"mcpServers": {
"portfolio": {
"command": "npx",
"args": ["tsx", "/absolute/path/to/portfolio-mcp/src/local.ts"],
"env": {
"GEMINI_API_KEY": "your-key-here",
"GITHUB_TOKEN": "optional-token-here"
}
}
}
}Local Worker (Cloudflare Workers runtime)
# put local secrets in .dev.vars (git-ignored), one KEY=value per line:
echo "GEMINI_API_KEY=your-key-here" >> .dev.vars
npm run devThen test the HTTP endpoint directly:
curl http://localhost:8787/health
curl -X POST http://localhost:8787/mcp \
-H "content-type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
curl -X POST http://localhost:8787/mcp \
-H "content-type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_profile","arguments":{}}}'Other commands
npm run typecheck # tsc --noEmit
npm run lint # eslint .
npm run lint:fix
npm run format # prettier --write .
npm test # vitest run
npm run test:watchDeployment (Cloudflare Workers)
Authenticate wrangler once locally:
npx wrangler login.Set secrets (never in
wrangler.jsonc):npx wrangler secret put GEMINI_API_KEY npx wrangler secret put GITHUB_TOKEN # optionalDeploy:
npm run deploy:dry-run # sanity check the build first npm run deploy
GitHub Actions (CI/CD)
.github/workflows/deploy.yml runs lint/typecheck/test on every push and PR to main, and deploys to
Cloudflare Workers on push to main. Add these repository secrets first:
CLOUDFLARE_API_TOKEN— a token with Workers Scripts:Edit permissionCLOUDFLARE_ACCOUNT_ID— your Cloudflare account ID
Environment variables
Variable | Required | Purpose |
| No | Enables all AI tools ( |
| No | Enables pinned repos + contribution calendar (GraphQL), and raises the REST rate limit for live repo metadata on |
| No | Defaults to |
Tools
Tool | Description |
| Full profile: name, role, company, location, tagline, bio, focus areas |
| About-me narrative |
| Email, phone, location |
| GitHub, LinkedIn, Instagram, X, Hugging Face, Linktree |
| Skills grouped by category, optional category filter |
| Keyword/semantic search across all skills |
| Full work experience timeline |
| Natural-language summary of experience (AI or templated) |
| Education timeline |
| Projects with filter (cluster/status/featured), sort, pagination |
| One project by id, optionally enriched with live GitHub metadata |
| Keyword/semantic search across projects |
| Best-matching projects for a query or skill list |
| Most recent projects |
| 45 certifications/courses/badges/publications/achievements, filter + pagination |
| Keyword/semantic search across certifications |
| Resume as markdown + PDF link |
| AI-tailored resume summary paragraph |
| Fit score, strengths, gaps, best-matching projects vs. a job description |
| ATS keyword-match score, matched/missing keywords, suggestions |
| Likely interview questions grounded in real projects/experience |
| Skills to learn + certifications to pursue |
| Natural-language portfolio overview tailored to an audience |
All AI tools (compare_job, ats_match, interview_questions, recommend_learning, portfolio_summary,
plus the AI paths of experience_summary/generate_resume_summary) require GEMINI_API_KEY. Every tool
validates input with Zod and returns a proper MCP error result (never an uncaught exception) on bad input,
missing config, or unexpected failures.
Resources
portfolio://resume.md · portfolio://profile.json · portfolio://skills.json ·
portfolio://experience.json · portfolio://education.json · portfolio://projects.json ·
portfolio://certifications.json · portfolio://contact.json
Prompts
professional_bio · linkedin_summary · resume_summary · interview_introduction ·
project_explanation · cover_letter · portfolio_overview
Connecting from ChatGPT
Deploy the Worker (see above) so you have a public URL, e.g.
https://portfolio-mcp.<you>.workers.dev.In ChatGPT, open Settings → Connectors → Advanced → Developer mode (requires a ChatGPT plan that supports custom connectors) and add a new connector pointing at:
https://portfolio-mcp.<you>.workers.dev/mcpEnable the connector in a chat and ask things like "What are Karthikeyan's featured projects?" or "Compare this job description against Karthikeyan's profile: ...".
Example tool responses
get_profile:
{
"name": "Karthikeyan K",
"headline": "AI Engineer",
"currentRole": "Associate Data Analyst",
"currentCompany": "Zinnov",
"tagline": "Building intelligent systems that act — not just answer."
}ats_match (with GEMINI_API_KEY set):
{
"atsScore": 78,
"matchedKeywords": ["LangChain", "RAG", "Python", "Vector Databases"],
"missingKeywords": ["Kubernetes", "Terraform"],
"suggestions": ["Add measurable infra/deployment experience if applicable."]
}Adding a new tool
Add (or extend) a module under
src/tools/, exporting aregister*Tools(server, env)function.Call
server.registerTool(name, { title, description, inputSchema }, safeTool(name, handler))—inputSchemais a Zod raw shape (object of Zod validators, notz.object(...)), andsafeTool(fromsrc/utils/errors.ts) converts thrown errors/Zod failures into proper MCP error results automatically.Register the module in
src/server.ts'screateMcpServer.Add a test under
tests/(unit test the underlying logic, or extendtests/integration/server.test.tsfor a full round-trip check).
Adding a new resource
Add an entry to the RESOURCES array in src/resources/index.ts with a unique name/uri, a
getContent() function, and register it — registerResources handles the rest.
Troubleshooting
AI tools return "requires GEMINI_API_KEY": expected without a key configured — set it via
.dev.vars(local) orwrangler secret put GEMINI_API_KEY(deployed).get_project'sliveMetadatais alwaysnull: GitHub REST is unauthenticated by default and rate-limited; setGITHUB_TOKENto raise the limit. Pinned repos / contribution summary specifically requireGITHUB_TOKEN(GraphQL) — expected to benullwithout it.CORS errors from a browser-based MCP client:
src/index.tsalready sends permissive CORS headers on every response includingOPTIONS; check the client is hitting/mcpwithPOST, notGET./metricscounters reset unexpectedly: they're per-isolate, in-memory only — a Cloudflare Workers cold start resets them. This is a documented limitation, not a bug.wrangler devcan't find secrets: local secrets go in a git-ignored.dev.varsfile (KEY=valueper line), not.env—.envis only for the stdio dev entry (npm run dev:stdio).
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Public portfolio MCP for resume, services, availability, project evidence, and introductions.
Generate tailored, ATS-optimized resume PDFs and cover letters from a job description, over MCP.
CareerProof MCP gives AI agents direct access to a professional-grade career and workforce intelligence platform. Two namespaces: atlas_* for HR/TA teams (candidate evaluation, batch shortlisting, competency scoring, interview generation, JD analysis, custom eval frameworks, research reports) and ceevee_* for professionals (CV optimization, career positioning, salary intelligence, market reports). Backed by RAG knowledge from 50+ premium research sources (McKinsey, BCG, HBR, Gartner, WEF)
MCP for 8,700+ current AI jobs. 13 tools: search, match, salaries, companies, commerce quotes.
Related MCP Servers
- AlicenseAqualityAmaintenanceExposes Aniket Charjan's resume as a queryable API for AI assistants, implementing MCP tools, resources, and prompts for detailed resume queries.1178 npmMIT
- AlicenseNot gradedqualityCmaintenanceTransforms professional data (CV, projects) into MCP tools for LLMs to query, list, match job descriptions, and ask about experience.52 npmMIT
- AlicenseAqualityBmaintenanceExposes a personal portfolio of projects, skills, and resume as callable tools for MCP-compatible AI assistants like Claude Desktop.4MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI assistants to discover and retrieve professional portfolio information including profile, experience, skills, projects, and contact details through MCP tools.-