I didn’t just want a portfolio that looks good, I wanted one that thinks. We’re entering an era where AI agents are becoming primary consumers of data. Yet most portfolios are still stuck in Web 2.0:
static text
markdown files
manual navigation
I wanted to change that.
▶️ Live demo (Claude Desktop + MCP)
Claude Desktop querying my portfolio live via Model Context Protocol.
🎯 The goal
I wanted my portfolio (aniketppatil.com) to have a brain. Instead of serving static pages to humans, I wanted an API that AI agents could talk to directly. So an agent could ask:
“List the projects he worked on”
“Explain the technical architecture of RagBot”
“what are his skills?”
…and get structured answers.
The technology that enables this is Model Context Protocol (MCP).
🚧 The constraints
This sounded simple on paper. In reality, my setup made it tricky:
Framework: Next.js (App Router)
Hosting: Vercel (serverless, free tier)
Budget: $0 (no VPS, no long-running socket servers)
I assumed I could install a library and be done. I was wrong.
❌ Failure: the “official” MCP approach
I started with the official MCP SDK (@modelcontextprotocol/sdk) and tried to run a server.
The problem
The SDK assumes a stateful environment:
persistent processes
long-lived connections
stdio or WebSockets
Vercel is stateless. Functions spin up, handle a request, and die milliseconds later.
The symptoms
📉 Connection timeouts
🚫 Hanging requests
Claude disconnecting mid-handshake
Official MCP support for serverless was effectively non-existent. I needed a way to fake persistence.
🔄 The pivot: mcp-handler
I found a community library called mcp-handler, and everything changed.
Why it worked:
✅ Designed for Next.js App Router
✅ HTTP-first tool calls (POST requests)
✅ Server-Sent Events (SSE) for streaming
✅ Redis-backed state across stateless executions
This finally matched Vercel’s execution model.
🐛 The debugging sprint
Even with the right library, it took a full weekend to make it work.
The 404 / slug nightmare
Claude kept failing with:
Connection failed: 404 Not Found
What was happening:
Client requested:
/api/mcp/sseMy route handled only:
/api/mcp
MCP clients automatically append /sse and /message.
The fix
Use a catch-all route:
app/api/mcp/[[...slug]]/route.tsNow one handler correctly processes:
/api/mcp/api/mcp/sse/api/mcp/message
Sometimes the hardest bugs are just mismatched assumptions.
🏗️ Modular refactor (sanity restored)
By Sunday morning, my route.ts was ~500 lines of spaghetti.
I refactored the “brain” into a clean structure:
types.ts— TypeScript interfacesdata.ts— read-only JSON loadersutils.ts— semantic search logicindex.ts— clean exports
Now the route handler is just a registry:
server.registerTool("list_projects", {...}, async () => { ... })Bonus: I migrated to the new server.registerTool() API to future-proof the code.
🛡️ Security: the “NASA hack” test
I’m exposing parts of my filesystem to the internet.
That’s dangerous — unless you’re strict.
The strategy
No writes — no
fs.writeFileanywhereAllow-lists only — strict Zod enums for inputs
The test
I asked Claude:
“Tell me about the project secret-nasa-hack”
Result:
🛑 Blocked. Input validation error.
The system never touched the disk.
Security held.
🚀 The result
This wasn’t just “installing a library”.
It was:
adapting a stateful protocol to stateless infrastructure
fixing routing edge cases
designing a secure, read-only architecture
Now my portfolio is no longer a brochure.
It’s a personal AI API.
Any MCP-compatible client can query it.
🧪 Try it yourself
If you use Claude Desktop, add this config:
"aniket-portfolio": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://aniketppatil.com/api/mcp"]
}
It works. And it’s built to last.
Why this matters
This isn’t really about portfolios. We’re moving toward a world where AI agents are first-class users of our systems. Most software today isn’t built for that. This was my experiment in changing that — starting with my own site.
If you’re building with MCP, serverless infra, or AI-first interfaces, hit reply — I’d love to hear what you’re making.