Personal MCP Server
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., "@Personal MCP Serversearch my inbox for unread emails from John"
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.
Personal MCP Server
A private, single-user MCP (Model Context Protocol) server that gives AI agents access to your email, Slack notifications, and a personal knowledge base ("soul docs"). Written in TypeScript, runs over Streamable HTTP or stdio.
Capabilities at a glance:
Search and read Gmail or any IMAP mailbox
Send email via Gmail API or custom SMTP (with a prepare→confirm safety flow)
Post Slack notifications via incoming webhooks
CRUD a personal knowledge base backed by SQLite or Turso/libSQL
Configure all credentials at runtime via MCP tools — no
.envediting required
Tools
Mail — Gmail
Tool | Summary |
| Search Gmail with typed filters (from, to, subject, text, label, unread, date range). Returns summaries. |
| Fetch a full Gmail message by ID — headers, plain text body, HTML body. |
Mail — Custom IMAP / SMTP
Tool | Summary |
| Search a custom IMAP mailbox with the same typed filters. |
| Fetch a full message from the custom IMAP mailbox by UID. |
| Validate and stage an email draft. Returns a confirmation ID; does not send. |
| Send a previously staged email using its confirmation ID. The two-step flow prevents accidental sends. |
Slack
Tool | Summary |
| Post a message (plain text or Block Kit blocks) to a configured Slack incoming webhook. |
Soul Docs (personal knowledge base)
Tool | Summary |
| Search docs by full-text query (title, content, source) or filter by tag. Returns newest first. |
| Create or update a doc. Pass an |
Setup (runtime configuration)
When MCP_ENABLE_SETUP_TOOLS=true (the default), these tools let you configure every service at runtime and test connections immediately. All changes are in-memory — they override .env values but reset on restart.
Tool | Summary |
| Show which services are configured (no secrets exposed). |
| Set a Turso/libSQL URL or local file path, then test the connection. Accepts |
| Generate a Google OAuth authorization URL. Optionally override client ID, secret, and redirect URI. |
| Exchange the OAuth authorization code for a refresh token and store it in the runtime config. |
| Set IMAP host, port, credentials, and mailbox — then test the connection. |
| Set SMTP host, port, credentials, and default from address — then verify the connection. |
| Set a Slack incoming webhook URL and send a test notification. |
Set MCP_ENABLE_SETUP_TOOLS=false to disable all setup tools and use .env-only configuration.
Related MCP server: mcp-email
Configuration reference
Every setting can be provided via .env or overridden at runtime by the corresponding setup_* tool.
Runtime
Variable | Default | Notes |
|
|
|
|
| HTTP listen port |
|
| Set to |
| — | Optional. When set, requires |
|
| Comma-separated origins or |
|
| Set to |
Database
Variable | Default | Notes |
|
|
|
| — | Required for Turso remote databases |
| — | Sync endpoint for embedded replicas |
| — | Sync interval in ms for embedded replicas |
Gmail OAuth
Variable | Required | Notes |
| Yes | From Google Cloud Console |
| Yes | From Google Cloud Console |
| Yes | e.g. |
| — | Obtained via OAuth flow; stored at runtime by |
| — | Optional CSRF state string |
Scopes requested: gmail.readonly, gmail.send.
Custom IMAP
Variable | Default | Notes |
| — | IMAP server hostname |
|
| |
|
| TLS |
| — | Usually the full email address |
| — | App password recommended |
|
|
Custom SMTP
Variable | Default | Notes |
| — | SMTP server hostname |
|
| |
|
|
|
| — | Optional |
| — | Optional |
| — | Default sender address |
|
| Expiry for staged-but-unsent emails |
Slack
Variable | Notes |
| Incoming webhook URL |
Getting started
Path A: .env (static config)
npm install
cp .env.example .env
# Edit .env with your credentials
npm run build
npm start # HTTP on port 3000
# or: npm run start:stdio # stdio transportPath B: setup tools (runtime config)
npm install
npm run build
npm startThen, from your MCP client, call the setup_* tools in any order:
setup_database— point to your DB (or skip;file:local.dbis the default)setup_gmail_oauth_start→ open the URL →setup_gmail_oauth_completewith the codesetup_custom_mail_imap+setup_custom_mail_smtp— configure mailsetup_slack_webhook— configure Slacksetup_status— verify everything is wired up
Endpoints
Method | Path | Description |
|
| MCP Streamable HTTP — requires |
|
| Health check |
|
| Start Gmail OAuth flow (HTTP mode only) |
|
| Gmail OAuth callback (HTTP mode only) |
Architecture
src/
├── config.ts Env parsing (Zod), defaults, setConfigValue helper
├── index.ts Entry point — loads config, creates services, starts transport
├── runtime.ts Service factory — wires up all services with shared config
├── server.ts MCP server — registers all tools, conditionally includes setup tools
├── setup-tools.ts Runtime config tools — mutate config, test connections
├── tools.ts Core tool handlers — email, Slack, soul docs
├── errors.ts Custom error classes
├── logger.ts Structured JSON logging
├── types.ts Shared TypeScript interfaces
├── transports/
│ ├── http.ts Streamable HTTP transport + OAuth callback routes
│ └── stdio.ts Stdio transport
├── services/
│ ├── database.ts Turso/libSQL client — soul_docs, send_confirmations, audit_log
│ ├── gmail-api.ts Thin Gmail REST wrapper via google-auth-library + fetch (replaces googleapis)
│ ├── gmail.ts Gmail service — search, read, send, OAuth
│ ├── custom-mail.ts IMAP (imapflow) + SMTP (nodemailer) — search, read, send
│ ├── email-sender.tsComposite — delegates to Gmail or SMTP based on provider
│ └── slack.ts Slack incoming webhook via fetch
└── utils/
├── email.ts MIME builder, base64url helpers
└── mcp.ts jsonText() response formatterKey design choices:
Shared mutable config — All services hold a reference to the same
AppConfigobject. Setup tools mutate it directly; lazy services (Gmail, IMAP, SMTP, Slack) pick up changes on the next call. OnlyDatabaseServiceneeds an explicitreconnect()since it creates the libSQL client eagerly.Audit logging — Every tool call is logged to the
audit_logtable with success/failure, args (secrets redacted), and a timestamp.Two-step email send —
email_prepare_sendstages a draft (stored in DB with a TTL),email_confirm_sendconsumes it. Prevents accidental sends and gives the agent a chance to review.OAuth works in both transports — Streamable HTTP mode has dedicated callback routes; stdio mode uses the
setup_gmail_oauth_start/setup_gmail_oauth_completetools where the user copies the code manually.
Database
Three tables are created automatically on startup:
Table | Purpose |
| Personal knowledge base — title, content, tags, source, metadata, timestamps |
| Staged email drafts with expiry and single-use tokens |
| Immutable record of every tool invocation |
Local vs remote
# Local SQLite (relative to working directory)
TURSO_DATABASE_URL=file:local.db
# Local SQLite (absolute — for pods/containers that need persistent state)
TURSO_DATABASE_URL=file:/data/my-server.db
# Remote Turso DB
TURSO_DATABASE_URL=libsql://your-db.turso.io
TURSO_AUTH_TOKEN=your-tokenPlain paths passed to setup_database are auto-prefixed with file: — so /data/db.sqlite becomes file:/data/db.sqlite.
Deployment
Docker (GHCR)
Pre-built multi-arch images (linux/amd64, linux/arm64) are published to GitHub Container Registry on every push to main.
# Pull and run
docker run -d --name personal-mcp \
--env-file .env \
-p 3000:3000 \
-v "$(pwd)/data:/app/data" \
ghcr.io/olamide226/personal-mcp-server:latest
# Or with a specific version
docker run -d --name personal-mcp \
--env-file .env \
-p 3000:3000 \
-v "$(pwd)/data:/app/data" \
ghcr.io/olamide226/personal-mcp-server:v1.0.0Docker Compose (local build)
docker compose up --buildTo pull from GHCR instead of building locally, swap the build: . line in docker-compose.yml for:
image: ghcr.io/olamide226/personal-mcp-server:latestBare metal / pod
npm ci --omit=dev
npm run build
MCP_HOST=0.0.0.0 MCP_PORT=3000 TURSO_DATABASE_URL=file:/data/server.db node dist/index.jsFor production, set a strong MCP_BEARER_TOKEN and restrict MCP_ALLOWED_ORIGINS to your client origin(s).
Image tags
Tag | When |
| Every push to |
| Every push to |
| Version tag (e.g. |
| Major.minor alias |
| Major alias |
Semantic releases
Merging a PR to main triggers the release.yml workflow which parses conventional commits since the last tag:
Commit prefix | Version bump |
| minor |
| patch |
| major |
If any meaningful commits are found, a vX.Y.Z tag is created and pushed — which triggers a Docker build with semver tags.
Health check
curl http://localhost:3000/healthz
# {"ok":true,"name":"personal-mcp-server","version":"0.1.0"}Development
npm install
npm run dev # tsx watch — auto-reload on changes
npm run build # tsc
npm test # vitest
npm run lint # eslint
npm run typecheck # tsc --noEmit (includes tests/)This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- 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/olamide226/personal-mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server