@netpad/mcp-server 2.4.2 → 2.4.4

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/README.md CHANGED
@@ -1,6 +1,40 @@
1
1
  # @netpad/mcp-server
2
2
 
3
- An MCP (Model Context Protocol) server for AI-assisted NetPad application development. This comprehensive toolkit provides **75 tools** for building forms, workflows, applications, and data-driven experiences with the NetPad platform.
3
+ An MCP (Model Context Protocol) server for AI-assisted NetPad application development. This comprehensive toolkit provides **80+ tools** for building forms, workflows, applications, and data-driven experiences with the NetPad platform.
4
+
5
+ > **Build forms in minutes, not hours.** Describe what you need to Claude, get a working form with one click.
6
+ >
7
+ > Learn more at [netpad.io/mcp](https://netpad.io/mcp)
8
+
9
+ ## Build from Claude
10
+
11
+ The most powerful way to use NetPad is through conversational building with Claude:
12
+
13
+ ```
14
+ You: "I need a customer feedback form with rating, comments, and contact info"
15
+
16
+ Claude: I've generated your feedback form with:
17
+ - Star rating field (1-5)
18
+ - Comments textarea with 500 character limit
19
+ - Email and phone fields with validation
20
+
21
+ Click to import: https://netpad.io/import/imp_abc123
22
+ ```
23
+
24
+ **That's it.** One conversation, one click, working form.
25
+
26
+ ### How It Works
27
+
28
+ 1. **Describe** - Tell Claude what form you need in plain English
29
+ 2. **Generate** - Claude uses `generate_form` to create the configuration
30
+ 3. **Import** - Click the link or use `create_import_link` for a shareable URL
31
+ 4. **Customize** - Fine-tune in NetPad's visual builder if needed
32
+
33
+ ## What's New in v2.4.0
34
+
35
+ - **One-Click Import Links**: `create_import_link` tool generates shareable URLs for instant form import
36
+ - **Import URLs in Output**: `generate_form` now includes a direct import link in the response
37
+ - **Deep Link Integration**: Forms created in Claude can be imported to NetPad Cloud with one click
4
38
 
5
39
  ## What's New in v2.0.0
6
40
 
@@ -97,7 +131,13 @@ Add to `~/.cursor/mcp.json`:
97
131
  }
98
132
  ```
99
133
 
100
- ## Available Tools (75 total)
134
+ ## Available Tools (80+ total)
135
+
136
+ ### Import & Sharing Tools (1) - NEW
137
+
138
+ | Tool | Description |
139
+ |------|-------------|
140
+ | `create_import_link` | Generate a shareable one-click import URL for any form configuration |
101
141
 
102
142
  ### Form Building Tools (6)
103
143
 
@@ -298,6 +338,44 @@ The server exposes documentation as MCP resources:
298
338
 
299
339
  ## Example Interactions
300
340
 
341
+ ### Build a Form with One Click
342
+
343
+ ```
344
+ User: I need a job application form with resume upload, experience level,
345
+ and availability
346
+
347
+ Claude: [Uses generate_form tool]
348
+ I've generated your job application form with:
349
+ - Resume file upload (PDF, DOC, DOCX - max 10MB)
350
+ - Experience level dropdown (Entry, Mid, Senior, Executive)
351
+ - Availability date picker
352
+ - Cover letter textarea
353
+ - Contact information section
354
+
355
+ Click to import: https://netpad.io/import/imp_xyz789
356
+
357
+ The form is ready to use - just click the link to add it to your NetPad workspace.
358
+ ```
359
+
360
+ ### Generate and Share an Import Link
361
+
362
+ ```
363
+ User: Can you create a patient intake form and give me a link I can share
364
+ with my team?
365
+
366
+ Claude: [Uses generate_form, then create_import_link]
367
+ Here's your patient intake form with:
368
+ - Personal information section
369
+ - Medical history fields
370
+ - Insurance details
371
+ - Emergency contact
372
+
373
+ Shareable import link: https://netpad.io/import/imp_abc123
374
+
375
+ This link expires in 7 days. Anyone with a NetPad account can click it
376
+ to import this form into their workspace.
377
+ ```
378
+
301
379
  ### Create an Application
302
380
 
303
381
  ```
@@ -386,9 +464,11 @@ echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_form_
386
464
 
387
465
  ## Related Packages
388
466
 
467
+ - [@netpad/mcp-server-remote](https://www.npmjs.com/package/@netpad/mcp-server-remote) - Remote HTTP MCP server for Claude custom connectors
389
468
  - [@netpad/forms](https://www.npmjs.com/package/@netpad/forms) - React form renderer library
390
469
  - [@netpad/workflows](https://www.npmjs.com/package/@netpad/workflows) - Workflow automation client
391
470
  - [NetPad Platform](https://netpad.io) - Full form builder platform with database integration
471
+ - [NetPad MCP Guide](https://netpad.io/mcp) - Learn more about building with Claude
392
472
 
393
473
  ## License
394
474
 
package/dist/index.js CHANGED
@@ -21826,6 +21826,21 @@ function formatToolOutput(output) {
21826
21826
  }
21827
21827
 
21828
21828
  // src/index.ts
21829
+ import {
21830
+ UI_RESOURCES
21831
+ } from "@netpad/mcp-apps";
21832
+ function withUIResource(textContent, resourceId, data) {
21833
+ const resource = UI_RESOURCES[resourceId];
21834
+ return {
21835
+ content: [{ type: "text", text: textContent }],
21836
+ _meta: {
21837
+ ui: {
21838
+ resourceUri: resource.uri,
21839
+ data
21840
+ }
21841
+ }
21842
+ };
21843
+ }
21829
21844
  function createNetPadMcpServer(options) {
21830
21845
  const server = new McpServer({
21831
21846
  name: options?.name ?? "@netpad/mcp-server",
@@ -22204,14 +22219,26 @@ This will open NetPad where you can:
22204
22219
  If you prefer to work with code, here's the complete TypeScript implementation:
22205
22220
 
22206
22221
  ${formatToolOutput(output)}`;
22207
- return {
22208
- content: [
22209
- {
22210
- type: "text",
22211
- text: outputWithImport
22212
- }
22213
- ]
22222
+ const formPreviewData = {
22223
+ form: {
22224
+ name: schema.name,
22225
+ description: schema.description,
22226
+ fields: (schema.fieldConfigs || []).map((f) => ({
22227
+ id: f.path,
22228
+ type: f.type,
22229
+ label: f.label,
22230
+ placeholder: f.placeholder,
22231
+ required: f.required,
22232
+ options: f.options
22233
+ })),
22234
+ theme: schema.theme,
22235
+ multiPage: schema.multiPage
22236
+ },
22237
+ theme: "dark",
22238
+ mode: "traditional",
22239
+ readonly: true
22214
22240
  };
22241
+ return withUIResource(outputWithImport, "form-preview", formPreviewData);
22215
22242
  }
22216
22243
  );
22217
22244
  server.tool(
@@ -22650,17 +22677,27 @@ Available topics: ${Object.keys(docs).join(", ")}`
22650
22677
  fieldCount: t.fields.length,
22651
22678
  hasMultiPage: !!t.multiPage?.enabled
22652
22679
  }));
22653
- return {
22654
- content: [{
22655
- type: "text",
22656
- text: JSON.stringify({
22657
- templateType: "form",
22658
- templates: summary,
22659
- total: summary.length,
22660
- categories: [...new Set(templates.map((t) => t.category))]
22661
- }, null, 2)
22662
- }]
22680
+ const textResponse = JSON.stringify({
22681
+ templateType: "form",
22682
+ templates: summary,
22683
+ total: summary.length,
22684
+ categories: [...new Set(templates.map((t) => t.category))]
22685
+ }, null, 2);
22686
+ const galleryItems = templates.map((t) => ({
22687
+ id: t.id,
22688
+ name: t.name,
22689
+ description: t.description,
22690
+ category: t.category,
22691
+ tags: t.tags,
22692
+ icon: t.icon || "\u{1F4DD}"
22693
+ }));
22694
+ const galleryData = {
22695
+ templates: galleryItems,
22696
+ templateType: "form",
22697
+ theme: "dark",
22698
+ showCategories: true
22663
22699
  };
22700
+ return withUIResource(textResponse, "template-gallery", galleryData);
22664
22701
  }
22665
22702
  case "application": {
22666
22703
  if (action === "categories") {
@@ -22748,7 +22785,33 @@ Available topics: ${Object.keys(docs).join(", ")}`
22748
22785
  edges: template.edges
22749
22786
  }
22750
22787
  };
22751
- return { content: [{ type: "text", text: JSON.stringify(workflowForUI, null, 2) }] };
22788
+ const uiData = {
22789
+ workflow: {
22790
+ nodes: template.nodes.map((n) => ({
22791
+ id: n.id,
22792
+ type: n.type,
22793
+ position: n.position,
22794
+ data: { label: n.label || n.type, ...n.config }
22795
+ })),
22796
+ edges: template.edges.map((e) => ({
22797
+ id: e.id,
22798
+ source: e.source,
22799
+ target: e.target,
22800
+ sourceHandle: e.sourceHandle,
22801
+ targetHandle: e.targetHandle,
22802
+ label: e.condition?.label
22803
+ }))
22804
+ },
22805
+ theme: "dark",
22806
+ fitView: true,
22807
+ showMinimap: true,
22808
+ autoLayout: true
22809
+ };
22810
+ return withUIResource(
22811
+ JSON.stringify(workflowForUI, null, 2),
22812
+ "workflow-viewer",
22813
+ uiData
22814
+ );
22752
22815
  }
22753
22816
  let templates = Object.values(WORKFLOW_TEMPLATES);
22754
22817
  if (category) {
@@ -22759,17 +22822,27 @@ Available topics: ${Object.keys(docs).join(", ")}`
22759
22822
  nodesCount: t.nodes.length,
22760
22823
  edgesCount: t.edges.length
22761
22824
  }));
22762
- return {
22763
- content: [{
22764
- type: "text",
22765
- text: JSON.stringify({
22766
- templateType: "workflow",
22767
- templates: summary,
22768
- total: summary.length,
22769
- categories: [...new Set(Object.values(WORKFLOW_TEMPLATES).map((t) => t.category))]
22770
- }, null, 2)
22771
- }]
22825
+ const textResponse = JSON.stringify({
22826
+ templateType: "workflow",
22827
+ templates: summary,
22828
+ total: summary.length,
22829
+ categories: [...new Set(Object.values(WORKFLOW_TEMPLATES).map((t) => t.category))]
22830
+ }, null, 2);
22831
+ const galleryItems = templates.map((t) => ({
22832
+ id: t.id,
22833
+ name: t.name,
22834
+ description: t.description,
22835
+ category: t.category,
22836
+ tags: t.tags,
22837
+ icon: "\u26A1"
22838
+ }));
22839
+ const galleryData = {
22840
+ templates: galleryItems,
22841
+ templateType: "workflow",
22842
+ theme: "dark",
22843
+ showCategories: true
22772
22844
  };
22845
+ return withUIResource(textResponse, "template-gallery", galleryData);
22773
22846
  }
22774
22847
  case "conversational": {
22775
22848
  if (action === "categories") {
@@ -26290,6 +26363,249 @@ You can use any valid hex color.`
26290
26363
  };
26291
26364
  }
26292
26365
  );
26366
+ server.tool(
26367
+ "import_google_form",
26368
+ `Import a Google Form into NetPad. This tool generates the API call and mapping preview for importing a Google Form.
26369
+
26370
+ IMPORTANT: Before using this tool, the user must have:
26371
+ 1. Connected their Google account via the NetPad UI (Settings > Integrations > Google Forms)
26372
+ 2. Obtained a credentialId for their Google Forms connection
26373
+
26374
+ The tool returns:
26375
+ - Instructions for completing the import via the NetPad API
26376
+ - A preview of how the Google Form fields will be mapped to NetPad field types
26377
+ - Any warnings about fields that may need manual adjustment`,
26378
+ {
26379
+ formId: external_exports.string().describe("The Google Form ID to import. Can be found in the form URL: https://docs.google.com/forms/d/{FORM_ID}/edit"),
26380
+ organizationId: external_exports.string().describe("The NetPad organization ID"),
26381
+ projectId: external_exports.string().describe("The NetPad project ID where the form will be created"),
26382
+ credentialId: external_exports.string().optional().describe("The credential ID for the Google Forms connection. If not provided, the user must set it up via the UI first."),
26383
+ preview: external_exports.boolean().optional().describe("If true, only return the mapping preview without creating the form (default: true)")
26384
+ },
26385
+ async ({ formId, organizationId, projectId, credentialId, preview = true }) => {
26386
+ const baseUrl = "https://netpad.io";
26387
+ const fieldTypeMapping = `
26388
+ ## Google Forms to NetPad Field Type Mapping
26389
+
26390
+ | Google Forms Type | NetPad Type | Confidence |
26391
+ |-------------------|-------------|------------|
26392
+ | SHORT_ANSWER | short_text | Exact |
26393
+ | PARAGRAPH_TEXT | long_text | Exact |
26394
+ | MULTIPLE_CHOICE | radio | Exact |
26395
+ | CHECKBOXES | checkbox_group | Exact |
26396
+ | DROPDOWN | dropdown | Exact |
26397
+ | LINEAR_SCALE | rating | Exact |
26398
+ | MULTIPLE_CHOICE_GRID | matrix | Approximate |
26399
+ | CHECKBOX_GRID | matrix | Approximate |
26400
+ | DATE | date | Exact |
26401
+ | TIME | time | Exact |
26402
+ | FILE_UPLOAD | file | Exact |
26403
+ `;
26404
+ if (!credentialId) {
26405
+ return {
26406
+ content: [{
26407
+ type: "text",
26408
+ text: `# Google Forms Import
26409
+
26410
+ ## Setup Required
26411
+
26412
+ Before importing Google Forms, you need to connect your Google account:
26413
+
26414
+ 1. Go to **Settings > Integrations** in NetPad
26415
+ 2. Click **Connect Google Account** under Google Forms
26416
+ 3. Authorize NetPad to read your forms
26417
+ 4. Copy the credential ID from the connection details
26418
+
26419
+ Or use this URL to start the OAuth flow:
26420
+ \`\`\`
26421
+ ${baseUrl}/api/auth/google?provider=google_forms&orgId=${organizationId}
26422
+ \`\`\`
26423
+
26424
+ ## Once Connected
26425
+
26426
+ Call this tool again with the \`credentialId\` parameter to preview or import the form.
26427
+
26428
+ ${fieldTypeMapping}`
26429
+ }]
26430
+ };
26431
+ }
26432
+ const previewApiCall = `
26433
+ ## Preview Import
26434
+
26435
+ To see how the Google Form will be mapped to NetPad fields:
26436
+
26437
+ \`\`\`bash
26438
+ curl "${baseUrl}/api/integrations/google-forms/preview?\\
26439
+ orgId=${organizationId}&\\
26440
+ credentialId=${credentialId}&\\
26441
+ formId=${formId}"
26442
+ \`\`\`
26443
+
26444
+ Or using fetch:
26445
+
26446
+ \`\`\`typescript
26447
+ const response = await fetch(
26448
+ '${baseUrl}/api/integrations/google-forms/preview?' + new URLSearchParams({
26449
+ orgId: '${organizationId}',
26450
+ credentialId: '${credentialId}',
26451
+ formId: '${formId}',
26452
+ })
26453
+ );
26454
+ const preview = await response.json();
26455
+ console.log('Fields:', preview.previewConfig.fieldConfigs);
26456
+ console.log('Warnings:', preview.mappingResult.warnings);
26457
+ \`\`\`
26458
+ `;
26459
+ const importApiCall = `
26460
+ ## Execute Import
26461
+
26462
+ To create the NetPad form from the Google Form:
26463
+
26464
+ \`\`\`bash
26465
+ curl -X POST "${baseUrl}/api/integrations/google-forms/import" \\
26466
+ -H "Content-Type: application/json" \\
26467
+ -d '{
26468
+ "orgId": "${organizationId}",
26469
+ "credentialId": "${credentialId}",
26470
+ "formId": "${formId}",
26471
+ "projectId": "${projectId}"
26472
+ }'
26473
+ \`\`\`
26474
+
26475
+ Or using fetch:
26476
+
26477
+ \`\`\`typescript
26478
+ const response = await fetch('${baseUrl}/api/integrations/google-forms/import', {
26479
+ method: 'POST',
26480
+ headers: { 'Content-Type': 'application/json' },
26481
+ body: JSON.stringify({
26482
+ orgId: '${organizationId}',
26483
+ credentialId: '${credentialId}',
26484
+ formId: '${formId}',
26485
+ projectId: '${projectId}',
26486
+ // Optional customizations:
26487
+ customizations: {
26488
+ name: 'Custom Form Name', // Override the form name
26489
+ fieldOverrides: { // Modify specific field mappings
26490
+ 'field_path': { type: 'dropdown' }
26491
+ },
26492
+ excludeFields: ['unwanted_field'] // Skip certain fields
26493
+ }
26494
+ })
26495
+ });
26496
+
26497
+ const result = await response.json();
26498
+ if (result.success) {
26499
+ console.log('Form created:', result.form.id);
26500
+ console.log('Edit URL:', result.importReport.viewUrl);
26501
+ }
26502
+ \`\`\`
26503
+ `;
26504
+ return {
26505
+ content: [{
26506
+ type: "text",
26507
+ text: `# Google Forms Import
26508
+
26509
+ **Form ID:** ${formId}
26510
+ **Organization:** ${organizationId}
26511
+ **Project:** ${projectId}
26512
+ **Credential:** ${credentialId}
26513
+
26514
+ ${previewApiCall}
26515
+
26516
+ ${preview ? "" : importApiCall}
26517
+
26518
+ ${fieldTypeMapping}
26519
+
26520
+ ## Using the Import Wizard UI
26521
+
26522
+ Alternatively, users can import Google Forms through the NetPad UI:
26523
+
26524
+ 1. Go to **Forms** in your project
26525
+ 2. Click **Import** > **Import from Google Forms**
26526
+ 3. Select the Google account and form
26527
+ 4. Review the field mappings
26528
+ 5. Click **Import**
26529
+
26530
+ ## Notes
26531
+
26532
+ - Only form structure is imported, not responses/submissions
26533
+ - Some field types may be approximated (see mapping table)
26534
+ - Validation rules are preserved where possible
26535
+ - Section/page breaks become multi-page form pages
26536
+ - Images and videos in the form are not imported`
26537
+ }]
26538
+ };
26539
+ }
26540
+ );
26541
+ server.tool(
26542
+ "list_google_forms",
26543
+ "List Google Forms available for import. Requires a connected Google account.",
26544
+ {
26545
+ organizationId: external_exports.string().describe("The NetPad organization ID"),
26546
+ credentialId: external_exports.string().describe("The credential ID for the Google Forms connection"),
26547
+ searchQuery: external_exports.string().optional().describe("Search query to filter forms by name")
26548
+ },
26549
+ async ({ organizationId, credentialId, searchQuery }) => {
26550
+ const baseUrl = "https://netpad.io";
26551
+ const params = new URLSearchParams({
26552
+ orgId: organizationId,
26553
+ credentialId
26554
+ });
26555
+ if (searchQuery) {
26556
+ params.set("q", searchQuery);
26557
+ }
26558
+ return {
26559
+ content: [{
26560
+ type: "text",
26561
+ text: `# List Google Forms
26562
+
26563
+ To list forms available for import, make this API call:
26564
+
26565
+ \`\`\`bash
26566
+ curl "${baseUrl}/api/integrations/google-forms?${params.toString()}"
26567
+ \`\`\`
26568
+
26569
+ Or using fetch:
26570
+
26571
+ \`\`\`typescript
26572
+ const response = await fetch(
26573
+ '${baseUrl}/api/integrations/google-forms?${params.toString()}'
26574
+ );
26575
+ const data = await response.json();
26576
+
26577
+ // data.forms contains:
26578
+ // - id: Form ID for importing
26579
+ // - name: Form title
26580
+ // - modifiedTime: Last modified date
26581
+ // - webViewLink: Link to edit form in Google
26582
+
26583
+ for (const form of data.forms) {
26584
+ console.log(\`\${form.name} (ID: \${form.id})\`);
26585
+ }
26586
+ \`\`\`
26587
+
26588
+ ## Response Format
26589
+
26590
+ \`\`\`json
26591
+ {
26592
+ "forms": [
26593
+ {
26594
+ "id": "1BxiMVs0XRA5nFMdkkxYYfYpHm5PZ-abc123",
26595
+ "name": "Customer Feedback Survey",
26596
+ "modifiedTime": "2024-01-15T10:30:00Z",
26597
+ "webViewLink": "https://docs.google.com/forms/d/.../edit"
26598
+ }
26599
+ ],
26600
+ "nextPageToken": "..." // For pagination
26601
+ }
26602
+ \`\`\`
26603
+
26604
+ Use the \`import_google_form\` tool with the form ID to import a specific form.`
26605
+ }]
26606
+ };
26607
+ }
26608
+ );
26293
26609
  server.resource(
26294
26610
  "netpad-extension-reference",
26295
26611
  "netpad://reference/extensions",