selfmcp
A personal MCP server that exposes your profile (bio, skills, projects, work experience) as structured tools for any MCP-compatible AI client, secured with OAuth 2.1 and Dynamic Client Registration.
README
selfMCP
A personal Model Context Protocol (MCP) server. It exposes a profile — bio, skills, projects, work experience — as structured tools that any MCP-compatible AI client (Claude, ChatGPT, Codex) can query directly, secured with OAuth 2.1 and Dynamic Client Registration (DCR, RFC 7591) so clients can connect without any manually-issued API key.
Stack
- Node.js + TypeScript + Express
- SQLite via
node:sqlite(Node's built-in driver, stable as of Node 22.5+) — no native addon compilation required, sonpm installworks out of the box on any machine, including one without build tools installed @modelcontextprotocol/sdkfor the MCP protocol itself (Streamable HTTP transport) and for the full OAuth 2.1 authorization server implementation (mcpAuthRouter), including Dynamic Client Registrationjosefor signing/verifying access tokens (stateless JWTs)- EJS for the landing page and OAuth consent screen — no frontend build step
Project layout
src/
config/env.ts Environment variable loading + validation (zod)
db/ SQLite schema, seed script, and typed repository functions
mcp/
server.ts Builds an McpServer and registers tools
tools/ get_bio, list_skills, list_projects, search_projects, list_experience
router.ts Mounts the Streamable HTTP transport at POST /mcp
oauth/
store.ts SQLite-backed OAuthRegisteredClientsStore (handles DCR persistence)
provider.ts Implements the SDK's OAuthServerProvider interface
tokens.ts JWT access tokens + hashed, rotating refresh tokens
consent.ts Signs/verifies the short-lived consent-screen round trip
web/
routes.ts Landing page (GET /) and consent decision handler (POST /consent)
views/ landing.ejs, consent.ejs
app.ts Wires everything into one Express app
server.ts Entry point
public/style.css Landing page styling
Getting started
npm install
cp .env.example .env
Generate a signing secret and put it in .env as TOKEN_SIGNING_SECRET:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
Edit src/db/seed.ts with your real name, bio, skills, projects, and experience, then run:
npm run seed
npm run dev
The server listens on http://localhost:3000 by default. Visit it in a browser to see the landing
page, or run npm run inspector in another terminal to poke the MCP endpoint directly with the
official MCP Inspector.
Scripts
| Script | Purpose |
|---|---|
npm run dev |
Run with hot reload (tsx watch) |
npm run build |
Compile TypeScript to dist/ |
npm start |
Run the compiled build (node dist/server.js) |
npm run seed |
(Re-)populate the database from src/db/seed.ts |
npm run typecheck |
tsc --noEmit |
npm run lint |
ESLint |
npm run format |
Prettier (writes) |
npm run inspector |
Launch the MCP Inspector dev tool |
How the OAuth / DCR flow works
This server is both the MCP resource server and its own OAuth 2.1 authorization server, built on
the SDK's mcpAuthRouter:
- Discovery — clients fetch
/.well-known/oauth-authorization-serverand/.well-known/oauth-protected-resource/mcpto learn where to register and authenticate. - Registration (DCR) —
POST /registerlets any client dynamically obtain aclient_id(RFC 7591), no pre-registration or manual API key needed. - Authorization —
GET /authorizerenders a consent screen (src/web/views/consent.ejs). The pending request (client, redirect URI, PKCE challenge, scope, state) is carried in a short-lived signed JWT in a hidden form field rather than server-side session state, since there's nothing else to persist between the redirect and the user's click. - Consent decision —
POST /consentissues a one-time authorization code (stored in SQLite, 60s TTL by default) and redirects back to the client'sredirect_uri. - Token exchange —
POST /tokenexchanges the code (with PKCE verification handled by the SDK itself) for an access token (a signed, stateless JWT, 1 hour TTL by default) and a refresh token (opaque, hashed at rest, single-use with rotation — reusing a spent refresh token fails immediately). - Calling the server —
POST /mcprequiresAuthorization: Bearer <access_token>, verified byrequireBearerAuthagainst the same provider.
Trade-off worth knowing: because access tokens are stateless JWTs, they can't be individually revoked before they expire — only refresh tokens are revocable. Given the short (1 hour) default TTL, this is a standard and acceptable trade-off, not an oversight.
Connecting an MCP client
Claude Desktop / Claude Code — add a remote connector, or in claude_desktop_config.json:
{
"mcpServers": {
"selfmcp": { "url": "https://your-deployed-url/mcp" }
}
}
ChatGPT / Codex — add a custom connector using the same MCP endpoint URL from each app's connector settings screen.
All three should auto-discover the OAuth metadata and complete DCR + the consent flow on first connection.
Deployment
Any host that can run a persistent Node process works (Render, Railway, Fly.io, a small VM). Avoid serverless/functions platforms — the Streamable HTTP transport and SSE want a long-lived connection, and the SQLite file needs a persistent disk.
- Set
BASE_URLto your real HTTPS URL (OAuth requires HTTPS in production; onlylocalhostis exempted). - Set
TOKEN_SIGNING_SECRETto a real random secret (never reuse the.env.exampleplaceholder). - Make sure the volume/disk backing
DATABASE_PATHpersists across restarts. - Run
npm run build && npm run seed && npm start, or use the includedDockerfile.
Render (free tier)
A render.yaml Blueprint is included: on Render, New + → Blueprint, connect this repo, and it
configures the service automatically (build command, start command, and a generated
TOKEN_SIGNING_SECRET).
The free plan has no persistent disk — its filesystem resets not just on every deploy but every time the service wakes up from idle (it spins down after ~15 minutes of inactivity). Two consequences of that, and how this project handles them:
- Profile/skills/projects/experience data would be lost on every wake-up.
render.yamlworks around this by runningnpm run seedas part of the start command (not just at build time), so this static data is restored every time the process boots. - OAuth client registrations and refresh tokens are dynamic, so they can't be "reseeded" the
same way — they're genuinely wiped on each wake-up. In practice this just means an MCP client
that registered before a spin-down will get an
invalid_clienterror on its next call and needs to redo Dynamic Client Registration, which is expected, spec-compliant behavior for a client talking to a server that doesn't guarantee permanent client storage — not a bug. If this matters for your use case, move to a paid Render plan with a persistent disk instead.
Known dependency advisory
npm audit flags a moderate path-traversal issue in @hono/node-server (a transitive dependency
of @modelcontextprotocol/sdk's static-file-serving helper). This project never uses that
static-serving code path — only the SDK's protocol/transport and auth modules — so it isn't
exploitable here. Revisit this once the SDK bumps its @hono/node-server dependency.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.