@netpad/mcp-server 2.0.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 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,
@@ -17471,7 +17373,7 @@ var WORKFLOW_NODE_TYPES = {
17471
17373
  { key: "database", label: "Database", type: "string", required: true },
17472
17374
  { key: "collection", label: "Collection", type: "string", required: true },
17473
17375
  { key: "operation", label: "Operation", type: "select", options: ["find", "findOne", "aggregate"] },
17474
- { key: "filter", label: "Filter", type: "json" },
17376
+ { key: "filter", label: "Filter", type: "json", aiAssist: { enabled: true, promptHint: 'e.g., "Find users created in the last 7 days"', buttonLabel: "Generate Query" } },
17475
17377
  { key: "projection", label: "Projection", type: "json" },
17476
17378
  { key: "sort", label: "Sort", type: "json" },
17477
17379
  { key: "limit", label: "Limit", type: "number" }
@@ -17505,7 +17407,7 @@ var WORKFLOW_NODE_TYPES = {
17505
17407
  inputs: [{ id: "input", label: "Input", type: "any" }],
17506
17408
  outputs: [{ id: "output", label: "Output", type: "any" }],
17507
17409
  configFields: [
17508
- { key: "expression", label: "Transform Expression", type: "expression", required: true }
17410
+ { key: "expression", label: "Transform Expression", type: "expression", required: true, aiAssist: { enabled: true, promptHint: 'e.g., "Extract emails from user array" or "Add processed timestamp"', buttonLabel: "Generate Transform" } }
17509
17411
  ]
17510
17412
  },
17511
17413
  {
@@ -17537,9 +17439,9 @@ var WORKFLOW_NODE_TYPES = {
17537
17439
  inputs: [{ id: "data", label: "Template Data", type: "object" }],
17538
17440
  outputs: [{ id: "result", label: "Result", type: "object" }],
17539
17441
  configFields: [
17540
- { key: "to", label: "To", type: "expression", required: true },
17541
- { key: "subject", label: "Subject", type: "expression", required: true },
17542
- { key: "body", label: "Body", type: "richtext", required: true },
17442
+ { key: "to", label: "To", type: "expression", required: true, aiAssist: { enabled: true, promptHint: 'e.g., "Send to form submitter email"', buttonLabel: "Generate Recipients" } },
17443
+ { key: "subject", label: "Subject", type: "expression", required: true, aiAssist: { enabled: true, promptHint: 'e.g., "Create a thank you subject with user name"', buttonLabel: "Generate Subject" } },
17444
+ { key: "body", label: "Body", type: "richtext", required: true, aiAssist: { enabled: true, promptHint: 'e.g., "Write a professional confirmation email"', buttonLabel: "Generate Body" } },
17543
17445
  { key: "from", label: "From", type: "string" },
17544
17446
  { key: "replyTo", label: "Reply To", type: "string" }
17545
17447
  ]
@@ -17558,10 +17460,10 @@ var WORKFLOW_NODE_TYPES = {
17558
17460
  { id: "status", label: "Status Code", type: "number" }
17559
17461
  ],
17560
17462
  configFields: [
17561
- { key: "url", label: "URL", type: "expression", required: true },
17463
+ { key: "url", label: "URL", type: "expression", required: true, aiAssist: { enabled: true, promptHint: 'e.g., "Build Slack webhook URL with channel"', buttonLabel: "Generate URL" } },
17562
17464
  { key: "method", label: "Method", type: "select", options: ["GET", "POST", "PUT", "PATCH", "DELETE"] },
17563
- { key: "headers", label: "Headers", type: "json" },
17564
- { key: "body", label: "Body", type: "json" },
17465
+ { key: "headers", label: "Headers", type: "json", aiAssist: { enabled: true, promptHint: 'e.g., "Add authorization and content-type headers"', buttonLabel: "Generate Headers" } },
17466
+ { key: "body", label: "Body", type: "json", aiAssist: { enabled: true, promptHint: 'e.g., "Create Slack message payload from form data"', buttonLabel: "Generate Body" } },
17565
17467
  { key: "authentication", label: "Authentication", type: "select", options: ["none", "basic", "bearer", "api_key"] }
17566
17468
  ]
17567
17469
  },
@@ -17593,7 +17495,7 @@ var WORKFLOW_NODE_TYPES = {
17593
17495
  inputs: [{ id: "context", label: "Context", type: "object" }],
17594
17496
  outputs: [{ id: "response", label: "AI Response", type: "string" }],
17595
17497
  configFields: [
17596
- { key: "prompt", label: "Prompt", type: "richtext", required: true },
17498
+ { key: "prompt", label: "Prompt", type: "richtext", required: true, aiAssist: { enabled: true, promptHint: 'e.g., "Write a prompt to summarize customer feedback"', buttonLabel: "Generate Prompt" } },
17597
17499
  { key: "model", label: "Model", type: "select", options: ["gpt-4", "gpt-3.5-turbo", "claude-3"] },
17598
17500
  { key: "temperature", label: "Temperature", type: "number", default: 0.7 },
17599
17501
  { key: "maxTokens", label: "Max Tokens", type: "number", default: 1e3 }
@@ -17613,8 +17515,26 @@ var WORKFLOW_NODE_TYPES = {
17613
17515
  { id: "confidence", label: "Confidence", type: "number" }
17614
17516
  ],
17615
17517
  configFields: [
17616
- { key: "categories", label: "Categories", type: "array", itemType: "string", required: true },
17617
- { key: "instructions", label: "Classification Instructions", type: "richtext" }
17518
+ { key: "categories", label: "Categories", type: "array", itemType: "string", required: true, aiAssist: { enabled: true, promptHint: 'e.g., "Create sentiment categories" or "Support ticket priorities"', buttonLabel: "Generate Categories" } },
17519
+ { key: "instructions", label: "Classification Instructions", type: "richtext", aiAssist: { enabled: true, promptHint: 'e.g., "Write instructions to classify support tickets by urgency"', buttonLabel: "Generate Instructions" } }
17520
+ ]
17521
+ }
17522
+ ],
17523
+ // Custom
17524
+ custom: [
17525
+ {
17526
+ type: "code",
17527
+ name: "Custom Code",
17528
+ description: "Execute custom JavaScript code",
17529
+ icon: "code",
17530
+ color: "#455A64",
17531
+ category: "custom",
17532
+ stage: "processor",
17533
+ inputs: [{ id: "input", label: "Input", type: "any" }],
17534
+ outputs: [{ id: "output", label: "Output", type: "any" }],
17535
+ configFields: [
17536
+ { key: "code", label: "JavaScript Code", type: "code", required: true, aiAssist: { enabled: true, promptHint: 'e.g., "Calculate order total from items array"', buttonLabel: "Generate Code" } },
17537
+ { key: "timeout", label: "Timeout (ms)", type: "number", default: 5e3 }
17618
17538
  ]
17619
17539
  }
17620
17540
  ]
@@ -17880,92 +17800,6 @@ var WORKFLOW_TEMPLATES = {
17880
17800
  ]
17881
17801
  }
17882
17802
  };
17883
- function generateCreateWorkflowCode(options) {
17884
- const { name, description, templateId, applicationId, projectId, organizationId, tags } = options;
17885
- const template = templateId ? WORKFLOW_TEMPLATES[templateId] : null;
17886
- let code = `// Create a new NetPad workflow
17887
- // Using: NetPad Platform API
17888
-
17889
- const workflowData = {
17890
- name: '${name}',
17891
- ${description ? `description: '${description}',` : ""}
17892
- projectId: '${projectId}',
17893
- organizationId: '${organizationId}',
17894
- ${applicationId ? `applicationId: '${applicationId}',` : ""}
17895
- ${tags && tags.length > 0 ? `tags: ${JSON.stringify(tags)},` : ""}
17896
- `;
17897
- if (template) {
17898
- code += `
17899
- // Using template: ${template.name}
17900
- canvas: {
17901
- nodes: ${JSON.stringify(template.nodes, null, 4)},
17902
- edges: ${JSON.stringify(template.edges, null, 4)},
17903
- viewport: { x: 0, y: 0, zoom: 1 },
17904
- },
17905
- `;
17906
- } else {
17907
- code += `
17908
- // Empty canvas - add nodes in the visual editor
17909
- canvas: {
17910
- nodes: [],
17911
- edges: [],
17912
- viewport: { x: 0, y: 0, zoom: 1 },
17913
- },
17914
- `;
17915
- }
17916
- code += `
17917
- settings: {
17918
- executionMode: 'auto',
17919
- maxExecutionTime: 300000,
17920
- retryPolicy: {
17921
- maxRetries: 3,
17922
- backoffMultiplier: 2,
17923
- initialDelayMs: 1000,
17924
- },
17925
- errorHandling: 'stop',
17926
- timezone: 'UTC',
17927
- },
17928
- variables: [],
17929
- };
17930
-
17931
- const response = await fetch('/api/workflows', {
17932
- method: 'POST',
17933
- headers: {
17934
- 'Content-Type': 'application/json',
17935
- 'Authorization': \`Bearer \${process.env.NETPAD_API_KEY}\`,
17936
- },
17937
- body: JSON.stringify(workflowData),
17938
- });
17939
-
17940
- const { workflow } = await response.json();
17941
- console.log('Created workflow:', workflow.id);
17942
- `;
17943
- return code;
17944
- }
17945
- function generateWorkflowConfig(options) {
17946
- const { name, description, templateId, tags } = options;
17947
- const template = templateId ? WORKFLOW_TEMPLATES[templateId] : null;
17948
- return {
17949
- workflow: {
17950
- name,
17951
- description: description || (template ? template.description : ""),
17952
- tags: tags || (template ? template.tags : []),
17953
- status: "draft"
17954
- },
17955
- template: template ? {
17956
- id: template.id,
17957
- name: template.name,
17958
- category: template.category
17959
- } : null,
17960
- canvas: template ? {
17961
- nodes: template.nodes,
17962
- edges: template.edges
17963
- } : {
17964
- nodes: [],
17965
- edges: []
17966
- }
17967
- };
17968
- }
17969
17803
  function generateAddNodeCode(workflowId, node) {
17970
17804
  return `// Add node to workflow
17971
17805
  const nodeData = ${JSON.stringify(node, null, 2)};
@@ -20097,121 +19931,852 @@ collections.forEach((coll: any) => {
20097
19931
  `;
20098
19932
  }
20099
19933
 
20100
- // src/index.ts
20101
- var server = new McpServer({
20102
- name: "@netpad/mcp-server",
20103
- version: "2.0.0"
20104
- });
20105
- server.resource(
20106
- "netpad-docs",
20107
- "netpad://docs/readme",
20108
- async () => ({
20109
- contents: [
20110
- {
20111
- uri: "netpad://docs/readme",
20112
- mimeType: "text/markdown",
20113
- text: DOCUMENTATION.readme
20114
- }
20115
- ]
20116
- })
20117
- );
20118
- server.resource(
20119
- "netpad-architecture",
20120
- "netpad://docs/architecture",
20121
- async () => ({
20122
- contents: [
20123
- {
20124
- uri: "netpad://docs/architecture",
20125
- mimeType: "text/markdown",
20126
- text: DOCUMENTATION.architecture
20127
- }
20128
- ]
20129
- })
20130
- );
20131
- server.resource(
20132
- "netpad-quick-start",
20133
- "netpad://docs/quick-start",
20134
- async () => ({
20135
- contents: [
20136
- {
20137
- uri: "netpad://docs/quick-start",
20138
- mimeType: "text/markdown",
20139
- text: QUICK_START_GUIDE
20140
- }
20141
- ]
20142
- })
20143
- );
20144
- server.resource(
20145
- "netpad-examples",
20146
- "netpad://docs/examples",
20147
- async () => ({
20148
- contents: [
20149
- {
20150
- uri: "netpad://docs/examples",
20151
- mimeType: "text/markdown",
20152
- text: EXAMPLES
20153
- }
20154
- ]
20155
- })
20156
- );
20157
- server.resource(
20158
- "netpad-field-types",
20159
- "netpad://reference/field-types",
20160
- async () => ({
20161
- contents: [
20162
- {
20163
- uri: "netpad://reference/field-types",
20164
- mimeType: "application/json",
20165
- text: JSON.stringify(FIELD_TYPES, null, 2)
20166
- }
20167
- ]
20168
- })
20169
- );
20170
- server.resource(
20171
- "netpad-operators",
20172
- "netpad://reference/operators",
20173
- async () => ({
20174
- contents: [
20175
- {
20176
- uri: "netpad://reference/operators",
20177
- mimeType: "application/json",
20178
- text: JSON.stringify(OPERATORS, null, 2)
20179
- }
20180
- ]
20181
- })
20182
- );
20183
- server.resource(
20184
- "netpad-formulas",
20185
- "netpad://reference/formulas",
20186
- async () => ({
20187
- contents: [
20188
- {
20189
- uri: "netpad://reference/formulas",
20190
- mimeType: "application/json",
20191
- text: JSON.stringify(FORMULA_FUNCTIONS, null, 2)
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;
20192
20244
  }
20193
- ]
20194
- })
20195
- );
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
+
20558
+ // src/index.ts
20559
+ var server = new McpServer({
20560
+ name: "@netpad/mcp-server",
20561
+ version: "2.0.0"
20562
+ });
20563
+ server.resource(
20564
+ "netpad-docs",
20565
+ "netpad://docs/readme",
20566
+ async () => ({
20567
+ contents: [
20568
+ {
20569
+ uri: "netpad://docs/readme",
20570
+ mimeType: "text/markdown",
20571
+ text: DOCUMENTATION.readme
20572
+ }
20573
+ ]
20574
+ })
20575
+ );
20576
+ server.resource(
20577
+ "netpad-architecture",
20578
+ "netpad://docs/architecture",
20579
+ async () => ({
20580
+ contents: [
20581
+ {
20582
+ uri: "netpad://docs/architecture",
20583
+ mimeType: "text/markdown",
20584
+ text: DOCUMENTATION.architecture
20585
+ }
20586
+ ]
20587
+ })
20588
+ );
20589
+ server.resource(
20590
+ "netpad-quick-start",
20591
+ "netpad://docs/quick-start",
20592
+ async () => ({
20593
+ contents: [
20594
+ {
20595
+ uri: "netpad://docs/quick-start",
20596
+ mimeType: "text/markdown",
20597
+ text: QUICK_START_GUIDE
20598
+ }
20599
+ ]
20600
+ })
20601
+ );
20602
+ server.resource(
20603
+ "netpad-examples",
20604
+ "netpad://docs/examples",
20605
+ async () => ({
20606
+ contents: [
20607
+ {
20608
+ uri: "netpad://docs/examples",
20609
+ mimeType: "text/markdown",
20610
+ text: EXAMPLES
20611
+ }
20612
+ ]
20613
+ })
20614
+ );
20615
+ server.resource(
20616
+ "netpad-field-types",
20617
+ "netpad://reference/field-types",
20618
+ async () => ({
20619
+ contents: [
20620
+ {
20621
+ uri: "netpad://reference/field-types",
20622
+ mimeType: "application/json",
20623
+ text: JSON.stringify(FIELD_TYPES, null, 2)
20624
+ }
20625
+ ]
20626
+ })
20627
+ );
20628
+ server.resource(
20629
+ "netpad-operators",
20630
+ "netpad://reference/operators",
20631
+ async () => ({
20632
+ contents: [
20633
+ {
20634
+ uri: "netpad://reference/operators",
20635
+ mimeType: "application/json",
20636
+ text: JSON.stringify(OPERATORS, null, 2)
20637
+ }
20638
+ ]
20639
+ })
20640
+ );
20641
+ server.resource(
20642
+ "netpad-formulas",
20643
+ "netpad://reference/formulas",
20644
+ async () => ({
20645
+ contents: [
20646
+ {
20647
+ uri: "netpad://reference/formulas",
20648
+ mimeType: "application/json",
20649
+ text: JSON.stringify(FORMULA_FUNCTIONS, null, 2)
20650
+ }
20651
+ ]
20652
+ })
20653
+ );
20196
20654
  server.tool(
20197
20655
  "generate_form",
20198
- "Generate a complete NetPad form configuration from a description. Provide a natural language description of the form you want to create, and this tool will generate the full FormConfiguration object.",
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`.",
20199
20657
  {
20200
20658
  description: external_exports.string().describe("Natural language description of the form to generate"),
20201
20659
  formName: external_exports.string().describe("Name of the form"),
20202
20660
  includeMultiPage: external_exports.boolean().optional().describe("Whether to organize fields into multiple pages"),
20203
- 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')
20204
20663
  },
20205
- async ({ description, formName, includeMultiPage, includeTheme }) => {
20664
+ async ({ description, formName, includeMultiPage, includeTheme, outputFormat = "typescript" }) => {
20206
20665
  const schema = generateFormSchema(description, formName, {
20207
20666
  multiPage: includeMultiPage,
20208
20667
  theme: includeTheme
20209
20668
  });
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
+ });
20210
20775
  return {
20211
20776
  content: [
20212
20777
  {
20213
20778
  type: "text",
20214
- text: JSON.stringify(schema, null, 2)
20779
+ text: formatToolOutput(output)
20215
20780
  }
20216
20781
  ]
20217
20782
  };
@@ -20363,9 +20928,440 @@ server.tool(
20363
20928
  }
20364
20929
  }
20365
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
+ );
20366
21362
  server.tool(
20367
21363
  "list_field_types",
20368
- "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.',
20369
21365
  {
20370
21366
  category: external_exports.string().optional().describe('Filter by category (e.g., "text", "selection", "date")')
20371
21367
  },
@@ -20378,7 +21374,9 @@ server.tool(
20378
21374
  content: [
20379
21375
  {
20380
21376
  type: "text",
20381
- text: JSON.stringify(types, null, 2)
21377
+ text: `DEPRECATED: This tool is deprecated. Use get_reference with type="field_types" instead.
21378
+
21379
+ ${JSON.stringify(types, null, 2)}`
20382
21380
  }
20383
21381
  ]
20384
21382
  };
@@ -20386,14 +21384,16 @@ server.tool(
20386
21384
  );
20387
21385
  server.tool(
20388
21386
  "list_operators",
20389
- "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.',
20390
21388
  {},
20391
21389
  async () => {
20392
21390
  return {
20393
21391
  content: [
20394
21392
  {
20395
21393
  type: "text",
20396
- text: JSON.stringify(OPERATORS, null, 2)
21394
+ text: `DEPRECATED: This tool is deprecated. Use get_reference with type="operators" instead.
21395
+
21396
+ ${JSON.stringify(OPERATORS, null, 2)}`
20397
21397
  }
20398
21398
  ]
20399
21399
  };
@@ -20401,7 +21401,7 @@ server.tool(
20401
21401
  );
20402
21402
  server.tool(
20403
21403
  "list_formula_functions",
20404
- "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.',
20405
21405
  {
20406
21406
  category: external_exports.string().optional().describe('Filter by category (e.g., "math", "string", "date")')
20407
21407
  },
@@ -20414,7 +21414,9 @@ server.tool(
20414
21414
  content: [
20415
21415
  {
20416
21416
  type: "text",
20417
- text: JSON.stringify(functions, null, 2)
21417
+ text: `DEPRECATED: This tool is deprecated. Use get_reference with type="formula_functions" instead.
21418
+
21419
+ ${JSON.stringify(functions, null, 2)}`
20418
21420
  }
20419
21421
  ]
20420
21422
  };
@@ -20422,14 +21424,16 @@ server.tool(
20422
21424
  );
20423
21425
  server.tool(
20424
21426
  "list_validation_options",
20425
- "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.',
20426
21428
  {},
20427
21429
  async () => {
20428
21430
  return {
20429
21431
  content: [
20430
21432
  {
20431
21433
  type: "text",
20432
- text: JSON.stringify(VALIDATION_OPTIONS, null, 2)
21434
+ text: `DEPRECATED: This tool is deprecated. Use get_reference with type="validation_options" instead.
21435
+
21436
+ ${JSON.stringify(VALIDATION_OPTIONS, null, 2)}`
20433
21437
  }
20434
21438
  ]
20435
21439
  };
@@ -20437,14 +21441,16 @@ server.tool(
20437
21441
  );
20438
21442
  server.tool(
20439
21443
  "list_theme_options",
20440
- "List all available theme customization options.",
21444
+ '[DEPRECATED - use get_reference with type="theme_options" instead] List all available theme customization options.',
20441
21445
  {},
20442
21446
  async () => {
20443
21447
  return {
20444
21448
  content: [
20445
21449
  {
20446
21450
  type: "text",
20447
- text: JSON.stringify(THEME_OPTIONS, null, 2)
21451
+ text: `DEPRECATED: This tool is deprecated. Use get_reference with type="theme_options" instead.
21452
+
21453
+ ${JSON.stringify(THEME_OPTIONS, null, 2)}`
20448
21454
  }
20449
21455
  ]
20450
21456
  };
@@ -20452,7 +21458,7 @@ server.tool(
20452
21458
  );
20453
21459
  server.tool(
20454
21460
  "get_documentation",
20455
- "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.',
20456
21462
  {
20457
21463
  topic: external_exports.enum(["readme", "architecture", "quick-start", "examples", "api-client"]).describe("The documentation topic to retrieve")
20458
21464
  },
@@ -20468,7 +21474,9 @@ server.tool(
20468
21474
  content: [
20469
21475
  {
20470
21476
  type: "text",
20471
- text: docs[topic] || "Documentation not found"
21477
+ text: `DEPRECATED: This tool is deprecated. Use get_reference with type="documentation" and topic="${topic}" instead.
21478
+
21479
+ ${docs[topic] || "Documentation not found"}`
20472
21480
  }
20473
21481
  ]
20474
21482
  };
@@ -20476,73 +21484,404 @@ server.tool(
20476
21484
  );
20477
21485
  server.tool(
20478
21486
  "generate_react_code",
20479
- "Generate React component code that uses @netpad/forms to render a form.",
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.",
20480
21488
  {
20481
21489
  formConfig: external_exports.string().describe("The form configuration JSON"),
20482
21490
  componentName: external_exports.string().optional().describe("Name of the React component"),
20483
21491
  includeSubmitHandler: external_exports.boolean().optional().describe("Whether to include a submit handler"),
20484
- useNetPadClient: external_exports.boolean().optional().describe("Whether to use NetPad API client for submission")
21492
+ includeApiSubmission: external_exports.boolean().optional().describe("Whether to submit to NetPad API via fetch (default: true)")
20485
21493
  },
20486
- async ({ formConfig, componentName = "MyForm", includeSubmitHandler = true, useNetPadClient = false }) => {
20487
- let code = `import { FormRenderer } from '@netpad/forms';
20488
- `;
20489
- if (useNetPadClient) {
20490
- code += `import { createNetPadClient } from '@netpad/forms';
20491
- `;
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 };
20492
21592
  }
20493
- code += `import type { FormConfiguration } from '@netpad/forms';
20494
21593
 
20495
- `;
20496
- code += `const formConfig: FormConfiguration = ${formConfig};
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
+ };
20497
21626
 
20498
- `;
20499
- if (useNetPadClient) {
20500
- code += `const client = createNetPadClient({
20501
- baseUrl: process.env.NEXT_PUBLIC_NETPAD_URL || 'https://your-netpad-instance.com',
20502
- apiKey: process.env.NETPAD_API_KEY || '',
20503
- });
21627
+ const validateForm = (): boolean => {
21628
+ const newErrors: Record<string, string> = {};
20504
21629
 
20505
- `;
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
+ }
20506
21650
  }
20507
- code += `export function ${componentName}() {
20508
- `;
20509
- if (includeSubmitHandler) {
20510
- if (useNetPadClient) {
20511
- code += ` const handleSubmit = async (data: Record<string, unknown>) => {
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
+
20512
21664
  try {
20513
- const result = await client.submitForm(formConfig.formId || formConfig.slug || '', data);
20514
- console.log('Submission successful:', result);
20515
- // Handle success (e.g., show notification, redirect)
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 });`}
20516
21669
  } catch (error) {
20517
- console.error('Submission failed:', error);
20518
- // Handle error
21670
+ setSubmitResult({
21671
+ success: false,
21672
+ error: error instanceof Error ? error.message : 'Submission failed',
21673
+ });
21674
+ } finally {
21675
+ setIsSubmitting(false);
20519
21676
  }
20520
- };
21677
+ };` : ""}
20521
21678
 
20522
- `;
20523
- } else {
20524
- code += ` const handleSubmit = async (data: Record<string, unknown>) => {
20525
- console.log('Form submitted:', data);
20526
- // TODO: Handle form submission
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
+ );
20527
21737
  };
20528
21738
 
20529
- `;
20530
- }
20531
- }
20532
- code += ` return (
20533
- <FormRenderer
20534
- config={formConfig}
20535
- onSubmit={${includeSubmitHandler ? "handleSubmit" : "undefined"}}
20536
- mode="create"
20537
- />
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>
20538
21866
  );
20539
21867
  }
21868
+
21869
+ export default ${componentName};
20540
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
+ });
20541
21880
  return {
20542
21881
  content: [
20543
21882
  {
20544
21883
  type: "text",
20545
- text: code
21884
+ text: formatToolOutput(output)
20546
21885
  }
20547
21886
  ]
20548
21887
  };
@@ -20643,7 +21982,7 @@ server.tool(
20643
21982
  );
20644
21983
  server.tool(
20645
21984
  "get_use_case_template",
20646
- "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.',
20647
21986
  {
20648
21987
  useCase: external_exports.enum(["leadCapture", "eventRegistration", "feedbackSurvey"]).describe("The use case template to retrieve")
20649
21988
  },
@@ -21032,7 +22371,7 @@ ${context ? `
21032
22371
  }
21033
22372
  server.tool(
21034
22373
  "list_application_templates",
21035
- "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.',
21036
22375
  {
21037
22376
  category: external_exports.string().optional().describe('Filter by category (e.g., "lead-generation", "events", "surveys", "hr", "ecommerce")')
21038
22377
  },
@@ -21064,7 +22403,7 @@ server.tool(
21064
22403
  );
21065
22404
  server.tool(
21066
22405
  "get_application_template",
21067
- "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.',
21068
22407
  {
21069
22408
  templateId: external_exports.enum(["contact-form", "lead-capture", "event-registration", "feedback-survey", "job-application", "order-form", "blank"]).describe("The template ID")
21070
22409
  },
@@ -21088,7 +22427,7 @@ server.tool(
21088
22427
  );
21089
22428
  server.tool(
21090
22429
  "create_application",
21091
- "Generate code to create a new NetPad application. Can use a template or start from scratch. Returns API code and configuration.",
22430
+ "Generate a single, complete TypeScript file that creates a NetPad application with all forms and workflows. Run with `npx tsx` - no SDK required.",
21092
22431
  {
21093
22432
  name: external_exports.string().describe("Name of the application"),
21094
22433
  description: external_exports.string().optional().describe("Description of the application"),
@@ -21101,20 +22440,264 @@ server.tool(
21101
22440
  organizationId: external_exports.string().describe("Organization ID")
21102
22441
  },
21103
22442
  async (options) => {
21104
- const code = generateCreateApplicationCode(options);
21105
- const config2 = generateApplicationConfig(options);
21106
- return {
21107
- content: [{
21108
- type: "text",
21109
- text: `## Application Creation Code
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
+ };
21110
22489
 
21111
- ${code}
22490
+ // Form Configurations
22491
+ const FORM_CONFIGS: FormConfig[] = ${JSON.stringify(formConfigs, null, 2)};
21112
22492
 
21113
- ## Application Configuration
22493
+ // Workflow Configurations
22494
+ const WORKFLOW_CONFIGS: WorkflowConfig[] = ${JSON.stringify(workflowConfigs, null, 2)};`;
22495
+ const functionsCode = `// ============================================================================
22496
+ // API Helper Functions
22497
+ // ============================================================================
21114
22498
 
21115
- \`\`\`json
21116
- ${JSON.stringify(config2, null, 2)}
21117
- \`\`\``
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)
21118
22701
  }]
21119
22702
  };
21120
22703
  }
@@ -21573,7 +23156,7 @@ server.resource(
21573
23156
  );
21574
23157
  server.tool(
21575
23158
  "list_workflow_templates",
21576
- "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.',
21577
23160
  {
21578
23161
  category: external_exports.string().optional().describe('Filter by category (e.g., "notifications", "data", "sales")')
21579
23162
  },
@@ -21605,7 +23188,7 @@ server.tool(
21605
23188
  );
21606
23189
  server.tool(
21607
23190
  "get_workflow_template",
21608
- "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.',
21609
23192
  {
21610
23193
  templateId: external_exports.enum(["form-to-email", "form-to-database", "lead-qualification", "webhook-to-database", "scheduled-report"]).describe("The template ID")
21611
23194
  },
@@ -21670,33 +23253,240 @@ server.tool(
21670
23253
  );
21671
23254
  server.tool(
21672
23255
  "create_workflow_from_template",
21673
- "Generate code to create a new workflow, optionally using a template.",
23256
+ "Generate a complete, self-contained TypeScript file to create a workflow. Returns validated code with inline types - run with `npx tsx`.",
21674
23257
  {
21675
23258
  name: external_exports.string().describe("Name of the workflow"),
21676
23259
  description: external_exports.string().optional().describe("Description of the workflow"),
21677
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"),
21678
23263
  applicationId: external_exports.string().optional().describe("Application ID to attach workflow to"),
21679
23264
  projectId: external_exports.string().describe("Project ID"),
21680
23265
  organizationId: external_exports.string().describe("Organization ID"),
21681
- 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")
21682
23268
  },
21683
23269
  async (options) => {
21684
- const code = generateCreateWorkflowCode(options);
21685
- const config2 = generateWorkflowConfig(options);
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) {
21686
23349
  return {
21687
- content: [{
21688
- type: "text",
21689
- text: `## Workflow Creation Code
23350
+ success: false,
23351
+ error: error instanceof Error ? error.message : 'Unknown error',
23352
+ };
23353
+ }
23354
+ }
21690
23355
 
21691
- \`\`\`typescript
21692
- ${code}
21693
- \`\`\`
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
+ );
21694
23370
 
21695
- ## Workflow Configuration
23371
+ if (!response.ok) {
23372
+ return { success: false, error: await response.text() };
23373
+ }
21696
23374
 
21697
- \`\`\`json
21698
- ${JSON.stringify(config2, null, 2)}
21699
- \`\`\``
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)
21700
23490
  }]
21701
23491
  };
21702
23492
  }
@@ -21881,7 +23671,7 @@ server.resource(
21881
23671
  );
21882
23672
  server.tool(
21883
23673
  "list_conversational_templates",
21884
- "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.',
21885
23675
  {
21886
23676
  category: external_exports.string().optional().describe('Filter by category (e.g., "support", "feedback", "intake")')
21887
23677
  },
@@ -21913,7 +23703,7 @@ server.tool(
21913
23703
  );
21914
23704
  server.tool(
21915
23705
  "get_conversational_template",
21916
- "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.',
21917
23707
  {
21918
23708
  templateId: external_exports.enum(["it-helpdesk", "customer-feedback", "lead-qualification", "patient-intake"]).describe("The template ID")
21919
23709
  },
@@ -22228,7 +24018,7 @@ server.resource(
22228
24018
  );
22229
24019
  server.tool(
22230
24020
  "list_template_categories",
22231
- "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.',
22232
24022
  {},
22233
24023
  async () => {
22234
24024
  return {
@@ -22245,7 +24035,7 @@ server.tool(
22245
24035
  );
22246
24036
  server.tool(
22247
24037
  "list_form_templates",
22248
- "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.',
22249
24039
  {
22250
24040
  category: external_exports.string().optional().describe('Filter by category (business, events, feedback, support, ecommerce, healthcare, hr, finance, education, real-estate, or "all")'),
22251
24041
  search: external_exports.string().optional().describe("Search templates by name, description, or tags")
@@ -22284,7 +24074,7 @@ server.tool(
22284
24074
  );
22285
24075
  server.tool(
22286
24076
  "get_form_template",
22287
- "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.',
22288
24078
  {
22289
24079
  templateId: external_exports.string().describe('Template ID (e.g., "contact-form", "lead-capture", "patient-intake")')
22290
24080
  },
@@ -22467,7 +24257,7 @@ ${code}
22467
24257
  );
22468
24258
  server.tool(
22469
24259
  "list_query_templates",
22470
- "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.',
22471
24261
  {},
22472
24262
  async () => {
22473
24263
  const templates = listQueryTemplates();
@@ -22484,7 +24274,7 @@ server.tool(
22484
24274
  );
22485
24275
  server.tool(
22486
24276
  "get_query_template",
22487
- "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.',
22488
24278
  {
22489
24279
  templateId: external_exports.string().describe('Template ID (e.g., "find-all", "aggregate-group-count")')
22490
24280
  },