@codemation/agent-skills 0.1.10 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/dist/metadata.json +110 -0
- package/package.json +4 -1
- package/skills/codemation-ai-agent-node/SKILL.md +66 -0
- package/skills/codemation-ai-agent-node/references/anti-patterns.md +11 -0
- package/skills/codemation-cli/SKILL.md +30 -24
- package/skills/codemation-credential-development/SKILL.md +27 -12
- package/skills/codemation-custom-node-development/SKILL.md +42 -27
- package/skills/codemation-custom-node-development/references/credential-aware-nodes.md +38 -0
- package/skills/codemation-custom-node-development/references/define-batch-node.md +38 -0
- package/skills/codemation-custom-node-development/references/define-node-per-item.md +61 -0
- package/skills/codemation-custom-node-development/references/node-patterns.md +141 -0
- package/skills/codemation-framework-concepts/SKILL.md +23 -27
- package/skills/codemation-mcp-capabilities/SKILL.md +53 -0
- package/skills/codemation-mcp-capabilities/references/agent-with-mcp.ts +44 -0
- package/skills/codemation-plugin-development/SKILL.md +11 -32
- package/skills/codemation-plugin-development/references/plugin-anatomy.md +115 -0
- package/skills/codemation-workflow-dsl/SKILL.md +49 -59
- package/skills/codemation-workflow-dsl/references/builder-patterns.md +47 -15
- package/skills/codemation-workflow-dsl/references/complete-example.md +263 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Define Batch Node
|
|
2
|
+
|
|
3
|
+
Load this when you need to author a `defineBatchNode(...)` node that processes all items in one call.
|
|
4
|
+
|
|
5
|
+
## When to use `defineBatchNode` instead of `defineNode`
|
|
6
|
+
|
|
7
|
+
- The node must see the **entire activation batch** at once (e.g. an aggregation, a bulk API call, or a node that correlates items against each other).
|
|
8
|
+
- Legacy batch semantics are required by the calling workflow.
|
|
9
|
+
- You need the same contract as built-in batch-shaped nodes such as `Aggregate`.
|
|
10
|
+
|
|
11
|
+
For the common case (one-item-at-a-time logic), prefer `defineNode` — the engine handles iteration for you.
|
|
12
|
+
|
|
13
|
+
## Minimal skeleton
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { defineBatchNode } from "@codemation/core";
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
|
|
19
|
+
export const sumNode = defineBatchNode({
|
|
20
|
+
key: "example.sum",
|
|
21
|
+
title: "Sum numeric field",
|
|
22
|
+
inputSchema: z.object({ value: z.number() }),
|
|
23
|
+
async run(items, { config }) {
|
|
24
|
+
const total = items.reduce((acc, item) => acc + (item.json as { value: number }).value, 0);
|
|
25
|
+
return [{ json: { total } }];
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Contract
|
|
31
|
+
|
|
32
|
+
- `run(items, context)` receives the **full array** of activation items.
|
|
33
|
+
- Return an array of output items (same length as input is not required — you can fan-in to one, or fan-out to many).
|
|
34
|
+
- The context object exposes `config`, `credentials`, and `execution` (same as `defineNode`).
|
|
35
|
+
|
|
36
|
+
## Advanced fallback
|
|
37
|
+
|
|
38
|
+
Reach for class-based node APIs when constructor-injected collaborators are required, plugin packaging needs the lower-level runtime contract, or decorators/persisted metadata need tighter control.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Define Node (Per-Item)
|
|
2
|
+
|
|
3
|
+
Load this when you need to author a `defineNode(...)` node that processes one item at a time.
|
|
4
|
+
|
|
5
|
+
## When to use `defineNode`
|
|
6
|
+
|
|
7
|
+
- Node logic is straightforward.
|
|
8
|
+
- The node belongs to one app or plugin package.
|
|
9
|
+
- Helper-based credential slots are sufficient.
|
|
10
|
+
- You do not need to inspect the entire batch in one call.
|
|
11
|
+
|
|
12
|
+
## Minimal skeleton
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { defineNode } from "@codemation/core";
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
|
|
18
|
+
export const uppercaseNode = defineNode({
|
|
19
|
+
key: "example.uppercase",
|
|
20
|
+
title: "Uppercase field",
|
|
21
|
+
icon: "lucide:languages", // optional — Lucide, builtin:, si:, or image URL
|
|
22
|
+
inputSchema: z.object({ field: z.string() }),
|
|
23
|
+
async execute({ input, item }, { config }) {
|
|
24
|
+
return { ...input, [config.field]: String(input.field).toUpperCase() };
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Contract
|
|
30
|
+
|
|
31
|
+
- `execute(args, context)` is called **once per item** by the engine.
|
|
32
|
+
- Return a plain JSON object → one output item on `main`.
|
|
33
|
+
- Return a top-level array → **fan-out** (one item per element).
|
|
34
|
+
- Return `emitPorts({ portName: [...] })` for multi-port routing.
|
|
35
|
+
- Return an item-shaped `{ json, binary?, meta?, paired? }` when you need explicit binary/meta control.
|
|
36
|
+
|
|
37
|
+
## Config fields with `itemExpr`
|
|
38
|
+
|
|
39
|
+
Place **static** options (credentials, retry policy, labels) on `config`; place **per-item** values in `inputs` using `itemExpr` on config fields — consistent with built-in nodes.
|
|
40
|
+
|
|
41
|
+
## Input schema and `inputSchema`
|
|
42
|
+
|
|
43
|
+
Supply `inputSchema` (Zod) to get typed `input` in `execute` and to drive the canvas form. The engine validates items against it before calling `execute`.
|
|
44
|
+
|
|
45
|
+
## Testing with `WorkflowTestKit`
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { createEngineTestKit, registerDefinedNodes } from "@codemation/core/testing";
|
|
49
|
+
|
|
50
|
+
const kit = createEngineTestKit();
|
|
51
|
+
registerDefinedNodes([uppercaseNode]);
|
|
52
|
+
const result = await kit.runNode(uppercaseNode, { json: { field: "hello" } });
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Use `WorkflowTestKit` from `@codemation/core/testing` for engine-backed tests without the host.
|
|
56
|
+
|
|
57
|
+
## Custom assertion nodes
|
|
58
|
+
|
|
59
|
+
Set `emitsAssertions: true` on the node config to record results into `TestSuiteRun` infrastructure. The host's `TestSuiteRunTracker` listens for `nodeCompleted` events on runs with `ctx.testContext` set and persists each emitted item (matching `AssertionResult`) as a `TestAssertion` row.
|
|
60
|
+
|
|
61
|
+
Per-item nodes can also read `ctx.testContext?.{testSuiteRunId, testCaseIndex}` to branch on test mode — useful for synthetic outputs or skipping irreversible side effects.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Node Patterns
|
|
2
2
|
|
|
3
|
+
Load this when working with file data, binary payloads, HTTP binaries, MS Graph attachments, or when you need reference on fan-out return shapes and polling-trigger binary patterns.
|
|
4
|
+
|
|
3
5
|
## Start here
|
|
4
6
|
|
|
5
7
|
Use `defineNode(...)` when:
|
|
@@ -78,3 +80,142 @@ Reach for class-based node APIs when:
|
|
|
78
80
|
1. In `runCycle` (the polling step), fetch only the **metadata** (id, name, contentType, size). The result is persisted into the trigger's setup state and into emitted item JSON, so it must stay small.
|
|
79
81
|
2. In `execute(items, ctx)`, when the cfg opts into downloads, fetch each blob's bytes from the source API and register them via `ctx.binary.attach(...)`. Then return items via `ctx.binary.withAttachment(item, slot, stored)`.
|
|
80
82
|
- **Do not** request the full payload in the polling fetch (e.g. Microsoft Graph `$expand=attachments` returns base64 `contentBytes` inline; use `$expand=attachments($select=id,name,contentType,size)` to keep the response light). Large polling responses bloat the run state on every cycle, even when no item is emitted.
|
|
83
|
+
|
|
84
|
+
## Binary payloads in sub-workflow chains
|
|
85
|
+
|
|
86
|
+
Binary slots attached inside a node survive SubWorkflow boundaries with no extra work. The shared `BinaryStorage` DI singleton means `ctx.binary.openReadStream` works regardless of which run originally stored the bytes.
|
|
87
|
+
|
|
88
|
+
### Pattern: attach in a node, read in the parent after SubWorkflow
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
// Child node — attaches a slot and returns the modified item.
|
|
92
|
+
export const parseAndStoreNode = defineNode({
|
|
93
|
+
key: "example.parse-store",
|
|
94
|
+
title: "Parse and Store",
|
|
95
|
+
inputSchema: z.object({ filename: z.string() }),
|
|
96
|
+
async execute({ input, item }, { binary }) {
|
|
97
|
+
const bytes = Buffer.from("...parsed content...");
|
|
98
|
+
const att = await binary.attach({
|
|
99
|
+
name: "parsed",
|
|
100
|
+
body: bytes,
|
|
101
|
+
mimeType: "text/plain",
|
|
102
|
+
filename: `${input.filename}.txt`,
|
|
103
|
+
});
|
|
104
|
+
return binary.withAttachment(item, "parsed", att);
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
After `SubWorkflowNode` returns, the parent's continuation nodes see `item.binary["parsed"]` and can call `ctx.binary.openReadStream(item.binary["parsed"])` to read the bytes.
|
|
110
|
+
|
|
111
|
+
### Testing binary across SubWorkflow with `WorkflowTestKit`
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { DefaultExecutionContextFactory, InMemoryBinaryStorage } from "@codemation/core";
|
|
115
|
+
import { createEngineTestKit } from "@codemation/core/testing";
|
|
116
|
+
import { ItemHarnessNodeConfig } from "@codemation/core/testing";
|
|
117
|
+
|
|
118
|
+
const storage = new InMemoryBinaryStorage();
|
|
119
|
+
const kit = createEngineTestKit({
|
|
120
|
+
executionContextFactory: new DefaultExecutionContextFactory(storage),
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Use ItemHarnessNodeConfig (NOT CallbackNodeConfig) for nodes that must modify items:
|
|
124
|
+
const attachNode = new ItemHarnessNodeConfig(
|
|
125
|
+
"Attach",
|
|
126
|
+
z.unknown(),
|
|
127
|
+
async ({ item, ctx }) => {
|
|
128
|
+
const att = await ctx.binary.attach({
|
|
129
|
+
name: "doc",
|
|
130
|
+
body: Buffer.from("content"),
|
|
131
|
+
mimeType: "application/pdf",
|
|
132
|
+
filename: "doc.pdf",
|
|
133
|
+
});
|
|
134
|
+
return ctx.binary.withAttachment(item as Item, "doc", att);
|
|
135
|
+
},
|
|
136
|
+
{ id: "attach" },
|
|
137
|
+
);
|
|
138
|
+
// CallbackNodeConfig is fine for assertion-only (observe) nodes — it echoes input unchanged.
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Important: `CallbackNodeConfig` discards its callback return value and always echoes input items. Never use it for nodes that must attach binary or transform items.
|
|
142
|
+
|
|
143
|
+
## MS Graph: selective attachment download
|
|
144
|
+
|
|
145
|
+
Use `OutlookAttachmentDownload` from `@codemation/core-nodes-msgraph` when you have already obtained attachment metadata (filename, contentType, id) and want to download only specific attachments.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import { onNewMsGraphMailTrigger, outlookAttachmentDownloadNode } from "@codemation/core-nodes-msgraph";
|
|
149
|
+
|
|
150
|
+
workflow("wf.download-resumes")
|
|
151
|
+
.trigger(onNewMsGraphMailTrigger, { mailbox: "me", folderId: "inbox" })
|
|
152
|
+
.then(
|
|
153
|
+
outlookAttachmentDownloadNode.create(
|
|
154
|
+
{
|
|
155
|
+
messageId: "", // falls back to item.json when empty
|
|
156
|
+
attachmentId: "", // falls back to item.json when empty
|
|
157
|
+
binarySlot: "resume",
|
|
158
|
+
sizeCapBytes: 10 * 1024 * 1024,
|
|
159
|
+
},
|
|
160
|
+
"DownloadResume",
|
|
161
|
+
),
|
|
162
|
+
)
|
|
163
|
+
.build();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Key constraints:
|
|
167
|
+
|
|
168
|
+
- Only `#microsoft.graph.fileAttachment` is supported — `itemAttachment` / `referenceAttachment` throw immediately.
|
|
169
|
+
- Set `keepBinaries: true` on any downstream node that needs to pass the binary slot forward.
|
|
170
|
+
- The credential is `msGraphMailOAuthCredentialType`; `Mail.Read` scope is sufficient.
|
|
171
|
+
|
|
172
|
+
## HTTP + binary: download to a slot, then upload from a slot
|
|
173
|
+
|
|
174
|
+
`HttpRequest` (from `@codemation/core-nodes`) natively handles binary response and request bodies.
|
|
175
|
+
|
|
176
|
+
### Download a file to a binary slot
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
import { HttpRequest } from "@codemation/core-nodes";
|
|
180
|
+
import { workflow } from "@codemation/host";
|
|
181
|
+
|
|
182
|
+
export default workflow("wf.download-pdf")
|
|
183
|
+
.manualTrigger<{ url: string }>("Start", { url: "" })
|
|
184
|
+
.then(
|
|
185
|
+
new HttpRequest("DownloadResume", {
|
|
186
|
+
responseFormat: "binary",
|
|
187
|
+
responseBinarySlot: "resume", // default is "response"
|
|
188
|
+
responseSizeCapBytes: 10 * 1024 * 1024, // 10 MiB cap (default 100 MiB)
|
|
189
|
+
}),
|
|
190
|
+
)
|
|
191
|
+
.build();
|
|
192
|
+
// item.json gets: { status, headers, binarySlot, contentType, size, filename? }
|
|
193
|
+
// item.binary["resume"] holds the BinaryAttachment reference — never base64.
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Upload binary bytes from a slot
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
new HttpRequest("UploadResume", {
|
|
200
|
+
method: "POST",
|
|
201
|
+
url: "https://api.example.com/files",
|
|
202
|
+
body: { kind: "binary", slot: "resume" },
|
|
203
|
+
// Content-Type defaults to the attachment's mimeType.
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Download then upload (full round-trip)
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
export default workflow("wf.mirror-pdf")
|
|
211
|
+
.manualTrigger<{ sourceUrl: string; targetUrl: string }>("Start", { sourceUrl: "", targetUrl: "" })
|
|
212
|
+
.then(new HttpRequest("Download", { urlField: "sourceUrl", responseFormat: "binary", responseBinarySlot: "file" }))
|
|
213
|
+
.then(new HttpRequest("Upload", { urlField: "targetUrl", method: "PUT", body: { kind: "binary", slot: "file" } }))
|
|
214
|
+
.build();
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Key rules:
|
|
218
|
+
|
|
219
|
+
- Never put bytes or base64 in `item.json` — always use `ctx.binary`.
|
|
220
|
+
- `responseSizeCapBytes` is checked against `Content-Length` before reading the body; set it for untrusted sources.
|
|
221
|
+
- Use `keepBinaries: true` on downstream nodes that must forward the slot.
|
|
@@ -1,45 +1,41 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: codemation-framework-concepts
|
|
3
|
-
description: Explains Codemation package boundaries, runtime concepts, observability shape, and the normal consumer mental model. Use when the user asks where code belongs across `@codemation/core`, `@codemation/host`, `@codemation/next-host`, `@codemation/cli`, workflows, plugins, credentials, activation, telemetry, or runtime modes.
|
|
3
|
+
description: Explains Codemation package boundaries, runtime concepts, observability shape, and the normal consumer mental model. Use when the user asks where code belongs across `@codemation/core`, `@codemation/host`, `@codemation/next-host`, `@codemation/cli`, workflows, plugins, credentials, activation, telemetry, or runtime modes. Read this first when starting any Codemation task — it points at the right skill for the work.
|
|
4
4
|
compatibility: Designed for Codemation apps, plugins, and framework contributors.
|
|
5
|
+
tags: concepts, architecture
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
# Codemation Framework Concepts
|
|
8
9
|
|
|
9
|
-
##
|
|
10
|
+
## Mental model
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
Codemation is a workflow engine with a layered package structure. `@codemation/core` owns the engine and runtime contracts (must stay pure — no HTTP, UI, or vendor SDKs). `@codemation/host` adds persistence, credentials, APIs, and scheduler wiring. `@codemation/next-host` is the framework UI shell. `@codemation/cli` runs local development, build, and serve. Consumer apps define behavior in `codemation.config.ts` and `src/workflows/` — they never touch core internals.
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
## When to use / when NOT
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
Use this skill to orient on package ownership, runtime shape, observability boundaries, and the consumer/framework divide.
|
|
17
|
+
Do not use as a substitute for detailed CLI, workflow DSL, or plugin implementation guidance when you already know which skill you need.
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
2. `@codemation/host` adds config loading, persistence, credentials, APIs, and scheduler wiring.
|
|
19
|
-
3. `@codemation/next-host` owns the framework UI.
|
|
20
|
-
4. `@codemation/cli` runs local development, build, serve, and user commands.
|
|
21
|
-
5. Consumer apps define `codemation.config.ts` and workflow files.
|
|
19
|
+
## Core concepts
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
- **workflows** define behavior; **triggers** start runs; **nodes** process items; **items** carry `item.json` data.
|
|
22
|
+
- **credentials** provide typed runtime resources (bound per operator instance, not per workflow code).
|
|
23
|
+
- **activation** is framework-managed and happens in the UI — consumer code does not call it directly.
|
|
24
|
+
- **telemetry** is observability-first: traces, spans, artifacts, and metric points are framework-owned runtime data.
|
|
25
|
+
- **workflow testing** is a first-class primitive: `TestTrigger` yields one item per test case; `Assertion` nodes record per-run results into `TestAssertion` rows; the canvas exposes a Tests tab.
|
|
26
|
+
- **run retention** and **telemetry retention** can differ — trend data can outlive raw run state.
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
- triggers start runs
|
|
27
|
-
- nodes process items
|
|
28
|
-
- items carry workflow data
|
|
29
|
-
- credentials provide typed runtime resources
|
|
30
|
-
- activation is framework-managed and happens in the UI
|
|
31
|
-
- telemetry is observability-first: traces, spans, artifacts, and metric points are framework-owned runtime data
|
|
32
|
-
- run retention and telemetry retention can differ, so trend data can outlive raw run state
|
|
33
|
-
- **workflow testing** is a first-class primitive: a `TestTrigger` node yields one item per test case, the orchestrator dispatches a workflow run per case with `executionOptions.testContext` set, and `Assertion` nodes (`emitsAssertions: true`) record per-run results into `TestAssertion` rows; the canvas exposes a Tests tab parallel to Live and Executions
|
|
28
|
+
## Where to go next
|
|
34
29
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
30
|
+
| Task | Skill |
|
|
31
|
+
| ------------------------------------- | ------------------------------------ |
|
|
32
|
+
| Authoring workflows | `codemation-workflow-dsl` |
|
|
33
|
+
| Building a reusable node | `codemation-custom-node-development` |
|
|
34
|
+
| Building a credential type | `codemation-credential-development` |
|
|
35
|
+
| Packaging as a plugin | `codemation-plugin-development` |
|
|
36
|
+
| Calling an MCP server from a workflow | `codemation-mcp-capabilities` |
|
|
37
|
+
| CLI commands / dev loop | `codemation-cli` |
|
|
41
38
|
|
|
42
39
|
## Read next when needed
|
|
43
40
|
|
|
44
41
|
- Read `references/architecture-map.md` for package ownership and runtime-mode guidance.
|
|
45
|
-
- Use the `codemation-workflow-dsl` skill (and its `references/workflow-testing.md`) for hands-on test authoring with TestTrigger / IsTestRun / Assertion.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codemation-mcp-capabilities
|
|
3
|
+
description: Discover MCP servers registered on the Codemation control plane. Use before authoring agent workflows that reference mcpServers to find available server ids and their credential requirements.
|
|
4
|
+
compatibility: Requires an installation paired with a connected control plane (Sprint 2+).
|
|
5
|
+
tags: mcp, agent, tool
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Codemation MCP Capabilities
|
|
9
|
+
|
|
10
|
+
## Mental model
|
|
11
|
+
|
|
12
|
+
MCP servers extend `AIAgent` with tool access to external services (Gmail, Sheets, etc.). Server ids and credential requirements come from the control-plane registry — they are not hard-coded in framework code. The agent's `mcpServers` array contains stable server id slugs; each declared server surfaces a credential slot the operator must bind in the canvas before activation.
|
|
13
|
+
|
|
14
|
+
## When to use / when NOT
|
|
15
|
+
|
|
16
|
+
Use this skill before writing `agent({ mcpServers: ["..."] })` to discover available server ids and their credential types.
|
|
17
|
+
Do not use for general AIAgent authoring — read `codemation-ai-agent-node` for that.
|
|
18
|
+
|
|
19
|
+
## Managed mode: CP-loaded MCP servers (default path)
|
|
20
|
+
|
|
21
|
+
In **managed mode**, MCP servers are loaded from the **control plane (CP)** — not declared in plugin code. Discover available servers by querying the CP registry:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
GET /api/registry/capabilities?query=gmail
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Response contains objects with `{ kind, id, displayName, description, acceptedCredentialTypes }`. Use `id` in the workflow's `mcpServers` array. An empty `query` string returns all registered servers.
|
|
28
|
+
|
|
29
|
+
For a full wired example — cron workflow + AIAgent + mcpServers — use your harness's example-discovery tool: `find_examples({ query: "AIAgent gmail mcpServers" })` or `find_examples({ query: "mcp server" })`.
|
|
30
|
+
|
|
31
|
+
## Non-managed: plugin-declared MCP servers
|
|
32
|
+
|
|
33
|
+
In self-hosted / non-managed deployments, MCP servers can also be declared via `mcpServers: [...]` in a `definePlugin(...)` call. This is a framework-author pattern — do not use it in managed-mode workflows. See `references/plugin-anatomy.md` in the `codemation-plugin-development` skill for the plugin declaration syntax.
|
|
34
|
+
|
|
35
|
+
## Decision branches & gotchas
|
|
36
|
+
|
|
37
|
+
**Credential types:** `"oauth.google.gmail"` requires the user to connect a Google account via the credential dialog before the workflow runs. The same instance can be shared between a `GmailTrigger` and the Gmail MCP server. An empty `acceptedCredentialTypes` array means no credential is needed.
|
|
38
|
+
|
|
39
|
+
**Multiple instances:** a user may have multiple instances of the same credential type (personal vs work Gmail). The canvas credential dropdown surfaces all matching instances — the operator picks the one to bind.
|
|
40
|
+
|
|
41
|
+
**Bind via UI only:** there is no inline credential field on the workflow definition. The operator binds the credential instance via the canvas credential dropdown before activation.
|
|
42
|
+
|
|
43
|
+
**Typical flow (managed):**
|
|
44
|
+
|
|
45
|
+
1. `GET /api/registry/capabilities?query=<term>` → find `id` and `acceptedCredentialTypes`.
|
|
46
|
+
2. Add `id` to `mcpServers` in the `AIAgent` config.
|
|
47
|
+
3. Report: "The user will need to bind a `<type>` credential instance via the canvas before activating."
|
|
48
|
+
|
|
49
|
+
## Anti-patterns
|
|
50
|
+
|
|
51
|
+
- Do not guess server ids — always query the registry first.
|
|
52
|
+
- Do not add `acceptedCredentialTypes` to the workflow definition — credential binding is UI-driven, not code-driven.
|
|
53
|
+
- Do not declare MCP servers inside plugin code for managed-mode workflows — use the CP registry instead.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference: using an MCP server in a workflow agent node.
|
|
3
|
+
*
|
|
4
|
+
* Before writing this, call GET /api/registry/capabilities?query=<name> to confirm
|
|
5
|
+
* the server id and credential type. Then list the server id under `mcpServers`.
|
|
6
|
+
*
|
|
7
|
+
* Cron / webhook workflows use createWorkflowBuilder({id, name}).trigger(new XxxTrigger(...))
|
|
8
|
+
* and chain with .then(new SomeNodeConfig(...)). The fluent .map/.if/.agent helpers are
|
|
9
|
+
* only available via workflow("id").manualTrigger(...). See codemation-workflow-dsl skill.
|
|
10
|
+
*
|
|
11
|
+
* `mcpServers` is a plain array of server ids. Each declared server surfaces a credential
|
|
12
|
+
* slot on the materialized MCP connection node (same shape as ChatModel/Tool connection
|
|
13
|
+
* nodes). The user binds a credential instance via the canvas credential dropdown before
|
|
14
|
+
* activation — same flow as trigger credentials.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { AIAgent, CronTrigger, createWorkflowBuilder } from "@codemation/core-nodes";
|
|
18
|
+
|
|
19
|
+
// Example: cron-triggered agent that uses the Gmail MCP server.
|
|
20
|
+
// The "gmail" id comes from the registry (acceptedCredentialTypes: ["oauth.google.gmail"]).
|
|
21
|
+
// The user must have connected their Google account and bound the credential before this runs.
|
|
22
|
+
|
|
23
|
+
export const summariseEmailsWorkflow = createWorkflowBuilder({
|
|
24
|
+
id: "wf.summarise-emails",
|
|
25
|
+
name: "Summarise unread emails",
|
|
26
|
+
})
|
|
27
|
+
.trigger(new CronTrigger("Weekdays at 09:00", { schedule: "0 9 * * 1-5", timezone: "UTC" }))
|
|
28
|
+
.then(
|
|
29
|
+
new AIAgent({
|
|
30
|
+
name: "Summarise",
|
|
31
|
+
mcpServers: ["gmail"],
|
|
32
|
+
messages: [
|
|
33
|
+
{
|
|
34
|
+
role: "system",
|
|
35
|
+
content: [
|
|
36
|
+
"You are an email assistant. Read the user's unread Gmail messages from the last 24 hours.",
|
|
37
|
+
"Summarise each one in one sentence. Output as a bullet list.",
|
|
38
|
+
"Do not draft or send any replies.",
|
|
39
|
+
].join("\n"),
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
}),
|
|
43
|
+
)
|
|
44
|
+
.build();
|
|
@@ -2,49 +2,28 @@
|
|
|
2
2
|
name: codemation-plugin-development
|
|
3
3
|
description: Guides Codemation plugin package development, including `definePlugin(...)`, plugin sandboxes, custom nodes, custom credentials, and publishable plugin package structure. Use when building or updating a Codemation plugin package or the plugin starter template.
|
|
4
4
|
compatibility: Designed for Codemation plugin packages and the Codemation plugin starter template.
|
|
5
|
+
tags: plugin, node, package
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
# Codemation Plugin Development
|
|
8
9
|
|
|
9
|
-
##
|
|
10
|
+
## Mental model
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
A Codemation plugin is an npm package with a `codemation.plugin.ts` composition root that calls `definePlugin(...)`. It registers custom nodes and credential types, optionally declares MCP servers, and ships a sandbox app so the plugin is immediately testable. Consumers load the built JavaScript entry (`package.json#codemation.plugin`) — not TypeScript source. Plugin code follows the same `defineNode` / `defineCredential` patterns as app-level code; the plugin boundary is purely about packaging and distribution.
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
## When to use / when NOT
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
**Plugin authoring is a framework-author / non-managed task.** Managed-mode agents work with credential slots and workflow DSL — they do not author or modify plugin packages.
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
2. Register custom credentials and custom nodes from explicit modules.
|
|
19
|
-
3. Keep the sandbox app small and useful so plugin behavior is testable immediately.
|
|
20
|
-
4. Prefer helper-based node and credential definitions first, then drop to class-based APIs only when needed.
|
|
18
|
+
Use this skill for published plugin packages, plugin starter work, and sandbox-driven plugin development. Do not use for ordinary consumer workflow-only changes.
|
|
21
19
|
|
|
22
|
-
##
|
|
20
|
+
## Decision branches & gotchas
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
2. Keep plugin registration separate from node and credential implementation modules.
|
|
26
|
-
3. Use the sandbox app to exercise the plugin right away.
|
|
27
|
-
4. Keep the package publishable like a normal npm package.
|
|
28
|
-
5. Treat `codemation.plugin.ts` as the plugin repo's source composition root; consumer projects should load the built JavaScript entry declared in `package.json#codemation.plugin`.
|
|
22
|
+
**MCP servers in plugins:** Plugin-declared `mcpServers` is a non-managed pattern for self-hosted / framework-author scenarios. In managed mode, MCP servers are loaded from the control plane — see `codemation-mcp-capabilities` for the managed path.
|
|
29
23
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
- `codemation.plugin.ts`: plugin registration and sandbox app source, compiled to the published plugin entry in `dist/`
|
|
33
|
-
- `src/nodes/*`: custom node definitions (`defineNode` → **`execute`**; `defineBatchNode` → batch **`run`**)
|
|
34
|
-
- `src/credentialTypes/*`: custom credential definitions
|
|
35
|
-
- `src/index.ts`: package exports
|
|
36
|
-
- `test/*.test.ts` (optional): Vitest + `WorkflowTestKit` from `@codemation/core/testing` for engine-backed unit tests without starting the full host (`pnpm test`)
|
|
37
|
-
|
|
38
|
-
## Packaging guardrail
|
|
39
|
-
|
|
40
|
-
- `package.json#codemation.plugin` should point at runnable JavaScript such as `./dist/codemation.plugin.js`.
|
|
41
|
-
- Do not rely on consumers TypeScript-loading plugin files from `node_modules`.
|
|
42
|
-
- Prefer publishing `dist/**` plus package metadata/docs rather than shipping source-only plugin entry files as runtime dependencies.
|
|
43
|
-
|
|
44
|
-
## Unit tests (`WorkflowTestKit`)
|
|
45
|
-
|
|
46
|
-
Import **`WorkflowTestKit`** from **`@codemation/core/testing`**. Use **`registerDefinedNodes([...])`** for `defineNode` packages, then **`runNode({ node: yourNode.create(...), items })`** or **`run({ workflow, items })`** for fuller graphs. Prefer this for fast node tests; use **`codemation dev:plugin`** when you need the UI and persistence.
|
|
24
|
+
**Publishing guardrail:** `package.json#codemation.plugin` must point at runnable JavaScript (`./dist/codemation.plugin.js`). Do not rely on consumers TypeScript-loading plugin files from `node_modules`.
|
|
47
25
|
|
|
48
26
|
## Read next when needed
|
|
49
27
|
|
|
50
|
-
- Read `references/plugin-
|
|
28
|
+
- Read `references/plugin-anatomy.md` for the full `definePlugin(...)` code, package layout, sandbox setup, MCP server declaration, binary payload rules, and publishing guidance.
|
|
29
|
+
- Read `references/plugin-structure.md` for a concise package layout reference.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Plugin Anatomy
|
|
2
|
+
|
|
3
|
+
Plugin authoring is a **framework-author / non-managed task**. Managed-mode agents almost never need to create or modify plugin packages — they work with credential slots and workflow DSL. This reference is for developers building and publishing reusable Codemation plugin packages.
|
|
4
|
+
|
|
5
|
+
## Quickstart
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { definePlugin } from "@codemation/host/authoring";
|
|
9
|
+
|
|
10
|
+
export default definePlugin({
|
|
11
|
+
nodes: [myNode],
|
|
12
|
+
credentials: [myCredentialType],
|
|
13
|
+
// mcpServers: [...], // optional — see MCP section below
|
|
14
|
+
});
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Plugin package layout
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
codemation.plugin.ts ← composition root; calls definePlugin(...)
|
|
21
|
+
src/
|
|
22
|
+
nodes/ ← defineNode / defineBatchNode / defineRestNode files
|
|
23
|
+
credentialTypes/ ← defineCredential files
|
|
24
|
+
index.ts ← public package exports (types, session shapes)
|
|
25
|
+
test/
|
|
26
|
+
*.test.ts ← Vitest + WorkflowTestKit tests
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Composition root (`codemation.plugin.ts`)
|
|
30
|
+
|
|
31
|
+
The single file that:
|
|
32
|
+
|
|
33
|
+
- calls `definePlugin(...)` and registers nodes + credentials
|
|
34
|
+
- optionally defines a sandbox app via `defineCodemationApp(...)`
|
|
35
|
+
|
|
36
|
+
Consumers discover the plugin through `package.json#codemation.plugin`, which must point at built JavaScript in `dist/` — NOT TypeScript source.
|
|
37
|
+
|
|
38
|
+
## Node guidance
|
|
39
|
+
|
|
40
|
+
- Start with `defineNode(...)` and `execute(...)` for per-item nodes (most common).
|
|
41
|
+
- Use `defineBatchNode(...)` only when the node must process the whole activation batch in one `run(items, ...)`.
|
|
42
|
+
- Keep runtime logic close to the node definition; use class-based APIs only when you need constructor-injected collaborators.
|
|
43
|
+
|
|
44
|
+
## Credential guidance
|
|
45
|
+
|
|
46
|
+
- Start with `defineCredential(...)`.
|
|
47
|
+
- Build typed sessions in `createSession(...)`.
|
|
48
|
+
- Implement `test(...)` so operators can validate configuration before activation.
|
|
49
|
+
- For OAuth2 redirect flows, use the URL-template variant (`auth: { kind: "oauth2", authorizeUrl, tokenUrl, scopes }`).
|
|
50
|
+
- See the `codemation-credential-development` skill for detailed credential patterns.
|
|
51
|
+
|
|
52
|
+
## Declaring MCP servers in a plugin
|
|
53
|
+
|
|
54
|
+
> **Non-managed pattern.** In managed mode, MCP servers are loaded from the control plane — see `codemation-mcp-capabilities`. Plugin-declared MCP servers are for self-hosted / framework-author scenarios.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { definePlugin } from "@codemation/host/authoring";
|
|
58
|
+
import type { McpServerDeclaration } from "@codemation/host/authoring";
|
|
59
|
+
|
|
60
|
+
const myMcpServer: McpServerDeclaration = {
|
|
61
|
+
id: "my-provider-mcp", // globally unique slug /^[a-z0-9-]+$/
|
|
62
|
+
displayName: "My Provider",
|
|
63
|
+
description: "Exposes My Provider tools to AIAgent.",
|
|
64
|
+
transport: "streamable-http",
|
|
65
|
+
url: "https://my-provider.example.com/mcp",
|
|
66
|
+
acceptedCredentialTypes: ["my-provider.api-key"],
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default definePlugin({
|
|
70
|
+
nodes: [myNode],
|
|
71
|
+
credentials: [myCredentialType],
|
|
72
|
+
mcpServers: [myMcpServer],
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Merge precedence:** plugin declarations < `codemation.config.ts` < control-plane registry. A warning is logged when a higher-priority source shadows a plugin declaration.
|
|
77
|
+
|
|
78
|
+
Use plugin-declared MCP servers only when the provider has non-standard auth or when co-locating with custom nodes for the same provider. For standard OAuth/API-key providers, prefer the control-plane registry.
|
|
79
|
+
|
|
80
|
+
## WorkflowTestKit
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { WorkflowTestKit } from "@codemation/core/testing";
|
|
84
|
+
// For defineNode packages:
|
|
85
|
+
import { registerDefinedNodes } from "@codemation/core/testing";
|
|
86
|
+
registerDefinedNodes([myNode]);
|
|
87
|
+
// Then use runNode(...) or run(...) for fuller graph tests.
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Binary payloads — never put bytes on item.json
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
// Inside execute(items, ctx) when a node fetches binary content:
|
|
94
|
+
const stored = await ctx.binary.attach({
|
|
95
|
+
name: "report.pdf",
|
|
96
|
+
body: Buffer.from(bytes),
|
|
97
|
+
mimeType: "application/pdf",
|
|
98
|
+
filename: "report.pdf",
|
|
99
|
+
});
|
|
100
|
+
const enriched = ctx.binary.withAttachment(item, "report.pdf", stored);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Only the `BinaryAttachment` reference (id, storageKey, mimeType, size) belongs on the item — not the bytes.
|
|
104
|
+
|
|
105
|
+
## Publishing
|
|
106
|
+
|
|
107
|
+
- `package.json#codemation.plugin` must point at `./dist/codemation.plugin.js`.
|
|
108
|
+
- Do not rely on consumers TypeScript-loading plugin files from `node_modules`.
|
|
109
|
+
- Treat the plugin as a normal npm package: install it in a Codemation app for auto-discovery.
|
|
110
|
+
|
|
111
|
+
## Anti-patterns
|
|
112
|
+
|
|
113
|
+
- Do not put plugin registration logic inside workflow files — use `codemation.plugin.ts`.
|
|
114
|
+
- Do not ship source-only plugin entries as runtime dependencies — publish `dist/**`.
|
|
115
|
+
- Do not declare an MCP server in a plugin for standard OAuth/API-key providers already in the control-plane registry.
|