Scrydon

Premiers pas

Créez votre première intégration personnalisée en 5 minutes

Ce guide vous accompagne dans la création d'une intégration minimale « Hello World » qui journalise un message et transmet des données. À la fin, vous disposerez d'un fichier .bundle.tar.gz prêt à être importé.

Prérequis

  • Bun v1.1+ (ou Node.js 20+)
  • TypeScript 5.7+

Mise en place du projet

Chemin le plus rapide : générez l'intégralité du projet avec la CLI au lieu des étapes manuelles ci-dessous —

bunx @scrydon/sdk-authoring integrations init my-vendor --outDir ./integrations
cd integrations/my-vendor

Les invites interactives permettent de choisir le type d'authentification (OAuth, API key ou none), les capacités à générer et une couleur de marque, puis adaptent le fichier src/index.ts généré à vos choix. Passez --yes pour ignorer les invites et accepter les valeurs par défaut (auth = none, capacités = blocks & tools) — utile pour la CI. Dans tous les cas, vous obtenez package.json, tsconfig.json, un squelette defineVendor() et un exemple de test live dans src/__tests__/.

mkdir my-integration && cd my-integration
bun init -y

Mettez à jour package.json :

{
  "name": "my-integration",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "main": "src/index.ts"
}
bun add -d @scrydon/sdk-authoring/integrations zod
npm install --save-dev @scrydon/sdk-authoring/integrations zod

Écrire l'intégration

Créez src/index.ts :

import {
  defineVendor,
  defineProduct,
  defineTool,
  defineBlock,
} from "@scrydon/sdk-authoring/integrations/define";
import type { ToolResponse } from "@scrydon/sdk-authoring/integrations/context";
import { z } from "zod";

// 1. Define a tool — the runtime logic
const sayHelloTool = defineTool({
  id: "say-hello",
  name: "Say Hello",
  version: "1.0.0",
  description: "Logs a greeting and passes the message through.",
  input: z.object({
    message: z.string().describe("The message to pass through"),
  }),
  output: z.object({
    message: z.string().describe("The original message, unchanged"),
  }),
  params: {
    message: {
      type: "string",
      required: true,
      visibility: "user-or-llm",
      description: "Message to pass through the block",
    },
  },
  async execute(input, ctx): Promise<ToolResponse<{ message: string }>> {
    ctx.logger.info(`Hello World! Received: "${input.message}"`);
    return {
      success: true,
      output: { message: input.message },
    };
  },
});

// 2. Define a block — the workflow editor UI
const helloBlock = defineBlock({
  type: "hello_world",
  name: "Hello World",
  description: "Logs a greeting and passes input to output.",
  category: "integration",
  bgColor: "#22C55E",
  authMode: "none",
  subBlocks: [
    {
      id: "message",
      title: "Message",
      type: "short-input",
      placeholder: "Enter a message…",
      required: true,
    },
  ],
  tools: {
    access: ["say-hello"],
  },
  inputs: {
    message: { type: "string", description: "Incoming message" },
  },
  outputs: {
    message: { type: "string", description: "Outgoing message" },
  },
});

// 3. Define a product — the grouping unit
const helloProduct = defineProduct({
  id: "hello-world",
  name: "Hello World",
  description: "A minimal demo integration.",
  icon: "./assets/icon.svg",
  capabilities: {
    tools: [sayHelloTool],
    triggers: [],
  },
  block: helloBlock,
});

// 4. Export the vendor — the top-level container
export default defineVendor({
  id: "hello-world",
  name: "Hello World",
  version: "1.0.0",
  description: "A minimal example integration.",
  color: "#22C55E",
  icon: "./assets/icon.svg",
  categories: ["integration"],
  connectivity: "local",
  auth: {
    credentials: { none: { type: "none" } },
    default: "none",
  },
  products: [helloProduct],
});

Le point d'entrée doit avoir un default export qui est un résultat de defineVendor(). La CLI de build le lit pour extraire le manifeste.

Raccourci d'ID d'outil — Vous pouvez utiliser des IDs d'outil courts comme "say-hello" au lieu du nom complet "hello-world:hello-world:say-hello". Le SDK qualifie automatiquement les IDs courts en ajoutant {vendorId}:{productId}: lors de defineVendor(). Toutes les intégrations natives utilisent cette forme courte.

Comprendre les couches

Le code ci-dessus comporte quatre couches, chacune avec un rôle précis :

CoucheCe qu'elle définitChamps clés
defineTool()Logique d'exécutioninput (Zod), output (Zod), fonction execute()
defineBlock()Interface de l'éditeur de workflowsubBlocks (champs de formulaire), inputs/outputs (flux de données)
defineProduct()Unité de regroupementcapabilities.tools, block, activé/désactivé par organisation
defineVendor()Conteneur de niveau supérieurconfiguration auth, tableau products, métadonnées du vendeur

Flux de données : les subBlocks du bloc définissent les champs du formulaire. Le tableau tools.access du bloc renvoie vers les IDs d'outils. Lors de l'exécution du workflow, la plateforme appelle la fonction execute() de l'outil avec l'entrée validée et un PureContext.

Compiler le bundle

bunx @scrydon/sdk-authoring integrations build

Sortie :

Bundle created: dist/hello-world-1.0.0.bundle.tar.gz
  Vendor: Hello World (hello-world)
  Version: 1.0.0
  Products: 1
  Tools: 1
  Dependencies: 1 (SBOM: meta/sbom.cdx.json)

Ce que fait la compilation

  1. Importe votre point d'entrée et vérifie que l'export par défaut est un résultat de defineVendor()
  2. Extrait toutes les métadonnées dans manifest.json — les schémas Zod deviennent des JSON Schemas
  3. Bundle votre code avec esbuild dans un seul dist/index.js (toutes les dépendances intégrées)
  4. Génère un SBOM CycloneDX 1.6 listant tous les packages NPM du bundle (meta/sbom.cdx.json)
  5. Empaquète tout dans {vendorId}-{version}.bundle.tar.gz

Inspecter la sortie

# List archive contents
tar -tzf dist/hello-world-1.0.0.bundle.tar.gz

# Pretty-print the manifest
tar -xzf dist/hello-world-1.0.0.bundle.tar.gz -O manifest.json | python3 -m json.tool

Valider sans importer

Le SDK inclut une commande de test :

# Static validation — checks manifest, schemas, ID formats
bunx @scrydon/sdk-authoring integrations test --level static

# Sandbox validation — loads the bundle in a Worker Thread
bunx @scrydon/sdk-authoring integrations test --level sandbox

# Test a specific tool
bunx @scrydon/sdk-authoring integrations test --level sandbox --tool hello-world:hello-world:say-hello

Importer et utiliser

Consultez le guide Import pour les instructions détaillées. Version rapide :

  1. Allez dans Paramètres > Plateforme > Intégrations et sélectionnez l'onglet Personnalisées
  2. Choisissez Import manuel, puis faites glisser votre .bundle.tar.gz sur la zone d'import
  3. Cliquez sur Importer
  4. Ouvrez l'Éditeur de Workflow et recherchez « Hello World »

Étapes suivantes : ajouter l'authentification

La plupart des intégrations réelles ont besoin de credentials. Voici comment ajouter l'authentification par clé API :

// Change the auth config
export default defineVendor({
  // ...
  auth: {
    credentials: {
      apiKey: {
        type: "apiKey",
        label: "API Key",
        description: "Your service API key",
        headerName: "Authorization",
        headerPrefix: "Bearer",
      },
    },
    default: "apiKey",
  },
  // ...
});

Mettez à jour le bloc pour afficher un champ de saisie de clé API :

const myBlock = defineBlock({
  // ...
  authMode: "apiKey",
  subBlocks: [
    {
      id: "apiKey",
      title: "API Key",
      type: "short-input",
      password: true,
      placeholder: "Enter your API key",
    },
    // ... other fields
  ],
  // ...
});

La clé est ensuite disponible dans ctx.auth.apiKey à l'intérieur de votre fonction execute() :

async execute(input, ctx) {
  const response = await fetch("https://api.example.com/data", {
    headers: {
      Authorization: `Bearer ${ctx.auth.apiKey}`,
    },
  });
  const data = await response.json();
  return { success: true, output: data };
},

Étapes suivantes : plusieurs outils

Ajoutez un deuxième outil au même produit en définissant un autre defineTool() et en l'incluant dans capabilities.tools :

const reverseTool = defineTool({
  id: "reverse",
  name: "Reverse Message",
  version: "1.0.0",
  description: "Reverses the input message.",
  input: z.object({ message: z.string() }),
  output: z.object({ message: z.string() }),
  params: {
    message: {
      type: "string",
      required: true,
      visibility: "user-or-llm",
      description: "Message to reverse",
    },
  },
  async execute(input, ctx) {
    const reversed = input.message.split("").reverse().join("");
    ctx.logger.info(`Reversed: "${reversed}"`);
    return { success: true, output: { message: reversed } };
  },
});

// Add to capabilities and block
const helloProduct = defineProduct({
  // ...
  capabilities: {
    tools: [sayHelloTool, reverseTool],
    triggers: [],
  },
  block: defineBlock({
    // ...
    subBlocks: [
      {
        id: "operation",
        title: "Operation",
        type: "dropdown",
        options: [
          { label: "Say Hello", id: "say-hello" },
          { label: "Reverse", id: "reverse" },
        ],
      },
      // ... message field
    ],
    tools: {
      access: ["say-hello", "reverse"],
      config: {
        tool: (params) =>
          params.operation === "reverse"
            ? "reverse"
            : "say-hello",
      },
    },
  }),
});

La fonction tools.config.tool sélectionne dynamiquement l'outil à exécuter en fonction du choix de l'utilisateur dans la liste déroulante.

Sur cette page

Sur cette page