A lightweight desktop notes application built with Python 3.11 and KivyMD.
- Features
- Architecture
- Storage
- Search
- Markdown Support
- Drawing Pad
- Synchronization
- MCP Server
- Development
- Building the Application
- Project Structure
- Version History
- Useful Links
- Organize notes into separate sections
- Add, rename, and delete sections
- Quickly filter sections in the navigation drawer
- Store notes in a plain text file
- Choose any local file as the notes storage
- Search notes within the current section or across all sections
- Case-sensitive and full-word search options
- Markdown preview
- Automatic saving while typing
- Configurable fonts, font sizes, foreground colors, and background colors
- Drawing pad associated with note sections
- Multiple drawing pen colors
- Drawing undo and clear functionality
- Detect changes made to the notes file outside the application
- Best-effort handling of concurrent modifications
- MCP server for integration with compatible AI clients
The application follows an MVC architecture combined with an observer notification pattern.
The main components are separated into:
View
│
▼
Controller
│
▼
Services
│
├── Notes / File handling
├── Search
└── Drawing
The view is responsible for the user interface, while application logic is implemented in the controller, services, and model layers.
The MCP server uses the same application services as the graphical application rather than implementing a separate notes-management layer.
Notes are stored in a plain text file.
Sections are separated using the application's section separator.
Example:
<section=first>
Your first section.
Here you can write your notes.
<drawing>
{
"version": 1,
"strokes": [...]
}
</drawing>
<section=second>
Another section of yours.
The notes file remains human-readable and can also be edited using an external text editor.
The application creates supporting files when required.
Stores metadata about the currently selected notes file:
{
"_file_path": {"value": "some/path/to/my_first_file.txt"},
"_file_size": {"value": 42},
"_last_updated_on": {"value": 1654674166}
}Stores application settings such as fonts and colors:
{
"font_name": {"value": "RobotoMono-Regular"},
"font_size": {"value": "14.0"},
"background_color": {"value": "black"},
"foreground_color": {"value": "green"}
}The application provides note searching with several options:
- Search the current section
- Search all sections
- Case-sensitive search
- Full-word search
- Real-time filtering of sections in the navigation drawer
The section filter is intended to make navigation practical when a large number of sections are present.
The application supports a small subset of Markdown syntax through a custom renderer.
Markdown is rendered into KivyMD widgets rather than being processed by a full Markdown parser.
| Syntax | Example | Result |
|---|---|---|
| Heading 1 | # Heading |
Large heading |
| Heading 2 | ## Heading |
Medium heading |
| Heading 3 | ### Heading |
Small heading |
| Bold | **text** |
Bold text |
| Italic | *text* |
Italic text |
| Bullet | - item |
• item |
| Indented bullet | -- item |
Indented bullet |
| Quote | > text |
Quote displayed in a card |
| Horizontal separator | --- |
Horizontal separator |
| URL | https://example.com |
Clickable, underlined URL |
| Code block | See below | Monospace code block |
Three heading levels are supported:
# Heading 1
## Heading 2
### Heading 3
Bold and italic text are supported:
**bold text**
*italic text*
The renderer converts these into Kivy markup internally.
Simple bullet items are supported:
- First item
- Second item
- Third item
An indented bullet can be created using two hyphens:
- Main item
-- Indented item
-- Another indented item
- Another main item
The indented form is rendered visually as an indented - item. It is not a general-purpose nested Markdown list implementation.
Lines beginning with > are rendered as a separate card:
> This is a quote
A line beginning with --- is rendered as a horizontal separator:
---
URLs beginning with http:// or https:// are automatically detected:
Visit https://example.com for more information.
The URL is rendered as a clickable, underlined link. Clicking it opens the URL using the system web browser.
Fenced code blocks are supported using triple backticks:
```python
def hello():
print("Hello")
```
The content between the opening and closing triple backticks is rendered in a monospace font (RobotoMono-Regular) inside a card.
The language identifier after the opening backticks is not interpreted for syntax highlighting.
Each note section can have an associated drawing.
The drawing pad supports:
- Pencil drawing
- Multiple pen colors
- Selection of the active pen
- Undo
- Clear
- Save without closing the drawing window
- Closing the drawing window without saving
Drawings are stored as json-serialized strokes in the note text file and are placed in the associated notes section.
The notes file can be stored in a directory managed by an external synchronization service such as Dropbox.
For example:
- Place the notes file inside a synchronized folder.
- Open the application.
- Open the storage menu.
- Select Choose storage file.
- Select the synchronized notes file.
The application stores metadata about the notes file and uses difflib to provide best-effort handling when the file has been modified by another instance or outside the application.
This allows the same notes file to be shared between multiple devices, subject to the limitations of file-based synchronization.
The project includes a small Model Context Protocol (MCP) server that exposes the notes application services to compatible AI clients.
The server uses the existing NotesService, so AI clients can interact with the same notes storage and section structure used by the desktop application.
Available operations include:
notes.search
notes.list_sections
notes.get_note
notes.save_note
notes.list_markdown_commands
The server can be run independently:
python mcp_server.pyAn MCP-compatible client can then be configured to start the server.
Example configuration:
{
"mcpServers": {
"notes": {
"command": "python",
"args": [
"/path/to/notes_app/mcp_server.py"
]
}
}
}On Windows:
{
"mcpServers": {
"notes": {
"command": "python",
"args": [
"C:\\Projects\\Notes\\mcp_server.py"
]
}
}
}The application is developed and tested with:
- Python 3.11
- Kivy
- KivyMD
- Pytest
- MCP Python SDK
A requirements.txt file is included in the project.
Pipenv can also be used for local development.
Create a virtual environment:
python -m venv venvActivate it on Windows:
venv\Scripts\activateActivate it on Linux/macOS:
source venv/bin/activateInstall dependencies:
python -m pip install --upgrade pip
pip install -r requirements.txtRun the application:
python notes_app/main.pyThe project uses Pytest.
From the project root:
pytestPyInstaller is used to package the application as a standalone desktop application.
Build the application on the same operating system for which it is intended.
Create a virtual environment:
python -m venv venv_notes_app
venv_notes_app\Scripts\activate.batInstall the required packages:
python -m pip install --upgrade pip wheel setuptools
python -m pip install kivy docutils pygments pypiwin32
python -m pip install kivy.deps.sdl2 kivy.deps.glew kivy.deps.gstreamer kivy.deps.angle
python -m pip install PyInstallerGenerate the PyInstaller specification:
PyInstaller --name notes notes_app/main.pyRename the specification:
ren notes.spec notes_win.specThe KivyMD KV file must be included in the datas section:
datas=[
("notes_app\\view\\notes_view.kv", "notes_app\\view\\"),
]Build:
PyInstaller notes_win.specThe packaged application will be created under:
dist/
Create a virtual environment:
python3.11 -m venv venv_notes_app
source venv_notes_app/bin/activateInstall dependencies:
python -m pip install --upgrade pip wheel setuptools
python -m pip install -r requirements.txt
python -m pip install PyInstallerGenerate the specification:
PyInstaller --name notes notes_app/main.pyRename it:
mv notes.spec notes_linux.specInclude the KV file:
datas=[
("notes_app/view/notes_view.kv", "notes_app/view/"),
]Build:
pyinstaller notes_linux.specBuild the macOS application on macOS rather than attempting to cross-build it from Windows.
Create a Python 3.11 environment:
python3.11 -m venv venv_notes_app
source venv_notes_app/bin/activateInstall dependencies:
python -m pip install --upgrade pip wheel setuptools
python -m pip install -r requirements.txt
python -m pip install PyInstallerTest the application before packaging:
python notes_app/main.pyGenerate the PyInstaller specification:
PyInstaller --name Notes notes_app/main.pyInclude the KV file:
datas=[
("notes_app/view/notes_view.kv", "notes_app/view/"),
]Build:
PyInstaller Notes.specThe resulting application should be available as:
dist/
└── Notes.app
Launch it with:
open dist/Notes.appFor macOS builds, dependencies such as Kivy's SDL2/GStreamer stack may require additional PyInstaller configuration depending on the Python, Kivy, Homebrew, and PyInstaller versions being used.
The project is organized around the MVC architecture and separates UI, application logic, and services.
| Version | Date | Description |
|---|---|---|
| 1.0.0 | 29/07/2026 | Python 3.11 upgrade, MCP server, Markdown support, Drawpad |
| 0.1.2 | 10/10/2022 | Minor bug fixes |
| 0.1.1 | 12/07/2022 | Removed item drawer menu highlight, improved diff feature, bug fixes |
| 0.1.0 | 19/06/2022 | Initial release |

