Lieber ansehen statt lesen? Der Artikel als Video.

Dieser Beitrag gehört zum Video Redmine per KI-Agent bedienen (7 Minuten). Hier steht alles zum Nachlesen und Kopieren.

Ein Coding-Agent kann mehr als Programmcode schreiben. Zeigt man ihm, wie eine Schnittstelle bedient wird, übernimmt er auch die Arbeit drumherum. Zum Beispiel das Ticketsystem: nachsehen, was offen ist, ein Ticket anlegen, den Status setzen und einen Kommentar dazu schreiben.

Dafür braucht es weder ein Plugin noch Programmierung, sondern eine Textdatei. Einen Skill. Darin steht, was das Werkzeug kann und wie ein richtiger Aufruf aussieht. Der Agent liest das und richtet sich danach.

Was ein Skill ist

Ein Ordner mit einer Datei darin:

~/.claude/skills/redmine/SKILL.md

opencode liest dieses Verzeichnis automatisch. Dieselben Skills funktionieren also auch in Claude Code. Der Kopf der Datei ist der wichtigste Teil: An der description erkennt der Agent, wann dieser Skill überhaupt gemeint ist.

Den Zugriffsschlüssel holen

In Redmine oben rechts auf Mein Konto, rechts unter API-Zugriffsschlüssel auf Anzeigen.

Der Schlüssel kann alles, was der zugehörige Benutzer kann. Nimm dafür nicht dein Administratorkonto, sondern lege einen eigenen Benutzer mit genau den Rechten an, die gebraucht werden. Wenn der Agent nur lesen und Tickets anlegen soll, dann gib ihm auch nur das.

Zugang in die Umgebung, nicht in die Datei

echo 'export REDMINE_URL=https://redmine.example.de' >> ~/.bashrc
echo 'export REDMINE_KEY=dein-schluessel' >> ~/.bashrc
grep REDMINE ~/.bashrc      # Kontrolle: beide Zeilen stehen drin
source ~/.bashrc

Das ist die einzige Stelle, an der der Schlüssel steht. In der Skill-Datei steht nur $REDMINE_KEY, also der Verweis darauf. Das hat einen praktischen Grund: Skill-Ordner landen früher oder später in einem Repository, und dann liegt der Schlüssel beim nächsten git push auf einem fremden Server.

Die Skill-Datei

Anlegen mit:

mkdir -p ~/.claude/skills/redmine

Und dann der Inhalt von ~/.claude/skills/redmine/SKILL.md:

---
name: redmine
description: Tickets im Redmine lesen, anlegen und ändern — über die REST-API.
---

# Redmine

Ticketsystem unter `$REDMINE_URL`. Der Zugriff läuft über die REST-API mit
einem persönlichen Zugriffsschlüssel.

## Zugang

Beide Werte stehen in der Umgebung, **nicht in dieser Datei**:

- `REDMINE_URL` — z. B. `https://redmine.example.de`
- `REDMINE_KEY` — der persönliche Zugriffsschlüssel aus *Mein Konto*

Jeder Aufruf schickt den Schlüssel im Kopf mit:
`-H "X-Redmine-API-Key: $REDMINE_KEY"`

## Regeln

1. **IDs niemals raten.** Projekt, Tracker, Status und Priorität haben Zahlen-IDs,
   die sich von Redmine zu Redmine unterscheiden. Vor dem Schreiben nachschlagen.
2. **Vor dem Ändern lesen.** Erst das Ticket anzeigen, dann ändern — sonst wird
   der falsche Vorgang angefasst.
3. **Nichts doppelt anlegen.** Vor jedem POST mit `subject=~<Titel>` suchen,
   ob es den Vorgang schon gibt. Ein fehlgeschlagen wirkender Aufruf ist oft
   trotzdem durchgelaufen — ein zweiter Versuch erzeugt dann eine Dublette.
4. **Nichts löschen.** Dieser Skill legt an und ändert. Löschen ist in Redmine
   unwiderruflich und gehört in die Oberfläche.

## Tickets auflisten

```bash
curl -s -H "X-Redmine-API-Key: $REDMINE_KEY" \
  "$REDMINE_URL/issues.json?project_id=demo-webshop&status_id=open&limit=50" \
  | jq '.issues[] | {id, subject, status: .status.name, priority: .priority.name}'
```

Filter, die sich kombinieren lassen: `status_id=open|closed|*`,
`assigned_to_id=me`, `tracker_id=<ID>`, `sort=updated_on:desc`.

## Ein Ticket im Detail, mit Kommentaren

```bash
curl -s -H "X-Redmine-API-Key: $REDMINE_KEY" \
  "$REDMINE_URL/issues/<ID>.json?include=journals" \
  | jq '{subject: .issue.subject, status: .issue.status.name,
         beschreibung: .issue.description,
         kommentare: [.issue.journals[] | select(.notes != "") | {von: .user.name, .notes}]}'
```

## IDs nachschlagen

```bash
curl -s -H "X-Redmine-API-Key: $REDMINE_KEY" "$REDMINE_URL/projects.json" \
  | jq '.projects[] | {id, identifier, name}'
curl -s -H "X-Redmine-API-Key: $REDMINE_KEY" "$REDMINE_URL/trackers.json" \
  | jq '.trackers[] | {id, name}'
curl -s -H "X-Redmine-API-Key: $REDMINE_KEY" "$REDMINE_URL/issue_statuses.json" \
  | jq '.issue_statuses[] | {id, name, is_closed}'
curl -s -H "X-Redmine-API-Key: $REDMINE_KEY" \
  "$REDMINE_URL/enumerations/issue_priorities.json" \
  | jq '.issue_priorities[] | {id, name}'
```

## Vor dem Anlegen: gibt es den Vorgang schon?

```bash
curl -s -H "X-Redmine-API-Key: $REDMINE_KEY" \
  "$REDMINE_URL/issues.json?project_id=demo-webshop&status_id=*&subject=~Mengenrabatt" \
  | jq '.issues[] | {id, subject}'
```

## Ticket anlegen

```bash
curl -s -X POST -H "X-Redmine-API-Key: $REDMINE_KEY" \
  -H "Content-Type: application/json" "$REDMINE_URL/issues.json" -d '{
  "issue": {
    "project_id": <PROJEKT_ID>,
    "tracker_id": <TRACKER_ID>,
    "priority_id": <PRIORITAET_ID>,
    "subject": "<TITEL>",
    "description": "<BESCHREIBUNG>"
  }
}' | jq '.issue | {id, subject}'
```

## Ticket ändern oder kommentieren

```bash
curl -s -o /dev/null -w '%{http_code}\n' \
  -X PUT -H "X-Redmine-API-Key: $REDMINE_KEY" \
  -H "Content-Type: application/json" "$REDMINE_URL/issues/<ID>.json" -d '{
  "issue": {
    "status_id": <STATUS_ID>,
    "notes": "<KOMMENTAR>"
  }
}'
```

Die Antwort ist leer — Redmine meldet Erfolg mit **204**. Deshalb den HTTP-Code
ausgeben lassen und darauf prüfen, statt auf eine JSON-Antwort zu warten.

Die Aufträge aus dem Video

Nutze den redmine-Skill. Was ist im Projekt demo-webshop gerade offen?
Gib mir eine kurze Übersicht mit Status und Priorität.

Welches davon liegt am längsten unbearbeitet?

Leg im Projekt demo-webshop ein Ticket an: Im Warenkorb fehlt die Anzeige
des Mengenrabatts. Tracker Fehler, Priorität hoch.

Setz das gerade angelegte Ticket auf In Bearbeitung und schreib als Kommentar
dazu: Ursache gefunden, die Rabattstaffel wird im Template nicht ausgegeben.

Stolpersteine

Redmine antwortet auf PUT mit HTTP 204 und leerem Rumpf. Wer auf eine JSON-Antwort wartet, hält das für einen Fehler und versucht es noch einmal. Im Skill steht deshalb, auf den Statuscode zu prüfen: -w '%{http_code}'.

IDs unterscheiden sich von Redmine zu Redmine. Tracker, Status und Priorität haben Zahlen-IDs, die bei jeder Installation anders vergeben sind. Ein Agent, der sie rät, schreibt fröhlich in den falschen Vorgangstyp. Vorher nachschlagen über /trackers.json und /issue_statuses.json.

Ein POST, der fehlgeschlagen aussieht, ist oft trotzdem durchgelaufen. Der zweite Versuch legt dann eine Dublette an. Uns ist genau das beim Dreh passiert: Im Beweisbild stand derselbe Fehler zweimal untereinander, und die Aufnahme war hin. Seitdem steht im Skill die Regel, vor dem Anlegen mit subject=~<Titel> zu suchen.

Ein 403 auf /projects/<id>/issue_categories.json ist normal, der Endpunkt ist gesperrt. Stattdessen /projects/<id>.json?include=issue_categories verwenden. Und wenn der Agent gar nichts tut, ist meist die REST-API selbst nicht eingeschaltet: Administration → Konfiguration → API.

Was ihn davon abhält, Unsinn zu machen

Danach wird jedes Mal gefragt, und zu Recht. Die Absicherung liegt zum größten Teil nicht beim Agenten, sondern bei den Rechten: Der Schlüssel gehört einem eigenen Benutzer und kann nur das, was dieser Benutzer darf. Alles andere ist Bequemlichkeit.

Dazu kommt, dass der Skill keinen Löschbefehl kennt. Er legt an und ändert. Löschen ist in Redmine endgültig und gehört in die Oberfläche, wo jemand zweimal hinsieht. Jede Änderung landet außerdem in der Historie des Tickets, mit Benutzer und Zeitstempel. Wer wissen will, was der Agent getan hat, sieht es dort.

Skills muss man nicht selbst schreiben

Man kann sie sich auch vom Agenten erzeugen lassen: den Link zur API-Dokumentation dazugeben, sagen, was gebraucht wird und wie der Skill heißen soll. Was dabei herauskommt, sollte man einmal durchlesen, bevor man es benutzt. In unserem Versuch stand im Entwurf ein Löschbefehl, den niemand bestellt hatte.

Damit lässt sich alles anbinden, was eine dokumentierte Schnittstelle hat: Zeiterfassung, Warenwirtschaft, Monitoring, das eigene Kundenportal.

Und das Modell dahinter?

Das lief bei uns am Standort auf eigener Hardware. Keine Ticketbeschreibung, kein Kundenname und kein Projektstand ist dabei nach außen gegangen. Ein Cloud-Agent schickt jede Ticketbeschreibung an einen fremden Anbieter. Wer Kundenprojekte im Ticketsystem stehen hat, muss diese Frage beantworten können, bevor er einen Agenten daranlässt.

Kostenloses Erstgespräch

Was hier beschrieben ist,
lässt sich umsetzen.

Sie haben konkrete Fragen zum Einsatz von KI in Ihrem Unternehmen? Wir schauen uns gemeinsam an, was davon für Ihren Fall realistisch und sinnvoll ist.

  • 30 Minuten, kostenlos und unverbindlich
  • Konkrete Einschätzung für Ihr Unternehmen
  • Rückmeldung innerhalb eines Werktages
Jetzt anfragen Direkt Termin buchen

Kein Verkaufsgespräch. Kein Risiko.

Kommentare

Noch keine Kommentare. Sei der Erste!

Kommentar schreiben

Kommentare werden nach manueller Prüfung freigeschaltet.