# CLAUDE.md
## Project Overview
portfolio-mcp is a Model Context Protocol (MCP) server that lets AI assistants (Claude Desktop, etc.) manage blog posts and projects for a personal portfolio site. It operates against a separate portfolio repository (`suthar-portfolio`) -- reading and writing MDX blog posts and a JavaScript projects data file, with Git integration for committing and pushing changes.
The server has three runtime modes:
1. **Local MCP server** (`src/index.ts`) -- stdio-based MCP server for direct Claude Desktop integration
2. **REST API server** (`src/api-server.ts`) -- Express-based HTTP API for web-based management
3. **Vercel serverless function** (`api/portfolio.ts`) -- serverless deployment that uses the GitHub API directly (no local filesystem access)
There is also an **MCP proxy** (`src/mcp-proxy.ts`) that bridges Claude Desktop to a remote Vercel-deployed API by translating MCP tool calls into HTTP requests.
## Tech Stack
- **Language**: TypeScript (ES2020 target, CommonJS modules)
- **Runtime**: Node.js
- **MCP SDK**: `@modelcontextprotocol/sdk` (stdio transport)
- **HTTP Framework**: Express 4 (for the REST API server)
- **Markdown Parsing**: `gray-matter` (YAML frontmatter extraction from MDX files)
- **Git Operations**: `simple-git` (for local git status, commit, push)
- **Deployment**: Vercel (serverless functions via `@vercel/node`)
- **Build**: `tsc` (TypeScript compiler)
## Directory Structure
```
portfolio-mcp/
├── src/ # TypeScript source files
│ ├── index.ts # Main MCP server (stdio transport, local filesystem)
│ ├── api-server.ts # Express REST API server
│ ├── mcp-proxy.ts # MCP-to-HTTP proxy for remote Vercel backend
│ ├── config.ts # Config loader (env vars or JSON file)
│ ├── types.ts # TypeScript interfaces (Config, Post, Project, etc.)
│ ├── post-tools.ts # PostTools class -- CRUD on MDX blog posts
│ ├── project-tools.ts # ProjectTools class -- CRUD on projectsData.js
│ ├── git-tools.ts # GitTools class -- git status, commit, push
│ └── utils.ts # Helpers: titleToSlug, todayDate, buildFrontmatter
├── api/ # Vercel serverless functions
│ └── portfolio.ts # Single catch-all handler (uses GitHub API directly)
├── dist/ # Compiled JavaScript output (generated by tsc)
├── portfolio-mcp.config.json.example # Config template for local development
├── vercel.json # Vercel routing config (all routes -> api/portfolio)
├── package.json
├── tsconfig.json
└── .gitignore
```
## Key Commands
```bash
# Install dependencies
npm install
# Build TypeScript to dist/
npm run build
# Run the MCP server (stdio, for Claude Desktop)
npm start
# Run the REST API server
npm run start:api
# Development mode (ts-node, no build needed)
npm run dev # MCP server
npm run dev:api # REST API server
```
There are no tests in this project.
## Configuration
The server loads config in this priority order:
1. **Environment variables** (for production / Vercel): `PORTFOLIO_PATH`, `POSTS_DIR`, `PROJECTS_FILE`, `GIT_REMOTE`, `GIT_BRANCH`, `API_PORT`, `API_HOST`, `API_KEY`, `ALLOWED_ORIGIN`, `GITHUB_TOKEN`
2. **Config file** (`portfolio-mcp.config.json`): Copy from `.example` and set `portfolioPath` to the local portfolio repo path
The config points to a *separate* portfolio repository on disk (default structure: `data/posts/*.mdx` for blog posts, `data/projectsData.js` for projects).
## Architecture Notes
- **Local mode** (`index.ts` + tool classes): Reads/writes files directly on the local filesystem of the portfolio repo. Uses `simple-git` for git operations.
- **Vercel mode** (`api/portfolio.ts`): Completely self-contained serverless handler. Does NOT use the tool classes or `gray-matter`. Instead, it calls the GitHub Contents API directly to read/write files, with its own minimal frontmatter parser. Requires `GITHUB_TOKEN` with repo scope.
- **MCP proxy** (`mcp-proxy.ts`): A thin stdio MCP server that forwards all tool calls as HTTP requests to the deployed Vercel API. Used for Claude Desktop when the backend runs remotely. Configured via `PORTFOLIO_API_URL` and `PORTFOLIO_API_KEY` env vars.
- The Vercel handler (`api/portfolio.ts`) duplicates logic from `post-tools.ts`, `project-tools.ts`, and `utils.ts` because it cannot access the local filesystem and uses GitHub API instead. Keep them in sync when making changes to post/project schemas.
## Coding Conventions
- TypeScript with strict mode enabled
- CommonJS module output (not ESM), but source uses ES import syntax
- `.js` extensions in import paths (required for CommonJS resolution of compiled TS)
- Classes for tool groupings (`PostTools`, `ProjectTools`, `GitTools`)
- Express middleware pattern for auth and logging in the API server
- No linter or formatter configured
- No test framework configured
- Error handling: tool handlers catch errors and return them as MCP error responses or HTTP error JSON
## Data Formats
### Blog Posts
MDX files with YAML frontmatter in `data/posts/`:
```yaml
---
title: 'Post Title'
date: '2024-06-27'
tags: [tag1, tag2, tag3]
draft: false
summary: 'Short description'
pinned: true # optional
pinnedtext: 'text' # optional
---
MDX body content...
```
### Projects
JavaScript file (`data/projectsData.js`) exporting an array:
```js
const projectsData = [
{
title: 'Project Name',
description: 'Description',
imgSrc: '/image.jpg',
href: 'https://example.com',
github: 'https://github.com/...', // optional
tech1: 'React', // optional
tech2: 'TypeScript', // optional
tech3: 'Node.js' // optional
}
];
export default projectsData;
```
## Important Warnings
- The `.env` file contains real secrets (GitHub token, API key). It is gitignored but present on disk. Never commit it.
- The `portfolio-mcp.config.json` file contains local paths and is also gitignored. Only the `.example` version is tracked.
- The Vercel handler parses the projects JS file with regex and string manipulation (not a real JS parser), so unusual formatting in `projectsData.js` may break parsing.
- The `dist/` directory is committed to git. After changing source files, run `npm run build` and commit the updated dist.