saagarpatel.dev Portfolio

io.github.saagpatelPublished by GitHub account saagpatelv0.1.0Updated Oct 24, 2026

Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.

The publisher's own server: PortfolioFit

VerifiedStreamable HTTPWeb executableWeb Search & ScrapingKnowledge & Memory

Overview

AI-generated overview

Read-only MCP server over the public saagarpatel.dev corpus, letting an assistant search and read essays, book chapters and notes, and browse projects and…

What you can do
  • Search the whole corpus with ranked BM25 matches, snippets and ids
  • Read a single document's full Markdown by id, or list the full table of contents with titles, types, dates and word counts
  • Get the author's profile card covering about, now and uses pages
  • List curated public projects with stack, status and activity signals
  • List and read public-safe repo answering profiles with freshness and proof counts
  • Retrieve public, sanitized OPERANT per-model calibration results
When to use it
Useful when an assistant needs grounded answers about Saagar Patel's writing, projects or published benchmark results instead of scraping the website HTML. Suited to research, summarization and introduction tasks over that specific public corpus.
Requirements
Runs either as a remote streamable HTTP endpoint or locally via npx saagar-portfolio-mcp over stdio. No authentication, API keys, headers or environment variables are declared; the corpus is baked into the server, so no runtime network fetches are needed.
Before you install
All tools are read-only and stateless, and no tool accepts a URL or filesystem path. Content is limited to one author's public material, and repo profiles are static artifacts rather than live repository health; OPERANT results may be unavailable if no dataset was baked.

Installation

In SourceWeft

  1. Open saagarpatel.dev Portfolio in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.

Use with an agent

Paste this prompt into Claude Code, Codex, Cursor or another agent — or into SourceWeft chat. The agent reads this server's install guide, shows you its publisher, endpoint and sign-in, and adds it to your client once you agree. Keys never go into a command.

Read https://sourceweft.com/mcp/io.github.saagpatel%2Fportfolio-mcp/install.md and connect the MCP server it describes to my client. Before changing any config, show me its publisher, endpoint and sign-in method and wait for my OK. Never put an API key in a command or in a file I would commit; ask me where it should come from.

Read the install guide the agent follows

Other MCP clients

Add this to your client's mcpServers config.

{
  "mcpServers": {
    "saagarpatel-dev-portfolio": {
      "type": "http",
      "url": "https://mcp.saagarpatel.dev/mcp"
    }
  }
}

README

portfolio-mcp

[Glama MCP server]

The agent-native layer of saagarpatel.dev: a Model Context Protocol server that lets any AI agent query Saagar's writing, projects, public-safe repo profiles, and benchmark results directly, instead of scraping HTML.

Read-only. Stateless. Public. No auth, no tracking, no database, no runtime egress.

How it fits

The website stays a pure static site. This server is a sibling, not a backend bolted onto it:

  • Layer 0 (in the portfolio-index repo): the build emits a static machine corpus — corpus-index.json, per-document corpus/<id>.json, and .well-known/mcp.json — plus public-safe repo profile artifacts when present, served alongside the HTML. Already public.
  • Layer 1 (this repo, src/index.ts): a stateless Cloudflare Worker that bakes the Layer 0 corpus into its bundle and serves it over MCP (streamable HTTP, the WebStandardStreamableHTTPServerTransport). Zero runtime fetches.
  • Layer 2 (this repo, src/stdio.ts): the same server over stdio, for running locally via npx saagar-portfolio-mcp. Identical tool/resource/prompt surface.

The shared core (src/server.ts) is transport-agnostic; both layers wrap it.

Tool surface

All read-only (readOnlyHint: true). No tool takes a URL or filesystem path (no SSRF / exfil surface).

ToolPurpose
searchBM25 over the whole corpus; optional section filter, limit
get_documentFull Markdown of one document by id
list_corpusThe table of contents; optional type filter
get_profileThe "who is this" card (about / now / uses)
list_projectsCurated public-safe projects + anonymized aggregates
list_repo_profilesPublic-safe repo profile index with freshness and proof counts
get_repo_profileOne repo answering profile by the repoId argument
get_operant_resultsPublic, sanitized OPERANT calibration results (per-model OCS)

Documents are also exposed as Resources (portfolio://essays/{slug}, portfolio://book/{slug}, portfolio://notes/{slug}, portfolio://profile), and there are two Prompts: introduce_saagar and summarize_writing_on (grounded in a live search).

Retrieval

BM25 over a baked index (no embeddings in v1 — the committed corpus contains 166 docs and the calling LLM supplies the semantics). Titles are boosted. Embeddings are a measured Phase 3 upgrade, added only if retrieval quality proves insufficient.

Layout

src/  types.ts            corpus + projects + operant shapes  bm25.ts             dependency-free BM25 + snippet (pure)  tools.ts            createTools(corpus) -> the 8 tools (pure, injectable)  corpus.ts           loads the baked corpus + accessors  corpus.generated.ts AUTO-GENERATED by build:corpus  server.ts           buildServer(): shared MCP core (tools + resources + prompts)  index.ts            Cloudflare Worker transport (streamable HTTP)  stdio.ts            Layer 2 stdio transport (the npx CLI)scripts/	build-corpus.mjs    bakes Layer 0 and local OPERANT inputs into corpus.generated.ts  probe-mcp.mjs        probes an MCP HTTP endpoint (saagar-mcp-kit driver + domain calls)  audit-mcp.sh        connected MCPAudit scan of this server (dogfood)test/                 vitest: bm25, tools, full-protocol server tests

Develop

From the repository root, use the pinned Node and npm versions from .node-version and package.json. The checked-in public-safe corpus is enough for routine checks; no sibling checkout, local portfolio import, signing key, or Cloudflare credentials are needed.

sh
npm cimake check

make check runs lint, format validation, typecheck, tests, the stdio build and protocol test, smoke, parity, and a package-content check. See CI for the same aggregate gate. npm run dev starts the local Worker.

npm run build:corpus regenerates source from a sibling checkout or an explicit network URL; URL-mode builds omit OPERANT because its build inputs are not published. Corpus generation is not required for ordinary fixture verification. npm run probe:mcp targets the live Worker by default, and the connected audit starts a local Worker and invokes the separately installed mcp-audit CLI. Keep those separate from local gates; deployment and signing remain operator-gated as described below.

Inspect either transport with the MCP inspector:

sh
npx @modelcontextprotocol/inspector http://localhost:8787/mcp   # Layer 1 (HTTP)npx @modelcontextprotocol/inspector node dist/stdio.js          # Layer 2 (stdio, after build:cli)

Deploy (Layer 1)

sh
npm run build:corpus && npm run deploy   # wrangler deploynpm run probe:mcp                        # post-deploy live MCP readback

Operator-gated (needs Cloudflare auth). v1 still deploys to the default portfolio-mcp.<account>.workers.dev URL, and npm run probe:mcp uses that stable Worker URL by default. Public discovery now advertises the verified custom endpoint https://mcp.saagarpatel.dev/mcp; after any deploy, verify both the Worker and the website manifest/readback path before changing .well-known/mcp.json.

wrangler.jsonc pins workers_dev: true so the public Worker URL stays live during any future custom-domain experiments; do not remove it unless the website manifest has already moved to a verified replacement endpoint.

Publish (Layer 2)

sh
npm run build:corpus && npm run build:cli   # -> dist/stdio.jsnpm login && npm publish                     # public package: saagar-portfolio-mcp

Once published, anyone can run it locally with npx saagar-portfolio-mcp (no install).

Sign the manifest (optional trust signal)

Ed25519-sign .well-known/mcp.json so an agent or registry can verify it authentically comes from Saagar (via saagar-mcp-kit's signing CLI, Node built-in crypto):

sh
npx mcp-kit-sign gen-key --manifest=../portfolio-index/.well-known/mcp.json  # one-time; private key -> .signing/ (gitignored, NEVER commit)npm run sign                             # writes <manifest>.sig + publishes mcp-ed25519.pubnpm run sign:verify                      # checks manifest bytes against .sig + public key

Override paths with --manifest=/--key=/--pub=/--sig=. Commit the .sig + mcp-ed25519.pub (never the private key) into portfolio-index next to the manifest, then redeploy the site. Re-run sign whenever the manifest changes (it signs the exact served bytes).

Audit posture

Designed to pass MCPAudit / mcp-trust (Saagar's own tools): only the inbound MCP transport, no shell_execution / file_access / destructive / exfiltration, and no caller-controlled egress (the corpus is baked). All tools are annotated read-only with plain, non-injectable descriptions. bash scripts/audit-mcp.sh runs a connected scan.

Dogfooding this server surfaced a substring-matching false-positive bug in MCPAudit (it matched port inside portfolio://); that fix lives in the MCPAudit repo and cut this server's findings 62 → 14. The genuine tool surface scans clean (high_risk_servers: 0).

Status

Stack

Node.js 24.21.0, npm 12.2.0, TypeScript, Cloudflare Workers, and the MCP SDK.

Known risks

URL-mode corpus builds omit OPERANT, whose inputs are only available to local corpus builds. Worker deployment and package publication are not part of local verification.

Next steps

Keep the published MCP endpoint and signed discovery manifest in sync when an operator authorizes a deployment. Registry publication remains a separate, explicitly authorized release action.

Source: README.md at commit 55de013

Tools

8

get_document

Get a document

Return the full Markdown of one document by id, e.g. "writing/the-handoff" or "book/preface". Get ids from search or list_corpus.

Parametersidrequired

list_corpus

List the corpus

List every document with its id, title, type, date, and word count, plus per-type counts. The table of contents for the whole site. Optionally filter by type.

Parameterstype

get_profile

Get profile

Return Saagar Patel's profile: name, one-line summary, site, and the about / now / uses pages. Start here to understand who this is and what they work on.

Takes no parameters.

list_projects

List projects

List the curated, public projects with stack, status, last-active date, and test/CI signals, plus anonymized portfolio-wide aggregates. Optionally filter by status.

Parameterslimitstatus

list_repo_profiles

List repo profiles

List public-safe repo answering profiles with repo id, attention state, freshness, and proof counts. Profiles are static artifacts baked from saagarpatel.dev, not live repo health.

ParameterslimitattentionState

get_repo_profile

Get repo profile

Return one public-safe repo answering profile by repo_id, including purpose, safe commands, agent surfaces, proof refs, freshness, exclusions, limitations, and integrity metadata.

ParametersrepoIdrequired

get_operant_results

Get OPERANT benchmark results

Return the public, sanitized OPERANT results: per-model operator-calibration-score (OCS) profiles, the headline figures, and the calibration-profiles (not a flat leaderboard) framing. Sourced from the published OPERANT mirror. Returns availability=false if no dataset was baked.

Takes no parameters.

Version history

1
  1. v0.1.0LatestSep 16, 2026

Score

100% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 daysRequired25 pts
  • Auth declaredRequired10 pts
  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts

Other Portfolio integrations