Communitygithub.com

xiaowu89/skill-function

KI-gestützte Bildinhaltsmoderation – erkennt anstößige, politische und gewalttätige Inhalte in Bildern.

Was ist skill-function?

Dieses Skill ermöglicht die automatisierte Überprüfung von Bildern auf anstößige, politische und gewalttätige Inhalte unter Verwendung des nx-mcp-audit MCP-Dienstes. Entwickler können damit qualifizierte Moderation direkt in ihren Workflow integrieren, ohne auf externe APIs angewiesen zu sein. Der Skill unterstützt Plattformen wie Claude Code und andere MCP-kompatible Umgebungen. Der Ablauf umfasst die Konfiguration der MCP-Verbindung, die Installation von Sharp zur Bildkomprimierung (im Arbeitsspeicher) und das Senden der Daten zur Analyse an den MCP-Dienst. Zurückgegeben wird eine detaillierte Bewertung pro Bild mit Angabe der Sicherheit (safe/blocked), der Prüfquelle und Fehlermeldungen. So wird eine effiziente und datenschutzkonforme Bildmoderation ermöglicht.

Funktioniert mit✓Claude Code~Codex CLI~Cursor
npx skills add xiaowu89/skill-function

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Bildinhaltsmoderation

Prüfen Sie Bilder auf anstößige, politische und gewalttätige Inhalte mithilfe des nx-mcp-audit MCP-Dienstes.

Konfiguration

Erstellen Sie im Projektstammverzeichnis die Datei .mcp.json:

{
  "mcpServers": {
    "nx-mcp-audit": {
      "type": "url",
      "url": "https://mcp.api-inference.modelscope.net/da16b3f65bdb4e/mcp",
      "env": {
        "NX_API_KEY": "Dein API-Key"
      }
    }
  }
}

Suchreihenfolge: Projektstammverzeichnis → Benutzerverzeichnis. Extrahiere url → MCP_URL, env.NX_API_KEY → API_KEY.

Das Skill verbindet sich direkt mit dem MCP-Endpunkt, ein Neustart von Claude Code ist nicht erforderlich.

Kein API-Key? Kontaktiere uns per WeChat zhjian_2026, um einen zu erhalten.

Prüfablauf

Schritt 1: Konfiguration prüfen (bei Fehlen sofort abbrechen)

cat .mcp.json 2>/dev/null || cat ~/.mcp.json 2>/dev/null
  • Gefunden → url und NX_API_KEY notieren, weiter mit Schritt 2
  • Nicht gefunden → Benutzer fragen, ob er bereits einen API-Key hat:
    • Hat Key: Hilf dem Benutzer, ~/.mcp.json (im Benutzerverzeichnis) zu erstellen – gilt global und auch für Projektinstallationen
    • Hat keinen Key: Den Benutzer bitten, per WeChat zhjian_2026 einen zu besorgen, dann nach Erhalt weiterzukonfigurieren

⚠️ Bei fehlender Konfiguration weder sharp installieren noch die nächsten Schritte ausführen; zuerst die Konfiguration lösen.

Schritt 2: Sharp installieren + Prüfung ausführen (ein Bash-Aufruf, rein im Arbeitsspeicher, null Dateien)

Nachdem PIC_DIR, MCP_URL und API_KEY ersetzt wurden, wird das Heredoc direkt an Node über stdin übergeben, es werden keine Dateien geschrieben:

NODE_PATH=$(npm root -g) node -e "require('sharp')" 2>/dev/null || npm install -g sharp
NODE_PATH=$(npm root -g) node << 'AUDITEOF'
const fs=require('fs'),path=require('path'),sharp=require('sharp');
let PIC_DIR='<absoluter Pfad zum Zielverzeichnis>';
const SINGLE_FILE='<Pfad zu einem einzelnen Bild, leer für gesamtes Verzeichnis>';
const MCP_URL='<url aus .mcp.json>';
const API_KEY='<NX_API_KEY aus .mcp.json>';
(async()=>{
const exts=['.png','.jpg','.jpeg','.webp','.bmp','.tga'];
let imgs;
if(SINGLE_FILE){imgs=[path.basename(SINGLE_FILE)];PIC_DIR=path.dirname(SINGLE_FILE)}
else{imgs=fs.readdirSync(PIC_DIR).filter(f=>exts.includes(path.extname(f).toLowerCase())).sort()}
const origTotal=imgs.reduce((s,f)=>s+fs.statSync(path.join(PIC_DIR,f)).size,0);
console.log(`Gesamt ${imgs.length} Bilder, ${(origTotal/1024).toFixed(0)}KB`);
const records=[],data_urls=[];let compTotal=0;console.time('Komprimierung');
for(let i=0;i<imgs.length;i++){const f=imgs[i],fp=path.join(PIC_DIR,f),osz=fs.statSync(fp).size;
try{const buf=await sharp(fp,{limitInputPixels:false}).resize({width:500,height:500,fit:'inside',withoutEnlargement:true}).jpeg({quality:40}).toBuffer();
const url='data:image/jpeg;base64,'+buf.toString('base64');records.push({name:f,origKb:osz,compKb:buf.length,dataUrl:url});
data_urls.push(url);compTotal+=buf.length;console.log(`  [${i+1}/${imgs.length}] ${f} ${(osz/1024).toFixed(0)}KB→${(buf.length/1024).toFixed(0)}KB`)}
catch(e){records.push({name:f,origKb:osz,compKb:0,dataUrl:null,error:e.message});console.log(`  [${i+1}/${imgs.length}] ${f} ❌ ${e.message}`)}}
console.timeEnd('Komprimierung');
console.log(`payload: ${(compTotal/1024).toFixed(0)}KB`);
console.time('MCP-Prüfung');
const H={'Content-Type':'application/json','Accept':'application/json, text/event-stream','Authorization':`Bearer ${API_KEY}`};
const r1=await fetch(MCP_URL,{method:'POST',headers:H,body:JSON.stringify({jsonrpc:'2.0',id:'1',method:'initialize',params:{protocolVersion:'2025-06-18',capabilities:{},clientInfo:{name:'cc',version:'1'}}})});
const sid=r1.headers.get('Mcp-Session-Id');H['Mcp-Session-Id']=sid;console.log(`MCP: init→${sid}`);
await fetch(MCP_URL,{method:'POST',headers:H,body:JSON.stringify({jsonrpc:'2.0',method:'notifications/initialized'})});console.log('MCP: notified→202');
let items=[];
if(data_urls.length>0){const r3=await fetch(MCP_URL,{method:'POST',headers:H,body:JSON.stringify({jsonrpc:'2.0',id:'3',method:'tools/call',params:{name:'nx_img_audit',arguments:{files:data_urls,apiKey:API_KEY}}})});
const raw=await r3.json();const inner=JSON.parse(raw.result.content[0].text);items=inner.items}
console.timeEnd('MCP-Prüfung');
let itemIdx=0,pass=0,block=0,fail=0;
console.log('\n'+'='.repeat(85));console.log(`${'Datei'.padEnd(50)} ${'Original'.padStart(6)} ${'Ergebnis'.padStart(6)} ${'Quelle'.padStart(6)} Beschreibung`);console.log('-'.repeat(85));
for(const r of records){const oszS=(r.origKb/1024).toFixed(0)+'KB';
if(r.dataUrl){const item=items[itemIdx++],safe=item.safe,src=item.source||'-';const ec=item.errcode,em=item.errmsg||item.error||'';let st;
if(em==='invalid api key'||em==='未配置 API Key'){st='⚠️ Fehler';fail++}else if(safe===true){st='✅ Bestanden';pass++}else if(safe===false){st='⛔ Verstoß';block++}else{st='❌ Fehlgeschlagen';fail++}
console.log(`${r.name.padEnd(50)} ${oszS.padStart(6)} ${st.padStart(6)} ${src.padStart(6)} ${em.padStart(8)}`)}
else{console.log(`${r.name.padEnd(50)} ${oszS.padStart(6)} ${'❌ Fehlgeschlagen'.padStart(6)} ${'—'.padStart(6)} Komprimierung fehlgeschlagen`);fail++}}
const total=records.length;console.log(`\n📊 ${total} Bilder | ✅ ${pass} bestanden | ⛔ ${block} Verstöße | ⚠️ ${fail} Fehler/Fehlschläge | v${items[0]?.auditVersion||'?'}`);
})();
AUDITEOF

Empfehlungen

  • ✅ Bestanden: Kann normal verwendet werden
  • ⛔ Verstoß: Löschen oder manuelle Überprüfung empfohlen
  • ❌ Fehlgeschlagen: Einmal wiederholen

Schnellübersicht der Rückgabefelder

FeldTypBeschreibung
safebooleantrue=bestanden, false=Verstoß
sourcestringPrüfengine (wechat / api)
errcodenumberFehlercode, 0=normal
errmsgstringFehlermeldung, "ok"=normal
messagestringBeschreibung des Prüfergebnisses
auditVersionstringDienstversionsnummer
summaryobject{total, pass, block, error}

Schnellübersicht häufiger Fehler

FehlerUrsacheRichtige Vorgehensweise
-32602 Invalid request parametersKein notifications/initialized gesendetDrei Schritte einhalten: init → notified → call
406 Not AcceptableFehlender Accept-HeaderSowohl application/json als auch text/event-stream angeben
"Bitte geben Sie urls oder files-Parameter an"Verwendung des nicht existierenden imagePathfiles verwenden (Array von base64-dataUrl)
"invalid api key"API-Key falsch oder abgelaufen (errcode=-1)Überprüfen, ob der Key in .mcp.json korrekt ist
"Kein API-Key konfiguriert"Kein apiKey übergebenMuss übergeben werden, die Tool-Definition gibt fälschlicherweise an, dass es optional sei
413 Payload Too LargePayload überschreitet GrenzwertNach Komprimierung normal <200KB, kein Auslöser; unkomprimierte große Bilder aufteilen
Cannot find module 'sharp'Nicht global installiert oder NODE_PATH fehltNODE_PATH=$(npm root -g) node ... verwenden

Verbotene Handlungen

  • ❌ Komprimierung nicht überspringen (auch nicht bei sehr kleinen Bildern)
  • ❌ Kein imagePath-Parameter verwenden (existiert nicht)
  • ❌ Kein apiKey-Parameter auslassen
  • ❌ Schritt notifications/initialized nicht auslassen
  • ❌ Keine temporären Dateien schreiben (Heredoc direkt an stdin, rein im Speicher)
  • ❌ Ablauf nicht in mehrere Bash-Aufrufe aufteilen (alles in einem node << 'AUDITEOF' erledigen)
  • ❌ Keine Backslash-Pfade verwenden (d:\pfad), da diese im Bash-Heredoc escaped werden – immer Schrägstriche (d:/pfad) verwenden

Verwandte Skills