selfmcp

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.

Category
Visit Server

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, so npm install works out of the box on any machine, including one without build tools installed
  • @modelcontextprotocol/sdk for the MCP protocol itself (Streamable HTTP transport) and for the full OAuth 2.1 authorization server implementation (mcpAuthRouter), including Dynamic Client Registration
  • jose for 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:

  1. Discovery — clients fetch /.well-known/oauth-authorization-server and /.well-known/oauth-protected-resource/mcp to learn where to register and authenticate.
  2. Registration (DCR)POST /register lets any client dynamically obtain a client_id (RFC 7591), no pre-registration or manual API key needed.
  3. AuthorizationGET /authorize renders 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.
  4. Consent decisionPOST /consent issues a one-time authorization code (stored in SQLite, 60s TTL by default) and redirects back to the client's redirect_uri.
  5. Token exchangePOST /token exchanges 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).
  6. Calling the serverPOST /mcp requires Authorization: Bearer <access_token>, verified by requireBearerAuth against 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.

  1. Set BASE_URL to your real HTTPS URL (OAuth requires HTTPS in production; only localhost is exempted).
  2. Set TOKEN_SIGNING_SECRET to a real random secret (never reuse the .env.example placeholder).
  3. Make sure the volume/disk backing DATABASE_PATH persists across restarts.
  4. Run npm run build && npm run seed && npm start, or use the included Dockerfile.

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.yaml works around this by running npm run seed as 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_client error 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

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.

Official
Featured
TypeScript
Magic Component Platform (MCP)

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.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

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.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

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.

Official
Featured
TypeScript
Kagi MCP Server

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.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

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.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured