Content
# MCP Note App
A full-stack note-taking application with a React UI, Express/SQLite backend, real-time Server-Sent Events, JWT auth, and AI agent integration through the Model Context Protocol (MCP).
## What You Get
- Create, edit, delete, tag, and search notes in a modern React interface.
- Live UI refreshes through SSE when notes change through REST or MCP tools.
- JWT-protected REST note routes with local API-key login.
- MCP SSE transport for AI clients and a Python Gemini-powered CLI agent.
- SQLite persistence via a repository layer.
## Architecture
```text
┌─────────────────┐ MCP/SSE ┌──────────────────┐
│ Python Agent │ ◄──────────────► │ │
│ (Gemini AI) │ │ Node.js Server │
│ │ Tools │ (MCP + REST) │
└─────────────────┘ │ │
│ SQLite DB │
┌─────────────────┐ REST/SSE │ │
│ React Frontend │ ◄──────────────► │ │
│ (Vite+Tailwind) │ └──────────────────┘
└─────────────────┘
```
- **Backend**: TypeScript, Express, MCP server, SQLite (`better-sqlite3`), Zod, JWT.
- **Frontend**: React 18, Vite, Tailwind CSS, TanStack React Query, Zustand, SSE.
- **Agent**: Python MCP client connected to Google Gemini (`gemini-2.5-flash` by default).
## Prerequisites
- Node.js 20+ recommended.
- npm.
- Python 3.10+ recommended.
- A Gemini API key for the optional Python agent.
## Quick Start
### 1. Install dependencies
Install everything from the repository root:
```bash
npm run install:all
```
Or install each layer manually:
```bash
npm install --prefix server
npm install --prefix client-ui
python3 -m venv agent/.venv
agent/.venv/bin/python -m pip install -r agent/requirements.txt
```
Install the root orchestration dependency only if needed:
```bash
npm install
```
### 2. Configure environment
Create `server/.env`:
```env
PORT=3000
NODE_ENV=development
JWT_SECRET=change-this-to-a-long-random-secret-at-least-32-chars
JWT_EXPIRY=15m
JWT_REFRESH_EXPIRY=7d
DATABASE_PATH=./data/notes.db
CORS_ORIGIN=http://localhost:5173
AUTH_TOKEN=dev-auth-token-change-in-production
```
Create `agent/.env` if you plan to use the Gemini agent:
```env
GEMINI_API_KEY=your-gemini-api-key
```
Optional stdio transport variables for the agent:
```env
MCP_STDIO_CMD=npx
MCP_STDIO_ARGS=tsx,server/src/index.ts
```
> Do not commit `.env` files, API keys, JWT secrets, or local SQLite database files.
### 3. Start the app
Start backend and frontend together:
```bash
npm run dev
```
Or run them separately:
```bash
npm run dev:server
npm run dev:client
```
Then open the frontend at `http://localhost:5173`.
### 4. Start the AI agent
Make sure the backend is running first, then run:
```bash
npm run dev:agent
```
One-shot example:
```bash
agent/.venv/bin/python agent/agent.py --one-shot "Create a note titled Demo with content Hello from Gemini"
```
## Scripts
Run these from the repository root unless noted otherwise.
| Command | Description |
|--------|-------------|
| `npm run install:all` | Install server, client, and Python agent dependencies. |
| `npm run install:agent` | Create `agent/.venv` and install Python dependencies. |
| `npm run dev` | Start backend and frontend concurrently. |
| `npm run dev:server` | Start only the Express/MCP server. |
| `npm run dev:client` | Start only the Vite frontend. |
| `npm run dev:agent` | Start the Python Gemini MCP agent. |
| `npm run build` | Build server and client. |
| `npm run build --prefix server` | Type-check/build backend. |
| `npm run build --prefix client-ui` | Type-check/build frontend. |
| `npm run test --prefix client-ui` | Run frontend unit tests with Vitest. |
| `npm run test:e2e --prefix client-ui` | Run Playwright end-to-end tests. |
## Project Structure
```text
mcp-note-app/
├── server/ # Express REST API, MCP server, SQLite persistence
│ ├── src/
│ │ ├── auth/ # JWT signing and verification
│ │ ├── config/ # Environment validation
│ │ ├── db/ # SQLite connection and schema
│ │ ├── events/ # Event bus and SSE handlers
│ │ ├── mcp/ # MCP tool server and transports
│ │ ├── middleware/ # Auth, CORS, error handling
│ │ ├── repositories/ # NoteRepository + SQLite implementation
│ │ ├── routes/ # Notes REST routes and live updates
│ │ ├── types.ts # Zod schemas and shared backend types
│ │ └── index.ts # Express entry point
│ └── package.json
├── client-ui/ # React frontend
│ ├── e2e/ # Playwright tests
│ ├── src/
│ │ ├── components/ # Presentational and UI components
│ │ ├── hooks/ # React Query and SSE hooks
│ │ ├── services/ # API client and query client
│ │ ├── stores/ # Zustand auth/UI state
│ │ ├── test/ # Vitest setup and component tests
│ │ ├── types/ # Frontend note types
│ │ ├── App.tsx
│ │ └── main.tsx
│ └── package.json
├── agent/ # Python MCP + Gemini CLI agent
│ ├── agent.py
│ ├── mcp_gemini_adapter.py
│ └── requirements.txt
├── AGENTS.md # Coding-agent project guidance
├── README.md
└── package.json # Root orchestration scripts
```
## Environment Variables
### Server
| Variable | Default | Description |
|----------|---------|-------------|
| `PORT` | `3000` | Express server port. |
| `NODE_ENV` | `development` | Runtime environment. |
| `JWT_SECRET` | required | Secret for JWT signing. Must be at least 32 characters. |
| `JWT_EXPIRY` | `15m` | Access token lifetime. |
| `JWT_REFRESH_EXPIRY` | `7d` | Refresh token lifetime. |
| `DATABASE_PATH` | `./data/notes.db` | SQLite database path, relative to `server/` when running server scripts. |
| `CORS_ORIGIN` | `http://localhost:5173` | Allowed frontend origin. |
| `AUTH_TOKEN` | required | Development API key used by `/api/login`. |
### Agent
| Variable | Default | Description |
|----------|---------|-------------|
| `GEMINI_API_KEY` | required for agent | Google Gemini API key. |
| `MCP_STDIO_CMD` | `npx` | Command used for agent stdio transport. |
| `MCP_STDIO_ARGS` | `tsx,server/src/index.ts` | Comma-separated stdio command args. |
## API Endpoints
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `POST` | `/api/login` | API key | Login with `{ "apiKey": "..." }`; returns access and refresh tokens. |
| `POST` | `/api/refresh` | Refresh token | Body: `{ "refreshToken": "..." }`; returns a new access token. |
| `GET` | `/api/notes` | JWT | List notes. Query params: `query`, `limit`, `offset`. |
| `POST` | `/api/notes` | JWT | Create a note with `title`, optional `content`, optional `tags`. |
| `GET` | `/api/notes/:id` | JWT | Get one note. |
| `PUT` | `/api/notes/:id` | JWT | Update note fields. |
| `DELETE` | `/api/notes/:id` | JWT | Delete a note. |
| `GET` | `/api/live-updates` | none | SSE stream for frontend refresh events. |
| `GET` | `/sse` | none | MCP SSE connection endpoint. |
| `POST` | `/messages` | none | MCP JSON-RPC message endpoint. |
| `GET` | `/api/health` | none | Health check. |
### Note payloads
Create note:
```json
{
"title": "Meeting notes",
"content": "Discuss launch plan",
"tags": ["work", "planning"]
}
```
Update note:
```json
{
"title": "Updated title",
"content": "Updated content",
"tags": ["updated"]
}
```
## MCP Tools
The MCP server exposes these tools to AI clients:
| Tool | Description |
|------|-------------|
| `list_notes` | List notes with optional search query, `limit`, and `offset`. |
| `read_note` | Read a single note by ID. |
| `create_note` | Create a note with title, optional content, and optional tags. |
| `update_note` | Update selected fields on an existing note. |
| `delete_note` | Delete a note by ID. |
REST and MCP mutations both emit note update events so connected frontends can refresh through SSE.
## Agent Usage
Interactive mode:
```bash
npm run dev:agent
```
Direct Python invocation:
```bash
agent/.venv/bin/python agent/agent.py --transport sse --url http://localhost:3000/sse
```
One-shot mode:
```bash
agent/.venv/bin/python agent/agent.py --one-shot "List my notes about planning"
```
Output formats:
```bash
# Script-friendly text, the default for --one-shot
agent/.venv/bin/python agent/agent.py --one-shot "List my notes" --format plain
# Rich Markdown, tool status, and note tables
agent/.venv/bin/python agent/agent.py --one-shot "List my notes" --format pretty
# One JSON object for scripts and integrations
agent/.venv/bin/python agent/agent.py --one-shot "List my notes" --format json
```
Interactive mode uses the rich terminal renderer by default. It supports persistent command
history, completion for slash commands, and multiline prompts: press `Esc` then `Enter` to
insert a newline, and press `Enter` to submit. Prompts in the same running process share
conversation context, including relevant tool calls and results. Use `/reset` to forget the
model context, or `/history` to inspect a bounded summary. Context is automatically trimmed
at complete turn boundaries when it exceeds the memory budget; it is lost when the process
exits. Use `/help` to view available commands.
Note IDs are full UUIDs. For delete, update, or read requests, you can identify a note by
title instead of copying an ID: the agent will search with `list_notes` and use the exact ID
from that result. Never use a shortened ID from a manually copied table value.
Options:
```text
--transport sse|stdio Transport protocol (default: sse)
--url URL MCP server URL (default: http://localhost:3000/sse)
--model MODEL Gemini model (default: gemini-2.5-flash)
--api-key KEY Gemini API key; prefer GEMINI_API_KEY in agent/.env
--one-shot QUERY Single query mode
--format FORMAT Output format: plain, pretty, or json
--verbose Show connection, turn, and tool-result details
--no-color Disable ANSI color in pretty output
--history-file PATH Override the interactive prompt history path
--memory-max-chars N Approximate maximum model context size across turns (default: 24000)
```
Interactive commands:
```text
/help Show command help
/tools List available MCP tools
/clear Clear the terminal
/reset Forget the current model conversation
/history Show a summary of model conversation memory
/format [FORMAT] View or set plain, pretty, or json output
/model [MODEL] View or set the Gemini model for future prompts
/verbose [on|off] View or set detailed logging
/quit Exit the agent
```
## Frontend Notes
- The Vite dev server runs on `http://localhost:5173` and proxies API calls to the backend.
- The app logs in with the development API key by default for local use.
- Note operations use React Query and invalidate `['notes']` after mutations.
- `useSSE` listens for backend update events and refreshes note queries live.
## Validation
Recommended checks before committing application changes:
```bash
npm run build
npm run test --prefix client-ui
npm run test:e2e --prefix client-ui
```
For targeted changes:
```bash
npm run build --prefix server
npm run build --prefix client-ui
```
## Troubleshooting
### Server fails on startup with environment errors
Check `server/.env`. `JWT_SECRET` must be at least 32 characters and `AUTH_TOKEN` must be set.
### Frontend cannot load notes
Make sure the backend is running on `http://localhost:3000`, the frontend is running through Vite, and `CORS_ORIGIN` matches `http://localhost:5173`.
### Agent asks for a Gemini key
Create `agent/.env` with `GEMINI_API_KEY=...`, or pass `--api-key` for a one-off run.
### Agent cannot connect to MCP
Start the backend first and verify `http://localhost:3000/api/health` returns `{"status":"ok",...}`. The default MCP endpoint is `http://localhost:3000/sse`.
## Development Login
Default local API key:
```text
dev-auth-token-change-in-production
```
Change this in `server/.env` for any non-local environment.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available