portfolio-mcp
Integrates with GitHub to fetch repository metadata, pinned repositories, and contribution calendar data, enriching portfolio information.
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-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: my-mcp-server
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 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.Last updatedMIT
- AlicenseAqualityCmaintenanceExposes Aniket Charjan's resume as a queryable API for AI assistants, implementing MCP tools, resources, and prompts for detailed resume queries.Last updated11105MIT
- 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.Last updated1MIT
- Alicense-qualityDmaintenanceTransforms professional data (CV, projects) into MCP tools for LLMs to query, list, match job descriptions, and ask about experience.Last updated64MIT
Related MCP Connectors
Job search and interview prep MCP. 11 tools, OAuth 2.1, cross-LLM. four-leaf.ai.
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.
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/Karthikeyan260/Portfolio-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server