Skip to main content
Glama
HarshdipSaha

harshdipsaha-mcp

by HarshdipSaha
README.md
# harshdipsaha-mcp

A public, read-only [Model Context Protocol](https://modelcontextprotocol.io) server for
[harshdipsaha.tech](https://harshdipsaha.tech). Add its URL to any MCP client (Claude Code, Codex,
Cursor, …) to query Harshdip Saha's projects and profile directly, instead of scraping the site.

Design doc: [`docs/plans/2026-09-05-mcp-server-design.md`](https://github.com/HarshdipSaha/HARSHDIPSAHA.github.io/blob/main/docs/plans/2026-09-05-mcp-server-design.md)
in the portfolio repo. Originated from [issue #62](https://github.com/HarshdipSaha/HARSHDIPSAHA.github.io/issues/62).

**Not to be confused with WebMCP.** The portfolio site separately registers a WebMCP tool
(`document.modelContext`, [ADR 0014](https://github.com/HarshdipSaha/HARSHDIPSAHA.github.io/blob/main/docs/adr/0014-agent-facing-site-llms-txt-and-webmcp.md)/[0020](https://github.com/HarshdipSaha/HARSHDIPSAHA.github.io/blob/main/docs/adr/0020-webmcp-document-modelcontext.md))
— a browser API consumed by an agent embedded in a browser tab. This repo is the real Model
Context Protocol, consumed by external clients over a network connection. They share a name and
nothing else.

## Tools

- **`searchProjects(query, limit?)`** — keyword search over the project list, same matching
  semantics as the portfolio's own WebMCP tool: every whitespace-separated term must appear in the
  title, summary, slug, or year.
- **`getProfile()`** — bio, skills, research interests (each with how it's being pursued), contact
  info, résumé link, and site URL.

Deliberately just these two, matching ADR 0014's minimal-surface reasoning: every additional tool
is a schema that has to stay correct.

## Architecture

```
harshdipsaha.tech/agent-data.json  ->  fetch (10 min edge cache)  ->  this Worker  ->  MCP client
```

The Worker holds no data of its own except `interests` (`src/lib/interests.mjs`, MCP-only copy that
needs no site rebuild; if `agent-data.json` ever ships its own `interests`, that wins). It fetches `agent-data.json` (generated by the portfolio
repo's `scripts/build-agent-data.mjs`) on every tool call, cached at Cloudflare's edge via the
`cf.cacheTtl` fetch option, so a burst of MCP traffic never hits GitHub Pages more than once per
10-minute window. There is no database, no KV, no Durable Object — the server is stateless, per the
MCP spec's 2026-07-28 revision.

- `src/lib/search-projects.mjs`, `src/lib/format.mjs`, `src/lib/interests.mjs` — pure logic,
  unit-tested with `node --test`.
- `src/lib/agent-data-client.mjs` — the one place that does I/O (the fetch).
- `src/server.mjs` — builds the `McpServer` and registers both tools.
- `src/index.mjs` — the Worker entry point, wrapping `createServer` with Cloudflare's
  `createMcpHandler` (`agents/mcp/server`) for the Streamable HTTP transport.

## Run locally

```bash
npm install
npm run typecheck   # tsc, split into tsconfig.json (src, Workers types) + tsconfig.test.json (test, Node types)
npm test            # node --test (default discovery) — the pure-function suite
npm run dev          # wrangler dev — needs Node >= 22 (see below)
```

**Node version:** `wrangler` hard-requires Node ≥ 22. This project's own code runs fine on Node 20+
(`npm test`/`npm run typecheck` don't need 22), but `npm run dev`/`npm run deploy` will refuse to
start otherwise. If you're on an older Node, switch first (`nvm use 22` or equivalent).

Verified locally (Node 22.23.2, `wrangler dev`, no deploy): `initialize`, `tools/list`, and a real
`tools/call` for both tools against the live `agent-data.json` all returned correct responses.

## Deploy

```bash
npx wrangler login   # one-time, opens a browser OAuth flow — do this yourself, not from an agent session
npm run deploy
```

Publishes to `<worker-name>.<your-subdomain>.workers.dev` with no custom domain and no
authentication configured, matching the design doc's scope (public, read-only, no auth).

## Keeping this in sync

`src/lib/types.mjs`'s JSDoc typedefs mirror the portfolio repo's `scripts/lib/agent-data.mjs`
output field-for-field. There's no shared package between the two repos — if that shape changes on
the portfolio side, update the typedefs and `src/server.mjs`'s Zod schemas here by hand.

## Out of scope (see the design doc)

- Any write/contact tool.
- Auth of any kind.
- The portfolio site's own discoverability addition (a footer link / `/mcp` section) — a separate,
  later change to `HARSHDIPSAHA.github.io`.