npm saagar-portfolio-mcpstreamable-httpMITupdated 17d ago
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.
Was kannst du mit saagarpatel dev Portfolio machen?
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.
Tool surface
All read-only (readOnlyHint: true). No tool takes a URL or filesystem path (no
SSRF / exfil surface).
| Tool | Purpose |
|---|---|
search |
BM25 over the whole corpus; optional section filter, limit |
get_document |
Full Markdown of one document by id |
list_corpus |
The table of contents; optional type filter |
get_profile |
The "who is this" card (about / now / uses) |
list_projects |
Curated public-safe projects + anonymized aggregates |
list_repo_profiles |
Public-safe repo profile index with freshness and proof counts |
get_repo_profile |
One repo answering profile by repo_id |
get_operant_results |
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 tests
Develop
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_ENDPOINT
Inspect 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 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)
npm run build:corpus && npm run build:cli # -> dist/stdio.js
npm 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):
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 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
- 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.
Installation
saagarpatel dev Portfolio zu deinem Client hinzufügen. Wähl den, den du nutzt.
{
"servers": {
"saagar-portfolio-mcp": {
"type": "http",
"url": "https://mcp.saagarpatel.dev/mcp"
}
}
}Add to `.vscode/mcp.json` in your workspace.
claude mcp add saagar-portfolio-mcp -- npx -y saagar-portfolio-mcpcodex mcp add saagar-portfolio-mcp -- npx -y saagar-portfolio-mcpamp mcp add saagar-portfolio-mcp -- npx -y saagar-portfolio-mcp{
"mcpServers": {
"saagar-portfolio-mcp": {
"command": "npx",
"args": [
"-y",
"saagar-portfolio-mcp"
]
}
}
}Add to `claude_desktop_config.json`, then restart Claude Desktop.
{
"mcpServers": {
"saagar-portfolio-mcp": {
"command": "npx",
"args": [
"-y",
"saagar-portfolio-mcp"
]
}
}
}Add to `~/.cursor/mcp.json`, or `.cursor/mcp.json` for a single project.
{
"mcpServers": {
"saagar-portfolio-mcp": {
"command": "npx",
"args": [
"-y",
"saagar-portfolio-mcp"
]
}
}
}Add to `~/.codeium/windsurf/mcp_config.json`.
{
"mcpServers": {
"saagar-portfolio-mcp": {
"command": "npx",
"args": [
"-y",
"saagar-portfolio-mcp"
]
}
}
}Add to `cline_mcp_settings.json` via the MCP Servers panel.
{
"mcpServers": {
"saagar-portfolio-mcp": {
"command": "npx",
"args": [
"-y",
"saagar-portfolio-mcp"
]
}
}
}Add to `~/.gemini/settings.json`.
{
"mcpServers": {
"saagar-portfolio-mcp": {
"type": "local",
"command": "npx",
"args": [
"-y",
"saagar-portfolio-mcp"
],
"tools": [
"*"
]
}
}
}Add to `~/.copilot/mcp-config.json`, or run `/mcp add` inside the CLI.
{
"context_servers": {
"saagar-portfolio-mcp": {
"command": {
"path": "npx",
"args": [
"-y",
"saagar-portfolio-mcp"
]
}
}
}
}Add to your Zed `settings.json`.
npx -y saagar-portfolio-mcpRun `goose configure`, choose **Add Extension → Command-line Extension**, and paste this command.
7 Tools
saagarpatel dev Portfolio stellt einem verbundenen Agent 7 Tools bereit.
- get_document
- Full Markdown of one document by `id`
- list_corpus
- The table of contents; optional `type` filter
- get_profile
- The "who is this" card (about / now / uses)
- list_projects
- Curated public-safe projects + anonymized aggregates
- list_repo_profiles
- Public-safe repo profile index with freshness and proof counts
- get_repo_profile
- One repo answering profile by `repo_id`
- get_operant_results
- Public, sanitized OPERANT calibration results (per-model OCS)
Score
73 / 100
Gut
- Dokumentation20/25
- Pflege19/25
- Vertrauen13/20
- Funktionsumfang6/15
- Installation15/15
- Documents what it does and how to connect
- Has a resolvable package or endpoint
- Exposes at least one tool, prompt or resource
- README has substantive content
- Includes a code example
- Documents its configuration
- Mentions credentials or security posture
- Last commit 10 days ago
- Has a release history
- Repository is not archived
- Licensed MIT
- Namespace verified in the official MCP registry
- Claimed by its owner
- Published under an organisation
- 7 tool(s) documented
- Provides prompt templates
- Provides resources
- 18 documented install method(s)
- Published to a package registry
- Offers a hosted endpoint — no local install
Versionsverlauf
| Versionen | Veröffentlicht |
|---|---|
| 0.1.0Aktuell | 28. Juni 2026 |