Skill: sol1986/mindstudio-to-api-custom-function-builder (version 1.1.0)

--- name: mindstudio-to-api-custom-function-builder description: Build a complete MindStudio Run Function integration for any API the user wants to connect. Use this skill whenever the user mentions MindStudio, wants to connect an API to a workflow, asks for a "Run Function", wants to call an external service from MindStudio, or says things like "I want to use X API in MindStudio", "build me a MindStudio function for X", "how do I connect X to MindStudio", or "write me a script for MindStudio". Always use this skill — do not try to wing it from memory. --- # MindStudio Function Builder Produce a complete, ready-to-paste MindStudio custom function for any API the user wants to connect. Always output all three sections: **Code Tab**, **Configuration Tab**, and **Test Data Tab**. --- ## What to gather before writing Before writing, collect (from the conversation or by asking): 1. **API endpoint** — the full URL to call 2. **HTTP method** — GET, POST, etc. 3. **Authentication** — API key header name, Bearer token, Basic auth, OAuth, or none 4. **Required fields** — what parameters/body fields the API needs 5. **Optional fields** — any useful optional params (like `research_effort`, `language`, `model`, etc.) 6. **Response shape** — what fields come back (ask or infer from docs/examples) 7. **Output variables** — what the user wants to store in their workflow after the call 8. **Execution environment** — Sandbox (default, fast) or Virtual Machine (needed for npm packages) If the user pastes API docs or a code example, extract all of the above from it directly. Only ask for what's missing. --- ## Output Format Always produce exactly these three sections, in this order, formatted as fenced code blocks. --- ### Section 1 — Code Tab (JavaScript) Rules: - Read all user-configurable values from `ai.config.*` - **CRITICAL — input vs output variable pattern:** - `inputVariable` type config fields are resolved by MindStudio BEFORE the code runs. The value arrives as a plain string. Always read them as `ai.config.fieldName` directly. NEVER use `ai.vars[ai.config.fieldName]` for inputs. - `outputVariableName` type config fields hold the NAME of a workflow variable chosen by the user. Always write outputs as `ai.vars[ai.config.outputVarName] = value` so results land in the right place. - Always validate required fields and throw clear errors if missing - Use `ai.log(...)` to show progress to the user during long calls - Use `await fetch(...)` for HTTP calls — no imports needed in Sandbox - Wrap the fetch in try/catch and re-throw with a readable message - Use `input` not `query` for You.com APIs (learned fix) - For Virtual Machine functions, export a named `handler` async function Template structure: ```javascript // --- Read config --- const [REDACTED_SECRET]; const inputVal = ai.config.inputField; // inputVariable type: value is already resolved, read directly from ai.config const optional = ai.config.optionalField || "default_value"; // --- Validate --- if (!apiKey) throw new Error("Missing API key. Set it in block configuration."); if (!inputVal) throw new Error("Missing [field]. Provide it in block configuration."); ai.log("Calling [API name]..."); // --- Request --- const url = "https://api.example.com/endpoint"; const res = await fetch(url, { method: "POST", // or GET, etc. headers: { "Authorization": `Bearer ${apiKey}`, // adjust per API auth style "Content-Type": "application/json" }, body: JSON.stringify({ field: inputVal, option: optional }) }); if (!res.ok) { const err = await res.text(); throw new Error(`[API name] error (${res.status}): ${err}`); } const data = await res.json(); ai.log("Done. Storing results..."); // --- Store outputs --- // outputVariableName type: user names the variable, so use ai.vars[ai.config.outputMain] pattern ai.vars[ai.config.outputMain] = data.someField ?? JSON.stringify(data); ai.vars[ai.config.outputRaw] = JSON.stringify(data); ``` --- ### Section 2 — Configuration Tab Rules: - Use `"secret"` type for API keys — never `"text"` - Use `"inputVariable"` type for fields that should accept a `{{variable}}` from the workflow. MindStudio resolves the value before code runs — read it in code as `ai.config.fieldName`, not via `ai.vars` - Use `"select"` type with `selectOptions` for fixed-choice fields (e.g. model names, effort levels) - Use `"outputVariableName"` type for fields where the user names their output variables - Group into logical sections with clear `title` values - Add `helpText` to every item - Never include a dropdown for something the user controls via a workflow variable Template: ```javascript config = { configurationSections: [ { title: "API Settings", items: [ { type: "secret", label: "API Key", variable: "apiKey", helpText: "Your [API name] API key. Stored securely, not transferred on remix." }, { type: "inputVariable", label: "Input Field Label", variable: "inputField", helpText: "Description of what this input does. Use a {{variable}} from your workflow or type a value directly." }, { type: "select", label: "Option Label", variable: "optionalField", helpText: "Description of this option.", selectOptions: [ { label: "Option A", value: "value_a" }, { label: "Option B", value: "value_b" } ] } ] }, { title: "Output", items: [ { type: "outputVariableName", label: "Main Output Variable", variable: "outputMain", helpText: "Workflow variable where the main result will be stored." }, { type: "outputVariableName", label: "Raw Response Variable", variable: "outputRaw", helpText: "Workflow variable for the full raw JSON response." } ] } ] } ``` --- ### Section 3 — Test Data Tab Rules: - Mirror every `ai.config.*` variable used in the code - Use realistic placeholder values (not "test123") - Use the actual default output variable names the user will likely use - Match the output variable name values to what the user typed in the Output section config fields - For `inputVariable` type fields, set the value directly in `config` (not in `vars`) — e.g. `inputField: "AAPL"`. MindStudio does not require a vars entry for these during testing. Template: ```javascript environment = { vars: {}, config: { [REDACTED_SECRET]", inputField: "A realistic example input for this API", optionalField: "value_a", outputMain: "result", outputRaw: "resultJSON" } } ``` --- ## API Authentication Patterns | Auth type | Header / approach | |-----------|-------------------| | API key header | `"X-API-Key": apiKey` | | Bearer token | `"Authorization": \`Bearer ${apiKey}\`` | | Basic auth | `"Authorization": "Basic " + btoa(user + ":" + pass)` | | No auth | Omit headers object or leave empty | --- ## Execution Environment Notes **Sandbox** (default — use unless told otherwise): - Runs in <50ms - No npm installs - `fetch`, `JSON`, `ai.*` methods all available - Python via Pyodide (no pip in Sandbox) - Does NOT support browser/Node APIs like `URLSearchParams`, `Buffer`, `process`, `require`, `crypto`, `fs`, or `path` **Virtual Machine** (use when npm packages needed): - Slower (~5s+ startup) - Full Node.js or Python - Must export a `handler` function: ```javascript import * as someLib from 'some-lib'; export const handler = async () => { // your code here ai.vars.result = someLib.doSomething(); }; ``` --- ## Common Mistakes to Avoid - **NEVER use `ai.vars[ai.config.fieldName]` for input fields.** `inputVariable` type config fields are resolved by MindStudio before the code runs and arrive as plain strings via `ai.config.fieldName`. Only use the `ai.vars[ai.config.outputVarName]` pattern for output variables where the user has named where results should land. - Never use `URLSearchParams` in Sandbox — it is not available. Build query strings manually instead: ```javascript let query = "?token=" + apiKey; if (symbol) query += "&symbol=" + encodeURIComponent(symbol); const url = "https://api.example.com/endpoint" + query; ``` - Never use `Buffer`, `process`, `require`, `crypto`, or `fs` in Sandbox — these are Node.js APIs not available there - Never use `query` as a body field for You.com APIs — use `input` - Never use backtick template literals to inject `{{variables}}` into HTML — use `<script type="application/json">` instead - Never hardcode API keys in the Code Tab — always use `ai.config.apiKey` with `"secret"` type - Never use `ai.vars.outputName = value` when the output variable name is user-configured — use `ai.vars[ai.config.outputVarName] = value` - Don't add a config dropdown for a value the user sets via a workflow variable — read it from `ai.vars` instead --- ## After Delivering the Function Always end with a short note explaining: 1. What each output variable contains 2. Which config fields the user needs to fill in before running 3. Any gotchas specific to this API (rate limits, required plan tier, field naming quirks, etc.)

path: EXAMPLES.md
content: # scripts/

This folder contains reference examples of functions generated by this skill.
Use these as a reference to understand the expected output quality and structure.

---

## examples/youcom-research.js — You.com Research API

A complete working example generated by this skill for the You.com `/v1/research` endpoint.

### Code Tab
```javascript
const [REDACTED_SECRET];
const query          = ai.config.query;
const researchEffort = ai.config.researchEffort || "standard";

if (!apiKey) throw new Error("Missing API key. Set it in block configuration.");
if (!query)  throw new Error("Missing query. Provide it in block configuration.");

ai.log("Sending research request to You.com...");

const url = "https://api.you.com/v1/research";
const res = await fetch(url, {
  method: "POST",
  headers: {
    "X-API-Key": apiKey,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    input: query,                    // Note: You.com uses "input" not "query"
    research_effort: researchEffort
  })
});

if (!res.ok) {
  const err = await res.text();
  throw new Error("You.com API error (" + res.status + "): " + err);
}

const data = await res.json();
ai.log("Research complete. Storing results...");

ai.vars[ai.config.researchAnswer] = data.answer ?? data.response ?? JSON.stringify(data);
ai.vars[ai.config.researchRaw]    = JSON.stringify(data);
```

### Configuration Tab
```javascript
config = {
  configurationSections: [
    {
      title: "You.com API Settings",
      items: [
        {
          type: "secret",
          label: "API Key",
          variable: "apiKey",
          helpText: "Your You.com API key. Stored securely and not transferred on remix."
        },
        {
          type: "inputVariable",
          label: "Research Query",
          variable: "query",
          helpText: "The research question to send to You.com. Use a {{variable}} from your workflow."
        },
        {
          type: "select",
          label: "Research Effort",
          variable: "researchEffort",
          helpText: "Controls how deeply You.com researches the query.",
          selectOptions: [
            { label: "Standard", value: "standard" },
            { label: "High", value: "high" }
          ]
        }
      ]
    },
    {
      title: "Output",
      items: [
        {
          type: "outputVariableName",
          label: "Answer Variable",
          variable: "researchAnswer",
          helpText: "The variable name where the research answer will be stored."
        },
        {
          type: "outputVariableName",
          label: "Raw Response Variable",
          variable: "researchRaw",
          helpText: "The full raw JSON response from You.com, for advanced use."
        }
      ]
    }
  ]
}
```

### Test Data Tab
```javascript
environment = {
  vars: {},
  config: {
    [REDACTED_SECRET]",
    query:           "What are the latest advancements in fusion energy as of 2025?",
    researchEffort:  "standard",
    researchAnswer:  "answer",
    researchRaw:     "answerJSON"
  }
}
```

---

## examples/finnhub-company-profile.js — Finnhub Company Profile API

A complete working example for the Finnhub `/api/v1/stock/profile2` endpoint.
Demonstrates GET request with query param auth and multiple optional lookup fields.

### Code Tab
```javascript
const [REDACTED_SECRET];
const symbol = ai.config.symbol || ai.vars.symbol;
const isin   = ai.config.isin   || ai.vars.isin   || "";
const cusip  = ai.config.cusip  || ai.vars.cusip  || "";

if (!apiKey) throw new Error("Missing API key. Set it in block configuration.");
if (!symbol && !isin && !cusip) throw new Error("Provide at least one of: symbol, ISIN, or CUSIP.");

ai.log("Fetching company profile from Finnhub...");

// Note: URLSearchParams is NOT available in Sandbox — build manually
let query = "?token=" + apiKey;
if (symbol) query += "&symbol=" + encodeURIComponent(symbol);
if (isin)   query += "&isin="   + encodeURIComponent(isin);
if (cusip)  query += "&cusip="  + encodeURIComponent(cusip);

const url = "https://finnhub.io/api/v1/stock/profile2" + query;

try {
  const res = await fetch(url);

  if (!res.ok) {
    const err = await res.text();
    throw new Error("Finnhub error (" + res.status + "): " + err);
  }

  const data = await res.json();

  if (!data || !data.name) {
    throw new Error("No company found for the given symbol/ISIN/CUSIP.");
  }

  ai.log("Profile retrieved. Storing results...");

  ai.vars[ai.config.outputProfile] = JSON.stringify(data);
  ai.vars[ai.config.outputName]    = data.name || "";
  ai.vars[ai.config.outputSummary] =
    data.name + " (" + data.ticker + ") — " + data.finnhubIndustry + ", " + data.country + ". " +
    "Market cap: $" + (data.marketCapitalization ? data.marketCapitalization.toLocaleString() : "N/A") + "M. " +
    "Exchange: " + data.exchange + ". IPO: " + data.ipo + ". Website: " + data.weburl;

} catch (error) {
  ai.log("Error: " + error.message);
  throw error;
}
```

### Configuration Tab
```javascript
config = {
  configurationSections: [
    {
      title: "Finnhub API Settings",
      items: [
        {
          type: "secret",
          label: "API Key",
          variable: "apiKey",
          helpText: "Your Finnhub API key. Get one free at finnhub.io."
        },
        {
          type: "inputVariable",
          label: "Stock Symbol",
          variable: "symbol",
          helpText: "Ticker symbol, e.g. AAPL, TSLA. Use a {{variable}} from your workflow."
        },
        {
          type: "text",
          label: "ISIN (optional)",
          variable: "isin",
          helpText: "e.g. US0378331005. Leave blank if using symbol."
        },
        {
          type: "text",
          label: "CUSIP (optional)",
          variable: "cusip",
          helpText: "e.g. 037833100. Leave blank if using symbol."
        }
      ]
    },
    {
      title: "Output",
      items: [
        {
          type: "outputVariableName",
          label: "Full Profile Variable",
          variable: "outputProfile",
          helpText: "Stores the complete raw JSON profile."
        },
        {
          type: "outputVariableName",
          label: "Company Name Variable",
          variable: "outputName",
          helpText: "Stores just the company name as plain text."
        },
        {
          type: "outputVariableName",
          label: "Summary Variable",
          variable: "outputSummary",
          helpText: "Stores a readable one-line summary of the company."
        }
      ]
    }
  ]
}
```

### Test Data Tab
```javascript
environment = {
  vars: {},
  config: {
    [REDACTED_SECRET]",
    symbol:        "AAPL",
    isin:          "",
    cusip:         "",
    outputProfile: "companyProfile",
    outputName:    "companyName",
    outputSummary: "companySummary"
  }
}
```


path: README.md
content: # MindStudio To API Custom Function Builder — Claude Skill

A Claude skill that generates complete, ready-to-paste **MindStudio Run Function** integrations for any API you want to drop into your Run Function block.

---

## What it does

Point Claude at any API — paste the docs URL, a code snippet, or just describe what you want — and it produces all three sections you need to paste into MindStudio:

- **Code Tab** — JavaScript that calls the API, validates inputs, logs progress, and stores outputs
- **Configuration Tab** — the config JSON that renders fields in the MindStudio block panel (API key, inputs, output variable names)
- **Test Data Tab** — realistic test values so you can click Test immediately

If you paste API docs or a code example, Claude extracts everything it needs from that directly and only asks for what's missing.

---

## Example usage

> "Build me a MindStudio function for the Finnhub company profile API"

> "I want to connect the OpenAI chat completions endpoint to MindStudio"

> "Write me a MindStudio Run Function for Airtable — here are the docs: [url]"

> "Build a MindStudio function for Stripe to look up a customer by email"

---

## What gets generated

### Code Tab
- Reads all configurable values from `ai.config.*`
- `inputVariable` fields are resolved by MindStudio before the code runs — Claude reads them as `ai.config.fieldName` directly, never via `ai.vars`
- `outputVariableName` fields hold the name of a user-chosen workflow variable — Claude writes outputs as `ai.vars[ai.config.outputVarName]` so results land where you expect
- Validates required fields with clear error messages
- Uses `ai.log()` to show progress during long calls
- Wraps fetch in try/catch with readable error output
- Builds query strings manually — no `URLSearchParams` (not available in Sandbox)

### Configuration Tab
- `secret` type for API keys (never plain text)
- `inputVariable` type for fields that accept `{{workflow variables}}`
- `select` type with options for fixed-choice fields (model names, effort levels, etc.)
- `outputVariableName` type so you name your own output variables
- `helpText` on every field

### Test Data Tab
- Mirrors every `ai.config.*` variable used in the code
- `inputVariable` values are set directly in `config`, not in `vars` — matching how MindStudio resolves them at runtime
- Uses realistic placeholder values, not "test123"
- Ready to run immediately after pasting your API key

---

## Installation

### Claude.ai
1. Download `mindstudio-to-api-custom-function-builder.skill`
2. Go to **Settings → Customize → Skills**
3. Upload the `.skill` file

### Claude Code
```bash
mkdir -p ~/.claude/skills/mindstudio-to-api-custom-function-builder
cp SKILL.md ~/.claude/skills/mindstudio-to-api-custom-function-builder/SKILL.md
```

---

## MindStudio Sandbox limitations

The skill is aware of what's available in MindStudio's Sandbox execution environment:

| Available | Not available |
|-----------|---------------|
| `fetch` | `URLSearchParams` |
| `JSON` | `Buffer` |
| `ai.config`, `ai.vars`, `ai.log()` | `process`, `require` |
| `ai.searchGoogle()`, `ai.scrapeUrl()` | `crypto`, `fs`, `path` |
| `encodeURIComponent()` | npm packages |
| Python via Pyodide | pip installs |

For functions that need npm packages, the skill will use the **Virtual Machine** environment instead, which requires exporting a `handler` function and has a ~5s startup cost.

---

## Authentication patterns supported

| Type | Implementation |
|------|----------------|
| API key header | `"X-API-Key": apiKey` |
| Bearer token | `"Authorization": "Bearer " + apiKey` |
| Basic auth | `"Authorization": "Basic " + btoa(user + ":" + pass)` |
| Query param | `"?token=" + apiKey` appended to URL |
| No auth | Headers omitted |

---

## Common mistakes the skill avoids

- Using `ai.vars[ai.config.fieldName]` for input fields (wrong — inputs are already resolved strings in `ai.config`)
- Using `URLSearchParams` in Sandbox (not available — query strings are built manually)
- Using `Buffer`, `process`, `require`, `crypto`, or `fs` in Sandbox (Node.js only)
- Using `query` instead of `input` for You.com APIs
- Using backtick template literals to inject `{{variables}}` into HTML (use `<script type="application/json">` instead)
- Hardcoding API keys in the Code Tab
- Using `ai.vars.outputName = value` when the output variable name is user-configured

---

## File structure

```
mindstudio-to-api-custom-function-builder/
├── SKILL.md          — Claude skill instructions (install this)
├── README.md         — This file
└── skill.json        — Metadata for skill registries
```

---

## Built from real experience

This skill was built and tested while connecting the **You.com Research API** and **Finnhub Company Profile API** to MindStudio. Every rule and gotcha in the skill comes from something that actually broke during that process — including the `URLSearchParams` Sandbox error, the You.com `input` vs `query` field naming issue, and the dynamic output variable naming pattern.

---

## License

MIT — use freely, modify as needed, attribution appreciated but not required.


path: skill.json
content: {
  "name": "mindstudio-to-api-custom-function-builder",
  "version": "1.1.0",
  "displayName": "MindStudio To API Custom Function Builder",
  "description": "Generates complete MindStudio Run Function integrations (Code Tab, Configuration Tab, Test Data Tab) for any API. Includes Sandbox-safe patterns, auth templates, and output variable best practices.",
  "author": "Sol",
  "license": "MIT",
  "tags": ["mindstudio", "api", "integration", "workflow", "automation", "run-function"],
  "compatibility": {
    "claude": ["claude.ai", "claude-code"]
  },
  "triggers": [
    "MindStudio",
    "Run Function",
    "connect API to MindStudio",
    "build MindStudio function",
    "MindStudio workflow",
    "custom function MindStudio"
  ],
  "changelog": {
    "1.1.0": "Added URLSearchParams Sandbox limitation to Common Mistakes. Added full list of unavailable Node/browser APIs in Sandbox notes.",
    "1.0.0": "Initial release. Covers Code Tab, Configuration Tab, Test Data Tab generation. Includes auth patterns, Sandbox vs VM guidance, and output variable naming best practices."
  }
}


Security scan verdicts — ClawScan: clean | static analysis: suspicious | VirusTotal: clean | SkillSpector: clean (LOW)