Publish a changelog in Cursor
The agent that wrote the change already knows what changed. With the Nordva Launch MCP server connected, it can publish the release note to your hosted changelog at the end of the session, schedule it for later, or check what has already been posted.
Cursor reads MCP servers from a JSON file, either per user or per project. Remote servers take a url and optional headers.
Setup
You need a secret key (nv_live_…) from Dashboard → API keys.
01Add the server to mcp.json
~/.cursor/mcp.json (or .cursor/mcp.json in a project)
{
"mcpServers": {
"nordva": {
"url": "https://mcp.nordva.dev",
"headers": {
"Authorization": "Bearer nv_live_..."
}
}
}
}Use the file in your home directory if the key should not live in the repository. A project-level .cursor/mcp.json containing a key belongs in .gitignore.
02Check the connection
Open Cursor Settings and find the MCP section (Customize > MCPs). nordva should be listed with its tools. In Agent mode, ask it to run setup_check.
Then ask Cursor
- Publish a changelog entry for what we just shipped. Category: fix.
- Draft an entry for the new export feature and schedule it for Monday 09:00 UTC.
- What did we publish on the changelog this month?
- Archive the entry about the beta importer, it went out by mistake.
For “Publish a changelog entry for the CSV export fix we just merged.” Cursor calls publish_changelog_entry with:
{
"title": "CSV export no longer drops rows with commas in the name",
"body_markdown": "Names containing a comma were split into two columns, which shifted every field after them. Values are now quoted.\n\nAffects exports made since 2 September. Re-export to get a correct file.",
"category": "fix",
"version": "v1.8.2"
} Returned: The entry id, its status (published or scheduled) and the public URL of the entry on the hosted changelog page.
Changelog tools and arguments
publish_changelog_entry
Publish or schedule a changelog entry
| title | required | string, max 200 | User-facing headline. |
| body_markdown | required | string, max 50,000 | Full description in Markdown. |
| category | required | "feature" | "fix" | "improvement" | "security" | "breaking" | Use 'breaking' only when customers must change code/config. |
| version | optional | string, max 64 | Free-form version string. Not validated as semver. |
| scheduled_at | optional | string | ISO 8601 timestamp. If set, entry is scheduled, not published. |
| notify_subscribers | optional | boolean | Email the changelog subscribers when the entry publishes. Only on the developer's explicit say-so. |
| notify_in_app | optional | boolean | Post one broadcast to every user's in-app bell when the entry publishes. Leave out to use the project's default. |
| cover_image_url | optional | string, max 2,048 | https:// link to an image they host. |
| cta_label | optional | string, max 40 | |
| cta_url | optional | string, max 2,048 | http(s) URL or a path starting with /. Send with cta_label. |
| labels | optional | array | Label slugs. |
| audience | optional | "public" | "signed_in" | "segment" | |
| segment | optional | object | With audience "segment" (Builder). |
| pinned | optional | boolean | |
| expires_at | optional | string | |
| announcement | optional | object | Indie and up. One entry per project at a time. |
| notify_chat | optional | boolean | false keeps it out of Slack and Discord webhooks. |
| status | optional | literal | "draft" saves without publishing. |
list_changelog_entries
Read-onlyList recent changelog entries
| limit | optional | number, 1–50 | |
| category | optional | "feature" | "fix" | "improvement" | "security" | "breaking" | |
| status | optional | "draft" | "published" | "scheduled" | "archived" | |
| since | optional | string | |
| label | optional | string, max 40 | A label slug. |
| q | optional | string, max 100 | Words in the title or text. |
get_changelog_entry
Read-onlyRead one changelog entry
| entry_id | required | string, max 64 |
update_changelog_entry
Edit, reschedule or publish an unpublished changelog entry
| entry_id | required | string, max 64 | |
| title | optional | string, max 200 | |
| body_markdown | optional | string, max 20,000 | |
| version | optional | string, max 64 | |
| category | optional | "feature" | "improvement" | "fix" | "security" | "breaking" | |
| scheduled_at | optional | string | |
| notify_subscribers | optional | boolean | |
| notify_in_app | optional | boolean | |
| publish_now | optional | boolean | |
| cover_image_url | optional | string, max 2,048 | https:// link to an image they host. |
| cta_label | optional | string, max 40 | |
| cta_url | optional | string, max 2,048 | http(s) URL or a path starting with /. Send with cta_label. |
| labels | optional | array | Label slugs. |
| audience | optional | "public" | "signed_in" | "segment" | |
| segment | optional | object | With audience "segment" (Builder). |
| pinned | optional | boolean | |
| expires_at | optional | string | |
| announcement | optional | object | Indie and up. One entry per project at a time. |
| notify_chat | optional | boolean | false keeps it out of Slack and Discord webhooks. |
archive_changelog_entry
Confirms firstRemove a changelog entry from public view
| entry_id | required | string | The id of the changelog entry to archive (e.g. ce_01hwxyz...). |
draft_changelog_from_commits
Draft a changelog entry from commits (never publishes)
| commits | required | array | |
| title | optional | string, max 200 | |
| version | optional | string, max 64 | |
| category | optional | "feature" | "fix" | "improvement" | "security" | "breaking" | |
| dry_run | optional | boolean |
list_changelog_labels
Read-onlyList changelog labels
No arguments.
create_changelog_label
Create a changelog label
| name | required | string, max 40 | |
| slug | optional | string, max 40 | |
| colour | optional | "gray" | "red" | "orange" | "amber" | "green" | "teal" | "blue" | "indigo" | "purple" | "pink" |
update_changelog_label
Rename or recolour a changelog label
| label_id | required | string, max 64 | |
| name | optional | string, max 40 | |
| slug | optional | string, max 40 | |
| colour | optional | "gray" | "red" | "orange" | "amber" | "green" | "teal" | "blue" | "indigo" | "purple" | "pink" |
delete_changelog_label
Confirms firstDelete a changelog label
| label_id | required | string, max 64 |
get_changelog_settings
Read-onlyRead the changelog widget settings
No arguments.
update_changelog_settings
Choose the changelog widget's reactions
| reaction_set | required | array |
get_changelog_entry_stats
Read-onlyNumbers for one changelog entry
| entry_id | required | string, max 64 |
get_changelog_stats
Read-onlyNumbers for the latest changelog entries
| limit | optional | number, 1–100 |
get_changelog_after_reading
Read-onlyBuilderWhat happened after people read an entry
| entry_id | required | string, max 64 |
list_changelog_subscribers
Read-onlyList changelog email subscribers
| label | optional | string, max 40 | |
| limit | optional | number, 1–100 | |
| cursor | optional | string, max 64 |
set_changelog_subscriber_labels
Change which labels a subscriber follows
| subscriber_id | required | string, max 64 | |
| labels | required | array |
get_changelog_widget_view
Read-onlySee the changelog widget as one person sees it
| user_id | optional | string, max 256 | |
| q | optional | string, max 100 | |
| label | optional | string, max 40 | |
| category | optional | "feature" | "fix" | "improvement" | "security" | "breaking" | |
| limit | optional | number, 1–50 | |
| cursor | optional | string, max 200 |
record_changelog_widget_event
Test the changelog widget: record a view, reaction or click
| action | required | "seen" | "react" | "unreact" | "click" | |
| entry_id | required | string, max 64 | |
| reaction | optional | "like" | "celebrate" | "love" | "laugh" | "fire" | "eyes" | "confused" | "dislike" | |
| user_id | optional | string, max 256 | |
| client_id | optional | string |
Behaviour worth knowing
- Without scheduled_at the entry is public immediately. With it, the entry stays in scheduled status until a cron that runs every minute publishes it.
- Archiving is a soft delete: the entry leaves the public page and feed at once but is not destroyed.
- On the Free plan the eleventh published entry is refused with PLAN_LIMIT_REACHED. The tool description tells the agent to show the upgrade link and stop rather than retry.
- Published entries are emailed to confirmed subscribers on plans that include subscribers.
- Cursor's agent decides when to call a tool from the tool descriptions; naming the tool in the prompt ("use publish_changelog_entry") removes the guesswork.
- Tools that send email or remove content are flagged destructive by the server, and their descriptions tell the agent to confirm with you first.