private-journal-mcp
The private-journal-mcp server lets you store, search, and retrieve private journal entries locally, with optional Git sync.
write_journal: Write a new journal entry with content, organized into one of six sections:reflections,observations(default),project_notes,user_context,technical_insights, orworld_knowledge.search_journal: Perform semantic search over entries using natural language queries (via multilingual embeddings), with optional filters for section, result limit, and minimum relevance score.read_journal: Retrieve the full Markdown content of a specific journal entry by its file path.list_journal: Browse recent entries chronologically, filterable by number of days or result limit, returning paths, dates, and sections.Local-first storage: All entries and embedding inference run locally with no external API calls required.
Optional Git sync: Automatically push/pull journal changes to a private Git remote, with conflict resolution, retry logic, and automatic commits on write.
SessionStart hook: When installed as a plugin, automatically syncs with the configured Git remote at the start of each session, enabling multi-device use.
Enables automatic syncing of journal entries to a Git remote, with commit, pull --rebase, and push on write, as well as a session-start sync hook.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@private-journal-mcpwrite a reflection on today's team meeting"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
private-journal-mcp
An MCP server that stores journal entries in local files and searches them semantically with multilingual embeddings. Search and embedding inference run locally; the embedding model is downloaded and cached once on first use. Optionally, entries can be auto-synced to a Git remote.
Tools
write_journalStores an entry from a required
titleandcontent.Optional arg:
section(reflections,observations,project_notes,user_context,technical_insights,world_knowledge)sectiondefaults toobservations.
search_journalPerforms semantic search over stored entries.
Required arg:
queryOptional args:
limit,section
read_journalReads a full individual Markdown entry by
path.
list_journalLists recent entries.
Optional args:
limit,days
Related MCP server: mesh-memory
Storage Locations
Markdown front matter
New entries use YAML front matter with the caller-provided title and a canonical UTC
created_at value. Legacy date and timestamp fields are converted by the data revision
migration; they are not written for new entries.
---
title: 검색 결과 캐시 오류 수정
created_at: 2026-06-25T12:34:56.789Z
---Journal data
Priority order:
PRIVATE_JOURNAL_PATH$XDG_DATA_HOME/private-journal~/.local/share/private-journal
Model cache
Priority order:
$XDG_CACHE_HOME/private-journal/models~/.cache/private-journal/models
The default embedding model is Xenova/multilingual-e5-small.
Install / Build
npm install
npm run buildRun locally:
node dist/index.jsThe first upgrade from the legacy .embedding sidecars requires a one-time
index migration. It creates the SQLite vector index, verifies it, and removes
sidecars only after success:
node dist/index.js migrate-indexWithout a Git remote, the sync subcommand still runs local data migrations and
incrementally indexes changed Markdown files, but does not perform Git operations.
node dist/index.js syncInstall as a Plugin (recommended)
This repo is a plugin for both Claude Code (.claude-plugin/plugin.json) and Codex
(.codex-plugin/plugin.json). Installing it registers the MCP server and the
SessionStart sync hook in one step — no manual settings.json/config.toml edits.
The MCP server is declared inline in each manifest's mcpServers field (not a root
.mcp.json, which would auto-load as a project-scope server). It resolves the plugin's
own install path via ${CLAUDE_PLUGIN_ROOT} (Claude Code) / a ./bin relative path
with cwd (Codex). The bundled hooks/hooks.json resolves paths the same way.
Build first so dist/ exists, then install:
npm install && npm run buildClaude Code:
/plugin install /absolute/path/to/private-journal-mcpWhen Claude Code enables the plugin, it asks for an optional Git remote.
Enter a remote URL to enable Git sync, or leave it blank for local-only storage.
To change it later, open the plugin configuration dialog and select
private-journal-mcp.
Codex:
codex plugin install /absolute/path/to/private-journal-mcpOpenCode:
Published package, in opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["private-journal-mcp"]
}Local checkout:
npm install && npm run build
mkdir -p .opencode/plugins
ln -sfn "$(pwd)/opencode-plugin.mjs" .opencode/plugins/private-journal-mcp.jsThe symlink keeps the plugin's relative dist/ import rooted at the checkout.
OpenCode automatically loads plugins from the project .opencode/plugins/
directory; use ~/.config/opencode/plugins/ for a global local plugin instead.
The plugin exposes write_journal, search_journal, read_journal, and
list_journal as native OpenCode tools. It uses the same local data path and
Git remote environment variables as the MCP server. Set
PRIVATE_JOURNAL_GIT_REMOTE for Git sync; leave it unset for local-only storage.
Manual MCP registration (without the plugin)
claude mcp add private-journal -- node /absolute/path/to/private-journal-mcp/dist/index.jsGit Sync (optional)
Claude Code passes this setting to the plugin as
CLAUDE_PLUGIN_OPTION_GIT_REMOTE. Existing Codex and manual MCP setups can
continue to use PRIVATE_JOURNAL_GIT_REMOTE:
export PRIVATE_JOURNAL_GIT_REMOTE="git@github.com:youruser/my-journal.git"Recommended prerequisites:
You must already be authenticated for that remote via
gh auth loginor equivalent Git credentials.Do not put credentials or tokens in the remote URL. Use SSH or a Git credential helper instead.
Setting up the remote
Create a private repo and point the server at it:
gh repo create <your-account>/private-journal-vault --private
export PRIVATE_JOURNAL_GIT_REMOTE=git@github.com:<your-account>/private-journal-vault.gitIf the remote is empty, the data directory is initialized in place (and stays silent — no error — on the first sync). If the remote already has entries, it is cloned and merged with whatever is already local.
Behavior:
A
write_journalsave returns after Markdown and the SQLite index are durable. Git pull/commit/rebase/push runs in a detached background process.Push is retried up to 5 times (
PUSH_RETRY_LIMIT) with exponential backoff (100/200/400/800ms), which lets several machines writing at once converge without losing entries.Network commands (
fetch,push,ls-remote,clone) time out after 10s, tunable viaPRIVATE_JOURNAL_GIT_TIMEOUT_MS. Local rebase is never interrupted — cutting a rebase short would leave the repo unable to commit.If a previous run left an interrupted rebase, the next sync resolves it, or aborts it, or as a last resort force-cleans unreadable rebase state. Local commits are preserved either way.
Within one machine, sync is serialized by a
.private-journal-sync.lockfile in the data directory. If another session already holds it, this run is skipped (not queued); the next run picks up whatever is pending. Locks older than 120s are considered stale and stolen.All Claude Code and Codex sessions for one OS user share one embedding worker and one active model inference. Query embeddings have priority over queued passage backfill. SQLite WAL allows concurrent index readers and short writes.
Markdown and Git remain canonical. SQLite is disposable derived state at
.private-journal-index.sqlite; its WAL/SHM files are excluded from Git.A missing or incomplete SQLite index is backfilled from Markdown once. Once it is complete, startup processes only Git-reported changed paths.
Reads (
search_journal,list_journal,read_journal) do not pull. A session sees the snapshot from when it started, plus anything it wrote itself. Changes from other machines arrive at the next session start or the next write.node dist/index.js syncpulls and pushes any pending commits before a session starts.
Git commit identity
Automatic sync commits use journal <journal@localhost> by default. Override
the identity for the MCP process with GIT_NAME and GIT_EMAIL:
{
"env": {
"GIT_NAME": "your-name",
"GIT_EMAIL": "your-github-email@example.com"
}
}The same values are used for both the Git author and committer. Use an email linked to your GitHub account if these commits should count toward your contribution graph.
Data-format compatibility
When the same journal is used on multiple computers, an app that upgrades the journal data format records the new version in the journal. Older app versions then stop before reading or writing and tell you to update, instead of risking an incompatible change.
The 1 -> 2 data migration rewrites legacy YAML front matter in a staging directory
and preserves the original data if any entry cannot be converted.
SessionStart sync hook
When installed as a plugin, the SessionStart sync hook is registered automatically
(see hooks/hooks.json) — nothing to configure. Without a configured remote it
still runs local data migrations and incremental index backfill.
The hook uses sync --background, so SessionStart returns immediately while the
existing sync process continues in the background. To run the same sync in the
foreground, use node dist/index.js sync.
To wire it up manually instead, add to ~/.claude/settings.json:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "node /absolute/path/to/private-journal-mcp/dist/index.js sync --background"
}
]
}
]
}
}Conflict Handling
Distinct entries mostly coexist automatically because filenames include a microsecond suffix.
When two entries share a filename, the one with the larger timestamp derived from frontmatter
created_atwins.If the derived timestamp is identical, the local version takes precedence.
The SQLite row for the adopted Markdown is regenerated from the source file.
Legacy
.embeddingfiles are not part of runtime conflict handling; runmigrate-indexonce to convert and remove them.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityAmaintenancePersistent memory for AI agents with sectioned entries (project/user/context/etc), semantic search, CLI, and per-project scope. Local JSON, zero config, no server required.Last updated124MIT
- Alicense-qualityDmaintenanceSelf-hosted semantic memory for AI agents. Save worklogs, decisions, and notes via MCP, then recall them across sessions by meaning rather than keyword. Backed by Postgres + pgvector with local embeddings (multilingual-e5-base).Last updated1MIT
- AlicenseAqualityCmaintenanceEnables semantic search over local markdown note collections using vector embeddings, with real-time file watching and zero-config setup.Last updated4MIT
- Alicense-qualityAmaintenanceProvides a persistent, versioned, and searchable context store for AI agents with local embedding and hybrid search.Last updated283MIT
Related MCP Connectors
Persistent memory for AI agents. Search, store, and recall across sessions.
Long-term memory for AI assistants. Hybrid retrieval, query expansion, auto-topics.
User-owned memory for AI agents, Copilot, Claude, IDEs, CLIs, and chat apps over remote MCP.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/baleen37/private-journal-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server