MorgenMCP
An unofficial MCP server for the Morgen calendar API.
Requirements
- uv - Install with
curl -LsSf https://astral.sh/uv/install.sh | sh,brew install uv, orwinget install astral-sh.uv - mise (optional) - Install with
curl https://mise.run | sh,brew install mise, orwinget install jdx.mise. Convenient for local development: it puts the right Python onPATHin a plain shell and fails fast with setup instructions whenMORGEN_API_KEYis unset. Everything works with uv alone — see Environment Setup. - A Morgen API key - Get one from Morgen Developer Portal
- Python 3.12 or newer — Only needed when working from a clone (
uvandmiseprovision this automatically from.python-version). End users running viauvxrequire no setup.
Installation
No installation required — MCP clients run the server directly via uvx (published on PyPI).
Most clients use the same mcpServers JSON shape. Add this entry to your
client's MCP configuration:
{
"mcpServers": {
"morgen": {
"command": "uvx",
"args": ["morgenmcp"],
"env": {
"MORGEN_API_KEY": "your_api_key_here"
}
}
}
}
The server speaks stdio, so no port or URL is involved. If your client asks for
a command rather than JSON, it is uvx morgenmcp with
MORGEN_API_KEY in the environment. If it cannot find uvx, see
Troubleshooting.
To pin to a specific version, pass the version in args (e.g. ["morgenmcp==0.2.1"]). You can also run unreleased commits directly from GitHub: ["--from", "git+https://github.com/k3KAW8Pnf7mkmdSMPHz27/MorgenMCP@main", "morgenmcp"].
Claude Desktop
The config file lives at ~/Library/Application Support/Claude/claude_desktop_config.json
on macOS, or %APPDATA%\Claude\claude_desktop_config.json on Windows. Paste the
block above, then restart Claude Desktop.
Available Tools
Tools across accounts, calendars, events, tasks, and tags — read-only and mutating:
- Accounts & calendars:
list_accounts,list_calendars,update_calendar_metadata - Events:
list_events,create_event,update_event,delete_event,batch_update_events,batch_delete_events - Tasks:
list_tasks,list_task_lists,get_task,create_task,update_task,move_task,complete_task,reopen_task,delete_task,batch_delete_tasks - Tags:
list_tags,get_tag,create_tag,update_tag,delete_tag
Pass --read-only (or set MORGENMCP_READ_ONLY=1) to expose only the read-only tools. For the exact live list with schemas and annotations, connect any MCP client or run the MCP Inspector.
Configuration
MORGEN_API_KEY is required; everything else is optional. Each setting has an
equivalent CLI flag, and the flag wins over the environment variable.
| Environment variable | CLI flag | Default | Description |
|---|---|---|---|
MORGEN_API_KEY |
— | (required) | Morgen API key. The server refuses to start without it. |
MORGENMCP_READ_ONLY |
--read-only |
off | Truthy (1/true/yes/on) exposes only the read tools, disabling every mutating one. |
MORGENMCP_TASKS_LIMIT |
--tasks-limit N |
100 |
Default limit sent to /tasks/list. Integer 1–100. |
MORGENMCP_TAGS_LIMIT |
--tags-limit N |
(unset) | Default limit sent to /tags/list. Integer ≥ 1, no upper bound. Unset returns all tags. |
MORGENMCP_DISPLAY_TZ |
— | system local | IANA timezone (e.g. America/Chicago) for rendering compact event times. |
MORGENMCP_DATA_DIR |
— | platform data dir | Override the virtual-ID persistence directory. |
An invalid limit fails at startup with a clear error rather than silently returning fewer results:
$ uv run morgenmcp --tasks-limit 500
morgenmcp: error: --tasks-limit must be <= 100 (got 500)
The two list limits are deliberately asymmetric, because Morgen documents the
two endpoints differently: /tasks/list documents a default of 1 and a
maximum of 100, while /tags/list documents neither and returns all tags when
the parameter is omitted. A per-call limit argument on the tool always
overrides both the flag and the environment variable.
Note:
MORGENMCP_TASKS_LIMITdefaults to100, which is MorgenMCP's own choice rather than Morgen's./tasks/listreturns a single task whenlimitis omitted — its documented default is 1, and the API docs tell callers to always set the parameter explicitly, so the server never leaves it to Morgen. This is only a fallback: the tool asks the model to pass alimitsuited to the request, and a per-call value always wins. Set this variable to bound what a model can pull in one call, not to express a typical page size.
Troubleshooting
MORGEN_API_KEY is not set — the key goes in the env block of your MCP
client's server entry, not in a shell profile: the client launches the server
itself and does not inherit your terminal's environment. Get a key from the
Morgen Developer Portal.
uvx: command not found, or the server never starts — desktop clients are
launched by the OS, not by your shell, so they see a minimal PATH that
usually excludes ~/.local/bin, /opt/homebrew/bin, and version-manager
shims. Use an absolute path instead:
which uvx # e.g. /opt/homebrew/bin/uvx
"command": "/opt/homebrew/bin/uvx"
Nothing appears in the client — check the client's MCP log. Both failures above surface there and nowhere else, because a stdio server's stderr goes to the client, not to a terminal.
Development
# Clone the repository
git clone https://github.com/k3KAW8Pnf7mkmdSMPHz27/MorgenMCP.git
cd MorgenMCP
# Install dependencies
uv sync --all-extras
# Run tests
uv run pytest
Environment Setup
The server reads MORGEN_API_KEY from the process environment — it does not load a .env file itself. Either of the following supplies it. Both keep the key in a gitignored file; neither is required if the variable is already exported in your shell.
With uv alone. Put the key in a .env file (already gitignored) and let uv load it:
echo 'MORGEN_API_KEY=your_api_key' > .env
UV_ENV_FILE=.env uv run morgenmcp
Export UV_ENV_FILE=.env in your shell profile to skip the prefix. uv run --env-file .env <cmd> works the same way for one-off commands.
If
uvis itself installed through mise (a shim under~/.local/share/mise/shims/uv), everyuvcall routes through mise, and mise refuses to run against this repo's untrustedmise.toml— souv runfails until youmise trust. That is a property of that install, not of this project; a standaloneuv(brew install uv) is unaffected.
With mise (optional, recommended if you already use it). This repo ships a mise.toml declaring MORGEN_API_KEY as required, so a missing key fails immediately with setup instructions instead of surfacing on the first tool call. mise set writes to a gitignored mise.local.toml, never the committed config:
mise trust
mise set --file mise.local.toml MORGEN_API_KEY=your_api_key
uv run morgenmcp
mise also puts the interpreter from .python-version on PATH in a plain shell, so a bare python3 in a clone is the right one — useful because the source requires Python 3.12 or newer (see Requirements).
Local Debugging with MCP Inspector
The MCP Inspector provides a web UI for testing tools and inspecting requests/responses:
npx @modelcontextprotocol/inspector uv run morgenmcp
Opens at http://localhost:6274
Releasing
Releases are managed via git tags:
git tag -a v0.2.1 -m "Release v0.2.1"
git push origin v0.2.1
Pushing a tag triggers the GitHub Actions release workflow, which runs tests, builds wheel and sdist distributions, creates a GitHub Release, and publishes to PyPI automatically.
Contributing
See AGENTS.md for environment setup, conventions, and the checks to run before handing off changes.
AI-assisted contributions are welcome and must be disclosed — see AI_POLICY.md, adapted from the Ghostty project's AI policy (MIT).
License
Metadata
Release files for morgenmcp 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| morgenmcp-0.2.1.tar.gz | 170.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| morgenmcp-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 227.5 kB
Release files / morgenmcp-0.2.1.tar.gz
| Download URL | morgenmcp-0.2.1.tar.gz |
|---|---|
| Size | 170.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f66419194481c2b0872655d7304048fdf9ea10442ade688cc442068f47cab282
|
|
BLAKE2b-256 checksum How to use checksums |
2395b28f1b03b33e83b68bb40f48e327ae68a4b35d10dc540568f4d6c79685ff
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / morgenmcp-0.2.1-py3-none-any.whl
| Download URL | morgenmcp-0.2.1-py3-none-any.whl |
|---|---|
| Size | 56.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d2820cd61b8613daaa553dfbda13cae9e6809068283668200918c81acf926357
|
|
BLAKE2b-256 checksum How to use checksums |
de93c236a851f6f3530df1156cafe1a90c71afdc0c7588cb6893dcdbb6af9691
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for morgenmcp 0.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.