@netpad/mcp-server 2.1.0 → 2.2.1
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 +2646 -311
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
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
|
|
@@ -16567,104 +16959,6 @@ var APPLICATION_TEMPLATES = {
|
|
|
16567
16959
|
}
|
|
16568
16960
|
}
|
|
16569
16961
|
};
|
|
16570
|
-
function generateCreateApplicationCode(options) {
|
|
16571
|
-
const { name, description, slug, templateId, icon, color, tags, projectId, organizationId } = options;
|
|
16572
|
-
const template = templateId ? APPLICATION_TEMPLATES[templateId] : null;
|
|
16573
|
-
let code = `// Create a new NetPad application
|
|
16574
|
-
// Using: NetPad Platform API
|
|
16575
|
-
|
|
16576
|
-
const applicationData = {
|
|
16577
|
-
name: '${name}',
|
|
16578
|
-
${description ? `description: '${description}',` : ""}
|
|
16579
|
-
${slug ? `slug: '${slug}',` : ""}
|
|
16580
|
-
${icon ? `icon: '${icon}',` : ""}
|
|
16581
|
-
${color ? `color: '${color}',` : ""}
|
|
16582
|
-
${tags && tags.length > 0 ? `tags: ${JSON.stringify(tags)},` : ""}
|
|
16583
|
-
projectId: '${projectId}',
|
|
16584
|
-
organizationId: '${organizationId}',
|
|
16585
|
-
};
|
|
16586
|
-
|
|
16587
|
-
// API call to create application
|
|
16588
|
-
const response = await fetch('/api/applications', {
|
|
16589
|
-
method: 'POST',
|
|
16590
|
-
headers: {
|
|
16591
|
-
'Content-Type': 'application/json',
|
|
16592
|
-
'Authorization': \`Bearer \${process.env.NETPAD_API_KEY}\`,
|
|
16593
|
-
},
|
|
16594
|
-
body: JSON.stringify(applicationData),
|
|
16595
|
-
});
|
|
16596
|
-
|
|
16597
|
-
const { application } = await response.json();
|
|
16598
|
-
console.log('Created application:', application.applicationId);
|
|
16599
|
-
`;
|
|
16600
|
-
if (template && template.structure.forms.length > 0) {
|
|
16601
|
-
code += `
|
|
16602
|
-
// Create forms from template: ${template.name}
|
|
16603
|
-
`;
|
|
16604
|
-
for (const form of template.structure.forms) {
|
|
16605
|
-
code += `
|
|
16606
|
-
// Create form: ${form.name}
|
|
16607
|
-
const ${form.slug.replace(/-/g, "")}FormData = {
|
|
16608
|
-
name: '${form.name}',
|
|
16609
|
-
slug: '${form.slug}',
|
|
16610
|
-
applicationId: application.applicationId,
|
|
16611
|
-
projectId: '${projectId}',
|
|
16612
|
-
organizationId: '${organizationId}',
|
|
16613
|
-
fieldConfigs: ${JSON.stringify(form.fields, null, 2)},
|
|
16614
|
-
${form.multiPage ? `multiPage: { enabled: true, pages: ${JSON.stringify(form.pages)} },` : ""}
|
|
16615
|
-
};
|
|
16616
|
-
|
|
16617
|
-
await fetch('/api/forms', {
|
|
16618
|
-
method: 'POST',
|
|
16619
|
-
headers: {
|
|
16620
|
-
'Content-Type': 'application/json',
|
|
16621
|
-
'Authorization': \`Bearer \${process.env.NETPAD_API_KEY}\`,
|
|
16622
|
-
},
|
|
16623
|
-
body: JSON.stringify(${form.slug.replace(/-/g, "")}FormData),
|
|
16624
|
-
});
|
|
16625
|
-
`;
|
|
16626
|
-
}
|
|
16627
|
-
}
|
|
16628
|
-
if (template && template.structure.workflows.length > 0) {
|
|
16629
|
-
code += `
|
|
16630
|
-
// Create workflows from template
|
|
16631
|
-
`;
|
|
16632
|
-
for (const workflow of template.structure.workflows) {
|
|
16633
|
-
code += `
|
|
16634
|
-
// Workflow: ${workflow.name}
|
|
16635
|
-
// Trigger: ${workflow.trigger}
|
|
16636
|
-
// Steps: ${workflow.steps.join(" -> ")}
|
|
16637
|
-
// Note: Create workflow in NetPad UI or via API with full node configuration
|
|
16638
|
-
`;
|
|
16639
|
-
}
|
|
16640
|
-
}
|
|
16641
|
-
return code;
|
|
16642
|
-
}
|
|
16643
|
-
function generateApplicationConfig(options) {
|
|
16644
|
-
const { name, description, slug, templateId, icon, color, tags } = options;
|
|
16645
|
-
const template = templateId ? APPLICATION_TEMPLATES[templateId] : null;
|
|
16646
|
-
const config2 = {
|
|
16647
|
-
application: {
|
|
16648
|
-
name,
|
|
16649
|
-
description: description || (template ? template.description : ""),
|
|
16650
|
-
slug: slug || name.toLowerCase().replace(/[^a-z0-9]+/g, "-"),
|
|
16651
|
-
icon,
|
|
16652
|
-
color,
|
|
16653
|
-
tags: tags || (template ? template.tags : []),
|
|
16654
|
-
version: "1.0.0"
|
|
16655
|
-
}
|
|
16656
|
-
};
|
|
16657
|
-
if (template) {
|
|
16658
|
-
config2.template = {
|
|
16659
|
-
id: template.id,
|
|
16660
|
-
name: template.name,
|
|
16661
|
-
category: template.category
|
|
16662
|
-
};
|
|
16663
|
-
config2.forms = template.structure.forms;
|
|
16664
|
-
config2.workflows = template.structure.workflows;
|
|
16665
|
-
}
|
|
16666
|
-
return config2;
|
|
16667
|
-
}
|
|
16668
16962
|
function generateApplicationContract(options) {
|
|
16669
16963
|
const {
|
|
16670
16964
|
applicationId,
|
|
@@ -17898,92 +18192,6 @@ var WORKFLOW_TEMPLATES = {
|
|
|
17898
18192
|
]
|
|
17899
18193
|
}
|
|
17900
18194
|
};
|
|
17901
|
-
function generateCreateWorkflowCode(options) {
|
|
17902
|
-
const { name, description, templateId, applicationId, projectId, organizationId, tags } = options;
|
|
17903
|
-
const template = templateId ? WORKFLOW_TEMPLATES[templateId] : null;
|
|
17904
|
-
let code = `// Create a new NetPad workflow
|
|
17905
|
-
// Using: NetPad Platform API
|
|
17906
|
-
|
|
17907
|
-
const workflowData = {
|
|
17908
|
-
name: '${name}',
|
|
17909
|
-
${description ? `description: '${description}',` : ""}
|
|
17910
|
-
projectId: '${projectId}',
|
|
17911
|
-
organizationId: '${organizationId}',
|
|
17912
|
-
${applicationId ? `applicationId: '${applicationId}',` : ""}
|
|
17913
|
-
${tags && tags.length > 0 ? `tags: ${JSON.stringify(tags)},` : ""}
|
|
17914
|
-
`;
|
|
17915
|
-
if (template) {
|
|
17916
|
-
code += `
|
|
17917
|
-
// Using template: ${template.name}
|
|
17918
|
-
canvas: {
|
|
17919
|
-
nodes: ${JSON.stringify(template.nodes, null, 4)},
|
|
17920
|
-
edges: ${JSON.stringify(template.edges, null, 4)},
|
|
17921
|
-
viewport: { x: 0, y: 0, zoom: 1 },
|
|
17922
|
-
},
|
|
17923
|
-
`;
|
|
17924
|
-
} else {
|
|
17925
|
-
code += `
|
|
17926
|
-
// Empty canvas - add nodes in the visual editor
|
|
17927
|
-
canvas: {
|
|
17928
|
-
nodes: [],
|
|
17929
|
-
edges: [],
|
|
17930
|
-
viewport: { x: 0, y: 0, zoom: 1 },
|
|
17931
|
-
},
|
|
17932
|
-
`;
|
|
17933
|
-
}
|
|
17934
|
-
code += `
|
|
17935
|
-
settings: {
|
|
17936
|
-
executionMode: 'auto',
|
|
17937
|
-
maxExecutionTime: 300000,
|
|
17938
|
-
retryPolicy: {
|
|
17939
|
-
maxRetries: 3,
|
|
17940
|
-
backoffMultiplier: 2,
|
|
17941
|
-
initialDelayMs: 1000,
|
|
17942
|
-
},
|
|
17943
|
-
errorHandling: 'stop',
|
|
17944
|
-
timezone: 'UTC',
|
|
17945
|
-
},
|
|
17946
|
-
variables: [],
|
|
17947
|
-
};
|
|
17948
|
-
|
|
17949
|
-
const response = await fetch('/api/workflows', {
|
|
17950
|
-
method: 'POST',
|
|
17951
|
-
headers: {
|
|
17952
|
-
'Content-Type': 'application/json',
|
|
17953
|
-
'Authorization': \`Bearer \${process.env.NETPAD_API_KEY}\`,
|
|
17954
|
-
},
|
|
17955
|
-
body: JSON.stringify(workflowData),
|
|
17956
|
-
});
|
|
17957
|
-
|
|
17958
|
-
const { workflow } = await response.json();
|
|
17959
|
-
console.log('Created workflow:', workflow.id);
|
|
17960
|
-
`;
|
|
17961
|
-
return code;
|
|
17962
|
-
}
|
|
17963
|
-
function generateWorkflowConfig(options) {
|
|
17964
|
-
const { name, description, templateId, tags } = options;
|
|
17965
|
-
const template = templateId ? WORKFLOW_TEMPLATES[templateId] : null;
|
|
17966
|
-
return {
|
|
17967
|
-
workflow: {
|
|
17968
|
-
name,
|
|
17969
|
-
description: description || (template ? template.description : ""),
|
|
17970
|
-
tags: tags || (template ? template.tags : []),
|
|
17971
|
-
status: "draft"
|
|
17972
|
-
},
|
|
17973
|
-
template: template ? {
|
|
17974
|
-
id: template.id,
|
|
17975
|
-
name: template.name,
|
|
17976
|
-
category: template.category
|
|
17977
|
-
} : null,
|
|
17978
|
-
canvas: template ? {
|
|
17979
|
-
nodes: template.nodes,
|
|
17980
|
-
edges: template.edges
|
|
17981
|
-
} : {
|
|
17982
|
-
nodes: [],
|
|
17983
|
-
edges: []
|
|
17984
|
-
}
|
|
17985
|
-
};
|
|
17986
|
-
}
|
|
17987
18195
|
function generateAddNodeCode(workflowId, node) {
|
|
17988
18196
|
return `// Add node to workflow
|
|
17989
18197
|
const nodeData = ${JSON.stringify(node, null, 2)};
|
|
@@ -20115,23 +20323,647 @@ collections.forEach((coll: any) => {
|
|
|
20115
20323
|
`;
|
|
20116
20324
|
}
|
|
20117
20325
|
|
|
20118
|
-
// src/
|
|
20119
|
-
|
|
20120
|
-
|
|
20121
|
-
|
|
20122
|
-
|
|
20123
|
-
|
|
20124
|
-
|
|
20125
|
-
|
|
20126
|
-
|
|
20127
|
-
|
|
20128
|
-
|
|
20129
|
-
|
|
20130
|
-
|
|
20131
|
-
|
|
20132
|
-
|
|
20133
|
-
|
|
20134
|
-
|
|
20326
|
+
// src/validation.ts
|
|
20327
|
+
import ts from "typescript";
|
|
20328
|
+
var INLINE_TYPES = `
|
|
20329
|
+
// ============================================================================
|
|
20330
|
+
// Types (inline - no external dependencies)
|
|
20331
|
+
// ============================================================================
|
|
20332
|
+
|
|
20333
|
+
interface NetPadConfig {
|
|
20334
|
+
baseUrl: string;
|
|
20335
|
+
apiKey: string;
|
|
20336
|
+
organizationId?: string;
|
|
20337
|
+
projectId?: string;
|
|
20338
|
+
}
|
|
20339
|
+
|
|
20340
|
+
interface FormFieldValidation {
|
|
20341
|
+
min?: number;
|
|
20342
|
+
max?: number;
|
|
20343
|
+
minLength?: number;
|
|
20344
|
+
maxLength?: number;
|
|
20345
|
+
pattern?: string;
|
|
20346
|
+
errorMessage?: string;
|
|
20347
|
+
}
|
|
20348
|
+
|
|
20349
|
+
interface FormFieldOption {
|
|
20350
|
+
label: string;
|
|
20351
|
+
value: string;
|
|
20352
|
+
}
|
|
20353
|
+
|
|
20354
|
+
interface ConditionalLogicCondition {
|
|
20355
|
+
field: string;
|
|
20356
|
+
operator: string;
|
|
20357
|
+
value?: string | number | boolean;
|
|
20358
|
+
}
|
|
20359
|
+
|
|
20360
|
+
interface ConditionalLogic {
|
|
20361
|
+
action: 'show' | 'hide';
|
|
20362
|
+
logicType: 'all' | 'any';
|
|
20363
|
+
conditions: ConditionalLogicCondition[];
|
|
20364
|
+
}
|
|
20365
|
+
|
|
20366
|
+
interface FormField {
|
|
20367
|
+
path: string;
|
|
20368
|
+
label: string;
|
|
20369
|
+
type: string;
|
|
20370
|
+
included?: boolean;
|
|
20371
|
+
required?: boolean;
|
|
20372
|
+
disabled?: boolean;
|
|
20373
|
+
readOnly?: boolean;
|
|
20374
|
+
placeholder?: string;
|
|
20375
|
+
helpText?: string;
|
|
20376
|
+
options?: FormFieldOption[];
|
|
20377
|
+
validation?: FormFieldValidation;
|
|
20378
|
+
fieldWidth?: 'full' | 'half' | 'third' | 'quarter';
|
|
20379
|
+
defaultValue?: unknown;
|
|
20380
|
+
conditionalLogic?: ConditionalLogic;
|
|
20381
|
+
}
|
|
20382
|
+
|
|
20383
|
+
interface FormTheme {
|
|
20384
|
+
primaryColor?: string;
|
|
20385
|
+
backgroundColor?: string;
|
|
20386
|
+
surfaceColor?: string;
|
|
20387
|
+
textColor?: string;
|
|
20388
|
+
errorColor?: string;
|
|
20389
|
+
successColor?: string;
|
|
20390
|
+
borderRadius?: number;
|
|
20391
|
+
spacing?: 'compact' | 'comfortable' | 'spacious';
|
|
20392
|
+
inputStyle?: 'outlined' | 'filled' | 'standard';
|
|
20393
|
+
mode?: 'light' | 'dark';
|
|
20394
|
+
}
|
|
20395
|
+
|
|
20396
|
+
interface FormPage {
|
|
20397
|
+
id: string;
|
|
20398
|
+
title: string;
|
|
20399
|
+
description?: string;
|
|
20400
|
+
fields: string[];
|
|
20401
|
+
}
|
|
20402
|
+
|
|
20403
|
+
interface MultiPageConfig {
|
|
20404
|
+
enabled: boolean;
|
|
20405
|
+
pages: FormPage[];
|
|
20406
|
+
showProgressBar?: boolean;
|
|
20407
|
+
showPageTitles?: boolean;
|
|
20408
|
+
allowSkip?: boolean;
|
|
20409
|
+
showReview?: boolean;
|
|
20410
|
+
}
|
|
20411
|
+
|
|
20412
|
+
interface FormConfig {
|
|
20413
|
+
name: string;
|
|
20414
|
+
slug?: string;
|
|
20415
|
+
description?: string;
|
|
20416
|
+
fieldConfigs: FormField[];
|
|
20417
|
+
submitButtonText?: string;
|
|
20418
|
+
successMessage?: string;
|
|
20419
|
+
theme?: FormTheme;
|
|
20420
|
+
multiPage?: MultiPageConfig;
|
|
20421
|
+
}
|
|
20422
|
+
|
|
20423
|
+
interface SubmitResult {
|
|
20424
|
+
success: boolean;
|
|
20425
|
+
submissionId?: string;
|
|
20426
|
+
error?: string;
|
|
20427
|
+
}
|
|
20428
|
+
|
|
20429
|
+
interface ApiResponse<T> {
|
|
20430
|
+
success: boolean;
|
|
20431
|
+
data?: T;
|
|
20432
|
+
error?: string;
|
|
20433
|
+
}
|
|
20434
|
+
`;
|
|
20435
|
+
var INLINE_WORKFLOW_TYPES = `
|
|
20436
|
+
interface WorkflowNode {
|
|
20437
|
+
id: string;
|
|
20438
|
+
type: string;
|
|
20439
|
+
label: string;
|
|
20440
|
+
position: { x: number; y: number };
|
|
20441
|
+
config: Record<string, unknown>;
|
|
20442
|
+
enabled?: boolean;
|
|
20443
|
+
}
|
|
20444
|
+
|
|
20445
|
+
interface WorkflowEdge {
|
|
20446
|
+
id: string;
|
|
20447
|
+
source: string;
|
|
20448
|
+
sourceHandle: string;
|
|
20449
|
+
target: string;
|
|
20450
|
+
targetHandle: string;
|
|
20451
|
+
condition?: {
|
|
20452
|
+
expression: string;
|
|
20453
|
+
label?: string;
|
|
20454
|
+
};
|
|
20455
|
+
}
|
|
20456
|
+
|
|
20457
|
+
interface WorkflowSettings {
|
|
20458
|
+
executionMode?: 'auto' | 'manual';
|
|
20459
|
+
maxExecutionTime?: number;
|
|
20460
|
+
retryPolicy?: {
|
|
20461
|
+
maxRetries: number;
|
|
20462
|
+
backoffMultiplier: number;
|
|
20463
|
+
initialDelayMs: number;
|
|
20464
|
+
};
|
|
20465
|
+
}
|
|
20466
|
+
|
|
20467
|
+
interface WorkflowConfig {
|
|
20468
|
+
name: string;
|
|
20469
|
+
description?: string;
|
|
20470
|
+
tags?: string[];
|
|
20471
|
+
nodes: WorkflowNode[];
|
|
20472
|
+
edges: WorkflowEdge[];
|
|
20473
|
+
settings?: WorkflowSettings;
|
|
20474
|
+
}
|
|
20475
|
+
|
|
20476
|
+
interface CreateWorkflowResult {
|
|
20477
|
+
success: boolean;
|
|
20478
|
+
workflowId?: string;
|
|
20479
|
+
error?: string;
|
|
20480
|
+
}
|
|
20481
|
+
`;
|
|
20482
|
+
var SKIPPED_ERROR_PATTERNS = [
|
|
20483
|
+
"Cannot find module",
|
|
20484
|
+
// Global types (not available in isolated compilation)
|
|
20485
|
+
"Cannot find global type",
|
|
20486
|
+
"Cannot find name 'Array'",
|
|
20487
|
+
"Cannot find name 'String'",
|
|
20488
|
+
"Cannot find name 'Number'",
|
|
20489
|
+
"Cannot find name 'Boolean'",
|
|
20490
|
+
"Cannot find name 'Object'",
|
|
20491
|
+
"Cannot find name 'Function'",
|
|
20492
|
+
"Cannot find name 'Symbol'",
|
|
20493
|
+
"Cannot find name 'Error'",
|
|
20494
|
+
"Cannot find name 'Promise'",
|
|
20495
|
+
"Cannot find name 'Record'",
|
|
20496
|
+
"Cannot find name 'Partial'",
|
|
20497
|
+
"Cannot find name 'Required'",
|
|
20498
|
+
"Cannot find name 'Readonly'",
|
|
20499
|
+
"Cannot find name 'Pick'",
|
|
20500
|
+
"Cannot find name 'Omit'",
|
|
20501
|
+
"Cannot find name 'Exclude'",
|
|
20502
|
+
"Cannot find name 'Extract'",
|
|
20503
|
+
"Cannot find name 'Map'",
|
|
20504
|
+
"Cannot find name 'Set'",
|
|
20505
|
+
"Cannot find name 'WeakMap'",
|
|
20506
|
+
"Cannot find name 'WeakSet'",
|
|
20507
|
+
"Cannot find name 'JSON'",
|
|
20508
|
+
"Cannot find name 'Date'",
|
|
20509
|
+
"Cannot find name 'RegExp'",
|
|
20510
|
+
"Cannot find name 'Math'",
|
|
20511
|
+
// Runtime globals
|
|
20512
|
+
"Cannot find name 'process'",
|
|
20513
|
+
"Cannot find name 'fetch'",
|
|
20514
|
+
"Cannot find name 'console'",
|
|
20515
|
+
"Cannot find name 'RequestInit'",
|
|
20516
|
+
"Cannot find name 'Response'",
|
|
20517
|
+
"Cannot find name 'Headers'",
|
|
20518
|
+
"Cannot find name 'URL'",
|
|
20519
|
+
"Cannot find name 'URLSearchParams'",
|
|
20520
|
+
"Cannot find name 'FormData'",
|
|
20521
|
+
"Cannot find name 'Blob'",
|
|
20522
|
+
"Cannot find name 'File'",
|
|
20523
|
+
"Cannot find name 'AbortController'",
|
|
20524
|
+
"Cannot find name 'AbortSignal'",
|
|
20525
|
+
"Cannot find name 'setTimeout'",
|
|
20526
|
+
"Cannot find name 'clearTimeout'",
|
|
20527
|
+
"Cannot find name 'setInterval'",
|
|
20528
|
+
"Cannot find name 'clearInterval'",
|
|
20529
|
+
// React-specific
|
|
20530
|
+
"Cannot find name 'React'",
|
|
20531
|
+
"Cannot find namespace 'React'",
|
|
20532
|
+
"Cannot find name 'JSX'",
|
|
20533
|
+
// Node.js specific
|
|
20534
|
+
"Cannot find name 'Buffer'",
|
|
20535
|
+
"Cannot find name '__dirname'",
|
|
20536
|
+
"Cannot find name '__filename'",
|
|
20537
|
+
"Cannot find name 'require'",
|
|
20538
|
+
"Cannot find name 'module'",
|
|
20539
|
+
"Cannot find name 'exports'",
|
|
20540
|
+
// DOM types
|
|
20541
|
+
"Cannot find name 'document'",
|
|
20542
|
+
"Cannot find name 'window'",
|
|
20543
|
+
"Cannot find name 'HTMLElement'",
|
|
20544
|
+
"Cannot find name 'Event'",
|
|
20545
|
+
"Cannot find name 'EventTarget'",
|
|
20546
|
+
// Library suggestions
|
|
20547
|
+
"Do you need to change your target library",
|
|
20548
|
+
// Strict mode catch clause errors (handled by instanceof checks at runtime)
|
|
20549
|
+
"is of type 'unknown'"
|
|
20550
|
+
];
|
|
20551
|
+
function validateTypeScript(code) {
|
|
20552
|
+
const result = validateTypeScriptDetailed(code);
|
|
20553
|
+
return result.errors.map(
|
|
20554
|
+
(e) => e.line ? `Line ${e.line}:${e.column}: ${e.message}` : e.message
|
|
20555
|
+
);
|
|
20556
|
+
}
|
|
20557
|
+
function validateTypeScriptDetailed(code) {
|
|
20558
|
+
const errors = [];
|
|
20559
|
+
const warnings = [];
|
|
20560
|
+
const sourceFile = ts.createSourceFile(
|
|
20561
|
+
"generated.ts",
|
|
20562
|
+
code,
|
|
20563
|
+
ts.ScriptTarget.ES2020,
|
|
20564
|
+
true,
|
|
20565
|
+
ts.ScriptKind.TS
|
|
20566
|
+
);
|
|
20567
|
+
const compilerOptions = {
|
|
20568
|
+
target: ts.ScriptTarget.ES2020,
|
|
20569
|
+
module: ts.ModuleKind.ESNext,
|
|
20570
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
20571
|
+
strict: true,
|
|
20572
|
+
noEmit: true,
|
|
20573
|
+
esModuleInterop: true,
|
|
20574
|
+
skipLibCheck: true,
|
|
20575
|
+
// Don't require external type definitions
|
|
20576
|
+
types: [],
|
|
20577
|
+
lib: ["ES2020", "DOM"],
|
|
20578
|
+
// JSX support for React components
|
|
20579
|
+
jsx: ts.JsxEmit.React,
|
|
20580
|
+
jsxFactory: "React.createElement",
|
|
20581
|
+
jsxFragmentFactory: "React.Fragment"
|
|
20582
|
+
};
|
|
20583
|
+
const defaultCompilerHost = ts.createCompilerHost(compilerOptions);
|
|
20584
|
+
const customCompilerHost = {
|
|
20585
|
+
...defaultCompilerHost,
|
|
20586
|
+
getSourceFile: (fileName, languageVersion) => {
|
|
20587
|
+
if (fileName === "generated.ts") {
|
|
20588
|
+
return sourceFile;
|
|
20589
|
+
}
|
|
20590
|
+
if (fileName.endsWith(".ts") || fileName.endsWith(".tsx")) {
|
|
20591
|
+
return ts.createSourceFile(
|
|
20592
|
+
fileName,
|
|
20593
|
+
"",
|
|
20594
|
+
languageVersion,
|
|
20595
|
+
true
|
|
20596
|
+
);
|
|
20597
|
+
}
|
|
20598
|
+
return defaultCompilerHost.getSourceFile(fileName, languageVersion);
|
|
20599
|
+
},
|
|
20600
|
+
fileExists: (fileName) => {
|
|
20601
|
+
if (fileName === "generated.ts") return true;
|
|
20602
|
+
return defaultCompilerHost.fileExists(fileName);
|
|
20603
|
+
},
|
|
20604
|
+
readFile: (fileName) => {
|
|
20605
|
+
if (fileName === "generated.ts") return code;
|
|
20606
|
+
return defaultCompilerHost.readFile(fileName);
|
|
20607
|
+
}
|
|
20608
|
+
};
|
|
20609
|
+
const program = ts.createProgram(
|
|
20610
|
+
["generated.ts"],
|
|
20611
|
+
compilerOptions,
|
|
20612
|
+
customCompilerHost
|
|
20613
|
+
);
|
|
20614
|
+
const allDiagnostics = ts.getPreEmitDiagnostics(program);
|
|
20615
|
+
for (const diagnostic of allDiagnostics) {
|
|
20616
|
+
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
20617
|
+
const shouldSkip = SKIPPED_ERROR_PATTERNS.some((pattern) => message.includes(pattern));
|
|
20618
|
+
if (shouldSkip) {
|
|
20619
|
+
continue;
|
|
20620
|
+
}
|
|
20621
|
+
if (diagnostic.category === ts.DiagnosticCategory.Warning) {
|
|
20622
|
+
warnings.push(message);
|
|
20623
|
+
continue;
|
|
20624
|
+
}
|
|
20625
|
+
if (diagnostic.category === ts.DiagnosticCategory.Error) {
|
|
20626
|
+
const error48 = {
|
|
20627
|
+
message,
|
|
20628
|
+
code: diagnostic.code,
|
|
20629
|
+
fixable: isFixableError(diagnostic.code, message),
|
|
20630
|
+
fixSuggestion: getFixSuggestion(diagnostic.code, message)
|
|
20631
|
+
};
|
|
20632
|
+
if (diagnostic.file && diagnostic.start !== void 0) {
|
|
20633
|
+
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
|
20634
|
+
error48.line = line + 1;
|
|
20635
|
+
error48.column = character + 1;
|
|
20636
|
+
}
|
|
20637
|
+
errors.push(error48);
|
|
20638
|
+
}
|
|
20639
|
+
}
|
|
20640
|
+
return {
|
|
20641
|
+
valid: errors.length === 0,
|
|
20642
|
+
errors,
|
|
20643
|
+
warnings,
|
|
20644
|
+
fixable: errors.some((e) => e.fixable)
|
|
20645
|
+
};
|
|
20646
|
+
}
|
|
20647
|
+
function isFixableError(code, message) {
|
|
20648
|
+
if (code === 1005) return true;
|
|
20649
|
+
if (code === 7006) return true;
|
|
20650
|
+
if (code === 1064) return true;
|
|
20651
|
+
if (code === 2304 && message.includes("Cannot find name")) return false;
|
|
20652
|
+
if (code === 2345) return false;
|
|
20653
|
+
if (code === 2339) return false;
|
|
20654
|
+
return false;
|
|
20655
|
+
}
|
|
20656
|
+
function getFixSuggestion(code, message) {
|
|
20657
|
+
if (code === 1005) {
|
|
20658
|
+
return "Add missing semicolon";
|
|
20659
|
+
}
|
|
20660
|
+
if (code === 7006) {
|
|
20661
|
+
return "Add type annotation to parameter (e.g., ': unknown' or ': string')";
|
|
20662
|
+
}
|
|
20663
|
+
if (code === 1064) {
|
|
20664
|
+
return "Add Promise return type to async function";
|
|
20665
|
+
}
|
|
20666
|
+
if (code === 2304) {
|
|
20667
|
+
const match = message.match(/Cannot find name '(\w+)'/);
|
|
20668
|
+
if (match) {
|
|
20669
|
+
return `Ensure '${match[1]}' is defined or imported`;
|
|
20670
|
+
}
|
|
20671
|
+
}
|
|
20672
|
+
if (code === 2345) {
|
|
20673
|
+
return "Check that the argument type matches the expected parameter type";
|
|
20674
|
+
}
|
|
20675
|
+
if (code === 2339) {
|
|
20676
|
+
const match = message.match(/Property '(\w+)' does not exist/);
|
|
20677
|
+
if (match) {
|
|
20678
|
+
return `Add '${match[1]}' property to the type definition or use type assertion`;
|
|
20679
|
+
}
|
|
20680
|
+
}
|
|
20681
|
+
return void 0;
|
|
20682
|
+
}
|
|
20683
|
+
function attemptAutoFix(code, _errors) {
|
|
20684
|
+
let fixedCode = code;
|
|
20685
|
+
const result = validateTypeScriptDetailed(code);
|
|
20686
|
+
for (const error48 of result.errors) {
|
|
20687
|
+
if (!error48.fixable) continue;
|
|
20688
|
+
if (error48.code === 1005 && error48.message.includes("';' expected")) {
|
|
20689
|
+
fixedCode = fixedCode.replace(
|
|
20690
|
+
/(\})\s*(\n\s*(?:const|let|var|function|export|import|class|interface|type|async|if|for|while|switch|try))/g,
|
|
20691
|
+
"$1;\n$2"
|
|
20692
|
+
);
|
|
20693
|
+
fixedCode = fixedCode.replace(
|
|
20694
|
+
/(\s=\s[^;{]+)\s*(\n\s*(?:const|let|var|function|export|import|class|interface|type|async))/g,
|
|
20695
|
+
"$1;$2"
|
|
20696
|
+
);
|
|
20697
|
+
}
|
|
20698
|
+
if (error48.code === 7006 && error48.message.includes("implicitly has an")) {
|
|
20699
|
+
fixedCode = fixedCode.replace(
|
|
20700
|
+
/\(([a-zA-Z_][a-zA-Z0-9_]*)\s*\)/g,
|
|
20701
|
+
"($1: unknown)"
|
|
20702
|
+
);
|
|
20703
|
+
fixedCode = fixedCode.replace(
|
|
20704
|
+
/\(([a-zA-Z_][a-zA-Z0-9_]*)\s*,\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)/g,
|
|
20705
|
+
"($1: unknown, $2: unknown)"
|
|
20706
|
+
);
|
|
20707
|
+
}
|
|
20708
|
+
if (error48.code === 1064) {
|
|
20709
|
+
fixedCode = fixedCode.replace(
|
|
20710
|
+
/async function (\w+)\(([^)]*)\)\s*\{/g,
|
|
20711
|
+
(match, name, params) => {
|
|
20712
|
+
if (match.includes(":")) return match;
|
|
20713
|
+
return `async function ${name}(${params}): Promise<unknown> {`;
|
|
20714
|
+
}
|
|
20715
|
+
);
|
|
20716
|
+
fixedCode = fixedCode.replace(
|
|
20717
|
+
/const (\w+) = async \(([^)]*)\)\s*=>/g,
|
|
20718
|
+
(match, name, params) => {
|
|
20719
|
+
if (match.includes(":")) return match;
|
|
20720
|
+
return `const ${name} = async (${params}): Promise<unknown> =>`;
|
|
20721
|
+
}
|
|
20722
|
+
);
|
|
20723
|
+
}
|
|
20724
|
+
}
|
|
20725
|
+
fixedCode = fixedCode.replace(/,(\s*[}\]])/g, "$1");
|
|
20726
|
+
fixedCode = fixedCode.replace(
|
|
20727
|
+
/export \{ ([^}]+) \}(\s*\n)/g,
|
|
20728
|
+
"export { $1 };$2"
|
|
20729
|
+
);
|
|
20730
|
+
return fixedCode;
|
|
20731
|
+
}
|
|
20732
|
+
function attemptAutoFixIterative(code, maxIterations = 3) {
|
|
20733
|
+
let currentCode = code;
|
|
20734
|
+
let iterations = 0;
|
|
20735
|
+
let previousErrorCount = Infinity;
|
|
20736
|
+
while (iterations < maxIterations) {
|
|
20737
|
+
const errors = validateTypeScript(currentCode);
|
|
20738
|
+
if (errors.length === 0 || errors.length >= previousErrorCount) {
|
|
20739
|
+
return {
|
|
20740
|
+
code: currentCode,
|
|
20741
|
+
fixed: errors.length === 0,
|
|
20742
|
+
iterations,
|
|
20743
|
+
remainingErrors: errors
|
|
20744
|
+
};
|
|
20745
|
+
}
|
|
20746
|
+
previousErrorCount = errors.length;
|
|
20747
|
+
currentCode = attemptAutoFix(currentCode, errors);
|
|
20748
|
+
iterations++;
|
|
20749
|
+
}
|
|
20750
|
+
const finalErrors = validateTypeScript(currentCode);
|
|
20751
|
+
return {
|
|
20752
|
+
code: currentCode,
|
|
20753
|
+
fixed: finalErrors.length === 0,
|
|
20754
|
+
iterations,
|
|
20755
|
+
remainingErrors: finalErrors
|
|
20756
|
+
};
|
|
20757
|
+
}
|
|
20758
|
+
function getSyntaxErrors(code) {
|
|
20759
|
+
const sourceFile = ts.createSourceFile(
|
|
20760
|
+
"check.ts",
|
|
20761
|
+
code,
|
|
20762
|
+
ts.ScriptTarget.ES2020,
|
|
20763
|
+
true,
|
|
20764
|
+
ts.ScriptKind.TS
|
|
20765
|
+
);
|
|
20766
|
+
const syntaxErrors = sourceFile.parseDiagnostics || [];
|
|
20767
|
+
return syntaxErrors.map((diagnostic) => {
|
|
20768
|
+
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
20769
|
+
if (diagnostic.start !== void 0) {
|
|
20770
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(diagnostic.start);
|
|
20771
|
+
return `Line ${line + 1}:${character + 1}: ${message}`;
|
|
20772
|
+
}
|
|
20773
|
+
return message;
|
|
20774
|
+
});
|
|
20775
|
+
}
|
|
20776
|
+
var STANDARD_ENV_VARS = [
|
|
20777
|
+
{
|
|
20778
|
+
name: "NETPAD_URL",
|
|
20779
|
+
description: "NetPad API URL",
|
|
20780
|
+
example: "https://api.netpad.io"
|
|
20781
|
+
},
|
|
20782
|
+
{
|
|
20783
|
+
name: "NETPAD_API_KEY",
|
|
20784
|
+
description: "Your NetPad API key",
|
|
20785
|
+
example: "np_live_xxxxx"
|
|
20786
|
+
},
|
|
20787
|
+
{
|
|
20788
|
+
name: "NETPAD_ORG_ID",
|
|
20789
|
+
description: "Your organization ID",
|
|
20790
|
+
example: "org_xxxxx"
|
|
20791
|
+
},
|
|
20792
|
+
{
|
|
20793
|
+
name: "NETPAD_PROJECT_ID",
|
|
20794
|
+
description: "Your project ID",
|
|
20795
|
+
example: "proj_xxxxx"
|
|
20796
|
+
}
|
|
20797
|
+
];
|
|
20798
|
+
function generateSelfContainedCode(options) {
|
|
20799
|
+
const {
|
|
20800
|
+
title,
|
|
20801
|
+
description,
|
|
20802
|
+
includeFormTypes = true,
|
|
20803
|
+
includeWorkflowTypes = false,
|
|
20804
|
+
configCode,
|
|
20805
|
+
functionsCode,
|
|
20806
|
+
mainCode
|
|
20807
|
+
} = options;
|
|
20808
|
+
let code = `/**
|
|
20809
|
+
* ${title}
|
|
20810
|
+
* ${description || "Generated by NetPad"}
|
|
20811
|
+
*
|
|
20812
|
+
* Run with: npx tsx ${title.toLowerCase().replace(/\s+/g, "-")}.ts
|
|
20813
|
+
*/
|
|
20814
|
+
|
|
20815
|
+
`;
|
|
20816
|
+
if (includeFormTypes) {
|
|
20817
|
+
code += INLINE_TYPES + "\n";
|
|
20818
|
+
}
|
|
20819
|
+
if (includeWorkflowTypes) {
|
|
20820
|
+
code += INLINE_WORKFLOW_TYPES + "\n";
|
|
20821
|
+
}
|
|
20822
|
+
code += `// ============================================================================
|
|
20823
|
+
// Configuration
|
|
20824
|
+
// ============================================================================
|
|
20825
|
+
|
|
20826
|
+
const CONFIG = {
|
|
20827
|
+
baseUrl: process.env.NETPAD_URL ?? 'https://api.netpad.io',
|
|
20828
|
+
apiKey: process.env.NETPAD_API_KEY ?? '',
|
|
20829
|
+
organizationId: process.env.NETPAD_ORG_ID ?? '',
|
|
20830
|
+
projectId: process.env.NETPAD_PROJECT_ID ?? '',
|
|
20831
|
+
};
|
|
20832
|
+
|
|
20833
|
+
${configCode}
|
|
20834
|
+
|
|
20835
|
+
// ============================================================================
|
|
20836
|
+
// API Functions
|
|
20837
|
+
// ============================================================================
|
|
20838
|
+
|
|
20839
|
+
${functionsCode}
|
|
20840
|
+
`;
|
|
20841
|
+
if (mainCode) {
|
|
20842
|
+
code += `
|
|
20843
|
+
// ============================================================================
|
|
20844
|
+
// Main
|
|
20845
|
+
// ============================================================================
|
|
20846
|
+
|
|
20847
|
+
${mainCode}
|
|
20848
|
+
`;
|
|
20849
|
+
}
|
|
20850
|
+
return code;
|
|
20851
|
+
}
|
|
20852
|
+
function createToolOutput(options) {
|
|
20853
|
+
const { code, filename, additionalFiles, envVars, dependencies, skipValidation } = options;
|
|
20854
|
+
if (skipValidation) {
|
|
20855
|
+
return {
|
|
20856
|
+
code,
|
|
20857
|
+
filename,
|
|
20858
|
+
additionalFiles,
|
|
20859
|
+
envVars: envVars || STANDARD_ENV_VARS,
|
|
20860
|
+
dependencies,
|
|
20861
|
+
validated: true
|
|
20862
|
+
// Assumed valid
|
|
20863
|
+
};
|
|
20864
|
+
}
|
|
20865
|
+
const syntaxErrors = getSyntaxErrors(code);
|
|
20866
|
+
if (syntaxErrors.length > 0) {
|
|
20867
|
+
return {
|
|
20868
|
+
code,
|
|
20869
|
+
filename,
|
|
20870
|
+
additionalFiles,
|
|
20871
|
+
envVars: envVars || STANDARD_ENV_VARS,
|
|
20872
|
+
dependencies,
|
|
20873
|
+
validated: false,
|
|
20874
|
+
validationErrors: syntaxErrors
|
|
20875
|
+
};
|
|
20876
|
+
}
|
|
20877
|
+
const fixResult = attemptAutoFixIterative(code);
|
|
20878
|
+
return {
|
|
20879
|
+
code: fixResult.code,
|
|
20880
|
+
filename,
|
|
20881
|
+
additionalFiles,
|
|
20882
|
+
envVars: envVars || STANDARD_ENV_VARS,
|
|
20883
|
+
dependencies,
|
|
20884
|
+
validated: fixResult.fixed,
|
|
20885
|
+
validationErrors: fixResult.remainingErrors.length > 0 ? fixResult.remainingErrors : void 0
|
|
20886
|
+
};
|
|
20887
|
+
}
|
|
20888
|
+
function formatToolOutput(output) {
|
|
20889
|
+
let result = "";
|
|
20890
|
+
if (output.validated) {
|
|
20891
|
+
result += "\u2705 **Code validated successfully** - Ready to use!\n\n";
|
|
20892
|
+
} else {
|
|
20893
|
+
result += "\u26A0\uFE0F **Code validation warnings:**\n\n";
|
|
20894
|
+
result += "The generated code has some issues that may need manual review:\n\n";
|
|
20895
|
+
for (const error48 of output.validationErrors || []) {
|
|
20896
|
+
result += `- ${error48}
|
|
20897
|
+
`;
|
|
20898
|
+
}
|
|
20899
|
+
result += "\n> Note: The code may still work in most environments. These warnings are from strict TypeScript checking.\n\n";
|
|
20900
|
+
}
|
|
20901
|
+
result += "## Quick Start\n\n";
|
|
20902
|
+
result += "```bash\n";
|
|
20903
|
+
result += `# Save the code below to ${output.filename}
|
|
20904
|
+
`;
|
|
20905
|
+
result += `# Then run:
|
|
20906
|
+
`;
|
|
20907
|
+
result += `npx tsx ${output.filename}
|
|
20908
|
+
`;
|
|
20909
|
+
result += "```\n\n";
|
|
20910
|
+
if (output.envVars.length > 0) {
|
|
20911
|
+
result += "## Required Environment Variables\n\n";
|
|
20912
|
+
result += "Create a `.env` file or export these variables:\n\n";
|
|
20913
|
+
result += "```bash\n";
|
|
20914
|
+
for (const env of output.envVars) {
|
|
20915
|
+
result += `# ${env.description}
|
|
20916
|
+
`;
|
|
20917
|
+
result += `export ${env.name}="${env.example}"
|
|
20918
|
+
`;
|
|
20919
|
+
}
|
|
20920
|
+
result += "```\n\n";
|
|
20921
|
+
}
|
|
20922
|
+
if (output.dependencies && output.dependencies.length > 0) {
|
|
20923
|
+
result += "## Dependencies\n\n";
|
|
20924
|
+
result += "```bash\n";
|
|
20925
|
+
result += `npm install ${output.dependencies.join(" ")}
|
|
20926
|
+
`;
|
|
20927
|
+
result += "```\n\n";
|
|
20928
|
+
}
|
|
20929
|
+
result += `## ${output.filename}
|
|
20930
|
+
|
|
20931
|
+
`;
|
|
20932
|
+
result += "```typescript\n";
|
|
20933
|
+
result += output.code;
|
|
20934
|
+
result += "\n```\n";
|
|
20935
|
+
if (output.additionalFiles && output.additionalFiles.length > 0) {
|
|
20936
|
+
result += "\n## Additional Files\n";
|
|
20937
|
+
for (const file2 of output.additionalFiles) {
|
|
20938
|
+
result += `
|
|
20939
|
+
### ${file2.filename}
|
|
20940
|
+
|
|
20941
|
+
`;
|
|
20942
|
+
result += "```typescript\n";
|
|
20943
|
+
result += file2.code;
|
|
20944
|
+
result += "\n```\n";
|
|
20945
|
+
}
|
|
20946
|
+
}
|
|
20947
|
+
return result;
|
|
20948
|
+
}
|
|
20949
|
+
|
|
20950
|
+
// src/index.ts
|
|
20951
|
+
var server = new McpServer({
|
|
20952
|
+
name: "@netpad/mcp-server",
|
|
20953
|
+
version: "2.0.0"
|
|
20954
|
+
});
|
|
20955
|
+
server.resource(
|
|
20956
|
+
"netpad-docs",
|
|
20957
|
+
"netpad://docs/readme",
|
|
20958
|
+
async () => ({
|
|
20959
|
+
contents: [
|
|
20960
|
+
{
|
|
20961
|
+
uri: "netpad://docs/readme",
|
|
20962
|
+
mimeType: "text/markdown",
|
|
20963
|
+
text: DOCUMENTATION.readme
|
|
20964
|
+
}
|
|
20965
|
+
]
|
|
20966
|
+
})
|
|
20135
20967
|
);
|
|
20136
20968
|
server.resource(
|
|
20137
20969
|
"netpad-architecture",
|
|
@@ -20211,25 +21043,261 @@ server.resource(
|
|
|
20211
21043
|
]
|
|
20212
21044
|
})
|
|
20213
21045
|
);
|
|
21046
|
+
server.resource(
|
|
21047
|
+
"netpad-extensions",
|
|
21048
|
+
"netpad://docs/extensions",
|
|
21049
|
+
async () => ({
|
|
21050
|
+
contents: [
|
|
21051
|
+
{
|
|
21052
|
+
uri: "netpad://docs/extensions",
|
|
21053
|
+
mimeType: "text/markdown",
|
|
21054
|
+
text: DOCUMENTATION.extensions
|
|
21055
|
+
}
|
|
21056
|
+
]
|
|
21057
|
+
})
|
|
21058
|
+
);
|
|
21059
|
+
server.resource(
|
|
21060
|
+
"netpad-demo-node",
|
|
21061
|
+
"netpad://examples/demo-node",
|
|
21062
|
+
async () => ({
|
|
21063
|
+
contents: [
|
|
21064
|
+
{
|
|
21065
|
+
uri: "netpad://examples/demo-node",
|
|
21066
|
+
mimeType: "text/markdown",
|
|
21067
|
+
text: `# @netpad/demo-node Extension Example
|
|
21068
|
+
|
|
21069
|
+
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.
|
|
21070
|
+
|
|
21071
|
+
## What It Provides
|
|
21072
|
+
|
|
21073
|
+
A single workflow node called **"Log Message"** that:
|
|
21074
|
+
- Logs configurable messages to the console
|
|
21075
|
+
- Supports different log levels (info, warn, error)
|
|
21076
|
+
- Can pass through input data to downstream nodes
|
|
21077
|
+
- Demonstrates all key extension concepts
|
|
21078
|
+
|
|
21079
|
+
## Node Details
|
|
21080
|
+
|
|
21081
|
+
**Type:** \`demo:log-message\`
|
|
21082
|
+
**Category:** Custom
|
|
21083
|
+
**Icon:** Terminal (MUI icon)
|
|
21084
|
+
**Color:** #FF6B35 (orange)
|
|
21085
|
+
|
|
21086
|
+
### Configuration Fields
|
|
21087
|
+
|
|
21088
|
+
| Field | Type | Description |
|
|
21089
|
+
|-------|------|-------------|
|
|
21090
|
+
| Message | textarea | The message to log. Supports \`{{variable}}\` syntax for dynamic values |
|
|
21091
|
+
| Log Level | select | info, warn, or error |
|
|
21092
|
+
| Label | text | Custom label for the log entry |
|
|
21093
|
+
| Pass Through | boolean | Include input data in output (default: true) |
|
|
21094
|
+
|
|
21095
|
+
### Example Usage
|
|
21096
|
+
|
|
21097
|
+
1. Drag the "Log Message" node onto the workflow canvas
|
|
21098
|
+
2. Connect it after a form trigger or other node
|
|
21099
|
+
3. Configure the message: \`New submission from {{formData.email}}\`
|
|
21100
|
+
4. The node will log the message and pass data to the next node
|
|
21101
|
+
|
|
21102
|
+
## Using as a Template
|
|
21103
|
+
|
|
21104
|
+
1. **Copy the package** to a new directory
|
|
21105
|
+
2. **Update package.json** with your extension name
|
|
21106
|
+
3. **Modify src/index.ts**:
|
|
21107
|
+
- Change extension metadata (id, name, version)
|
|
21108
|
+
- Update node definition (type, label, icon, color, config fields)
|
|
21109
|
+
- Implement your business logic in the handler
|
|
21110
|
+
4. **Export the extension** as default export
|
|
21111
|
+
|
|
21112
|
+
## Key Concepts Demonstrated
|
|
21113
|
+
|
|
21114
|
+
- **Extension Metadata**: Identifies your extension in the system
|
|
21115
|
+
- **Workflow Nodes**: Custom nodes that appear in the workflow editor palette
|
|
21116
|
+
- **Node Definition**: Describes the node's appearance and configuration UI
|
|
21117
|
+
- **Node Handler**: The function that executes when the node runs
|
|
21118
|
+
- **Configuration Fields**: UI fields for node configuration
|
|
21119
|
+
- **Output Handles**: Connection points for downstream nodes
|
|
21120
|
+
- **Lifecycle Hooks**: initialize() and cleanup() functions
|
|
21121
|
+
|
|
21122
|
+
## File Structure
|
|
21123
|
+
|
|
21124
|
+
\`\`\`
|
|
21125
|
+
packages/demo-node/
|
|
21126
|
+
\u251C\u2500\u2500 package.json # Package metadata
|
|
21127
|
+
\u251C\u2500\u2500 README.md # Documentation
|
|
21128
|
+
\u2514\u2500\u2500 src/
|
|
21129
|
+
\u2514\u2500\u2500 index.ts # Extension + node definition + handler
|
|
21130
|
+
\`\`\`
|
|
21131
|
+
|
|
21132
|
+
## Extension Structure
|
|
21133
|
+
|
|
21134
|
+
\`\`\`typescript
|
|
21135
|
+
export const demoNodeExtension: NetPadExtension = {
|
|
21136
|
+
metadata: {
|
|
21137
|
+
id: 'netpad-demo-node',
|
|
21138
|
+
name: 'Demo Node Extension',
|
|
21139
|
+
version: '1.0.0',
|
|
21140
|
+
},
|
|
21141
|
+
features: ['custom:demo-node'],
|
|
21142
|
+
workflowNodes: [
|
|
21143
|
+
{
|
|
21144
|
+
definition: {
|
|
21145
|
+
type: 'demo:log-message',
|
|
21146
|
+
label: 'Log Message',
|
|
21147
|
+
category: 'custom',
|
|
21148
|
+
// ... node appearance config
|
|
21149
|
+
},
|
|
21150
|
+
handler: async (context) => {
|
|
21151
|
+
// ... execution logic
|
|
21152
|
+
return { success: true, data: {...} };
|
|
21153
|
+
},
|
|
21154
|
+
},
|
|
21155
|
+
],
|
|
21156
|
+
initialize: async () => { /* setup */ },
|
|
21157
|
+
cleanup: async () => { /* teardown */ },
|
|
21158
|
+
};
|
|
21159
|
+
\`\`\`
|
|
21160
|
+
|
|
21161
|
+
## Handler Implementation
|
|
21162
|
+
|
|
21163
|
+
The handler receives a \`NodeExecutionContext\` with:
|
|
21164
|
+
- \`resolvedConfig\`: Configuration with variables resolved
|
|
21165
|
+
- \`inputs\`: Data from previous nodes
|
|
21166
|
+
- \`trigger\`: Workflow trigger information
|
|
21167
|
+
- Helper functions for connections and credentials
|
|
21168
|
+
|
|
21169
|
+
See the full source code in \`packages/demo-node/src/index.ts\` for complete implementation details.
|
|
21170
|
+
`
|
|
21171
|
+
}
|
|
21172
|
+
]
|
|
21173
|
+
})
|
|
21174
|
+
);
|
|
20214
21175
|
server.tool(
|
|
20215
21176
|
"generate_form",
|
|
20216
|
-
"Generate a complete NetPad form configuration from a description.
|
|
21177
|
+
"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`.",
|
|
20217
21178
|
{
|
|
20218
21179
|
description: external_exports.string().describe("Natural language description of the form to generate"),
|
|
20219
21180
|
formName: external_exports.string().describe("Name of the form"),
|
|
20220
21181
|
includeMultiPage: external_exports.boolean().optional().describe("Whether to organize fields into multiple pages"),
|
|
20221
|
-
includeTheme: external_exports.boolean().optional().describe("Whether to include theme configuration")
|
|
21182
|
+
includeTheme: external_exports.boolean().optional().describe("Whether to include theme configuration"),
|
|
21183
|
+
outputFormat: external_exports.enum(["typescript", "json"]).optional().describe('Output format: "typescript" (default) for complete working code, "json" for raw config')
|
|
20222
21184
|
},
|
|
20223
|
-
async ({ description, formName, includeMultiPage, includeTheme }) => {
|
|
21185
|
+
async ({ description, formName, includeMultiPage, includeTheme, outputFormat = "typescript" }) => {
|
|
20224
21186
|
const schema = generateFormSchema(description, formName, {
|
|
20225
21187
|
multiPage: includeMultiPage,
|
|
20226
21188
|
theme: includeTheme
|
|
20227
21189
|
});
|
|
21190
|
+
if (outputFormat === "json") {
|
|
21191
|
+
return {
|
|
21192
|
+
content: [
|
|
21193
|
+
{
|
|
21194
|
+
type: "text",
|
|
21195
|
+
text: JSON.stringify(schema, null, 2)
|
|
21196
|
+
}
|
|
21197
|
+
]
|
|
21198
|
+
};
|
|
21199
|
+
}
|
|
21200
|
+
const slug = formName.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
21201
|
+
const configCode = `export const formConfig: FormConfig = ${JSON.stringify({
|
|
21202
|
+
...schema,
|
|
21203
|
+
slug
|
|
21204
|
+
}, null, 2)};`;
|
|
21205
|
+
const functionsCode = `/**
|
|
21206
|
+
* Submit form data to the NetPad API.
|
|
21207
|
+
*/
|
|
21208
|
+
export async function submitForm(data: Record<string, unknown>): Promise<SubmitResult> {
|
|
21209
|
+
try {
|
|
21210
|
+
const response = await fetch(
|
|
21211
|
+
\`\${CONFIG.baseUrl}/api/forms/\${formConfig.slug}/submit\`,
|
|
21212
|
+
{
|
|
21213
|
+
method: 'POST',
|
|
21214
|
+
headers: {
|
|
21215
|
+
'Content-Type': 'application/json',
|
|
21216
|
+
'Authorization': \`Bearer \${CONFIG.apiKey}\`,
|
|
21217
|
+
},
|
|
21218
|
+
body: JSON.stringify({ data }),
|
|
21219
|
+
}
|
|
21220
|
+
);
|
|
21221
|
+
|
|
21222
|
+
if (!response.ok) {
|
|
21223
|
+
const error = await response.text();
|
|
21224
|
+
return { success: false, error };
|
|
21225
|
+
}
|
|
21226
|
+
|
|
21227
|
+
const result = await response.json() as { submissionId: string };
|
|
21228
|
+
return { success: true, submissionId: result.submissionId };
|
|
21229
|
+
} catch (error) {
|
|
21230
|
+
return {
|
|
21231
|
+
success: false,
|
|
21232
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
21233
|
+
};
|
|
21234
|
+
}
|
|
21235
|
+
}
|
|
21236
|
+
|
|
21237
|
+
/**
|
|
21238
|
+
* Create the form in NetPad (run once to set up).
|
|
21239
|
+
*/
|
|
21240
|
+
export async function createForm(): Promise<{ formId: string } | { error: string }> {
|
|
21241
|
+
try {
|
|
21242
|
+
const response = await fetch(\`\${CONFIG.baseUrl}/api/forms\`, {
|
|
21243
|
+
method: 'POST',
|
|
21244
|
+
headers: {
|
|
21245
|
+
'Content-Type': 'application/json',
|
|
21246
|
+
'Authorization': \`Bearer \${CONFIG.apiKey}\`,
|
|
21247
|
+
},
|
|
21248
|
+
body: JSON.stringify({
|
|
21249
|
+
...formConfig,
|
|
21250
|
+
organizationId: CONFIG.organizationId,
|
|
21251
|
+
projectId: CONFIG.projectId,
|
|
21252
|
+
}),
|
|
21253
|
+
});
|
|
21254
|
+
|
|
21255
|
+
if (!response.ok) {
|
|
21256
|
+
return { error: await response.text() };
|
|
21257
|
+
}
|
|
21258
|
+
|
|
21259
|
+
const result = await response.json() as { form: { formId: string } };
|
|
21260
|
+
return { formId: result.form.formId };
|
|
21261
|
+
} catch (error) {
|
|
21262
|
+
return { error: error instanceof Error ? error.message : 'Unknown error' };
|
|
21263
|
+
}
|
|
21264
|
+
}`;
|
|
21265
|
+
const mainCode = `// Example usage
|
|
21266
|
+
async function main() {
|
|
21267
|
+
console.log('Creating form:', formConfig.name);
|
|
21268
|
+
|
|
21269
|
+
// Create the form (run once)
|
|
21270
|
+
const createResult = await createForm();
|
|
21271
|
+
if ('error' in createResult) {
|
|
21272
|
+
console.error('Failed to create form:', createResult.error);
|
|
21273
|
+
return;
|
|
21274
|
+
}
|
|
21275
|
+
|
|
21276
|
+
console.log('\u2705 Form created with ID:', createResult.formId);
|
|
21277
|
+
console.log('Form slug:', formConfig.slug);
|
|
21278
|
+
}
|
|
21279
|
+
|
|
21280
|
+
// Uncomment to run:
|
|
21281
|
+
// main().catch(console.error);
|
|
21282
|
+
`;
|
|
21283
|
+
const code = generateSelfContainedCode({
|
|
21284
|
+
title: formName,
|
|
21285
|
+
description: schema.description,
|
|
21286
|
+
includeFormTypes: true,
|
|
21287
|
+
configCode,
|
|
21288
|
+
functionsCode,
|
|
21289
|
+
mainCode
|
|
21290
|
+
});
|
|
21291
|
+
const output = createToolOutput({
|
|
21292
|
+
code,
|
|
21293
|
+
filename: `${slug}.ts`,
|
|
21294
|
+
envVars: STANDARD_ENV_VARS
|
|
21295
|
+
});
|
|
20228
21296
|
return {
|
|
20229
21297
|
content: [
|
|
20230
21298
|
{
|
|
20231
21299
|
type: "text",
|
|
20232
|
-
text:
|
|
21300
|
+
text: formatToolOutput(output)
|
|
20233
21301
|
}
|
|
20234
21302
|
]
|
|
20235
21303
|
};
|
|
@@ -20381,9 +21449,451 @@ server.tool(
|
|
|
20381
21449
|
}
|
|
20382
21450
|
}
|
|
20383
21451
|
);
|
|
21452
|
+
server.tool(
|
|
21453
|
+
"get_reference",
|
|
21454
|
+
"Get NetPad reference information: field types, operators, formula functions, validation options, theme options, or documentation. This is the recommended way to access all reference data.",
|
|
21455
|
+
{
|
|
21456
|
+
type: external_exports.enum([
|
|
21457
|
+
"field_types",
|
|
21458
|
+
"operators",
|
|
21459
|
+
"formula_functions",
|
|
21460
|
+
"validation_options",
|
|
21461
|
+
"theme_options",
|
|
21462
|
+
"documentation"
|
|
21463
|
+
]).describe("The type of reference to retrieve"),
|
|
21464
|
+
category: external_exports.string().optional().describe('Filter by category (for field_types: "text", "selection", "date"; for formula_functions: "math", "string", "date")'),
|
|
21465
|
+
topic: external_exports.enum(["readme", "architecture", "quick-start", "examples", "api-client"]).optional().describe('Documentation topic (required when type is "documentation")')
|
|
21466
|
+
},
|
|
21467
|
+
async ({ type, category, topic }) => {
|
|
21468
|
+
switch (type) {
|
|
21469
|
+
case "field_types": {
|
|
21470
|
+
let types = FIELD_TYPES;
|
|
21471
|
+
if (category) {
|
|
21472
|
+
types = types.filter((t) => t.category.toLowerCase() === category.toLowerCase());
|
|
21473
|
+
}
|
|
21474
|
+
return {
|
|
21475
|
+
content: [{
|
|
21476
|
+
type: "text",
|
|
21477
|
+
text: JSON.stringify({
|
|
21478
|
+
referenceType: "field_types",
|
|
21479
|
+
count: types.length,
|
|
21480
|
+
categories: [...new Set(FIELD_TYPES.map((t) => t.category))],
|
|
21481
|
+
data: types
|
|
21482
|
+
}, null, 2)
|
|
21483
|
+
}]
|
|
21484
|
+
};
|
|
21485
|
+
}
|
|
21486
|
+
case "operators": {
|
|
21487
|
+
return {
|
|
21488
|
+
content: [{
|
|
21489
|
+
type: "text",
|
|
21490
|
+
text: JSON.stringify({
|
|
21491
|
+
referenceType: "operators",
|
|
21492
|
+
count: OPERATORS.length,
|
|
21493
|
+
data: OPERATORS
|
|
21494
|
+
}, null, 2)
|
|
21495
|
+
}]
|
|
21496
|
+
};
|
|
21497
|
+
}
|
|
21498
|
+
case "formula_functions": {
|
|
21499
|
+
let functions = FORMULA_FUNCTIONS;
|
|
21500
|
+
if (category) {
|
|
21501
|
+
functions = functions.filter((f) => f.category.toLowerCase() === category.toLowerCase());
|
|
21502
|
+
}
|
|
21503
|
+
return {
|
|
21504
|
+
content: [{
|
|
21505
|
+
type: "text",
|
|
21506
|
+
text: JSON.stringify({
|
|
21507
|
+
referenceType: "formula_functions",
|
|
21508
|
+
count: functions.length,
|
|
21509
|
+
categories: [...new Set(FORMULA_FUNCTIONS.map((f) => f.category))],
|
|
21510
|
+
data: functions
|
|
21511
|
+
}, null, 2)
|
|
21512
|
+
}]
|
|
21513
|
+
};
|
|
21514
|
+
}
|
|
21515
|
+
case "validation_options": {
|
|
21516
|
+
return {
|
|
21517
|
+
content: [{
|
|
21518
|
+
type: "text",
|
|
21519
|
+
text: JSON.stringify({
|
|
21520
|
+
referenceType: "validation_options",
|
|
21521
|
+
data: VALIDATION_OPTIONS
|
|
21522
|
+
}, null, 2)
|
|
21523
|
+
}]
|
|
21524
|
+
};
|
|
21525
|
+
}
|
|
21526
|
+
case "theme_options": {
|
|
21527
|
+
return {
|
|
21528
|
+
content: [{
|
|
21529
|
+
type: "text",
|
|
21530
|
+
text: JSON.stringify({
|
|
21531
|
+
referenceType: "theme_options",
|
|
21532
|
+
data: THEME_OPTIONS
|
|
21533
|
+
}, null, 2)
|
|
21534
|
+
}]
|
|
21535
|
+
};
|
|
21536
|
+
}
|
|
21537
|
+
case "documentation": {
|
|
21538
|
+
const docs = {
|
|
21539
|
+
"readme": DOCUMENTATION.readme,
|
|
21540
|
+
"architecture": ARCHITECTURE_GUIDE,
|
|
21541
|
+
"quick-start": QUICK_START_GUIDE,
|
|
21542
|
+
"examples": EXAMPLES,
|
|
21543
|
+
"api-client": DOCUMENTATION.apiClient
|
|
21544
|
+
};
|
|
21545
|
+
const selectedTopic = topic || "readme";
|
|
21546
|
+
return {
|
|
21547
|
+
content: [{
|
|
21548
|
+
type: "text",
|
|
21549
|
+
text: `# NetPad Documentation: ${selectedTopic}
|
|
21550
|
+
|
|
21551
|
+
${docs[selectedTopic] || "Documentation not found"}
|
|
21552
|
+
|
|
21553
|
+
---
|
|
21554
|
+
Available topics: ${Object.keys(docs).join(", ")}`
|
|
21555
|
+
}]
|
|
21556
|
+
};
|
|
21557
|
+
}
|
|
21558
|
+
default:
|
|
21559
|
+
return {
|
|
21560
|
+
content: [{
|
|
21561
|
+
type: "text",
|
|
21562
|
+
text: "Invalid reference type. Use: field_types, operators, formula_functions, validation_options, theme_options, or documentation"
|
|
21563
|
+
}]
|
|
21564
|
+
};
|
|
21565
|
+
}
|
|
21566
|
+
}
|
|
21567
|
+
);
|
|
21568
|
+
server.tool(
|
|
21569
|
+
"browse_templates",
|
|
21570
|
+
"Browse all NetPad templates: forms (25+), applications (7), workflows (5), conversational (4), and query templates. This is the recommended way to discover and access all templates.",
|
|
21571
|
+
{
|
|
21572
|
+
templateType: external_exports.enum([
|
|
21573
|
+
"form",
|
|
21574
|
+
"application",
|
|
21575
|
+
"workflow",
|
|
21576
|
+
"conversational",
|
|
21577
|
+
"query",
|
|
21578
|
+
"use_case",
|
|
21579
|
+
"all"
|
|
21580
|
+
]).describe("Type of templates to browse"),
|
|
21581
|
+
action: external_exports.enum(["list", "get", "categories"]).optional().describe('Action: "list" (default) returns summaries, "get" returns full details, "categories" lists available categories'),
|
|
21582
|
+
templateId: external_exports.string().optional().describe('Template ID (required when action is "get")'),
|
|
21583
|
+
category: external_exports.string().optional().describe("Filter templates by category"),
|
|
21584
|
+
search: external_exports.string().optional().describe("Search templates by name, description, or tags (form templates only)")
|
|
21585
|
+
},
|
|
21586
|
+
async ({ templateType, action = "list", templateId, category, search }) => {
|
|
21587
|
+
const formatSummary = (id, name, desc, cat, extra = {}) => ({
|
|
21588
|
+
id,
|
|
21589
|
+
name,
|
|
21590
|
+
description: desc,
|
|
21591
|
+
category: cat,
|
|
21592
|
+
...extra
|
|
21593
|
+
});
|
|
21594
|
+
switch (templateType) {
|
|
21595
|
+
case "form": {
|
|
21596
|
+
if (action === "categories") {
|
|
21597
|
+
return {
|
|
21598
|
+
content: [{
|
|
21599
|
+
type: "text",
|
|
21600
|
+
text: JSON.stringify({
|
|
21601
|
+
templateType: "form",
|
|
21602
|
+
categories: TEMPLATE_CATEGORIES,
|
|
21603
|
+
totalTemplates: Object.keys(FORM_TEMPLATES).length
|
|
21604
|
+
}, null, 2)
|
|
21605
|
+
}]
|
|
21606
|
+
};
|
|
21607
|
+
}
|
|
21608
|
+
if (action === "get" && templateId) {
|
|
21609
|
+
const template = getTemplateById(templateId);
|
|
21610
|
+
if (!template) {
|
|
21611
|
+
return {
|
|
21612
|
+
content: [{
|
|
21613
|
+
type: "text",
|
|
21614
|
+
text: JSON.stringify({
|
|
21615
|
+
error: `Form template "${templateId}" not found`,
|
|
21616
|
+
availableTemplates: Object.keys(FORM_TEMPLATES)
|
|
21617
|
+
}, null, 2)
|
|
21618
|
+
}]
|
|
21619
|
+
};
|
|
21620
|
+
}
|
|
21621
|
+
return { content: [{ type: "text", text: JSON.stringify(template, null, 2) }] };
|
|
21622
|
+
}
|
|
21623
|
+
let templates;
|
|
21624
|
+
if (search) {
|
|
21625
|
+
templates = searchTemplates(search);
|
|
21626
|
+
} else if (category) {
|
|
21627
|
+
templates = getTemplatesByCategory(category);
|
|
21628
|
+
} else {
|
|
21629
|
+
templates = Object.values(FORM_TEMPLATES);
|
|
21630
|
+
}
|
|
21631
|
+
const summary = templates.map((t) => formatSummary(t.id, t.name, t.description, t.category, {
|
|
21632
|
+
tags: t.tags,
|
|
21633
|
+
icon: t.icon,
|
|
21634
|
+
fieldCount: t.fields.length,
|
|
21635
|
+
hasMultiPage: !!t.multiPage?.enabled
|
|
21636
|
+
}));
|
|
21637
|
+
return {
|
|
21638
|
+
content: [{
|
|
21639
|
+
type: "text",
|
|
21640
|
+
text: JSON.stringify({
|
|
21641
|
+
templateType: "form",
|
|
21642
|
+
templates: summary,
|
|
21643
|
+
total: summary.length,
|
|
21644
|
+
categories: [...new Set(templates.map((t) => t.category))]
|
|
21645
|
+
}, null, 2)
|
|
21646
|
+
}]
|
|
21647
|
+
};
|
|
21648
|
+
}
|
|
21649
|
+
case "application": {
|
|
21650
|
+
if (action === "categories") {
|
|
21651
|
+
return {
|
|
21652
|
+
content: [{
|
|
21653
|
+
type: "text",
|
|
21654
|
+
text: JSON.stringify({
|
|
21655
|
+
templateType: "application",
|
|
21656
|
+
categories: [...new Set(Object.values(APPLICATION_TEMPLATES).map((t) => t.category))],
|
|
21657
|
+
totalTemplates: Object.keys(APPLICATION_TEMPLATES).length
|
|
21658
|
+
}, null, 2)
|
|
21659
|
+
}]
|
|
21660
|
+
};
|
|
21661
|
+
}
|
|
21662
|
+
if (action === "get" && templateId) {
|
|
21663
|
+
const template = APPLICATION_TEMPLATES[templateId];
|
|
21664
|
+
if (!template) {
|
|
21665
|
+
return {
|
|
21666
|
+
content: [{
|
|
21667
|
+
type: "text",
|
|
21668
|
+
text: JSON.stringify({
|
|
21669
|
+
error: `Application template "${templateId}" not found`,
|
|
21670
|
+
availableTemplates: Object.keys(APPLICATION_TEMPLATES)
|
|
21671
|
+
}, null, 2)
|
|
21672
|
+
}]
|
|
21673
|
+
};
|
|
21674
|
+
}
|
|
21675
|
+
return { content: [{ type: "text", text: JSON.stringify(template, null, 2) }] };
|
|
21676
|
+
}
|
|
21677
|
+
let templates = Object.values(APPLICATION_TEMPLATES);
|
|
21678
|
+
if (category) {
|
|
21679
|
+
templates = templates.filter((t) => t.category.toLowerCase() === category.toLowerCase());
|
|
21680
|
+
}
|
|
21681
|
+
const summary = templates.map((t) => formatSummary(t.id, t.name, t.description, t.category, {
|
|
21682
|
+
tags: t.tags,
|
|
21683
|
+
formsCount: t.structure.forms.length,
|
|
21684
|
+
workflowsCount: t.structure.workflows.length
|
|
21685
|
+
}));
|
|
21686
|
+
return {
|
|
21687
|
+
content: [{
|
|
21688
|
+
type: "text",
|
|
21689
|
+
text: JSON.stringify({
|
|
21690
|
+
templateType: "application",
|
|
21691
|
+
templates: summary,
|
|
21692
|
+
total: summary.length,
|
|
21693
|
+
categories: [...new Set(Object.values(APPLICATION_TEMPLATES).map((t) => t.category))]
|
|
21694
|
+
}, null, 2)
|
|
21695
|
+
}]
|
|
21696
|
+
};
|
|
21697
|
+
}
|
|
21698
|
+
case "workflow": {
|
|
21699
|
+
if (action === "categories") {
|
|
21700
|
+
return {
|
|
21701
|
+
content: [{
|
|
21702
|
+
type: "text",
|
|
21703
|
+
text: JSON.stringify({
|
|
21704
|
+
templateType: "workflow",
|
|
21705
|
+
categories: [...new Set(Object.values(WORKFLOW_TEMPLATES).map((t) => t.category))],
|
|
21706
|
+
totalTemplates: Object.keys(WORKFLOW_TEMPLATES).length
|
|
21707
|
+
}, null, 2)
|
|
21708
|
+
}]
|
|
21709
|
+
};
|
|
21710
|
+
}
|
|
21711
|
+
if (action === "get" && templateId) {
|
|
21712
|
+
const template = WORKFLOW_TEMPLATES[templateId];
|
|
21713
|
+
if (!template) {
|
|
21714
|
+
return {
|
|
21715
|
+
content: [{
|
|
21716
|
+
type: "text",
|
|
21717
|
+
text: JSON.stringify({
|
|
21718
|
+
error: `Workflow template "${templateId}" not found`,
|
|
21719
|
+
availableTemplates: Object.keys(WORKFLOW_TEMPLATES)
|
|
21720
|
+
}, null, 2)
|
|
21721
|
+
}]
|
|
21722
|
+
};
|
|
21723
|
+
}
|
|
21724
|
+
const workflowForUI = {
|
|
21725
|
+
id: template.id,
|
|
21726
|
+
name: template.name,
|
|
21727
|
+
description: template.description,
|
|
21728
|
+
category: template.category,
|
|
21729
|
+
tags: template.tags,
|
|
21730
|
+
canvas: {
|
|
21731
|
+
nodes: template.nodes,
|
|
21732
|
+
edges: template.edges
|
|
21733
|
+
}
|
|
21734
|
+
};
|
|
21735
|
+
return { content: [{ type: "text", text: JSON.stringify(workflowForUI, null, 2) }] };
|
|
21736
|
+
}
|
|
21737
|
+
let templates = Object.values(WORKFLOW_TEMPLATES);
|
|
21738
|
+
if (category) {
|
|
21739
|
+
templates = templates.filter((t) => t.category.toLowerCase() === category.toLowerCase());
|
|
21740
|
+
}
|
|
21741
|
+
const summary = templates.map((t) => formatSummary(t.id, t.name, t.description, t.category, {
|
|
21742
|
+
tags: t.tags,
|
|
21743
|
+
nodesCount: t.nodes.length,
|
|
21744
|
+
edgesCount: t.edges.length
|
|
21745
|
+
}));
|
|
21746
|
+
return {
|
|
21747
|
+
content: [{
|
|
21748
|
+
type: "text",
|
|
21749
|
+
text: JSON.stringify({
|
|
21750
|
+
templateType: "workflow",
|
|
21751
|
+
templates: summary,
|
|
21752
|
+
total: summary.length,
|
|
21753
|
+
categories: [...new Set(Object.values(WORKFLOW_TEMPLATES).map((t) => t.category))]
|
|
21754
|
+
}, null, 2)
|
|
21755
|
+
}]
|
|
21756
|
+
};
|
|
21757
|
+
}
|
|
21758
|
+
case "conversational": {
|
|
21759
|
+
if (action === "categories") {
|
|
21760
|
+
return {
|
|
21761
|
+
content: [{
|
|
21762
|
+
type: "text",
|
|
21763
|
+
text: JSON.stringify({
|
|
21764
|
+
templateType: "conversational",
|
|
21765
|
+
categories: [...new Set(Object.values(CONVERSATIONAL_TEMPLATES).map((t) => t.category))],
|
|
21766
|
+
totalTemplates: Object.keys(CONVERSATIONAL_TEMPLATES).length
|
|
21767
|
+
}, null, 2)
|
|
21768
|
+
}]
|
|
21769
|
+
};
|
|
21770
|
+
}
|
|
21771
|
+
if (action === "get" && templateId) {
|
|
21772
|
+
const template = CONVERSATIONAL_TEMPLATES[templateId];
|
|
21773
|
+
if (!template) {
|
|
21774
|
+
return {
|
|
21775
|
+
content: [{
|
|
21776
|
+
type: "text",
|
|
21777
|
+
text: JSON.stringify({
|
|
21778
|
+
error: `Conversational template "${templateId}" not found`,
|
|
21779
|
+
availableTemplates: Object.keys(CONVERSATIONAL_TEMPLATES)
|
|
21780
|
+
}, null, 2)
|
|
21781
|
+
}]
|
|
21782
|
+
};
|
|
21783
|
+
}
|
|
21784
|
+
return { content: [{ type: "text", text: JSON.stringify(template, null, 2) }] };
|
|
21785
|
+
}
|
|
21786
|
+
let templates = Object.values(CONVERSATIONAL_TEMPLATES);
|
|
21787
|
+
if (category) {
|
|
21788
|
+
templates = templates.filter((t) => t.category.toLowerCase() === category.toLowerCase());
|
|
21789
|
+
}
|
|
21790
|
+
const summary = templates.map((t) => formatSummary(t.id, t.name, t.description, t.category, {
|
|
21791
|
+
tags: t.tags,
|
|
21792
|
+
topicsCount: t.defaultConfig.topics.length,
|
|
21793
|
+
extractionFieldsCount: t.defaultConfig.extractionSchema.length
|
|
21794
|
+
}));
|
|
21795
|
+
return {
|
|
21796
|
+
content: [{
|
|
21797
|
+
type: "text",
|
|
21798
|
+
text: JSON.stringify({
|
|
21799
|
+
templateType: "conversational",
|
|
21800
|
+
templates: summary,
|
|
21801
|
+
total: summary.length,
|
|
21802
|
+
categories: [...new Set(Object.values(CONVERSATIONAL_TEMPLATES).map((t) => t.category))]
|
|
21803
|
+
}, null, 2)
|
|
21804
|
+
}]
|
|
21805
|
+
};
|
|
21806
|
+
}
|
|
21807
|
+
case "query": {
|
|
21808
|
+
if (action === "get" && templateId) {
|
|
21809
|
+
const template = getQueryTemplate(templateId);
|
|
21810
|
+
if (!template) {
|
|
21811
|
+
return {
|
|
21812
|
+
content: [{
|
|
21813
|
+
type: "text",
|
|
21814
|
+
text: JSON.stringify({
|
|
21815
|
+
error: `Query template "${templateId}" not found`,
|
|
21816
|
+
availableTemplates: listQueryTemplates()
|
|
21817
|
+
}, null, 2)
|
|
21818
|
+
}]
|
|
21819
|
+
};
|
|
21820
|
+
}
|
|
21821
|
+
return { content: [{ type: "text", text: JSON.stringify(template, null, 2) }] };
|
|
21822
|
+
}
|
|
21823
|
+
const templates = listQueryTemplates();
|
|
21824
|
+
return {
|
|
21825
|
+
content: [{
|
|
21826
|
+
type: "text",
|
|
21827
|
+
text: JSON.stringify({
|
|
21828
|
+
templateType: "query",
|
|
21829
|
+
templates,
|
|
21830
|
+
total: templates.length
|
|
21831
|
+
}, null, 2)
|
|
21832
|
+
}]
|
|
21833
|
+
};
|
|
21834
|
+
}
|
|
21835
|
+
case "use_case": {
|
|
21836
|
+
if (action === "get" && templateId) {
|
|
21837
|
+
const template = USE_CASE_TEMPLATES[templateId];
|
|
21838
|
+
if (!template) {
|
|
21839
|
+
return {
|
|
21840
|
+
content: [{
|
|
21841
|
+
type: "text",
|
|
21842
|
+
text: JSON.stringify({
|
|
21843
|
+
error: `Use case template "${templateId}" not found`,
|
|
21844
|
+
availableTemplates: Object.keys(USE_CASE_TEMPLATES)
|
|
21845
|
+
}, null, 2)
|
|
21846
|
+
}]
|
|
21847
|
+
};
|
|
21848
|
+
}
|
|
21849
|
+
return { content: [{ type: "text", text: JSON.stringify(template, null, 2) }] };
|
|
21850
|
+
}
|
|
21851
|
+
const useCaseIds = Object.keys(USE_CASE_TEMPLATES);
|
|
21852
|
+
return {
|
|
21853
|
+
content: [{
|
|
21854
|
+
type: "text",
|
|
21855
|
+
text: JSON.stringify({
|
|
21856
|
+
templateType: "use_case",
|
|
21857
|
+
templates: useCaseIds,
|
|
21858
|
+
total: useCaseIds.length,
|
|
21859
|
+
note: 'Use action="get" with templateId to retrieve full template details'
|
|
21860
|
+
}, null, 2)
|
|
21861
|
+
}]
|
|
21862
|
+
};
|
|
21863
|
+
}
|
|
21864
|
+
case "all": {
|
|
21865
|
+
return {
|
|
21866
|
+
content: [{
|
|
21867
|
+
type: "text",
|
|
21868
|
+
text: JSON.stringify({
|
|
21869
|
+
overview: "NetPad Template Catalog",
|
|
21870
|
+
templateTypes: [
|
|
21871
|
+
{ type: "form", count: Object.keys(FORM_TEMPLATES).length, categories: TEMPLATE_CATEGORIES.length, description: "Pre-built form configurations with fields, validation, and styling" },
|
|
21872
|
+
{ type: "application", count: Object.keys(APPLICATION_TEMPLATES).length, description: "Complete applications with forms, workflows, and settings" },
|
|
21873
|
+
{ type: "workflow", count: Object.keys(WORKFLOW_TEMPLATES).length, description: "Automated workflow patterns with triggers, conditions, and actions" },
|
|
21874
|
+
{ type: "conversational", count: Object.keys(CONVERSATIONAL_TEMPLATES).length, description: "AI-powered conversational form templates" },
|
|
21875
|
+
{ type: "query", count: listQueryTemplates().length, description: "MongoDB query patterns for common operations" },
|
|
21876
|
+
{ type: "use_case", count: Object.keys(USE_CASE_TEMPLATES).length, description: "Industry use case blueprints" }
|
|
21877
|
+
],
|
|
21878
|
+
totalTemplates: Object.keys(FORM_TEMPLATES).length + Object.keys(APPLICATION_TEMPLATES).length + Object.keys(WORKFLOW_TEMPLATES).length + Object.keys(CONVERSATIONAL_TEMPLATES).length + listQueryTemplates().length + Object.keys(USE_CASE_TEMPLATES).length,
|
|
21879
|
+
usage: 'Use templateType to filter, action="get" with templateId to get details, or action="categories" to see available categories'
|
|
21880
|
+
}, null, 2)
|
|
21881
|
+
}]
|
|
21882
|
+
};
|
|
21883
|
+
}
|
|
21884
|
+
default:
|
|
21885
|
+
return {
|
|
21886
|
+
content: [{
|
|
21887
|
+
type: "text",
|
|
21888
|
+
text: "Invalid templateType. Use: form, application, workflow, conversational, query, use_case, or all"
|
|
21889
|
+
}]
|
|
21890
|
+
};
|
|
21891
|
+
}
|
|
21892
|
+
}
|
|
21893
|
+
);
|
|
20384
21894
|
server.tool(
|
|
20385
21895
|
"list_field_types",
|
|
20386
|
-
"List all supported field types in @netpad/forms with their descriptions and usage.
|
|
21896
|
+
'[DEPRECATED - use get_reference with type="field_types" instead] List all supported field types in @netpad/forms with their descriptions and usage.',
|
|
20387
21897
|
{
|
|
20388
21898
|
category: external_exports.string().optional().describe('Filter by category (e.g., "text", "selection", "date")')
|
|
20389
21899
|
},
|
|
@@ -20396,7 +21906,9 @@ server.tool(
|
|
|
20396
21906
|
content: [
|
|
20397
21907
|
{
|
|
20398
21908
|
type: "text",
|
|
20399
|
-
text:
|
|
21909
|
+
text: `DEPRECATED: This tool is deprecated. Use get_reference with type="field_types" instead.
|
|
21910
|
+
|
|
21911
|
+
${JSON.stringify(types, null, 2)}`
|
|
20400
21912
|
}
|
|
20401
21913
|
]
|
|
20402
21914
|
};
|
|
@@ -20404,14 +21916,16 @@ server.tool(
|
|
|
20404
21916
|
);
|
|
20405
21917
|
server.tool(
|
|
20406
21918
|
"list_operators",
|
|
20407
|
-
"List all available conditional logic operators with descriptions.
|
|
21919
|
+
'[DEPRECATED - use get_reference with type="operators" instead] List all available conditional logic operators with descriptions.',
|
|
20408
21920
|
{},
|
|
20409
21921
|
async () => {
|
|
20410
21922
|
return {
|
|
20411
21923
|
content: [
|
|
20412
21924
|
{
|
|
20413
21925
|
type: "text",
|
|
20414
|
-
text:
|
|
21926
|
+
text: `DEPRECATED: This tool is deprecated. Use get_reference with type="operators" instead.
|
|
21927
|
+
|
|
21928
|
+
${JSON.stringify(OPERATORS, null, 2)}`
|
|
20415
21929
|
}
|
|
20416
21930
|
]
|
|
20417
21931
|
};
|
|
@@ -20419,7 +21933,7 @@ server.tool(
|
|
|
20419
21933
|
);
|
|
20420
21934
|
server.tool(
|
|
20421
21935
|
"list_formula_functions",
|
|
20422
|
-
"List all available formula functions for computed fields.
|
|
21936
|
+
'[DEPRECATED - use get_reference with type="formula_functions" instead] List all available formula functions for computed fields.',
|
|
20423
21937
|
{
|
|
20424
21938
|
category: external_exports.string().optional().describe('Filter by category (e.g., "math", "string", "date")')
|
|
20425
21939
|
},
|
|
@@ -20432,7 +21946,9 @@ server.tool(
|
|
|
20432
21946
|
content: [
|
|
20433
21947
|
{
|
|
20434
21948
|
type: "text",
|
|
20435
|
-
text:
|
|
21949
|
+
text: `DEPRECATED: This tool is deprecated. Use get_reference with type="formula_functions" instead.
|
|
21950
|
+
|
|
21951
|
+
${JSON.stringify(functions, null, 2)}`
|
|
20436
21952
|
}
|
|
20437
21953
|
]
|
|
20438
21954
|
};
|
|
@@ -20440,14 +21956,16 @@ server.tool(
|
|
|
20440
21956
|
);
|
|
20441
21957
|
server.tool(
|
|
20442
21958
|
"list_validation_options",
|
|
20443
|
-
"List all available validation options for form fields.
|
|
21959
|
+
'[DEPRECATED - use get_reference with type="validation_options" instead] List all available validation options for form fields.',
|
|
20444
21960
|
{},
|
|
20445
21961
|
async () => {
|
|
20446
21962
|
return {
|
|
20447
21963
|
content: [
|
|
20448
21964
|
{
|
|
20449
21965
|
type: "text",
|
|
20450
|
-
text:
|
|
21966
|
+
text: `DEPRECATED: This tool is deprecated. Use get_reference with type="validation_options" instead.
|
|
21967
|
+
|
|
21968
|
+
${JSON.stringify(VALIDATION_OPTIONS, null, 2)}`
|
|
20451
21969
|
}
|
|
20452
21970
|
]
|
|
20453
21971
|
};
|
|
@@ -20455,14 +21973,16 @@ server.tool(
|
|
|
20455
21973
|
);
|
|
20456
21974
|
server.tool(
|
|
20457
21975
|
"list_theme_options",
|
|
20458
|
-
"List all available theme customization options.
|
|
21976
|
+
'[DEPRECATED - use get_reference with type="theme_options" instead] List all available theme customization options.',
|
|
20459
21977
|
{},
|
|
20460
21978
|
async () => {
|
|
20461
21979
|
return {
|
|
20462
21980
|
content: [
|
|
20463
21981
|
{
|
|
20464
21982
|
type: "text",
|
|
20465
|
-
text:
|
|
21983
|
+
text: `DEPRECATED: This tool is deprecated. Use get_reference with type="theme_options" instead.
|
|
21984
|
+
|
|
21985
|
+
${JSON.stringify(THEME_OPTIONS, null, 2)}`
|
|
20466
21986
|
}
|
|
20467
21987
|
]
|
|
20468
21988
|
};
|
|
@@ -20470,7 +21990,7 @@ server.tool(
|
|
|
20470
21990
|
);
|
|
20471
21991
|
server.tool(
|
|
20472
21992
|
"get_documentation",
|
|
20473
|
-
"Get NetPad forms documentation. Use this to learn about features, APIs, and best practices.
|
|
21993
|
+
'[DEPRECATED - use get_reference with type="documentation" instead] Get NetPad forms documentation. Use this to learn about features, APIs, and best practices.',
|
|
20474
21994
|
{
|
|
20475
21995
|
topic: external_exports.enum(["readme", "architecture", "quick-start", "examples", "api-client"]).describe("The documentation topic to retrieve")
|
|
20476
21996
|
},
|
|
@@ -20486,7 +22006,9 @@ server.tool(
|
|
|
20486
22006
|
content: [
|
|
20487
22007
|
{
|
|
20488
22008
|
type: "text",
|
|
20489
|
-
text:
|
|
22009
|
+
text: `DEPRECATED: This tool is deprecated. Use get_reference with type="documentation" and topic="${topic}" instead.
|
|
22010
|
+
|
|
22011
|
+
${docs[topic] || "Documentation not found"}`
|
|
20490
22012
|
}
|
|
20491
22013
|
]
|
|
20492
22014
|
};
|
|
@@ -20494,73 +22016,404 @@ server.tool(
|
|
|
20494
22016
|
);
|
|
20495
22017
|
server.tool(
|
|
20496
22018
|
"generate_react_code",
|
|
20497
|
-
"Generate React component
|
|
22019
|
+
"Generate a complete, self-contained React component with inline types and fetch-based API calls. No external @netpad/* imports required - ready to copy-paste and use.",
|
|
20498
22020
|
{
|
|
20499
22021
|
formConfig: external_exports.string().describe("The form configuration JSON"),
|
|
20500
22022
|
componentName: external_exports.string().optional().describe("Name of the React component"),
|
|
20501
22023
|
includeSubmitHandler: external_exports.boolean().optional().describe("Whether to include a submit handler"),
|
|
20502
|
-
|
|
22024
|
+
includeApiSubmission: external_exports.boolean().optional().describe("Whether to submit to NetPad API via fetch (default: true)")
|
|
20503
22025
|
},
|
|
20504
|
-
async ({ formConfig, componentName = "MyForm", includeSubmitHandler = true,
|
|
20505
|
-
let
|
|
20506
|
-
|
|
20507
|
-
|
|
20508
|
-
|
|
20509
|
-
|
|
22026
|
+
async ({ formConfig, componentName = "MyForm", includeSubmitHandler = true, includeApiSubmission = true }) => {
|
|
22027
|
+
let parsedConfig;
|
|
22028
|
+
try {
|
|
22029
|
+
parsedConfig = JSON.parse(formConfig);
|
|
22030
|
+
} catch {
|
|
22031
|
+
parsedConfig = { name: componentName, slug: componentName.toLowerCase().replace(/\s+/g, "-") };
|
|
22032
|
+
}
|
|
22033
|
+
const formSlug = parsedConfig.slug || parsedConfig.name?.toLowerCase().replace(/\s+/g, "-") || "form";
|
|
22034
|
+
const code = `/**
|
|
22035
|
+
* ${componentName} - React Form Component
|
|
22036
|
+
* Generated by NetPad
|
|
22037
|
+
*
|
|
22038
|
+
* This is a self-contained component with inline types.
|
|
22039
|
+
* No @netpad/* imports required.
|
|
22040
|
+
*/
|
|
22041
|
+
|
|
22042
|
+
'use client';
|
|
22043
|
+
|
|
22044
|
+
import React, { useState, FormEvent, ChangeEvent } from 'react';
|
|
22045
|
+
|
|
22046
|
+
// ============================================================================
|
|
22047
|
+
// Types (inline - no external dependencies)
|
|
22048
|
+
// ============================================================================
|
|
22049
|
+
|
|
22050
|
+
interface FormFieldOption {
|
|
22051
|
+
label: string;
|
|
22052
|
+
value: string;
|
|
22053
|
+
}
|
|
22054
|
+
|
|
22055
|
+
interface FormFieldValidation {
|
|
22056
|
+
min?: number;
|
|
22057
|
+
max?: number;
|
|
22058
|
+
minLength?: number;
|
|
22059
|
+
maxLength?: number;
|
|
22060
|
+
pattern?: string;
|
|
22061
|
+
errorMessage?: string;
|
|
22062
|
+
}
|
|
22063
|
+
|
|
22064
|
+
interface FormField {
|
|
22065
|
+
path: string;
|
|
22066
|
+
label: string;
|
|
22067
|
+
type: string;
|
|
22068
|
+
included?: boolean;
|
|
22069
|
+
required?: boolean;
|
|
22070
|
+
placeholder?: string;
|
|
22071
|
+
helpText?: string;
|
|
22072
|
+
options?: FormFieldOption[];
|
|
22073
|
+
validation?: FormFieldValidation;
|
|
22074
|
+
fieldWidth?: 'full' | 'half' | 'third' | 'quarter';
|
|
22075
|
+
}
|
|
22076
|
+
|
|
22077
|
+
interface FormConfig {
|
|
22078
|
+
name: string;
|
|
22079
|
+
slug?: string;
|
|
22080
|
+
description?: string;
|
|
22081
|
+
fieldConfigs: FormField[];
|
|
22082
|
+
submitButtonText?: string;
|
|
22083
|
+
successMessage?: string;
|
|
22084
|
+
}
|
|
22085
|
+
|
|
22086
|
+
interface SubmitResult {
|
|
22087
|
+
success: boolean;
|
|
22088
|
+
submissionId?: string;
|
|
22089
|
+
error?: string;
|
|
22090
|
+
}
|
|
22091
|
+
|
|
22092
|
+
// ============================================================================
|
|
22093
|
+
// Configuration
|
|
22094
|
+
// ============================================================================
|
|
22095
|
+
|
|
22096
|
+
const CONFIG = {
|
|
22097
|
+
baseUrl: process.env.NEXT_PUBLIC_NETPAD_URL ?? 'https://api.netpad.io',
|
|
22098
|
+
apiKey: process.env.NEXT_PUBLIC_NETPAD_API_KEY ?? '',
|
|
22099
|
+
};
|
|
22100
|
+
|
|
22101
|
+
const formConfig: FormConfig = ${formConfig};
|
|
22102
|
+
|
|
22103
|
+
// ============================================================================
|
|
22104
|
+
// API Functions (using fetch - no SDK required)
|
|
22105
|
+
// ============================================================================
|
|
22106
|
+
|
|
22107
|
+
${includeApiSubmission ? `async function submitToApi(data: Record<string, unknown>): Promise<SubmitResult> {
|
|
22108
|
+
try {
|
|
22109
|
+
const response = await fetch(
|
|
22110
|
+
\`\${CONFIG.baseUrl}/api/forms/${formSlug}/submit\`,
|
|
22111
|
+
{
|
|
22112
|
+
method: 'POST',
|
|
22113
|
+
headers: {
|
|
22114
|
+
'Content-Type': 'application/json',
|
|
22115
|
+
'Authorization': \`Bearer \${CONFIG.apiKey}\`,
|
|
22116
|
+
},
|
|
22117
|
+
body: JSON.stringify({ data }),
|
|
22118
|
+
}
|
|
22119
|
+
);
|
|
22120
|
+
|
|
22121
|
+
if (!response.ok) {
|
|
22122
|
+
const errorText = await response.text();
|
|
22123
|
+
return { success: false, error: errorText };
|
|
20510
22124
|
}
|
|
20511
|
-
code += `import type { FormConfiguration } from '@netpad/forms';
|
|
20512
22125
|
|
|
20513
|
-
|
|
20514
|
-
|
|
22126
|
+
const result = await response.json() as { submissionId: string };
|
|
22127
|
+
return { success: true, submissionId: result.submissionId };
|
|
22128
|
+
} catch (error) {
|
|
22129
|
+
return {
|
|
22130
|
+
success: false,
|
|
22131
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
22132
|
+
};
|
|
22133
|
+
}
|
|
22134
|
+
}` : ""}
|
|
22135
|
+
|
|
22136
|
+
// ============================================================================
|
|
22137
|
+
// Component
|
|
22138
|
+
// ============================================================================
|
|
22139
|
+
|
|
22140
|
+
export function ${componentName}() {
|
|
22141
|
+
const [formData, setFormData] = useState<Record<string, string>>({});
|
|
22142
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
22143
|
+
const [submitResult, setSubmitResult] = useState<SubmitResult | null>(null);
|
|
22144
|
+
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
22145
|
+
|
|
22146
|
+
const handleChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
|
22147
|
+
const { name, value } = e.target;
|
|
22148
|
+
setFormData(prev => ({ ...prev, [name]: value }));
|
|
22149
|
+
// Clear error when field is edited
|
|
22150
|
+
if (errors[name]) {
|
|
22151
|
+
setErrors(prev => {
|
|
22152
|
+
const newErrors = { ...prev };
|
|
22153
|
+
delete newErrors[name];
|
|
22154
|
+
return newErrors;
|
|
22155
|
+
});
|
|
22156
|
+
}
|
|
22157
|
+
};
|
|
20515
22158
|
|
|
20516
|
-
|
|
20517
|
-
|
|
20518
|
-
code += `const client = createNetPadClient({
|
|
20519
|
-
baseUrl: process.env.NEXT_PUBLIC_NETPAD_URL || 'https://your-netpad-instance.com',
|
|
20520
|
-
apiKey: process.env.NETPAD_API_KEY || '',
|
|
20521
|
-
});
|
|
22159
|
+
const validateForm = (): boolean => {
|
|
22160
|
+
const newErrors: Record<string, string> = {};
|
|
20522
22161
|
|
|
20523
|
-
|
|
22162
|
+
for (const field of formConfig.fieldConfigs) {
|
|
22163
|
+
if (!field.included) continue;
|
|
22164
|
+
|
|
22165
|
+
const value = formData[field.path] || '';
|
|
22166
|
+
|
|
22167
|
+
if (field.required && !value) {
|
|
22168
|
+
newErrors[field.path] = \`\${field.label} is required\`;
|
|
22169
|
+
}
|
|
22170
|
+
|
|
22171
|
+
if (field.validation) {
|
|
22172
|
+
if (field.validation.minLength && value.length < field.validation.minLength) {
|
|
22173
|
+
newErrors[field.path] = field.validation.errorMessage || \`Minimum \${field.validation.minLength} characters required\`;
|
|
22174
|
+
}
|
|
22175
|
+
if (field.validation.maxLength && value.length > field.validation.maxLength) {
|
|
22176
|
+
newErrors[field.path] = field.validation.errorMessage || \`Maximum \${field.validation.maxLength} characters allowed\`;
|
|
22177
|
+
}
|
|
22178
|
+
if (field.validation.pattern && !new RegExp(field.validation.pattern).test(value)) {
|
|
22179
|
+
newErrors[field.path] = field.validation.errorMessage || 'Invalid format';
|
|
22180
|
+
}
|
|
22181
|
+
}
|
|
20524
22182
|
}
|
|
20525
|
-
|
|
20526
|
-
|
|
20527
|
-
|
|
20528
|
-
|
|
20529
|
-
|
|
22183
|
+
|
|
22184
|
+
setErrors(newErrors);
|
|
22185
|
+
return Object.keys(newErrors).length === 0;
|
|
22186
|
+
};
|
|
22187
|
+
|
|
22188
|
+
${includeSubmitHandler ? ` const handleSubmit = async (e: FormEvent) => {
|
|
22189
|
+
e.preventDefault();
|
|
22190
|
+
|
|
22191
|
+
if (!validateForm()) return;
|
|
22192
|
+
|
|
22193
|
+
setIsSubmitting(true);
|
|
22194
|
+
setSubmitResult(null);
|
|
22195
|
+
|
|
20530
22196
|
try {
|
|
20531
|
-
const result = await
|
|
20532
|
-
|
|
20533
|
-
|
|
22197
|
+
${includeApiSubmission ? ` const result = await submitToApi(formData);
|
|
22198
|
+
setSubmitResult(result);` : ` // TODO: Implement your submission logic
|
|
22199
|
+
console.log('Form data:', formData);
|
|
22200
|
+
setSubmitResult({ success: true });`}
|
|
20534
22201
|
} catch (error) {
|
|
20535
|
-
|
|
20536
|
-
|
|
22202
|
+
setSubmitResult({
|
|
22203
|
+
success: false,
|
|
22204
|
+
error: error instanceof Error ? error.message : 'Submission failed',
|
|
22205
|
+
});
|
|
22206
|
+
} finally {
|
|
22207
|
+
setIsSubmitting(false);
|
|
20537
22208
|
}
|
|
20538
|
-
}
|
|
22209
|
+
};` : ""}
|
|
20539
22210
|
|
|
20540
|
-
|
|
20541
|
-
|
|
20542
|
-
|
|
20543
|
-
|
|
20544
|
-
|
|
22211
|
+
const renderField = (field: FormField) => {
|
|
22212
|
+
if (!field.included) return null;
|
|
22213
|
+
|
|
22214
|
+
const commonProps = {
|
|
22215
|
+
id: field.path,
|
|
22216
|
+
name: field.path,
|
|
22217
|
+
value: formData[field.path] || '',
|
|
22218
|
+
onChange: handleChange,
|
|
22219
|
+
required: field.required,
|
|
22220
|
+
placeholder: field.placeholder,
|
|
22221
|
+
disabled: isSubmitting,
|
|
22222
|
+
className: \`form-field \${errors[field.path] ? 'error' : ''}\`,
|
|
22223
|
+
};
|
|
22224
|
+
|
|
22225
|
+
let input;
|
|
22226
|
+
|
|
22227
|
+
switch (field.type) {
|
|
22228
|
+
case 'long_text':
|
|
22229
|
+
input = <textarea {...commonProps} rows={4} />;
|
|
22230
|
+
break;
|
|
22231
|
+
case 'email':
|
|
22232
|
+
input = <input {...commonProps} type="email" />;
|
|
22233
|
+
break;
|
|
22234
|
+
case 'number':
|
|
22235
|
+
input = <input {...commonProps} type="number" />;
|
|
22236
|
+
break;
|
|
22237
|
+
case 'phone':
|
|
22238
|
+
input = <input {...commonProps} type="tel" />;
|
|
22239
|
+
break;
|
|
22240
|
+
case 'date':
|
|
22241
|
+
input = <input {...commonProps} type="date" />;
|
|
22242
|
+
break;
|
|
22243
|
+
case 'dropdown':
|
|
22244
|
+
case 'select':
|
|
22245
|
+
input = (
|
|
22246
|
+
<select {...commonProps}>
|
|
22247
|
+
<option value="">Select...</option>
|
|
22248
|
+
{field.options?.map(opt => (
|
|
22249
|
+
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
|
22250
|
+
))}
|
|
22251
|
+
</select>
|
|
22252
|
+
);
|
|
22253
|
+
break;
|
|
22254
|
+
default:
|
|
22255
|
+
input = <input {...commonProps} type="text" />;
|
|
22256
|
+
}
|
|
22257
|
+
|
|
22258
|
+
return (
|
|
22259
|
+
<div key={field.path} className={\`form-group field-width-\${field.fieldWidth || 'full'}\`}>
|
|
22260
|
+
<label htmlFor={field.path}>
|
|
22261
|
+
{field.label}
|
|
22262
|
+
{field.required && <span className="required">*</span>}
|
|
22263
|
+
</label>
|
|
22264
|
+
{input}
|
|
22265
|
+
{field.helpText && <small className="help-text">{field.helpText}</small>}
|
|
22266
|
+
{errors[field.path] && <span className="error-message">{errors[field.path]}</span>}
|
|
22267
|
+
</div>
|
|
22268
|
+
);
|
|
20545
22269
|
};
|
|
20546
22270
|
|
|
20547
|
-
|
|
20548
|
-
|
|
20549
|
-
|
|
20550
|
-
|
|
20551
|
-
|
|
20552
|
-
|
|
20553
|
-
|
|
20554
|
-
|
|
20555
|
-
|
|
22271
|
+
if (submitResult?.success) {
|
|
22272
|
+
return (
|
|
22273
|
+
<div className="form-success">
|
|
22274
|
+
<h3>\u2713 {formConfig.successMessage || 'Thank you for your submission!'}</h3>
|
|
22275
|
+
{submitResult.submissionId && (
|
|
22276
|
+
<p>Confirmation ID: {submitResult.submissionId}</p>
|
|
22277
|
+
)}
|
|
22278
|
+
<button onClick={() => {
|
|
22279
|
+
setSubmitResult(null);
|
|
22280
|
+
setFormData({});
|
|
22281
|
+
}}>
|
|
22282
|
+
Submit Another
|
|
22283
|
+
</button>
|
|
22284
|
+
</div>
|
|
22285
|
+
);
|
|
22286
|
+
}
|
|
22287
|
+
|
|
22288
|
+
return (
|
|
22289
|
+
<div className="form-container">
|
|
22290
|
+
<h2>{formConfig.name}</h2>
|
|
22291
|
+
{formConfig.description && <p className="form-description">{formConfig.description}</p>}
|
|
22292
|
+
|
|
22293
|
+
<form onSubmit={${includeSubmitHandler ? "handleSubmit" : "(e) => e.preventDefault()"}}>
|
|
22294
|
+
{formConfig.fieldConfigs.map(renderField)}
|
|
22295
|
+
|
|
22296
|
+
{submitResult?.error && (
|
|
22297
|
+
<div className="form-error">
|
|
22298
|
+
<p>Error: {submitResult.error}</p>
|
|
22299
|
+
</div>
|
|
22300
|
+
)}
|
|
22301
|
+
|
|
22302
|
+
<button type="submit" disabled={isSubmitting} className="submit-button">
|
|
22303
|
+
{isSubmitting ? 'Submitting...' : (formConfig.submitButtonText || 'Submit')}
|
|
22304
|
+
</button>
|
|
22305
|
+
</form>
|
|
22306
|
+
|
|
22307
|
+
<style>{\`
|
|
22308
|
+
.form-container {
|
|
22309
|
+
max-width: 600px;
|
|
22310
|
+
margin: 0 auto;
|
|
22311
|
+
padding: 20px;
|
|
22312
|
+
}
|
|
22313
|
+
.form-group {
|
|
22314
|
+
margin-bottom: 16px;
|
|
22315
|
+
}
|
|
22316
|
+
.form-group label {
|
|
22317
|
+
display: block;
|
|
22318
|
+
margin-bottom: 4px;
|
|
22319
|
+
font-weight: 500;
|
|
22320
|
+
}
|
|
22321
|
+
.form-group input,
|
|
22322
|
+
.form-group textarea,
|
|
22323
|
+
.form-group select {
|
|
22324
|
+
width: 100%;
|
|
22325
|
+
padding: 8px 12px;
|
|
22326
|
+
border: 1px solid #ddd;
|
|
22327
|
+
border-radius: 4px;
|
|
22328
|
+
font-size: 14px;
|
|
22329
|
+
}
|
|
22330
|
+
.form-group input:focus,
|
|
22331
|
+
.form-group textarea:focus,
|
|
22332
|
+
.form-group select:focus {
|
|
22333
|
+
outline: none;
|
|
22334
|
+
border-color: #1976d2;
|
|
22335
|
+
}
|
|
22336
|
+
.form-group .error {
|
|
22337
|
+
border-color: #d32f2f;
|
|
22338
|
+
}
|
|
22339
|
+
.required {
|
|
22340
|
+
color: #d32f2f;
|
|
22341
|
+
margin-left: 4px;
|
|
22342
|
+
}
|
|
22343
|
+
.help-text {
|
|
22344
|
+
display: block;
|
|
22345
|
+
color: #666;
|
|
22346
|
+
margin-top: 4px;
|
|
22347
|
+
}
|
|
22348
|
+
.error-message {
|
|
22349
|
+
display: block;
|
|
22350
|
+
color: #d32f2f;
|
|
22351
|
+
font-size: 12px;
|
|
22352
|
+
margin-top: 4px;
|
|
22353
|
+
}
|
|
22354
|
+
.submit-button {
|
|
22355
|
+
background: #1976d2;
|
|
22356
|
+
color: white;
|
|
22357
|
+
padding: 12px 24px;
|
|
22358
|
+
border: none;
|
|
22359
|
+
border-radius: 4px;
|
|
22360
|
+
cursor: pointer;
|
|
22361
|
+
font-size: 16px;
|
|
22362
|
+
}
|
|
22363
|
+
.submit-button:hover {
|
|
22364
|
+
background: #1565c0;
|
|
22365
|
+
}
|
|
22366
|
+
.submit-button:disabled {
|
|
22367
|
+
background: #ccc;
|
|
22368
|
+
cursor: not-allowed;
|
|
22369
|
+
}
|
|
22370
|
+
.form-success {
|
|
22371
|
+
text-align: center;
|
|
22372
|
+
padding: 40px;
|
|
22373
|
+
}
|
|
22374
|
+
.form-error {
|
|
22375
|
+
background: #ffebee;
|
|
22376
|
+
color: #c62828;
|
|
22377
|
+
padding: 12px;
|
|
22378
|
+
border-radius: 4px;
|
|
22379
|
+
margin-bottom: 16px;
|
|
22380
|
+
}
|
|
22381
|
+
.field-width-half {
|
|
22382
|
+
display: inline-block;
|
|
22383
|
+
width: calc(50% - 8px);
|
|
22384
|
+
margin-right: 8px;
|
|
22385
|
+
}
|
|
22386
|
+
.field-width-third {
|
|
22387
|
+
display: inline-block;
|
|
22388
|
+
width: calc(33.33% - 8px);
|
|
22389
|
+
margin-right: 8px;
|
|
22390
|
+
}
|
|
22391
|
+
.field-width-quarter {
|
|
22392
|
+
display: inline-block;
|
|
22393
|
+
width: calc(25% - 8px);
|
|
22394
|
+
margin-right: 8px;
|
|
22395
|
+
}
|
|
22396
|
+
\`}</style>
|
|
22397
|
+
</div>
|
|
20556
22398
|
);
|
|
20557
22399
|
}
|
|
22400
|
+
|
|
22401
|
+
export default ${componentName};
|
|
20558
22402
|
`;
|
|
22403
|
+
const output = createToolOutput({
|
|
22404
|
+
code,
|
|
22405
|
+
filename: `${componentName}.tsx`,
|
|
22406
|
+
envVars: [
|
|
22407
|
+
{ name: "NEXT_PUBLIC_NETPAD_URL", description: "NetPad API URL (client-side accessible)", example: "https://api.netpad.io" },
|
|
22408
|
+
{ name: "NEXT_PUBLIC_NETPAD_API_KEY", description: "NetPad API key (client-side accessible)", example: "np_live_xxxxx" }
|
|
22409
|
+
],
|
|
22410
|
+
dependencies: ["react"]
|
|
22411
|
+
});
|
|
20559
22412
|
return {
|
|
20560
22413
|
content: [
|
|
20561
22414
|
{
|
|
20562
22415
|
type: "text",
|
|
20563
|
-
text:
|
|
22416
|
+
text: formatToolOutput(output)
|
|
20564
22417
|
}
|
|
20565
22418
|
]
|
|
20566
22419
|
};
|
|
@@ -20661,7 +22514,7 @@ server.tool(
|
|
|
20661
22514
|
);
|
|
20662
22515
|
server.tool(
|
|
20663
22516
|
"get_use_case_template",
|
|
20664
|
-
"Get a pre-built template for common form use cases including form configuration and workflow setup.
|
|
22517
|
+
'[DEPRECATED - use browse_templates with templateType="use_case" and action="get"] Get a pre-built template for common form use cases including form configuration and workflow setup.',
|
|
20665
22518
|
{
|
|
20666
22519
|
useCase: external_exports.enum(["leadCapture", "eventRegistration", "feedbackSurvey"]).describe("The use case template to retrieve")
|
|
20667
22520
|
},
|
|
@@ -21050,7 +22903,7 @@ ${context ? `
|
|
|
21050
22903
|
}
|
|
21051
22904
|
server.tool(
|
|
21052
22905
|
"list_application_templates",
|
|
21053
|
-
"List all available application templates for creating new NetPad applications. Templates include pre-configured forms, workflows, and settings.
|
|
22906
|
+
'[DEPRECATED - use browse_templates with templateType="application"] List all available application templates for creating new NetPad applications. Templates include pre-configured forms, workflows, and settings.',
|
|
21054
22907
|
{
|
|
21055
22908
|
category: external_exports.string().optional().describe('Filter by category (e.g., "lead-generation", "events", "surveys", "hr", "ecommerce")')
|
|
21056
22909
|
},
|
|
@@ -21082,7 +22935,7 @@ server.tool(
|
|
|
21082
22935
|
);
|
|
21083
22936
|
server.tool(
|
|
21084
22937
|
"get_application_template",
|
|
21085
|
-
"Get detailed information about a specific application template including its forms, workflows, and field configurations.
|
|
22938
|
+
'[DEPRECATED - use browse_templates with templateType="application" and action="get"] Get detailed information about a specific application template including its forms, workflows, and field configurations.',
|
|
21086
22939
|
{
|
|
21087
22940
|
templateId: external_exports.enum(["contact-form", "lead-capture", "event-registration", "feedback-survey", "job-application", "order-form", "blank"]).describe("The template ID")
|
|
21088
22941
|
},
|
|
@@ -21106,7 +22959,7 @@ server.tool(
|
|
|
21106
22959
|
);
|
|
21107
22960
|
server.tool(
|
|
21108
22961
|
"create_application",
|
|
21109
|
-
"Generate
|
|
22962
|
+
"Generate a single, complete TypeScript file that creates a NetPad application with all forms and workflows. Run with `npx tsx` - no SDK required.",
|
|
21110
22963
|
{
|
|
21111
22964
|
name: external_exports.string().describe("Name of the application"),
|
|
21112
22965
|
description: external_exports.string().optional().describe("Description of the application"),
|
|
@@ -21119,20 +22972,264 @@ server.tool(
|
|
|
21119
22972
|
organizationId: external_exports.string().describe("Organization ID")
|
|
21120
22973
|
},
|
|
21121
22974
|
async (options) => {
|
|
21122
|
-
const
|
|
21123
|
-
const
|
|
21124
|
-
|
|
21125
|
-
|
|
21126
|
-
|
|
21127
|
-
|
|
22975
|
+
const { name, description, templateId, icon, color, tags, projectId, organizationId } = options;
|
|
22976
|
+
const slug = options.slug || name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
22977
|
+
const template = templateId ? APPLICATION_TEMPLATES[templateId] : null;
|
|
22978
|
+
const formConfigs = template?.structure.forms.map((form) => ({
|
|
22979
|
+
name: form.name,
|
|
22980
|
+
slug: form.slug,
|
|
22981
|
+
fieldConfigs: form.fields.map((f) => ({ ...f, included: true })),
|
|
22982
|
+
submitButtonText: "Submit",
|
|
22983
|
+
successMessage: "Thank you for your submission!"
|
|
22984
|
+
})) || [];
|
|
22985
|
+
const workflowConfigs = template?.structure.workflows.map((workflow) => ({
|
|
22986
|
+
name: workflow.name,
|
|
22987
|
+
description: `Triggered on ${workflow.trigger}`,
|
|
22988
|
+
nodes: [
|
|
22989
|
+
{
|
|
22990
|
+
id: "trigger_1",
|
|
22991
|
+
type: workflow.trigger === "form_submission" ? "form-trigger" : "manual-trigger",
|
|
22992
|
+
label: "Trigger",
|
|
22993
|
+
position: { x: 100, y: 200 },
|
|
22994
|
+
config: { formSlug: formConfigs[0]?.slug || "" },
|
|
22995
|
+
enabled: true
|
|
22996
|
+
},
|
|
22997
|
+
...workflow.steps.map((step, stepIdx) => ({
|
|
22998
|
+
id: `step_${stepIdx + 1}`,
|
|
22999
|
+
type: step.includes("email") ? "email-send" : step.includes("database") ? "mongodb-write" : "transform",
|
|
23000
|
+
label: step.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
23001
|
+
position: { x: 100 + (stepIdx + 1) * 250, y: 200 },
|
|
23002
|
+
config: {},
|
|
23003
|
+
enabled: true
|
|
23004
|
+
}))
|
|
23005
|
+
],
|
|
23006
|
+
edges: [
|
|
23007
|
+
{ id: "edge_1", source: "trigger_1", sourceHandle: "form_data", target: "step_1", targetHandle: "input" }
|
|
23008
|
+
]
|
|
23009
|
+
})) || [];
|
|
23010
|
+
const configCode = `// Application Configuration
|
|
23011
|
+
const APPLICATION_CONFIG = {
|
|
23012
|
+
name: ${JSON.stringify(name)},
|
|
23013
|
+
description: ${JSON.stringify(description || `Application created from ${templateId || "scratch"}`)},
|
|
23014
|
+
slug: ${JSON.stringify(slug)},
|
|
23015
|
+
icon: ${JSON.stringify(icon || "\u{1F4CB}")},
|
|
23016
|
+
color: ${JSON.stringify(color || "#00ED64")},
|
|
23017
|
+
tags: ${JSON.stringify(tags || [])},
|
|
23018
|
+
projectId: CONFIG.projectId,
|
|
23019
|
+
organizationId: CONFIG.organizationId,
|
|
23020
|
+
};
|
|
21128
23021
|
|
|
21129
|
-
|
|
23022
|
+
// Form Configurations
|
|
23023
|
+
const FORM_CONFIGS: FormConfig[] = ${JSON.stringify(formConfigs, null, 2)};
|
|
21130
23024
|
|
|
21131
|
-
|
|
23025
|
+
// Workflow Configurations
|
|
23026
|
+
const WORKFLOW_CONFIGS: WorkflowConfig[] = ${JSON.stringify(workflowConfigs, null, 2)};`;
|
|
23027
|
+
const functionsCode = `// ============================================================================
|
|
23028
|
+
// API Helper Functions
|
|
23029
|
+
// ============================================================================
|
|
21132
23030
|
|
|
21133
|
-
|
|
21134
|
-
|
|
21135
|
-
|
|
23031
|
+
async function apiCall<T>(
|
|
23032
|
+
endpoint: string,
|
|
23033
|
+
options: RequestInit = {}
|
|
23034
|
+
): Promise<{ success: boolean; data?: T; error?: string }> {
|
|
23035
|
+
try {
|
|
23036
|
+
const response = await fetch(\`\${CONFIG.baseUrl}\${endpoint}\`, {
|
|
23037
|
+
...options,
|
|
23038
|
+
headers: {
|
|
23039
|
+
'Content-Type': 'application/json',
|
|
23040
|
+
'Authorization': \`Bearer \${CONFIG.apiKey}\`,
|
|
23041
|
+
...options.headers,
|
|
23042
|
+
},
|
|
23043
|
+
});
|
|
23044
|
+
|
|
23045
|
+
if (!response.ok) {
|
|
23046
|
+
const error = await response.text();
|
|
23047
|
+
return { success: false, error };
|
|
23048
|
+
}
|
|
23049
|
+
|
|
23050
|
+
const data = await response.json() as T;
|
|
23051
|
+
return { success: true, data };
|
|
23052
|
+
} catch (error) {
|
|
23053
|
+
return { success: false, error: error instanceof Error ? error.message : 'Unknown error' };
|
|
23054
|
+
}
|
|
23055
|
+
}
|
|
23056
|
+
|
|
23057
|
+
// ============================================================================
|
|
23058
|
+
// Application Setup Functions
|
|
23059
|
+
// ============================================================================
|
|
23060
|
+
|
|
23061
|
+
async function createApplication(): Promise<string | null> {
|
|
23062
|
+
console.log('\u{1F4F1} Creating application:', APPLICATION_CONFIG.name);
|
|
23063
|
+
|
|
23064
|
+
const result = await apiCall<{ application: { applicationId: string } }>('/api/applications', {
|
|
23065
|
+
method: 'POST',
|
|
23066
|
+
body: JSON.stringify(APPLICATION_CONFIG),
|
|
23067
|
+
});
|
|
23068
|
+
|
|
23069
|
+
if (!result.success || !result.data) {
|
|
23070
|
+
console.error('\u274C Failed to create application:', result.error);
|
|
23071
|
+
return null;
|
|
23072
|
+
}
|
|
23073
|
+
|
|
23074
|
+
console.log('\u2705 Application created:', result.data.application.applicationId);
|
|
23075
|
+
return result.data.application.applicationId;
|
|
23076
|
+
}
|
|
23077
|
+
|
|
23078
|
+
async function createForms(applicationId: string): Promise<string[]> {
|
|
23079
|
+
const formIds: string[] = [];
|
|
23080
|
+
|
|
23081
|
+
for (const formConfig of FORM_CONFIGS) {
|
|
23082
|
+
console.log('\u{1F4DD} Creating form:', formConfig.name);
|
|
23083
|
+
|
|
23084
|
+
const result = await apiCall<{ form: { formId: string } }>('/api/forms', {
|
|
23085
|
+
method: 'POST',
|
|
23086
|
+
body: JSON.stringify({
|
|
23087
|
+
...formConfig,
|
|
23088
|
+
applicationId,
|
|
23089
|
+
projectId: CONFIG.projectId,
|
|
23090
|
+
organizationId: CONFIG.organizationId,
|
|
23091
|
+
}),
|
|
23092
|
+
});
|
|
23093
|
+
|
|
23094
|
+
if (result.success && result.data) {
|
|
23095
|
+
console.log('\u2705 Form created:', result.data.form.formId);
|
|
23096
|
+
formIds.push(result.data.form.formId);
|
|
23097
|
+
} else {
|
|
23098
|
+
console.error('\u274C Failed to create form:', formConfig.name, result.error);
|
|
23099
|
+
}
|
|
23100
|
+
}
|
|
23101
|
+
|
|
23102
|
+
return formIds;
|
|
23103
|
+
}
|
|
23104
|
+
|
|
23105
|
+
async function createWorkflows(applicationId: string, formIds: string[]): Promise<string[]> {
|
|
23106
|
+
const workflowIds: string[] = [];
|
|
23107
|
+
|
|
23108
|
+
for (const workflowConfig of WORKFLOW_CONFIGS) {
|
|
23109
|
+
console.log('\u26A1 Creating workflow:', workflowConfig.name);
|
|
23110
|
+
|
|
23111
|
+
// Update trigger with actual form ID if available
|
|
23112
|
+
const updatedNodes = workflowConfig.nodes.map(node => {
|
|
23113
|
+
if (node.type === 'form-trigger' && formIds.length > 0) {
|
|
23114
|
+
return { ...node, config: { ...node.config, formId: formIds[0] } };
|
|
23115
|
+
}
|
|
23116
|
+
return node;
|
|
23117
|
+
});
|
|
23118
|
+
|
|
23119
|
+
const result = await apiCall<{ workflow: { id: string } }>('/api/workflows', {
|
|
23120
|
+
method: 'POST',
|
|
23121
|
+
body: JSON.stringify({
|
|
23122
|
+
...workflowConfig,
|
|
23123
|
+
nodes: updatedNodes,
|
|
23124
|
+
applicationId,
|
|
23125
|
+
projectId: CONFIG.projectId,
|
|
23126
|
+
organizationId: CONFIG.organizationId,
|
|
23127
|
+
}),
|
|
23128
|
+
});
|
|
23129
|
+
|
|
23130
|
+
if (result.success && result.data) {
|
|
23131
|
+
console.log('\u2705 Workflow created:', result.data.workflow.id);
|
|
23132
|
+
workflowIds.push(result.data.workflow.id);
|
|
23133
|
+
} else {
|
|
23134
|
+
console.error('\u274C Failed to create workflow:', workflowConfig.name, result.error);
|
|
23135
|
+
}
|
|
23136
|
+
}
|
|
23137
|
+
|
|
23138
|
+
return workflowIds;
|
|
23139
|
+
}
|
|
23140
|
+
|
|
23141
|
+
async function activateWorkflows(workflowIds: string[]): Promise<void> {
|
|
23142
|
+
for (const workflowId of workflowIds) {
|
|
23143
|
+
console.log('\u{1F504} Activating workflow:', workflowId);
|
|
23144
|
+
|
|
23145
|
+
const result = await apiCall(\`/api/workflows/\${workflowId}/activate\`, {
|
|
23146
|
+
method: 'POST',
|
|
23147
|
+
});
|
|
23148
|
+
|
|
23149
|
+
if (result.success) {
|
|
23150
|
+
console.log('\u2705 Workflow activated');
|
|
23151
|
+
} else {
|
|
23152
|
+
console.warn('\u26A0\uFE0F Failed to activate workflow:', result.error);
|
|
23153
|
+
}
|
|
23154
|
+
}
|
|
23155
|
+
}`;
|
|
23156
|
+
const mainCode = `// ============================================================================
|
|
23157
|
+
// Main Setup Script
|
|
23158
|
+
// ============================================================================
|
|
23159
|
+
|
|
23160
|
+
async function setup() {
|
|
23161
|
+
console.log('\\n\u{1F680} Setting up ${name}...\\n');
|
|
23162
|
+
console.log('Template: ${templateId || "blank"}');
|
|
23163
|
+
console.log('Project: ${projectId}');
|
|
23164
|
+
console.log('Organization: ${organizationId}\\n');
|
|
23165
|
+
|
|
23166
|
+
// Validate configuration
|
|
23167
|
+
if (!CONFIG.apiKey) {
|
|
23168
|
+
console.error('\u274C Error: NETPAD_API_KEY environment variable is required');
|
|
23169
|
+
console.log('\\nSet it in your environment or .env file:');
|
|
23170
|
+
console.log(' export NETPAD_API_KEY="np_live_xxxxx"\\n');
|
|
23171
|
+
process.exit(1);
|
|
23172
|
+
}
|
|
23173
|
+
|
|
23174
|
+
// Step 1: Create application
|
|
23175
|
+
const applicationId = await createApplication();
|
|
23176
|
+
if (!applicationId) {
|
|
23177
|
+
process.exit(1);
|
|
23178
|
+
}
|
|
23179
|
+
|
|
23180
|
+
// Step 2: Create forms
|
|
23181
|
+
const formIds = await createForms(applicationId);
|
|
23182
|
+
console.log(\`\\n\u{1F4CA} Created \${formIds.length} form(s)\\n\`);
|
|
23183
|
+
|
|
23184
|
+
// Step 3: Create workflows
|
|
23185
|
+
const workflowIds = await createWorkflows(applicationId, formIds);
|
|
23186
|
+
console.log(\`\\n\u26A1 Created \${workflowIds.length} workflow(s)\\n\`);
|
|
23187
|
+
|
|
23188
|
+
// Step 4: Activate workflows
|
|
23189
|
+
if (workflowIds.length > 0) {
|
|
23190
|
+
await activateWorkflows(workflowIds);
|
|
23191
|
+
}
|
|
23192
|
+
|
|
23193
|
+
// Summary
|
|
23194
|
+
console.log('\\n' + '='.repeat(50));
|
|
23195
|
+
console.log('\u2705 Setup Complete!');
|
|
23196
|
+
console.log('='.repeat(50));
|
|
23197
|
+
console.log(\`
|
|
23198
|
+
Application: ${name}
|
|
23199
|
+
Application ID: \${applicationId}
|
|
23200
|
+
Forms: \${formIds.length}
|
|
23201
|
+
Workflows: \${workflowIds.length}
|
|
23202
|
+
|
|
23203
|
+
Next steps:
|
|
23204
|
+
1. Visit your NetPad dashboard to customize forms and workflows
|
|
23205
|
+
2. Test form submissions
|
|
23206
|
+
3. Configure email templates and integrations
|
|
23207
|
+
\`);
|
|
23208
|
+
}
|
|
23209
|
+
|
|
23210
|
+
// Run setup
|
|
23211
|
+
setup().catch((error) => {
|
|
23212
|
+
console.error('\u274C Setup failed:', error);
|
|
23213
|
+
process.exit(1);
|
|
23214
|
+
});`;
|
|
23215
|
+
const code = generateSelfContainedCode({
|
|
23216
|
+
title: `${name} Setup`,
|
|
23217
|
+
description: description || `Complete setup script for ${name}`,
|
|
23218
|
+
includeFormTypes: true,
|
|
23219
|
+
includeWorkflowTypes: true,
|
|
23220
|
+
configCode,
|
|
23221
|
+
functionsCode,
|
|
23222
|
+
mainCode
|
|
23223
|
+
});
|
|
23224
|
+
const output = createToolOutput({
|
|
23225
|
+
code,
|
|
23226
|
+
filename: `setup-${slug}.ts`,
|
|
23227
|
+
envVars: STANDARD_ENV_VARS
|
|
23228
|
+
});
|
|
23229
|
+
return {
|
|
23230
|
+
content: [{
|
|
23231
|
+
type: "text",
|
|
23232
|
+
text: formatToolOutput(output)
|
|
21136
23233
|
}]
|
|
21137
23234
|
};
|
|
21138
23235
|
}
|
|
@@ -21591,7 +23688,7 @@ server.resource(
|
|
|
21591
23688
|
);
|
|
21592
23689
|
server.tool(
|
|
21593
23690
|
"list_workflow_templates",
|
|
21594
|
-
"List all available workflow templates for creating automated workflows.
|
|
23691
|
+
'[DEPRECATED - use browse_templates with templateType="workflow"] List all available workflow templates for creating automated workflows.',
|
|
21595
23692
|
{
|
|
21596
23693
|
category: external_exports.string().optional().describe('Filter by category (e.g., "notifications", "data", "sales")')
|
|
21597
23694
|
},
|
|
@@ -21623,7 +23720,7 @@ server.tool(
|
|
|
21623
23720
|
);
|
|
21624
23721
|
server.tool(
|
|
21625
23722
|
"get_workflow_template",
|
|
21626
|
-
"Get detailed information about a specific workflow template including its nodes, edges, and configuration.
|
|
23723
|
+
'[DEPRECATED - use browse_templates with templateType="workflow" and action="get"] Get detailed information about a specific workflow template including its nodes, edges, and configuration.',
|
|
21627
23724
|
{
|
|
21628
23725
|
templateId: external_exports.enum(["form-to-email", "form-to-database", "lead-qualification", "webhook-to-database", "scheduled-report"]).describe("The template ID")
|
|
21629
23726
|
},
|
|
@@ -21637,10 +23734,21 @@ server.tool(
|
|
|
21637
23734
|
}]
|
|
21638
23735
|
};
|
|
21639
23736
|
}
|
|
23737
|
+
const workflowForUI = {
|
|
23738
|
+
id: template.id,
|
|
23739
|
+
name: template.name,
|
|
23740
|
+
description: template.description,
|
|
23741
|
+
category: template.category,
|
|
23742
|
+
tags: template.tags,
|
|
23743
|
+
canvas: {
|
|
23744
|
+
nodes: template.nodes,
|
|
23745
|
+
edges: template.edges
|
|
23746
|
+
}
|
|
23747
|
+
};
|
|
21640
23748
|
return {
|
|
21641
23749
|
content: [{
|
|
21642
23750
|
type: "text",
|
|
21643
|
-
text: JSON.stringify(
|
|
23751
|
+
text: JSON.stringify(workflowForUI, null, 2)
|
|
21644
23752
|
}]
|
|
21645
23753
|
};
|
|
21646
23754
|
}
|
|
@@ -21688,33 +23796,242 @@ server.tool(
|
|
|
21688
23796
|
);
|
|
21689
23797
|
server.tool(
|
|
21690
23798
|
"create_workflow_from_template",
|
|
21691
|
-
"Generate
|
|
23799
|
+
"Generate a complete, self-contained TypeScript file to create a workflow. Returns validated code with inline types - run with `npx tsx`.",
|
|
21692
23800
|
{
|
|
21693
23801
|
name: external_exports.string().describe("Name of the workflow"),
|
|
21694
23802
|
description: external_exports.string().optional().describe("Description of the workflow"),
|
|
21695
23803
|
templateId: external_exports.enum(["form-to-email", "form-to-database", "lead-qualification", "webhook-to-database", "scheduled-report"]).optional().describe("Template to use"),
|
|
23804
|
+
formId: external_exports.string().optional().describe("Form ID for form-triggered workflows"),
|
|
23805
|
+
formSlug: external_exports.string().optional().describe("Form slug for form-triggered workflows"),
|
|
21696
23806
|
applicationId: external_exports.string().optional().describe("Application ID to attach workflow to"),
|
|
21697
23807
|
projectId: external_exports.string().describe("Project ID"),
|
|
21698
23808
|
organizationId: external_exports.string().describe("Organization ID"),
|
|
21699
|
-
tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization")
|
|
23809
|
+
tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization"),
|
|
23810
|
+
salesEmail: external_exports.string().optional().describe("Email address for sales notifications")
|
|
21700
23811
|
},
|
|
21701
23812
|
async (options) => {
|
|
21702
|
-
const
|
|
21703
|
-
const
|
|
23813
|
+
const { name, description, templateId, formId, formSlug, applicationId, tags, salesEmail } = options;
|
|
23814
|
+
const slug = name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
23815
|
+
const template = templateId ? WORKFLOW_TEMPLATES[templateId] : null;
|
|
23816
|
+
const workflowNodes = template?.nodes || [
|
|
23817
|
+
{
|
|
23818
|
+
id: "trigger_1",
|
|
23819
|
+
type: "manual-trigger",
|
|
23820
|
+
label: "Manual Trigger",
|
|
23821
|
+
position: { x: 100, y: 200 },
|
|
23822
|
+
config: {},
|
|
23823
|
+
enabled: true
|
|
23824
|
+
}
|
|
23825
|
+
];
|
|
23826
|
+
const workflowEdges = template?.edges || [];
|
|
23827
|
+
const updatedNodes = workflowNodes.map((node) => {
|
|
23828
|
+
if (node.type === "form-trigger") {
|
|
23829
|
+
return {
|
|
23830
|
+
...node,
|
|
23831
|
+
config: {
|
|
23832
|
+
...node.config,
|
|
23833
|
+
formId: formId || node.config.formId || "",
|
|
23834
|
+
formSlug: formSlug || node.config.formSlug || ""
|
|
23835
|
+
}
|
|
23836
|
+
};
|
|
23837
|
+
}
|
|
23838
|
+
if (node.type === "email-send" && salesEmail) {
|
|
23839
|
+
return {
|
|
23840
|
+
...node,
|
|
23841
|
+
config: {
|
|
23842
|
+
...node.config,
|
|
23843
|
+
to: salesEmail
|
|
23844
|
+
}
|
|
23845
|
+
};
|
|
23846
|
+
}
|
|
23847
|
+
return node;
|
|
23848
|
+
});
|
|
23849
|
+
const configCode = `// Workflow Configuration
|
|
23850
|
+
const WORKFLOW_CONFIG: WorkflowConfig = {
|
|
23851
|
+
name: ${JSON.stringify(name)},
|
|
23852
|
+
description: ${JSON.stringify(description || template?.description || "Custom workflow")},
|
|
23853
|
+
tags: ${JSON.stringify(tags || template?.tags || [])},
|
|
23854
|
+
canvas: {
|
|
23855
|
+
nodes: ${JSON.stringify(updatedNodes, null, 2)},
|
|
23856
|
+
edges: ${JSON.stringify(workflowEdges, null, 2)},
|
|
23857
|
+
},
|
|
23858
|
+
settings: {
|
|
23859
|
+
executionMode: 'auto',
|
|
23860
|
+
maxExecutionTime: 300000,
|
|
23861
|
+
retryPolicy: {
|
|
23862
|
+
maxRetries: 3,
|
|
23863
|
+
backoffMultiplier: 2,
|
|
23864
|
+
initialDelayMs: 1000,
|
|
23865
|
+
},
|
|
23866
|
+
},
|
|
23867
|
+
};`;
|
|
23868
|
+
const functionsCode = `/**
|
|
23869
|
+
* Create the workflow via NetPad API.
|
|
23870
|
+
*/
|
|
23871
|
+
export async function createWorkflow(): Promise<CreateWorkflowResult> {
|
|
23872
|
+
try {
|
|
23873
|
+
const response = await fetch(\`\${CONFIG.baseUrl}/api/workflows\`, {
|
|
23874
|
+
method: 'POST',
|
|
23875
|
+
headers: {
|
|
23876
|
+
'Content-Type': 'application/json',
|
|
23877
|
+
'Authorization': \`Bearer \${CONFIG.apiKey}\`,
|
|
23878
|
+
},
|
|
23879
|
+
body: JSON.stringify({
|
|
23880
|
+
...WORKFLOW_CONFIG,
|
|
23881
|
+
${applicationId ? `applicationId: '${applicationId}',` : ""}
|
|
23882
|
+
projectId: CONFIG.projectId,
|
|
23883
|
+
organizationId: CONFIG.organizationId,
|
|
23884
|
+
}),
|
|
23885
|
+
});
|
|
23886
|
+
|
|
23887
|
+
if (!response.ok) {
|
|
23888
|
+
return { success: false, error: await response.text() };
|
|
23889
|
+
}
|
|
23890
|
+
|
|
23891
|
+
const result = await response.json() as { workflow: { id: string } };
|
|
23892
|
+
return { success: true, workflowId: result.workflow.id };
|
|
23893
|
+
} catch (error) {
|
|
21704
23894
|
return {
|
|
21705
|
-
|
|
21706
|
-
|
|
21707
|
-
|
|
23895
|
+
success: false,
|
|
23896
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
23897
|
+
};
|
|
23898
|
+
}
|
|
23899
|
+
}
|
|
21708
23900
|
|
|
21709
|
-
|
|
21710
|
-
|
|
21711
|
-
|
|
23901
|
+
/**
|
|
23902
|
+
* Activate the workflow to start processing.
|
|
23903
|
+
*/
|
|
23904
|
+
export async function activateWorkflow(workflowId: string): Promise<{ success: boolean; error?: string }> {
|
|
23905
|
+
try {
|
|
23906
|
+
const response = await fetch(
|
|
23907
|
+
\`\${CONFIG.baseUrl}/api/workflows/\${workflowId}/activate\`,
|
|
23908
|
+
{
|
|
23909
|
+
method: 'POST',
|
|
23910
|
+
headers: {
|
|
23911
|
+
'Authorization': \`Bearer \${CONFIG.apiKey}\`,
|
|
23912
|
+
},
|
|
23913
|
+
}
|
|
23914
|
+
);
|
|
21712
23915
|
|
|
21713
|
-
|
|
23916
|
+
if (!response.ok) {
|
|
23917
|
+
return { success: false, error: await response.text() };
|
|
23918
|
+
}
|
|
21714
23919
|
|
|
21715
|
-
|
|
21716
|
-
|
|
21717
|
-
|
|
23920
|
+
return { success: true };
|
|
23921
|
+
} catch (error) {
|
|
23922
|
+
return {
|
|
23923
|
+
success: false,
|
|
23924
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
23925
|
+
};
|
|
23926
|
+
}
|
|
23927
|
+
}
|
|
23928
|
+
|
|
23929
|
+
/**
|
|
23930
|
+
* Test the workflow with sample data.
|
|
23931
|
+
*/
|
|
23932
|
+
export async function testWorkflow(workflowId: string, testData: Record<string, unknown>): Promise<{ success: boolean; executionId?: string; error?: string }> {
|
|
23933
|
+
try {
|
|
23934
|
+
const response = await fetch(
|
|
23935
|
+
\`\${CONFIG.baseUrl}/api/workflows/\${workflowId}/execute\`,
|
|
23936
|
+
{
|
|
23937
|
+
method: 'POST',
|
|
23938
|
+
headers: {
|
|
23939
|
+
'Content-Type': 'application/json',
|
|
23940
|
+
'Authorization': \`Bearer \${CONFIG.apiKey}\`,
|
|
23941
|
+
},
|
|
23942
|
+
body: JSON.stringify({ payload: testData }),
|
|
23943
|
+
}
|
|
23944
|
+
);
|
|
23945
|
+
|
|
23946
|
+
if (!response.ok) {
|
|
23947
|
+
return { success: false, error: await response.text() };
|
|
23948
|
+
}
|
|
23949
|
+
|
|
23950
|
+
const result = await response.json() as { executionId: string };
|
|
23951
|
+
return { success: true, executionId: result.executionId };
|
|
23952
|
+
} catch (error) {
|
|
23953
|
+
return {
|
|
23954
|
+
success: false,
|
|
23955
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
23956
|
+
};
|
|
23957
|
+
}
|
|
23958
|
+
}`;
|
|
23959
|
+
const mainCode = `// ============================================================================
|
|
23960
|
+
// Main Setup
|
|
23961
|
+
// ============================================================================
|
|
23962
|
+
|
|
23963
|
+
async function setup() {
|
|
23964
|
+
console.log('\u26A1 Creating workflow:', WORKFLOW_CONFIG.name);
|
|
23965
|
+
console.log('Template: ${templateId || "custom"}');
|
|
23966
|
+
${formId ? `console.log('Form ID: ${formId}');` : ""}
|
|
23967
|
+
${formSlug ? `console.log('Form Slug: ${formSlug}');` : ""}
|
|
23968
|
+
console.log('');
|
|
23969
|
+
|
|
23970
|
+
// Validate configuration
|
|
23971
|
+
if (!CONFIG.apiKey) {
|
|
23972
|
+
console.error('\u274C Error: NETPAD_API_KEY environment variable is required');
|
|
23973
|
+
process.exit(1);
|
|
23974
|
+
}
|
|
23975
|
+
|
|
23976
|
+
// Create the workflow
|
|
23977
|
+
const createResult = await createWorkflow();
|
|
23978
|
+
if (!createResult.success) {
|
|
23979
|
+
console.error('\u274C Failed to create workflow:', createResult.error);
|
|
23980
|
+
process.exit(1);
|
|
23981
|
+
}
|
|
23982
|
+
|
|
23983
|
+
console.log('\u2705 Workflow created:', createResult.workflowId);
|
|
23984
|
+
|
|
23985
|
+
// Activate the workflow
|
|
23986
|
+
console.log('\\n\u{1F504} Activating workflow...');
|
|
23987
|
+
const activateResult = await activateWorkflow(createResult.workflowId!);
|
|
23988
|
+
if (activateResult.success) {
|
|
23989
|
+
console.log('\u2705 Workflow activated');
|
|
23990
|
+
} else {
|
|
23991
|
+
console.warn('\u26A0\uFE0F Failed to activate:', activateResult.error);
|
|
23992
|
+
}
|
|
23993
|
+
|
|
23994
|
+
// Summary
|
|
23995
|
+
console.log('\\n' + '='.repeat(50));
|
|
23996
|
+
console.log('\u2705 Workflow Setup Complete!');
|
|
23997
|
+
console.log('='.repeat(50));
|
|
23998
|
+
console.log(\`
|
|
23999
|
+
Workflow: ${name}
|
|
24000
|
+
Workflow ID: \${createResult.workflowId}
|
|
24001
|
+
Nodes: ${updatedNodes.length}
|
|
24002
|
+
Edges: ${workflowEdges.length}
|
|
24003
|
+
${templateId ? `Template: ${templateId}` : ""}
|
|
24004
|
+
|
|
24005
|
+
Next steps:
|
|
24006
|
+
1. Configure node settings in the NetPad dashboard
|
|
24007
|
+
2. Test with sample data
|
|
24008
|
+
3. Monitor executions
|
|
24009
|
+
\`);
|
|
24010
|
+
}
|
|
24011
|
+
|
|
24012
|
+
// Uncomment to run:
|
|
24013
|
+
// setup().catch(console.error);
|
|
24014
|
+
|
|
24015
|
+
// Export for use as module
|
|
24016
|
+
export { WORKFLOW_CONFIG, createWorkflow, activateWorkflow, testWorkflow };`;
|
|
24017
|
+
const code = generateSelfContainedCode({
|
|
24018
|
+
title: name,
|
|
24019
|
+
description: description || `Workflow created from ${templateId || "scratch"}`,
|
|
24020
|
+
includeFormTypes: false,
|
|
24021
|
+
includeWorkflowTypes: true,
|
|
24022
|
+
configCode,
|
|
24023
|
+
functionsCode,
|
|
24024
|
+
mainCode
|
|
24025
|
+
});
|
|
24026
|
+
const output = createToolOutput({
|
|
24027
|
+
code,
|
|
24028
|
+
filename: `${slug}-workflow.ts`,
|
|
24029
|
+
envVars: STANDARD_ENV_VARS
|
|
24030
|
+
});
|
|
24031
|
+
return {
|
|
24032
|
+
content: [{
|
|
24033
|
+
type: "text",
|
|
24034
|
+
text: formatToolOutput(output)
|
|
21718
24035
|
}]
|
|
21719
24036
|
};
|
|
21720
24037
|
}
|
|
@@ -21874,15 +24191,33 @@ ${code}
|
|
|
21874
24191
|
server.resource(
|
|
21875
24192
|
"netpad-workflow-templates",
|
|
21876
24193
|
"netpad://reference/workflow-templates",
|
|
21877
|
-
async () =>
|
|
21878
|
-
|
|
21879
|
-
|
|
21880
|
-
|
|
21881
|
-
|
|
21882
|
-
|
|
21883
|
-
|
|
21884
|
-
|
|
21885
|
-
|
|
24194
|
+
async () => {
|
|
24195
|
+
const templatesForUI = Object.fromEntries(
|
|
24196
|
+
Object.entries(WORKFLOW_TEMPLATES).map(([key, template]) => [
|
|
24197
|
+
key,
|
|
24198
|
+
{
|
|
24199
|
+
id: template.id,
|
|
24200
|
+
name: template.name,
|
|
24201
|
+
description: template.description,
|
|
24202
|
+
category: template.category,
|
|
24203
|
+
tags: template.tags,
|
|
24204
|
+
canvas: {
|
|
24205
|
+
nodes: template.nodes,
|
|
24206
|
+
edges: template.edges
|
|
24207
|
+
}
|
|
24208
|
+
}
|
|
24209
|
+
])
|
|
24210
|
+
);
|
|
24211
|
+
return {
|
|
24212
|
+
contents: [
|
|
24213
|
+
{
|
|
24214
|
+
uri: "netpad://reference/workflow-templates",
|
|
24215
|
+
mimeType: "application/json",
|
|
24216
|
+
text: JSON.stringify(templatesForUI, null, 2)
|
|
24217
|
+
}
|
|
24218
|
+
]
|
|
24219
|
+
};
|
|
24220
|
+
}
|
|
21886
24221
|
);
|
|
21887
24222
|
server.resource(
|
|
21888
24223
|
"netpad-workflow-nodes",
|
|
@@ -21899,7 +24234,7 @@ server.resource(
|
|
|
21899
24234
|
);
|
|
21900
24235
|
server.tool(
|
|
21901
24236
|
"list_conversational_templates",
|
|
21902
|
-
"List all available conversational form templates for AI-powered data collection.
|
|
24237
|
+
'[DEPRECATED - use browse_templates with templateType="conversational"] List all available conversational form templates for AI-powered data collection.',
|
|
21903
24238
|
{
|
|
21904
24239
|
category: external_exports.string().optional().describe('Filter by category (e.g., "support", "feedback", "intake")')
|
|
21905
24240
|
},
|
|
@@ -21931,7 +24266,7 @@ server.tool(
|
|
|
21931
24266
|
);
|
|
21932
24267
|
server.tool(
|
|
21933
24268
|
"get_conversational_template",
|
|
21934
|
-
"Get detailed information about a specific conversational form template including topics, extraction schema, and persona configuration.
|
|
24269
|
+
'[DEPRECATED - use browse_templates with templateType="conversational" and action="get"] Get detailed information about a specific conversational form template including topics, extraction schema, and persona configuration.',
|
|
21935
24270
|
{
|
|
21936
24271
|
templateId: external_exports.enum(["it-helpdesk", "customer-feedback", "lead-qualification", "patient-intake"]).describe("The template ID")
|
|
21937
24272
|
},
|
|
@@ -21978,7 +24313,7 @@ server.tool(
|
|
|
21978
24313
|
})).describe("Topics to cover in conversation"),
|
|
21979
24314
|
extractionSchema: external_exports.array(external_exports.object({
|
|
21980
24315
|
field: external_exports.string().describe("Field name"),
|
|
21981
|
-
type: external_exports.enum(["string", "number", "boolean", "enum", "array", "object"]).describe("Field type"),
|
|
24316
|
+
type: external_exports.enum(["string", "number", "boolean", "enum", "array", "object", "file"]).describe("Field type"),
|
|
21982
24317
|
required: external_exports.boolean().describe("Whether required"),
|
|
21983
24318
|
description: external_exports.string().describe("Field description"),
|
|
21984
24319
|
options: external_exports.array(external_exports.string()).optional().describe("Options for enum type"),
|
|
@@ -22246,7 +24581,7 @@ server.resource(
|
|
|
22246
24581
|
);
|
|
22247
24582
|
server.tool(
|
|
22248
24583
|
"list_template_categories",
|
|
22249
|
-
"List all available form template categories with descriptions and template counts.
|
|
24584
|
+
'[DEPRECATED - use browse_templates with templateType="form" and action="categories"] List all available form template categories with descriptions and template counts.',
|
|
22250
24585
|
{},
|
|
22251
24586
|
async () => {
|
|
22252
24587
|
return {
|
|
@@ -22263,7 +24598,7 @@ server.tool(
|
|
|
22263
24598
|
);
|
|
22264
24599
|
server.tool(
|
|
22265
24600
|
"list_form_templates",
|
|
22266
|
-
"List all available form templates (25+) across multiple categories. Returns template summaries with field counts.
|
|
24601
|
+
'[DEPRECATED - use browse_templates with templateType="form"] List all available form templates (25+) across multiple categories. Returns template summaries with field counts.',
|
|
22267
24602
|
{
|
|
22268
24603
|
category: external_exports.string().optional().describe('Filter by category (business, events, feedback, support, ecommerce, healthcare, hr, finance, education, real-estate, or "all")'),
|
|
22269
24604
|
search: external_exports.string().optional().describe("Search templates by name, description, or tags")
|
|
@@ -22302,7 +24637,7 @@ server.tool(
|
|
|
22302
24637
|
);
|
|
22303
24638
|
server.tool(
|
|
22304
24639
|
"get_form_template",
|
|
22305
|
-
"Get detailed information about a specific form template including all fields, validation rules, and configuration.
|
|
24640
|
+
'[DEPRECATED - use browse_templates with templateType="form" and action="get"] Get detailed information about a specific form template including all fields, validation rules, and configuration.',
|
|
22306
24641
|
{
|
|
22307
24642
|
templateId: external_exports.string().describe('Template ID (e.g., "contact-form", "lead-capture", "patient-intake")')
|
|
22308
24643
|
},
|
|
@@ -22485,7 +24820,7 @@ ${code}
|
|
|
22485
24820
|
);
|
|
22486
24821
|
server.tool(
|
|
22487
24822
|
"list_query_templates",
|
|
22488
|
-
"List all available MongoDB query templates for common operations.
|
|
24823
|
+
'[DEPRECATED - use browse_templates with templateType="query"] List all available MongoDB query templates for common operations.',
|
|
22489
24824
|
{},
|
|
22490
24825
|
async () => {
|
|
22491
24826
|
const templates = listQueryTemplates();
|
|
@@ -22502,7 +24837,7 @@ server.tool(
|
|
|
22502
24837
|
);
|
|
22503
24838
|
server.tool(
|
|
22504
24839
|
"get_query_template",
|
|
22505
|
-
"Get a specific query template with example code.
|
|
24840
|
+
'[DEPRECATED - use browse_templates with templateType="query" and action="get"] Get a specific query template with example code.',
|
|
22506
24841
|
{
|
|
22507
24842
|
templateId: external_exports.string().describe('Template ID (e.g., "find-all", "aggregate-group-count")')
|
|
22508
24843
|
},
|