# GO AI Tools (remote · goaichat.app)

31 no-AI tools: image conversion and EXIF stripping, App Store assets, colour maths. No API key.

- Trust score: 77/100 (medium)
- Change this week: +3
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-20

## Components

- remote · `goaichat.app`: 77/100 (this document), [markdown](https://verifymcp.io/servers/app-goaichat-tools/mcp-tools-mcp.md), [page](https://verifymcp.io/servers/app-goaichat-tools/mcp-tools-mcp)

## Channel facts

- Endpoint: `https://goaichat.app/mcp-tools/mcp`
- Transports: `streamable-http`
- Auth: `none`
- Version: `1.0.0`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically against the live server, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-09-20.

- **Endpoint Security**: 77/100
  - The endpoint's TLS certificate is valid, in date, and uses a strong key.
  - No authorisation is required to call this server. Every tool declares its destructiveHint and none is destructive, so open access doesn't expose one.
  - HTTPS is enforced; there's no plaintext access path.
  - HSTS check failed: the Strict-Transport-Security header is absent.
  - DNSSEC is configured correctly; the domain's records validate against the full chain to the root.
- **Transport & Reachability**: 100/100
  - Verified streamable-http transport via a live MCP handshake.
- **Schema Quality & AI Usability**: 75/100
  - 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 13769 tokens (~430/item across 32 items; 31 tools + 1 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 36/100
  - Stability check failed: schema churn in the 12 days we've observed: 1 tool removals, 0 breaking changes, 0 auth/transport breaks, 1 additions.
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
  - Structured output schemas are declared (100% of tools); any adoption earns full credit.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - We read all 31 captured tool definition(s), and no name or description among them implies an irreversible operation.
  - An AI judge read all 32 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

## Install

### How do I install the GO AI Tools MCP server?

GO AI Tools is a hosted endpoint at https://goaichat.app/mcp-tools/mcp, so there is nothing to install locally. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add --transport http app-goaichat-tools 'https://goaichat.app/mcp-tools/mcp'
```

### Cursor

```json
{
  "mcpServers": {
    "app-goaichat-tools": {
      "url": "https://goaichat.app/mcp-tools/mcp"
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "app-goaichat-tools": {
      "type": "http",
      "url": "https://goaichat.app/mcp-tools/mcp"
    }
  }
}
```

### Codex

```toml
[mcp_servers.app-goaichat-tools]
url = "https://goaichat.app/mcp-tools/mcp"
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "app-goaichat-tools": {
      "type": "remote",
      "url": "https://goaichat.app/mcp-tools/mcp",
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add app-goaichat-tools --url 'https://goaichat.app/mcp-tools/mcp' --transport streamable-http
```

### Hermes

```yaml
mcp_servers:
  app-goaichat-tools:
    url: "https://goaichat.app/mcp-tools/mcp"
```

### Netclaw

```json
{
  "McpServers": {
    "app-goaichat-tools": {
      "Transport": "http",
      "Url": "https://goaichat.app/mcp-tools/mcp"
    }
  }
}
```

### Vellum

```bash
assistant mcp add app-goaichat-tools -t streamable-http -u 'https://goaichat.app/mcp-tools/mcp'
```

### Other

```json
{
  "mcpServers": {
    "app-goaichat-tools": {
      "type": "http",
      "url": "https://goaichat.app/mcp-tools/mcp"
    }
  }
}
```

The mcpServers block is a cross-client convention. Remote transports vary, so check your client's docs.

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-18 (score 77, +1)

No change was recorded against any check on this day. Stability & Change Management went from 26 to 30.

### 2026-09-16 (score 76, +1)

- [security regression] Stability: unverified → fail
- [security improvement] Authorization: unverified → partial
- [security improvement] Injection markers: unverified → pass
- [functional improvement] Endpoint reachability: unreachable → reachable
- [functional improvement] Tool coverage: unverified → 100
- [functional improvement] Schema quality: unverified → 100
- [functional improvement] MCP protocol: unverified → pass

### 2026-09-15 (score 75, 0)

- [security regression] Endpoint reachability: reachable → unreachable
- [security regression] Stability: fail → unverified
- [security regression] Tool safety: pass → unverified
- [security regression] Authorization: partial → unverified
- [functional regression] Schema quality: 100 → unverified
- [functional regression] Capabilities: pass → unverified
- [functional regression] Tool coverage: 100 → unverified

### 2026-09-14 (score 75, +1)

No change was recorded against any check on this day. Stability & Change Management went from 13 to 16.

### 2026-09-12 (score 74, +1)

No change was recorded against any check on this day. Stability & Change Management went from 6 to 10.

### 2026-09-10 (score 73, +1)

No change was recorded against any check on this day. Stability & Change Management went from 0 to 3.

### 2026-09-09 (score 72, +7)

- [security regression] Stability: unverified → fail
- [security regression] A breaking change shipped without a version bump: still 1.0.0
- [security regression] Tool “goai_build_app_privacy_label” was removed
- [security improvement] Authorization: unverified → partial
- [functional improvement] Tool coverage: 93% → 100%
- [functional improvement] Tool “appstore_compare_markets” now declares an output schema
- [functional improvement] Tool “appstore_search” now declares an output schema
- [functional improvement] Tool “bpm_delay_calculator” now declares an output schema
- [functional improvement] Tool “build_app_store_link” now declares an output schema
- [functional improvement] Tool “calculate_app_store_net_revenue” now declares an output schema
- [functional improvement] Tool “calculate_bmi_and_body_fat” now declares an output schema
- [functional improvement] Tool “calculate_tdee” now declares an output schema
- [functional improvement] Tool “check_contrast” now declares an output schema
- [functional improvement] Tool “check_strings_files” now declares an output schema
- [functional improvement] Tool “convert_heic_to_jpg_png” now declares an output schema
- [functional improvement] Tool “convert_video_to_gif” now declares an output schema
- [functional improvement] Tool “css_clamp_calculator” now declares an output schema
- [functional improvement] Tool “estimate_ai_tokens” now declares an output schema
- [functional improvement] Tool “estimate_window_light” now declares an output schema
- [functional improvement] Tool “find_nearest_passing_color” now declares an output schema
- [functional improvement] Tool “generate_app_icon_set” now declares an output schema
- [functional improvement] Tool “generate_favicon_set” now declares an output schema
- [functional improvement] Tool “image_compress” now declares an output schema
- [functional improvement] Tool “image_compression_curve” now declares an output schema
- [functional improvement] Tool “inspect_mobileprovision” now declares an output schema
- [functional improvement] Tool “oklch_convert” now declares an output schema
- [functional improvement] Tool “oklch_ramp” now declares an output schema
- [functional improvement] Tool “plant_watering_calendar” now declares an output schema
- [functional improvement] Tool “project_weight_goal_date” now declares an output schema
- [functional improvement] Tool “recipe_convert_ingredient” now declares an output schema
- [functional improvement] Tool “recipe_scale” now declares an output schema
- [functional improvement] Tool “recommend_ai_model” now declares an output schema
- [functional improvement] Tool “render_app_store_screenshot” now declares an output schema
- [functional improvement] Tool “resize_images” now declares an output schema
- [functional improvement] Tool “strip_image_metadata” now declares an output schema
- [functional] First check of Tool coverage: 100
- [functional] New tool “build_app_privacy_label”
- [cosmetic] “calculate_tdee” reworded the description of “activity_level”
- [cosmetic] “calculate_tdee” reworded the description of “age”
- [cosmetic] “calculate_tdee” reworded the description of “goal”
- [cosmetic] “calculate_tdee” reworded the description of “height_cm”
- [cosmetic] “calculate_tdee” reworded the description of “height_ft”
- [cosmetic] “calculate_tdee” reworded the description of “height_in”
- [cosmetic] “calculate_tdee” reworded the description of “sex”
- [cosmetic] “calculate_tdee” reworded the description of “units”
- [cosmetic] “calculate_tdee” reworded the description of “weight_kg”
- [cosmetic] “calculate_tdee” reworded the description of “weight_lb”

### 2026-09-08 (score 65)

First indexed and scored.

## MCP tools (31)

### `estimate_ai_tokens` (~415 tokens)

Estimate AI token count, context fit & cost

Heuristic estimate (NOT a real per-model BPE tokenizer) of how many tokens a piece of text will become, ported from GO AI's browser-side token counter. Blends a chars/4 and words/0.75 baseline with surcharges for punctuation, digits and non-Latin script; typically within ±10-15% for ordinary English prose and ±15-25% when non-Latin script is detected -- code and heavily punctuated text tend to tokenize denser than this suggests. Also reports whether the text fits a set of context windows (default: 8K/128K/200K/1M/2M tokens, or pass your own) and, if you supply per-million-token prices, an estimated USD cost. Use a provider's own tokenizer for exact billing figures.

Input parameters:

- `calls` (number): Number of times this input/output pair will be sent, to scale total cost. Defaults to 1; values below 1 are treated as 1.
- `contextWindows` (array): Custom context windows to check fit against, e.g. [{"name":"my-model","sizeTokens":32000}]. Omit to use the site default set (8K, 128K, 200K, 1M, 2M).
- `expectedOutputTokens` (number): Assumed reply length in tokens, used only to estimate output cost. Defaults to 500.
- `inputPricePerMillionUsd` (number): USD price per 1,000,000 input tokens, from the provider's current pricing page. Omit to skip input cost -- no default is assumed, since a stale hardcoded price would be worse than none.
- `outputPricePerMillionUsd` (number): USD price per 1,000,000 output tokens. Omit to skip output cost.
- `text` (string, required): The text to estimate, up to 1000000 characters (roughly 250K tokens). An empty string is valid and yields 0 tokens.

Output parameters:

- `accuracyBand` (object): An honesty bound on the token count: this is a heuristic, not a real tokenizer, and the band says how far off it usually is.
- `characters` (integer): Character count of the input text.
- `charsPerToken` (number): Characters divided by tokens -- the density that drives the accuracy band.
- `contextFit` (array): The text measured against each context window, so the caller sees which models it fits.
- `cost` (object): Projected spend. Every field is null unless the matching per-million price was supplied.
- `readingTime` (object): How long the text takes a person to read, as a sanity check on the size.
- `tokens` (integer): Estimated token count for the text.
- `words` (integer): Whitespace-delimited word count.

### `generate_app_icon_set` (~499 tokens)

App icon set generator (iOS + Android)

Turns one source image into a ZIP containing a complete iOS Xcode AppIcon.appiconset (AppIcon.png at 1024x1024, plus a hand-written Contents.json Xcode accepts, and optionally AppIcon-Dark.png and AppIcon-Tinted.png for the appearance variants modern iOS asks for), a full Android mipmap-mdpi/hdpi/xhdpi/xxhdpi/xxxhdpi set of ic_launcher.png files (48/72/96/144/192px), and a flat sizes/ folder with 24 icon-<px>.png files from 16 to 1024px. Any transparency in the source is flattened onto the given background colour first (Apple rejects icons with alpha). A non-square source is centered and letterboxed onto a square canvas rather than stretched. Every output size is produced by repeatedly halving the source (a box-filter-equivalent downscale) before one final resize to the exact target, which keeps small icons sharp instead of muddy. The dark variant is a mechanical darken (source composited at 82% opacity over black) offered as a starting point, not a real design pass -- review it before shipping. The tinted variant is a Rec.709 greyscale luminance map, which is what iOS actually wants for that slot (it applies the colour itself). The JSON result reports the source dimensions, whether it had transparency, the file count, and two non-fatal warnings when the source was not square or not exactly 1024px on its longest side. Always returns as a resource_link (a 30+ file ZIP is never small enough to inline) -- fetch the link to get the archive.

Input parameters:

- `backgroundColor` (string): CSS/hex colour (e.g. "#ffffff") used to flatten transparency and to pad a non-square source before it is centered onto a square canvas.
- `imageBase64` (string, required): Base64-encoded source image bytes (PNG, JPEG, or WebP). 1024x1024 is ideal; anything else is padded to square and/or scaled.
- `variants` (string): Which iOS appearance variants to include alongside the required light AppIcon.png. 'none': light only. 'dark': light + AppIcon-Dark.png. 'all': light + AppIcon-Dark.png + AppIcon-Tinted.png. Contents…

Output parameters:

- `backgroundColor` (string): The colour transparency was flattened onto, and the letterbox colour for a non-square source.
- `files` (array): Every file in the archive, so the contents can be checked without unzipping it.
- `filesOut` (integer): Total number of files inside the ZIP.
- `hadAlpha` (boolean): True when the source carried transparency, which was flattened onto backgroundColor because Apple rejects icons with alpha.
- `source` (object): Dimensions of the image that was supplied, before squaring.
- `variants` (string): Which appearance variants were generated, echoed from the input.
- `warnings` (object): Non-fatal quality warnings. Both are null on an ideal 1024px square source -- neither stops the ZIP being produced.

### `build_app_store_link` (~504 tokens)

App Store campaign link builder + QR code

Builds a correct apps.apple.com URL for an app ID or a pasted App Store URL, in a chosen two-letter storefront, with optional App Store Connect campaign attribution parameters (pt, ct, and mt=8). Always returns the assembled URL and a params table describing what each part does. When includeQr is true, also renders the URL as a QR code PNG using the same hand-rolled, dependency-free Reed-Solomon byte-mode encoder (error-correction level M, versions 1-10) the source browser page draws to its own <canvas> -- not a generic QR library, so the module layout, masking, and pixel output match that page exactly. The encoder tops out at 213 UTF-8 bytes for the assembled URL (version 10 ceiling); a longer URL fails cleanly naming the byte counts rather than returning a broken code -- shorten pt/ct or call again with includeQr:false to still get the plain URL. The storefront code is not checked against the list of real App Store storefronts -- a well-formed but unused code just will not have the app listed on it.

Input parameters:

- `appIdOrUrl` (string, required): Numeric App Store app ID (e.g. "6478912345"), or a full pasted App Store URL containing one (e.g. "https://apps.apple.com/us/app/x/id6478912345"). The id is extracted from anywhere in a URL; a bare v…
- `ct` (string): Campaign token (ct) for App Store Connect campaign attribution, up to 256 characters. Trimmed; omitted from the URL entirely if empty after trimming.
- `includeQr` (boolean): Also render the URL as a QR code PNG and return it as image bytes. Defaults to false (URL + params only).
- `mt` (boolean): Append mt=8 (media type: software), marking the link as pointing at an app. Defaults to true, matching the source page's checkbox (checked by default).
- `pt` (string): Provider token (pt) for App Store Connect campaign attribution, up to 256 characters. Trimmed; omitted from the URL entirely if empty after trimming.
- `store` (string, required): 2-letter App Store storefront code (e.g. "us", "gb", "jp"). Decides which country's listing, price, and availability the link opens.

Output parameters:

- `appId` (string): The numeric app ID, whether given directly or extracted from a pasted URL.
- `params` (array): Every query parameter in the URL, explained.
- `qr` (object): Details of the rendered QR code. Present only when includeQr was true -- the PNG itself comes back as a separate content block.
- `store` (string): The two-letter storefront code the URL targets.
- `url` (string): The assembled apps.apple.com URL -- the answer most callers want.

### `calculate_app_store_net_revenue` (~417 tokens)

App Store net revenue calculator

Computes what a developer actually receives from an App Store sale: first removes the storefront's VAT/GST already baked into the customer-facing sticker price, then applies Apple's commission (standard 30%, Small Business Program 15%, or the post-year-one subscription 15%) to that tax-exclusive remainder -- NOT to the sticker price itself, which is the mistake most naive calculators make. Returns a full price breakdown, the effective share of the sticker Apple actually keeps, a side-by-side comparison across all three commission rates (including per-1,000-sales figures), a naive-calculator sanity check showing how far off a 'just take the commission off the top' estimate would be, and warnings when the selected storefront's real tax rate varies by province, state or category (India, Canada, Brazil) rather than being a single fixed number.

Input parameters:

- `commissionScenario` (string): Which Apple commission rate applies to the tax-exclusive remainder: 'standard30' (30%, the default for most apps), 'small15' (15%, Apple's Small Business Program for developers under the program's pr…
- `countryCode` (string): Two-letter App Store storefront code (e.g. us, gb, de, jp, in). Selects the default tax rate and currency symbol. Defaults to gb, matching the tool page.
- `price` (number, required): The customer-facing sticker price for this storefront (already tax-inclusive everywhere except the US).
- `taxRatePercent` (number): Override the storefront's default tax rate baked into the price, as a percent (e.g. 20 for 20% VAT). Defaults to the selected countryCode's standard rate (0 for the US). Some storefronts' real rates…

Output parameters:

- `appleShareOfStickerPercent` (number): Apple's commission as a percentage of the sticker price, which is lower than the headline rate because tax comes off first.
- `breakdown` (object): The sticker price decomposed in the order the money is actually removed.
- `comparison` (array): All three commission scenarios side by side, regardless of which was requested.
- `input` (object): What the calculation actually ran on, including the defaults that were filled in.
- `naiveComparison` (object): A sanity check against the common mistake of applying commission to the tax-inclusive price.
- `warnings` (array): Cautions about the storefront, chiefly that its real tax rate varies by province, state or category. Empty when the rate is a single fixed number.

### `appstore_search` (~211 tokens)

Search an App Store storefront

Searches one Apple App Store country storefront by term, via Apple's public iTunes Search API (the same request the GO AI "App Store storefront checker" tool makes from the browser), and returns matching apps with title, seller, price, rating and a direct App Store link. Returns up to 200 results, Apple's own per-request maximum -- a result of exactly 200 likely means more exist. Results are ordered by Apple's internal relevance, which does not match the ranked list shown in the App Store app, so this cannot be used to track keyword rank.

Input parameters:

- `country` (string): 2-letter App Store storefront code, e.g. "US", "GB", "JP". Defaults to "US".
- `entity` (string): App type: "software" (iPhone), "iPadSoftware" (iPad), or "macSoftware" (Mac).
- `term` (string, required): Search phrase, up to 200 characters. Sent verbatim to Apple's Search API as the query term.

Output parameters:

- `apps` (array): The matching apps. Every field is nullable because Apple omits fields per storefront rather than returning empty values.
- `capped` (boolean): True when the result hit the Search API's 200-per-request ceiling, meaning there are likely more matches than were returned.
- `country` (string): The storefront that was searched.
- `entity` (string): Which catalogue was searched (iPhone, iPad or Mac software).
- `resultCount` (integer): Apple's own reported match count for the query.
- `term` (string): The search term, echoed back.

### `appstore_compare_markets` (~243 tokens)

Compare an App Store listing across markets

Looks up one app (by numeric App Store id, or a pasted apps.apple.com URL) in up to 30 App Store storefronts in a single call, via Apple's public iTunes Lookup API, and reports per-market availability, localized title, price and rating. Capped at 30 storefronts per call by design -- there are 173 total App Store storefronts, and this deliberately never sweeps all of them in one call; call again with a different storefronts list to cover more markets. Lookups run one at a time with a short pause between each (matching the pacing the source browser tool uses for its 30-market quick-compare), so a full 30-market call takes roughly ten seconds, not an instant burst.

Input parameters:

- `appIdOrUrl` (string, required): Numeric App Store id (e.g. "6742322421") or a full apps.apple.com URL containing one.
- `storefronts` (array): Up to 30 2-letter storefront codes (e.g. ["US","GB","JP"]) after de-duplication. Omit to use the same 30 major markets the source tool defaults to.

Output parameters:

- `appId` (string): The app ID that was looked up, extracted from the input if a URL was given.
- `checked` (integer): How many storefronts were queried.
- `distinctTitles` (integer): Number of different titles seen across the storefronts that resolved -- more than one means the listing is localized.
- `failed` (integer): How many lookups errored rather than returning a verdict.
- `found` (integer): How many of them have the app listed.
- `rows` (array): One row per requested storefront, in the order given. A row is one of three shapes: listed, not listed, or errored.

### `calculate_bmi_and_body_fat` (~313 tokens)

BMI and Navy body fat calculator

Computes BMI (with its standard weight-category label) and body fat percentage via the US Navy circumference method from height, weight, neck, waist, and (for females) hip measurements, plus the resulting lean body mass. The Navy equations are fitted in inches, so metric inputs are converted internally; the body-fat percentage is clamped to 2-75% and rounded to a whole number, matching GO AI's body-fat tool page exactly, including its 'waist must exceed neck (and hip, for the female formula)' impossible-input warning and its out-of-fitted-range caution for raw results below 4% or above 60%.

Input parameters:

- `height` (number, required): Height, in cm (metric) or inches (imperial).
- `hip` (number): Hip circumference at the widest point, in cm or inches. Required when sex is 'female'; ignored for 'male'.
- `neck` (number, required): Neck circumference, just below the larynx, in cm or inches.
- `sex` (string, required): Sex, which selects the Navy formula variant.
- `units` (string): metric = cm/kg, imperial = inches/lb. Applies to every measurement below.
- `waist` (number, required): Waist circumference (at the navel for men, at the narrowest point for women), in cm or inches.
- `weight` (number, required): Weight, in kg (metric) or lb (imperial). Used for BMI and lean mass, not for the body-fat percentage itself.

Output parameters:

- `bmi` (number|null): Body mass index to 1 decimal place, or null if it could not be computed.
- `bmiCategory` (string|null): The WHO band that BMI falls in, e.g. 'Underweight', 'Normal', 'Overweight', 'Obese'. Null when bmi is null.
- `bodyFatPercent` (number|null): US Navy tape-method body fat as a whole percent, clamped to 2-75. Null when the measurements make the formula undefined (see warnings).
- `bodyFatRaw` (number|null): The same figure before clamping and rounding, to 2 decimals -- this is the one that reveals an implausible measurement. Null in the same case as bodyFatPercent.
- `leanMass` (number|null): Lean mass in the same unit system as the input (kg for metric, lb for imperial). Null when body fat could not be computed.
- `units` (string): Which unit system leanMass is expressed in -- otherwise the bare number is ambiguous.
- `warnings` (array): Plain-language cautions: the measurements make the formula undefined, or the result sits outside the range it was fitted on. Empty when neither applies.

### `bpm_delay_calculator` (~438 tokens)

BPM tap tempo & delay time calculator

Resolves a musical tempo — either a direct BPM, or a set of tap timestamps/intervals run through the same median-filtered outlier rejection and 2-second session-reset logic as GO AI's tap-tempo tool — then returns the full straight/dotted/triplet delay and LFO-rate table (ms and Hz) for a set of note divisions, plus bar-length and bars<->seconds conversion for a time signature. All arithmetic (not a real audio engine) — useful for setting delay/reverb/LFO times to a track's tempo.

Input parameters:

- `bars` (number): Number of bars to convert to seconds. At most one of bars/seconds is used if both are given (bars takes priority).
- `bpm` (number): Tempo in beats per minute, used directly and unrounded. Provide exactly one of bpm, tapTimesMs, or tapIntervalsMs.
- `divisions` (array): Note divisions (denominator of 1/d) to include in the delay table, up to 64. Defaults to [1,2,4,8,16,32].
- `seconds` (number): Number of seconds to convert to bars. Ignored if bars is also given.
- `tapIntervalsMs` (array): Milliseconds between each tap and the one before it (one fewer entry than the number of taps), up to 4096. Provide exactly one of bpm, tapTimesMs, or tapIntervalsMs.
- `tapTimesMs` (array): Strictly increasing millisecond timestamps of taps (e.g. from a high-resolution clock), in tap order, up to 4096. Provide exactly one of bpm, tapTimesMs, or tapIntervalsMs.
- `timeSignature`: One of "4/4","3/4","2/4","6/8","5/4","7/8","12/8" (the site's presets), or a custom {beats, unit} pair. Defaults to 4/4. Uses the written beat count — 6/8 is 6 beats of an eighth-note unit, not 2 dot…

Output parameters:

- `barDurationSeconds` (number): Length of one bar in seconds at this tempo and signature.
- `bars` (number): The bar count that seconds corresponds to. Present only when bars or seconds was supplied.
- `bpm` (number): The tempo the table was computed at, in beats per minute.
- `bpmSource` (string): Where that tempo came from: given directly, or averaged from tap times or tap intervals -- worth surfacing, since a tapped tempo carries the tapper's error.
- `delayTable` (array): One row per requested division, each with straight, dotted and triplet timings.
- `seconds` (number): Duration in seconds of that many bars at this tempo. Present only when bars or seconds was supplied.
- `timeSignature` (object): The time signature used to size a bar.

### `css_clamp_calculator` (~383 tokens)

CSS clamp() Calculator

Generates a CSS clamp() declaration (in rem) that linearly interpolates a size between a value at a small viewport width and a value at a large one, exactly matching GO AI's clamp() calculator tool (same slope/intercept math, rounded to 4 decimal places). Returns a status: 'sameViewports' when the two viewport widths are equal (no line can be drawn -- css is null), 'flat' when the min and max sizes are equal (a constant, clamp() unneeded), 'inverted' when the max size is smaller than the min (still valid CSS, but the computed size shrinks as the viewport grows), or 'ok' otherwise. Optionally evaluates the computed pixel size at a list of preview viewport widths, and optionally generates a matched fluid type scale by multiplying both endpoints by ratio^step for a given number of steps -- unlike the website's five-option dropdown, any positive ratio is accepted here since the underlying math is not limited to those presets.

Input parameters:

- `maxSizePx` (number, required): Size in px at the large viewport.
- `maxViewportPx` (number, required): The large viewport width in px.
- `minSizePx` (number, required): Size in px at the small viewport.
- `minViewportPx` (number, required): The small viewport width in px.
- `previewViewportsPx` (array): Optional list of viewport widths (px), up to 64, to evaluate the resulting clamp() at, returned as computed pixel sizes.
- `rootFontSizePx` (number): Root font size in px used to convert the px sizes to rem. Defaults to 16; a value of 0 also falls back to 16.
- `typeScale` (object): Optional: when provided, also returns a fluid type scale of this many steps, each step scaling both endpoints by ratio^step.

Output parameters:

- `css` (string|null): The finished CSS clamp() declaration in rem, or null when status is sameViewports.
- `math`: The four numbers the css string is assembled from, each rounded to 4 decimal places. Null when status is sameViewports.
- `note` (string): Human-readable explanation of a non-ok status, or an empty string when status is ok.
- `preview` (array): One entry per requested preview viewport, in the order given. Empty when previewViewportsPx was omitted.
- `scale` (array): The fluid type scale, one entry per step. Empty when typeScale was omitted or no step could be built.
- `status` (string): 'ok' for a normal fluid size; 'sameViewports' when the two viewport widths match, so no line can be drawn and css is null; 'flat' when both sizes are equal (a constant, clamp() unneeded); 'inverted'…

### `image_compress` (~361 tokens)

Compress an image (JPEG/WebP/AVIF)

Downscales an image to fit within a maximum dimension (default 1600px on the longer side, never enlarges -- matching GO AI's browser compressor tool) and re-encodes it as JPEG, WebP or AVIF at a given quality (1-100 scale, default 75). Input is base64-encoded image bytes (any format sharp/libvips can decode: JPEG, PNG, WebP, AVIF, GIF, TIFF, ...), not a file path or URL, capped at this server's input size limit. Returns the compressed image bytes plus a stats object: original and compressed dimensions and byte sizes, and the percent size change (positive = smaller, negative = the re-encode came out bigger, which happens when compressing an already-small, already-compressed image). Re-encoding always strips EXIF/ICC metadata, exactly like the source tool.

Input parameters:

- `format` (string): Output format. 'webp' (default, matches the source's own default whenever WebP is available) is smaller than JPEG at equivalent visual quality; 'jpeg' is the safest choice for old viewers or print; '…
- `imageBase64` (string, required): Base64-encoded source image bytes (not a file path or URL).
- `maxDimension` (integer): Longer-side cap in pixels before encoding; the image is downscaled to fit inside this (aspect preserved) and is never enlarged. Defaults to 1600, matching the source.
- `quality` (integer): Encoder quality, 5-100 (matches the source's slider range). Defaults to 75. 100 is the least-lossy setting the encoder offers, not truly lossless.

Output parameters:

- `compressed` (object): The returned file. Its bytes come back as a separate content block.
- `format` (string): Output format used.
- `original` (object): The image as supplied.
- `percentChange` (number): Size change against the original as a percentage; negative means smaller. Can be positive when re-encoding an already-optimised file.
- `quality` (integer): Quality level the image was encoded at.
- `working` (object): The image after being fitted to maxDimension but before re-encoding. Never an enlargement.

### `image_compression_curve` (~357 tokens)

Image compression size-vs-quality curve

Analysis-only tool (no image bytes returned): re-encodes an image at the same fixed 14 quality levels GO AI's browser compressor samples to draw its size-against-quality curve (5, 10, ..., through 100 -- see the qualities in each returned point), for JPEG, WebP or AVIF, and reports the resulting byte size and percent change at each level. The image is first downscaled to fit within maxDimension (default 1600px on the longer side, never enlarged), exactly as the source's working canvas is, then every quality level is encoded one at a time. Because this repeats the encode 14 times, its maxDimension is capped at 2000px -- lower than image_compress's 4000px -- which costs nothing in practice, since the knee of the curve is a property of the image's content and sits in the same place at either size. Use this to find that 'knee' (where size stops dropping much per quality point) for a specific image, or image_compress to actually get the compressed bytes at one chosen quality. Input is base64-encoded image bytes, not a file path or URL.

Input parameters:

- `format` (string): Format to sample the curve in. Defaults to 'webp', matching the source's own default.
- `imageBase64` (string, required): Base64-encoded source image bytes (not a file path or URL).
- `maxDimension` (integer): Longer-side cap in pixels before encoding, aspect preserved, never enlarged. Defaults to 1600, matching the source. Capped at 2000 here (vs 4000 for image_compress) because this tool runs the encode…

Output parameters:

- `format` (string): Format every point was encoded in.
- `original` (object): The image as supplied.
- `points` (array): The 14 fixed sample points, ascending by quality. The 'knee' -- where size stops dropping much per quality point -- is what this tool exists to locate. No image bytes are returned.
- `working` (object): The downscaled image every quality level was encoded from. The knee sits in the same place at either size, so this does not distort the curve.

### `check_contrast` (~161 tokens)

WCAG/APCA contrast checker

Computes the WCAG 2.x contrast ratio between a foreground (text) and background hex colour, and reports AA/AAA pass or fail for normal text, large text, and UI components (icons/borders/focus rings) separately, since WCAG grades each case on its own threshold. Also reports the newer APCA Lc figure alongside it for information -- APCA is not yet what conformance is measured against, only the WCAG ratio is.

Input parameters:

- `background` (string, required): Background colour as a hex string, same format as foreground.
- `foreground` (string, required): Foreground/text colour as a hex string, e.g. "#6b7280" or "6b7" (3- or 6-digit, leading # optional).

Output parameters:

- `apca` (object): The APCA (WCAG 3 draft) reading, which models perceived contrast rather than a pure luminance ratio and often disagrees with WCAG 2.
- `backgroundHex` (string): The background colour normalised to hex.
- `cases` (array): The pair judged against each WCAG use case, since one ratio passes for large text and fails for body copy.
- `foregroundHex` (string): The foreground colour normalised to hex.
- `ratio` (number): WCAG 2 contrast ratio, from 1 (identical) to 21 (black on white).
- `ratioDisplay` (string): That ratio formatted the way it is conventionally written, e.g. '4.53:1'.
- `summary` (object): The tally, so a caller can gate on one boolean instead of walking cases.

### `find_nearest_passing_color` (~292 tokens)

Nearest WCAG-passing colour (hue-preserving)

Given a foreground/background hex pair, moves only the lightness of one side (hue and saturation held fixed) in the smallest step that clears a target WCAG contrast ratio, so a failing brand colour can be fixed without draining its hue. Defaults to the 4.5:1 normal-text AA threshold, matching the source page's fix buttons; pass a named threshold ('normal-aa', 'normal-aaa', 'large-aa', 'large-aaa', 'ui-aa') or a custom numeric ratio to target something else. Reports found:false when even pure black or white in that hue cannot reach the target -- that pair needs a different hue, not a different lightness.

Input parameters:

- `adjust` (string, required): Which colour to move toward passing; the other one stays fixed.
- `background` (string, required): Background colour as a hex string, same format as foreground.
- `foreground` (string, required): Foreground/text colour as a hex string, e.g. "#6b7280" (3- or 6-digit, leading # optional).
- `target`: WCAG threshold to clear: 'normal-aa' 4.5:1 (default, matches the page's buttons), 'normal-aaa' 7:1, 'large-aa'/'ui-aa' 3:1, 'large-aaa' 4.5:1, or any custom positive ratio.

Output parameters:

- `adjust` (string): Which colour was moved, echoed from the input.
- `alreadyPassing` (boolean): True when the input pair already met the target and nothing needed moving.
- `currentRatio` (number): The ratio the original pair had.
- `direction` (string|null): Whether the colour was lightened or darkened; null when nothing moved.
- `found` (boolean): Whether a passing colour was reached. False means the target is unreachable by lightness alone from this starting colour.
- `lightnessStepPercent` (number|null): How far the colour had to travel in lightness, as a percentage -- a large value means the passing colour is no longer the colour that was asked for.
- `message` (string): Plain-language summary of the outcome, including why no colour was found when found is false.
- `movedHex` (string|null): The adjusted colour in hex, or null when nothing was moved or nothing was found.
- `newRatio` (number|null): The contrast ratio after the move, or null when there was no move.
- `targetRatio` (number): The contrast ratio being aimed for, resolved from the named target or given directly.

### `strip_image_metadata` (~323 tokens)

Strip image metadata (EXIF/GPS/XMP/IPTC/PNG text)

Reads a JPEG or PNG's hidden metadata (JPEG: EXIF — camera make/model, lens, body/lens serial numbers, capture date, GPS coordinates and altitude — plus XMP, IPTC/Photoshop resources and comment segments; PNG: tEXt/iTXt/zTXt/eXIf/tIME chunks) and returns a structured report of exactly what it found, including the size of every metadata segment/chunk that was removed. Optionally (include_cleaned_image, default true) also returns the same image with those segments/chunks stripped at the byte level: no re-encoding and no pixel decode, so the compressed image data, ICC profile, and JFIF/PNG structure are copied unchanged. It runs a byte-for-byte self-check that the retained image data is actually unchanged before handing back a cleaned file — if that check fails, no cleaned file is returned even though the report is still produced. Only JPEG and PNG signatures are recognized (not TIFF or other formats); stripping a photo's EXIF Orientation tag can make it appear rotated, which the report flags.

Input parameters:

- `filename` (string): Original filename, used only to name the returned cleaned file (defaults to "image"). Format detection is by content signature, not by this name or extension.
- `image_base64` (string, required): Base64-encoded bytes of the JPEG or PNG file to inspect.
- `include_cleaned_image` (boolean): When true (default), also return the metadata-stripped image bytes alongside the report. Set to false to get only the report.

Output parameters:

- `cleanedImageIncluded` (boolean): Whether a cleaned file accompanies this report as a content block. False when include_cleaned_image was off, when there was nothing to strip, or when verification failed.
- `cleanedSizeBytes`: Size of the cleaned file, or null when no cleaned image was produced.
- `fields` (array): Every metadata field read from the file -- this is the disclosure the caller is usually checking for.
- `fieldsFound` (integer): How many individual metadata fields were read out.
- `format` (string): Which of the two supported formats the file was read as.
- `gps`: Embedded location, or null when the file carried none. The single most sensitive thing in a photo, so it is surfaced on its own rather than only inside fields.
- `make` (string|null): Camera make, when EXIF carried one.
- `message` (string): Plain-language summary, including why no cleaned file was returned when that is the case.
- `metadataFound` (boolean): Whether any strippable metadata was present at all.
- `model` (string|null): Camera model, when EXIF carried one.
- `originalSizeBytes` (integer): Size of the supplied file in bytes.
- `recognized` (boolean): Always true here. A file that is neither a JPEG nor a PNG comes back as an error result instead, not as recognized:false.
- `removedBytes`: Bytes saved by stripping, or null when no cleaned image was produced.
- `removedSegmentCount` (integer): Number of segments/chunks removed.
- `removedSegments` (array): Each metadata segment or chunk that was stripped, with its size.
- `rotationWarning` (boolean): True when an EXIF Orientation tag was removed, which can make the cleaned image appear rotated in viewers that relied on it.
- `verified` (boolean|null): Result of the byte-for-byte check that the retained image data is unchanged. Null when no cleaning was attempted; false means the check failed and no cleaned file was returned.
- `verifiedBytes`: How many bytes that check compared. Null when no cleaning was attempted.

### `generate_favicon_set` (~730 tokens)

Generate a favicon and web-app-manifest set

Turns one source image into a complete favicon set: favicon.ico (a real multi-resolution ICO container holding 16, 32 and 48px PNG-encoded entries -- not a single-size file with an .ico extension), favicon-96x96.png, apple-touch-icon.png (180px, flattened onto backgroundColor because iOS paints transparency black there), web-app-manifest-192x192.png and web-app-manifest-512x512.png (optionally padded to Android's maskable safe zone -- the artwork shrunk to a centred 80%-width square on the background colour, reproducing the source tool's square padding rather than clipping to the actual inscribed-circle safe area), site.webmanifest, and the exact 5-line HTML <head> snippet (favicon.ico link, favicon-96x96.png link, apple-touch-icon link, apple-mobile-web-app-title meta, manifest link) ready to paste. A non-square source is centred on a transparent square canvas first, so nothing is stretched. Ported from GO AI's browser-based favicon tool, run server-side with @napi-rs/canvas instead of a DOM <canvas>. Every PNG and the .ico are small (a few KB to worst-case a couple hundred KB), so all six files are returned individually rather than zipped -- each name matters (favicon.ico and site.webmanifest belong at the site root) and there is no batching benefit to a ZIP at this size. The JSON result carries the manifest JSON text, the HTML snippet text, a per-file table of what each output is for, and a warning when the source is smaller than 512px on its longer side (the 512 icon will be upscaled). Fails with a clear message rather than throwing if the input cannot be decoded as an image.

Input parameters:

- `backgroundColor` (string): Hex colour (# optional, 3- or 6-digit) used three ways: the manifest's background_color field, the flatten colour behind the apple-touch-icon (iOS does not honour alpha there), and the padding colour…
- `imageBase64` (string, required): Source image, base64-encoded. Any raster/SVG format @napi-rs/canvas can decode (PNG, JPEG, WebP, GIF, SVG). May be non-square and may carry transparency -- it is centred on a square canvas before any…
- `maskablePadding` (boolean): When true (default), the 192x192 and 512x512 manifest icons are padded: the artwork is shrunk to a centred 80%-width square on the background colour, matching Android's maskable-icon safe zone, and t…
- `shortName` (string): Short name for a home-screen label (roughly 12 characters fit). Used as the manifest's "short_name" and in the apple-mobile-web-app-title meta tag; falls back to siteName when empty.
- `siteName` (string): Full site name, used as the manifest's "name" field and as the fallback for short_name / the apple-mobile-web-app-title meta tag when shortName is empty.
- `themeColor` (string): Hex colour (# optional, 3- or 6-digit) written to the manifest's theme_color field. Purely metadata -- never painted onto any icon.

Output parameters:

- `backgroundColor` (string): Background colour written into the manifest.
- `manifest` (string): A complete web app manifest as JSON text, ready to serve as site.webmanifest.
- `maskablePadding` (boolean): Whether the maskable icon was padded to survive Android's safe-zone crop.
- `snippet` (string): The <link> and <meta> tags to paste into the page head, matching the files that were generated.
- `sourceHeight` (integer): Source image height in px.
- `sourceWidth` (integer): Source image width in px.
- `squaredSide` (integer): Side length of the square canvas the source was fitted onto, in px.
- `table` (array): Every generated file and what it is for, so the set can be checked without unzipping it.
- `themeColor` (string): Theme colour written into the manifest and meta tags.
- `warning` (string|null): A quality caution about the source image, chiefly that it was not square or too small. Null when there is nothing to flag.

### `project_weight_goal_date` (~593 tokens)

Weight loss goal-date projector

Projects the calendar date a goal body weight is reached under a fixed daily calorie intake. Unlike a naive straight-line calculator (kg to lose x 7700 / deficit, also returned here for comparison), this recomputes Mifflin-St Jeor maintenance calories at each simulated day as weight falls, so the effective deficit narrows the way it really does -- meaning the projected date is later than, or equal to, the naive one, never earlier. Refuses to project a date when the resulting intake falls below a 1500 kcal (men) / 1200 kcal (women) floor, and reports a 'plateau' outcome instead of a date when the goal sits at or below the weight where maintenance would settle at that intake (an asymptote that a fixed intake alone can never cross). Adult-only inputs (18+); only projects weight loss (goalWeight must be less than currentWeight).

Input parameters:

- `activityLevel` (string, required): Activity multiplier bucket, the site's five options: sedentary x1.2 (desk job, no exercise), light x1.375 (1-3 sessions/week), moderate x1.55 (3-5 sessions/week, the site default), very_active x1.725…
- `age` (number, required): Age in years, 18-120. The source page is not validated for children; 18+ only.
- `currentWeight` (number, required): Current body weight, kg if units is metric, lb if imperial.
- `dailyDeficitKcal` (number, required): Daily calorie deficit to hold against today's computed maintenance. The site's quick picks are 250/500/750/1000 kcal, but any positive number is accepted.
- `goalWeight` (number, required): Goal body weight, same unit as currentWeight. Must be less than currentWeight -- this tool only projects weight loss, matching the source page.
- `heightCm` (number): Height in centimeters. Required when units is 'metric'.
- `heightFt` (number): Feet part of height. Required when units is 'imperial'.
- `heightIn` (number): Inches part of height (0-11). Required when units is 'imperial'.
- `includeChartSeries` (boolean): If true, also return the weekly weight series and the naive straight-line end week -- the same two lines the source page charts. Set false to skip them for a lighter response.
- `referenceDate` (string): ISO date (YYYY-MM-DD) the projection counts forward from. Defaults to today (UTC) when omitted.
- `sex` (string, required): Biological sex, used by the Mifflin-St Jeor formula.
- `units` (string): metric = cm/kg, imperial = ft+in/lb. Applies to height and both weight fields.

Output parameters:

- `activityMultiplier` (number): The multiplier the chosen activity level maps to.
- `currentWeightKg` (number): Starting weight in kg, after any imperial conversion.
- `daysToGoal` (number|null): Days to the goal under the adaptive model, or null when the goal is never reached.
- `floorKcal` (number): The intake floor that was enforced, below which the projection is not modelled.
- `gapDays` (number|null): How many days longer the honest answer is than the naive one -- the whole point of the tool. Null when the goal is never reached.
- `goalWeightKg` (number): Target weight in kg, after any imperial conversion.
- `horizonDays` (number|null): Length of the modelled horizon in days, or null when not modelled.
- `intakeKcal` (number): Daily intake implied by the requested deficit, after the safety floor is applied.
- `maintenanceKcal` (number): Maintenance calories at the starting weight.
- `naiveDays` (number|null): Days predicted by the flat 7,700 kcal-per-kg rule that ignores the falling maintenance -- the figure most calculators stop at.
- `naiveLineEndWeek` (number|null): Where the naive straight line would end on the same axes, so the two can be drawn together. Null when there is no series.
- `naiveWeeks` (number|null): The same figure in whole weeks.
- `note` (string): Plain-language reading of the result, including why the two answers differ or why the goal is unreachable.
- `plateau` (boolean|null): True when the settle weight is above the goal, i.e. this deficit alone will never get there. Null when it does not apply.
- `projectedDate` (string|null): Calendar date the goal is reached, ISO YYYY-MM-DD, or null when it is not reached.
- `reachedWithinHorizon` (boolean): Whether the goal is reached inside the modelled horizon.
- `series`: Weekly projection points for plotting. Null when includeChartSeries was false.
- `settleWeightKg` (number|null): The weight this intake eventually settles at, since maintenance falls as weight does. Null when it does not apply.
- `status` (string): How the projection ended: the goal is reached, or it settles short of the goal, or it is unreachable at this intake. The field to branch on before reading the dates.
- `weeksToGoal` (number|null): The same figure in whole weeks.

### `convert_heic_to_jpg_png` (~498 tokens)

Convert HEIC/HEIF to JPG or PNG

Converts iPhone HEIC/HEIF photos to JPEG or PNG, decoding with a WebAssembly build of libheif (LGPL-3.0) and re-encoding with sharp -- the same pipeline as GO AI's browser-based HEIC converter tool, run server-side. Accepts 1-50 files as {filename, dataBase64} (each up to ~30MB decoded); every file is byte-sniffed by its actual magic bytes, never by filename extension or claimed mime type, so an iPhone photo that iOS already delivered as a JPEG (picked from Photos rather than Files) is detected and reported as already_converted -- passed through unchanged, not re-encoded -- while real HEIC/HEIF input is decoded and re-encoded to the requested format (JPEG with a caller-set quality 50-100, or lossless PNG). Any orientation stored on the HEIC is applied automatically during decode, so output comes out right-side up. Metadata (EXIF: GPS, timestamp, camera) is not carried over, matching the source page. Each converted or passed-through file is returned individually (inline if small, as a download link if large), or bundled as one converted.zip when bundleAsZip is true and more than one file produced output; a file that fails to decode is reported with status failed and an error message rather than aborting the batch. The JSON report lists status, dimensions, and before/after byte sizes for every input file, in input order.

Input parameters:

- `bundleAsZip` (boolean): When true and more than one file produced output bytes (converted or passed-through), bundle them into one converted.zip instead of returning each file as its own separate output.
- `files` (array, required): 1 to 50 files, in the order results are reported. Mix HEIC/HEIF and already-JPEG/PNG files freely -- each is byte-sniffed on its own. The batch is additionally capped at 4000000 bytes of combined dec…
- `format` (string): Output format for any real HEIC/HEIF input. Ignored for input already sniffed as JPEG or PNG, which is passed through unchanged rather than re-encoded.
- `quality` (integer): JPEG quality, 50-100. Ignored when format is image/png (PNG is lossless).

Output parameters:

- `format` (string): Output MIME type requested for the batch.
- `results` (array): One row per input file, in the order supplied. Rows come in three shapes depending on status.

### `inspect_mobileprovision` (~211 tokens)

Inspect a provisioning profile

Reads an iOS/macOS provisioning profile (.mobileprovision or .provisionprofile, given as base64) by byte-searching its CMS/PKCS7-signed container for the embedded '<?xml ... </plist>' property list and parsing that XML -- it never validates or decodes the cryptographic signature, and does not report on the embedded certificates beyond how many there are. Reports expiry status and days remaining, profile type (development/ad hoc vs. enterprise vs. App Store distribution, classified only from the registered-device list and the ProvisionsAllDevices flag), team and application identifiers, the full entitlements dictionary, and the count and UDIDs of registered devices. Returns a structured { ok: false, error, message } result (not a thrown error) for an unreadable input, a file with no embedded plist, or a plist that fails to parse.

Input parameters:

- `base64` (string, required): Base64-encoded raw bytes of the .mobileprovision or .provisionprofile file.

Output parameters:

- `appIdName` (string|null): The App ID name registered with the profile.
- `applicationIdentifier` (string|null): The application-identifier entitlement, i.e. team ID plus bundle ID -- the field that says what this profile actually signs.
- `certificateCount` (integer): How many signing certificates are embedded. The certificates themselves are not decoded -- only counted.
- `createdDate` (string|null): Creation date as an ISO 8601 string, or null when absent or unparseable.
- `deviceCount` (integer): Number of registered device UDIDs; 0 for an App Store or enterprise profile.
- `devices` (array): The registered device UDIDs, normally strings. Empty when the profile registers none.
- `entitlements` (object): The full entitlements dictionary verbatim from the profile, keys and values as the plist held them.
- `expirationDate` (string|null): Expiry date as an ISO 8601 string, or null when absent or unparseable.
- `expiry` (object): Whether this profile still works, which is the question most callers are actually asking.
- `name` (string|null): The profile's name, or null if the plist omits it.
- `ok` (boolean): Always true on a readable profile. An unreadable file, a file with no embedded plist, or a plist that will not parse comes back as an error result instead, with the reason as its text.
- `platform`: Platforms the profile covers, normally an array such as ['ios']. Null when absent.
- `profileType` (object): Development/ad hoc vs. enterprise vs. App Store distribution.
- `rawPlistXml` (string): The embedded property list as raw XML, for anything this report does not surface.
- `teamIdentifier`: Team identifiers from the plist, normally an array of one team ID string. Null when absent.
- `teamName` (string|null): Developer team name.
- `timeToLiveDays` (number|null): The profile's TimeToLive in days, or null when absent.
- `uuid` (string|null): The profile's UUID.

### `recommend_ai_model` (~365 tokens)

Recommend which AI model to use for a task

Static editorial recommendation -- NOT a live benchmark or leaderboard -- for which of six AI models (GPT-5, Gemini, Grok 4, Claude, DeepSeek, Kimi) to use for a given kind of task, ported from GO AI's own daily side-by-side-use judgement, current as of August 2026. Pick a task and get the recommended model plus the reasoning and a second-opinion backup. The optional `priority` can bias the pick toward cost (routes to DeepSeek) or freshness (routes to Grok) instead of the default quality pick -- but only for tasks where that tradeoff is actually offered; otherwise the quality default is returned unchanged. This reflects one team's opinion, not measured accuracy or pricing data.

Input parameters:

- `priority` (string): "quality" (default) returns the task's default best-result pick. "cost" swaps to DeepSeek when this task supports a cheaper substitute. "fresh" swaps to Grok when this task supports a fresher/more cu…
- `task` (string, required): The kind of task, one of: write (Long or structured writing), copy (Punchy copy, hooks, social), edit (Editing my own draft), longdoc (Summarising long documents), code (Writing code), review (Review…

Output parameters:

- `adjusted` (boolean): True when the stated priority (cost or freshness) moved the answer away from the quality-first pick.
- `backup` (string): A second choice worth trying if the first does not suit.
- `disclaimer` (string): Standing note that this is a fixed editorial table, not a live benchmark.
- `model` (string): The recommended model's display name.
- `modelKey` (string): Stable identifier for the recommended model, safe to switch on.
- `note` (string|null): What that adjustment was, when adjusted is true; otherwise null.
- `tag` (string): Short label for why this model wins the task, e.g. its standout strength.
- `taskLabel` (string): Human-readable name of the task that was matched.
- `why` (string): The reasoning behind the recommendation.

### `oklch_convert` (~330 tokens)

Convert an OKLCH / hex / RGB / HSL color

Converts a color to and from OKLCH. Accepts either a CSS color string via `color` (hex, rgb()/rgba(), hsl()/hsla(), or oklch()) or explicit OKLCH lightness/chroma/hue via l, c and h — supply one or the other. Returns the canonical oklch() text for the color exactly as requested, plus its sRGB-gamut-mapped hex/rgb()/hsl() equivalents. Gamut mapping follows the CSS Color 4 algorithm (bisecting on chroma at constant lightness and hue, not channel-clipping) and only targets sRGB — there is no P3 or Rec2020 support.

Input parameters:

- `c` (number): OKLCH chroma. sRGB's most saturated colour reaches about 0.31; the source page's own slider goes to 0.4 so out-of-gamut requests are possible on purpose. Used (with l and h) only when color is omitte…
- `color` (string): CSS color to convert from — hex ('#5b8cff' or '#58f'), rgb()/rgba(), hsl()/hsla(), or oklch(). Takes precedence over l/c/h when both are supplied.
- `h` (number): OKLCH hue in degrees, 0-360. Used (with l and c) only when color is omitted.
- `l` (number): OKLCH lightness, 0 (black) to 1 (white). Used (with c and h) only when color is omitted.

Output parameters:

- `hex` (string): The sRGB result as a hex string.
- `hsl` (object): The same colour in HSL, for code that still expects it.
- `hslText` (string): The colour as a CSS hsl() declaration.
- `inGamut` (boolean): Whether the requested colour exists in sRGB.
- `mapped` (boolean): True when the colour was gamut-mapped to fit sRGB, meaning hex below is a near miss rather than the exact request.
- `oklchText` (string): The colour as a CSS oklch() declaration.
- `requested` (object): The OKLCH coordinates that were asked for, before any gamut mapping -- keep these to see how far the sRGB answer had to move.
- `rgb` (object): The same colour as 8-bit sRGB channels.
- `rgbText` (string): The colour as a CSS rgb() declaration.

### `oklch_ramp` (~460 tokens)

Build an OKLCH swatch ramp / CSS custom-property block

Builds a perceptually even OKLCH swatch ramp from a base color (same `color` or l/c/h input as oklch_convert) by sweeping one channel — hue, lightness, or chroma — while holding the other two fixed, so every swatch keeps the same visual weight (this is the reason to build a ramp in OKLCH rather than HSL). Each step is gamut-mapped to sRGB with the CSS Color 4 chroma-reduction algorithm and returned with its hex preview, plus a ready-to-paste `:root { --name-1: oklch(...); ... }` CSS block.

Input parameters:

- `c` (number): OKLCH chroma. sRGB's most saturated colour reaches about 0.31; the source page's own slider goes to 0.4 so out-of-gamut requests are possible on purpose. Used (with l and h) only when color is omitte…
- `color` (string): CSS color to convert from — hex ('#5b8cff' or '#58f'), rgb()/rgba(), hsl()/hsla(), or oklch(). Takes precedence over l/c/h when both are supplied.
- `h` (number): OKLCH hue in degrees, 0-360. Used (with l and c) only when color is omitted.
- `l` (number): OKLCH lightness, 0 (black) to 1 (white). Used (with c and h) only when color is omitted.
- `mode` (string): Which channel the ramp sweeps while holding the other two fixed: 'hue' rotates hue around the wheel, 'light' sweeps lightness from a near-black floor to a near-white ceiling, 'chroma' sweeps chroma f…
- `name` (string): Stem for the generated CSS custom properties (e.g. 'brand' produces --brand-1, --brand-2, ...); sanitized to [a-z0-9-] the same way the source page does.
- `steps` (integer): Number of swatches in the ramp, 3-24.

Output parameters:

- `base` (object): The OKLCH coordinates the ramp was generated from.
- `css` (string): The whole ramp as CSS custom properties, ready to paste into a stylesheet.
- `mode` (string): Which coordinate was varied across the steps, echoed from the input.
- `steps` (array): The ramp in order. Because each step is mapped independently, some can be mapped and others not.

### `plant_watering_calendar` (~509 tokens)

Plant watering calendar (.ics)

Builds a soil-check interval per houseplant from a fixed drought-tolerance table (32 common houseplants), adjusted by pot size, pot material, light and season, and returns both a schedule breakdown and the full text of a downloadable RFC 5545 .ics calendar file -- one recurring all-day 'check the soil' reminder per plant (deliberately never 'water', since only the plant's own soil can say that). Matches GO AI's browser watering-calendar tool exactly, including its northern-hemisphere-season default when `season` is omitted, its terracotta/glazed/low-light/winter multipliers, and its 75-octet .ics line folding. The starting intervals are heuristic drought-tolerance bands, not a measurement of any specific plant, pot or room -- the tool says so in its own FAQ.

Input parameters:

- `light` (string): Light level: bright direct sun dries soil fastest, low light slowest. Defaults to bright-but-indirect.
- `plants` (array, required): Houseplant ids to include (each event uses this exact catalog, no free text). Duplicates are ignored. Ids: snake, zz, aloe, jade, echeveria, cactus, hawortia, ponytail, rubber, monstera, pothos, phil…
- `potMaterial` (string): Unglazed terracotta breathes and dries noticeably faster than plastic (default) or glazed ceramic.
- `potSize` (string): Pot diameter band: small (under 12cm), medium (12-25cm, default), large (over 25cm). A larger pot holds more soil and dries more slowly.
- `season` (string): Current season, which scales every interval (summer fastest, winter roughly 60% slower). If omitted, defaults from today's calendar month assuming the *northern* hemisphere, exactly like the source p…
- `startDate` (string): First reminder date, YYYY-MM-DD. Every plant's recurring event starts on this same date, each with its own repeat interval. Defaults to today.

Output parameters:

- `calendar` (object): The schedule as a subscribable calendar. Returned inline as text, not as a file download.
- `note` (string): Standing caution that these intervals are a starting point to adjust against the actual soil.
- `schedule` (array): One entry per requested plant, in catalogue order.
- `summary` (object): The set at a glance, for deciding a single watering day.

### `build_app_privacy_label` (~267 tokens)

Build an App Store privacy label checklist

Given which SDKs and features are present in an iOS app (analytics, crash reporting, ads, attribution, accounts, in-app purchases, location, HealthKit, contacts, search, etc.), returns the App Store Connect privacy data types those items most likely require declaring, grouped the way Apple groups them (Contact info, Identifiers, Usage data, Diagnostics, ...), each with the reason (which chosen item(s) caused it), plus whether App Tracking Transparency/tracking applies and the vendor-documentation date the mapping was checked against. This is a fixed, deterministic lookup table shipped with GO AI's privacy-label-builder page -- not a code scanner and not compliance advice. SDKs change what they collect (sometimes in a minor version) and app configuration changes it further, so treat the result as a starting checklist to verify against each SDK's current documentation, not an answer to submit as-is.

Input parameters:

- `items` (array, required): SDKs/features present in the app binary. One or more of: firebaseAnalytics, crashlytics, sentry, amplitude, admob, metaSdk, attribution, push, accounts, signInApple, iap, revenuecat, support, userPho…

Output parameters:

- `checklistText` (string): The whole result as pasteable plain text, for dropping into a ticket or a submission checklist.
- `dataTypeCount` (integer): Total number of distinct data types to declare across all groups.
- `disclaimer` (string): Standing note that this is a deterministic lookup, not a code scan and not compliance advice.
- `groups` (array): Data types to declare, grouped the way App Store Connect groups them.
- `sdkCount` (integer): How many distinct SDKs/features were considered, after repeats collapse.
- `trackingNote` (string): Explanation of the tracking verdict and what it obliges.
- `trackingRequired` (boolean): True when at least one chosen item implies App Tracking Transparency / the "Used to Track You" declaration.
- `vendorDocumentationAsOf` (string): The date this mapping table was last checked against vendor documentation -- SDKs change what they collect, so this bounds how much to trust the answer.

### `recipe_convert_ingredient` (~450 tokens)

Cup-to-grams converter (per-ingredient density)

Converts an amount of one ingredient between cup, tablespoon, teaspoon, millilitre, gram and ounce using that specific ingredient's own USDA FoodData Central portion weight, not a single water-based figure applied to everything -- a cup of flour (125 g) and a cup of honey (339 g) do not weigh the same. Supports an ingredient's own measurement-style variants where USDA publishes more than one (packed vs loose brown sugar, sifted vs unsifted powdered sugar, whole vs sliced/slivered/ground nuts, etc.), and for baking powder, baking soda and yeast uses USDA's own published teaspoon weight rather than dividing the cup weight by 48, since a spoon measurement of a fine powder is not proportionate to its cup measurement. Also reports, for information, what a generic water-density converter would have said for the same input and by how much that would have been wrong -- omitted when the requested unit is already grams or ounces.

Input parameters:

- `amount` (string, required): Amount as a decimal ("1.5"), a simple fraction ("3/4"), or a mixed number ("1 1/2").
- `cupSizeMl` (number): Cup size in ml, for the 'cup' unit (and 'tbsp'/'tsp' on ingredients without their own USDA spoon weight). Defaults to the US customary cup (236.588 ml); other common values are 240 (US nutrition-labe…
- `ingredient` (string, required): Which ingredient to convert, identified by its GO AI recipe-tool id (e.g. "flour-ap", "sugar-brown", "honey").
- `unit` (string, required): Unit the amount is given in.
- `variant` (string): Measurement style for ingredients that have more than one USDA figure (e.g. sugar-brown: "packed"/"loose"; sugar-icing: "unsifted"/"sifted"; almonds: "whole"/"sliced"/"slivered"/"ground"). Omit for i…

Output parameters:

- `densityGPerMl` (number): The density used for this ingredient, in grams per millilitre.
- `densityGPerMlRounded` (number): That density rounded for display.
- `grams` (number): The converted weight in grams, unrounded.
- `gramsRounded` (number): The same weight rounded the way a kitchen scale reads it -- the number to actually use.
- `ounces` (number): The converted weight in ounces, unrounded.
- `ouncesRounded` (number): The same weight in ounces, rounded for practical use.
- `waterComparison` (string): How this ingredient compares in weight to the same volume of water -- the intuition that explains why a cup of flour and a cup of honey are nothing alike.
- `workSentence` (string): The conversion written out as an equation, so the caller can check the cup size and density rather than trusting a bare number.

### `recipe_scale` (~331 tokens)

Recipe servings scaler with gram weights

Scales a pasted recipe (one ingredient per array entry, e.g. '2 cups all-purpose flour') from one servings count to another, multiplying each line's leading amount by the ratio and reformatting it as a whole number or simple/mixed fraction rather than a decimal. When a line's unit and ingredient can both be recognised against GO AI's density table, appends the scaled amount's weight in grams in parentheses; lines already given in grams or ounces, lines whose ingredient isn't recognised, and lines that don't start with a parseable amount are returned unchanged (the last completely as-is, typos included).

Input parameters:

- `cupSizeMl` (number): Cup size in ml used when converting a scaled "cup"/"tbsp"/"tsp" amount to grams. Defaults to the US customary cup (236.588 ml); other common values are 240 or 250.
- `fromServings` (number): Servings the recipe as written makes. 0 (or an omitted field) falls back to 1, matching the source tool's own guard.
- `lines` (array, required): One ingredient per array entry, as free text (e.g. "2 cups all-purpose flour", "1/2 tsp table salt"), up to 500 lines of 1000 characters each. Lines with no parseable leading amount, or with an amoun…
- `toServings` (number): Servings wanted. 0 (or an omitted field) falls back to 1, matching the source tool's own guard.

Output parameters:

- `lines` (array): The ingredient lines rescaled, one output line per input line and in the same order. A line whose quantity could not be parsed is returned unchanged rather than dropped, so the list stays aligned wit…

### `resize_images` (~706 tokens)

Batch image resizer with real presets

Resizes a batch of 1+ images to a named real-world preset (App Store screenshots, Open Graph/social cards, Android launcher icon densities, a favicon set) or a custom width/height, using contain (whole picture, padded), cover (fills the frame, crops overflow) or stretch (distorts to fit) fitting. Each resized image is PNG-encoded (EXIF/ICC metadata stripped on re-encode, matching the source page) and every image is auto-rotated per its embedded EXIF orientation before resizing. All outputs are packaged into one uncompressed ZIP (PNG bytes are already compressed, so a second pass would not shrink them), foldered by preset (e.g. "social/photo-1200x630.png", or "android/mipmap-hdpi/icon.png" for the Android density preset, which names each size's folder after its density instead of suffixing the pixel size). This always returns as a resource_link (a batch ZIP is never small enough, or singular enough, to inline) -- fetch the link to get the archive. Per-file output dimensions and byte sizes are reported in the JSON result. Each input image (base64) is capped by this server's per-input byte limit.

Input parameters:

- `customHeight` (integer): Target height in px. Only used when preset is "custom".
- `customWidth` (integer): Target width in px. Only used when preset is "custom".
- `fit` (string): How each image fills its target box. 'contain' keeps the whole picture and pads the gap with paddingColor. 'cover' fills the box and crops whatever hangs over. 'stretch' distorts the image to the exa…
- `images` (array, required): One to 60 images. Each is resized independently to every size in the chosen preset. A call is additionally capped at 48000000 total output pixels across images x sizes, so a preset with large or nume…
- `paddingColor` (string): Hex color (e.g. "#ffffff") used to pad the frame when fit is "contain". Ignored for "cover" and "stretch".
- `preset` (string): Which size(s) to produce for every image. appstore69: App Store screenshot, 6.9" iPhone, 1320x2868. appstore67: App Store screenshot, 6.7" iPhone, 1290x2796. appstoreIpad: App Store screenshot, 13" i…

Output parameters:

- `filesOut` (integer): How many files were produced, i.e. imagesIn x sizes.
- `fit` (string): How each image was fitted to its target box.
- `imagesIn` (integer): How many source images were supplied.
- `outputs` (array): Every produced file, so the archive can be checked without unzipping it.
- `paddingColor` (string|null): The letterbox colour, which only applies to fit 'contain'. Null for 'cover' and 'stretch', where nothing is padded.
- `preset` (string): The preset that was applied, echoed from the input.
- `sizes` (integer): How many target sizes the chosen preset expands to.

### `render_app_store_screenshot` (~486 tokens)

App Store screenshot generator

Composite one or more app screenshots into a drawn device frame (rounded bezel, Dynamic Island on phone-shaped canvases) with a gradient background and optional headline/caption text, and export as PNG at any/all of the 4 official App Store Connect sizes. A blank page (image omitted) draws no device, for a title card or an outro. A device positioned past the edge of its page is drawn spilling onto the neighbouring page too, so a set can run one screenshot into the next. LIMITS — this runs on a small shared host, so an over-limit call is refused immediately, before any rendering, with a message saying how to split it: at most 10 pages at any one size, 2 at size:"all" (which renders every page 4 times over — a longer set needs one call per size); at most 12 rendered PNGs per call (pages x sizes); 6 MP per screenshot and 4 MB of screenshot bytes summed across the whole call (send JPEG, not PNG, for a multi-page set); and at most 40 text layers of 512 characters each.

Input parameters:

- `backgroundFrom` (string): Gradient start color (top-left), hex.
- `backgroundTo` (string): Gradient end color (bottom-right), hex.
- `joinedScene` (boolean): When true and there is more than one page, one gradient stretches across the whole set instead of repeating per page.
- `pages` (array, required): One entry per exported page/PNG, in order. Use null for a blank page. At most 10 pages at any single size; size:"all" renders every page 4 times over and fits 2. Over that, the call is refused before…
- `size` (string): Which official App Store Connect size to export: 6.9"/6.7"/6.5" iPhone or 13" iPad, or "all" for every size (returned as a ZIP). "all" renders 4 canvases per page, so it is limited to 2 pages per cal…
- `texts` (array): Zero or more text layers, at most 40. Order is z-order (later entries draw on top); each is confined to one page.

Output parameters:

- `sizesRendered` (array): One entry per rendered device size. The images themselves come back as separate content blocks -- a single render inline, a multi-page or multi-size batch as a ZIP behind a resource_link.

### `check_strings_files` (~310 tokens)

iOS .strings localization checker

Compares two or more iOS Localizable.strings files (sent as base64-encoded raw file bytes, not text) against a base file and reports keys missing from each other file, keys present in another file but not the base ("extra", reported but not counted toward the issue total), values byte-identical to the base (often untranslated, sometimes intentionally so), duplicate values under different keys within the same file (case- and trailing-punctuation-insensitive), and lines that fail to parse with their line number. Sniffs a UTF-8 or UTF-16 LE/BE byte-order mark per file so files exported by Xcode in UTF-16 decode correctly instead of producing a wall of parse errors. Does not support the newer .xcstrings JSON catalogue format, and does not check plural rules or placeholder (%@/%d) consistency between files -- it is a pure key/value diff.

Input parameters:

- `baseFile` (string): Exact name of one of files[] to treat as the base ("compare against") file. When omitted, auto-selects the first file (after sorting all names alphabetically) whose name starts with "base." / "base-"…
- `files` (array, required): Two to 40 .strings files to compare against each other.

Output parameters:

- `badLines` (array): Lines that are neither a comment, blank, nor a parseable key/value pair -- typically a missing semicolon or an unescaped quote.
- `baseFile` (string): Which file every other file was compared against, whether given or chosen automatically.
- `duplicateValues` (array): Keys within one file that share a value, which is how a copy-paste translation error looks.
- `extraKeys` (array): Keys in a translation that the base file no longer has -- usually left behind by a rename or a deletion.
- `fileCount` (integer): How many files were compared.
- `files` (array): Per-file summary, including the base file.
- `identicalToBase` (array): Keys whose translation is byte-identical to the base language. Sometimes correct (a proper noun), often a forgotten translation.
- `issueCount` (integer): Total findings across every category below. Zero means the files agree.
- `keysInBase` (integer): Number of keys in the base file -- the denominator for the translation coverage.
- `missingKeys` (array): Keys present in the base file but absent from another -- untranslated strings that will fall back at runtime.
- `missingKeysStrings` (string): Every missing key formatted as ready-to-paste .strings entries with the base value, so the gap can be filled without retyping. Empty when nothing is missing.

### `calculate_tdee` (~647 tokens)

TDEE & macro calculator

Calculate Basal Metabolic Rate with Mifflin-St Jeor and Harris-Benedict (plus Katch-McArdle when body_fat_percent is given), then Total Daily Energy Expenditure, a goal-adjusted calorie target (cut/maintain/bulk), and a protein/fat/carb macro split, from sex, age, height, weight and activity level. These are population-average formulas, not a measurement of the person's actual metabolism -- real expenditure commonly varies 200-300 kcal from the estimate, and the result includes a warning flag when a deficit target falls below the usual safety floor.

Input parameters:

- `activity_level` (string, required): Multiplier applied to BMR to reach TDEE: sedentary x1.2 (desk job, no exercise), light x1.375 (1-3 sessions/week), moderate x1.55 (3-5), very_active x1.725 (6-7), extra_active x1.9 (physical job or t…
- `age` (integer, required): Age in years, 14-100. Every BMR formula here subtracts a per-year term, so this moves the result directly.
- `body_fat_percent` (number): Optional. Unlocks the Katch-McArdle formula, which uses lean mass instead of total weight.
- `goal` (string, required): Calorie adjustment applied to TDEE to get the target: lose_20 is a 20% deficit, lose_15 a 15% deficit, maintain no change, gain_10 a 10% surplus. A deficit landing under the safety floor sets below_s…
- `height_cm` (number): Height in centimetres. Required when units is 'metric', ignored when units is 'imperial'.
- `height_ft` (integer): Height, whole feet, combined with height_in. Required when units is 'imperial', ignored when units is 'metric'.
- `height_in` (number): Height, the inches remainder on top of height_ft (0-11). Used only when units is 'imperial', and treated as 0 when omitted -- so 6 ft flat is height_ft 6 with no height_in.
- `sex` (string, required): Biological sex. Selects the sex constant in the BMR formulas (Mifflin-St Jeor +5/-161, Harris-Benedict its own pair) and the calorie safety floor the goal target is checked against (1,500 kcal/day fo…
- `units` (string): Which height/weight pair is read. 'metric' uses height_cm and weight_kg; 'imperial' uses height_ft (+ height_in) and weight_lb. The pair belonging to the other system is ignored, not merged. Defaults…
- `weight_kg` (number): Weight in kilograms. Required when units is 'metric', ignored when units is 'imperial'.
- `weight_lb` (number): Weight in pounds, converted internally at 2.20462 lb per kg. Required when units is 'imperial', ignored when units is 'metric'.

Output parameters:

- `activity_factor` (number): The multiplier the chosen activity_level maps to (1.2 to 1.9).
- `activity_label` (string): The full human-readable label for that activity level.
- `below_safety_floor` (boolean): True when a deficit target falls below the usual floor for this sex -- the one field worth checking before presenting the target.
- `bmr` (object): Basal metabolic rate by formula, before any activity multiplier.
- `disclaimer` (string): Standing note that these are population-average estimates, not a measurement.
- `formula_comparison` (array): All three formulas side by side, so the spread is visible rather than implied.
- `goal_label` (string): Wording for that adjustment, e.g. '15% deficit', 'maintenance', '10% surplus'.
- `goal_pct` (number): The goal adjustment as a signed percentage (-20, -15, 0, or +10).
- `goal_target_kcal` (number): The goal-adjusted daily calorie target in kcal.
- `lean_mass_kg` (number|null): Lean body mass in kg, or null when body_fat_percent was not supplied.
- `macros` (object): A protein/fat/carb split of goal_target_kcal.
- `primary_formula` (string): Which formula drove the headline numbers: 'Katch-McArdle' when body fat % was given, otherwise 'Mifflin-St Jeor'.
- `resolved_inputs` (object): What the imperial/metric inputs actually resolved to internally -- the numbers every formula above was fed.
- `safety_floor_kcal` (number): The floor that was checked against: 1500 kcal/day for male, 1200 for female.
- `safety_warning` (string|null): The full warning text when below_safety_floor is true, otherwise null.
- `spread_kcal` (number): Difference in kcal/day between the highest and lowest TDEE among the formulas that computed.
- `tdee_kcal` (number): Total daily energy expenditure in kcal/day: primary BMR x activity factor.

### `convert_video_to_gif` (~1577 tokens)

Trim a short video into a GIF, MP4 or WebM

Ports GO AI's browser Live Photo-to-GIF tool server-side: trims a SHORT video clip and converts it to an animated GIF, or (a server-side addition the browser tool cannot do) a real MP4/WebM video. Input is base64 video bytes plus its mimeType (any container/codec this server's ffmpeg build can decode) and a trim range in seconds (startSeconds/endSeconds). 

HARD LIMITS -- this runs in a 400 MB container on one shared vCPU behind a proxy that hangs up at 125s, so an over-budget request is REJECTED up front with a message naming the limit rather than accepted and then killed. Read these before calling: (1) the video itself must decode to at most 4 MB -- send a pre-trimmed clip, not a whole recording; (2) the trim range must span at least 0.25s and at most 10s; (3) width x height x frames must not exceed 12 megapixels TOTAL across the whole animation. That third one is the binding limit in practice and it is easy to trip with a portrait clip: at size=480 a 9:16 video is 480x854, so it fits about 29 frames (~2.9s at fps=10) -- while a 16:9 video at the same size is 480x270 and fits the full frame cap. If a call is rejected, the error names the exact frame/size that would fit; do not retry with the same numbers. 

size picks the output width in px (0 keeps the source's own width, clamped to 720; otherwise 720/480/320/240, default 320). Height is derived to preserve aspect ratio and both are forced even (a hard requirement of GIF and most video codecs). fps (10/12/15/20/25, default 10) is the target sampling rate; because GIF frame delays are whole hundredths of a second, the achieved rate (actualFps in the result) is rounded and rarely matches exactly what was requested -- always read actualFps back, do not assume it equals fps. speed (0.5/1/1.5/2) scales playback; speed below 1 samples MORE frames from the same span and so costs more of the pixel budget. motion is loop (plays once per cycle, restarts abruptly), bounce (plays forward then backward so it never visibly cu…

Input parameters:

- `colours` (number): GIF palette size: 256 (default), 128 or 64 shared colours across the whole clip. Ignored for mp4/webm.
- `dither` (boolean): Floyd-Steinberg dithering for the GIF palette (default true). Smooths gradients at the cost of file size and adds noise to flat graphics -- turn off for screen recordings or flat artwork. Ignored for…
- `endSeconds` (number, required): Trim end, in seconds. Must leave at least 0.25s and at most 10s between startSeconds and endSeconds.
- `format` (string): Output container. 'gif' (default) runs the full sampling + shared-palette + LZW pipeline. 'mp4' (H.264) and 'webm' (VP9) skip that pipeline and have ffmpeg encode the trimmed/scaled/speed-adjusted cl…
- `fps` (number): Target sampling/output frame rate: 10 (default), 12, 15, 20 or 25. Higher fps spends the frame budget faster, so it shortens the trim range that will fit. For GIF, the true achieved rate is reported…
- `mimeType` (string, required): The source video's mime type, e.g. "video/quicktime" or "video/mp4". Used only to pick a temp-file extension; the actual format is content-sniffed by ffmpeg.
- `motion` (string): 'loop' (restarts from the first frame each cycle), 'bounce' (plays forward then backward, never visibly cuts -- roughly doubles the emitted frame count and so halves the span that fits the pixel budg…
- `size` (number): Output width in px: 0 keeps the source's own width (clamped to 720); otherwise 720, 480, 320 (default) or 240. Height is derived to preserve aspect ratio; both dimensions are forced even. Bigger size…
- `speed` (number): Playback speed multiplier: 0.5, 1 (default), 1.5 or 2. Higher speed samples fewer frames per second of source video (and so fits a longer trim); 0.5 samples twice as many.
- `startSeconds` (number): Trim start, in seconds from the beginning of the video. Clamped to the video's own duration.
- `videoBase64` (string, required): Base64-encoded source video bytes (not a file path or URL). The decoded video must be at most 4 MB; larger payloads are rejected before any decoding happens.

Output parameters:

- `actualFps` (number): The frame rate actually achieved, which can differ because frames are sampled by ffmpeg's fps filter rather than seeking to exact times.
- `audio` (string): What happened to the source audio track. mp4/webm output only.
- `byteSize` (integer): Size of the produced file in bytes.
- `delayCentiseconds` (number): Per-frame delay written into the GIF, in centiseconds. GIF output only.
- `dither` (boolean): Whether dithering was applied during quantisation. GIF output only.
- `durationSeconds` (number): Duration of the produced clip in seconds. mp4/webm output only.
- `format` (string): Output format. This field decides which of the conditional fields below are present.
- `frameCount` (integer): Number of frames encoded. GIF output only.
- `height` (integer): Output height in px.
- `maxColors` (integer): Palette size the median-cut quantiser was allowed. GIF output only.
- `maxOutputPixels` (integer): The ceiling outputPixels was checked against, so a rejection is explicable from the result.
- `motion` (string): Playback behaviour applied.
- `outputPixels` (integer): Width x height x frames -- the bound that actually governs this tool's memory use.
- `requestedFps` (number): The frame rate that was asked for.
- `speed` (number): Speed multiplier applied to the source.
- `trimEnd` (number): End of the trimmed span in seconds. GIF output only.
- `trimSpan` (number): Length of the trimmed span in seconds. GIF output only.
- `trimStart` (number): Start of the trimmed span in seconds. GIF output only.
- `width` (integer): Output width in px.

### `estimate_window_light` (~334 tokens)

Window light estimator for houseplants

Estimates the indoor light level a window gives a plant, in the category words houseplant care guides use (Direct sun / Bright indirect / Medium light / Low light / Too dark), from the window's hemisphere, compass aspect, what obstructs it, and how far back the plant sits from the glass. This is a small heuristic scoring table ported from GO AI's window-light tool page, not a physical light-meter reading or a per-location sun-position model — it correctly flips which compass direction is 'bright' for the southern hemisphere and flags the direct-west-sun scorch risk, and returns the reasoning behind the verdict plus a shortlist of plants suited to that light level.

Input parameters:

- `aspect` (string, required): True compass direction the window faces (before any hemisphere adjustment).
- `blocked` (string, required): What stands between the glass and the open sky: 'clear' = nothing, 'sheer' = a net curtain/blind or distant trees, 'near' = a tree or building close by, 'heavy' = mostly blocked.
- `distance` (string, required): How far back from the glass the plant sits: 'sill' = on the sill, 'd1' = within an arm's reach, 'd2' = one to two metres back, 'd3' = two to three metres back, 'd4' = further into the room.
- `hemisphere` (string, required): Hemisphere the window is in. South of the equator the sun tracks the northern sky, so the compass direction that gets the strongest light is reversed from what most (northern-hemisphere-written) plan…

Output parameters:

- `aspect` (string): The compass direction the window faces, echoed from the input.
- `band` (string): The score bucketed into a named light level, the single field most callers want.
- `plants` (string): Plant types suited to this light level.
- `reasoning` (string): Why the score came out where it did, naming the aspect, glazing and distance contributions.
- `score` (number): The computed light score the band and verdict are derived from -- higher is brighter.
- `solarAspect` (string): That aspect mapped to its solar equivalent for the given hemisphere -- a south-facing window means the opposite thing north and south of the equator.
- `verdict` (string): One-line summary of what this window is good for.
- `verdictSub` (string): A supporting line qualifying the verdict.

## Diagnostics

Captured diagnostic sections: TLS, DNSSEC, Authorisation, Transports. The full working is on the page: https://verifymcp.io/servers/app-goaichat-tools/mcp-tools-mcp#diagnostics

## Score history

- 2026-09-20: 77
- 2026-09-19: 77
- 2026-09-18: 77
- 2026-09-17: 76
- 2026-09-16: 76
- 2026-09-15: 75
- 2026-09-14: 75
- 2026-09-13: 74
- 2026-09-12: 74
- 2026-09-11: 73
- 2026-09-10: 73
- 2026-09-09: 72
- 2026-09-08: 65

## Common questions

### What is the GO AI Tools MCP server?

GO AI Tools is an MCP server listed in the public MCP registry as app.goaichat/tools. 31 no-AI tools: image conversion and EXIF stripping, App Store assets, colour maths. No API key. This page covers its hosted endpoint (https://goaichat.app/mcp-tools/mcp).

### Is the GO AI Tools MCP server safe to use?

GO AI Tools scores 77 out of 100 on VerifyMCP. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the GO AI Tools MCP server expose?

GO AI Tools exposes 31 tools: estimate_ai_tokens, generate_app_icon_set, build_app_store_link, calculate_app_store_net_revenue, appstore_search, and 26 more. Their descriptions and schemas cost roughly 13,721 tokens of context every time the server is loaded.

### Does the GO AI Tools MCP server require authentication?

No. We connected to GO AI Tools without credentials and it answered, so anything it exposes is reachable by anyone who knows the address.

### Is the GO AI Tools MCP server still maintained?

GO AI Tools is still listed as active in the MCP registry. We last reached this channel on 20 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

## Links

- Remote endpoint: https://goaichat.app/mcp-tools/mcp
- Website: https://goaichat.app/mcp-tools
- Changelog RSS feed: https://verifymcp.io/servers/app-goaichat-tools/mcp-tools-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/app-goaichat-tools/mcp-tools-mcp.json
- HTML version of this page: https://verifymcp.io/servers/app-goaichat-tools/mcp-tools-mcp
