feat: comprehensive Listmonk API coverage — 41 new tools #2
No reviewers
Labels
No labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
waiting-on-julian
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
jlxq0/listmonk-mcp!2
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/comprehensive-api-coverage"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Lands the three commits that have sat unmerged on this branch since 2026-05-07.
masteris byte-identical torhnvrm/listmonk-mcp@mastertoday, so until this merges every deployment built from the default branch ships upstream's server without any of this.What it contains
@mcp.toolregistrationsListmonkClientasync methods addedclient.py,server.pyThree commits:
4661545feat: comprehensive API coveragedc07b44fix(test_campaign): send full campaign payload, not just subscribers4fd84c2fix(update_setting): send raw JSON value, not wrapped in{"value": ...}The 41 new tools group as:
patch_subscriber,subscriber_send_optin,blocklist_subscriber(s),manage_subscriber_lists,get_subscriber_activity, and the four by-query variants (delete_subscribers_by_query,blocklist_subscribers_by_query,manage_subscriber_lists_by_query)list_bounces,get_bounce,delete_bounce(s),get_subscriber_bounces,delete_subscriber_bounces,blocklist_bounced_subscribersimport_subscribers,get_import_status,get_import_logs,stop_importtest_campaign,change_campaign_status,archive_campaign,delete_campaign(s),get_running_campaign_stats,get_campaign_analyticsset_default_template,preview_template_bodyget_settings,update_settings,update_setting,test_smtp_settings,reload_app,get_logs,get_about_infoget_dashboard_counts,get_dashboard_chartsgc_subscribers,gc_campaign_analytics,gc_unconfirmed_subscriptionsVersion bumps
0.1.0→0.2.0.Fork or upstream
This is a fork patch, and it is the fork's whole divergence.
mastercarries zero divergence fromrhnvrm/listmonk-mcptoday; merging this makes it 1192 lines. The tools are a broad API-surface expansion rather than a bug fix, so sending them upstream is a real conversation with the upstream maintainer and not a mechanical cherry-pick — worth having, but it should not block deployment work here.By contrast the two follow-up commits (
dc07b44,4fd84c2) are plain bug fixes against upstream behaviour and would be accepted upstream on their own if the tools ever go up.Scope of this PR
No new files, no CI, no transport change. Deployment work (streamable-HTTP, Dockerfile, Forgejo Actions) is jlxq0/listmonk-mcp#1 and stacks on top of this branch.
Verification
Not verified by execution in this PR:
masterhas notests/directory and no Forgejo workflow, so there is no gate to run these commits through. Adding one is part of jlxq0/listmonk-mcp#1.Refs #1
Adds wrappers for every operational Listmonk REST endpoint that was missing from the MCP, bringing total tool count from ~40 to 70. Skips admin user management (users/profile/roles/2FA) since those make more sense in the web UI. Campaign lifecycle (7 tools): - test_campaign — POST /api/campaigns/{id}/test - change_campaign_status — pause / cancel / draft / scheduled / running - archive_campaign — toggle the public archive flag, set slug & template - delete_campaign / delete_campaigns — single + bulk - get_running_campaign_stats — live sent / rate / ETA per campaign - get_campaign_analytics — views / clicks / links / bounces Subscribers (11 tools): - patch_subscriber, subscriber_send_optin - blocklist_subscriber / blocklist_subscribers (single + bulk) - manage_subscriber_lists — bulk add/remove/unsubscribe - get_subscriber_activity — opt-ins, list changes, bounces - delete_subscribers_by_query, blocklist_subscribers_by_query, manage_subscriber_lists_by_query — advanced SQL-query operations - get_subscriber_bounces, delete_subscriber_bounces Bounces (5 tools): - list_bounces, get_bounce, delete_bounce, delete_bounces (with all-flag), blocklist_bounced_subscribers Subscriber import (4 tools): - import_subscribers — multipart CSV/zip upload - get_import_status, get_import_logs, stop_import Templates (2 tools): - set_default_template, preview_template_body Settings & system (9 tools): - get_settings, update_settings, update_setting (single-key, avoids the password-masking footgun where GET masks secrets as bullets and PUT overwrites them with literal bullets) - test_smtp_settings, reload_app - get_logs, get_about_info, get_dashboard_counts, get_dashboard_charts Maintenance (3 tools): - gc_subscribers, gc_campaign_analytics, gc_unconfirmed_subscriptions All new client methods follow the existing httpx-based pattern with typed parameters and JSON-data payloads. All new tools use the existing safe_execute_async wrapper for consistent error handling. Smoke-tested against a live Listmonk instance — all reads return expected shapes; analytics correctly rejects empty campaign-id list with the same "Missing field(s): id" message the web UI sees, confirming param encoding. Bumps version 0.1.0 → 0.2.0.Listmonk's POST /api/campaigns/{id}/test endpoint validates the request as a full campReq — name, subject, lists, body, messenger, content_type. Sending only {"subscribers": [...]} fails with "Invalid length for name" because the empty campaign name fails strHasLen validation. Fix: fetch the saved campaign first and merge its fields into the test request, mirroring the pattern in update_campaign. Test recipients are appended via the "subscribers" key. Also flag in the MCP docstring that recipients must already exist as subscribers — listmonk's TestCampaign handler looks them up via GetSubscribersByEmail and silently skips unknown addresses, so an unknown email returns success but sends nothing. Verified end-to-end against a live instance — test send delivered, response {"data": true}.Listmonk's UpdateSettingsByKey handler binds the request body as a json.RawMessage and writes it directly into the settings document. Sending {\"value\": value} caused the whole object to be stored under the key, which broke the field (e.g. custom_js stopped executing, custom_css stopped applying) until restored from the full-settings PUT path. Loosens _request's json_data type from dict to Any since httpx's json= argument accepts any JSON-serialisable value. Verified by hand: direct PUT of the raw string to /api/settings/appearance.public.custom_js correctly updates the public-page JS; PUTting {\"value\": \"...\"} stores garbage.Held, not stalled, and the reason is not the code.
Julian's decision, asked and answered: this waits until the scope of the Listmonk API credential is set.
What made it a question rather than a merge. The branch is 41 new tools, not 30 — 29
@mcp.toolregistrations onmasteragainst 70 here, counted rather than recalled — and two of them are larger than the count suggests:update_settingsreplaces the whole settings document. Its own docstring records why that is sharp:So the obvious way to use it — read, change one field, write it back — destroys Listmonk's SMTP credentials. That is the tool working as written, not a misuse of it.
reload_appsits beside it and makes the new settings live.The credential Julian is scoping decides whether either can be called at all, and
campaigns:sendbeing distinct fromcampaigns:managein Listmonk v6.1.0 does not speak to settings permissions. Merging first would have made the credential the only thing standing between an agent and a broken mail path, before anyone had decided what the credential is.Nothing here needs changing to unblock it. When the scope is set, this merges as it stands.
The red check is not this branch
Deploy Docs / buildfails because it is a GitHub Pages workflow —actions/configure-pages,deploy-pages— inherited fromrhnvrm/listmonk-mcpand incapable of running on a Forgejo runner.It runs here only because this branch predates
.forgejo/workflows/. Forgejo reads.forgejo/workflowsand ignores.github/workflowsentirely when the former exists, which is worth stating because it is not obvious and it decides whether.github/should be deleted. It should not.Verified rather than assumed:
caldav-mcpcarries both directories, its.github/workflows/ci.ymldeclares jobsqualityandcontainer, and across the full 35-record task history of that repository the only job names that have ever run arecargoanddocker.jmap-mcpis the same shape.So once #3 is in the tree,
.github/workflowsstops running here,publish.ymlincluded, and this check disappears on its own.