anubhavmalhotra.com MCP server
OfficialREADME.md
# anubhavmalhotra.com
Source for [anubhavmalhotra.com](https://anubhavmalhotra.com/): a one-page, README-style personal site that also speaks [MCP](https://modelcontextprotocol.io/). Hosted on Vercel.
## Layout
- `public/` is the site: `index.html` (no JavaScript, no webfonts), plus machine-readable twins `README.md`, `llms.txt` and `profile.json`.
- `api/mcp.js` is a Vercel function served at `/mcp` (see `vercel.json`): a stateless, read-only MCP server over Streamable HTTP, built on the official SDK and answering from `public/profile.json` and `public/README.md`.
- `lib/mcp-server.js` holds the tools and resources. `dev/server.mjs` runs the same function locally.
- `test/mcp-smoke.mjs` connects with the official MCP client and exercises every tool, resource and static file.
Edit `public/index.html`, `public/README.md` and `public/profile.json` together when facts change.
## The download gate
A password-protected page at a secret path describes a package and lets it be downloaded at most five times. Nothing about it is in `public/`: the path, the password hash and the storage token live only in the project's environment variables (`GATE_PATH`, `GATE_PASSWORD_HASH`, `BLOB_READ_WRITE_TOKEN`), and `api/gate.js` answers `/<path>` and `/<path>/download` through the catch-all rewrites in `vercel.json`.
A correct password sets a signed cookie (12 hours, keyed by the password hash, so changing the password ends all sessions). With the cookie, `/<path>` shows the description page with a Download button, and `/<path>/download` records one download and streams the file. The page text, the file and the download count live in a private Vercel Blob store; the count is updated with an ETag-conditional write, so the limit holds under concurrent requests. Each click on Download counts, whether or not the transfer finishes.
The description page is an HTML fragment kept out of this public repository. Keep the source in `private/` (ignored by git) and upload it; a `<!-- download -->` marker in it places the download block, otherwise the block is appended.
```sh
npx vercel env pull .env.local # once, for the scripts below
npm run gate:page -- private/zeus.html # upload the description page
npm run gate:page:pull # fetch the current page back into private/
npm run gate:upload -- ~/Downloads/thing.pkg # put a file behind the gate; resets the count
npm run gate:status # what is uploaded, downloads used
npm run gate:reset # five more downloads for the same file
npm run gate:password -- 'new password' # prints the hash to store in GATE_PASSWORD_HASH
```
To change the password, store the printed hash with `npx vercel env add GATE_PASSWORD_HASH production` (and `preview`, `development`), then redeploy. `npm run test:gate` exercises the whole flow with a throwaway fixture; it refuses to run while a real file is uploaded and leaves the page alone.
## Run
```sh
npm install
npm run dev # http://localhost:8787 and http://localhost:8787/mcp
npm test # against the dev server; SITE_URL=https://anubhavmalhotra.com npm test after deploying
```
## Deploy
```sh
npx vercel login
npx vercel --prod # first run creates the Vercel project and deploys
npx vercel domains add anubhavmalhotra.com
npx vercel domains add www.anubhavmalhotra.com
```
The domain already uses Vercel's nameservers, so it goes live as soon as it is attached to the project.
For automatic deploys on every push, import this repository at https://vercel.com/new instead of deploying from the CLI, then add the domains in the project's settings.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues