Skip to main content
Glama

Server Details

Ask your assistant for an app and it lands on your phone: offline, data on the phone.

Ownership verified
Status
Healthy
Uptime
99.2% over 21 days
OAuth
Works in Glama
Last Tested
Transport
Streamable HTTP · MCP 2025-11-25
URL
Repository
cloudcrane-dev/cloudcrane-mcp
GitHub Stars
0
Server Listing
CloudCrane MCP

TDQS

A3.9/5.0

Scored across 8 tools

Disambiguation4/5

Each tool maps to a distinct lifecycle action, and descriptions clarify the subtle get_app vs get_spec split (code included vs blueprint-only) and update_app vs publish_app (replace existing vs create new). The get_app/get_spec pair is the only closely overlapping area, and the descriptions mitigate it well.

Naming Consistency5/5

All eight tools follow a strict snake_case verb_noun pattern (delete_app, get_app, list_versions, publish_app, rollback_app, update_app). The only variation is the resource noun (app vs spec/versions), which is a meaningful distinction rather than an inconsistency.

Tool Count5/5

Eight tools is well-scoped for an app-hosting/versioning server. Each tool earns its place: CRUD plus version listing, rollback, and a read-only spec view—no redundancy or filler.

Completeness4/5

Full lifecycle is covered: create (publish_app), read (get_app, get_spec, list_apps), update, delete, and version browsing/rollback. Minor gap: there is no get_version to inspect a specific historical version's code without first rolling back to it.

Available Tools

8 tools
delete_appA
DestructiveIdempotent
Inspect

Deletes an app and every version of it; it stops opening on the user's phone and can't be brought back. Only do this when the user has clearly asked for that app to be deleted.

NameRequiredDescriptionDefault
appYesThe app's address (its slug, as list_apps shows it) or its id.

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already carry destructiveHint=true and idempotentHint=true, and the description adds genuine value beyond them: the irreversible consequence ('can't be brought back') and the user-visible effect ('stops opening on the user's phone'). It stops short of describing any confirmation or cascade side effects outside the app itself.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two compact sentences; the consequence is stated first and the usage constraint second. Nothing is padded or repeated from the annotations.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter destructive tool with annotations covering the safety profile and no output schema, the description supplies exactly the missing pieces: irreversibility, user impact, and a usage gate.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the single 'app' parameter is fully documented (slug as list_apps shows it, or id). The description adds no parameter-level detail beyond that, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource plus the blast radius ('every version of it'), which is exactly what separates it from update_app or rollback_app. An agent knows this removes the entire artifact, not one version.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a clear gating condition ('Only do this when the user has clearly asked for that app to be deleted'), which is meaningful for a destructive tool. It does not name sibling alternatives such as rollback_app for recovery, so it falls short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_appA
Read-only
Inspect

One app with its code, blueprint and tests, to read or change it. The version served unless you ask for another.

NameRequiredDescriptionDefault
appYesThe app's address (its slug, as list_apps shows it) or its id.
versionNoOptional: an earlier version's code.

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, so the safety profile is covered. The description adds genuinely useful context beyond that: it discloses the payload (code, blueprint, tests) and the versioning default, which matters since there is no output schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, front-loaded with the resource and payload, followed by the version note. Nearly zero waste, though 'to read or change it' is slightly redundant and mildly confusing.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description adequately conveys what comes back (code, blueprint, tests) and the version default, and annotations cover safety. Auth requirements and failure behavior for a bad slug/id are left implicit.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds meaning beyond the schema by explaining the version default ('the version served unless you ask for another'), which clarifies behavior the schema only labels as 'Optional'.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb ('One app with its code, blueprint and tests') and singular scope that clearly separates it from list_apps and get_spec. The trailing 'to read or change it' is a bit ambiguous for a read-only tool but the core resource is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'The version served unless you ask for another' gives implied guidance on default vs explicit version. There is no explicit when-to-use/when-not or routing to siblings like list_versions or get_spec, so usage is only implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_specA
Read-only
Inspect

An app's blueprint, tests and the last test run, without its code. The version served unless you ask for another. Read it before changing an app.

NameRequiredDescriptionDefault
appYesThe app's address (its slug, as list_apps shows it) or its id.
versionNoOptional: an earlier version's blueprint and tests.

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, so the description only needs to add context — and it does: it discloses the default version selection behavior and that code is excluded from the payload. It does not discuss size limits or pagination for potentially large blueprints, which is the one behavioral gap for a read tool returning substantial content.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, each doing distinct work: scoping the payload, explaining version defaulting, and prescribing the workflow. The key scoping fact is front-loaded and nothing is redundant.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description must signal return contents, and it does (blueprint, tests, last test run, no code). Safety is covered by annotations. It omits any note on payload size or shape for large blueprints, but an agent has enough to invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, with both app (slug or id) and version fully documented in the schema, so the schema does the heavy lifting and a 3 is the baseline. The description reinforces the version default ('the version served unless you ask for another'), which adds a small amount of meaning beyond the schema's terse 'an earlier version's blueprint and tests.'

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names the exact resource contents returned (blueprint, tests, last test run) and scopes it against the obvious alternative by saying 'without its code', which implicitly separates it from get_app. The retrieval verb itself is only implied by the tool name, not stated in the prose, so it falls just short of a full verb+resource statement.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Read it before changing an app' gives a concrete when-to-use rule tied to a workflow with sibling mutations (update_app, publish_app, rollback_app). 'The version served unless you ask for another' sets expectations for the default case. No explicit when-not or named alternative (e.g. list_versions, get_app) is offered, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_appsB
Read-only
Inspect

The user's apps: name, address, the version installed on their phone, and the install link.

NameRequiredDescriptionDefault

No parameters

TDQS

B3.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, so safety is covered by structured data. The description's added value is the scope ('the user's apps') and the returned fields, but it says nothing about ordering, pagination, or what an empty result means; with annotations doing the safety work, this is adequate but thin.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One short sentence that front-loads the resource and lists the fields without filler. The sentence fragment lacks a verb, which makes it read as a data label rather than an action statement.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description carries the return-value load, and it does list the key fields. It still leaves out pagination, ordering, and whether the list is exhaustive, which an agent calling a listing tool would want to know.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline of 4 applies; there is nothing for the description to disambiguate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names the resource ('the user's apps') and enumerates the fields it reports, which lets an agent distinguish it from get_app, delete_app, or list_versions. It never states the verb ('list') explicitly, relying on the name and title to convey the read action.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance on when to call this versus siblings such as get_app (single app) or list_versions, and no mention of preconditions. The agent must infer from the name alone that this is the bulk-listing entry point.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_versionsA
Read-only
Inspect

Every version of an app, newest first, with what changed in each and which one is installed.

NameRequiredDescriptionDefault
appYesThe app's address (its slug, as list_apps shows it) or its id.

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, so the safety profile is covered. The description adds genuinely useful behavior: results are ordered newest first and include change details plus an installed marker, which the annotations do not convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One compact sentence with zero waste; the ordering and return content are front-loaded where an agent will read them.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With one parameter, no output schema, and simple read semantics, the description covers what an agent needs, including return shape and ordering. It could have mentioned pagination or result size limits for completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the single 'app' parameter is fully documented in the schema as a slug or id. The description adds no further parameter guidance, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific resource and scope (every version of an app) and is clearly distinguishable from siblings like get_app or list_apps. It lacks an explicit verb ('List'), but the intent is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The use case (inspect version history) is implied by the content described, but there is no explicit when-to-use guidance or reference to alternatives such as get_app for a single version or rollback_app for reverting. The agent must infer the routing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

publish_appAInspect

Publishes a new app and returns its install link. Its code is checked first, and its tests run in a browser; a version is kept only if they pass. Password or card fields, and forms that send to other sites, are refused.

NameRequiredDescriptionDefault
htmlYesThe whole app, as one self-contained HTML file. At most 1 MB.
nameYesA short name for the home screen, like Habits.
noteNoWhat changed, in a few words the user would understand.
testsYesThe tests that prove every journey step and promise. Required.
blueprintYesThe app's blueprint: what it is for, the data it keeps, its journeys and promises, and the sites it talks to. Required.

TDQS

A4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the annotations, which only say it is a non-destructive write. The description discloses the full validation pipeline (code check, browser-run tests), the versioning consequence (a version is kept only if tests pass), the content policy (password/card fields and cross-site forms refused), and that an install link comes back.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, all load-bearing, with the core action and its return value front-loaded. No repetition of field names or restating of the title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a five-parameter tool with a deeply nested blueprint/tests schema, the description covers what the tool does and the main behavioral surprises. The gaps are what failure looks like (validation errors and whether anything is left behind) and how this relates to list_versions/rollback_app, which matters given there is no output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so name, html, blueprint and tests are already documented in place. The description adds only indirect meaning — that the supplied tests are actually executed and gate the published version — and says nothing about the blueprint structure or the optional note.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Publishes a new app") plus the immediate outcome ("returns its install link"). "New" implicitly separates it from update_app, but the sibling is never named, so an agent must infer the boundary between publishing and updating.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage context is implied rather than stated: publishing is for a brand-new app, and the refusal rules (password/card fields, off-site forms) pre-empt some attempts. There is no explicit 'when not to use this' or 'use update_app instead' routing against the seven sibling tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

rollback_appA
Idempotent
Inspect

Serves an earlier version of an app again. Nothing is deleted, so it can be rolled forward the same way.

NameRequiredDescriptionDefault
appYesThe app's address (its slug, as list_apps shows it) or its id.
versionYesThe version to serve.

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare non-destructive, idempotent mutation, but the description adds genuinely useful context beyond them: nothing is deleted by a rollback and the change can be reversed by rolling forward again. That reversibility framing is not derivable from destructiveHint=false alone and materially reduces hesitation about calling a write tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences with the action front-loaded and the risk-reassurance second. No filler, no restatement of the title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter, fully documented, richly annotated mutation with no output schema, the definition covers what an agent needs to act safely. The only gap is failing to point at list_versions as the way to discover valid version numbers.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and both parameters are documented inline, including that 'app' accepts a slug or id and that 'version' is the version to serve. The description adds no further parameter meaning, which is the correct baseline when the schema carries the load.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (serve an earlier version) and resource (app), and the phrase 'again' makes clear it is a revert operation, distinguishing it from publish_app and update_app. It stops short of naming the siblings it is not, so it is clear but not fully differentiated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: the agent must infer it needs an existing version, and list_versions or publish_app are never referenced as the prerequisite or alternative path. There are no explicit when/when-not conditions, but the operation is conventional enough that inference is low-risk.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_appAInspect

Replaces an app's code with a new version, which the user's phone picks up the next time the app opens. Send the whole file, blueprint and tests. Earlier versions are kept, so it can be undone.

NameRequiredDescriptionDefault
appYesThe app's address (its slug, as list_apps shows it) or its id.
htmlYesThe whole app, as one self-contained HTML file. At most 1 MB.
nameNoOptional: a new name.
noteNoWhat changed, in a few words the user would understand.
testsYesThe tests that prove every journey step and promise. Required.
blueprintYesThe app's blueprint: what it is for, the data it keeps, its journeys and promises, and the sites it talks to. Required.

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare non-read-only, non-destructive, closed-world, so the bar is lower. The description still adds real behavioral context: the change is a whole-file replacement (not a partial patch), it propagates on next app open, and prior versions are retained so the update is reversible — consistent with destructiveHint=false.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, front-loaded with the core action, then the propagation timing, then the completeness requirement and reversibility. No filler; every sentence carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex tool with deeply nested schema and no output schema, the description covers what matters behaviorally (full replacement, propagation timing, undo). It stops short of stating return behavior or whether validation failures leave the app untouched, which would fully close the loop.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description reinforces that html, blueprint and tests must all be sent in full rather than the schema's per-field hints adding anything new, and it says nothing about the optional name or note parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource — "Replaces an app's code with a new version" — so the agent knows this is a full-replacement update rather than a patch or a publish. It does not explicitly distinguish itself from siblings like publish_app or rollback_app, which is the only thing keeping it from a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: the mention that the phone picks up the new version on next open and that earlier versions are kept suggests an iterative edit-then-rollout flow, but the description never says when to choose this over publish_app or rollback_app, nor any preconditions (e.g. the app must already exist).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updates
    • Addedget_spec
    • Changedpublish_app3 fields changed
      • addedInput schema / properties / blueprint
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "The app's blueprint: what it is for, the data it keeps, its journeys and promises, and the sites it talks to. Required.",
        +  "properties": {
        +    "data": {
        +      "additionalProperties": false,
        +      "description": "The data the app keeps on the phone.",
        +      "properties": {
        +        "keeps": {
        +          "description": "Each storage key and what it holds.",
        +          "items": {
        +            "additionalProperties": false,
        +            "properties": {
        +              "key": {
        +                "type": "string"
        +              },
        +              "what": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "key",
        +              "what"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "version": {
        +          "description": "Raise it when the saved shape changes, and migrate the old shape on load.",
        +          "type": "integer"
        +        }
        +      },
        +      "required": [
        +        "version",
        +        "keeps"
        +      ],
        +      "type": "object"
        +    },
        +    "design": {
        +      "additionalProperties": false,
        +      "properties": {
        +        "notes": {
        +          "type": "string"
        +        },
        +        "palette": {
        +          "items": {
        +            "type": "string"
        +          },
        +          "type": "array"
        +        }
        +      },
        +      "required": [],
        +      "type": "object"
        +    },
        +    "icon": {
        +      "description": "The app's home-screen icon, one of these names; without one the icon shows the app's first letter.",
        +      "enum": [
        +        "activity",
        +        "alarm-clock",
        +        "apple",
        +        "baby",
        +        "backpack",
        +        "badge-check",
        +        "banknote",
        +        "bath",
        +        "battery",
        +        "bed",
        +        "bell",
        +        "bike",
        +        "book",
        +        "book-open",
        +        "bookmark",
        +        "brain",
        +        "briefcase",
        +        "brush",
        +        "bus",
        +        "cake",
        +        "calculator",
        +        "calendar",
        +        "calendar-check",
        +        "camera",
        +        "car",
        +        "carrot",
        +        "cat",
        +        "chart-line",
        +        "chart-pie",
        +        "chef-hat",
        +        "circle-check",
        +        "clipboard-list",
        +        "clock",
        +        "cloud",
        +        "cloud-rain",
        +        "cloud-sun",
        +        "coffee",
        +        "coins",
        +        "compass",
        +        "cookie",
        +        "credit-card",
        +        "dog",
        +        "dollar-sign",
        +        "droplet",
        +        "dumbbell",
        +        "egg",
        +        "file-text",
        +        "film",
        +        "fish",
        +        "flag",
        +        "flame",
        +        "flower",
        +        "footprints",
        +        "frown",
        +        "gamepad-2",
        +        "gift",
        +        "glass-water",
        +        "globe",
        +        "graduation-cap",
        +        "guitar",
        +        "hammer",
        +        "hand-heart",
        +        "headphones",
        +        "heart",
        +        "heart-pulse",
        +        "hospital",
        +        "house",
        +        "key",
        +        "laptop",
        +        "laugh",
        +        "leaf",
        +        "lightbulb",
        +        "list-checks",
        +        "list-todo",
        +        "luggage",
        +        "map",
        +        "map-pin",
        +        "medal",
        +        "meh",
        +        "message-circle",
        +        "mic",
        +        "moon",
        +        "mountain",
        +        "music",
        +        "notebook-pen",
        +        "package",
        +        "palette",
        +        "paw-print",
        +        "pen-line",
        +        "phone",
        +        "piggy-bank",
        +        "pill",
        +        "pizza",
        +        "plane",
        +        "puzzle",
        +        "receipt",
        +        "repeat",
        +        "rocket",
        +        "salad",
        +        "scale",
        +        "school",
        +        "shirt",
        +        "shopping-bag",
        +        "shopping-basket",
        +        "shopping-cart",
        +        "shower-head",
        +        "smile",
        +        "snowflake",
        +        "sofa",
        +        "sparkles",
        +        "sprout",
        +        "star",
        +        "stethoscope",
        +        "sun",
        +        "sunrise",
        +        "syringe",
        +        "target",
        +        "tent",
        +        "thermometer",
        +        "timer",
        +        "train-front",
        +        "tree-pine",
        +        "trophy",
        +        "umbrella",
        +        "users",
        +        "utensils",
        +        "wallet",
        +        "watch",
        +        "weight",
        +        "wine"
        +      ],
        +      "type": "string"
        +    },
        +    "journeys": {
        +      "description": "What the person does, step by step. Every step is proved by a test.",
        +      "items": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "id": {
        +            "description": "J-1, J-2, …",
        +            "type": "string"
        +          },
        +          "steps": {
        +            "items": {
        +              "additionalProperties": false,
        +              "properties": {
        +                "id": {
        +                  "description": "J-1.1, J-1.2, … within journey J-1",
        +                  "type": "string"
        +                },
        +                "text": {
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "id",
        +                "text"
        +              ],
        +              "type": "object"
        +            },
        +            "minItems": 1,
        +            "type": "array"
        +          },
        +          "title": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "title",
        +          "steps"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "network": {
        +      "description": "The sites the app talks to, at most 5; every other site is blocked.",
        +      "items": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "site": {
        +            "description": "A bare website name, like api.open-meteo.com",
        +            "type": "string"
        +          },
        +          "why": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "site",
        +          "why"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "promises": {
        +      "description": "What always holds. Every promise is proved by a test.",
        +      "items": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "id": {
        +            "description": "P-1, P-2, …",
        +            "type": "string"
        +          },
        +          "kind": {
        +            "const": "data",
        +            "description": "\"data\" for a promise about saved data.",
        +            "type": "string"
        +          },
        +          "text": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "text"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "purpose": {
        +      "description": "What the app is for, in a sentence.",
        +      "type": "string"
        +    },
        +    "screens": {
        +      "description": "Each screen and what it shows.",
        +      "items": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "id": {
        +            "type": "string"
        +          },
        +          "says": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "says"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "v": {
        +      "const": 1,
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "v",
        +    "purpose",
        +    "screens",
        +    "data",
        +    "journeys",
        +    "promises"
        +  ],
        +  "type": "object"
        +}
      • addedInput schema / properties / tests
        Added value: +{
        +  "description": "The tests that prove every journey step and promise. Required.",
        +  "items": {
        +    "anyOf": [
        +      {
        +        "additionalProperties": false,
        +        "description": "Runs its steps on the app, opened fresh.",
        +        "properties": {
        +          "id": {
        +            "description": "T-1, T-2, …",
        +            "type": "string"
        +          },
        +          "kind": {
        +            "const": "behaviour",
        +            "type": "string"
        +          },
        +          "proves": {
        +            "description": "The journey step and promise ids this test proves.",
        +            "items": {
        +              "type": "string"
        +            },
        +            "type": "array"
        +          },
        +          "steps": {
        +            "items": {
        +              "anyOf": [
        +                {
        +                  "additionalProperties": false,
        +                  "description": "Finds what to tap by its text, label or role (and name): give at least one of text, label or role.",
        +                  "properties": {
        +                    "do": {
        +                      "const": "tap",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "name": {
        +                      "type": "string"
        +                    },
        +                    "role": {
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "type",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "value": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "label",
        +                    "value"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "select",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "value": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "label",
        +                    "value"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "reload",
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "wait",
        +                      "type": "string"
        +                    },
        +                    "ms": {
        +                      "minimum": 0,
        +                      "type": "integer"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "ms"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "expect": {
        +                      "enum": [
        +                        "see",
        +                        "notSee"
        +                      ],
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "text"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "expect": {
        +                      "enum": [
        +                        "checked",
        +                        "notChecked"
        +                      ],
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "label"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "equals": {
        +                      "minimum": 0,
        +                      "type": "integer"
        +                    },
        +                    "expect": {
        +                      "const": "count",
        +                      "type": "string"
        +                    },
        +                    "role": {
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "equals"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "equals": {
        +                      "type": "string"
        +                    },
        +                    "expect": {
        +                      "const": "value",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "label",
        +                    "equals"
        +                  ],
        +                  "type": "object"
        +                }
        +              ]
        +            },
        +            "minItems": 1,
        +            "type": "array"
        +          },
        +          "title": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "title",
        +          "proves",
        +          "steps"
        +        ],
        +        "type": "object"
        +      },
        +      {
        +        "additionalProperties": false,
        +        "description": "Proves saved data survives: setup, then check.",
        +        "properties": {
        +          "check": {
        +            "description": "After a reload, checks the data still shows. On an update these run in the new version.",
        +            "items": {
        +              "anyOf": [
        +                {
        +                  "additionalProperties": false,
        +                  "description": "Finds what to tap by its text, label or role (and name): give at least one of text, label or role.",
        +                  "properties": {
        +                    "do": {
        +                      "const": "tap",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "name": {
        +                      "type": "string"
        +                    },
        +                    "role": {
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "type",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "value": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "label",
        +                    "value"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "select",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "value": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "label",
        +                    "value"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "reload",
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "wait",
        +                      "type": "string"
        +                    },
        +                    "ms": {
        +                      "minimum": 0,
        +                      "type": "integer"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "ms"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "expect": {
        +                      "enum": [
        +                        "see",
        +                        "notSee"
        +                      ],
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "text"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "expect": {
        +                      "enum": [
        +                        "checked",
        +                        "notChecked"
        +                      ],
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "label"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "equals": {
        +                      "minimum": 0,
        +                      "type": "integer"
        +                    },
        +                    "expect": {
        +                      "const": "count",
        +                      "type": "string"
        +                    },
        +                    "role": {
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "equals"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "equals": {
        +                      "type": "string"
        +                    },
        +                    "expect": {
        +                      "const": "value",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "label",
        +                    "equals"
        +                  ],
        +                  "type": "object"
        +                }
        +              ]
        +            },
        +            "minItems": 1,
        +            "type": "array"
        +          },
        +          "id": {
        +            "description": "T-1, T-2, …",
        +            "type": "string"
        +          },
        +          "kind": {
        +            "const": "data",
        +            "type": "string"
        +          },
        +          "proves": {
        +            "description": "The promise ids about saved data this test proves.",
        +            "items": {
        +              "type": "string"
        +            },
        +            "type": "array"
        +          },
        +          "setup": {
        +            "description": "Saves data. On an update these run in the version before.",
        +            "items": {
        +              "anyOf": [
        +                {
        +                  "additionalProperties": false,
        +                  "description": "Finds what to tap by its text, label or role (and name): give at least one of text, label or role.",
        +                  "properties": {
        +                    "do": {
        +                      "const": "tap",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "name": {
        +                      "type": "string"
        +                    },
        +                    "role": {
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "type",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "value": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "label",
        +                    "value"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "select",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "value": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "label",
        +                    "value"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "reload",
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "wait",
        +                      "type": "string"
        +                    },
        +                    "ms": {
        +                      "minimum": 0,
        +                      "type": "integer"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "ms"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "expect": {
        +                      "enum": [
        +                        "see",
        +                        "notSee"
        +                      ],
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "text"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "expect": {
        +                      "enum": [
        +                        "checked",
        +                        "notChecked"
        +                      ],
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "label"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "equals": {
        +                      "minimum": 0,
        +                      "type": "integer"
        +                    },
        +                    "expect": {
        +                      "const": "count",
        +                      "type": "string"
        +                    },
        +                    "role": {
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "equals"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "equals": {
        +                      "type": "string"
        +                    },
        +                    "expect": {
        +                      "const": "value",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "label",
        +                    "equals"
        +                  ],
        +                  "type": "object"
        +                }
        +              ]
        +            },
        +            "minItems": 1,
        +            "type": "array"
        +          },
        +          "title": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "title",
        +          "proves",
        +          "kind",
        +          "setup",
        +          "check"
        +        ],
        +        "type": "object"
        +      }
        +    ]
        +  },
        +  "type": "array"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "name",
        -  "html"
        -]New value: +[
        +  "name",
        +  "html",
        +  "blueprint",
        +  "tests"
        +]
    • Changedupdate_app3 fields changed
      • addedInput schema / properties / blueprint
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "The app's blueprint: what it is for, the data it keeps, its journeys and promises, and the sites it talks to. Required.",
        +  "properties": {
        +    "data": {
        +      "additionalProperties": false,
        +      "description": "The data the app keeps on the phone.",
        +      "properties": {
        +        "keeps": {
        +          "description": "Each storage key and what it holds.",
        +          "items": {
        +            "additionalProperties": false,
        +            "properties": {
        +              "key": {
        +                "type": "string"
        +              },
        +              "what": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "key",
        +              "what"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "version": {
        +          "description": "Raise it when the saved shape changes, and migrate the old shape on load.",
        +          "type": "integer"
        +        }
        +      },
        +      "required": [
        +        "version",
        +        "keeps"
        +      ],
        +      "type": "object"
        +    },
        +    "design": {
        +      "additionalProperties": false,
        +      "properties": {
        +        "notes": {
        +          "type": "string"
        +        },
        +        "palette": {
        +          "items": {
        +            "type": "string"
        +          },
        +          "type": "array"
        +        }
        +      },
        +      "required": [],
        +      "type": "object"
        +    },
        +    "icon": {
        +      "description": "The app's home-screen icon, one of these names; without one the icon shows the app's first letter.",
        +      "enum": [
        +        "activity",
        +        "alarm-clock",
        +        "apple",
        +        "baby",
        +        "backpack",
        +        "badge-check",
        +        "banknote",
        +        "bath",
        +        "battery",
        +        "bed",
        +        "bell",
        +        "bike",
        +        "book",
        +        "book-open",
        +        "bookmark",
        +        "brain",
        +        "briefcase",
        +        "brush",
        +        "bus",
        +        "cake",
        +        "calculator",
        +        "calendar",
        +        "calendar-check",
        +        "camera",
        +        "car",
        +        "carrot",
        +        "cat",
        +        "chart-line",
        +        "chart-pie",
        +        "chef-hat",
        +        "circle-check",
        +        "clipboard-list",
        +        "clock",
        +        "cloud",
        +        "cloud-rain",
        +        "cloud-sun",
        +        "coffee",
        +        "coins",
        +        "compass",
        +        "cookie",
        +        "credit-card",
        +        "dog",
        +        "dollar-sign",
        +        "droplet",
        +        "dumbbell",
        +        "egg",
        +        "file-text",
        +        "film",
        +        "fish",
        +        "flag",
        +        "flame",
        +        "flower",
        +        "footprints",
        +        "frown",
        +        "gamepad-2",
        +        "gift",
        +        "glass-water",
        +        "globe",
        +        "graduation-cap",
        +        "guitar",
        +        "hammer",
        +        "hand-heart",
        +        "headphones",
        +        "heart",
        +        "heart-pulse",
        +        "hospital",
        +        "house",
        +        "key",
        +        "laptop",
        +        "laugh",
        +        "leaf",
        +        "lightbulb",
        +        "list-checks",
        +        "list-todo",
        +        "luggage",
        +        "map",
        +        "map-pin",
        +        "medal",
        +        "meh",
        +        "message-circle",
        +        "mic",
        +        "moon",
        +        "mountain",
        +        "music",
        +        "notebook-pen",
        +        "package",
        +        "palette",
        +        "paw-print",
        +        "pen-line",
        +        "phone",
        +        "piggy-bank",
        +        "pill",
        +        "pizza",
        +        "plane",
        +        "puzzle",
        +        "receipt",
        +        "repeat",
        +        "rocket",
        +        "salad",
        +        "scale",
        +        "school",
        +        "shirt",
        +        "shopping-bag",
        +        "shopping-basket",
        +        "shopping-cart",
        +        "shower-head",
        +        "smile",
        +        "snowflake",
        +        "sofa",
        +        "sparkles",
        +        "sprout",
        +        "star",
        +        "stethoscope",
        +        "sun",
        +        "sunrise",
        +        "syringe",
        +        "target",
        +        "tent",
        +        "thermometer",
        +        "timer",
        +        "train-front",
        +        "tree-pine",
        +        "trophy",
        +        "umbrella",
        +        "users",
        +        "utensils",
        +        "wallet",
        +        "watch",
        +        "weight",
        +        "wine"
        +      ],
        +      "type": "string"
        +    },
        +    "journeys": {
        +      "description": "What the person does, step by step. Every step is proved by a test.",
        +      "items": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "id": {
        +            "description": "J-1, J-2, …",
        +            "type": "string"
        +          },
        +          "steps": {
        +            "items": {
        +              "additionalProperties": false,
        +              "properties": {
        +                "id": {
        +                  "description": "J-1.1, J-1.2, … within journey J-1",
        +                  "type": "string"
        +                },
        +                "text": {
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "id",
        +                "text"
        +              ],
        +              "type": "object"
        +            },
        +            "minItems": 1,
        +            "type": "array"
        +          },
        +          "title": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "title",
        +          "steps"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "network": {
        +      "description": "The sites the app talks to, at most 5; every other site is blocked.",
        +      "items": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "site": {
        +            "description": "A bare website name, like api.open-meteo.com",
        +            "type": "string"
        +          },
        +          "why": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "site",
        +          "why"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "promises": {
        +      "description": "What always holds. Every promise is proved by a test.",
        +      "items": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "id": {
        +            "description": "P-1, P-2, …",
        +            "type": "string"
        +          },
        +          "kind": {
        +            "const": "data",
        +            "description": "\"data\" for a promise about saved data.",
        +            "type": "string"
        +          },
        +          "text": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "text"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "purpose": {
        +      "description": "What the app is for, in a sentence.",
        +      "type": "string"
        +    },
        +    "screens": {
        +      "description": "Each screen and what it shows.",
        +      "items": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "id": {
        +            "type": "string"
        +          },
        +          "says": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "says"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "v": {
        +      "const": 1,
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "v",
        +    "purpose",
        +    "screens",
        +    "data",
        +    "journeys",
        +    "promises"
        +  ],
        +  "type": "object"
        +}
      • addedInput schema / properties / tests
        Added value: +{
        +  "description": "The tests that prove every journey step and promise. Required.",
        +  "items": {
        +    "anyOf": [
        +      {
        +        "additionalProperties": false,
        +        "description": "Runs its steps on the app, opened fresh.",
        +        "properties": {
        +          "id": {
        +            "description": "T-1, T-2, …",
        +            "type": "string"
        +          },
        +          "kind": {
        +            "const": "behaviour",
        +            "type": "string"
        +          },
        +          "proves": {
        +            "description": "The journey step and promise ids this test proves.",
        +            "items": {
        +              "type": "string"
        +            },
        +            "type": "array"
        +          },
        +          "steps": {
        +            "items": {
        +              "anyOf": [
        +                {
        +                  "additionalProperties": false,
        +                  "description": "Finds what to tap by its text, label or role (and name): give at least one of text, label or role.",
        +                  "properties": {
        +                    "do": {
        +                      "const": "tap",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "name": {
        +                      "type": "string"
        +                    },
        +                    "role": {
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "type",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "value": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "label",
        +                    "value"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "select",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "value": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "label",
        +                    "value"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "reload",
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "wait",
        +                      "type": "string"
        +                    },
        +                    "ms": {
        +                      "minimum": 0,
        +                      "type": "integer"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "ms"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "expect": {
        +                      "enum": [
        +                        "see",
        +                        "notSee"
        +                      ],
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "text"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "expect": {
        +                      "enum": [
        +                        "checked",
        +                        "notChecked"
        +                      ],
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "label"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "equals": {
        +                      "minimum": 0,
        +                      "type": "integer"
        +                    },
        +                    "expect": {
        +                      "const": "count",
        +                      "type": "string"
        +                    },
        +                    "role": {
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "equals"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "equals": {
        +                      "type": "string"
        +                    },
        +                    "expect": {
        +                      "const": "value",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "label",
        +                    "equals"
        +                  ],
        +                  "type": "object"
        +                }
        +              ]
        +            },
        +            "minItems": 1,
        +            "type": "array"
        +          },
        +          "title": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "title",
        +          "proves",
        +          "steps"
        +        ],
        +        "type": "object"
        +      },
        +      {
        +        "additionalProperties": false,
        +        "description": "Proves saved data survives: setup, then check.",
        +        "properties": {
        +          "check": {
        +            "description": "After a reload, checks the data still shows. On an update these run in the new version.",
        +            "items": {
        +              "anyOf": [
        +                {
        +                  "additionalProperties": false,
        +                  "description": "Finds what to tap by its text, label or role (and name): give at least one of text, label or role.",
        +                  "properties": {
        +                    "do": {
        +                      "const": "tap",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "name": {
        +                      "type": "string"
        +                    },
        +                    "role": {
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "type",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "value": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "label",
        +                    "value"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "select",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "value": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "label",
        +                    "value"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "reload",
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "wait",
        +                      "type": "string"
        +                    },
        +                    "ms": {
        +                      "minimum": 0,
        +                      "type": "integer"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "ms"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "expect": {
        +                      "enum": [
        +                        "see",
        +                        "notSee"
        +                      ],
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "text"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "expect": {
        +                      "enum": [
        +                        "checked",
        +                        "notChecked"
        +                      ],
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "label"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "equals": {
        +                      "minimum": 0,
        +                      "type": "integer"
        +                    },
        +                    "expect": {
        +                      "const": "count",
        +                      "type": "string"
        +                    },
        +                    "role": {
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "equals"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "equals": {
        +                      "type": "string"
        +                    },
        +                    "expect": {
        +                      "const": "value",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "label",
        +                    "equals"
        +                  ],
        +                  "type": "object"
        +                }
        +              ]
        +            },
        +            "minItems": 1,
        +            "type": "array"
        +          },
        +          "id": {
        +            "description": "T-1, T-2, …",
        +            "type": "string"
        +          },
        +          "kind": {
        +            "const": "data",
        +            "type": "string"
        +          },
        +          "proves": {
        +            "description": "The promise ids about saved data this test proves.",
        +            "items": {
        +              "type": "string"
        +            },
        +            "type": "array"
        +          },
        +          "setup": {
        +            "description": "Saves data. On an update these run in the version before.",
        +            "items": {
        +              "anyOf": [
        +                {
        +                  "additionalProperties": false,
        +                  "description": "Finds what to tap by its text, label or role (and name): give at least one of text, label or role.",
        +                  "properties": {
        +                    "do": {
        +                      "const": "tap",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "name": {
        +                      "type": "string"
        +                    },
        +                    "role": {
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "type",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "value": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "label",
        +                    "value"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "select",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "value": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "label",
        +                    "value"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "reload",
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "do"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "do": {
        +                      "const": "wait",
        +                      "type": "string"
        +                    },
        +                    "ms": {
        +                      "minimum": 0,
        +                      "type": "integer"
        +                    }
        +                  },
        +                  "required": [
        +                    "do",
        +                    "ms"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "expect": {
        +                      "enum": [
        +                        "see",
        +                        "notSee"
        +                      ],
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "text"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "expect": {
        +                      "enum": [
        +                        "checked",
        +                        "notChecked"
        +                      ],
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "label"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "equals": {
        +                      "minimum": 0,
        +                      "type": "integer"
        +                    },
        +                    "expect": {
        +                      "const": "count",
        +                      "type": "string"
        +                    },
        +                    "role": {
        +                      "type": "string"
        +                    },
        +                    "text": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "equals"
        +                  ],
        +                  "type": "object"
        +                },
        +                {
        +                  "additionalProperties": false,
        +                  "properties": {
        +                    "equals": {
        +                      "type": "string"
        +                    },
        +                    "expect": {
        +                      "const": "value",
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "expect",
        +                    "label",
        +                    "equals"
        +                  ],
        +                  "type": "object"
        +                }
        +              ]
        +            },
        +            "minItems": 1,
        +            "type": "array"
        +          },
        +          "title": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "title",
        +          "proves",
        +          "kind",
        +          "setup",
        +          "check"
        +        ],
        +        "type": "object"
        +      }
        +    ]
        +  },
        +  "type": "array"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "app",
        -  "html"
        -]New value: +[
        +  "app",
        +  "html",
        +  "blueprint",
        +  "tests"
        +]
  2. 24 tool updates
    • Addeddelete_app
    • Addedget_app
    • Removedget_approval
    • Removedget_dataset
    • Removedget_next_actions
    • Removedget_readiness
    • Removedget_receipts
    • Removedget_run
    • Removedget_tool_insights
    • Removedget_usage
    • Removedlist_approvals
    • Addedlist_apps
    • Removedlist_contracts
    • Removedlist_datasets
    • Removedlist_drift_alerts
    • Removedlist_events
    • Removedlist_review_items
    • Removedlist_scenarios
    • Removedlist_tools
    • Removedlist_value_sets
    • Addedlist_versions
    • Addedpublish_app
    • Addedrollback_app
    • Addedupdate_app
  3. 9 tool updates
    • Addedget_approval
    • Addedget_next_actions
    • Addedget_tool_insights
    • Addedget_usage
    • Addedlist_approvals
    • Addedlist_drift_alerts
    • Addedlist_events
    • Addedlist_scenarios
    • Addedlist_tools
  4. 8 tool updates
    • First observedget_dataset
    • First observedget_readiness
    • First observedget_receipts
    • First observedget_run
    • First observedlist_contracts
    • First observedlist_datasets
    • First observedlist_review_items
    • First observedlist_value_sets

Publisher details

Operator
CloudCrane · Publisher source
Vendor relationship
First-party · Publisher source
Restrictions
Not available

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Turn your Android phone into an MCP server. AI assistants connect on demand, query your health data, notifications, app usage, and take actions on your device — all end-to-end encrypted, no cloud sync.
    9
    Apache 2.0
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI assistants to transcribe local audio or video files on-device, with optional timestamps and cleanup, and to search locally stored dictation history without uploading audio or needing an API key. It also lets clients pre-download and verify the speech model before the first transcription.
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Connect an AI assistant to aCelery, describe the app you want in plain words, and it writes the app, creates its database, runs it on your phone, checks for errors and fixes them — while you watch.
    GPL 3.0
Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.