Overview
Push a release tag, and a GitHub Action runs Claude Code, reads the commits since the previous tag, and leaves one draft changelog entry per user-facing change in SeggWat. You review the drafts in the dashboard and publish them.
SeggWat has no built-in "AI changelog" button. The work is done by Claude Code over the SeggWat MCP server, following an agent skill: a Markdown file named seggwat-changelog that tells Claude which commits to keep, how to write for customers, and which SeggWat tools to call. You can read and edit it.
What that means for you:
- No inference cost on our side. The model runs on your Claude account or API key.
- Your data goes to your own model provider, not to one we picked.
- You keep the publish button. The run is set up to create drafts only; publishing is your click.
Prerequisites
- A SeggWat project, and admin or owner access to its organization
- Claude Code installed locally, to test the skill before putting it in CI
- Two credentials, which are easy to mix up:
- A SeggWat Organization Access Token (
oat_...) so Claude can write to your changelog. Create it under Dashboard → Settings → API Tokens → Create New Token and copy it right away; it is shown once. - An Anthropic credential so Claude Code can run in CI: an API key from the Claude Console (pay per token), or a token from
claude setup-tokento bill a Claude subscription.
- A SeggWat Organization Access Token (
Install the Skill
The skill lives in the public repo SeggWat/cli-seggwat. Run this in your project:
npx skills add SeggWat/cli-seggwat --skill seggwat-changelogIf the installer asks, choose Claude Code, project scope and symlink. It puts the skill in .agents/skills/seggwat-changelog/ and symlinks it from .claude/skills/seggwat-changelog, so Claude Code and other agents share one copy. Check that the file is there:
ls .claude/skills/seggwat-changelog/SKILL.mdHeadless Claude Code in CI reads the skill from the checked-out repo, so commit both paths plus skills-lock.json before you tag.
The skill is plain Markdown. Change the rules to fit your team, for example which commit types to drop.
Connect Claude Code
No API key needed:
claude mcp add --transport http seggwat https://seggwat.com/mcpRun /mcp in Claude Code and sign in to SeggWat. Then try the skill on your last release:
Claude lists the drafts it created. Delete them in the dashboard afterwards if you only wanted a test.
See the MCP guide for other clients and connector details.
Draft the Changelog on Release Tags
Add repository secrets
In your GitHub repository, go to Settings → Secrets and variables → Actions and add:
SEGGWAT_API_KEY: youroat_...Organization Access TokenANTHROPIC_API_KEY: your Anthropic API key
To bill a Claude subscription instead, run claude setup-token locally, store the result as CLAUDE_CODE_OAUTH_TOKEN, and replace the ANTHROPIC_API_KEY line in the workflow with:
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}Add the workflow
Save as .github/workflows/changelog-draft.yml and replace Acme Web with your project name:
# .github/workflows/changelog-draft.yml
name: Changelog draft
on:
push:
tags: ['v*']
permissions:
contents: read
jobs:
draft:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history and tags for git describe/log
- name: Install Claude Code
run: |
curl -fsSL https://claude.ai/install.sh | bash
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Draft SeggWat changelog
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
SEGGWAT_API_KEY: ${{ secrets.SEGGWAT_API_KEY }}
TAG: ${{ github.ref_name }}
run: |
set -o pipefail
cat > "$RUNNER_TEMP/mcp.json" <<EOF
{"mcpServers":{"seggwat":{"type":"http","url":"https://seggwat.com/mcp",
"headers":{"Authorization":"Bearer ${SEGGWAT_API_KEY}"}}}}
EOF
claude -p "Use the seggwat-changelog skill to draft changelog entries for tag $TAG in SeggWat project \"Acme Web\". Drafts only: do not publish and do not update feedback." \
--mcp-config "$RUNNER_TEMP/mcp.json" \
--output-format stream-json --verbose \
--allowedTools "Bash(git describe:*)" "Bash(git log:*)" "Bash(git tag:*)" \
"mcp__seggwat__list_projects" "mcp__seggwat__list_changelog" \
"mcp__seggwat__create_changelog_entry" "mcp__seggwat__update_changelog_entry" \
| tee "$RUNNER_TEMP/out.jsonl" | jq -r 'select(.type=="result") | .result'
# Fail the run if Claude never reached the SeggWat MCP server.
jq -e 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use" and (.name|startswith("mcp__seggwat__")))' \
"$RUNNER_TEMP/out.jsonl" >/dev/null \
|| { echo "::error::Claude never called a SeggWat MCP tool: check the MCP connection and SEGGWAT_API_KEY"; exit 1; }Commit, then tag
The tag has to point at a commit that already contains the workflow and the skill:
git add .github/workflows/changelog-draft.yml .agents/skills .claude/skills skills-lock.json
git commit -m "ci: draft changelog on release tags"
git push
git tag v1.4.0 && git push origin v1.4.0Open Actions → Changelog draft on GitHub to watch the run. It takes about a minute.
What the run does:
- Reads the commits between the previous
v*tag and this one, and dropschore,ci,docs,testandrefactorcommits that users don't notice. On a first release with no earlier tag, it uses the whole history up to the tag. - Checks
list_changelogfirst. If some entries for this version already exist, from an earlier run or a GitHub release sync, it updates the matching drafts and creates only what is missing, so a rerun after a partial failure completes the release. It never changes published entries. - Creates one draft per user-facing change, all sharing the tag as
version(vX.Y.Z), written for customers: no commit hashes, no PR numbers.
The example assumes one product with vX.Y.Z tags. For a monorepo with components, use a tag pattern such as cli-v*, change the skill's git describe --match 'v*' to --match 'cli-v*' so other products' tags don't shorten the range, and name the component in the prompt: ... for tag $TAG, component "cli", in SeggWat project "Acme Web". The version stays the part after the prefix, v1.4.0.
The allowlist. set_changelog_status and update_feedback are deliberately not on --allowedTools, so the run can't flip an entry to published or resolve feedback, which would email the submitter. create_changelog_entry does accept a publish flag; the skill never sets it, and the prompt repeats "drafts only". Review the drafts before publishing either way.
The gate. A headless run that can't reach the SeggWat tools says so politely and exits 0, so the job would go green with nothing drafted. The last jq step checks that Claude called at least one mcp__seggwat__ tool and fails the run if it didn't. It proves the run attempted a SeggWat call, not that every call succeeded or that the drafts are good. Read the run's last message either way.
Review and Publish Drafts
Open Dashboard → your project → Changelog. The page opens on the Draft bucket. Edit any entry, then click Publish. Publish all publishes every draft in the project, including older ones, so check the list first. See Public Changelog for categories, components and release blocks.
Troubleshooting
The gate fired
The log shows Claude never called a SeggWat MCP tool. Check, in order:
SEGGWAT_API_KEYexists under exactly that name and starts withoat_. An empty secret sendsBearerwith no token, and Claude gets no SeggWat tools.- The Anthropic credential is set (
ANTHROPIC_API_KEYorCLAUDE_CODE_OAUTH_TOKEN). Without it, Claude Code stops before calling anything. - The project name in the prompt matches a project in your organization.
- The MCP debug log, below.
Connected, but no tools
Claude reports that the SeggWat tools aren't available even though the server answers. "Connected" doesn't mean the tools loaded. Add two flags to the claude -p line in the workflow:
--debug='mcp' --debug-file "$RUNNER_TEMP/mcp.log" \and a step after it that prints the log even when the gate has failed the run:
- name: Show MCP debug log
if: always()
run: grep "Failed to fetch tools" "$RUNNER_TEMP/mcp.log" || trueA match means the client rejected the server's tool list. Claude Code 2.1.x did this with SeggWat before v1.9.8, which fixed it on our side. If you still see it, send us the log line.
No drafts, but the run passed
Claude reached SeggWat but no drafts appeared. Either every commit in the range was internal (chore, ci, ...), entries for that version already existed, or a SeggWat call failed after the first one went through (the gate only checks that a call was attempted). The run's last message says which; it is printed at the end of the step log.
