@netpad/mcp-server 2.2.0 → 2.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/dist/index.js CHANGED
@@ -14105,6 +14105,59 @@ var FIELD_TYPES = [
14105
14105
  included: true,
14106
14106
  layout: { type: "image", imageUrl: "https://example.com/logo.png", alignment: "center" }
14107
14107
  }
14108
+ },
14109
+ // File upload fields
14110
+ {
14111
+ type: "file",
14112
+ category: "file",
14113
+ description: "File upload field for images and documents",
14114
+ muiComponent: "FileUpload (custom)",
14115
+ example: {
14116
+ path: "attachment",
14117
+ label: "Upload File",
14118
+ type: "file",
14119
+ included: true,
14120
+ validation: { maxSize: 10485760 }
14121
+ // 10MB
14122
+ }
14123
+ },
14124
+ {
14125
+ type: "file_upload",
14126
+ category: "file",
14127
+ description: "File upload field (alias for file)",
14128
+ muiComponent: "FileUpload (custom)",
14129
+ example: {
14130
+ path: "document",
14131
+ label: "Upload Document",
14132
+ type: "file_upload",
14133
+ included: true
14134
+ }
14135
+ },
14136
+ {
14137
+ type: "image_upload",
14138
+ category: "file",
14139
+ description: "Image-specific upload field with preview",
14140
+ muiComponent: "ImageUpload (custom)",
14141
+ example: {
14142
+ path: "profilePhoto",
14143
+ label: "Profile Photo",
14144
+ type: "image_upload",
14145
+ included: true,
14146
+ validation: { accept: "image/*" }
14147
+ }
14148
+ },
14149
+ {
14150
+ type: "document_upload",
14151
+ category: "file",
14152
+ description: "Document upload field (PDF, Word, Excel)",
14153
+ muiComponent: "DocumentUpload (custom)",
14154
+ example: {
14155
+ path: "resume",
14156
+ label: "Resume",
14157
+ type: "document_upload",
14158
+ included: true,
14159
+ validation: { accept: ".pdf,.doc,.docx" }
14160
+ }
14108
14161
  }
14109
14162
  ];
14110
14163
  var OPERATORS = [
@@ -14441,7 +14494,10 @@ var FIELD_TYPE_KEYWORDS = {
14441
14494
  rating: ["rating", "stars", "rate", "review"],
14442
14495
  nps: ["nps", "recommend", "likelihood", "net promoter"],
14443
14496
  autocomplete: ["search", "autocomplete", "typeahead"],
14444
- tags: ["tags", "keywords", "labels"]
14497
+ tags: ["tags", "keywords", "labels"],
14498
+ file: ["file", "upload", "attachment", "attach"],
14499
+ image_upload: ["photo", "picture", "image upload", "profile photo", "avatar", "thumbnail"],
14500
+ document_upload: ["document", "resume", "cv", "pdf", "contract", "agreement"]
14445
14501
  };
14446
14502
  function inferFieldType(label, description) {
14447
14503
  const text = `${label} ${description || ""}`.toLowerCase();
@@ -14988,6 +15044,342 @@ try {
14988
15044
  }
14989
15045
  }
14990
15046
  \`\`\`
15047
+ `,
15048
+ extensions: `# NetPad Extensions
15049
+
15050
+ NetPad's extension system allows you to add custom functionality to your NetPad installation through modular, independently deployable packages. Extensions can provide API routes, UI components, services, middleware, and more.
15051
+
15052
+ ## What Are Extensions?
15053
+
15054
+ Extensions are npm packages that follow the \`NetPadExtension\` interface. They integrate seamlessly with NetPad's core functionality while remaining isolated and independently maintainable.
15055
+
15056
+ \`\`\`
15057
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
15058
+ \u2502 NetPad Core \u2502
15059
+ \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524
15060
+ \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502
15061
+ \u2502 \u2502 Extension \u2502 \u2502 Extension \u2502 \u2502 Extension \u2502 ... \u2502
15062
+ \u2502 \u2502 (Cloud) \u2502 \u2502(Collaborate)\u2502 \u2502 (Custom) \u2502 \u2502
15063
+ \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502
15064
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
15065
+ \`\`\`
15066
+
15067
+ ## Extension Capabilities
15068
+
15069
+ Extensions can provide:
15070
+
15071
+ | Capability | Description |
15072
+ | ------------------- | --------------------------------------------------------- |
15073
+ | **Routes** | Custom API endpoints under /api/ext/{extension-name}/ |
15074
+ | **Services** | Shared business logic accessible throughout the extension |
15075
+ | **Middleware** | Request/response processing with priority ordering |
15076
+ | **Components** | React UI components for use in pages |
15077
+ | **Features** | Feature flags that enable/disable functionality |
15078
+ | **Lifecycle Hooks** | Initialize and cleanup logic |
15079
+ | **Workflow Nodes** | Custom workflow node types for automation |
15080
+
15081
+ ## Built-in Extensions
15082
+
15083
+ NetPad includes several built-in extensions:
15084
+
15085
+ ### @netpad/cloud-features
15086
+
15087
+ The cloud features extension provides premium functionality for NetPad Cloud:
15088
+
15089
+ * **Billing & Subscriptions** - Stripe integration for payments
15090
+ * **Atlas Provisioning** - Automatic MongoDB cluster creation
15091
+ * **Premium AI Features** - Advanced RAG and AI capabilities
15092
+ * **Usage Analytics** - Detailed usage tracking and reporting
15093
+
15094
+ ### @netpad/collaborate
15095
+
15096
+ Community collaboration features:
15097
+
15098
+ * **Gallery** - Community showcase of forms, workflows, and integrations
15099
+ * **Contributors** - Leaderboard and contributor profiles
15100
+ * **Submissions** - Collaboration application system
15101
+ * **UI Components** - Pre-built React components
15102
+
15103
+ ### @netpad/demo-node
15104
+
15105
+ A simple demonstration extension that shows how to create custom workflow nodes:
15106
+
15107
+ * **Log Message Node** - A custom workflow node that logs messages with configurable levels
15108
+ * **Template for Extension Development** - Use as a starting point for your own extensions
15109
+ * **Complete Example** - Shows node definition, handler implementation, and extension structure
15110
+
15111
+ **Node Details:**
15112
+ - Type: demo:log-message
15113
+ - Category: Custom
15114
+ - Config Fields: message (textarea), level (select: info/warn/error), label (text), passthrough (boolean)
15115
+ - Outputs: Success output with log data and optional passthrough of input data
15116
+
15117
+ This extension serves as the canonical example for creating workflow node extensions. See the package README for detailed instructions on using it as a template.
15118
+
15119
+ ## Extension Architecture
15120
+
15121
+ Extensions follow a clear architectural pattern:
15122
+
15123
+ \`\`\`typescript
15124
+ // Extension structure
15125
+ const myExtension: NetPadExtension = {
15126
+ metadata: {
15127
+ id: 'my-extension',
15128
+ name: 'My Extension',
15129
+ version: '1.0.0',
15130
+ description: 'Description of what this extension does',
15131
+ },
15132
+
15133
+ // Features this extension provides
15134
+ features: ['custom:my_feature'],
15135
+
15136
+ // API routes
15137
+ routes: [
15138
+ { path: '/api/ext/my-extension/data', method: 'GET', handler: handleGet },
15139
+ { path: '/api/ext/my-extension/data', method: 'POST', handler: handlePost },
15140
+ ],
15141
+
15142
+ // Request middleware
15143
+ middleware: [
15144
+ { path: '/api/ext/my-extension/*', handler: authMiddleware, priority: 10 },
15145
+ ],
15146
+
15147
+ // Shared services
15148
+ services: {
15149
+ myService: myServiceInstance,
15150
+ },
15151
+
15152
+ // Custom workflow nodes
15153
+ workflowNodes: [
15154
+ {
15155
+ definition: {
15156
+ type: 'my-custom-node',
15157
+ name: 'My Custom Node',
15158
+ category: 'custom',
15159
+ description: 'Does something custom',
15160
+ configSchema: { /* ... */ },
15161
+ },
15162
+ handler: async (config, context) => { /* ... */ },
15163
+ },
15164
+ ],
15165
+
15166
+ // Lifecycle hooks
15167
+ initialize: async () => { /* Setup logic */ },
15168
+ cleanup: async () => { /* Cleanup logic */ },
15169
+ };
15170
+ \`\`\`
15171
+
15172
+ ## Quick Start
15173
+
15174
+ ### 1. Enable an Extension
15175
+
15176
+ Add the extension package name to your \`.env.local\`:
15177
+
15178
+ \`\`\`
15179
+ # Enable single extension
15180
+ NETPAD_EXTENSIONS=@netpad/collaborate
15181
+
15182
+ # Enable multiple extensions
15183
+ NETPAD_EXTENSIONS=@netpad/collaborate,@myorg/custom-extension
15184
+ \`\`\`
15185
+
15186
+ ### 2. Install the Package
15187
+
15188
+ \`\`\`bash
15189
+ npm install @netpad/collaborate
15190
+ \`\`\`
15191
+
15192
+ ### 3. Restart NetPad
15193
+
15194
+ Extensions are loaded during application startup.
15195
+
15196
+ ## Creating Custom Extensions
15197
+
15198
+ To create your own extension, use **@netpad/demo-node** as a template:
15199
+
15200
+ 1. **Copy the demo-node package** to a new directory
15201
+ 2. **Update package.json** with your extension name and metadata
15202
+ 3. **Modify src/index.ts**:
15203
+ - Update extension metadata (id, name, version, description)
15204
+ - Define your workflow nodes (type, label, category, config fields)
15205
+ - Implement your node handlers (business logic)
15206
+ - Export the extension as default
15207
+ 4. **Install and enable**:
15208
+ - Run: npm install @myorg/my-extension
15209
+ - Add to .env.local: NETPAD_EXTENSIONS=@myorg/my-extension
15210
+ 5. **Restart NetPad** - Your extension will be loaded automatically
15211
+
15212
+ ### Example: Creating a Custom Workflow Node
15213
+
15214
+ Based on the demo-node structure:
15215
+
15216
+ \`\`\`typescript
15217
+ // 1. Define your node configuration interface
15218
+ interface MyNodeConfig {
15219
+ action: string;
15220
+ target: string;
15221
+ }
15222
+
15223
+ // 2. Implement the node handler
15224
+ const myNodeHandler: NodeHandler = async (context) => {
15225
+ const config = context.resolvedConfig as MyNodeConfig;
15226
+ const inputs = context.inputs;
15227
+
15228
+ // Your business logic here
15229
+ const result = await performAction(config.action, config.target);
15230
+
15231
+ return {
15232
+ success: true,
15233
+ data: { result },
15234
+ metadata: { durationMs: Date.now() - startTime },
15235
+ };
15236
+ };
15237
+
15238
+ // 3. Define the node appearance
15239
+ const myNodeDefinition = {
15240
+ type: 'myext:my-node', // Unique type (convention: extid:nodename)
15241
+ label: 'My Custom Node',
15242
+ description: 'Does something custom',
15243
+ category: 'custom' as const,
15244
+ color: '#FF6B35',
15245
+ icon: 'Extension', // MUI icon name
15246
+ version: '1.0.0',
15247
+ configFields: [
15248
+ {
15249
+ name: 'action',
15250
+ label: 'Action',
15251
+ type: 'select' as const,
15252
+ options: [
15253
+ { label: 'Create', value: 'create' },
15254
+ { label: 'Update', value: 'update' },
15255
+ ],
15256
+ },
15257
+ {
15258
+ name: 'target',
15259
+ label: 'Target',
15260
+ type: 'text' as const,
15261
+ required: true,
15262
+ },
15263
+ ],
15264
+ outputs: [{ id: 'output', label: 'Success', primary: true }],
15265
+ };
15266
+
15267
+ // 4. Export the extension
15268
+ export const myExtension: NetPadExtension = {
15269
+ metadata: {
15270
+ id: 'my-extension',
15271
+ name: 'My Extension',
15272
+ version: '1.0.0',
15273
+ },
15274
+ workflowNodes: [
15275
+ {
15276
+ definition: myNodeDefinition,
15277
+ handler: myNodeHandler,
15278
+ },
15279
+ ],
15280
+ initialize: async () => {
15281
+ console.log('[My Extension] Initialized!');
15282
+ },
15283
+ };
15284
+
15285
+ export default myExtension;
15286
+ \`\`\`
15287
+
15288
+ ### Node Handler Context
15289
+
15290
+ The handler receives a \`NodeExecutionContext\` with:
15291
+
15292
+ - **config**: Raw configuration from the editor
15293
+ - **resolvedConfig**: Configuration with variables like \`{{formData.email}}\` already resolved
15294
+ - **inputs**: Data from previous nodes in the workflow
15295
+ - **trigger**: Information about what triggered the workflow
15296
+ - **getConnection()**: Helper to get MongoDB connection details
15297
+ - **getEmailCredentials()**: Helper to get email service credentials
15298
+
15299
+ ### Node Categories
15300
+
15301
+ When defining your node, choose an appropriate category:
15302
+
15303
+ - **triggers**: Workflow entry points (form submission, webhook, schedule)
15304
+ - **logic**: Control flow (condition, switch, loop, delay)
15305
+ - **integrations**: External services (Slack, email, HTTP)
15306
+ - **actions**: Data operations (MongoDB queries, transforms)
15307
+ - **data**: Data manipulation (set variable, code execution)
15308
+ - **ai**: AI-powered operations (generate, classify, extract)
15309
+ - **forms**: Form-specific operations
15310
+ - **custom**: Custom extensions (use this for your own nodes)
15311
+ - **annotations**: Non-executable nodes (notes, labels)
15312
+
15313
+ ## Extension Loading
15314
+
15315
+ Extensions are loaded in this order:
15316
+
15317
+ 1. **Cloud Extension** - \`@netpad/cloud-features\` (if \`NETPAD_CLOUD=true\`)
15318
+ 2. **Plugin Extensions** - Packages listed in \`NETPAD_EXTENSIONS\`
15319
+ 3. **Programmatic Extensions** - Registered via \`registerExtensionManually()\`
15320
+
15321
+ Each extension goes through:
15322
+
15323
+ 1. Package resolution and import
15324
+ 2. Registration in the extension registry
15325
+ 3. Initialization via the \`initialize()\` hook
15326
+
15327
+ ## Feature Checking
15328
+
15329
+ Extensions can declare features that other parts of the application can check:
15330
+
15331
+ \`\`\`typescript
15332
+ import { isFeatureAvailable } from '@/lib/extensions/registry';
15333
+
15334
+ // Check if a feature is available
15335
+ if (isFeatureAvailable('billing')) {
15336
+ // Show billing UI
15337
+ }
15338
+
15339
+ // Custom extension features use the custom: prefix
15340
+ if (isFeatureAvailable('custom:collaborate')) {
15341
+ // Show collaborate features
15342
+ }
15343
+ \`\`\`
15344
+
15345
+ ## Best Practices
15346
+
15347
+ 1. **Use descriptive metadata** - Clear names and descriptions help users understand your extension
15348
+ 2. **Namespace your routes** - Always use \`/api/ext/{your-extension}/\` for routes
15349
+ 3. **Handle errors gracefully** - Extensions should not crash the main application
15350
+ 4. **Clean up resources** - Implement the \`cleanup()\` hook for proper teardown
15351
+ 5. **Document your extension** - Provide clear usage instructions and API documentation
15352
+ 6. **Test thoroughly** - Extensions run in production, so ensure they're well-tested
15353
+
15354
+ ## Extension Types
15355
+
15356
+ ### Service Extensions
15357
+
15358
+ Provide business logic services:
15359
+ - Billing service (Stripe integration)
15360
+ - Atlas provisioning service
15361
+ - Usage tracking service
15362
+ - Admin service
15363
+
15364
+ ### Feature Extensions
15365
+
15366
+ Add new capabilities:
15367
+ - Custom workflow nodes
15368
+ - Additional form field types
15369
+ - UI components
15370
+ - Integration connectors
15371
+
15372
+ ### Middleware Extensions
15373
+
15374
+ Intercept and modify requests:
15375
+ - Authentication middleware
15376
+ - Rate limiting
15377
+ - Request logging
15378
+ - Data transformation
15379
+
15380
+ ## API Reference
15381
+
15382
+ See the full extension API documentation at: https://docs.netpad.io/docs/extensions/api-reference
14991
15383
  `
14992
15384
  };
14993
15385
  var QUICK_START_GUIDE = `# Quick Start Guide
@@ -17537,6 +17929,53 @@ var WORKFLOW_NODE_TYPES = {
17537
17929
  { key: "timeout", label: "Timeout (ms)", type: "number", default: 5e3 }
17538
17930
  ]
17539
17931
  }
17932
+ ],
17933
+ // Forms (Form Reactions)
17934
+ forms: [
17935
+ {
17936
+ type: "field-event-trigger",
17937
+ name: "Field Event Trigger",
17938
+ description: "Trigger workflow when a form field event occurs (blur, change, focus). Entry point for form reaction workflows.",
17939
+ icon: "bolt",
17940
+ color: "#00BCD4",
17941
+ category: "forms",
17942
+ stage: "trigger",
17943
+ inputs: [],
17944
+ outputs: [
17945
+ { id: "triggerField", label: "Trigger Field", type: "string" },
17946
+ { id: "triggerEvent", label: "Event Type", type: "string" },
17947
+ { id: "fieldValue", label: "Field Value", type: "any" },
17948
+ { id: "formData", label: "Form Data", type: "object" },
17949
+ { id: "formId", label: "Form ID", type: "string" },
17950
+ { id: "reactionId", label: "Reaction ID", type: "string" }
17951
+ ],
17952
+ configFields: [
17953
+ { key: "formId", label: "Form ID", type: "string", required: false },
17954
+ { key: "triggerMode", label: "Trigger Mode", type: "select", options: ["any", "all"], default: "any" },
17955
+ { key: "debounceMs", label: "Debounce (ms)", type: "number", default: 0 }
17956
+ ]
17957
+ },
17958
+ {
17959
+ type: "form-field-update",
17960
+ name: "Form Field Update",
17961
+ description: "Update form fields with workflow data. Maps workflow outputs to form fields for real-time updates.",
17962
+ icon: "edit_note",
17963
+ color: "#00BCD4",
17964
+ category: "forms",
17965
+ stage: "action",
17966
+ inputs: [{ id: "data", label: "Input Data", type: "object" }],
17967
+ outputs: [
17968
+ { id: "success", label: "Success", type: "boolean" },
17969
+ { id: "updates", label: "Updates", type: "object" },
17970
+ { id: "updatedFields", label: "Updated Fields", type: "array" },
17971
+ { id: "skippedFields", label: "Skipped Fields", type: "array" }
17972
+ ],
17973
+ configFields: [
17974
+ { key: "feedbackMode", label: "Feedback Mode", type: "select", options: ["silent", "subtle", "toast"], default: "silent" },
17975
+ { key: "validateAfterUpdate", label: "Validate After Update", type: "boolean", default: false },
17976
+ { key: "updates", label: "Field Mappings", type: "array", itemType: "object", required: true }
17977
+ ]
17978
+ }
17540
17979
  ]
17541
17980
  };
17542
17981
  var WORKFLOW_TEMPLATES = {
@@ -17798,6 +18237,126 @@ var WORKFLOW_TEMPLATES = {
17798
18237
  targetHandle: "data"
17799
18238
  }
17800
18239
  ]
18240
+ },
18241
+ "company-lookup-reaction": {
18242
+ id: "company-lookup-reaction",
18243
+ name: "Company Domain Lookup Reaction",
18244
+ description: "Form reaction that fetches company data when user enters a domain and auto-fills related fields",
18245
+ category: "forms",
18246
+ tags: ["reaction", "forms", "auto-fill", "api"],
18247
+ nodes: [
18248
+ {
18249
+ id: "trigger_1",
18250
+ type: "field-event-trigger",
18251
+ label: "Domain Field Event",
18252
+ position: { x: 100, y: 200 },
18253
+ config: { triggerMode: "any", debounceMs: 500 },
18254
+ enabled: true
18255
+ },
18256
+ {
18257
+ id: "http_1",
18258
+ type: "http-request",
18259
+ label: "Fetch Company Data",
18260
+ position: { x: 350, y: 200 },
18261
+ config: {
18262
+ url: "https://api.example.com/company?domain={{nodes.trigger_1.fieldValue}}",
18263
+ method: "GET",
18264
+ headers: { "Accept": "application/json" }
18265
+ },
18266
+ enabled: true
18267
+ },
18268
+ {
18269
+ id: "update_1",
18270
+ type: "form-field-update",
18271
+ label: "Update Form Fields",
18272
+ position: { x: 600, y: 200 },
18273
+ config: {
18274
+ feedbackMode: "subtle",
18275
+ validateAfterUpdate: false,
18276
+ updates: [
18277
+ { fieldPath: "companyName", sourcePath: "http_1.response.name", nullBehavior: "skip" },
18278
+ { fieldPath: "industry", sourcePath: "http_1.response.industry", nullBehavior: "skip" },
18279
+ { fieldPath: "employeeCount", sourcePath: "http_1.response.size", nullBehavior: "skip" }
18280
+ ]
18281
+ },
18282
+ enabled: true
18283
+ }
18284
+ ],
18285
+ edges: [
18286
+ {
18287
+ id: "edge_1",
18288
+ source: "trigger_1",
18289
+ sourceHandle: "fieldValue",
18290
+ target: "http_1",
18291
+ targetHandle: "body"
18292
+ },
18293
+ {
18294
+ id: "edge_2",
18295
+ source: "http_1",
18296
+ sourceHandle: "response",
18297
+ target: "update_1",
18298
+ targetHandle: "data"
18299
+ }
18300
+ ]
18301
+ },
18302
+ "ai-categorization-reaction": {
18303
+ id: "ai-categorization-reaction",
18304
+ name: "AI Field Categorization Reaction",
18305
+ description: "Form reaction that uses AI to categorize text input and auto-fills category field",
18306
+ category: "forms",
18307
+ tags: ["reaction", "forms", "ai", "classification"],
18308
+ nodes: [
18309
+ {
18310
+ id: "trigger_1",
18311
+ type: "field-event-trigger",
18312
+ label: "Description Field Event",
18313
+ position: { x: 100, y: 200 },
18314
+ config: { triggerMode: "any", debounceMs: 1e3 },
18315
+ enabled: true
18316
+ },
18317
+ {
18318
+ id: "classify_1",
18319
+ type: "ai-classify",
18320
+ label: "Classify Content",
18321
+ position: { x: 350, y: 200 },
18322
+ config: {
18323
+ categories: ["Technical Support", "Billing", "Feature Request", "Bug Report", "General Inquiry"],
18324
+ instructions: "Classify the support ticket based on its description."
18325
+ },
18326
+ enabled: true
18327
+ },
18328
+ {
18329
+ id: "update_1",
18330
+ type: "form-field-update",
18331
+ label: "Update Category",
18332
+ position: { x: 600, y: 200 },
18333
+ config: {
18334
+ feedbackMode: "subtle",
18335
+ validateAfterUpdate: false,
18336
+ updates: [
18337
+ { fieldPath: "category", sourcePath: "classify_1.category", nullBehavior: "skip" },
18338
+ { fieldPath: "aiConfidence", sourcePath: "classify_1.confidence", nullBehavior: "skip" }
18339
+ ]
18340
+ },
18341
+ enabled: true
18342
+ }
18343
+ ],
18344
+ edges: [
18345
+ {
18346
+ id: "edge_1",
18347
+ source: "trigger_1",
18348
+ sourceHandle: "fieldValue",
18349
+ target: "classify_1",
18350
+ targetHandle: "text"
18351
+ },
18352
+ {
18353
+ id: "edge_2",
18354
+ source: "classify_1",
18355
+ sourceHandle: "category",
18356
+ target: "update_1",
18357
+ targetHandle: "data"
18358
+ }
18359
+ ]
17801
18360
  }
17802
18361
  };
17803
18362
  function generateAddNodeCode(workflowId, node) {
@@ -20651,6 +21210,135 @@ server.resource(
20651
21210
  ]
20652
21211
  })
20653
21212
  );
21213
+ server.resource(
21214
+ "netpad-extensions",
21215
+ "netpad://docs/extensions",
21216
+ async () => ({
21217
+ contents: [
21218
+ {
21219
+ uri: "netpad://docs/extensions",
21220
+ mimeType: "text/markdown",
21221
+ text: DOCUMENTATION.extensions
21222
+ }
21223
+ ]
21224
+ })
21225
+ );
21226
+ server.resource(
21227
+ "netpad-demo-node",
21228
+ "netpad://examples/demo-node",
21229
+ async () => ({
21230
+ contents: [
21231
+ {
21232
+ uri: "netpad://examples/demo-node",
21233
+ mimeType: "text/markdown",
21234
+ text: `# @netpad/demo-node Extension Example
21235
+
21236
+ The demo-node extension is a complete, working example of how to create NetPad workflow node extensions. Use it as a template for your own extensions.
21237
+
21238
+ ## What It Provides
21239
+
21240
+ A single workflow node called **"Log Message"** that:
21241
+ - Logs configurable messages to the console
21242
+ - Supports different log levels (info, warn, error)
21243
+ - Can pass through input data to downstream nodes
21244
+ - Demonstrates all key extension concepts
21245
+
21246
+ ## Node Details
21247
+
21248
+ **Type:** \`demo:log-message\`
21249
+ **Category:** Custom
21250
+ **Icon:** Terminal (MUI icon)
21251
+ **Color:** #FF6B35 (orange)
21252
+
21253
+ ### Configuration Fields
21254
+
21255
+ | Field | Type | Description |
21256
+ |-------|------|-------------|
21257
+ | Message | textarea | The message to log. Supports \`{{variable}}\` syntax for dynamic values |
21258
+ | Log Level | select | info, warn, or error |
21259
+ | Label | text | Custom label for the log entry |
21260
+ | Pass Through | boolean | Include input data in output (default: true) |
21261
+
21262
+ ### Example Usage
21263
+
21264
+ 1. Drag the "Log Message" node onto the workflow canvas
21265
+ 2. Connect it after a form trigger or other node
21266
+ 3. Configure the message: \`New submission from {{formData.email}}\`
21267
+ 4. The node will log the message and pass data to the next node
21268
+
21269
+ ## Using as a Template
21270
+
21271
+ 1. **Copy the package** to a new directory
21272
+ 2. **Update package.json** with your extension name
21273
+ 3. **Modify src/index.ts**:
21274
+ - Change extension metadata (id, name, version)
21275
+ - Update node definition (type, label, icon, color, config fields)
21276
+ - Implement your business logic in the handler
21277
+ 4. **Export the extension** as default export
21278
+
21279
+ ## Key Concepts Demonstrated
21280
+
21281
+ - **Extension Metadata**: Identifies your extension in the system
21282
+ - **Workflow Nodes**: Custom nodes that appear in the workflow editor palette
21283
+ - **Node Definition**: Describes the node's appearance and configuration UI
21284
+ - **Node Handler**: The function that executes when the node runs
21285
+ - **Configuration Fields**: UI fields for node configuration
21286
+ - **Output Handles**: Connection points for downstream nodes
21287
+ - **Lifecycle Hooks**: initialize() and cleanup() functions
21288
+
21289
+ ## File Structure
21290
+
21291
+ \`\`\`
21292
+ packages/demo-node/
21293
+ \u251C\u2500\u2500 package.json # Package metadata
21294
+ \u251C\u2500\u2500 README.md # Documentation
21295
+ \u2514\u2500\u2500 src/
21296
+ \u2514\u2500\u2500 index.ts # Extension + node definition + handler
21297
+ \`\`\`
21298
+
21299
+ ## Extension Structure
21300
+
21301
+ \`\`\`typescript
21302
+ export const demoNodeExtension: NetPadExtension = {
21303
+ metadata: {
21304
+ id: 'netpad-demo-node',
21305
+ name: 'Demo Node Extension',
21306
+ version: '1.0.0',
21307
+ },
21308
+ features: ['custom:demo-node'],
21309
+ workflowNodes: [
21310
+ {
21311
+ definition: {
21312
+ type: 'demo:log-message',
21313
+ label: 'Log Message',
21314
+ category: 'custom',
21315
+ // ... node appearance config
21316
+ },
21317
+ handler: async (context) => {
21318
+ // ... execution logic
21319
+ return { success: true, data: {...} };
21320
+ },
21321
+ },
21322
+ ],
21323
+ initialize: async () => { /* setup */ },
21324
+ cleanup: async () => { /* teardown */ },
21325
+ };
21326
+ \`\`\`
21327
+
21328
+ ## Handler Implementation
21329
+
21330
+ The handler receives a \`NodeExecutionContext\` with:
21331
+ - \`resolvedConfig\`: Configuration with variables resolved
21332
+ - \`inputs\`: Data from previous nodes
21333
+ - \`trigger\`: Workflow trigger information
21334
+ - Helper functions for connections and credentials
21335
+
21336
+ See the full source code in \`packages/demo-node/src/index.ts\` for complete implementation details.
21337
+ `
21338
+ }
21339
+ ]
21340
+ })
21341
+ );
20654
21342
  server.tool(
20655
21343
  "generate_form",
20656
21344
  "Generate a complete NetPad form configuration from a description. Returns validated TypeScript code by default (can optionally return JSON). The TypeScript output includes inline types, form config, and API functions - ready to run with `npx tsx`.",
@@ -21200,7 +21888,18 @@ server.tool(
21200
21888
  }]
21201
21889
  };
21202
21890
  }
21203
- return { content: [{ type: "text", text: JSON.stringify(template, null, 2) }] };
21891
+ const workflowForUI = {
21892
+ id: template.id,
21893
+ name: template.name,
21894
+ description: template.description,
21895
+ category: template.category,
21896
+ tags: template.tags,
21897
+ canvas: {
21898
+ nodes: template.nodes,
21899
+ edges: template.edges
21900
+ }
21901
+ };
21902
+ return { content: [{ type: "text", text: JSON.stringify(workflowForUI, null, 2) }] };
21204
21903
  }
21205
21904
  let templates = Object.values(WORKFLOW_TEMPLATES);
21206
21905
  if (category) {
@@ -23202,10 +23901,21 @@ server.tool(
23202
23901
  }]
23203
23902
  };
23204
23903
  }
23904
+ const workflowForUI = {
23905
+ id: template.id,
23906
+ name: template.name,
23907
+ description: template.description,
23908
+ category: template.category,
23909
+ tags: template.tags,
23910
+ canvas: {
23911
+ nodes: template.nodes,
23912
+ edges: template.edges
23913
+ }
23914
+ };
23205
23915
  return {
23206
23916
  content: [{
23207
23917
  type: "text",
23208
- text: JSON.stringify(template, null, 2)
23918
+ text: JSON.stringify(workflowForUI, null, 2)
23209
23919
  }]
23210
23920
  };
23211
23921
  }
@@ -23308,8 +24018,10 @@ const WORKFLOW_CONFIG: WorkflowConfig = {
23308
24018
  name: ${JSON.stringify(name)},
23309
24019
  description: ${JSON.stringify(description || template?.description || "Custom workflow")},
23310
24020
  tags: ${JSON.stringify(tags || template?.tags || [])},
23311
- nodes: ${JSON.stringify(updatedNodes, null, 2)},
23312
- edges: ${JSON.stringify(workflowEdges, null, 2)},
24021
+ canvas: {
24022
+ nodes: ${JSON.stringify(updatedNodes, null, 2)},
24023
+ edges: ${JSON.stringify(workflowEdges, null, 2)},
24024
+ },
23313
24025
  settings: {
23314
24026
  executionMode: 'auto',
23315
24027
  maxExecutionTime: 300000,
@@ -23646,15 +24358,33 @@ ${code}
23646
24358
  server.resource(
23647
24359
  "netpad-workflow-templates",
23648
24360
  "netpad://reference/workflow-templates",
23649
- async () => ({
23650
- contents: [
23651
- {
23652
- uri: "netpad://reference/workflow-templates",
23653
- mimeType: "application/json",
23654
- text: JSON.stringify(WORKFLOW_TEMPLATES, null, 2)
23655
- }
23656
- ]
23657
- })
24361
+ async () => {
24362
+ const templatesForUI = Object.fromEntries(
24363
+ Object.entries(WORKFLOW_TEMPLATES).map(([key, template]) => [
24364
+ key,
24365
+ {
24366
+ id: template.id,
24367
+ name: template.name,
24368
+ description: template.description,
24369
+ category: template.category,
24370
+ tags: template.tags,
24371
+ canvas: {
24372
+ nodes: template.nodes,
24373
+ edges: template.edges
24374
+ }
24375
+ }
24376
+ ])
24377
+ );
24378
+ return {
24379
+ contents: [
24380
+ {
24381
+ uri: "netpad://reference/workflow-templates",
24382
+ mimeType: "application/json",
24383
+ text: JSON.stringify(templatesForUI, null, 2)
24384
+ }
24385
+ ]
24386
+ };
24387
+ }
23658
24388
  );
23659
24389
  server.resource(
23660
24390
  "netpad-workflow-nodes",
@@ -23750,7 +24480,7 @@ server.tool(
23750
24480
  })).describe("Topics to cover in conversation"),
23751
24481
  extractionSchema: external_exports.array(external_exports.object({
23752
24482
  field: external_exports.string().describe("Field name"),
23753
- type: external_exports.enum(["string", "number", "boolean", "enum", "array", "object"]).describe("Field type"),
24483
+ type: external_exports.enum(["string", "number", "boolean", "enum", "array", "object", "file"]).describe("Field type"),
23754
24484
  required: external_exports.boolean().describe("Whether required"),
23755
24485
  description: external_exports.string().describe("Field description"),
23756
24486
  options: external_exports.array(external_exports.string()).optional().describe("Options for enum type"),