Development
Project structure
spacecadet/
├── server.py # MCP server (Python, FastMCP)
├── emacs-config/
│ └── init.el # Elisp backend (org-mode operations)
├── tests/
│ ├── __init__.py
│ ├── test_path_validation.py # Unit tests (no emacs required)
│ └── test_integration.py # Integration tests (requires emacs)
├── docs/ # Documentation source (MkDocs)
├── mkdocs.yml # MkDocs configuration
├── pyproject.toml # Python project metadata
├── requirements.txt # Python dependencies
├── setup.sh # Setup script
├── .spacecadet.conf # Auto-generated: path to org directory (gitignored)
├── LICENSE # MIT license
└── README.md
Task data is stored outside the repo (default: ~/spacecadet-tasks/). See Getting Started for configuration.
Running tests
Install dev dependencies and run:
pip install -e ".[dev]"
pytest
Test categories
Unit tests (tests/test_path_validation.py):
- Path traversal validation
- Task environment variable builder
- No external dependencies -- runs anywhere with Python
Integration tests (tests/test_integration.py):
- Full tool operations: add, get, list, update, delete, refile, notes, properties
- ID-based and heading-based lookup
- Path traversal rejection end-to-end
- Requires emacs -- automatically skipped if emacs is not in PATH
- Each test uses an isolated temporary org directory
Run only unit tests:
pytest tests/test_path_validation.py
Run only integration tests:
pytest tests/test_integration.py
Adding a new tool
-
Define the elisp function in
emacs-config/init.el:- Read parameters from environment variables using
(spacecadet--env "SC_PARAM") - Use
spacecadet--find-task-smartfor task lookup (supports both ID and heading) - Output JSON via
(princ (json-encode ...)) - Call
(save-buffer)after writes
- Read parameters from environment variables using
-
Define the MCP tool in
server.py:- Add
@mcp.tool()decorated function - For task-targeting tools, accept both
idandheadingparameters - Use
_task_env(id, heading)to build the environment dict - Pass user data via
extra_env, never inline in elisp strings - Set
write=Trueinrun_emacs()for write operations (enables file locking)
- Add
-
Add tests in
tests/test_integration.py:- Use the
org_dirfixture for an isolated test environment - Test both success and error cases
- Use the
Building docs
pip install mkdocs mkdocs-material
mkdocs serve # local preview at http://127.0.0.1:8000
mkdocs gh-deploy # deploy to GitHub Pages