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-vendorLes 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-integrationbun init -yMettez à 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 zodnpm 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 :
| Couche | Ce qu'elle définit | Champs clés |
|---|---|---|
defineTool() | Logique d'exécution | input (Zod), output (Zod), fonction execute() |
defineBlock() | Interface de l'éditeur de workflow | subBlocks (champs de formulaire), inputs/outputs (flux de données) |
defineProduct() | Unité de regroupement | capabilities.tools, block, activé/désactivé par organisation |
defineVendor() | Conteneur de niveau supérieur | configuration 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 buildSortie :
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
- Importe votre point d'entrée et vérifie que l'export par défaut est un résultat de
defineVendor() - Extrait toutes les métadonnées dans
manifest.json— les schémas Zod deviennent des JSON Schemas - Bundle votre code avec esbuild dans un seul
dist/index.js(toutes les dépendances intégrées) - Génère un SBOM CycloneDX 1.6 listant tous les packages NPM du bundle (
meta/sbom.cdx.json) - 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.toolValider 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-helloImporter et utiliser
Consultez le guide Import pour les instructions détaillées. Version rapide :
- Allez dans Paramètres > Plateforme > Intégrations et sélectionnez l'onglet Personnalisées
- Choisissez Import manuel, puis faites glisser votre
.bundle.tar.gzsur la zone d'import - Cliquez sur Importer
- 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.