portfolio-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@portfolio-mcpSearch for articles about Model Context Protocol."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
portfolio-mcp
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-indexrepo): the build emits a static machine corpus —corpus-index.json, per-documentcorpus/<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, theWebStandardStreamableHTTPServerTransport). Zero runtime fetches.Layer 2 (this repo,
src/stdio.ts): the same server over stdio, for running locally vianpx saagar-portfolio-mcp. Identical tool/resource/prompt surface.
The shared core (src/server.ts) is transport-agnostic; both layers wrap it.
Related MCP server: CodeAlive MCP
Tool surface
All read-only (readOnlyHint: true). No tool takes a URL or filesystem path (no
SSRF / exfil surface).
Tool | Purpose |
| BM25 over the whole corpus; optional |
| Full Markdown of one document by |
| The table of contents; optional |
| The "who is this" card (about / now / uses) |
| Curated public-safe projects + anonymized aggregates |
| Public-safe repo profile index with freshness and proof counts |
| One repo answering profile by |
| Public, sanitized OPERANT calibration results (per-model OCS) |
Documents are also exposed as Resources (portfolio://essays/{slug}, book/{slug},
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 corpus is ~50 small 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 (+ OPERANT) 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 testsDevelop
npm install
npm run build:corpus # bake from ../portfolio-index (or --url=https://saagarpatel.dev)
npm run typecheck
npm test
npm run dev # wrangler dev -> http://localhost:8787/mcp
npm run smoke # end-to-end MCP smoke under the real workerd runtime (saagar-mcp-kit)
npm run probe:mcp # live Worker probe, or set PORTFOLIO_MCP_ENDPOINTInspect either transport with the MCP inspector:
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)
npm run build:corpus && npm run deploy # wrangler deploy
npm run probe:mcp # post-deploy live MCP readbackOperator-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)
npm run build:corpus && npm run build:cli # -> dist/stdio.js
npm login && npm publish # public package: saagar-portfolio-mcpOnce 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):
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.pub
npm run sign:verify # checks manifest bytes against .sig + public keyOverride 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
Built + locally verified: Layers 0–2. Shared core + 8 tools + Resources + 2 prompts +
get_operant_results. typecheck clean; test suite passes (incl. full MCP protocol via the fetch handler). Live Worker probe and deploy remain operator-gated. Public discovery advertisesmcp.saagarpatel.devwith a valid Ed25519-signed manifest.Gated / next: publish the stdio package (
npm publish, after removing"private": trueby explicit operator approval only), glama.ai registry listing, and continued signed-manifest readback checks after website manifest changes.
This server cannot be installed
Maintenance
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/saagpatel/portfolio-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server