Skip to main content
Glama

Personal MCP server

Русская версия →

A public door where other people's agents read your context and leave you a message. No authentication, and none coming — a read layer that anyone can read is the whole point of a door.

This repo is two things at once. It is a reference: the same code runs postnikov.ai/mcp, answering strangers' agents since August 2026. And it is a template: nothing personal lives in the code, all of it lives in context/, which never enters git. Fork it, drop your own files in, run it — you have a server built to the HCP shape.

Why you'd want one at all — the idea, the live example and what not to build: humancontext.pro.

Five minutes

# 1. Your own copy — "Use this template" on GitHub, or locally:
git clone https://github.com/postnikov/hcp-server.git my-mcp
cd my-mcp && rm -rf .git && git init
npm install

# 2. Your personal layer
cp -r context.example context
$EDITOR context/who.md          # one file is enough to start
$EDITOR context/config.json     # your name, your domain, your caps

# 3. Check it here
npm test
npm start                       # http://localhost:3000/mcp

# 4. Point an agent at it
claude mcp add --transport http me http://localhost:3000/mcp

You don't have to write context/ by hand. The prompt prompts/generate-my-hcp.md in hcp-protocol drafts these files from material you already published — your CV, your posts, project READMEs, talk transcripts — and asks you questions only where the material ran out. Details in context.example/README.md.

Related MCP server: ohmyself

Three tiers: read, ask, knock

The default file set plus ask gives you seven tools, and add a file and you get an eighth. The names don't matter much; the split into three tiers does.

Tier

Cost

What it is

file tools

free · instant · no model in the path

Return a markdown file from your context, unchanged. No LLM, no latency, nothing to inject a prompt into. This tier does most of the work — build it first and you may never need the rest

ask

one API call · hard daily cap

For the question your files don't answer head-on. A model answers strictly from the same files, under a daily ceiling so an experiment can't become a bill. Off by default: the only tool that costs money

leave_message

the one write · human in the loop

A note lands in data/inbox.jsonl and pushes to your phone. Nothing is published, nothing is auto-answered, and the reply comes from you or not at all

Access is uniform — every tier is open to every agent, with no key and no account. What separates the tiers is what each one costs you and how far it reaches: reading is free and touches nothing, ask spends your money against a budget you set, leave_message is the only path that puts something in front of a human. Everything an agent can reach is something you published on purpose; the layer you keep private is the one you never put in context/.

The server serves what you have

Tools are declared by file presence. No speaking.md, no get_speaking in tools/list — an agent never gets an empty answer from a tool you don't have. Drop in context/reading.md and get_reading appears. No code to touch.

File in context/

Tool

who.md + now.md

get_profile

speaking.md

get_speaking

services.md

get_services

channels.md

get_channels

writing.md

get_writing

any X.md of yours

get_X

The floor is one file. who.md and nothing else gives you a working server with two tools (get_profile, leave_message). Zero files and the server refuses to start and tells you why: an empty server is worse than no server.

Every call is written to data/journal.jsonl. That log is the instrument — it's how you find out whether anyone's agent actually comes knocking, and what they came to ask.

Your personal layer

context/                  ← yours, never enters git (.gitignore)
  config.json             name, domain, caps, description overrides
  *.md                    context files; a YAML header names the tool
  deploy.env              VPS address and domain — read only by deploy.sh
context.example/          ← the same thing as a template, committed
.env                      ← secrets, on the server; sample in .env.example

A context file may carry a header. It never reaches the agent:

---
tool: get_speaking
title: Speaking
for_agent: >
  What is inside and when it's worth calling. The agent reads this BEFORE
  it calls the tool.
---
# Speaking — Your Name

No header, and the convention from config.json → context.groups applies, then get_<filename>. No description, and an honest one-liner is generated instead of an invented one.

Everything the server says about you comes out of context/: there is not one personal word in src/. A test checks that, so it's a fact and not a promise.

config.json

The file is optional — without it the server still runs, it just talks about "the owner of this server". Full annotated version: context.example/_config.md.

Key

What it sets

person

name, short, headline, site, pronouns — everything the server says about you that isn't a context file

server

id (the name in the handshake), connectAs (the name in claude mcp add), url (your public /mcp), instructions (what an agent reads before its first call; empty = assembled from your person and the tools that actually exist)

context.groups

the file → tool map, and the order tools appear in tools/list. A file not named here still ships, as get_<filename>

tools

overrides the descriptions of ask and leave_message. File tools are described by for_agent in their own header, next to the content they describe

ask

enabled, model, maxTokens, and the two daily ceilings

limits

per-IP buckets: calls per minute, ask and leave_message per day

discovery

whether this process serves its own /llms.txt and server card

Environment

All optional: with no variables at all the server serves files and takes a message. Commented sample in .env.example.

Variable

What it gives you

ASK_ENABLED

turns on ask (or set it in config.json)

ANTHROPIC_API_KEY

the key for ask. BYOK: you pay

ASK_MODEL, ASK_CALLS_PER_DAY, ASK_TOKENS_PER_DAY

model and daily ceilings

NOTIFY_TG_BOT_TOKEN, NOTIFY_TG_CHAT_ID

Telegram push when a message arrives

NOTIFY_RESEND_API_KEY, NOTIFY_EMAIL_TO, NOTIFY_EMAIL_FROM

the same by email, through Resend's HTTP API

SERVE_DISCOVERY

serve your own /llms.txt and server card (default: yes)

MCP_PUBLIC_URL

the public address of /mcp

PORT, DATA_DIR, CONTEXT_DIR

where to listen, where data goes, where your personal layer lives

Secrets travel by environment only. They are not in the code, not in the config files, and not in git.

Limits and money

What

Default

Where it lives

All calls

60/min per IP

process memory

ask

10/day per IP

process memory

leave_message

5/day per IP

process memory

ask — calls

20/day, everyone combined

data/budget.json, survives restarts

ask — tokens

150,000/day, everyone combined

data/budget.json, survives restarts

The daily ask ceilings are money-safety. Per-IP limits are not enough, because IP addresses are free for an agent and your API key is not. Hit either ceiling and the tool refuses without touching the API, and lists the free file tools instead. Resets at 00:00 UTC. Numbers live in context/config.json → ask.

Discovery

With discovery.enabled the server serves two files itself:

  • /.well-known/mcp/server-card.json — the MCP registry schema (2025-09-29): reverse-DNS name, description under 100 characters, remotes[].type: streamable-http;

  • /llms.txt — the same truth for an agent that reads text instead of JSON.

Both are assembled from your config and the real tool list, so there is nothing there to promise a capability you don't have. If your site already serves those files on the same domain, set SERVE_DISCOVERY=0. Two truths are worse than one.

Protocol: two eras, one endpoint

Built on SDK v2 (@modelcontextprotocol/server + @modelcontextprotocol/node). createMcpHandler serves both protocol eras on the same /mcp, from a single server factory:

  • modern — 2026-07-28: no initialize; version and client ride in a per-request envelope (_meta), method and tool name are mirrored in the Mcp-Method / Mcp-Name headers (SEP-2243), and server/discover returns the overview. The response is JSON.

  • legacy — 2025-era: the ordinary initialize, version negotiated from the client's request (the SDK's ceiling is 2025-11-25). The response is an SSE frame, not JSON — that's what the stateless path looks like in SDK v2. It is not a change of contract: a 200 only ever went to a client that declared Accept: text/event-stream.

Use a current SDK when you fork this. Modern clients do not negotiate their way down to legacy-era servers on their own, and an old server is a door today's agents can't open.

Deploy to your own VPS

cp context.example/deploy.env context/deploy.env   # server, domain, names
$EDITOR context/deploy.env
./deploy.sh

deploy.sh runs the tests, previews what would be deleted on production (a gate in front of --delete), mirrors the working tree, loads context/deploy.env into the environment and rebuilds the container, then smoke-tests MCP_PUBLIC_URL. .env and data/ never cross the border: secrets and the journal live on production only.

docker-compose.yml assumes traefik — domain, router priority and certresolver all come from deploy.env. The router priority must be higher than that of the site on the same domain, or requests to /mcp go to the site and the agent gets an HTML 404 instead of JSON-RPC.

No traefik? Drop the labels block, publish the port and put anything that speaks TLS in front.

Check a running server

# the tool list
curl -s localhost:3000/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq -r '.result.tools[].name'

# a file tool
curl -s localhost:3000/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_profile","arguments":{}}}' \
  | jq -r '.result.content[0].text'

A hand-written curl in the modern era, with no Mcp-Method / Mcp-Name, gets -32020 back. That is the correct answer, not a breakage. The real check is an official MCP client.

Rules worth keeping

  • Public facts only. Everything in context/ goes to any agent, with no authorization. Nothing about family, health, finances or deal sizes.

  • No prices at all — "pricing on request, use leave_message". Otherwise your rate card ends up in someone else's cache and outlives every correction you make.

  • Context is read at process start. Edit a file, restart the server.

  • No tool executes the content of its arguments: the question in ask is sent declared as data, and a leave_message goes to a file and to a notification with no markup. Don't break that.

  • Read every version with your own eyes before it ships. An agent that assembles your pack from your notes will happily carry something private across.

Layout

context.example/  template for the personal layer (context/ is yours, not in git)
src/config.js     config: defaults, context/config.json, env
src/context.js    your files → sections and the tool list
src/mcp.js        the protocol: tools, schemas, journal, mounting /mcp
src/lib.js        state on disk, budget cap, validation, Anthropic + notifications
src/discovery.js  server-card.json and llms.txt from the config
src/server.js     express, client IP behind a proxy, rate-limit buckets, startup
test.js           node:test on fixtures, everything outbound mocked
data/             journal.jsonl, inbox.jsonl, budget.json (not in git)

Two dependencies: express and the MCP SDK. No ORM, no database, no SMTP client — mail goes out over plain fetch. Keep it that way.

License

MIT — take it, change it, run it, sell services on top of it. Attribution belongs in the code (the copyright line in LICENSE), not on your site. What's in your context/ is yours either way: the code never touches it.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Exposes a personal markdown-based second brain (Obsidian-style) as an MCP server, enabling agents to search, read, and write notes with privacy controls.
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    A local MCP server that semantically searches Markdown context files and extension context links, providing tools for context_search, context_read, and context_status.
  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides a file-first personal memory layer for AI agents, enabling them to store and retrieve memories as markdown files with an SQLite index. The MCP server offers read-only search by default, with optional write tools for manual memory addition and conflict resolution.
    11
    MIT

Latest Blog Posts

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/postnikov/hcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server