@netpad/mcp-server 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2059 -287
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -16567,104 +16567,6 @@ var APPLICATION_TEMPLATES = {
|
|
|
16567
16567
|
}
|
|
16568
16568
|
}
|
|
16569
16569
|
};
|
|
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
16570
|
function generateApplicationContract(options) {
|
|
16669
16571
|
const {
|
|
16670
16572
|
applicationId,
|
|
@@ -17898,92 +17800,6 @@ var WORKFLOW_TEMPLATES = {
|
|
|
17898
17800
|
]
|
|
17899
17801
|
}
|
|
17900
17802
|
};
|
|
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
17803
|
function generateAddNodeCode(workflowId, node) {
|
|
17988
17804
|
return `// Add node to workflow
|
|
17989
17805
|
const nodeData = ${JSON.stringify(node, null, 2)};
|
|
@@ -20115,6 +19931,630 @@ collections.forEach((coll: any) => {
|
|
|
20115
19931
|
`;
|
|
20116
19932
|
}
|
|
20117
19933
|
|
|
19934
|
+
// src/validation.ts
|
|
19935
|
+
import ts from "typescript";
|
|
19936
|
+
var INLINE_TYPES = `
|
|
19937
|
+
// ============================================================================
|
|
19938
|
+
// Types (inline - no external dependencies)
|
|
19939
|
+
// ============================================================================
|
|
19940
|
+
|
|
19941
|
+
interface NetPadConfig {
|
|
19942
|
+
baseUrl: string;
|
|
19943
|
+
apiKey: string;
|
|
19944
|
+
organizationId?: string;
|
|
19945
|
+
projectId?: string;
|
|
19946
|
+
}
|
|
19947
|
+
|
|
19948
|
+
interface FormFieldValidation {
|
|
19949
|
+
min?: number;
|
|
19950
|
+
max?: number;
|
|
19951
|
+
minLength?: number;
|
|
19952
|
+
maxLength?: number;
|
|
19953
|
+
pattern?: string;
|
|
19954
|
+
errorMessage?: string;
|
|
19955
|
+
}
|
|
19956
|
+
|
|
19957
|
+
interface FormFieldOption {
|
|
19958
|
+
label: string;
|
|
19959
|
+
value: string;
|
|
19960
|
+
}
|
|
19961
|
+
|
|
19962
|
+
interface ConditionalLogicCondition {
|
|
19963
|
+
field: string;
|
|
19964
|
+
operator: string;
|
|
19965
|
+
value?: string | number | boolean;
|
|
19966
|
+
}
|
|
19967
|
+
|
|
19968
|
+
interface ConditionalLogic {
|
|
19969
|
+
action: 'show' | 'hide';
|
|
19970
|
+
logicType: 'all' | 'any';
|
|
19971
|
+
conditions: ConditionalLogicCondition[];
|
|
19972
|
+
}
|
|
19973
|
+
|
|
19974
|
+
interface FormField {
|
|
19975
|
+
path: string;
|
|
19976
|
+
label: string;
|
|
19977
|
+
type: string;
|
|
19978
|
+
included?: boolean;
|
|
19979
|
+
required?: boolean;
|
|
19980
|
+
disabled?: boolean;
|
|
19981
|
+
readOnly?: boolean;
|
|
19982
|
+
placeholder?: string;
|
|
19983
|
+
helpText?: string;
|
|
19984
|
+
options?: FormFieldOption[];
|
|
19985
|
+
validation?: FormFieldValidation;
|
|
19986
|
+
fieldWidth?: 'full' | 'half' | 'third' | 'quarter';
|
|
19987
|
+
defaultValue?: unknown;
|
|
19988
|
+
conditionalLogic?: ConditionalLogic;
|
|
19989
|
+
}
|
|
19990
|
+
|
|
19991
|
+
interface FormTheme {
|
|
19992
|
+
primaryColor?: string;
|
|
19993
|
+
backgroundColor?: string;
|
|
19994
|
+
surfaceColor?: string;
|
|
19995
|
+
textColor?: string;
|
|
19996
|
+
errorColor?: string;
|
|
19997
|
+
successColor?: string;
|
|
19998
|
+
borderRadius?: number;
|
|
19999
|
+
spacing?: 'compact' | 'comfortable' | 'spacious';
|
|
20000
|
+
inputStyle?: 'outlined' | 'filled' | 'standard';
|
|
20001
|
+
mode?: 'light' | 'dark';
|
|
20002
|
+
}
|
|
20003
|
+
|
|
20004
|
+
interface FormPage {
|
|
20005
|
+
id: string;
|
|
20006
|
+
title: string;
|
|
20007
|
+
description?: string;
|
|
20008
|
+
fields: string[];
|
|
20009
|
+
}
|
|
20010
|
+
|
|
20011
|
+
interface MultiPageConfig {
|
|
20012
|
+
enabled: boolean;
|
|
20013
|
+
pages: FormPage[];
|
|
20014
|
+
showProgressBar?: boolean;
|
|
20015
|
+
showPageTitles?: boolean;
|
|
20016
|
+
allowSkip?: boolean;
|
|
20017
|
+
showReview?: boolean;
|
|
20018
|
+
}
|
|
20019
|
+
|
|
20020
|
+
interface FormConfig {
|
|
20021
|
+
name: string;
|
|
20022
|
+
slug?: string;
|
|
20023
|
+
description?: string;
|
|
20024
|
+
fieldConfigs: FormField[];
|
|
20025
|
+
submitButtonText?: string;
|
|
20026
|
+
successMessage?: string;
|
|
20027
|
+
theme?: FormTheme;
|
|
20028
|
+
multiPage?: MultiPageConfig;
|
|
20029
|
+
}
|
|
20030
|
+
|
|
20031
|
+
interface SubmitResult {
|
|
20032
|
+
success: boolean;
|
|
20033
|
+
submissionId?: string;
|
|
20034
|
+
error?: string;
|
|
20035
|
+
}
|
|
20036
|
+
|
|
20037
|
+
interface ApiResponse<T> {
|
|
20038
|
+
success: boolean;
|
|
20039
|
+
data?: T;
|
|
20040
|
+
error?: string;
|
|
20041
|
+
}
|
|
20042
|
+
`;
|
|
20043
|
+
var INLINE_WORKFLOW_TYPES = `
|
|
20044
|
+
interface WorkflowNode {
|
|
20045
|
+
id: string;
|
|
20046
|
+
type: string;
|
|
20047
|
+
label: string;
|
|
20048
|
+
position: { x: number; y: number };
|
|
20049
|
+
config: Record<string, unknown>;
|
|
20050
|
+
enabled?: boolean;
|
|
20051
|
+
}
|
|
20052
|
+
|
|
20053
|
+
interface WorkflowEdge {
|
|
20054
|
+
id: string;
|
|
20055
|
+
source: string;
|
|
20056
|
+
sourceHandle: string;
|
|
20057
|
+
target: string;
|
|
20058
|
+
targetHandle: string;
|
|
20059
|
+
condition?: {
|
|
20060
|
+
expression: string;
|
|
20061
|
+
label?: string;
|
|
20062
|
+
};
|
|
20063
|
+
}
|
|
20064
|
+
|
|
20065
|
+
interface WorkflowSettings {
|
|
20066
|
+
executionMode?: 'auto' | 'manual';
|
|
20067
|
+
maxExecutionTime?: number;
|
|
20068
|
+
retryPolicy?: {
|
|
20069
|
+
maxRetries: number;
|
|
20070
|
+
backoffMultiplier: number;
|
|
20071
|
+
initialDelayMs: number;
|
|
20072
|
+
};
|
|
20073
|
+
}
|
|
20074
|
+
|
|
20075
|
+
interface WorkflowConfig {
|
|
20076
|
+
name: string;
|
|
20077
|
+
description?: string;
|
|
20078
|
+
tags?: string[];
|
|
20079
|
+
nodes: WorkflowNode[];
|
|
20080
|
+
edges: WorkflowEdge[];
|
|
20081
|
+
settings?: WorkflowSettings;
|
|
20082
|
+
}
|
|
20083
|
+
|
|
20084
|
+
interface CreateWorkflowResult {
|
|
20085
|
+
success: boolean;
|
|
20086
|
+
workflowId?: string;
|
|
20087
|
+
error?: string;
|
|
20088
|
+
}
|
|
20089
|
+
`;
|
|
20090
|
+
var SKIPPED_ERROR_PATTERNS = [
|
|
20091
|
+
"Cannot find module",
|
|
20092
|
+
// Global types (not available in isolated compilation)
|
|
20093
|
+
"Cannot find global type",
|
|
20094
|
+
"Cannot find name 'Array'",
|
|
20095
|
+
"Cannot find name 'String'",
|
|
20096
|
+
"Cannot find name 'Number'",
|
|
20097
|
+
"Cannot find name 'Boolean'",
|
|
20098
|
+
"Cannot find name 'Object'",
|
|
20099
|
+
"Cannot find name 'Function'",
|
|
20100
|
+
"Cannot find name 'Symbol'",
|
|
20101
|
+
"Cannot find name 'Error'",
|
|
20102
|
+
"Cannot find name 'Promise'",
|
|
20103
|
+
"Cannot find name 'Record'",
|
|
20104
|
+
"Cannot find name 'Partial'",
|
|
20105
|
+
"Cannot find name 'Required'",
|
|
20106
|
+
"Cannot find name 'Readonly'",
|
|
20107
|
+
"Cannot find name 'Pick'",
|
|
20108
|
+
"Cannot find name 'Omit'",
|
|
20109
|
+
"Cannot find name 'Exclude'",
|
|
20110
|
+
"Cannot find name 'Extract'",
|
|
20111
|
+
"Cannot find name 'Map'",
|
|
20112
|
+
"Cannot find name 'Set'",
|
|
20113
|
+
"Cannot find name 'WeakMap'",
|
|
20114
|
+
"Cannot find name 'WeakSet'",
|
|
20115
|
+
"Cannot find name 'JSON'",
|
|
20116
|
+
"Cannot find name 'Date'",
|
|
20117
|
+
"Cannot find name 'RegExp'",
|
|
20118
|
+
"Cannot find name 'Math'",
|
|
20119
|
+
// Runtime globals
|
|
20120
|
+
"Cannot find name 'process'",
|
|
20121
|
+
"Cannot find name 'fetch'",
|
|
20122
|
+
"Cannot find name 'console'",
|
|
20123
|
+
"Cannot find name 'RequestInit'",
|
|
20124
|
+
"Cannot find name 'Response'",
|
|
20125
|
+
"Cannot find name 'Headers'",
|
|
20126
|
+
"Cannot find name 'URL'",
|
|
20127
|
+
"Cannot find name 'URLSearchParams'",
|
|
20128
|
+
"Cannot find name 'FormData'",
|
|
20129
|
+
"Cannot find name 'Blob'",
|
|
20130
|
+
"Cannot find name 'File'",
|
|
20131
|
+
"Cannot find name 'AbortController'",
|
|
20132
|
+
"Cannot find name 'AbortSignal'",
|
|
20133
|
+
"Cannot find name 'setTimeout'",
|
|
20134
|
+
"Cannot find name 'clearTimeout'",
|
|
20135
|
+
"Cannot find name 'setInterval'",
|
|
20136
|
+
"Cannot find name 'clearInterval'",
|
|
20137
|
+
// React-specific
|
|
20138
|
+
"Cannot find name 'React'",
|
|
20139
|
+
"Cannot find namespace 'React'",
|
|
20140
|
+
"Cannot find name 'JSX'",
|
|
20141
|
+
// Node.js specific
|
|
20142
|
+
"Cannot find name 'Buffer'",
|
|
20143
|
+
"Cannot find name '__dirname'",
|
|
20144
|
+
"Cannot find name '__filename'",
|
|
20145
|
+
"Cannot find name 'require'",
|
|
20146
|
+
"Cannot find name 'module'",
|
|
20147
|
+
"Cannot find name 'exports'",
|
|
20148
|
+
// DOM types
|
|
20149
|
+
"Cannot find name 'document'",
|
|
20150
|
+
"Cannot find name 'window'",
|
|
20151
|
+
"Cannot find name 'HTMLElement'",
|
|
20152
|
+
"Cannot find name 'Event'",
|
|
20153
|
+
"Cannot find name 'EventTarget'",
|
|
20154
|
+
// Library suggestions
|
|
20155
|
+
"Do you need to change your target library",
|
|
20156
|
+
// Strict mode catch clause errors (handled by instanceof checks at runtime)
|
|
20157
|
+
"is of type 'unknown'"
|
|
20158
|
+
];
|
|
20159
|
+
function validateTypeScript(code) {
|
|
20160
|
+
const result = validateTypeScriptDetailed(code);
|
|
20161
|
+
return result.errors.map(
|
|
20162
|
+
(e) => e.line ? `Line ${e.line}:${e.column}: ${e.message}` : e.message
|
|
20163
|
+
);
|
|
20164
|
+
}
|
|
20165
|
+
function validateTypeScriptDetailed(code) {
|
|
20166
|
+
const errors = [];
|
|
20167
|
+
const warnings = [];
|
|
20168
|
+
const sourceFile = ts.createSourceFile(
|
|
20169
|
+
"generated.ts",
|
|
20170
|
+
code,
|
|
20171
|
+
ts.ScriptTarget.ES2020,
|
|
20172
|
+
true,
|
|
20173
|
+
ts.ScriptKind.TS
|
|
20174
|
+
);
|
|
20175
|
+
const compilerOptions = {
|
|
20176
|
+
target: ts.ScriptTarget.ES2020,
|
|
20177
|
+
module: ts.ModuleKind.ESNext,
|
|
20178
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
20179
|
+
strict: true,
|
|
20180
|
+
noEmit: true,
|
|
20181
|
+
esModuleInterop: true,
|
|
20182
|
+
skipLibCheck: true,
|
|
20183
|
+
// Don't require external type definitions
|
|
20184
|
+
types: [],
|
|
20185
|
+
lib: ["ES2020", "DOM"],
|
|
20186
|
+
// JSX support for React components
|
|
20187
|
+
jsx: ts.JsxEmit.React,
|
|
20188
|
+
jsxFactory: "React.createElement",
|
|
20189
|
+
jsxFragmentFactory: "React.Fragment"
|
|
20190
|
+
};
|
|
20191
|
+
const defaultCompilerHost = ts.createCompilerHost(compilerOptions);
|
|
20192
|
+
const customCompilerHost = {
|
|
20193
|
+
...defaultCompilerHost,
|
|
20194
|
+
getSourceFile: (fileName, languageVersion) => {
|
|
20195
|
+
if (fileName === "generated.ts") {
|
|
20196
|
+
return sourceFile;
|
|
20197
|
+
}
|
|
20198
|
+
if (fileName.endsWith(".ts") || fileName.endsWith(".tsx")) {
|
|
20199
|
+
return ts.createSourceFile(
|
|
20200
|
+
fileName,
|
|
20201
|
+
"",
|
|
20202
|
+
languageVersion,
|
|
20203
|
+
true
|
|
20204
|
+
);
|
|
20205
|
+
}
|
|
20206
|
+
return defaultCompilerHost.getSourceFile(fileName, languageVersion);
|
|
20207
|
+
},
|
|
20208
|
+
fileExists: (fileName) => {
|
|
20209
|
+
if (fileName === "generated.ts") return true;
|
|
20210
|
+
return defaultCompilerHost.fileExists(fileName);
|
|
20211
|
+
},
|
|
20212
|
+
readFile: (fileName) => {
|
|
20213
|
+
if (fileName === "generated.ts") return code;
|
|
20214
|
+
return defaultCompilerHost.readFile(fileName);
|
|
20215
|
+
}
|
|
20216
|
+
};
|
|
20217
|
+
const program = ts.createProgram(
|
|
20218
|
+
["generated.ts"],
|
|
20219
|
+
compilerOptions,
|
|
20220
|
+
customCompilerHost
|
|
20221
|
+
);
|
|
20222
|
+
const allDiagnostics = ts.getPreEmitDiagnostics(program);
|
|
20223
|
+
for (const diagnostic of allDiagnostics) {
|
|
20224
|
+
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
20225
|
+
const shouldSkip = SKIPPED_ERROR_PATTERNS.some((pattern) => message.includes(pattern));
|
|
20226
|
+
if (shouldSkip) {
|
|
20227
|
+
continue;
|
|
20228
|
+
}
|
|
20229
|
+
if (diagnostic.category === ts.DiagnosticCategory.Warning) {
|
|
20230
|
+
warnings.push(message);
|
|
20231
|
+
continue;
|
|
20232
|
+
}
|
|
20233
|
+
if (diagnostic.category === ts.DiagnosticCategory.Error) {
|
|
20234
|
+
const error48 = {
|
|
20235
|
+
message,
|
|
20236
|
+
code: diagnostic.code,
|
|
20237
|
+
fixable: isFixableError(diagnostic.code, message),
|
|
20238
|
+
fixSuggestion: getFixSuggestion(diagnostic.code, message)
|
|
20239
|
+
};
|
|
20240
|
+
if (diagnostic.file && diagnostic.start !== void 0) {
|
|
20241
|
+
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
|
20242
|
+
error48.line = line + 1;
|
|
20243
|
+
error48.column = character + 1;
|
|
20244
|
+
}
|
|
20245
|
+
errors.push(error48);
|
|
20246
|
+
}
|
|
20247
|
+
}
|
|
20248
|
+
return {
|
|
20249
|
+
valid: errors.length === 0,
|
|
20250
|
+
errors,
|
|
20251
|
+
warnings,
|
|
20252
|
+
fixable: errors.some((e) => e.fixable)
|
|
20253
|
+
};
|
|
20254
|
+
}
|
|
20255
|
+
function isFixableError(code, message) {
|
|
20256
|
+
if (code === 1005) return true;
|
|
20257
|
+
if (code === 7006) return true;
|
|
20258
|
+
if (code === 1064) return true;
|
|
20259
|
+
if (code === 2304 && message.includes("Cannot find name")) return false;
|
|
20260
|
+
if (code === 2345) return false;
|
|
20261
|
+
if (code === 2339) return false;
|
|
20262
|
+
return false;
|
|
20263
|
+
}
|
|
20264
|
+
function getFixSuggestion(code, message) {
|
|
20265
|
+
if (code === 1005) {
|
|
20266
|
+
return "Add missing semicolon";
|
|
20267
|
+
}
|
|
20268
|
+
if (code === 7006) {
|
|
20269
|
+
return "Add type annotation to parameter (e.g., ': unknown' or ': string')";
|
|
20270
|
+
}
|
|
20271
|
+
if (code === 1064) {
|
|
20272
|
+
return "Add Promise return type to async function";
|
|
20273
|
+
}
|
|
20274
|
+
if (code === 2304) {
|
|
20275
|
+
const match = message.match(/Cannot find name '(\w+)'/);
|
|
20276
|
+
if (match) {
|
|
20277
|
+
return `Ensure '${match[1]}' is defined or imported`;
|
|
20278
|
+
}
|
|
20279
|
+
}
|
|
20280
|
+
if (code === 2345) {
|
|
20281
|
+
return "Check that the argument type matches the expected parameter type";
|
|
20282
|
+
}
|
|
20283
|
+
if (code === 2339) {
|
|
20284
|
+
const match = message.match(/Property '(\w+)' does not exist/);
|
|
20285
|
+
if (match) {
|
|
20286
|
+
return `Add '${match[1]}' property to the type definition or use type assertion`;
|
|
20287
|
+
}
|
|
20288
|
+
}
|
|
20289
|
+
return void 0;
|
|
20290
|
+
}
|
|
20291
|
+
function attemptAutoFix(code, _errors) {
|
|
20292
|
+
let fixedCode = code;
|
|
20293
|
+
const result = validateTypeScriptDetailed(code);
|
|
20294
|
+
for (const error48 of result.errors) {
|
|
20295
|
+
if (!error48.fixable) continue;
|
|
20296
|
+
if (error48.code === 1005 && error48.message.includes("';' expected")) {
|
|
20297
|
+
fixedCode = fixedCode.replace(
|
|
20298
|
+
/(\})\s*(\n\s*(?:const|let|var|function|export|import|class|interface|type|async|if|for|while|switch|try))/g,
|
|
20299
|
+
"$1;\n$2"
|
|
20300
|
+
);
|
|
20301
|
+
fixedCode = fixedCode.replace(
|
|
20302
|
+
/(\s=\s[^;{]+)\s*(\n\s*(?:const|let|var|function|export|import|class|interface|type|async))/g,
|
|
20303
|
+
"$1;$2"
|
|
20304
|
+
);
|
|
20305
|
+
}
|
|
20306
|
+
if (error48.code === 7006 && error48.message.includes("implicitly has an")) {
|
|
20307
|
+
fixedCode = fixedCode.replace(
|
|
20308
|
+
/\(([a-zA-Z_][a-zA-Z0-9_]*)\s*\)/g,
|
|
20309
|
+
"($1: unknown)"
|
|
20310
|
+
);
|
|
20311
|
+
fixedCode = fixedCode.replace(
|
|
20312
|
+
/\(([a-zA-Z_][a-zA-Z0-9_]*)\s*,\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)/g,
|
|
20313
|
+
"($1: unknown, $2: unknown)"
|
|
20314
|
+
);
|
|
20315
|
+
}
|
|
20316
|
+
if (error48.code === 1064) {
|
|
20317
|
+
fixedCode = fixedCode.replace(
|
|
20318
|
+
/async function (\w+)\(([^)]*)\)\s*\{/g,
|
|
20319
|
+
(match, name, params) => {
|
|
20320
|
+
if (match.includes(":")) return match;
|
|
20321
|
+
return `async function ${name}(${params}): Promise<unknown> {`;
|
|
20322
|
+
}
|
|
20323
|
+
);
|
|
20324
|
+
fixedCode = fixedCode.replace(
|
|
20325
|
+
/const (\w+) = async \(([^)]*)\)\s*=>/g,
|
|
20326
|
+
(match, name, params) => {
|
|
20327
|
+
if (match.includes(":")) return match;
|
|
20328
|
+
return `const ${name} = async (${params}): Promise<unknown> =>`;
|
|
20329
|
+
}
|
|
20330
|
+
);
|
|
20331
|
+
}
|
|
20332
|
+
}
|
|
20333
|
+
fixedCode = fixedCode.replace(/,(\s*[}\]])/g, "$1");
|
|
20334
|
+
fixedCode = fixedCode.replace(
|
|
20335
|
+
/export \{ ([^}]+) \}(\s*\n)/g,
|
|
20336
|
+
"export { $1 };$2"
|
|
20337
|
+
);
|
|
20338
|
+
return fixedCode;
|
|
20339
|
+
}
|
|
20340
|
+
function attemptAutoFixIterative(code, maxIterations = 3) {
|
|
20341
|
+
let currentCode = code;
|
|
20342
|
+
let iterations = 0;
|
|
20343
|
+
let previousErrorCount = Infinity;
|
|
20344
|
+
while (iterations < maxIterations) {
|
|
20345
|
+
const errors = validateTypeScript(currentCode);
|
|
20346
|
+
if (errors.length === 0 || errors.length >= previousErrorCount) {
|
|
20347
|
+
return {
|
|
20348
|
+
code: currentCode,
|
|
20349
|
+
fixed: errors.length === 0,
|
|
20350
|
+
iterations,
|
|
20351
|
+
remainingErrors: errors
|
|
20352
|
+
};
|
|
20353
|
+
}
|
|
20354
|
+
previousErrorCount = errors.length;
|
|
20355
|
+
currentCode = attemptAutoFix(currentCode, errors);
|
|
20356
|
+
iterations++;
|
|
20357
|
+
}
|
|
20358
|
+
const finalErrors = validateTypeScript(currentCode);
|
|
20359
|
+
return {
|
|
20360
|
+
code: currentCode,
|
|
20361
|
+
fixed: finalErrors.length === 0,
|
|
20362
|
+
iterations,
|
|
20363
|
+
remainingErrors: finalErrors
|
|
20364
|
+
};
|
|
20365
|
+
}
|
|
20366
|
+
function getSyntaxErrors(code) {
|
|
20367
|
+
const sourceFile = ts.createSourceFile(
|
|
20368
|
+
"check.ts",
|
|
20369
|
+
code,
|
|
20370
|
+
ts.ScriptTarget.ES2020,
|
|
20371
|
+
true,
|
|
20372
|
+
ts.ScriptKind.TS
|
|
20373
|
+
);
|
|
20374
|
+
const syntaxErrors = sourceFile.parseDiagnostics || [];
|
|
20375
|
+
return syntaxErrors.map((diagnostic) => {
|
|
20376
|
+
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
20377
|
+
if (diagnostic.start !== void 0) {
|
|
20378
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(diagnostic.start);
|
|
20379
|
+
return `Line ${line + 1}:${character + 1}: ${message}`;
|
|
20380
|
+
}
|
|
20381
|
+
return message;
|
|
20382
|
+
});
|
|
20383
|
+
}
|
|
20384
|
+
var STANDARD_ENV_VARS = [
|
|
20385
|
+
{
|
|
20386
|
+
name: "NETPAD_URL",
|
|
20387
|
+
description: "NetPad API URL",
|
|
20388
|
+
example: "https://api.netpad.io"
|
|
20389
|
+
},
|
|
20390
|
+
{
|
|
20391
|
+
name: "NETPAD_API_KEY",
|
|
20392
|
+
description: "Your NetPad API key",
|
|
20393
|
+
example: "np_live_xxxxx"
|
|
20394
|
+
},
|
|
20395
|
+
{
|
|
20396
|
+
name: "NETPAD_ORG_ID",
|
|
20397
|
+
description: "Your organization ID",
|
|
20398
|
+
example: "org_xxxxx"
|
|
20399
|
+
},
|
|
20400
|
+
{
|
|
20401
|
+
name: "NETPAD_PROJECT_ID",
|
|
20402
|
+
description: "Your project ID",
|
|
20403
|
+
example: "proj_xxxxx"
|
|
20404
|
+
}
|
|
20405
|
+
];
|
|
20406
|
+
function generateSelfContainedCode(options) {
|
|
20407
|
+
const {
|
|
20408
|
+
title,
|
|
20409
|
+
description,
|
|
20410
|
+
includeFormTypes = true,
|
|
20411
|
+
includeWorkflowTypes = false,
|
|
20412
|
+
configCode,
|
|
20413
|
+
functionsCode,
|
|
20414
|
+
mainCode
|
|
20415
|
+
} = options;
|
|
20416
|
+
let code = `/**
|
|
20417
|
+
* ${title}
|
|
20418
|
+
* ${description || "Generated by NetPad"}
|
|
20419
|
+
*
|
|
20420
|
+
* Run with: npx tsx ${title.toLowerCase().replace(/\s+/g, "-")}.ts
|
|
20421
|
+
*/
|
|
20422
|
+
|
|
20423
|
+
`;
|
|
20424
|
+
if (includeFormTypes) {
|
|
20425
|
+
code += INLINE_TYPES + "\n";
|
|
20426
|
+
}
|
|
20427
|
+
if (includeWorkflowTypes) {
|
|
20428
|
+
code += INLINE_WORKFLOW_TYPES + "\n";
|
|
20429
|
+
}
|
|
20430
|
+
code += `// ============================================================================
|
|
20431
|
+
// Configuration
|
|
20432
|
+
// ============================================================================
|
|
20433
|
+
|
|
20434
|
+
const CONFIG = {
|
|
20435
|
+
baseUrl: process.env.NETPAD_URL ?? 'https://api.netpad.io',
|
|
20436
|
+
apiKey: process.env.NETPAD_API_KEY ?? '',
|
|
20437
|
+
organizationId: process.env.NETPAD_ORG_ID ?? '',
|
|
20438
|
+
projectId: process.env.NETPAD_PROJECT_ID ?? '',
|
|
20439
|
+
};
|
|
20440
|
+
|
|
20441
|
+
${configCode}
|
|
20442
|
+
|
|
20443
|
+
// ============================================================================
|
|
20444
|
+
// API Functions
|
|
20445
|
+
// ============================================================================
|
|
20446
|
+
|
|
20447
|
+
${functionsCode}
|
|
20448
|
+
`;
|
|
20449
|
+
if (mainCode) {
|
|
20450
|
+
code += `
|
|
20451
|
+
// ============================================================================
|
|
20452
|
+
// Main
|
|
20453
|
+
// ============================================================================
|
|
20454
|
+
|
|
20455
|
+
${mainCode}
|
|
20456
|
+
`;
|
|
20457
|
+
}
|
|
20458
|
+
return code;
|
|
20459
|
+
}
|
|
20460
|
+
function createToolOutput(options) {
|
|
20461
|
+
const { code, filename, additionalFiles, envVars, dependencies, skipValidation } = options;
|
|
20462
|
+
if (skipValidation) {
|
|
20463
|
+
return {
|
|
20464
|
+
code,
|
|
20465
|
+
filename,
|
|
20466
|
+
additionalFiles,
|
|
20467
|
+
envVars: envVars || STANDARD_ENV_VARS,
|
|
20468
|
+
dependencies,
|
|
20469
|
+
validated: true
|
|
20470
|
+
// Assumed valid
|
|
20471
|
+
};
|
|
20472
|
+
}
|
|
20473
|
+
const syntaxErrors = getSyntaxErrors(code);
|
|
20474
|
+
if (syntaxErrors.length > 0) {
|
|
20475
|
+
return {
|
|
20476
|
+
code,
|
|
20477
|
+
filename,
|
|
20478
|
+
additionalFiles,
|
|
20479
|
+
envVars: envVars || STANDARD_ENV_VARS,
|
|
20480
|
+
dependencies,
|
|
20481
|
+
validated: false,
|
|
20482
|
+
validationErrors: syntaxErrors
|
|
20483
|
+
};
|
|
20484
|
+
}
|
|
20485
|
+
const fixResult = attemptAutoFixIterative(code);
|
|
20486
|
+
return {
|
|
20487
|
+
code: fixResult.code,
|
|
20488
|
+
filename,
|
|
20489
|
+
additionalFiles,
|
|
20490
|
+
envVars: envVars || STANDARD_ENV_VARS,
|
|
20491
|
+
dependencies,
|
|
20492
|
+
validated: fixResult.fixed,
|
|
20493
|
+
validationErrors: fixResult.remainingErrors.length > 0 ? fixResult.remainingErrors : void 0
|
|
20494
|
+
};
|
|
20495
|
+
}
|
|
20496
|
+
function formatToolOutput(output) {
|
|
20497
|
+
let result = "";
|
|
20498
|
+
if (output.validated) {
|
|
20499
|
+
result += "\u2705 **Code validated successfully** - Ready to use!\n\n";
|
|
20500
|
+
} else {
|
|
20501
|
+
result += "\u26A0\uFE0F **Code validation warnings:**\n\n";
|
|
20502
|
+
result += "The generated code has some issues that may need manual review:\n\n";
|
|
20503
|
+
for (const error48 of output.validationErrors || []) {
|
|
20504
|
+
result += `- ${error48}
|
|
20505
|
+
`;
|
|
20506
|
+
}
|
|
20507
|
+
result += "\n> Note: The code may still work in most environments. These warnings are from strict TypeScript checking.\n\n";
|
|
20508
|
+
}
|
|
20509
|
+
result += "## Quick Start\n\n";
|
|
20510
|
+
result += "```bash\n";
|
|
20511
|
+
result += `# Save the code below to ${output.filename}
|
|
20512
|
+
`;
|
|
20513
|
+
result += `# Then run:
|
|
20514
|
+
`;
|
|
20515
|
+
result += `npx tsx ${output.filename}
|
|
20516
|
+
`;
|
|
20517
|
+
result += "```\n\n";
|
|
20518
|
+
if (output.envVars.length > 0) {
|
|
20519
|
+
result += "## Required Environment Variables\n\n";
|
|
20520
|
+
result += "Create a `.env` file or export these variables:\n\n";
|
|
20521
|
+
result += "```bash\n";
|
|
20522
|
+
for (const env of output.envVars) {
|
|
20523
|
+
result += `# ${env.description}
|
|
20524
|
+
`;
|
|
20525
|
+
result += `export ${env.name}="${env.example}"
|
|
20526
|
+
`;
|
|
20527
|
+
}
|
|
20528
|
+
result += "```\n\n";
|
|
20529
|
+
}
|
|
20530
|
+
if (output.dependencies && output.dependencies.length > 0) {
|
|
20531
|
+
result += "## Dependencies\n\n";
|
|
20532
|
+
result += "```bash\n";
|
|
20533
|
+
result += `npm install ${output.dependencies.join(" ")}
|
|
20534
|
+
`;
|
|
20535
|
+
result += "```\n\n";
|
|
20536
|
+
}
|
|
20537
|
+
result += `## ${output.filename}
|
|
20538
|
+
|
|
20539
|
+
`;
|
|
20540
|
+
result += "```typescript\n";
|
|
20541
|
+
result += output.code;
|
|
20542
|
+
result += "\n```\n";
|
|
20543
|
+
if (output.additionalFiles && output.additionalFiles.length > 0) {
|
|
20544
|
+
result += "\n## Additional Files\n";
|
|
20545
|
+
for (const file2 of output.additionalFiles) {
|
|
20546
|
+
result += `
|
|
20547
|
+
### ${file2.filename}
|
|
20548
|
+
|
|
20549
|
+
`;
|
|
20550
|
+
result += "```typescript\n";
|
|
20551
|
+
result += file2.code;
|
|
20552
|
+
result += "\n```\n";
|
|
20553
|
+
}
|
|
20554
|
+
}
|
|
20555
|
+
return result;
|
|
20556
|
+
}
|
|
20557
|
+
|
|
20118
20558
|
// src/index.ts
|
|
20119
20559
|
var server = new McpServer({
|
|
20120
20560
|
name: "@netpad/mcp-server",
|
|
@@ -20213,24 +20653,131 @@ server.resource(
|
|
|
20213
20653
|
);
|
|
20214
20654
|
server.tool(
|
|
20215
20655
|
"generate_form",
|
|
20216
|
-
"Generate a complete NetPad form configuration from a description.
|
|
20656
|
+
"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
20657
|
{
|
|
20218
20658
|
description: external_exports.string().describe("Natural language description of the form to generate"),
|
|
20219
20659
|
formName: external_exports.string().describe("Name of the form"),
|
|
20220
20660
|
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")
|
|
20661
|
+
includeTheme: external_exports.boolean().optional().describe("Whether to include theme configuration"),
|
|
20662
|
+
outputFormat: external_exports.enum(["typescript", "json"]).optional().describe('Output format: "typescript" (default) for complete working code, "json" for raw config')
|
|
20222
20663
|
},
|
|
20223
|
-
async ({ description, formName, includeMultiPage, includeTheme }) => {
|
|
20664
|
+
async ({ description, formName, includeMultiPage, includeTheme, outputFormat = "typescript" }) => {
|
|
20224
20665
|
const schema = generateFormSchema(description, formName, {
|
|
20225
20666
|
multiPage: includeMultiPage,
|
|
20226
20667
|
theme: includeTheme
|
|
20227
20668
|
});
|
|
20228
|
-
|
|
20229
|
-
|
|
20230
|
-
|
|
20231
|
-
|
|
20232
|
-
|
|
20233
|
-
|
|
20669
|
+
if (outputFormat === "json") {
|
|
20670
|
+
return {
|
|
20671
|
+
content: [
|
|
20672
|
+
{
|
|
20673
|
+
type: "text",
|
|
20674
|
+
text: JSON.stringify(schema, null, 2)
|
|
20675
|
+
}
|
|
20676
|
+
]
|
|
20677
|
+
};
|
|
20678
|
+
}
|
|
20679
|
+
const slug = formName.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
20680
|
+
const configCode = `export const formConfig: FormConfig = ${JSON.stringify({
|
|
20681
|
+
...schema,
|
|
20682
|
+
slug
|
|
20683
|
+
}, null, 2)};`;
|
|
20684
|
+
const functionsCode = `/**
|
|
20685
|
+
* Submit form data to the NetPad API.
|
|
20686
|
+
*/
|
|
20687
|
+
export async function submitForm(data: Record<string, unknown>): Promise<SubmitResult> {
|
|
20688
|
+
try {
|
|
20689
|
+
const response = await fetch(
|
|
20690
|
+
\`\${CONFIG.baseUrl}/api/forms/\${formConfig.slug}/submit\`,
|
|
20691
|
+
{
|
|
20692
|
+
method: 'POST',
|
|
20693
|
+
headers: {
|
|
20694
|
+
'Content-Type': 'application/json',
|
|
20695
|
+
'Authorization': \`Bearer \${CONFIG.apiKey}\`,
|
|
20696
|
+
},
|
|
20697
|
+
body: JSON.stringify({ data }),
|
|
20698
|
+
}
|
|
20699
|
+
);
|
|
20700
|
+
|
|
20701
|
+
if (!response.ok) {
|
|
20702
|
+
const error = await response.text();
|
|
20703
|
+
return { success: false, error };
|
|
20704
|
+
}
|
|
20705
|
+
|
|
20706
|
+
const result = await response.json() as { submissionId: string };
|
|
20707
|
+
return { success: true, submissionId: result.submissionId };
|
|
20708
|
+
} catch (error) {
|
|
20709
|
+
return {
|
|
20710
|
+
success: false,
|
|
20711
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
20712
|
+
};
|
|
20713
|
+
}
|
|
20714
|
+
}
|
|
20715
|
+
|
|
20716
|
+
/**
|
|
20717
|
+
* Create the form in NetPad (run once to set up).
|
|
20718
|
+
*/
|
|
20719
|
+
export async function createForm(): Promise<{ formId: string } | { error: string }> {
|
|
20720
|
+
try {
|
|
20721
|
+
const response = await fetch(\`\${CONFIG.baseUrl}/api/forms\`, {
|
|
20722
|
+
method: 'POST',
|
|
20723
|
+
headers: {
|
|
20724
|
+
'Content-Type': 'application/json',
|
|
20725
|
+
'Authorization': \`Bearer \${CONFIG.apiKey}\`,
|
|
20726
|
+
},
|
|
20727
|
+
body: JSON.stringify({
|
|
20728
|
+
...formConfig,
|
|
20729
|
+
organizationId: CONFIG.organizationId,
|
|
20730
|
+
projectId: CONFIG.projectId,
|
|
20731
|
+
}),
|
|
20732
|
+
});
|
|
20733
|
+
|
|
20734
|
+
if (!response.ok) {
|
|
20735
|
+
return { error: await response.text() };
|
|
20736
|
+
}
|
|
20737
|
+
|
|
20738
|
+
const result = await response.json() as { form: { formId: string } };
|
|
20739
|
+
return { formId: result.form.formId };
|
|
20740
|
+
} catch (error) {
|
|
20741
|
+
return { error: error instanceof Error ? error.message : 'Unknown error' };
|
|
20742
|
+
}
|
|
20743
|
+
}`;
|
|
20744
|
+
const mainCode = `// Example usage
|
|
20745
|
+
async function main() {
|
|
20746
|
+
console.log('Creating form:', formConfig.name);
|
|
20747
|
+
|
|
20748
|
+
// Create the form (run once)
|
|
20749
|
+
const createResult = await createForm();
|
|
20750
|
+
if ('error' in createResult) {
|
|
20751
|
+
console.error('Failed to create form:', createResult.error);
|
|
20752
|
+
return;
|
|
20753
|
+
}
|
|
20754
|
+
|
|
20755
|
+
console.log('\u2705 Form created with ID:', createResult.formId);
|
|
20756
|
+
console.log('Form slug:', formConfig.slug);
|
|
20757
|
+
}
|
|
20758
|
+
|
|
20759
|
+
// Uncomment to run:
|
|
20760
|
+
// main().catch(console.error);
|
|
20761
|
+
`;
|
|
20762
|
+
const code = generateSelfContainedCode({
|
|
20763
|
+
title: formName,
|
|
20764
|
+
description: schema.description,
|
|
20765
|
+
includeFormTypes: true,
|
|
20766
|
+
configCode,
|
|
20767
|
+
functionsCode,
|
|
20768
|
+
mainCode
|
|
20769
|
+
});
|
|
20770
|
+
const output = createToolOutput({
|
|
20771
|
+
code,
|
|
20772
|
+
filename: `${slug}.ts`,
|
|
20773
|
+
envVars: STANDARD_ENV_VARS
|
|
20774
|
+
});
|
|
20775
|
+
return {
|
|
20776
|
+
content: [
|
|
20777
|
+
{
|
|
20778
|
+
type: "text",
|
|
20779
|
+
text: formatToolOutput(output)
|
|
20780
|
+
}
|
|
20234
20781
|
]
|
|
20235
20782
|
};
|
|
20236
20783
|
}
|
|
@@ -20381,9 +20928,440 @@ server.tool(
|
|
|
20381
20928
|
}
|
|
20382
20929
|
}
|
|
20383
20930
|
);
|
|
20931
|
+
server.tool(
|
|
20932
|
+
"get_reference",
|
|
20933
|
+
"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.",
|
|
20934
|
+
{
|
|
20935
|
+
type: external_exports.enum([
|
|
20936
|
+
"field_types",
|
|
20937
|
+
"operators",
|
|
20938
|
+
"formula_functions",
|
|
20939
|
+
"validation_options",
|
|
20940
|
+
"theme_options",
|
|
20941
|
+
"documentation"
|
|
20942
|
+
]).describe("The type of reference to retrieve"),
|
|
20943
|
+
category: external_exports.string().optional().describe('Filter by category (for field_types: "text", "selection", "date"; for formula_functions: "math", "string", "date")'),
|
|
20944
|
+
topic: external_exports.enum(["readme", "architecture", "quick-start", "examples", "api-client"]).optional().describe('Documentation topic (required when type is "documentation")')
|
|
20945
|
+
},
|
|
20946
|
+
async ({ type, category, topic }) => {
|
|
20947
|
+
switch (type) {
|
|
20948
|
+
case "field_types": {
|
|
20949
|
+
let types = FIELD_TYPES;
|
|
20950
|
+
if (category) {
|
|
20951
|
+
types = types.filter((t) => t.category.toLowerCase() === category.toLowerCase());
|
|
20952
|
+
}
|
|
20953
|
+
return {
|
|
20954
|
+
content: [{
|
|
20955
|
+
type: "text",
|
|
20956
|
+
text: JSON.stringify({
|
|
20957
|
+
referenceType: "field_types",
|
|
20958
|
+
count: types.length,
|
|
20959
|
+
categories: [...new Set(FIELD_TYPES.map((t) => t.category))],
|
|
20960
|
+
data: types
|
|
20961
|
+
}, null, 2)
|
|
20962
|
+
}]
|
|
20963
|
+
};
|
|
20964
|
+
}
|
|
20965
|
+
case "operators": {
|
|
20966
|
+
return {
|
|
20967
|
+
content: [{
|
|
20968
|
+
type: "text",
|
|
20969
|
+
text: JSON.stringify({
|
|
20970
|
+
referenceType: "operators",
|
|
20971
|
+
count: OPERATORS.length,
|
|
20972
|
+
data: OPERATORS
|
|
20973
|
+
}, null, 2)
|
|
20974
|
+
}]
|
|
20975
|
+
};
|
|
20976
|
+
}
|
|
20977
|
+
case "formula_functions": {
|
|
20978
|
+
let functions = FORMULA_FUNCTIONS;
|
|
20979
|
+
if (category) {
|
|
20980
|
+
functions = functions.filter((f) => f.category.toLowerCase() === category.toLowerCase());
|
|
20981
|
+
}
|
|
20982
|
+
return {
|
|
20983
|
+
content: [{
|
|
20984
|
+
type: "text",
|
|
20985
|
+
text: JSON.stringify({
|
|
20986
|
+
referenceType: "formula_functions",
|
|
20987
|
+
count: functions.length,
|
|
20988
|
+
categories: [...new Set(FORMULA_FUNCTIONS.map((f) => f.category))],
|
|
20989
|
+
data: functions
|
|
20990
|
+
}, null, 2)
|
|
20991
|
+
}]
|
|
20992
|
+
};
|
|
20993
|
+
}
|
|
20994
|
+
case "validation_options": {
|
|
20995
|
+
return {
|
|
20996
|
+
content: [{
|
|
20997
|
+
type: "text",
|
|
20998
|
+
text: JSON.stringify({
|
|
20999
|
+
referenceType: "validation_options",
|
|
21000
|
+
data: VALIDATION_OPTIONS
|
|
21001
|
+
}, null, 2)
|
|
21002
|
+
}]
|
|
21003
|
+
};
|
|
21004
|
+
}
|
|
21005
|
+
case "theme_options": {
|
|
21006
|
+
return {
|
|
21007
|
+
content: [{
|
|
21008
|
+
type: "text",
|
|
21009
|
+
text: JSON.stringify({
|
|
21010
|
+
referenceType: "theme_options",
|
|
21011
|
+
data: THEME_OPTIONS
|
|
21012
|
+
}, null, 2)
|
|
21013
|
+
}]
|
|
21014
|
+
};
|
|
21015
|
+
}
|
|
21016
|
+
case "documentation": {
|
|
21017
|
+
const docs = {
|
|
21018
|
+
"readme": DOCUMENTATION.readme,
|
|
21019
|
+
"architecture": ARCHITECTURE_GUIDE,
|
|
21020
|
+
"quick-start": QUICK_START_GUIDE,
|
|
21021
|
+
"examples": EXAMPLES,
|
|
21022
|
+
"api-client": DOCUMENTATION.apiClient
|
|
21023
|
+
};
|
|
21024
|
+
const selectedTopic = topic || "readme";
|
|
21025
|
+
return {
|
|
21026
|
+
content: [{
|
|
21027
|
+
type: "text",
|
|
21028
|
+
text: `# NetPad Documentation: ${selectedTopic}
|
|
21029
|
+
|
|
21030
|
+
${docs[selectedTopic] || "Documentation not found"}
|
|
21031
|
+
|
|
21032
|
+
---
|
|
21033
|
+
Available topics: ${Object.keys(docs).join(", ")}`
|
|
21034
|
+
}]
|
|
21035
|
+
};
|
|
21036
|
+
}
|
|
21037
|
+
default:
|
|
21038
|
+
return {
|
|
21039
|
+
content: [{
|
|
21040
|
+
type: "text",
|
|
21041
|
+
text: "Invalid reference type. Use: field_types, operators, formula_functions, validation_options, theme_options, or documentation"
|
|
21042
|
+
}]
|
|
21043
|
+
};
|
|
21044
|
+
}
|
|
21045
|
+
}
|
|
21046
|
+
);
|
|
21047
|
+
server.tool(
|
|
21048
|
+
"browse_templates",
|
|
21049
|
+
"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.",
|
|
21050
|
+
{
|
|
21051
|
+
templateType: external_exports.enum([
|
|
21052
|
+
"form",
|
|
21053
|
+
"application",
|
|
21054
|
+
"workflow",
|
|
21055
|
+
"conversational",
|
|
21056
|
+
"query",
|
|
21057
|
+
"use_case",
|
|
21058
|
+
"all"
|
|
21059
|
+
]).describe("Type of templates to browse"),
|
|
21060
|
+
action: external_exports.enum(["list", "get", "categories"]).optional().describe('Action: "list" (default) returns summaries, "get" returns full details, "categories" lists available categories'),
|
|
21061
|
+
templateId: external_exports.string().optional().describe('Template ID (required when action is "get")'),
|
|
21062
|
+
category: external_exports.string().optional().describe("Filter templates by category"),
|
|
21063
|
+
search: external_exports.string().optional().describe("Search templates by name, description, or tags (form templates only)")
|
|
21064
|
+
},
|
|
21065
|
+
async ({ templateType, action = "list", templateId, category, search }) => {
|
|
21066
|
+
const formatSummary = (id, name, desc, cat, extra = {}) => ({
|
|
21067
|
+
id,
|
|
21068
|
+
name,
|
|
21069
|
+
description: desc,
|
|
21070
|
+
category: cat,
|
|
21071
|
+
...extra
|
|
21072
|
+
});
|
|
21073
|
+
switch (templateType) {
|
|
21074
|
+
case "form": {
|
|
21075
|
+
if (action === "categories") {
|
|
21076
|
+
return {
|
|
21077
|
+
content: [{
|
|
21078
|
+
type: "text",
|
|
21079
|
+
text: JSON.stringify({
|
|
21080
|
+
templateType: "form",
|
|
21081
|
+
categories: TEMPLATE_CATEGORIES,
|
|
21082
|
+
totalTemplates: Object.keys(FORM_TEMPLATES).length
|
|
21083
|
+
}, null, 2)
|
|
21084
|
+
}]
|
|
21085
|
+
};
|
|
21086
|
+
}
|
|
21087
|
+
if (action === "get" && templateId) {
|
|
21088
|
+
const template = getTemplateById(templateId);
|
|
21089
|
+
if (!template) {
|
|
21090
|
+
return {
|
|
21091
|
+
content: [{
|
|
21092
|
+
type: "text",
|
|
21093
|
+
text: JSON.stringify({
|
|
21094
|
+
error: `Form template "${templateId}" not found`,
|
|
21095
|
+
availableTemplates: Object.keys(FORM_TEMPLATES)
|
|
21096
|
+
}, null, 2)
|
|
21097
|
+
}]
|
|
21098
|
+
};
|
|
21099
|
+
}
|
|
21100
|
+
return { content: [{ type: "text", text: JSON.stringify(template, null, 2) }] };
|
|
21101
|
+
}
|
|
21102
|
+
let templates;
|
|
21103
|
+
if (search) {
|
|
21104
|
+
templates = searchTemplates(search);
|
|
21105
|
+
} else if (category) {
|
|
21106
|
+
templates = getTemplatesByCategory(category);
|
|
21107
|
+
} else {
|
|
21108
|
+
templates = Object.values(FORM_TEMPLATES);
|
|
21109
|
+
}
|
|
21110
|
+
const summary = templates.map((t) => formatSummary(t.id, t.name, t.description, t.category, {
|
|
21111
|
+
tags: t.tags,
|
|
21112
|
+
icon: t.icon,
|
|
21113
|
+
fieldCount: t.fields.length,
|
|
21114
|
+
hasMultiPage: !!t.multiPage?.enabled
|
|
21115
|
+
}));
|
|
21116
|
+
return {
|
|
21117
|
+
content: [{
|
|
21118
|
+
type: "text",
|
|
21119
|
+
text: JSON.stringify({
|
|
21120
|
+
templateType: "form",
|
|
21121
|
+
templates: summary,
|
|
21122
|
+
total: summary.length,
|
|
21123
|
+
categories: [...new Set(templates.map((t) => t.category))]
|
|
21124
|
+
}, null, 2)
|
|
21125
|
+
}]
|
|
21126
|
+
};
|
|
21127
|
+
}
|
|
21128
|
+
case "application": {
|
|
21129
|
+
if (action === "categories") {
|
|
21130
|
+
return {
|
|
21131
|
+
content: [{
|
|
21132
|
+
type: "text",
|
|
21133
|
+
text: JSON.stringify({
|
|
21134
|
+
templateType: "application",
|
|
21135
|
+
categories: [...new Set(Object.values(APPLICATION_TEMPLATES).map((t) => t.category))],
|
|
21136
|
+
totalTemplates: Object.keys(APPLICATION_TEMPLATES).length
|
|
21137
|
+
}, null, 2)
|
|
21138
|
+
}]
|
|
21139
|
+
};
|
|
21140
|
+
}
|
|
21141
|
+
if (action === "get" && templateId) {
|
|
21142
|
+
const template = APPLICATION_TEMPLATES[templateId];
|
|
21143
|
+
if (!template) {
|
|
21144
|
+
return {
|
|
21145
|
+
content: [{
|
|
21146
|
+
type: "text",
|
|
21147
|
+
text: JSON.stringify({
|
|
21148
|
+
error: `Application template "${templateId}" not found`,
|
|
21149
|
+
availableTemplates: Object.keys(APPLICATION_TEMPLATES)
|
|
21150
|
+
}, null, 2)
|
|
21151
|
+
}]
|
|
21152
|
+
};
|
|
21153
|
+
}
|
|
21154
|
+
return { content: [{ type: "text", text: JSON.stringify(template, null, 2) }] };
|
|
21155
|
+
}
|
|
21156
|
+
let templates = Object.values(APPLICATION_TEMPLATES);
|
|
21157
|
+
if (category) {
|
|
21158
|
+
templates = templates.filter((t) => t.category.toLowerCase() === category.toLowerCase());
|
|
21159
|
+
}
|
|
21160
|
+
const summary = templates.map((t) => formatSummary(t.id, t.name, t.description, t.category, {
|
|
21161
|
+
tags: t.tags,
|
|
21162
|
+
formsCount: t.structure.forms.length,
|
|
21163
|
+
workflowsCount: t.structure.workflows.length
|
|
21164
|
+
}));
|
|
21165
|
+
return {
|
|
21166
|
+
content: [{
|
|
21167
|
+
type: "text",
|
|
21168
|
+
text: JSON.stringify({
|
|
21169
|
+
templateType: "application",
|
|
21170
|
+
templates: summary,
|
|
21171
|
+
total: summary.length,
|
|
21172
|
+
categories: [...new Set(Object.values(APPLICATION_TEMPLATES).map((t) => t.category))]
|
|
21173
|
+
}, null, 2)
|
|
21174
|
+
}]
|
|
21175
|
+
};
|
|
21176
|
+
}
|
|
21177
|
+
case "workflow": {
|
|
21178
|
+
if (action === "categories") {
|
|
21179
|
+
return {
|
|
21180
|
+
content: [{
|
|
21181
|
+
type: "text",
|
|
21182
|
+
text: JSON.stringify({
|
|
21183
|
+
templateType: "workflow",
|
|
21184
|
+
categories: [...new Set(Object.values(WORKFLOW_TEMPLATES).map((t) => t.category))],
|
|
21185
|
+
totalTemplates: Object.keys(WORKFLOW_TEMPLATES).length
|
|
21186
|
+
}, null, 2)
|
|
21187
|
+
}]
|
|
21188
|
+
};
|
|
21189
|
+
}
|
|
21190
|
+
if (action === "get" && templateId) {
|
|
21191
|
+
const template = WORKFLOW_TEMPLATES[templateId];
|
|
21192
|
+
if (!template) {
|
|
21193
|
+
return {
|
|
21194
|
+
content: [{
|
|
21195
|
+
type: "text",
|
|
21196
|
+
text: JSON.stringify({
|
|
21197
|
+
error: `Workflow template "${templateId}" not found`,
|
|
21198
|
+
availableTemplates: Object.keys(WORKFLOW_TEMPLATES)
|
|
21199
|
+
}, null, 2)
|
|
21200
|
+
}]
|
|
21201
|
+
};
|
|
21202
|
+
}
|
|
21203
|
+
return { content: [{ type: "text", text: JSON.stringify(template, null, 2) }] };
|
|
21204
|
+
}
|
|
21205
|
+
let templates = Object.values(WORKFLOW_TEMPLATES);
|
|
21206
|
+
if (category) {
|
|
21207
|
+
templates = templates.filter((t) => t.category.toLowerCase() === category.toLowerCase());
|
|
21208
|
+
}
|
|
21209
|
+
const summary = templates.map((t) => formatSummary(t.id, t.name, t.description, t.category, {
|
|
21210
|
+
tags: t.tags,
|
|
21211
|
+
nodesCount: t.nodes.length,
|
|
21212
|
+
edgesCount: t.edges.length
|
|
21213
|
+
}));
|
|
21214
|
+
return {
|
|
21215
|
+
content: [{
|
|
21216
|
+
type: "text",
|
|
21217
|
+
text: JSON.stringify({
|
|
21218
|
+
templateType: "workflow",
|
|
21219
|
+
templates: summary,
|
|
21220
|
+
total: summary.length,
|
|
21221
|
+
categories: [...new Set(Object.values(WORKFLOW_TEMPLATES).map((t) => t.category))]
|
|
21222
|
+
}, null, 2)
|
|
21223
|
+
}]
|
|
21224
|
+
};
|
|
21225
|
+
}
|
|
21226
|
+
case "conversational": {
|
|
21227
|
+
if (action === "categories") {
|
|
21228
|
+
return {
|
|
21229
|
+
content: [{
|
|
21230
|
+
type: "text",
|
|
21231
|
+
text: JSON.stringify({
|
|
21232
|
+
templateType: "conversational",
|
|
21233
|
+
categories: [...new Set(Object.values(CONVERSATIONAL_TEMPLATES).map((t) => t.category))],
|
|
21234
|
+
totalTemplates: Object.keys(CONVERSATIONAL_TEMPLATES).length
|
|
21235
|
+
}, null, 2)
|
|
21236
|
+
}]
|
|
21237
|
+
};
|
|
21238
|
+
}
|
|
21239
|
+
if (action === "get" && templateId) {
|
|
21240
|
+
const template = CONVERSATIONAL_TEMPLATES[templateId];
|
|
21241
|
+
if (!template) {
|
|
21242
|
+
return {
|
|
21243
|
+
content: [{
|
|
21244
|
+
type: "text",
|
|
21245
|
+
text: JSON.stringify({
|
|
21246
|
+
error: `Conversational template "${templateId}" not found`,
|
|
21247
|
+
availableTemplates: Object.keys(CONVERSATIONAL_TEMPLATES)
|
|
21248
|
+
}, null, 2)
|
|
21249
|
+
}]
|
|
21250
|
+
};
|
|
21251
|
+
}
|
|
21252
|
+
return { content: [{ type: "text", text: JSON.stringify(template, null, 2) }] };
|
|
21253
|
+
}
|
|
21254
|
+
let templates = Object.values(CONVERSATIONAL_TEMPLATES);
|
|
21255
|
+
if (category) {
|
|
21256
|
+
templates = templates.filter((t) => t.category.toLowerCase() === category.toLowerCase());
|
|
21257
|
+
}
|
|
21258
|
+
const summary = templates.map((t) => formatSummary(t.id, t.name, t.description, t.category, {
|
|
21259
|
+
tags: t.tags,
|
|
21260
|
+
topicsCount: t.defaultConfig.topics.length,
|
|
21261
|
+
extractionFieldsCount: t.defaultConfig.extractionSchema.length
|
|
21262
|
+
}));
|
|
21263
|
+
return {
|
|
21264
|
+
content: [{
|
|
21265
|
+
type: "text",
|
|
21266
|
+
text: JSON.stringify({
|
|
21267
|
+
templateType: "conversational",
|
|
21268
|
+
templates: summary,
|
|
21269
|
+
total: summary.length,
|
|
21270
|
+
categories: [...new Set(Object.values(CONVERSATIONAL_TEMPLATES).map((t) => t.category))]
|
|
21271
|
+
}, null, 2)
|
|
21272
|
+
}]
|
|
21273
|
+
};
|
|
21274
|
+
}
|
|
21275
|
+
case "query": {
|
|
21276
|
+
if (action === "get" && templateId) {
|
|
21277
|
+
const template = getQueryTemplate(templateId);
|
|
21278
|
+
if (!template) {
|
|
21279
|
+
return {
|
|
21280
|
+
content: [{
|
|
21281
|
+
type: "text",
|
|
21282
|
+
text: JSON.stringify({
|
|
21283
|
+
error: `Query template "${templateId}" not found`,
|
|
21284
|
+
availableTemplates: listQueryTemplates()
|
|
21285
|
+
}, null, 2)
|
|
21286
|
+
}]
|
|
21287
|
+
};
|
|
21288
|
+
}
|
|
21289
|
+
return { content: [{ type: "text", text: JSON.stringify(template, null, 2) }] };
|
|
21290
|
+
}
|
|
21291
|
+
const templates = listQueryTemplates();
|
|
21292
|
+
return {
|
|
21293
|
+
content: [{
|
|
21294
|
+
type: "text",
|
|
21295
|
+
text: JSON.stringify({
|
|
21296
|
+
templateType: "query",
|
|
21297
|
+
templates,
|
|
21298
|
+
total: templates.length
|
|
21299
|
+
}, null, 2)
|
|
21300
|
+
}]
|
|
21301
|
+
};
|
|
21302
|
+
}
|
|
21303
|
+
case "use_case": {
|
|
21304
|
+
if (action === "get" && templateId) {
|
|
21305
|
+
const template = USE_CASE_TEMPLATES[templateId];
|
|
21306
|
+
if (!template) {
|
|
21307
|
+
return {
|
|
21308
|
+
content: [{
|
|
21309
|
+
type: "text",
|
|
21310
|
+
text: JSON.stringify({
|
|
21311
|
+
error: `Use case template "${templateId}" not found`,
|
|
21312
|
+
availableTemplates: Object.keys(USE_CASE_TEMPLATES)
|
|
21313
|
+
}, null, 2)
|
|
21314
|
+
}]
|
|
21315
|
+
};
|
|
21316
|
+
}
|
|
21317
|
+
return { content: [{ type: "text", text: JSON.stringify(template, null, 2) }] };
|
|
21318
|
+
}
|
|
21319
|
+
const useCaseIds = Object.keys(USE_CASE_TEMPLATES);
|
|
21320
|
+
return {
|
|
21321
|
+
content: [{
|
|
21322
|
+
type: "text",
|
|
21323
|
+
text: JSON.stringify({
|
|
21324
|
+
templateType: "use_case",
|
|
21325
|
+
templates: useCaseIds,
|
|
21326
|
+
total: useCaseIds.length,
|
|
21327
|
+
note: 'Use action="get" with templateId to retrieve full template details'
|
|
21328
|
+
}, null, 2)
|
|
21329
|
+
}]
|
|
21330
|
+
};
|
|
21331
|
+
}
|
|
21332
|
+
case "all": {
|
|
21333
|
+
return {
|
|
21334
|
+
content: [{
|
|
21335
|
+
type: "text",
|
|
21336
|
+
text: JSON.stringify({
|
|
21337
|
+
overview: "NetPad Template Catalog",
|
|
21338
|
+
templateTypes: [
|
|
21339
|
+
{ type: "form", count: Object.keys(FORM_TEMPLATES).length, categories: TEMPLATE_CATEGORIES.length, description: "Pre-built form configurations with fields, validation, and styling" },
|
|
21340
|
+
{ type: "application", count: Object.keys(APPLICATION_TEMPLATES).length, description: "Complete applications with forms, workflows, and settings" },
|
|
21341
|
+
{ type: "workflow", count: Object.keys(WORKFLOW_TEMPLATES).length, description: "Automated workflow patterns with triggers, conditions, and actions" },
|
|
21342
|
+
{ type: "conversational", count: Object.keys(CONVERSATIONAL_TEMPLATES).length, description: "AI-powered conversational form templates" },
|
|
21343
|
+
{ type: "query", count: listQueryTemplates().length, description: "MongoDB query patterns for common operations" },
|
|
21344
|
+
{ type: "use_case", count: Object.keys(USE_CASE_TEMPLATES).length, description: "Industry use case blueprints" }
|
|
21345
|
+
],
|
|
21346
|
+
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,
|
|
21347
|
+
usage: 'Use templateType to filter, action="get" with templateId to get details, or action="categories" to see available categories'
|
|
21348
|
+
}, null, 2)
|
|
21349
|
+
}]
|
|
21350
|
+
};
|
|
21351
|
+
}
|
|
21352
|
+
default:
|
|
21353
|
+
return {
|
|
21354
|
+
content: [{
|
|
21355
|
+
type: "text",
|
|
21356
|
+
text: "Invalid templateType. Use: form, application, workflow, conversational, query, use_case, or all"
|
|
21357
|
+
}]
|
|
21358
|
+
};
|
|
21359
|
+
}
|
|
21360
|
+
}
|
|
21361
|
+
);
|
|
20384
21362
|
server.tool(
|
|
20385
21363
|
"list_field_types",
|
|
20386
|
-
"List all supported field types in @netpad/forms with their descriptions and usage.
|
|
21364
|
+
'[DEPRECATED - use get_reference with type="field_types" instead] List all supported field types in @netpad/forms with their descriptions and usage.',
|
|
20387
21365
|
{
|
|
20388
21366
|
category: external_exports.string().optional().describe('Filter by category (e.g., "text", "selection", "date")')
|
|
20389
21367
|
},
|
|
@@ -20396,7 +21374,9 @@ server.tool(
|
|
|
20396
21374
|
content: [
|
|
20397
21375
|
{
|
|
20398
21376
|
type: "text",
|
|
20399
|
-
text:
|
|
21377
|
+
text: `DEPRECATED: This tool is deprecated. Use get_reference with type="field_types" instead.
|
|
21378
|
+
|
|
21379
|
+
${JSON.stringify(types, null, 2)}`
|
|
20400
21380
|
}
|
|
20401
21381
|
]
|
|
20402
21382
|
};
|
|
@@ -20404,14 +21384,16 @@ server.tool(
|
|
|
20404
21384
|
);
|
|
20405
21385
|
server.tool(
|
|
20406
21386
|
"list_operators",
|
|
20407
|
-
"List all available conditional logic operators with descriptions.
|
|
21387
|
+
'[DEPRECATED - use get_reference with type="operators" instead] List all available conditional logic operators with descriptions.',
|
|
20408
21388
|
{},
|
|
20409
21389
|
async () => {
|
|
20410
21390
|
return {
|
|
20411
21391
|
content: [
|
|
20412
21392
|
{
|
|
20413
21393
|
type: "text",
|
|
20414
|
-
text:
|
|
21394
|
+
text: `DEPRECATED: This tool is deprecated. Use get_reference with type="operators" instead.
|
|
21395
|
+
|
|
21396
|
+
${JSON.stringify(OPERATORS, null, 2)}`
|
|
20415
21397
|
}
|
|
20416
21398
|
]
|
|
20417
21399
|
};
|
|
@@ -20419,7 +21401,7 @@ server.tool(
|
|
|
20419
21401
|
);
|
|
20420
21402
|
server.tool(
|
|
20421
21403
|
"list_formula_functions",
|
|
20422
|
-
"List all available formula functions for computed fields.
|
|
21404
|
+
'[DEPRECATED - use get_reference with type="formula_functions" instead] List all available formula functions for computed fields.',
|
|
20423
21405
|
{
|
|
20424
21406
|
category: external_exports.string().optional().describe('Filter by category (e.g., "math", "string", "date")')
|
|
20425
21407
|
},
|
|
@@ -20432,7 +21414,9 @@ server.tool(
|
|
|
20432
21414
|
content: [
|
|
20433
21415
|
{
|
|
20434
21416
|
type: "text",
|
|
20435
|
-
text:
|
|
21417
|
+
text: `DEPRECATED: This tool is deprecated. Use get_reference with type="formula_functions" instead.
|
|
21418
|
+
|
|
21419
|
+
${JSON.stringify(functions, null, 2)}`
|
|
20436
21420
|
}
|
|
20437
21421
|
]
|
|
20438
21422
|
};
|
|
@@ -20440,14 +21424,16 @@ server.tool(
|
|
|
20440
21424
|
);
|
|
20441
21425
|
server.tool(
|
|
20442
21426
|
"list_validation_options",
|
|
20443
|
-
"List all available validation options for form fields.
|
|
21427
|
+
'[DEPRECATED - use get_reference with type="validation_options" instead] List all available validation options for form fields.',
|
|
20444
21428
|
{},
|
|
20445
21429
|
async () => {
|
|
20446
21430
|
return {
|
|
20447
21431
|
content: [
|
|
20448
21432
|
{
|
|
20449
21433
|
type: "text",
|
|
20450
|
-
text:
|
|
21434
|
+
text: `DEPRECATED: This tool is deprecated. Use get_reference with type="validation_options" instead.
|
|
21435
|
+
|
|
21436
|
+
${JSON.stringify(VALIDATION_OPTIONS, null, 2)}`
|
|
20451
21437
|
}
|
|
20452
21438
|
]
|
|
20453
21439
|
};
|
|
@@ -20455,14 +21441,16 @@ server.tool(
|
|
|
20455
21441
|
);
|
|
20456
21442
|
server.tool(
|
|
20457
21443
|
"list_theme_options",
|
|
20458
|
-
"List all available theme customization options.
|
|
21444
|
+
'[DEPRECATED - use get_reference with type="theme_options" instead] List all available theme customization options.',
|
|
20459
21445
|
{},
|
|
20460
21446
|
async () => {
|
|
20461
21447
|
return {
|
|
20462
21448
|
content: [
|
|
20463
21449
|
{
|
|
20464
21450
|
type: "text",
|
|
20465
|
-
text:
|
|
21451
|
+
text: `DEPRECATED: This tool is deprecated. Use get_reference with type="theme_options" instead.
|
|
21452
|
+
|
|
21453
|
+
${JSON.stringify(THEME_OPTIONS, null, 2)}`
|
|
20466
21454
|
}
|
|
20467
21455
|
]
|
|
20468
21456
|
};
|
|
@@ -20470,7 +21458,7 @@ server.tool(
|
|
|
20470
21458
|
);
|
|
20471
21459
|
server.tool(
|
|
20472
21460
|
"get_documentation",
|
|
20473
|
-
"Get NetPad forms documentation. Use this to learn about features, APIs, and best practices.
|
|
21461
|
+
'[DEPRECATED - use get_reference with type="documentation" instead] Get NetPad forms documentation. Use this to learn about features, APIs, and best practices.',
|
|
20474
21462
|
{
|
|
20475
21463
|
topic: external_exports.enum(["readme", "architecture", "quick-start", "examples", "api-client"]).describe("The documentation topic to retrieve")
|
|
20476
21464
|
},
|
|
@@ -20486,7 +21474,9 @@ server.tool(
|
|
|
20486
21474
|
content: [
|
|
20487
21475
|
{
|
|
20488
21476
|
type: "text",
|
|
20489
|
-
text:
|
|
21477
|
+
text: `DEPRECATED: This tool is deprecated. Use get_reference with type="documentation" and topic="${topic}" instead.
|
|
21478
|
+
|
|
21479
|
+
${docs[topic] || "Documentation not found"}`
|
|
20490
21480
|
}
|
|
20491
21481
|
]
|
|
20492
21482
|
};
|
|
@@ -20494,73 +21484,404 @@ server.tool(
|
|
|
20494
21484
|
);
|
|
20495
21485
|
server.tool(
|
|
20496
21486
|
"generate_react_code",
|
|
20497
|
-
"Generate React component
|
|
21487
|
+
"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
21488
|
{
|
|
20499
21489
|
formConfig: external_exports.string().describe("The form configuration JSON"),
|
|
20500
21490
|
componentName: external_exports.string().optional().describe("Name of the React component"),
|
|
20501
21491
|
includeSubmitHandler: external_exports.boolean().optional().describe("Whether to include a submit handler"),
|
|
20502
|
-
|
|
21492
|
+
includeApiSubmission: external_exports.boolean().optional().describe("Whether to submit to NetPad API via fetch (default: true)")
|
|
20503
21493
|
},
|
|
20504
|
-
async ({ formConfig, componentName = "MyForm", includeSubmitHandler = true,
|
|
20505
|
-
let
|
|
20506
|
-
|
|
20507
|
-
|
|
20508
|
-
|
|
20509
|
-
|
|
21494
|
+
async ({ formConfig, componentName = "MyForm", includeSubmitHandler = true, includeApiSubmission = true }) => {
|
|
21495
|
+
let parsedConfig;
|
|
21496
|
+
try {
|
|
21497
|
+
parsedConfig = JSON.parse(formConfig);
|
|
21498
|
+
} catch {
|
|
21499
|
+
parsedConfig = { name: componentName, slug: componentName.toLowerCase().replace(/\s+/g, "-") };
|
|
21500
|
+
}
|
|
21501
|
+
const formSlug = parsedConfig.slug || parsedConfig.name?.toLowerCase().replace(/\s+/g, "-") || "form";
|
|
21502
|
+
const code = `/**
|
|
21503
|
+
* ${componentName} - React Form Component
|
|
21504
|
+
* Generated by NetPad
|
|
21505
|
+
*
|
|
21506
|
+
* This is a self-contained component with inline types.
|
|
21507
|
+
* No @netpad/* imports required.
|
|
21508
|
+
*/
|
|
21509
|
+
|
|
21510
|
+
'use client';
|
|
21511
|
+
|
|
21512
|
+
import React, { useState, FormEvent, ChangeEvent } from 'react';
|
|
21513
|
+
|
|
21514
|
+
// ============================================================================
|
|
21515
|
+
// Types (inline - no external dependencies)
|
|
21516
|
+
// ============================================================================
|
|
21517
|
+
|
|
21518
|
+
interface FormFieldOption {
|
|
21519
|
+
label: string;
|
|
21520
|
+
value: string;
|
|
21521
|
+
}
|
|
21522
|
+
|
|
21523
|
+
interface FormFieldValidation {
|
|
21524
|
+
min?: number;
|
|
21525
|
+
max?: number;
|
|
21526
|
+
minLength?: number;
|
|
21527
|
+
maxLength?: number;
|
|
21528
|
+
pattern?: string;
|
|
21529
|
+
errorMessage?: string;
|
|
21530
|
+
}
|
|
21531
|
+
|
|
21532
|
+
interface FormField {
|
|
21533
|
+
path: string;
|
|
21534
|
+
label: string;
|
|
21535
|
+
type: string;
|
|
21536
|
+
included?: boolean;
|
|
21537
|
+
required?: boolean;
|
|
21538
|
+
placeholder?: string;
|
|
21539
|
+
helpText?: string;
|
|
21540
|
+
options?: FormFieldOption[];
|
|
21541
|
+
validation?: FormFieldValidation;
|
|
21542
|
+
fieldWidth?: 'full' | 'half' | 'third' | 'quarter';
|
|
21543
|
+
}
|
|
21544
|
+
|
|
21545
|
+
interface FormConfig {
|
|
21546
|
+
name: string;
|
|
21547
|
+
slug?: string;
|
|
21548
|
+
description?: string;
|
|
21549
|
+
fieldConfigs: FormField[];
|
|
21550
|
+
submitButtonText?: string;
|
|
21551
|
+
successMessage?: string;
|
|
21552
|
+
}
|
|
21553
|
+
|
|
21554
|
+
interface SubmitResult {
|
|
21555
|
+
success: boolean;
|
|
21556
|
+
submissionId?: string;
|
|
21557
|
+
error?: string;
|
|
21558
|
+
}
|
|
21559
|
+
|
|
21560
|
+
// ============================================================================
|
|
21561
|
+
// Configuration
|
|
21562
|
+
// ============================================================================
|
|
21563
|
+
|
|
21564
|
+
const CONFIG = {
|
|
21565
|
+
baseUrl: process.env.NEXT_PUBLIC_NETPAD_URL ?? 'https://api.netpad.io',
|
|
21566
|
+
apiKey: process.env.NEXT_PUBLIC_NETPAD_API_KEY ?? '',
|
|
21567
|
+
};
|
|
21568
|
+
|
|
21569
|
+
const formConfig: FormConfig = ${formConfig};
|
|
21570
|
+
|
|
21571
|
+
// ============================================================================
|
|
21572
|
+
// API Functions (using fetch - no SDK required)
|
|
21573
|
+
// ============================================================================
|
|
21574
|
+
|
|
21575
|
+
${includeApiSubmission ? `async function submitToApi(data: Record<string, unknown>): Promise<SubmitResult> {
|
|
21576
|
+
try {
|
|
21577
|
+
const response = await fetch(
|
|
21578
|
+
\`\${CONFIG.baseUrl}/api/forms/${formSlug}/submit\`,
|
|
21579
|
+
{
|
|
21580
|
+
method: 'POST',
|
|
21581
|
+
headers: {
|
|
21582
|
+
'Content-Type': 'application/json',
|
|
21583
|
+
'Authorization': \`Bearer \${CONFIG.apiKey}\`,
|
|
21584
|
+
},
|
|
21585
|
+
body: JSON.stringify({ data }),
|
|
21586
|
+
}
|
|
21587
|
+
);
|
|
21588
|
+
|
|
21589
|
+
if (!response.ok) {
|
|
21590
|
+
const errorText = await response.text();
|
|
21591
|
+
return { success: false, error: errorText };
|
|
20510
21592
|
}
|
|
20511
|
-
code += `import type { FormConfiguration } from '@netpad/forms';
|
|
20512
21593
|
|
|
20513
|
-
|
|
20514
|
-
|
|
21594
|
+
const result = await response.json() as { submissionId: string };
|
|
21595
|
+
return { success: true, submissionId: result.submissionId };
|
|
21596
|
+
} catch (error) {
|
|
21597
|
+
return {
|
|
21598
|
+
success: false,
|
|
21599
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
21600
|
+
};
|
|
21601
|
+
}
|
|
21602
|
+
}` : ""}
|
|
21603
|
+
|
|
21604
|
+
// ============================================================================
|
|
21605
|
+
// Component
|
|
21606
|
+
// ============================================================================
|
|
21607
|
+
|
|
21608
|
+
export function ${componentName}() {
|
|
21609
|
+
const [formData, setFormData] = useState<Record<string, string>>({});
|
|
21610
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
21611
|
+
const [submitResult, setSubmitResult] = useState<SubmitResult | null>(null);
|
|
21612
|
+
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
21613
|
+
|
|
21614
|
+
const handleChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
|
21615
|
+
const { name, value } = e.target;
|
|
21616
|
+
setFormData(prev => ({ ...prev, [name]: value }));
|
|
21617
|
+
// Clear error when field is edited
|
|
21618
|
+
if (errors[name]) {
|
|
21619
|
+
setErrors(prev => {
|
|
21620
|
+
const newErrors = { ...prev };
|
|
21621
|
+
delete newErrors[name];
|
|
21622
|
+
return newErrors;
|
|
21623
|
+
});
|
|
21624
|
+
}
|
|
21625
|
+
};
|
|
20515
21626
|
|
|
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
|
-
});
|
|
21627
|
+
const validateForm = (): boolean => {
|
|
21628
|
+
const newErrors: Record<string, string> = {};
|
|
20522
21629
|
|
|
20523
|
-
|
|
21630
|
+
for (const field of formConfig.fieldConfigs) {
|
|
21631
|
+
if (!field.included) continue;
|
|
21632
|
+
|
|
21633
|
+
const value = formData[field.path] || '';
|
|
21634
|
+
|
|
21635
|
+
if (field.required && !value) {
|
|
21636
|
+
newErrors[field.path] = \`\${field.label} is required\`;
|
|
21637
|
+
}
|
|
21638
|
+
|
|
21639
|
+
if (field.validation) {
|
|
21640
|
+
if (field.validation.minLength && value.length < field.validation.minLength) {
|
|
21641
|
+
newErrors[field.path] = field.validation.errorMessage || \`Minimum \${field.validation.minLength} characters required\`;
|
|
21642
|
+
}
|
|
21643
|
+
if (field.validation.maxLength && value.length > field.validation.maxLength) {
|
|
21644
|
+
newErrors[field.path] = field.validation.errorMessage || \`Maximum \${field.validation.maxLength} characters allowed\`;
|
|
21645
|
+
}
|
|
21646
|
+
if (field.validation.pattern && !new RegExp(field.validation.pattern).test(value)) {
|
|
21647
|
+
newErrors[field.path] = field.validation.errorMessage || 'Invalid format';
|
|
21648
|
+
}
|
|
21649
|
+
}
|
|
20524
21650
|
}
|
|
20525
|
-
|
|
20526
|
-
|
|
20527
|
-
|
|
20528
|
-
|
|
20529
|
-
|
|
21651
|
+
|
|
21652
|
+
setErrors(newErrors);
|
|
21653
|
+
return Object.keys(newErrors).length === 0;
|
|
21654
|
+
};
|
|
21655
|
+
|
|
21656
|
+
${includeSubmitHandler ? ` const handleSubmit = async (e: FormEvent) => {
|
|
21657
|
+
e.preventDefault();
|
|
21658
|
+
|
|
21659
|
+
if (!validateForm()) return;
|
|
21660
|
+
|
|
21661
|
+
setIsSubmitting(true);
|
|
21662
|
+
setSubmitResult(null);
|
|
21663
|
+
|
|
20530
21664
|
try {
|
|
20531
|
-
const result = await
|
|
20532
|
-
|
|
20533
|
-
|
|
21665
|
+
${includeApiSubmission ? ` const result = await submitToApi(formData);
|
|
21666
|
+
setSubmitResult(result);` : ` // TODO: Implement your submission logic
|
|
21667
|
+
console.log('Form data:', formData);
|
|
21668
|
+
setSubmitResult({ success: true });`}
|
|
20534
21669
|
} catch (error) {
|
|
20535
|
-
|
|
20536
|
-
|
|
21670
|
+
setSubmitResult({
|
|
21671
|
+
success: false,
|
|
21672
|
+
error: error instanceof Error ? error.message : 'Submission failed',
|
|
21673
|
+
});
|
|
21674
|
+
} finally {
|
|
21675
|
+
setIsSubmitting(false);
|
|
20537
21676
|
}
|
|
20538
|
-
}
|
|
21677
|
+
};` : ""}
|
|
20539
21678
|
|
|
20540
|
-
|
|
20541
|
-
|
|
20542
|
-
|
|
20543
|
-
|
|
20544
|
-
|
|
21679
|
+
const renderField = (field: FormField) => {
|
|
21680
|
+
if (!field.included) return null;
|
|
21681
|
+
|
|
21682
|
+
const commonProps = {
|
|
21683
|
+
id: field.path,
|
|
21684
|
+
name: field.path,
|
|
21685
|
+
value: formData[field.path] || '',
|
|
21686
|
+
onChange: handleChange,
|
|
21687
|
+
required: field.required,
|
|
21688
|
+
placeholder: field.placeholder,
|
|
21689
|
+
disabled: isSubmitting,
|
|
21690
|
+
className: \`form-field \${errors[field.path] ? 'error' : ''}\`,
|
|
21691
|
+
};
|
|
21692
|
+
|
|
21693
|
+
let input;
|
|
21694
|
+
|
|
21695
|
+
switch (field.type) {
|
|
21696
|
+
case 'long_text':
|
|
21697
|
+
input = <textarea {...commonProps} rows={4} />;
|
|
21698
|
+
break;
|
|
21699
|
+
case 'email':
|
|
21700
|
+
input = <input {...commonProps} type="email" />;
|
|
21701
|
+
break;
|
|
21702
|
+
case 'number':
|
|
21703
|
+
input = <input {...commonProps} type="number" />;
|
|
21704
|
+
break;
|
|
21705
|
+
case 'phone':
|
|
21706
|
+
input = <input {...commonProps} type="tel" />;
|
|
21707
|
+
break;
|
|
21708
|
+
case 'date':
|
|
21709
|
+
input = <input {...commonProps} type="date" />;
|
|
21710
|
+
break;
|
|
21711
|
+
case 'dropdown':
|
|
21712
|
+
case 'select':
|
|
21713
|
+
input = (
|
|
21714
|
+
<select {...commonProps}>
|
|
21715
|
+
<option value="">Select...</option>
|
|
21716
|
+
{field.options?.map(opt => (
|
|
21717
|
+
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
|
21718
|
+
))}
|
|
21719
|
+
</select>
|
|
21720
|
+
);
|
|
21721
|
+
break;
|
|
21722
|
+
default:
|
|
21723
|
+
input = <input {...commonProps} type="text" />;
|
|
21724
|
+
}
|
|
21725
|
+
|
|
21726
|
+
return (
|
|
21727
|
+
<div key={field.path} className={\`form-group field-width-\${field.fieldWidth || 'full'}\`}>
|
|
21728
|
+
<label htmlFor={field.path}>
|
|
21729
|
+
{field.label}
|
|
21730
|
+
{field.required && <span className="required">*</span>}
|
|
21731
|
+
</label>
|
|
21732
|
+
{input}
|
|
21733
|
+
{field.helpText && <small className="help-text">{field.helpText}</small>}
|
|
21734
|
+
{errors[field.path] && <span className="error-message">{errors[field.path]}</span>}
|
|
21735
|
+
</div>
|
|
21736
|
+
);
|
|
20545
21737
|
};
|
|
20546
21738
|
|
|
20547
|
-
|
|
20548
|
-
|
|
20549
|
-
|
|
20550
|
-
|
|
20551
|
-
|
|
20552
|
-
|
|
20553
|
-
|
|
20554
|
-
|
|
20555
|
-
|
|
21739
|
+
if (submitResult?.success) {
|
|
21740
|
+
return (
|
|
21741
|
+
<div className="form-success">
|
|
21742
|
+
<h3>\u2713 {formConfig.successMessage || 'Thank you for your submission!'}</h3>
|
|
21743
|
+
{submitResult.submissionId && (
|
|
21744
|
+
<p>Confirmation ID: {submitResult.submissionId}</p>
|
|
21745
|
+
)}
|
|
21746
|
+
<button onClick={() => {
|
|
21747
|
+
setSubmitResult(null);
|
|
21748
|
+
setFormData({});
|
|
21749
|
+
}}>
|
|
21750
|
+
Submit Another
|
|
21751
|
+
</button>
|
|
21752
|
+
</div>
|
|
21753
|
+
);
|
|
21754
|
+
}
|
|
21755
|
+
|
|
21756
|
+
return (
|
|
21757
|
+
<div className="form-container">
|
|
21758
|
+
<h2>{formConfig.name}</h2>
|
|
21759
|
+
{formConfig.description && <p className="form-description">{formConfig.description}</p>}
|
|
21760
|
+
|
|
21761
|
+
<form onSubmit={${includeSubmitHandler ? "handleSubmit" : "(e) => e.preventDefault()"}}>
|
|
21762
|
+
{formConfig.fieldConfigs.map(renderField)}
|
|
21763
|
+
|
|
21764
|
+
{submitResult?.error && (
|
|
21765
|
+
<div className="form-error">
|
|
21766
|
+
<p>Error: {submitResult.error}</p>
|
|
21767
|
+
</div>
|
|
21768
|
+
)}
|
|
21769
|
+
|
|
21770
|
+
<button type="submit" disabled={isSubmitting} className="submit-button">
|
|
21771
|
+
{isSubmitting ? 'Submitting...' : (formConfig.submitButtonText || 'Submit')}
|
|
21772
|
+
</button>
|
|
21773
|
+
</form>
|
|
21774
|
+
|
|
21775
|
+
<style>{\`
|
|
21776
|
+
.form-container {
|
|
21777
|
+
max-width: 600px;
|
|
21778
|
+
margin: 0 auto;
|
|
21779
|
+
padding: 20px;
|
|
21780
|
+
}
|
|
21781
|
+
.form-group {
|
|
21782
|
+
margin-bottom: 16px;
|
|
21783
|
+
}
|
|
21784
|
+
.form-group label {
|
|
21785
|
+
display: block;
|
|
21786
|
+
margin-bottom: 4px;
|
|
21787
|
+
font-weight: 500;
|
|
21788
|
+
}
|
|
21789
|
+
.form-group input,
|
|
21790
|
+
.form-group textarea,
|
|
21791
|
+
.form-group select {
|
|
21792
|
+
width: 100%;
|
|
21793
|
+
padding: 8px 12px;
|
|
21794
|
+
border: 1px solid #ddd;
|
|
21795
|
+
border-radius: 4px;
|
|
21796
|
+
font-size: 14px;
|
|
21797
|
+
}
|
|
21798
|
+
.form-group input:focus,
|
|
21799
|
+
.form-group textarea:focus,
|
|
21800
|
+
.form-group select:focus {
|
|
21801
|
+
outline: none;
|
|
21802
|
+
border-color: #1976d2;
|
|
21803
|
+
}
|
|
21804
|
+
.form-group .error {
|
|
21805
|
+
border-color: #d32f2f;
|
|
21806
|
+
}
|
|
21807
|
+
.required {
|
|
21808
|
+
color: #d32f2f;
|
|
21809
|
+
margin-left: 4px;
|
|
21810
|
+
}
|
|
21811
|
+
.help-text {
|
|
21812
|
+
display: block;
|
|
21813
|
+
color: #666;
|
|
21814
|
+
margin-top: 4px;
|
|
21815
|
+
}
|
|
21816
|
+
.error-message {
|
|
21817
|
+
display: block;
|
|
21818
|
+
color: #d32f2f;
|
|
21819
|
+
font-size: 12px;
|
|
21820
|
+
margin-top: 4px;
|
|
21821
|
+
}
|
|
21822
|
+
.submit-button {
|
|
21823
|
+
background: #1976d2;
|
|
21824
|
+
color: white;
|
|
21825
|
+
padding: 12px 24px;
|
|
21826
|
+
border: none;
|
|
21827
|
+
border-radius: 4px;
|
|
21828
|
+
cursor: pointer;
|
|
21829
|
+
font-size: 16px;
|
|
21830
|
+
}
|
|
21831
|
+
.submit-button:hover {
|
|
21832
|
+
background: #1565c0;
|
|
21833
|
+
}
|
|
21834
|
+
.submit-button:disabled {
|
|
21835
|
+
background: #ccc;
|
|
21836
|
+
cursor: not-allowed;
|
|
21837
|
+
}
|
|
21838
|
+
.form-success {
|
|
21839
|
+
text-align: center;
|
|
21840
|
+
padding: 40px;
|
|
21841
|
+
}
|
|
21842
|
+
.form-error {
|
|
21843
|
+
background: #ffebee;
|
|
21844
|
+
color: #c62828;
|
|
21845
|
+
padding: 12px;
|
|
21846
|
+
border-radius: 4px;
|
|
21847
|
+
margin-bottom: 16px;
|
|
21848
|
+
}
|
|
21849
|
+
.field-width-half {
|
|
21850
|
+
display: inline-block;
|
|
21851
|
+
width: calc(50% - 8px);
|
|
21852
|
+
margin-right: 8px;
|
|
21853
|
+
}
|
|
21854
|
+
.field-width-third {
|
|
21855
|
+
display: inline-block;
|
|
21856
|
+
width: calc(33.33% - 8px);
|
|
21857
|
+
margin-right: 8px;
|
|
21858
|
+
}
|
|
21859
|
+
.field-width-quarter {
|
|
21860
|
+
display: inline-block;
|
|
21861
|
+
width: calc(25% - 8px);
|
|
21862
|
+
margin-right: 8px;
|
|
21863
|
+
}
|
|
21864
|
+
\`}</style>
|
|
21865
|
+
</div>
|
|
20556
21866
|
);
|
|
20557
21867
|
}
|
|
21868
|
+
|
|
21869
|
+
export default ${componentName};
|
|
20558
21870
|
`;
|
|
21871
|
+
const output = createToolOutput({
|
|
21872
|
+
code,
|
|
21873
|
+
filename: `${componentName}.tsx`,
|
|
21874
|
+
envVars: [
|
|
21875
|
+
{ name: "NEXT_PUBLIC_NETPAD_URL", description: "NetPad API URL (client-side accessible)", example: "https://api.netpad.io" },
|
|
21876
|
+
{ name: "NEXT_PUBLIC_NETPAD_API_KEY", description: "NetPad API key (client-side accessible)", example: "np_live_xxxxx" }
|
|
21877
|
+
],
|
|
21878
|
+
dependencies: ["react"]
|
|
21879
|
+
});
|
|
20559
21880
|
return {
|
|
20560
21881
|
content: [
|
|
20561
21882
|
{
|
|
20562
21883
|
type: "text",
|
|
20563
|
-
text:
|
|
21884
|
+
text: formatToolOutput(output)
|
|
20564
21885
|
}
|
|
20565
21886
|
]
|
|
20566
21887
|
};
|
|
@@ -20661,7 +21982,7 @@ server.tool(
|
|
|
20661
21982
|
);
|
|
20662
21983
|
server.tool(
|
|
20663
21984
|
"get_use_case_template",
|
|
20664
|
-
"Get a pre-built template for common form use cases including form configuration and workflow setup.
|
|
21985
|
+
'[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
21986
|
{
|
|
20666
21987
|
useCase: external_exports.enum(["leadCapture", "eventRegistration", "feedbackSurvey"]).describe("The use case template to retrieve")
|
|
20667
21988
|
},
|
|
@@ -21050,7 +22371,7 @@ ${context ? `
|
|
|
21050
22371
|
}
|
|
21051
22372
|
server.tool(
|
|
21052
22373
|
"list_application_templates",
|
|
21053
|
-
"List all available application templates for creating new NetPad applications. Templates include pre-configured forms, workflows, and settings.
|
|
22374
|
+
'[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
22375
|
{
|
|
21055
22376
|
category: external_exports.string().optional().describe('Filter by category (e.g., "lead-generation", "events", "surveys", "hr", "ecommerce")')
|
|
21056
22377
|
},
|
|
@@ -21082,7 +22403,7 @@ server.tool(
|
|
|
21082
22403
|
);
|
|
21083
22404
|
server.tool(
|
|
21084
22405
|
"get_application_template",
|
|
21085
|
-
"Get detailed information about a specific application template including its forms, workflows, and field configurations.
|
|
22406
|
+
'[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
22407
|
{
|
|
21087
22408
|
templateId: external_exports.enum(["contact-form", "lead-capture", "event-registration", "feedback-survey", "job-application", "order-form", "blank"]).describe("The template ID")
|
|
21088
22409
|
},
|
|
@@ -21106,7 +22427,7 @@ server.tool(
|
|
|
21106
22427
|
);
|
|
21107
22428
|
server.tool(
|
|
21108
22429
|
"create_application",
|
|
21109
|
-
"Generate
|
|
22430
|
+
"Generate a single, complete TypeScript file that creates a NetPad application with all forms and workflows. Run with `npx tsx` - no SDK required.",
|
|
21110
22431
|
{
|
|
21111
22432
|
name: external_exports.string().describe("Name of the application"),
|
|
21112
22433
|
description: external_exports.string().optional().describe("Description of the application"),
|
|
@@ -21119,20 +22440,264 @@ server.tool(
|
|
|
21119
22440
|
organizationId: external_exports.string().describe("Organization ID")
|
|
21120
22441
|
},
|
|
21121
22442
|
async (options) => {
|
|
21122
|
-
const
|
|
21123
|
-
const
|
|
21124
|
-
|
|
21125
|
-
|
|
21126
|
-
|
|
21127
|
-
|
|
22443
|
+
const { name, description, templateId, icon, color, tags, projectId, organizationId } = options;
|
|
22444
|
+
const slug = options.slug || name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
22445
|
+
const template = templateId ? APPLICATION_TEMPLATES[templateId] : null;
|
|
22446
|
+
const formConfigs = template?.structure.forms.map((form) => ({
|
|
22447
|
+
name: form.name,
|
|
22448
|
+
slug: form.slug,
|
|
22449
|
+
fieldConfigs: form.fields.map((f) => ({ ...f, included: true })),
|
|
22450
|
+
submitButtonText: "Submit",
|
|
22451
|
+
successMessage: "Thank you for your submission!"
|
|
22452
|
+
})) || [];
|
|
22453
|
+
const workflowConfigs = template?.structure.workflows.map((workflow) => ({
|
|
22454
|
+
name: workflow.name,
|
|
22455
|
+
description: `Triggered on ${workflow.trigger}`,
|
|
22456
|
+
nodes: [
|
|
22457
|
+
{
|
|
22458
|
+
id: "trigger_1",
|
|
22459
|
+
type: workflow.trigger === "form_submission" ? "form-trigger" : "manual-trigger",
|
|
22460
|
+
label: "Trigger",
|
|
22461
|
+
position: { x: 100, y: 200 },
|
|
22462
|
+
config: { formSlug: formConfigs[0]?.slug || "" },
|
|
22463
|
+
enabled: true
|
|
22464
|
+
},
|
|
22465
|
+
...workflow.steps.map((step, stepIdx) => ({
|
|
22466
|
+
id: `step_${stepIdx + 1}`,
|
|
22467
|
+
type: step.includes("email") ? "email-send" : step.includes("database") ? "mongodb-write" : "transform",
|
|
22468
|
+
label: step.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
22469
|
+
position: { x: 100 + (stepIdx + 1) * 250, y: 200 },
|
|
22470
|
+
config: {},
|
|
22471
|
+
enabled: true
|
|
22472
|
+
}))
|
|
22473
|
+
],
|
|
22474
|
+
edges: [
|
|
22475
|
+
{ id: "edge_1", source: "trigger_1", sourceHandle: "form_data", target: "step_1", targetHandle: "input" }
|
|
22476
|
+
]
|
|
22477
|
+
})) || [];
|
|
22478
|
+
const configCode = `// Application Configuration
|
|
22479
|
+
const APPLICATION_CONFIG = {
|
|
22480
|
+
name: ${JSON.stringify(name)},
|
|
22481
|
+
description: ${JSON.stringify(description || `Application created from ${templateId || "scratch"}`)},
|
|
22482
|
+
slug: ${JSON.stringify(slug)},
|
|
22483
|
+
icon: ${JSON.stringify(icon || "\u{1F4CB}")},
|
|
22484
|
+
color: ${JSON.stringify(color || "#00ED64")},
|
|
22485
|
+
tags: ${JSON.stringify(tags || [])},
|
|
22486
|
+
projectId: CONFIG.projectId,
|
|
22487
|
+
organizationId: CONFIG.organizationId,
|
|
22488
|
+
};
|
|
21128
22489
|
|
|
21129
|
-
|
|
22490
|
+
// Form Configurations
|
|
22491
|
+
const FORM_CONFIGS: FormConfig[] = ${JSON.stringify(formConfigs, null, 2)};
|
|
21130
22492
|
|
|
21131
|
-
|
|
22493
|
+
// Workflow Configurations
|
|
22494
|
+
const WORKFLOW_CONFIGS: WorkflowConfig[] = ${JSON.stringify(workflowConfigs, null, 2)};`;
|
|
22495
|
+
const functionsCode = `// ============================================================================
|
|
22496
|
+
// API Helper Functions
|
|
22497
|
+
// ============================================================================
|
|
21132
22498
|
|
|
21133
|
-
|
|
21134
|
-
|
|
21135
|
-
|
|
22499
|
+
async function apiCall<T>(
|
|
22500
|
+
endpoint: string,
|
|
22501
|
+
options: RequestInit = {}
|
|
22502
|
+
): Promise<{ success: boolean; data?: T; error?: string }> {
|
|
22503
|
+
try {
|
|
22504
|
+
const response = await fetch(\`\${CONFIG.baseUrl}\${endpoint}\`, {
|
|
22505
|
+
...options,
|
|
22506
|
+
headers: {
|
|
22507
|
+
'Content-Type': 'application/json',
|
|
22508
|
+
'Authorization': \`Bearer \${CONFIG.apiKey}\`,
|
|
22509
|
+
...options.headers,
|
|
22510
|
+
},
|
|
22511
|
+
});
|
|
22512
|
+
|
|
22513
|
+
if (!response.ok) {
|
|
22514
|
+
const error = await response.text();
|
|
22515
|
+
return { success: false, error };
|
|
22516
|
+
}
|
|
22517
|
+
|
|
22518
|
+
const data = await response.json() as T;
|
|
22519
|
+
return { success: true, data };
|
|
22520
|
+
} catch (error) {
|
|
22521
|
+
return { success: false, error: error instanceof Error ? error.message : 'Unknown error' };
|
|
22522
|
+
}
|
|
22523
|
+
}
|
|
22524
|
+
|
|
22525
|
+
// ============================================================================
|
|
22526
|
+
// Application Setup Functions
|
|
22527
|
+
// ============================================================================
|
|
22528
|
+
|
|
22529
|
+
async function createApplication(): Promise<string | null> {
|
|
22530
|
+
console.log('\u{1F4F1} Creating application:', APPLICATION_CONFIG.name);
|
|
22531
|
+
|
|
22532
|
+
const result = await apiCall<{ application: { applicationId: string } }>('/api/applications', {
|
|
22533
|
+
method: 'POST',
|
|
22534
|
+
body: JSON.stringify(APPLICATION_CONFIG),
|
|
22535
|
+
});
|
|
22536
|
+
|
|
22537
|
+
if (!result.success || !result.data) {
|
|
22538
|
+
console.error('\u274C Failed to create application:', result.error);
|
|
22539
|
+
return null;
|
|
22540
|
+
}
|
|
22541
|
+
|
|
22542
|
+
console.log('\u2705 Application created:', result.data.application.applicationId);
|
|
22543
|
+
return result.data.application.applicationId;
|
|
22544
|
+
}
|
|
22545
|
+
|
|
22546
|
+
async function createForms(applicationId: string): Promise<string[]> {
|
|
22547
|
+
const formIds: string[] = [];
|
|
22548
|
+
|
|
22549
|
+
for (const formConfig of FORM_CONFIGS) {
|
|
22550
|
+
console.log('\u{1F4DD} Creating form:', formConfig.name);
|
|
22551
|
+
|
|
22552
|
+
const result = await apiCall<{ form: { formId: string } }>('/api/forms', {
|
|
22553
|
+
method: 'POST',
|
|
22554
|
+
body: JSON.stringify({
|
|
22555
|
+
...formConfig,
|
|
22556
|
+
applicationId,
|
|
22557
|
+
projectId: CONFIG.projectId,
|
|
22558
|
+
organizationId: CONFIG.organizationId,
|
|
22559
|
+
}),
|
|
22560
|
+
});
|
|
22561
|
+
|
|
22562
|
+
if (result.success && result.data) {
|
|
22563
|
+
console.log('\u2705 Form created:', result.data.form.formId);
|
|
22564
|
+
formIds.push(result.data.form.formId);
|
|
22565
|
+
} else {
|
|
22566
|
+
console.error('\u274C Failed to create form:', formConfig.name, result.error);
|
|
22567
|
+
}
|
|
22568
|
+
}
|
|
22569
|
+
|
|
22570
|
+
return formIds;
|
|
22571
|
+
}
|
|
22572
|
+
|
|
22573
|
+
async function createWorkflows(applicationId: string, formIds: string[]): Promise<string[]> {
|
|
22574
|
+
const workflowIds: string[] = [];
|
|
22575
|
+
|
|
22576
|
+
for (const workflowConfig of WORKFLOW_CONFIGS) {
|
|
22577
|
+
console.log('\u26A1 Creating workflow:', workflowConfig.name);
|
|
22578
|
+
|
|
22579
|
+
// Update trigger with actual form ID if available
|
|
22580
|
+
const updatedNodes = workflowConfig.nodes.map(node => {
|
|
22581
|
+
if (node.type === 'form-trigger' && formIds.length > 0) {
|
|
22582
|
+
return { ...node, config: { ...node.config, formId: formIds[0] } };
|
|
22583
|
+
}
|
|
22584
|
+
return node;
|
|
22585
|
+
});
|
|
22586
|
+
|
|
22587
|
+
const result = await apiCall<{ workflow: { id: string } }>('/api/workflows', {
|
|
22588
|
+
method: 'POST',
|
|
22589
|
+
body: JSON.stringify({
|
|
22590
|
+
...workflowConfig,
|
|
22591
|
+
nodes: updatedNodes,
|
|
22592
|
+
applicationId,
|
|
22593
|
+
projectId: CONFIG.projectId,
|
|
22594
|
+
organizationId: CONFIG.organizationId,
|
|
22595
|
+
}),
|
|
22596
|
+
});
|
|
22597
|
+
|
|
22598
|
+
if (result.success && result.data) {
|
|
22599
|
+
console.log('\u2705 Workflow created:', result.data.workflow.id);
|
|
22600
|
+
workflowIds.push(result.data.workflow.id);
|
|
22601
|
+
} else {
|
|
22602
|
+
console.error('\u274C Failed to create workflow:', workflowConfig.name, result.error);
|
|
22603
|
+
}
|
|
22604
|
+
}
|
|
22605
|
+
|
|
22606
|
+
return workflowIds;
|
|
22607
|
+
}
|
|
22608
|
+
|
|
22609
|
+
async function activateWorkflows(workflowIds: string[]): Promise<void> {
|
|
22610
|
+
for (const workflowId of workflowIds) {
|
|
22611
|
+
console.log('\u{1F504} Activating workflow:', workflowId);
|
|
22612
|
+
|
|
22613
|
+
const result = await apiCall(\`/api/workflows/\${workflowId}/activate\`, {
|
|
22614
|
+
method: 'POST',
|
|
22615
|
+
});
|
|
22616
|
+
|
|
22617
|
+
if (result.success) {
|
|
22618
|
+
console.log('\u2705 Workflow activated');
|
|
22619
|
+
} else {
|
|
22620
|
+
console.warn('\u26A0\uFE0F Failed to activate workflow:', result.error);
|
|
22621
|
+
}
|
|
22622
|
+
}
|
|
22623
|
+
}`;
|
|
22624
|
+
const mainCode = `// ============================================================================
|
|
22625
|
+
// Main Setup Script
|
|
22626
|
+
// ============================================================================
|
|
22627
|
+
|
|
22628
|
+
async function setup() {
|
|
22629
|
+
console.log('\\n\u{1F680} Setting up ${name}...\\n');
|
|
22630
|
+
console.log('Template: ${templateId || "blank"}');
|
|
22631
|
+
console.log('Project: ${projectId}');
|
|
22632
|
+
console.log('Organization: ${organizationId}\\n');
|
|
22633
|
+
|
|
22634
|
+
// Validate configuration
|
|
22635
|
+
if (!CONFIG.apiKey) {
|
|
22636
|
+
console.error('\u274C Error: NETPAD_API_KEY environment variable is required');
|
|
22637
|
+
console.log('\\nSet it in your environment or .env file:');
|
|
22638
|
+
console.log(' export NETPAD_API_KEY="np_live_xxxxx"\\n');
|
|
22639
|
+
process.exit(1);
|
|
22640
|
+
}
|
|
22641
|
+
|
|
22642
|
+
// Step 1: Create application
|
|
22643
|
+
const applicationId = await createApplication();
|
|
22644
|
+
if (!applicationId) {
|
|
22645
|
+
process.exit(1);
|
|
22646
|
+
}
|
|
22647
|
+
|
|
22648
|
+
// Step 2: Create forms
|
|
22649
|
+
const formIds = await createForms(applicationId);
|
|
22650
|
+
console.log(\`\\n\u{1F4CA} Created \${formIds.length} form(s)\\n\`);
|
|
22651
|
+
|
|
22652
|
+
// Step 3: Create workflows
|
|
22653
|
+
const workflowIds = await createWorkflows(applicationId, formIds);
|
|
22654
|
+
console.log(\`\\n\u26A1 Created \${workflowIds.length} workflow(s)\\n\`);
|
|
22655
|
+
|
|
22656
|
+
// Step 4: Activate workflows
|
|
22657
|
+
if (workflowIds.length > 0) {
|
|
22658
|
+
await activateWorkflows(workflowIds);
|
|
22659
|
+
}
|
|
22660
|
+
|
|
22661
|
+
// Summary
|
|
22662
|
+
console.log('\\n' + '='.repeat(50));
|
|
22663
|
+
console.log('\u2705 Setup Complete!');
|
|
22664
|
+
console.log('='.repeat(50));
|
|
22665
|
+
console.log(\`
|
|
22666
|
+
Application: ${name}
|
|
22667
|
+
Application ID: \${applicationId}
|
|
22668
|
+
Forms: \${formIds.length}
|
|
22669
|
+
Workflows: \${workflowIds.length}
|
|
22670
|
+
|
|
22671
|
+
Next steps:
|
|
22672
|
+
1. Visit your NetPad dashboard to customize forms and workflows
|
|
22673
|
+
2. Test form submissions
|
|
22674
|
+
3. Configure email templates and integrations
|
|
22675
|
+
\`);
|
|
22676
|
+
}
|
|
22677
|
+
|
|
22678
|
+
// Run setup
|
|
22679
|
+
setup().catch((error) => {
|
|
22680
|
+
console.error('\u274C Setup failed:', error);
|
|
22681
|
+
process.exit(1);
|
|
22682
|
+
});`;
|
|
22683
|
+
const code = generateSelfContainedCode({
|
|
22684
|
+
title: `${name} Setup`,
|
|
22685
|
+
description: description || `Complete setup script for ${name}`,
|
|
22686
|
+
includeFormTypes: true,
|
|
22687
|
+
includeWorkflowTypes: true,
|
|
22688
|
+
configCode,
|
|
22689
|
+
functionsCode,
|
|
22690
|
+
mainCode
|
|
22691
|
+
});
|
|
22692
|
+
const output = createToolOutput({
|
|
22693
|
+
code,
|
|
22694
|
+
filename: `setup-${slug}.ts`,
|
|
22695
|
+
envVars: STANDARD_ENV_VARS
|
|
22696
|
+
});
|
|
22697
|
+
return {
|
|
22698
|
+
content: [{
|
|
22699
|
+
type: "text",
|
|
22700
|
+
text: formatToolOutput(output)
|
|
21136
22701
|
}]
|
|
21137
22702
|
};
|
|
21138
22703
|
}
|
|
@@ -21591,7 +23156,7 @@ server.resource(
|
|
|
21591
23156
|
);
|
|
21592
23157
|
server.tool(
|
|
21593
23158
|
"list_workflow_templates",
|
|
21594
|
-
"List all available workflow templates for creating automated workflows.
|
|
23159
|
+
'[DEPRECATED - use browse_templates with templateType="workflow"] List all available workflow templates for creating automated workflows.',
|
|
21595
23160
|
{
|
|
21596
23161
|
category: external_exports.string().optional().describe('Filter by category (e.g., "notifications", "data", "sales")')
|
|
21597
23162
|
},
|
|
@@ -21623,7 +23188,7 @@ server.tool(
|
|
|
21623
23188
|
);
|
|
21624
23189
|
server.tool(
|
|
21625
23190
|
"get_workflow_template",
|
|
21626
|
-
"Get detailed information about a specific workflow template including its nodes, edges, and configuration.
|
|
23191
|
+
'[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
23192
|
{
|
|
21628
23193
|
templateId: external_exports.enum(["form-to-email", "form-to-database", "lead-qualification", "webhook-to-database", "scheduled-report"]).describe("The template ID")
|
|
21629
23194
|
},
|
|
@@ -21688,33 +23253,240 @@ server.tool(
|
|
|
21688
23253
|
);
|
|
21689
23254
|
server.tool(
|
|
21690
23255
|
"create_workflow_from_template",
|
|
21691
|
-
"Generate
|
|
23256
|
+
"Generate a complete, self-contained TypeScript file to create a workflow. Returns validated code with inline types - run with `npx tsx`.",
|
|
21692
23257
|
{
|
|
21693
23258
|
name: external_exports.string().describe("Name of the workflow"),
|
|
21694
23259
|
description: external_exports.string().optional().describe("Description of the workflow"),
|
|
21695
23260
|
templateId: external_exports.enum(["form-to-email", "form-to-database", "lead-qualification", "webhook-to-database", "scheduled-report"]).optional().describe("Template to use"),
|
|
23261
|
+
formId: external_exports.string().optional().describe("Form ID for form-triggered workflows"),
|
|
23262
|
+
formSlug: external_exports.string().optional().describe("Form slug for form-triggered workflows"),
|
|
21696
23263
|
applicationId: external_exports.string().optional().describe("Application ID to attach workflow to"),
|
|
21697
23264
|
projectId: external_exports.string().describe("Project ID"),
|
|
21698
23265
|
organizationId: external_exports.string().describe("Organization ID"),
|
|
21699
|
-
tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization")
|
|
23266
|
+
tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization"),
|
|
23267
|
+
salesEmail: external_exports.string().optional().describe("Email address for sales notifications")
|
|
21700
23268
|
},
|
|
21701
23269
|
async (options) => {
|
|
21702
|
-
const
|
|
21703
|
-
const
|
|
23270
|
+
const { name, description, templateId, formId, formSlug, applicationId, tags, salesEmail } = options;
|
|
23271
|
+
const slug = name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
23272
|
+
const template = templateId ? WORKFLOW_TEMPLATES[templateId] : null;
|
|
23273
|
+
const workflowNodes = template?.nodes || [
|
|
23274
|
+
{
|
|
23275
|
+
id: "trigger_1",
|
|
23276
|
+
type: "manual-trigger",
|
|
23277
|
+
label: "Manual Trigger",
|
|
23278
|
+
position: { x: 100, y: 200 },
|
|
23279
|
+
config: {},
|
|
23280
|
+
enabled: true
|
|
23281
|
+
}
|
|
23282
|
+
];
|
|
23283
|
+
const workflowEdges = template?.edges || [];
|
|
23284
|
+
const updatedNodes = workflowNodes.map((node) => {
|
|
23285
|
+
if (node.type === "form-trigger") {
|
|
23286
|
+
return {
|
|
23287
|
+
...node,
|
|
23288
|
+
config: {
|
|
23289
|
+
...node.config,
|
|
23290
|
+
formId: formId || node.config.formId || "",
|
|
23291
|
+
formSlug: formSlug || node.config.formSlug || ""
|
|
23292
|
+
}
|
|
23293
|
+
};
|
|
23294
|
+
}
|
|
23295
|
+
if (node.type === "email-send" && salesEmail) {
|
|
23296
|
+
return {
|
|
23297
|
+
...node,
|
|
23298
|
+
config: {
|
|
23299
|
+
...node.config,
|
|
23300
|
+
to: salesEmail
|
|
23301
|
+
}
|
|
23302
|
+
};
|
|
23303
|
+
}
|
|
23304
|
+
return node;
|
|
23305
|
+
});
|
|
23306
|
+
const configCode = `// Workflow Configuration
|
|
23307
|
+
const WORKFLOW_CONFIG: WorkflowConfig = {
|
|
23308
|
+
name: ${JSON.stringify(name)},
|
|
23309
|
+
description: ${JSON.stringify(description || template?.description || "Custom workflow")},
|
|
23310
|
+
tags: ${JSON.stringify(tags || template?.tags || [])},
|
|
23311
|
+
nodes: ${JSON.stringify(updatedNodes, null, 2)},
|
|
23312
|
+
edges: ${JSON.stringify(workflowEdges, null, 2)},
|
|
23313
|
+
settings: {
|
|
23314
|
+
executionMode: 'auto',
|
|
23315
|
+
maxExecutionTime: 300000,
|
|
23316
|
+
retryPolicy: {
|
|
23317
|
+
maxRetries: 3,
|
|
23318
|
+
backoffMultiplier: 2,
|
|
23319
|
+
initialDelayMs: 1000,
|
|
23320
|
+
},
|
|
23321
|
+
},
|
|
23322
|
+
};`;
|
|
23323
|
+
const functionsCode = `/**
|
|
23324
|
+
* Create the workflow via NetPad API.
|
|
23325
|
+
*/
|
|
23326
|
+
export async function createWorkflow(): Promise<CreateWorkflowResult> {
|
|
23327
|
+
try {
|
|
23328
|
+
const response = await fetch(\`\${CONFIG.baseUrl}/api/workflows\`, {
|
|
23329
|
+
method: 'POST',
|
|
23330
|
+
headers: {
|
|
23331
|
+
'Content-Type': 'application/json',
|
|
23332
|
+
'Authorization': \`Bearer \${CONFIG.apiKey}\`,
|
|
23333
|
+
},
|
|
23334
|
+
body: JSON.stringify({
|
|
23335
|
+
...WORKFLOW_CONFIG,
|
|
23336
|
+
${applicationId ? `applicationId: '${applicationId}',` : ""}
|
|
23337
|
+
projectId: CONFIG.projectId,
|
|
23338
|
+
organizationId: CONFIG.organizationId,
|
|
23339
|
+
}),
|
|
23340
|
+
});
|
|
23341
|
+
|
|
23342
|
+
if (!response.ok) {
|
|
23343
|
+
return { success: false, error: await response.text() };
|
|
23344
|
+
}
|
|
23345
|
+
|
|
23346
|
+
const result = await response.json() as { workflow: { id: string } };
|
|
23347
|
+
return { success: true, workflowId: result.workflow.id };
|
|
23348
|
+
} catch (error) {
|
|
21704
23349
|
return {
|
|
21705
|
-
|
|
21706
|
-
|
|
21707
|
-
|
|
23350
|
+
success: false,
|
|
23351
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
23352
|
+
};
|
|
23353
|
+
}
|
|
23354
|
+
}
|
|
21708
23355
|
|
|
21709
|
-
|
|
21710
|
-
|
|
21711
|
-
|
|
23356
|
+
/**
|
|
23357
|
+
* Activate the workflow to start processing.
|
|
23358
|
+
*/
|
|
23359
|
+
export async function activateWorkflow(workflowId: string): Promise<{ success: boolean; error?: string }> {
|
|
23360
|
+
try {
|
|
23361
|
+
const response = await fetch(
|
|
23362
|
+
\`\${CONFIG.baseUrl}/api/workflows/\${workflowId}/activate\`,
|
|
23363
|
+
{
|
|
23364
|
+
method: 'POST',
|
|
23365
|
+
headers: {
|
|
23366
|
+
'Authorization': \`Bearer \${CONFIG.apiKey}\`,
|
|
23367
|
+
},
|
|
23368
|
+
}
|
|
23369
|
+
);
|
|
21712
23370
|
|
|
21713
|
-
|
|
23371
|
+
if (!response.ok) {
|
|
23372
|
+
return { success: false, error: await response.text() };
|
|
23373
|
+
}
|
|
21714
23374
|
|
|
21715
|
-
|
|
21716
|
-
|
|
21717
|
-
|
|
23375
|
+
return { success: true };
|
|
23376
|
+
} catch (error) {
|
|
23377
|
+
return {
|
|
23378
|
+
success: false,
|
|
23379
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
23380
|
+
};
|
|
23381
|
+
}
|
|
23382
|
+
}
|
|
23383
|
+
|
|
23384
|
+
/**
|
|
23385
|
+
* Test the workflow with sample data.
|
|
23386
|
+
*/
|
|
23387
|
+
export async function testWorkflow(workflowId: string, testData: Record<string, unknown>): Promise<{ success: boolean; executionId?: string; error?: string }> {
|
|
23388
|
+
try {
|
|
23389
|
+
const response = await fetch(
|
|
23390
|
+
\`\${CONFIG.baseUrl}/api/workflows/\${workflowId}/execute\`,
|
|
23391
|
+
{
|
|
23392
|
+
method: 'POST',
|
|
23393
|
+
headers: {
|
|
23394
|
+
'Content-Type': 'application/json',
|
|
23395
|
+
'Authorization': \`Bearer \${CONFIG.apiKey}\`,
|
|
23396
|
+
},
|
|
23397
|
+
body: JSON.stringify({ payload: testData }),
|
|
23398
|
+
}
|
|
23399
|
+
);
|
|
23400
|
+
|
|
23401
|
+
if (!response.ok) {
|
|
23402
|
+
return { success: false, error: await response.text() };
|
|
23403
|
+
}
|
|
23404
|
+
|
|
23405
|
+
const result = await response.json() as { executionId: string };
|
|
23406
|
+
return { success: true, executionId: result.executionId };
|
|
23407
|
+
} catch (error) {
|
|
23408
|
+
return {
|
|
23409
|
+
success: false,
|
|
23410
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
23411
|
+
};
|
|
23412
|
+
}
|
|
23413
|
+
}`;
|
|
23414
|
+
const mainCode = `// ============================================================================
|
|
23415
|
+
// Main Setup
|
|
23416
|
+
// ============================================================================
|
|
23417
|
+
|
|
23418
|
+
async function setup() {
|
|
23419
|
+
console.log('\u26A1 Creating workflow:', WORKFLOW_CONFIG.name);
|
|
23420
|
+
console.log('Template: ${templateId || "custom"}');
|
|
23421
|
+
${formId ? `console.log('Form ID: ${formId}');` : ""}
|
|
23422
|
+
${formSlug ? `console.log('Form Slug: ${formSlug}');` : ""}
|
|
23423
|
+
console.log('');
|
|
23424
|
+
|
|
23425
|
+
// Validate configuration
|
|
23426
|
+
if (!CONFIG.apiKey) {
|
|
23427
|
+
console.error('\u274C Error: NETPAD_API_KEY environment variable is required');
|
|
23428
|
+
process.exit(1);
|
|
23429
|
+
}
|
|
23430
|
+
|
|
23431
|
+
// Create the workflow
|
|
23432
|
+
const createResult = await createWorkflow();
|
|
23433
|
+
if (!createResult.success) {
|
|
23434
|
+
console.error('\u274C Failed to create workflow:', createResult.error);
|
|
23435
|
+
process.exit(1);
|
|
23436
|
+
}
|
|
23437
|
+
|
|
23438
|
+
console.log('\u2705 Workflow created:', createResult.workflowId);
|
|
23439
|
+
|
|
23440
|
+
// Activate the workflow
|
|
23441
|
+
console.log('\\n\u{1F504} Activating workflow...');
|
|
23442
|
+
const activateResult = await activateWorkflow(createResult.workflowId!);
|
|
23443
|
+
if (activateResult.success) {
|
|
23444
|
+
console.log('\u2705 Workflow activated');
|
|
23445
|
+
} else {
|
|
23446
|
+
console.warn('\u26A0\uFE0F Failed to activate:', activateResult.error);
|
|
23447
|
+
}
|
|
23448
|
+
|
|
23449
|
+
// Summary
|
|
23450
|
+
console.log('\\n' + '='.repeat(50));
|
|
23451
|
+
console.log('\u2705 Workflow Setup Complete!');
|
|
23452
|
+
console.log('='.repeat(50));
|
|
23453
|
+
console.log(\`
|
|
23454
|
+
Workflow: ${name}
|
|
23455
|
+
Workflow ID: \${createResult.workflowId}
|
|
23456
|
+
Nodes: ${updatedNodes.length}
|
|
23457
|
+
Edges: ${workflowEdges.length}
|
|
23458
|
+
${templateId ? `Template: ${templateId}` : ""}
|
|
23459
|
+
|
|
23460
|
+
Next steps:
|
|
23461
|
+
1. Configure node settings in the NetPad dashboard
|
|
23462
|
+
2. Test with sample data
|
|
23463
|
+
3. Monitor executions
|
|
23464
|
+
\`);
|
|
23465
|
+
}
|
|
23466
|
+
|
|
23467
|
+
// Uncomment to run:
|
|
23468
|
+
// setup().catch(console.error);
|
|
23469
|
+
|
|
23470
|
+
// Export for use as module
|
|
23471
|
+
export { WORKFLOW_CONFIG, createWorkflow, activateWorkflow, testWorkflow };`;
|
|
23472
|
+
const code = generateSelfContainedCode({
|
|
23473
|
+
title: name,
|
|
23474
|
+
description: description || `Workflow created from ${templateId || "scratch"}`,
|
|
23475
|
+
includeFormTypes: false,
|
|
23476
|
+
includeWorkflowTypes: true,
|
|
23477
|
+
configCode,
|
|
23478
|
+
functionsCode,
|
|
23479
|
+
mainCode
|
|
23480
|
+
});
|
|
23481
|
+
const output = createToolOutput({
|
|
23482
|
+
code,
|
|
23483
|
+
filename: `${slug}-workflow.ts`,
|
|
23484
|
+
envVars: STANDARD_ENV_VARS
|
|
23485
|
+
});
|
|
23486
|
+
return {
|
|
23487
|
+
content: [{
|
|
23488
|
+
type: "text",
|
|
23489
|
+
text: formatToolOutput(output)
|
|
21718
23490
|
}]
|
|
21719
23491
|
};
|
|
21720
23492
|
}
|
|
@@ -21899,7 +23671,7 @@ server.resource(
|
|
|
21899
23671
|
);
|
|
21900
23672
|
server.tool(
|
|
21901
23673
|
"list_conversational_templates",
|
|
21902
|
-
"List all available conversational form templates for AI-powered data collection.
|
|
23674
|
+
'[DEPRECATED - use browse_templates with templateType="conversational"] List all available conversational form templates for AI-powered data collection.',
|
|
21903
23675
|
{
|
|
21904
23676
|
category: external_exports.string().optional().describe('Filter by category (e.g., "support", "feedback", "intake")')
|
|
21905
23677
|
},
|
|
@@ -21931,7 +23703,7 @@ server.tool(
|
|
|
21931
23703
|
);
|
|
21932
23704
|
server.tool(
|
|
21933
23705
|
"get_conversational_template",
|
|
21934
|
-
"Get detailed information about a specific conversational form template including topics, extraction schema, and persona configuration.
|
|
23706
|
+
'[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
23707
|
{
|
|
21936
23708
|
templateId: external_exports.enum(["it-helpdesk", "customer-feedback", "lead-qualification", "patient-intake"]).describe("The template ID")
|
|
21937
23709
|
},
|
|
@@ -22246,7 +24018,7 @@ server.resource(
|
|
|
22246
24018
|
);
|
|
22247
24019
|
server.tool(
|
|
22248
24020
|
"list_template_categories",
|
|
22249
|
-
"List all available form template categories with descriptions and template counts.
|
|
24021
|
+
'[DEPRECATED - use browse_templates with templateType="form" and action="categories"] List all available form template categories with descriptions and template counts.',
|
|
22250
24022
|
{},
|
|
22251
24023
|
async () => {
|
|
22252
24024
|
return {
|
|
@@ -22263,7 +24035,7 @@ server.tool(
|
|
|
22263
24035
|
);
|
|
22264
24036
|
server.tool(
|
|
22265
24037
|
"list_form_templates",
|
|
22266
|
-
"List all available form templates (25+) across multiple categories. Returns template summaries with field counts.
|
|
24038
|
+
'[DEPRECATED - use browse_templates with templateType="form"] List all available form templates (25+) across multiple categories. Returns template summaries with field counts.',
|
|
22267
24039
|
{
|
|
22268
24040
|
category: external_exports.string().optional().describe('Filter by category (business, events, feedback, support, ecommerce, healthcare, hr, finance, education, real-estate, or "all")'),
|
|
22269
24041
|
search: external_exports.string().optional().describe("Search templates by name, description, or tags")
|
|
@@ -22302,7 +24074,7 @@ server.tool(
|
|
|
22302
24074
|
);
|
|
22303
24075
|
server.tool(
|
|
22304
24076
|
"get_form_template",
|
|
22305
|
-
"Get detailed information about a specific form template including all fields, validation rules, and configuration.
|
|
24077
|
+
'[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
24078
|
{
|
|
22307
24079
|
templateId: external_exports.string().describe('Template ID (e.g., "contact-form", "lead-capture", "patient-intake")')
|
|
22308
24080
|
},
|
|
@@ -22485,7 +24257,7 @@ ${code}
|
|
|
22485
24257
|
);
|
|
22486
24258
|
server.tool(
|
|
22487
24259
|
"list_query_templates",
|
|
22488
|
-
"List all available MongoDB query templates for common operations.
|
|
24260
|
+
'[DEPRECATED - use browse_templates with templateType="query"] List all available MongoDB query templates for common operations.',
|
|
22489
24261
|
{},
|
|
22490
24262
|
async () => {
|
|
22491
24263
|
const templates = listQueryTemplates();
|
|
@@ -22502,7 +24274,7 @@ server.tool(
|
|
|
22502
24274
|
);
|
|
22503
24275
|
server.tool(
|
|
22504
24276
|
"get_query_template",
|
|
22505
|
-
"Get a specific query template with example code.
|
|
24277
|
+
'[DEPRECATED - use browse_templates with templateType="query" and action="get"] Get a specific query template with example code.',
|
|
22506
24278
|
{
|
|
22507
24279
|
templateId: external_exports.string().describe('Template ID (e.g., "find-all", "aggregate-group-count")')
|
|
22508
24280
|
},
|