---
name: sudohulk-mcp
description: Connect to the public MCP server at https://sudohulk.com/mcp and use its tools to search, list and read Vignesh Kumar A's essays, notes, updates, projects, streams and tracks as markdown. Covers connecting, the protocol versions it speaks, each tool's arguments and results, its resources and its errors. Use when an agent with MCP needs content from sudohulk.com.
license: CC-BY-4.0
---

# The sudohulk.com MCP server

`https://sudohulk.com/mcp` serves the site's listed content to MCP clients: the essays, notes,
updates, projects and tracks of Vignesh Kumar A, a database engineer who writes about computer science,
databases first. It is stateless and public: no account, no key, no session.

Its card is `https://sudohulk.com/.well-known/mcp/server-card.json`. Everything it returns is
also on the site as static markdown; the skill `read-and-cite-sudohulk`
(`https://sudohulk.com/.well-known/agent-skills/read-and-cite-sudohulk/SKILL.md`) covers reading
and citing it without MCP.

## Connect

Claude Code:

```bash
claude mcp add --transport http sudohulk https://sudohulk.com/mcp
```

Any other client: add a remote server with the URL `https://sudohulk.com/mcp` and the Streamable
HTTP transport (some clients call it `http`). Leave authentication off.

## How it behaves

- **Versions.** 2026-07-28, with no handshake: every request carries
  `_meta["io.modelcontextprotocol/protocolVersion"]` and
  `_meta["io.modelcontextprotocol/clientCapabilities"]`, and the `MCP-Protocol-Version` and
  `Mcp-Method` headers, plus `Mcp-Name` when it calls a tool or reads a resource; each header must
  match the body. Also 2025-11-25, 2025-06-18 and 2025-03-26, where `initialize` comes first and
  the negotiated version goes in the `MCP-Protocol-Version` header after it. `server/discover`
  lists the versions. Any other version is an HTTP 400 that lists the ones it speaks.
- **One JSON answer per POST.** Nothing streams. `GET` and `DELETE` are 405, a notification gets
  202, and no session id is ever issued.
- **Any origin.** A page in a browser, an extension or a webview may call it; it sends no
  credentials and reads none.
- **Freshness.** Lists change only when the site is deployed. At 2026-07-28, list and read results
  carry `ttlMs` and `cacheScope: "public"`; keep them that long.

A raw call at 2026-07-28:

```bash
curl -s https://sudohulk.com/mcp \
  -H 'content-type: application/json' \
  -H 'MCP-Protocol-Version: 2026-07-28' -H 'Mcp-Method: tools/call' -H 'Mcp-Name: search' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search","arguments":{"query":"write-ahead log"},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'
```

## Tools

Every result has a readable `text` part and, for most, `structuredContent` with the same facts.
Each URL in a result is absolute: `url` is the canonical page to cite, `markdown` its twin. A
bad argument comes back as a result with `isError: true` and a sentence saying what to change.

### `search`

Finds essays, notes, updates, photo posts, projects and tracks by words in their titles,
descriptions, tags and section headings.

- `query` (string, required, up to 200 characters), such as `"two-phase commit"`.
- `type`: `essay`, `note`, `update`, `photos`, `project` or `track`.
- `stream`: the subject, one of `tech` (computer science, databases first), `books`,
  `learning`, `places`, `life`, `meta`.
- `limit`: 1 to 20, default 5.

Returns `{ query, results: [{ type, title, description, url, markdown, stream, published, score }] }`,
best first. Read a hit with `get_page`.

### `get_page`

Returns one page as markdown, headed by its canonical URL, dates and licence.

- `path` (string, required): the page's path (`/writing/<id>`, `/about`, `/tech` for a stream,
  `/` for the home page), its full URL, or its `.md` twin.

Returns the markdown as text, and `{ title, url, markdown }`. Only listed pages of this site: any
other path is an error that says so.

### `list_content`

Lists every listed entry and project, newest entries first.

- `type`: `essay`, `note`, `update`, `photos` or `project`. `stream`: as for `search`. `tag`: a
  tag as the pages show it, such as `postgresql`.

Returns `{ count, items: [{ type, id, title, description, url, markdown, slug, stream, maturity,
place, tags, track, published, updated, status, minutes }] }`. `maturity` says how far to trust an
entry (`seedling`, `budding`, `evergreen`): a note always has one. `status` is a project's, where
it stands.

### `list_streams`

Lists the streams, the subjects every entry is filed under, each with what it covers and how many
entries it has. No arguments.

Returns `{ count, streams: [{ id, label, description, url, markdown, count }] }`. A stream's
`markdown` lists every entry in it; pass its `id` to `search` or `list_content` as `stream`.

### `list_tracks`

Lists the tracks, the books, courses and series followed in public.

- `status`: `active`, `paused`, `planned`, `finished` or `dropped`. `stream`: as for `search`.

Returns `{ count, tracks: [{ id, title, description, url, markdown, stream, kind, unit, status,
done, total, percent, started, ended, target, targetMet, lastActivity, minutes, projection,
privateCount, writeup, tags }] }`. The status is worked out from the track's log; `projection` is
where the pace so far would finish it, not a promise. Public items only: `privateCount` counts the
rest without naming them.

### `get_track`

Returns one track as markdown: its plan item by item, its progress, a dated log and why it
exists.

- `id` (string, required): from `list_tracks`, such as `"building-sudohulk"`.

### `get_now`

Returns the `/now` page as markdown: what he is working on, reading and learning, and when it was
last updated. No arguments.

### `get_changes`

Returns the site log, `/changes`, as markdown: every change to a public entry (published,
revised, corrected, status, withdrawn) as a numbered record, newest first. The numbers only go up
and no record is ever removed.

- `since` (integer): the last record number you saw. Only the records numbered above it come back,
  so you can check for what is new without reading the corpus again. `/changes.json` has the same
  records as JSON.

### `get_feedback`

Returns the notes other AI readers left on one page that he approved, and how many said it was
useful, clear, or that they wanted more.

- `slug` (string, required): `writing/<id>` or `projects/<id>`, the `slug` from `list_content`.

### `leave_feedback`

The one tool that writes. It queues a reaction, a suggestion or both on one post, note or project
for him to read. Nothing appears on the site unless he approves it, and then it is labelled as
machine-written.

- `slug` (string, required): as for `get_feedback`.
- `agent` (object, required): `name` (up to 60 characters), and optionally `model` (up to 60) and
  an https `url` (up to 200).
- `kind`: `useful`, `clear` or `more`: what the piece did for you.
- `suggestion`: 20 to 1,200 characters of plain text, saying what would improve and why. Send a
  `kind`, a `suggestion` or both.

A suggestion with a link in it is refused. Sending the same thing twice changes nothing. The
limits are those of the site's feedback API, five an hour from one source; past them the result
is an error ending `(HTTP 429)`: stop and try later. A call from a browser page on another
origin is refused too, since the page would be sending it from its visitors' browsers: call it
from a client that is not a web page.

## Resources

Every listed entry and track is also a resource: `resources/list` names them, with
`mimeType: "text/markdown"`, and `resources/read` returns one. Each URI is the https URL of the
page's markdown twin, which any client may also fetch directly. There are no resource templates
and no prompts.

## Errors

- An unknown tool is JSON-RPC error `-32602`; an unknown method is `-32601` (HTTP 404 at
  2026-07-28).
- A version it does not speak is HTTP 400 with `-32022`, and `error.data.supported` lists the
  ones it does.
- At 2026-07-28, a header that disagrees with the body, or is missing, is HTTP 400 with `-32020`.
- A body over 64 KB is refused with 413, and a batch (2025-03-26 only) of more than ten messages
  with 400.
- Each tool call and resource read counts against an allowance of 60 a minute from one address.
  Past it the call is HTTP 429 with error `-31429` and a `Retry-After` header: wait that many
  seconds.

## Good use

- Search or list first, then read only the pages you need.
- Cite the canonical `url`, never the `.md` one. The content is CC BY 4.0: quote freely, credit
  Vignesh Kumar A.
- If you repeat a `seedling` note, say it is an early note.
