journal-mcp-server
A reference MCP server that enables AI agents to read, write, search, and analyze personal journal entries with mood scores over HTTP, featuring OAuth 2.0 and a swappable storage backend.
README
journal-mcp-server
A reference implementation of a remote MCP (Model Context Protocol) server extracted from a personal journaling app. It exposes journal read/write operations as typed tools that any MCP-compatible agent (Claude, GPT, etc.) can call over HTTP.
Built to demonstrate agent tool design, permission scoping, and OAuth 2.0 integration in a real-world single-user scenario.
What this is
The journaling app this was extracted from stores daily entries with a happiness score (1–10). This MCP layer lets an AI assistant — connected via claude.ai or Claude Desktop — read, write, and analyse those entries on behalf of the owner.
The interesting design challenges this solves:
- One owner, one agent. No multi-tenancy required, but the auth model still uses proper OAuth 2.0 so it works with Claude's connector UI without any custom SDK.
- Bearer token pinned to OAuth secret. The issued
access_tokenis the same value as the bearer token the/mcproute checks, so the OAuth consent flow and the API auth layer are a single secret — no separate token store. - Stateless transport. Each HTTP request creates a fresh MCP session. No WebSocket, no persistent server-side session.
- Storage abstracted behind an interface. The
JournalStoreinterface lets you swap the backing store (in-memory → SQLite → Postgres) without touching the tool definitions.
Architecture
claude.ai / Claude Desktop
│
│ POST /mcp (Bearer token)
▼
┌─────────────────────────────────────────┐
│ Express app │
│ │
│ mcpAuth middleware │ ← validates bearer token,
│ │ │ sets req.mcpUserId
│ ▼ │
│ handleMcpRequest() │ ← creates MCP session per request
│ │ │
│ ▼ │
│ McpServer (Streamable HTTP transport) │
│ │ │
│ ▼ │
│ JournalStore interface │ ← swappable backing store
└─────────────────────────────────────────┘
OAuth endpoints (same server):
GET /.well-known/oauth-authorization-server → RFC 8414 metadata
GET /oauth/authorize → consent page
POST /oauth/authorize → issue auth code
POST /oauth/token → exchange code for token
Tools
| Tool | Description |
|---|---|
create_entry |
Create a journal entry for a given date (one per day). Returns an error if an entry already exists for that date. |
list_recent_entries |
Return the N most recent entries, newest first. |
get_entry |
Fetch a single entry by date (YYYY-MM-DD). |
search_entries |
Case-insensitive keyword search across all entry content, newest first. |
get_mood_summary |
Aggregate happiness scores across a date range — average, min, max, and a count by score tier (low/mid/high). |
All tools are defined with Zod schemas so the MCP SDK generates accurate JSON Schema for the agent's tool-use call.
Auth model
Bearer token (primary)
Every POST /mcp request must include:
Authorization: Bearer <MCP_TOKEN>
On a 401 the server returns a WWW-Authenticate header pointing to the OAuth metadata URL, which allows MCP clients that support dynamic discovery to kick off the OAuth flow automatically.
OAuth 2.0 (for claude.ai connector UI)
Claude's connector UI only accepts OAuth credentials, not raw bearer tokens. This server implements a minimal Authorization Code flow (RFC 6749) with PKCE (RFC 7636) and server metadata (RFC 8414):
- Claude fetches
/.well-known/oauth-authorization-serverto discover endpoints. - Claude redirects the user to
/oauth/authorize— a consent page served by this server. - The user clicks Allow. The server issues a one-time auth code (5-minute TTL).
- Claude exchanges the code at
/oauth/token. The server validates PKCE and returnsMCP_TOKENas theaccess_token. - Claude uses the token as a bearer token on all subsequent
/mcpcalls.
Credentials to enter in Claude's connector UI:
| Field | Value |
|---|---|
| OAuth Client ID | claude (or whatever you set OAUTH_CLIENT_ID to) |
| OAuth Client Secret | your MCP_TOKEN value |
| Remote MCP Server URL | https://your-domain.com/mcp |
Transport
Streamable HTTP (MCP spec §3.2) — the server creates a fresh StreamableHTTPServerTransport for every request. This is the simplest MCP transport: no WebSocket handshake, no persistent connection, works through any HTTP reverse proxy.
Running locally
# 1. Install dependencies
npm install
# 2. Configure secrets
cp .env.example .env
# Edit .env — set MCP_TOKEN, MCP_USER_EMAIL, OAUTH_CLIENT_ID
# 3. Start the dev server (auto-reloads on file changes)
npm run dev
The server starts on port 3000 by default. Test with:
# Should return 401 with WWW-Authenticate header
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
# Should return tool list
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your MCP_TOKEN>" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
# OAuth metadata
curl http://localhost:3000/.well-known/oauth-authorization-server
Swapping the backing store
The JournalStore interface in src/journalStore.ts is the only contract the MCP tools depend on. To use a real database:
- Implement
JournalStoreagainst your ORM or query builder of choice. - Pass your implementation to
handleMcpRequest(store, req, res)insrc/server.ts.
The InMemoryJournalStore included here is suitable for local development and testing — data is lost on server restart.
Environment variables
| Variable | Required | Description |
|---|---|---|
MCP_TOKEN |
✅ | Bearer token for /mcp. Also issued as the OAuth access_token. |
MCP_USER_EMAIL |
✅ | Owner identifier — used as the userId passed to JournalStore. |
OAUTH_CLIENT_ID |
✅ | OAuth client_id (e.g. claude). |
PORT |
optional | HTTP port, defaults to 3000. |
Related projects
- Riley-Claude-Skills — Riley's versioned Claude Skills portfolio: reusable instruction sets for PM workflows, personal finance, and more.
License
MIT
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.
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.
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.
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.