Communitygithub.com

xiaowu89/skill-function

Compétence de modération de contenu alimentée par l'IA – détecte les contenus adultes, politiques et violents dans les images.

Qu'est-ce que skill-function ?

Cette compétence permet d'auditer des images en utilisant le service MCP nx-mcp-audit, capable de détecter des contenus adultes, politiques et violents. Le processus se déroule en deux étapes dans une seule invocation bash sans écriture de fichiers temporaires : d'abord, elle vérifie la configuration dans `.mcp.json` (projet ou répertoire utilisateur) et installe globalement la bibliothèque Sharp si nécessaire ; ensuite, elle compresse chaque image (redimensionnement à 500px, qualité JPEG 40 %) et les envoie par data URL au service MCP. Le résultat est affiché sous forme de tableau avec le statut de chaque fichier (✅ pass, ⛔ bloqué, ❌ erreur). Les cas d'erreur fréquents (API Key invalide, payload trop volumineux, module Sharp manquant) sont gérés avec des messages explicites. La compétence fonctionne sur Claude Code et nécessite une API Key obtenue via WeChat, sans redémarrage de l'agent.

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add xiaowu89/skill-function

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Modération de contenu image

Auditez les images pour les contenus adultes, politiques et violents à l'aide du service MCP nx-mcp-audit.

Configuration

Créez un fichier .mcp.json à la racine du projet :

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

Ordre de recherche : racine du projet → répertoire personnel. Extraire url → MCP_URL, env.NX_API_KEY → API_KEY.

La compétence se connecte directement au point d'accès MCP, aucun redémarrage de Claude Code nécessaire.

Pas de clé API ? Contactez WeChat zhjian_2026 pour en obtenir une.

Processus d'audit

Étape 1 : Vérification de la configuration (arrêt immédiat si absente)

cat .mcp.json 2>/dev/null || cat ~/.mcp.json 2>/dev/null
  • Trouvé → enregistrer url et NX_API_KEY, passer à l'étape 2
  • Pas trouvé → demander à l'utilisateur s'il possède déjà une clé API :
    • A une clé : aider l'utilisateur à créer ~/.mcp.json (dans le répertoire personnel, valable globalement et pour le projet)
    • N'a pas de clé : informer qu'il faut contacter WeChat zhjian_2026 pour en obtenir une, attendre qu'il revienne avec la clé

⚠️ En cas d'absence de configuration, n'installez pas sharp et ne poursuivez pas les étapes suivantes ; résolvez d'abord la configuration.

Étape 2 : Installation de sharp + exécution de l'audit (un seul appel bash, entièrement en mémoire, zéro fichier)

Après avoir remplacé PIC_DIR, MCP_URL, API_KEY, le heredoc est passé directement via stdin à node, sans écrire aucun fichier :

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='<chemin absolu du répertoire d'images cible>';
const SINGLE_FILE='<chemin de l'image unique, laisser vide pour auditer tout le répertoire>';
const MCP_URL='<url lue depuis .mcp.json>';
const API_KEY='<NX_API_KEY lue depuis .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(`Total ${imgs.length} images, ${(origTotal/1024).toFixed(0)} Ko`);
const records=[],data_urls=[];let compTotal=0;console.time('Compression');
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)}Ko→${(buf.length/1024).toFixed(0)}Ko`)}
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('Compression');
console.log(`Payload : ${(compTotal/1024).toFixed(0)} Ko`);
console.time('Audit MCP');
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 : notifié→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('Audit MCP');
let itemIdx=0,pass=0,block=0,fail=0;
console.log('\n'+'='.repeat(85));console.log(`${'Fichier'.padEnd(50)} ${'Original'.padStart(6)} ${'Résultat'.padStart(6)} ${'Moteur'.padStart(6)} Description`);console.log('-'.repeat(85));
for(const r of records){const oszS=(r.origKb/1024).toFixed(0)+'Ko';
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==='Clé API non configurée'){st='⚠️ Erreur';fail++}else if(safe===true){st='✅ Passé';pass++}else if(safe===false){st='⛔ Bloqué';block++}else{st='❌ Échec';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)} ${'❌ Échec'.padStart(6)} ${'—'.padStart(6)} Échec de compression`);fail++}}
const total=records.length;console.log(`\n📊 ${total} img | ✅ ${pass} passés | ⛔ ${block} bloqués | ⚠️ ${fail} erreurs/échecs | v${items[0]?.auditVersion||'?'}`);
})();
AUDITEOF

Suggestions

  • ✅ Passé : peut être utilisé normalement
  • ⛔ Bloqué : supprimer ou réviser manuellement
  • ❌ Échec : réessayer une fois

Référence rapide des champs retournés

ChampTypeDescription
safebooleantrue=passé, false=bloqué
sourcestringMoteur d'audit (wechat / api)
errcodenumberCode d'erreur, 0=normal
errmsgstringMessage d'erreur, "ok"=normal
messagestringDescription du résultat d'audit
auditVersionstringVersion du service
summaryobject{total, pass, block, error}

Référence rapide des erreurs courantes

Erreur observéeCauseSolution correcte
-32602 Invalid request parametersnotifications/initialized non envoyéProcédure en trois étapes : init → notified → call
406 Not AcceptableEn-tête Accept manquantDéclarer à la fois application/json et text/event-stream
"Veuillez fournir les paramètres urls ou files"Utilisation du paramètre imagePath inexistantUtiliser files (tableau de dataUrl base64)
"invalid api key"Clé API erronée ou expirée (errcode=-1)Vérifier la clé dans .mcp.json
"Clé API non configurée"Paramètre apiKey non fourniObligatoire, la définition indique "optionnel" par erreur
413 Payload Too LargePayload trop volumineuxAprès compression, généralement < 200 Ko ; sinon, diviser les images
Cannot find module 'sharp'Installation globale absente ou NODE_PATH manquantNODE_PATH=$(npm root -g) node ...

Interdictions

  • ❌ Ne pas sauter la compression (même pour de petites images)
  • ❌ Ne pas utiliser le paramètre imagePath (inexistant)
  • ❌ Ne pas omettre le paramètre apiKey
  • ❌ Ne pas omettre l'étape notifications/initialized
  • ❌ Ne pas écrire de fichiers temporaires (heredoc passé directement via stdin, exécution en mémoire)
  • ❌ Ne pas diviser le processus en plusieurs appels bash (faire tout en un seul node << 'AUDITEOF')
  • ❌ Ne pas utiliser de chemins avec des barres obliques inversées (d:\chemin) ; le heredoc bash serait échappé ; utilisez toujours des barres obliques (d:/chemin)

Skills associés