joshi-chinmay-016/AgentProof

Directives et modèles pour définir des outils, ressources et invites dans une application NitroStack avec validation de schéma via Zod, incluant la mise en cache, la limitation de débit et les téléchargements de fichiers en base64.

Qu'est-ce que AgentProof ?

Cette compétence fournit un guide complet pour définir des outils, ressources et invites MCP dans le cadre d'une application NitroStack. Elle explique l'utilisation des décorateurs TypeScript `@Tool`, `@Resource` et `@Prompt` avec validation de schéma Zod, ainsi que des options avancées comme `@Cache` pour la mise en cache des résultats et `@RateLimit` pour limiter le nombre d'appels. La compétence couvre également la gestion des téléchargements de fichiers via des chaînes base64, avec un décodeur universel et des mesures de sécurité contre les attaques de traversée de répertoire. Ce guide est essentiel pour les développeurs qui construisent des serveurs MCP avec NitroStack, que ce soit pour des applications d'IA agentique, des assistants de code ou des outils de développement, et il est compatible avec les clients MCP comme NitroStudio.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/joshi-chinmay-016/AgentProof/tree/HEAD/agentproof/.agents/skills/tools-resources-prompts

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

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

Documentation

Quand l'utiliser

Utilisez cette compétence chaque fois que vous définissez, modifiez ou validez des outils, ressources ou invites sur un serveur MCP NitroStack.

Définir des outils avec @Tool

Un outil MCP expose une fonction qu'un client IA peut invoquer. Décorez une méthode de service ou de contrôleur avec @Tool.

Options clés des outils :

  • name : Identifiant unique en kebab-case ou snake_case.
  • description : Description détaillée expliquant quand et comment le client doit l'utiliser.
  • inputSchema : Un schéma d'objet Zod pour une validation stricte des entrées.
  • outputSchema (optionnel) : Schéma Zod validant la structure de sortie.
import { ToolDecorator as Tool, ControllerDecorator as Controller, InitialTool, z, ExecutionContext } from '@nitrostack/core';

@Controller('weather')
export class WeatherService {
  @Tool({
    name: 'get_current_weather',
    description: 'Obtenez les prévisions météorologiques actuelles pour une ville spécifique.',
    inputSchema: z.object({
      city: z.string().describe('Le nom de la ville, ex: San Francisco'),
      unit: z.enum(['celsius', 'fahrenheit']).default('celsius'),
    }),
  })
  @InitialTool() // Invoqué automatiquement lors de l'initialisation/démarrage du client IA
  async getWeather(
    input: { city: string; unit: 'celsius' | 'fahrenheit' },
    ctx: ExecutionContext
  ) {
    ctx.logger.info(`Récupération de la météo pour ${input.city}`);
    // implémentation
    return {
      city: input.city,
      temp: 22,
      condition: 'Ensoleillé',
    };
  }
}

Définir des ressources avec @Resource

Une ressource MCP expose des fichiers/URI de données statiques ou dynamiques que le client IA peut lire.

Options clés des ressources :

  • uri : Modèle d'URI (ex: git://{owner}/{repo}/file ou statique app://config).
  • name : Nom unique de la ressource.
  • description : Explication des données fournies par cette ressource.
  • mimeType : Type MIME de la réponse (ex: text/plain, application/json).
import { Resource, ExecutionContext } from '@nitrostack/core';

export class ConfigResources {
  @Resource({
    uri: 'app://settings',
    name: 'Paramètres de l\'application',
    description: 'Paramètres et configurations à l\'échelle du système.',
    mimeType: 'application/json',
  })
  async getSettings(ctx: ExecutionContext) {
    return {
      environment: 'development',
      debugMode: true,
    };
  }
}

Définir des invites avec @Prompt

Une invite MCP expose des modèles réutilisables ou des ensembles d'instructions qui guident les LLM.

Options clés des invites :

  • name : Nom de l'invite.
  • description : Décrit la tâche que cette invite aide à accomplir.
  • arguments : Déclare les paramètres que le client peut fournir pour personnaliser le modèle d'invite.
import { Prompt, ExecutionContext } from '@nitrostack/core';

export class PromptTemplates {
  @Prompt({
    name: 'code_review',
    description: 'Fournissez une revue de code approfondie pour un extrait de code donné.',
    arguments: [
      { name: 'language', description: 'Le langage de programmation, ex: TypeScript', required: true },
      { name: 'code', description: 'L\'extrait de code à examiner', required: true },
    ],
  })
  async getCodeReviewPrompt(
    args: { language: string; code: string },
    ctx: ExecutionContext
  ) {
    return {
      messages: [
        {
          role: 'user',
          content: `Vous êtes un ingénieur logiciel expert. Examinez ce code ${args.language} :\n\n${args.code}`,
        },
      ],
    };
  }
}

Politiques des outils : Mise en cache (@Cache) et Limitation de débit (@RateLimit)

Vous pouvez contrôler les comportements d'exécution des outils (comme l'optimisation des performances et la limitation) à l'aide de décorateurs de méthode.

1. Mise en cache avec @Cache

Utilisez @Cache pour mettre en cache les sorties d'exécution d'un outil pendant une durée spécifiée (TTL en secondes). Cela réduit la surcharge des requêtes DB ou API pour des requêtes identiques fréquentes.

Options :

  • ttl : Durée de vie du cache en secondes (obligatoire).
  • key (optionnel) : Fonction personnalisée (input: any, context?: any) => string qui retourne une clé de cache unique basée sur les entrées. Si non définie, une clé est générée automatiquement à partir des arguments d'entrée sérialisés.

Exemple :

import { ToolDecorator as Tool, Cache, z } from '@nitrostack/core';

export class StationTools {
  @Tool({
    name: 'get_system_status',
    description: 'Récupère les métriques en temps réel de la station. La réponse est mise en cache.',
    inputSchema: z.object({}),
  })
  @Cache({ ttl: 60 }) // Met en cache l'état pendant 60 secondes
  async getSystemStatus() {
    return { température: 21.5, oxygène: 0.98 };
  }

  @Tool({
    name: 'get_crew_status',
    description: 'Récupère l\'état d\'un membre d\'équipage. Mis en cache par ID d\'équipage.',
    inputSchema: z.object({ id: z.string() }),
  })
  @Cache({
    ttl: 300,
    key: (input) => `crew:status:${input.id}`
  })
  async getCrewStatus(input: { id: string }) {
    // ...
  }
}

2. Limitation de débit avec @RateLimit

Utilisez @RateLimit pour restreindre le nombre d'invocations d'un outil dans une fenêtre de temps spécifiée, afin d'éviter les abus du client.

Options :

  • requests : Nombre de requêtes autorisées dans la fenêtre (obligatoire).
  • window : Durée de la fenêtre de limitation (obligatoire). Supporte des formats comme '1s', '1m', '1h'.
  • key (optionnel) : Fonction personnalisée (context: ExecutionContext) => string pour regrouper les limites de débit. Utile pour limiter le débit par rôle utilisateur ou clé API.

Exemple :

import { ToolDecorator as Tool, RateLimit, z, ExecutionContext } from '@nitrostack/core';

export class DiagnosticTools {
  @Tool({
    name: 'run_deep_diagnostic',
    description: 'Exécute un diagnostic intensif. Limité en débit.',
    inputSchema: z.object({}),
  })
  @RateLimit({ requests: 3, window: '1m' }) // Maximum 3 requêtes par minute globalement
  async runDeepDiagnostic() {
    return { diagnosticReport: 'Tous les systèmes sont opérationnels.' };
  }

  @Tool({
    name: 'request_supply_drop',
    description: 'Demande des fournitures d\'inventaire. Limité en débit par utilisateur.',
    inputSchema: z.object({ item: z.string() }),
  })
  @RateLimit({
    requests: 5,
    window: '1h',
    key: (ctx: ExecutionContext) => ctx.auth?.subject || 'anonymous'
  })
  async requestSupply(input: { item: string }, ctx: ExecutionContext) {
    // ...
  }
}

Gestion des téléchargements de fichiers dans les outils

NitroStack supporte les téléchargements de fichiers depuis les clients MCP (comme NitroStudio) en passant le fichier sous forme de chaîne encodée en base64 dans les paramètres d'entrée d'un outil.

1. Déclaration du schéma d'entrée pour les téléchargements de fichiers

Pour accepter un fichier téléchargé, définissez trois champs Zod dans votre inputSchema de l'outil :

  • file_name : Le nom du fichier (ex: rapport.csv).
  • file_type : Le type MIME (ex: text/csv).
  • file_content : La chaîne encodée en base64 contenant les données du fichier.
import { ToolDecorator as Tool, ExecutionContext, z } from '@nitrostack/core';

export class FileTools {
  @Tool({
    name: 'upload_document',
    description: 'Téléchargez un document texte ou une image.',
    inputSchema: z.object({
      file_name: z.string().describe('Nom du fichier téléchargé'),
      file_type: z.string().describe('Type MIME du fichier téléchargé'),
      file_content: z.string().describe('Contenu du fichier encodé en base64'),
    })
  })
  async uploadDocument(input: any, ctx: ExecutionContext) {
    // Logique de traitement
  }
}

2. Décodage des charges utiles Base64

Les téléchargements de fichiers peuvent arriver dans deux formats selon le client :

  1. Format Data URL : data:image/png;base64,iVBORw0KGgo...
  2. Format Base64 brut : iVBORw0KGgo...

Utilisez le modèle de décodeur universel suivant pour analyser l'un ou l'autre format en un Buffer Node :

import * as fs from 'fs';
import * as path from 'path';

function decodeBase64File(content: string): Buffer {
  const matches = content.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
  
  if (matches && matches.length === 3) {
    // Format Data URL - décoder matches[2]
    return Buffer.from(matches[2], 'base64');
  } else {
    // Format base64 brut - décoder directement l'entrée
    return Buffer.from(content, 'base64');
  }
}

3. Exemple de sauvegarde sécurisée de fichier

Validez toujours les chemins de répertoire pour éviter les attaques de traversée de répertoire lors de la sauvegarde des fichiers sur le disque.

import { ToolDecorator as Tool, ExecutionContext, z } from '@nitrostack/core';
import * as fs from 'fs';
import * as path from 'path';

const UPLOAD_DIR = path.join(process.cwd(), 'uploads');

export class SecureUploadTools {
  @Tool({
    name: 'save_uploaded_file',
    description: 'Décode et sauvegarde un fichier téléchargé de manière sécurisée.',
    inputSchema: z.object({
      file_name: z.string().describe('Nom du fichier téléchargé'),
      file_type: z.string().describe('Type MIME du fichier téléchargé'),
      file_content: z.string().describe('Contenu du fichier encodé en base64'),
    })
  })
  async saveFile(input: any, ctx: ExecutionContext) {
    // Assurez-vous que le répertoire uploads existe
    if (!fs.existsSync(UPLOAD_DIR)) {
      fs.mkdirSync(UPLOAD_DIR, { recursive: true });
    }

    // Chemin de destination sécurisé pour éviter la traversée de répertoire
    const safeName = path.basename(input.file_name);
    const filePath = path.join(UPLOAD_DIR, safeName);
    if (!filePath.startsWith(UPLOAD_DIR)) {
      throw new Error('Chemin de fichier invalide détecté (traversée de répertoire).');
    }

    // Décoder et écrire sur le disque
    const buffer = decodeBase64File(input.file_content);
    fs.writeFileSync(filePath, buffer);

    ctx.logger.info(`Fichier sauvegardé avec succès : ${safeName}`);
    return { success: true, path: filePath };
  }
}

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Skills associés