I didn’t just want a portfolio that looks good - I wanted one that thinks. We live in an era where AI agents are becoming the primary consumers of our data. Yet, most portfolios are stuck in Web 2.0: static text, markdown files, and manual navigation.
Here’s a live demo of Claude Desktop querying my site via MCP.
I wanted to change that. I wanted an AI Agent (specifically Claude Desktop) to be able to "plug in" to my site and query my professional data directly — projects, blogs, resume, everything — without me writing a 5-page prompt every time. The technology that enables this is Model Context Protocol (MCP).
🎯 The Goal
I wanted my portfolio (aniketppatil.com) to have a brain. Instead of just serving static pages to humans, I wanted an API that AI agents could talk to. I wanted them to ask structured questions like:
"List his open source projects"
"Tell me the technical architecture of RagBot"
"Why should I hire him?"
And I wanted my server to answer.
🚧 The Constraints
This sounded simple on paper. But my infrastructure made it tricky:
Framework: Next.js (App Router)
Hosting: Vercel (Serverless / Free Tier)
Budget: $0 (I didn’t want a separate VPS just to run a socket server)
I thought I could just install a library and be done. I was wrong.
❌ Failure 1: The "Official" Way vs. Serverless Reality
I started by following the official MCP documentation. I installed the standard SDK (@modelcontextprotocol/sdk) and tried to spin up a server.
The Problem
The official SDK is designed for stateful environments. It assumes your server stays alive forever, maintaining a persistent connection (via stdio or WebSockets). Vercel is stateless. Functions spin up, handle a request, and die milliseconds later.
The Symptom
📉 Connection timeouts.
🚫 "Hanging" requests.
Claude would try to connect, but Vercel would kill the process before the handshake could finish.
I realized: Official MCP support for serverless was basically non-existent. I needed a way to "fake" persistence.
🔄 The Pivot: Enter mcp-handler
I found a community library called mcp-handler (v1.0.5) that changed everything.
npm install mcp-handler @modelcontextprotocol/sdk zodWhy This Worked
✅ Designed for Next.js: Built specifically for App Router.
✅ HTTP-First: It uses standard POST requests for tool calls.
✅ Server-Sent Events (SSE): Uses SSE for the "push" messages (like logs).
✅ Redis Backed: It uses Redis to store the connection state across those stateless function calls.
This matched Vercel’s execution model perfectly.
🐛 The Debugging Sprint
Even with the right library, getting it to actually work took a weekend of debugging.
Issue 1: The 404 / Slug Nightmare
Claude Desktop kept failing with: Connection failed: 404 Not Found.
The Investigation: I looked at the logs.
Client Request:
GET https://aniketppatil.com/api/mcp/sseMy Route: Handles
/api/mcpONLY.
The MCP client automatically appends /sse and /message to your URL. My route wasn't catching those.
The Fix: Rename the route to a "Catch-All":
app/api/mcp/[[...slug]]/route.ts
Now, one file handles everything: /api/mcp, /api/mcp/sse, and /api/mcp/message.
🏗️ The Modular Refactor
By Sunday morning, my route.tsfile was500 lines of spaghetti code. It had Zod schemas, file reading logic, HTML parsing regex, and server config all mixed together. I hated it. So I refactored. I split the "brain" into a clean lib/mcp/ architecture:
📄
types.ts: Pure TypeScript interfaces.💾
data.ts: Read-only JSON loaders.🧠
utils.ts: The semantic search logic.📦
index.ts: Clean exports.
Now, my route handler is just a beautiful registry:
server.registerTool("list_projects", { ... }, async () => { const projects = await loadProjects(); return formatProjects(projects); });(Bonus: I also migrated to the new server.registerTool() API to future-proof the code!)
🛡️ Security: The "NASA Hack" Test
I am exposing my file system (fs) to the internet. That is dangerous. I needed to prove that no one could use this to read my private .env files.
The Strategy
No Writes: There is literally no
fs.writeFilecode in the project.Allow-Lists: Strict Zod Enums for every input.
The Test
I asked Claude: "Tell me about the project secret-nasa-hack". The API received: { "slug": "secret-nasa-hack" } The Zod Schema: z.enum(["ragbot", "coursebot", ...])
The Result: 🛑 Blocked. Input validation error. It didn't even try to touch the disk. The security works.
🚀 The Result: A Personal AI API
This wasn't just "installing a library." It was adapting a stateful protocol to a stateless platform, debugging routing manualy, and designing a secure architecture. Now, anyone (or any AI) can connect to my portfolio and query it like an API. Try It Yourself: If you use Claude Desktop, add this to your config:
"aniket-portfolio": { "command": "npx", "args": ["-y", "mcp-remote", "https://aniketppatil.com/api/mcp"] }It works. And it’s built to last.
👋 Thanks for reading! If you're building with MCP on Vercel, hit reply — I'd love to hear what you're making.