tada-mcp
An automated QA assistant for AI-written code. Connects your coding agent to Tada's tools over MCP: create a test, check it in a real browser, and get a verdict (Passed, Blocked or Inconclusive) with what to do next.
npm · fingerprint 3278a36f5ed380a214b64283 · repository · RSS
What changed
Changed between 13 October 2026 and 20 October 2026 (first seen 2026-10-20 11:33 UTC)
14 schema or annotation changes with the description left byte-identical.
--- pinned/about_app +++ observed/about_app { "description": "Read what Tada knows about an app before you create a test for it: a test account that cannot sign in (first, with its fix), its features and tests, its test accounts, its screens, and what is still to connect. Free; changes nothing.", "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "repo": { + "description": "The app, as \"owner/name\". Left out, the workspace's only app.", + "maxLength": 200, + "type": "string" + } + }, "type": "object" }, "name": "about_app"
--- pinned/check_feature +++ observed/check_feature { "description": "Old name for create_test; use create_test.", "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "address": { + "description": "With `goal`: where to check, when it is not the app's usual address.", + "format": "uri", + "type": "string" + }, + "device": { + "description": "'desktop' (default), 'mobile' or 'tablet'.", + "enum": [ + "desktop", + "mobile", + "tablet" + ], + "type": "string" + }, + "feature": { + "description": "A word for what this test is about, kept as a TAG on the test (tests are grouped by tags, not by feature). Check the group later with check_test and its tags. With `tests`, the name plan_tests returned.", + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "goal": { + "description": "What should work, in one or two sentences, with the result in it: \"A new customer can sign up and lands on their account page.\" Tada writes the test and checks it. Send the same sentence again after a fix: Tada keeps to the test it wrote for it.", + "maxLength": 1000, + "minLength": 1, + "type": "string" + }, + "hideSelectors": { + "description": "CSS selectors of the app's own floating widgets to hide for the whole check: a chat bubble, a cookie banner. A step that touches one fails.", + "items": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + "initScript": { + "description": "JavaScript run in every page before the app's own scripts, to seed state the app reads at boot, such as a locale.", + "maxLength": 20000, + "type": "string" + }, + "repo": { + "description": "The app, as \"owner/name\". Left out, the workspace's only app. Steps need no app.", + "maxLength": 200, + "type": "string" + }, + "role": { + "description": "With `goal`: who is signed in, in the app's own word (\"admin\"). about_app lists the test accounts.", + "maxLength": 40, + "minLength": 1, + "type": "string" + }, + "signIn": { + "description": "Ask Tada for a test account in a role it does not have, before the check (create_test_account does the same). Sent alone, nothing is checked.", + "properties": { + "email": { + "description": "Only when a sign-in was already made for Tada in the app: its address. Tada adopts it.", + "format": "email", + "maxLength": 254, + "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$", + "type": "string" + }, + "environment": { + "maxLength": 120, + "minLength": 1, + "type": "string" + }, + "password": { + "description": "The password of the sign-in named by `email`. Tada seals it and never hands it back.", + "maxLength": 256, + "minLength": 1, + "type": "string" + }, + "role": { + "description": "The role, in the app's own word: \"admin\", \"client\", \"staff\".", + "maxLength": 60, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "role" + ], + "type": "object" + }, + "steps": { + "description": "The steps of one test, in order: open the app with a `goto` first, find things by the words a person reads, and after each action that matters add an `expect` (`text`, `urlIncludes` or `selector`) for what the screen then shows. A test with no expect proves nothing. Sign in with {{secret.TADA_TEST_EMAIL}} and {{secret.TADA_TEST_PASSWORD}}, or `role` on a step.", + "items": { + "additionalProperties": {}, + "properties": { + "action": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action" + ], + "type": "object" + }, + "maxItems": 200, + "minItems": 1, + "type": "array" + }, + "tests": { + "description": "Every test of a feature planned with plan_tests, named as the plan names them, each with its steps. Send with `feature`.", + "items": { + "properties": { + "flowFile": { + "maxLength": 50000, + "minLength": 1, + "type": "string" + }, + "name": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "steps": { + "items": { + "additionalProperties": {}, + "properties": { + "action": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action" + ], + "type": "object" + }, + "maxItems": 200, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "name", + "steps" + ], + "type": "object" + }, + "maxItems": 24, + "minItems": 1, + "type": "array" + }, + "ticket": { + "description": "The ticket id as the tracker writes it, for example SOL-123. Kept as the test's reference.", + "maxLength": 60, + "minLength": 1, + "type": "string" + }, + "title": { + "description": "With `steps`: what the test is called.", + "maxLength": 200, + "minLength": 1, + "type": "string" + } + }, "type": "object" }, "name": "check_feature"
--- pinned/check_result +++ observed/check_result { "description": "Old name for read_report; use read_report.", "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "id": { + "description": "The check's id, as create_test or check_test gave it.", + "maxLength": 80, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "id" + ], "type": "object" }, "name": "check_result"
--- pinned/check_test +++ observed/check_test { "description": "Check a saved test again now, by its name (or a feature's, or tags), or every switched-on test of the app. Answers with the verdict and the report.", "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "match": { + "description": "With several tags: \"any\" (the default) or \"all\".", + "enum": [ + "any", + "all" + ], + "type": "string" + }, + "repo": { + "description": "The app, as \"owner/name\". Left out, the workspace's only app.", + "maxLength": 200, + "type": "string" + }, + "tags": { + "description": "Check only the switched-on tests carrying these tags. Send either `test` or `tags`, not both.", + "items": { + "maxLength": 60, + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + "test": { + "description": "The one test or feature to check, by its name as about_app lists it. Left out, every switched-on test of the app.", + "maxLength": 200, + "type": "string" + } + }, "type": "object" }, "name": "check_test"
--- pinned/connect_status +++ observed/connect_status { "description": "See what this Tada workspace still needs connected to check pull requests, with the link for each.", "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": {}, "type": "object" }, "name": "connect_status"
--- pinned/create_test +++ observed/create_test { "description": "Create a test of a feature and check it in a real browser on Tada's side: a `goal` (Tada writes the test) or `steps`. The answer leads with the verdict, Passed, Blocked or Inconclusive, then what to do, with the report's link. An app on localhost is reached through a tunnel.", "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "address": { + "description": "With `goal`: where to check, when it is not the app's usual address.", + "format": "uri", + "type": "string" + }, + "device": { + "description": "'desktop' (default), 'mobile' or 'tablet'.", + "enum": [ + "desktop", + "mobile", + "tablet" + ], + "type": "string" + }, + "feature": { + "description": "A word for what this test is about, kept as a TAG on the test (tests are grouped by tags, not by feature). Check the group later with check_test and its tags. With `tests`, the name plan_tests returned.", + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "goal": { + "description": "What should work, in one or two sentences, with the result in it: \"A new customer can sign up and lands on their account page.\" Tada writes the test and checks it. Send the same sentence again after a fix: Tada keeps to the test it wrote for it.", + "maxLength": 1000, + "minLength": 1, + "type": "string" + }, + "hideSelectors": { + "description": "CSS selectors of the app's own floating widgets to hide for the whole check: a chat bubble, a cookie banner. A step that touches one fails.", + "items": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + "initScript": { + "description": "JavaScript run in every page before the app's own scripts, to seed state the app reads at boot, such as a locale.", + "maxLength": 20000, + "type": "string" + }, + "repo": { + "description": "The app, as \"owner/name\". Left out, the workspace's only app. Steps need no app.", + "maxLength": 200, + "type": "string" + }, + "role": { + "description": "With `goal`: who is signed in, in the app's own word (\"admin\"). about_app lists the test accounts.", + "maxLength": 40, + "minLength": 1, + "type": "string" + }, + "signIn": { + "description": "Ask Tada for a test account in a role it does not have, before the check (create_test_account does the same). Sent alone, nothing is checked.", + "properties": { + "email": { + "description": "Only when a sign-in was already made for Tada in the app: its address. Tada adopts it.", + "format": "email", + "maxLength": 254, + "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$", + "type": "string" + }, + "environment": { + "maxLength": 120, + "minLength": 1, + "type": "string" + }, + "password": { + "description": "The password of the sign-in named by `email`. Tada seals it and never hands it back.", + "maxLength": 256, + "minLength": 1, + "type": "string" + }, + "role": { + "description": "The role, in the app's own word: \"admin\", \"client\", \"staff\".", + "maxLength": 60, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "role" + ], + "type": "object" + }, + "steps": { + "description": "The steps of one test, in order: open the app with a `goto` first, find things by the words a person reads, and after each action that matters add an `expect` (`text`, `urlIncludes` or `selector`) for what the screen then shows. A test with no expect proves nothing. Sign in with {{secret.TADA_TEST_EMAIL}} and {{secret.TADA_TEST_PASSWORD}}, or `role` on a step.", + "items": { + "additionalProperties": {}, + "properties": { + "action": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action" + ], + "type": "object" + }, + "maxItems": 200, + "minItems": 1, + "type": "array" + }, + "tests": { + "description": "Every test of a feature planned with plan_tests, named as the plan names them, each with its steps. Send with `feature`.", + "items": { + "properties": { + "flowFile": { + "maxLength": 50000, + "minLength": 1, + "type": "string" + }, + "name": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "steps": { + "items": { + "additionalProperties": {}, + "properties": { + "action": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action" + ], + "type": "object" + }, + "maxItems": 200, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "name", + "steps" + ], + "type": "object" + }, + "maxItems": 24, + "minItems": 1, + "type": "array" + }, + "ticket": { + "description": "The ticket id as the tracker writes it, for example SOL-123. Kept as the test's reference.", + "maxLength": 60, + "minLength": 1, + "type": "string" + }, + "title": { + "description": "With `steps`: what the test is called.", + "maxLength": 200, + "minLength": 1, + "type": "string" + } + }, "type": "object" }, "name": "create_test"
--- pinned/create_test_account +++ observed/create_test_account { "description": "Make a test account a test can sign in as (`role`), or hand Tada one you seeded (`role`, `email`, `password`).", "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "email": { + "description": "Only with `role`, and only when you ALREADY seeded a sign-in for Tada in the app: its address. Tada adopts it.", + "format": "email", + "maxLength": 254, + "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$", + "type": "string" + }, + "environment": { + "description": "Only with `role`: which environment, by name. Leave it out unless Tada asks which one.", + "maxLength": 120, + "minLength": 1, + "type": "string" + }, + "password": { + "description": "The password of the sign-in named by `email`. Tada seals it and never hands it back.", + "maxLength": 256, + "minLength": 1, + "type": "string" + }, + "repo": { + "description": "The app, as \"owner/name\". Left out, the workspace's only app.", + "maxLength": 200, + "type": "string" + }, + "role": { + "description": "A test account in this role, in the app's own word: \"admin\", \"client\", \"staff\". Left out, the app's one test account.", + "maxLength": 60, + "minLength": 1, + "type": "string" + } + }, "type": "object" }, "name": "create_test_account"
--- pinned/plan_feature +++ observed/plan_feature { "description": "Old name for plan_tests; use plan_tests.", "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "acceptanceCriteria": { + "description": "One criterion per entry, as the ticket states them.", + "items": { + "maxLength": 400, + "minLength": 1, + "type": "string" + }, + "maxItems": 30, + "type": "array" + }, + "changed": { + "description": "The branch, and what you changed in a sentence or two.", + "properties": { + "branch": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "summary": { + "maxLength": 2000, + "minLength": 1, + "type": "string" + } + }, + "type": "object" + }, + "outOfScope": { + "description": "What this ticket deliberately does not do.", + "items": { + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + "pages": { + "description": "The pages this feature adds or changes.", + "items": { + "properties": { + "purpose": { + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "reachedBy": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "route": { + "maxLength": 300, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "route" + ], + "type": "object" + }, + "maxItems": 40, + "type": "array" + }, + "proposal": { + "description": "Your tests, once plan_tests has told you the rules. Leave out on the first call.", + "properties": { + "access": { + "description": "permissions: who may see or do what once inside. sign_in: signing in and out itself. none: neither.", + "enum": [ + "permissions", + "sign_in", + "none" + ], + "type": "string" + }, + "area": { + "anyOf": [ + { + "maxLength": 80, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The area of the app this feature lives in, as its sidebar names it (Companies): one Tada already knows when it fits." + }, + "journeys": { + "items": { + "properties": { + "as": { + "anyOf": [ + { + "maxLength": 40, + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The role it runs as, as the app names it; null for the default login." + }, + "covers": { + "default": [], + "description": "The numbers of the acceptance criteria this journey proves. Empty when none were given.", + "items": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "maxItems": 30, + "type": "array" + }, + "kind": { + "enum": [ + "setup", + "invite", + "can", + "cant", + "revoke", + "direct_url", + "check" + ], + "type": "string" + }, + "name": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "needs": { + "default": [], + "description": "Other journeys of this plan, by name, that must pass before this one can run, for example \"Access taken away\" needs \"Admin creates a viewer role\". Empty when it stands alone.", + "items": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "maxItems": 10, + "type": "array" + }, + "says": { + "description": "What should happen and why, said before it is shown.", + "maxLength": 400, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "name", + "as", + "kind", + "says" + ], + "type": "object" + }, + "maxItems": 24, + "minItems": 1, + "type": "array" + }, + "name": { + "description": "The feature, named exactly as the ticket names it.", + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "notBrowserCheckable": { + "description": "What the ticket asks for that a browser cannot check, each with the criteria numbers it accounts for.", + "items": { + "properties": { + "covers": { + "default": [], + "items": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "maxItems": 30, + "type": "array" + }, + "what": { + "maxLength": 300, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "what" + ], + "type": "object" + }, + "maxItems": 10, + "type": "array" + }, + "ticketRef": { + "anyOf": [ + { + "maxLength": 60, + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The ticket id as the tracker writes it (SOL-123), or null." + }, + "uses": { + "description": "Other features this proof needs to exist first, by name.", + "items": { + "maxLength": 200, + "type": "string" + }, + "maxItems": 10, + "type": "array" + } + }, + "required": [ + "name", + "ticketRef", + "access", + "journeys", + "notBrowserCheckable", + "uses" + ], + "type": "object" + }, + "repo": { + "description": "The app, as \"owner/name\". Left out, the workspace's only app.", + "maxLength": 200, + "type": "string" + }, + "roles": { + "description": "Who uses it, and what each may and may not do.", + "items": { + "properties": { + "can": { + "default": [], + "items": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "maxItems": 10, + "type": "array" + }, + "cannot": { + "default": [], + "items": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "maxItems": 10, + "type": "array" + }, + "name": { + "maxLength": 40, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "maxItems": 12, + "type": "array" + }, + "setup": { + "description": "What must exist first, for example \"a client named Acme Holding\".", + "items": { + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + "text": { + "description": "The ticket description: what the feature should do and for whom.", + "maxLength": 20000, + "type": "string" + }, + "ticket": { + "description": "The ticket id as the tracker writes it, for example SOL-123.", + "maxLength": 60, + "minLength": 1, + "type": "string" + }, + "title": { + "description": "The ticket title, exactly as the tracker shows it.", + "maxLength": 200, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "title" + ], "type": "object" }, "name": "plan_feature"
--- pinned/plan_tests +++ observed/plan_tests { "description": "Send a ticket, get the rules its tests must meet; send your tests as `proposal` to have them held to those rules.", "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "acceptanceCriteria": { + "description": "One criterion per entry, as the ticket states them.", + "items": { + "maxLength": 400, + "minLength": 1, + "type": "string" + }, + "maxItems": 30, + "type": "array" + }, + "changed": { + "description": "The branch, and what you changed in a sentence or two.", + "properties": { + "branch": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "summary": { + "maxLength": 2000, + "minLength": 1, + "type": "string" + } + }, + "type": "object" + }, + "outOfScope": { + "description": "What this ticket deliberately does not do.", + "items": { + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + "pages": { + "description": "The pages this feature adds or changes.", + "items": { + "properties": { + "purpose": { + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "reachedBy": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "route": { + "maxLength": 300, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "route" + ], + "type": "object" + }, + "maxItems": 40, + "type": "array" + }, + "proposal": { + "description": "Your tests, once plan_tests has told you the rules. Leave out on the first call.", + "properties": { + "access": { + "description": "permissions: who may see or do what once inside. sign_in: signing in and out itself. none: neither.", + "enum": [ + "permissions", + "sign_in", + "none" + ], + "type": "string" + }, + "area": { + "anyOf": [ + { + "maxLength": 80, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The area of the app this feature lives in, as its sidebar names it (Companies): one Tada already knows when it fits." + }, + "journeys": { + "items": { + "properties": { + "as": { + "anyOf": [ + { + "maxLength": 40, + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The role it runs as, as the app names it; null for the default login." + }, + "covers": { + "default": [], + "description": "The numbers of the acceptance criteria this journey proves. Empty when none were given.", + "items": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "maxItems": 30, + "type": "array" + }, + "kind": { + "enum": [ + "setup", + "invite", + "can", + "cant", + "revoke", + "direct_url", + "check" + ], + "type": "string" + }, + "name": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "needs": { + "default": [], + "description": "Other journeys of this plan, by name, that must pass before this one can run, for example \"Access taken away\" needs \"Admin creates a viewer role\". Empty when it stands alone.", + "items": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "maxItems": 10, + "type": "array" + }, + "says": { + "description": "What should happen and why, said before it is shown.", + "maxLength": 400, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "name", + "as", + "kind", + "says" + ], + "type": "object" + }, + "maxItems": 24, + "minItems": 1, + "type": "array" + }, + "name": { + "description": "The feature, named exactly as the ticket names it.", + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "notBrowserCheckable": { + "description": "What the ticket asks for that a browser cannot check, each with the criteria numbers it accounts for.", + "items": { + "properties": { + "covers": { + "default": [], + "items": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "maxItems": 30, + "type": "array" + }, + "what": { + "maxLength": 300, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "what" + ], + "type": "object" + }, + "maxItems": 10, + "type": "array" + }, + "ticketRef": { + "anyOf": [ + { + "maxLength": 60, + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The ticket id as the tracker writes it (SOL-123), or null." + }, + "uses": { + "description": "Other features this proof needs to exist first, by name.", + "items": { + "maxLength": 200, + "type": "string" + }, + "maxItems": 10, + "type": "array" + } + }, + "required": [ + "name", + "ticketRef", + "access", + "journeys", + "notBrowserCheckable", + "uses" + ], + "type": "object" + }, + "repo": { + "description": "The app, as \"owner/name\". Left out, the workspace's only app.", + "maxLength": 200, + "type": "string" + }, + "roles": { + "description": "Who uses it, and what each may and may not do.", + "items": { + "properties": { + "can": { + "default": [], + "items": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "maxItems": 10, + "type": "array" + }, + "cannot": { + "default": [], + "items": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "maxItems": 10, + "type": "array" + }, + "name": { + "maxLength": 40, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "maxItems": 12, + "type": "array" + }, + "setup": { + "description": "What must exist first, for example \"a client named Acme Holding\".", + "items": { + "maxLength": 300, + "minLength": 1, + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + "text": { + "description": "The ticket description: what the feature should do and for whom.", + "maxLength": 20000, + "type": "string" + }, + "ticket": { + "description": "The ticket id as the tracker writes it, for example SOL-123.", + "maxLength": 60, + "minLength": 1, + "type": "string" + }, + "title": { + "description": "The ticket title, exactly as the tracker shows it.", + "maxLength": 200, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "title" + ], "type": "object" }, "name": "plan_tests"
--- pinned/read_report +++ observed/read_report { "description": "Read a check by its id: waiting, going, or its verdict and report. Read only.", "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "id": { + "description": "The check's id, as create_test or check_test gave it.", + "maxLength": 80, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "id" + ], "type": "object" }, "name": "read_report"
--- pinned/record_demo +++ observed/record_demo { "description": "Old name for create_test; use create_test.", "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "address": { + "description": "With `goal`: where to check, when it is not the app's usual address.", + "format": "uri", + "type": "string" + }, + "device": { + "description": "'desktop' (default), 'mobile' or 'tablet'.", + "enum": [ + "desktop", + "mobile", + "tablet" + ], + "type": "string" + }, + "feature": { + "description": "A word for what this test is about, kept as a TAG on the test (tests are grouped by tags, not by feature). Check the group later with check_test and its tags. With `tests`, the name plan_tests returned.", + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "goal": { + "description": "What should work, in one or two sentences, with the result in it: \"A new customer can sign up and lands on their account page.\" Tada writes the test and checks it. Send the same sentence again after a fix: Tada keeps to the test it wrote for it.", + "maxLength": 1000, + "minLength": 1, + "type": "string" + }, + "hideSelectors": { + "description": "CSS selectors of the app's own floating widgets to hide for the whole check: a chat bubble, a cookie banner. A step that touches one fails.", + "items": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + "initScript": { + "description": "JavaScript run in every page before the app's own scripts, to seed state the app reads at boot, such as a locale.", + "maxLength": 20000, + "type": "string" + }, + "repo": { + "description": "The app, as \"owner/name\". Left out, the workspace's only app. Steps need no app.", + "maxLength": 200, + "type": "string" + }, + "role": { + "description": "With `goal`: who is signed in, in the app's own word (\"admin\"). about_app lists the test accounts.", + "maxLength": 40, + "minLength": 1, + "type": "string" + }, + "signIn": { + "description": "Ask Tada for a test account in a role it does not have, before the check (create_test_account does the same). Sent alone, nothing is checked.", + "properties": { + "email": { + "description": "Only when a sign-in was already made for Tada in the app: its address. Tada adopts it.", + "format": "email", + "maxLength": 254, + "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$", + "type": "string" + }, + "environment": { + "maxLength": 120, + "minLength": 1, + "type": "string" + }, + "password": { + "description": "The password of the sign-in named by `email`. Tada seals it and never hands it back.", + "maxLength": 256, + "minLength": 1, + "type": "string" + }, + "role": { + "description": "The role, in the app's own word: \"admin\", \"client\", \"staff\".", + "maxLength": 60, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "role" + ], + "type": "object" + }, + "steps": { + "description": "The steps of one test, in order: open the app with a `goto` first, find things by the words a person reads, and after each action that matters add an `expect` (`text`, `urlIncludes` or `selector`) for what the screen then shows. A test with no expect proves nothing. Sign in with {{secret.TADA_TEST_EMAIL}} and {{secret.TADA_TEST_PASSWORD}}, or `role` on a step.", + "items": { + "additionalProperties": {}, + "properties": { + "action": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action" + ], + "type": "object" + }, + "maxItems": 200, + "minItems": 1, + "type": "array" + }, + "tests": { + "description": "Every test of a feature planned with plan_tests, named as the plan names them, each with its steps. Send with `feature`.", + "items": { + "properties": { + "flowFile": { + "maxLength": 50000, + "minLength": 1, + "type": "string" + }, + "name": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "steps": { + "items": { + "additionalProperties": {}, + "properties": { + "action": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action" + ], + "type": "object" + }, + "maxItems": 200, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "name", + "steps" + ], + "type": "object" + }, + "maxItems": 24, + "minItems": 1, + "type": "array" + }, + "ticket": { + "description": "The ticket id as the tracker writes it, for example SOL-123. Kept as the test's reference.", + "maxLength": 60, + "minLength": 1, + "type": "string" + }, + "title": { + "description": "With `steps`: what the test is called.", + "maxLength": 200, + "minLength": 1, + "type": "string" + } + }, "type": "object" }, "name": "record_demo"
--- pinned/setup_test_account +++ observed/setup_test_account { "description": "Old name for create_test_account; use create_test_account.", "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "email": { + "description": "Only with `role`, and only when you ALREADY seeded a sign-in for Tada in the app: its address. Tada adopts it.", + "format": "email", + "maxLength": 254, + "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$", + "type": "string" + }, + "environment": { + "description": "Only with `role`: which environment, by name. Leave it out unless Tada asks which one.", + "maxLength": 120, + "minLength": 1, + "type": "string" + }, + "password": { + "description": "The password of the sign-in named by `email`. Tada seals it and never hands it back.", + "maxLength": 256, + "minLength": 1, + "type": "string" + }, + "repo": { + "description": "The app, as \"owner/name\". Left out, the workspace's only app.", + "maxLength": 200, + "type": "string" + }, + "role": { + "description": "A test account in this role, in the app's own word: \"admin\", \"client\", \"staff\". Left out, the app's one test account.", + "maxLength": 60, + "minLength": 1, + "type": "string" + } + }, "type": "object" }, "name": "setup_test_account"
--- pinned/tada_connect_status +++ observed/tada_connect_status { "description": "Old name for connect_status; use connect_status.", "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": {}, "type": "object" }, "name": "tada_connect_status"
--- pinned/verify_feature +++ observed/verify_feature { "description": "Old name for create_test; use create_test.", "inputSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "address": { + "description": "With `goal`: where to check, when it is not the app's usual address.", + "format": "uri", + "type": "string" + }, + "device": { + "description": "'desktop' (default), 'mobile' or 'tablet'.", + "enum": [ + "desktop", + "mobile", + "tablet" + ], + "type": "string" + }, + "feature": { + "description": "A word for what this test is about, kept as a TAG on the test (tests are grouped by tags, not by feature). Check the group later with check_test and its tags. With `tests`, the name plan_tests returned.", + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "goal": { + "description": "What should work, in one or two sentences, with the result in it: \"A new customer can sign up and lands on their account page.\" Tada writes the test and checks it. Send the same sentence again after a fix: Tada keeps to the test it wrote for it.", + "maxLength": 1000, + "minLength": 1, + "type": "string" + }, + "hideSelectors": { + "description": "CSS selectors of the app's own floating widgets to hide for the whole check: a chat bubble, a cookie banner. A step that touches one fails.", + "items": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + "initScript": { + "description": "JavaScript run in every page before the app's own scripts, to seed state the app reads at boot, such as a locale.", + "maxLength": 20000, + "type": "string" + }, + "repo": { + "description": "The app, as \"owner/name\". Left out, the workspace's only app. Steps need no app.", + "maxLength": 200, + "type": "string" + }, + "role": { + "description": "With `goal`: who is signed in, in the app's own word (\"admin\"). about_app lists the test accounts.", + "maxLength": 40, + "minLength": 1, + "type": "string" + }, + "signIn": { + "description": "Ask Tada for a test account in a role it does not have, before the check (create_test_account does the same). Sent alone, nothing is checked.", + "properties": { + "email": { + "description": "Only when a sign-in was already made for Tada in the app: its address. Tada adopts it.", + "format": "email", + "maxLength": 254, + "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$", + "type": "string" + }, + "environment": { + "maxLength": 120, + "minLength": 1, + "type": "string" + }, + "password": { + "description": "The password of the sign-in named by `email`. Tada seals it and never hands it back.", + "maxLength": 256, + "minLength": 1, + "type": "string" + }, + "role": { + "description": "The role, in the app's own word: \"admin\", \"client\", \"staff\".", + "maxLength": 60, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "role" + ], + "type": "object" + }, + "steps": { + "description": "The steps of one test, in order: open the app with a `goto` first, find things by the words a person reads, and after each action that matters add an `expect` (`text`, `urlIncludes` or `selector`) for what the screen then shows. A test with no expect proves nothing. Sign in with {{secret.TADA_TEST_EMAIL}} and {{secret.TADA_TEST_PASSWORD}}, or `role` on a step.", + "items": { + "additionalProperties": {}, + "properties": { + "action": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action" + ], + "type": "object" + }, + "maxItems": 200, + "minItems": 1, + "type": "array" + }, + "tests": { + "description": "Every test of a feature planned with plan_tests, named as the plan names them, each with its steps. Send with `feature`.", + "items": { + "properties": { + "flowFile": { + "maxLength": 50000, + "minLength": 1, + "type": "string" + }, + "name": { + "maxLength": 200, + "minLength": 1, + "type": "string" + }, + "steps": { + "items": { + "additionalProperties": {}, + "properties": { + "action": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "action" + ], + "type": "object" + }, + "maxItems": 200, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "name", + "steps" + ], + "type": "object" + }, + "maxItems": 24, + "minItems": 1, + "type": "array" + }, + "ticket": { + "description": "The ticket id as the tracker writes it, for example SOL-123. Kept as the test's reference.", + "maxLength": 60, + "minLength": 1, + "type": "string" + }, + "title": { + "description": "With `steps`: what the test is called.", + "maxLength": 200, + "minLength": 1, + "type": "string" + } + }, "type": "object" }, "name": "verify_feature"
Changed 2026-10-13 14:08 UTC
8 descriptions rewritten, 6 tools added.
--- pinned/about_app +++ observed/about_app { - "description": "Read what Tada knows about an app before you check it: its features and their tests, its cast (the roles Tada can sign in as), the map of its screens, and what is still to connect.\nCall it first. It is free and changes nothing.\nLeave `repo` out and Tada reads the app from the git remote of this folder.", - "execution": { - "taskSupport": "forbidden" - }, + "description": "Read what Tada knows about an app before you create a test for it: a test account that cannot sign in (first, with its fix), its features and tests, its test accounts, its screens, and what is still to connect. Free; changes nothing.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "repo": { - "description": "The app, as \"owner/name\". Left out, Tada reads it from the git remote of this folder.", - "type": "string" - } - }, "type": "object" }, "name": "about_app"
--- pinned/check_feature +++ observed/check_feature { - "description": "Check a feature of the app in a real browser on Tada's side and get a report: the result, what failed, what was not covered, and a link to the report in the app. Use it whenever you are asked to check, test, verify or prove that something works.\nSay what should work in `goal`, in a sentence or two, with the app in `repo` and a `role` when it matters who is signed in, and Tada writes the test. Or send the steps of one test in `steps`, or several tests of one feature in `tests`. Nothing is driven on this computer, and an app on localhost is reached through a temporary tunnel.\nUntil it passes, each go is a try: free for the first few, not a report, and the answer says which try it was and how many free ones are left. The try that passes is the check: Tada keeps its test under Tests, counts it, and the answer links the report and the test.\nSend `feature` alone to check again what Tada already keeps, which is counted. Send `signIn` to ask Tada for a sign-in in a role its cast does not have.\nNever type a real password into a step: use the placeholders.", - "execution": { - "taskSupport": "forbidden" - }, + "description": "Old name for create_test; use create_test.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "address": { - "description": "With `goal`: where to check, when it is not the app's usual address. An address on this machine (localhost) is reached through a temporary tunnel.", - "format": "uri", - "type": "string" - }, - "device": { - "description": "Viewport preset: 'desktop' (default), 'mobile', or 'tablet'. Use 'mobile' only for mobile-first features.", - "enum": [ - "desktop", - "mobile", - "tablet" - ], - "type": "string" - }, - "feature": { - "description": "The feature this proves, named exactly as the ticket (for example \"Users, invitations and permissions\"). Omit it and the title names the feature.", - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "goal": { - "description": "What should work, in one or two sentences, with the result in it: \"A new customer can sign up and lands on their account page.\" Tada writes the test and checks it. Needs an app Tada knows (`repo`). Send the same sentence again after a fix: Tada keeps to the test it wrote for it. Leave out when sending `steps`.", - "maxLength": 240, - "minLength": 1, - "type": "string" - }, - "hideSelectors": { - "description": "CSS selectors for the app's own floating widgets to hide for the whole film: a chat bubble, a feedback tab, a cookie banner. Nothing is hidden unless you pass this. Each match, and everything inside it, is removed with display:none on every page of the check. A step that clicks, types into, hovers, drags, scrolls to or waits for anything inside a hidden element FAILS the check and names the selector, so hide only what the test never touches. Match the widget's container in the page itself: a selector cannot reach inside the widget's iframe or shadow root. Common containers (check them against your app, vendors change their markup): Intercom .intercom-app, .intercom-lightweight-app; HubSpot chat #hubspot-messages-iframe-container; Zendesk messaging iframe[title*=\"Zendesk\"], iframe[aria-label*=\"Zendesk\"]; Crisp #crisp-chatbox; Sentry user feedback #sentry-feedback; Userback .userback-button-container; Hotjar [class^=\"_hj\"]; OneTrust cookie banner #onetrust-consent-sdk; Cookiebot cookie banner #CybotCookiebotDialog.", - "items": { - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "maxItems": 20, - "type": "array" - }, - "initScript": { - "description": "JavaScript run in every page BEFORE the app's own scripts, to seed state the app reads at boot. Use it to force a locale or a feature flag, e.g. \"localStorage.setItem('lang','en')\" for an app that would otherwise render in another language.", - "type": "string" - }, - "repo": { - "description": "The app this proves, as \"owner/name\". Left out, Tada reads it from the git remote of the folder this server was started in, which is wrong whenever that is not the app.", - "type": "string" - }, - "role": { - "description": "With `goal`: the role to sign in as, in the app's own word, such as \"admin\" or \"client\". about_app lists the cast.", - "maxLength": 40, - "minLength": 1, - "type": "string" - }, - "room": { - "additionalProperties": false, - "description": "Save this as a test with several people from the app's cast, filmed like a video call (everyone joins and says hello, the lead reads the agenda, then each does their part). Every step needs a role. It is saved on the app and checked now. (The parameter keeps the name `room` so existing agent setups work.)", - "properties": { - "about": { - "description": "One line: what the test proves.", - "maxLength": 300, - "type": "string" - }, - "agenda": { - "description": "The chapters, in the order they happen.", - "items": { - "additionalProperties": false, - "properties": { - "from": { - "description": "The index (from 0) of the step the chapter starts on. That step must be this role's.", - "maximum": 199, - "minimum": 0, - "type": "integer" - }, - "line": { - "description": "What this person says as the chapter opens.", - "maxLength": 400, - "type": "string" - }, - "role": { - "description": "Whose part it is.", - "maxLength": 40, - "minLength": 1, - "type": "string" - }, - "title": { - "description": "The chapter, like \"Grace invites Ada as a client\".", - "maxLength": 120, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "title", - "role", - "from" - ], - "type": "object" - }, - "maxItems": 20, - "type": "array" - }, - "agendaLine": { - "description": "What the lead says once everyone is in. Defaults to the chapters read as a plan.", - "maxLength": 400, - "type": "string" - }, - "cast": { - "description": "Everyone in the cast, in the order they join; the admin always goes in first. Every role in the steps must be here; a role with no steps joins, says hello and stays.", - "items": { - "additionalProperties": false, - "properties": { - "character": { - "description": "One of the twelve cast characters: pip, juno, bea, moss, fern, tide, otto, nova, iris, lumi, ruby, sol. Left out, one nobody else has.", - "maxLength": 20, - "type": "string" - }, - "greeting": { - "description": "What they say as they join, like \"Hey, hi. I'm Grace, I run the team.\"", - "maxLength": 400, - "type": "string" - }, - "name": { - "description": "The person's name on screen, like \"Grace Hale\".", - "maxLength": 80, - "type": "string" - }, - "role": { - "description": "The role, exactly as the steps write it (step.role), like \"owner\".", - "maxLength": 40, - "minLength": 1, - "type": "string" - }, - "title": { - "description": "Their job, like \"Admin\".", - "maxLength": 60, - "type": "string" - }, - "voice": { - "anyOf": [ - { - "enum": [ - "brian", - "daniel", - "bella", - "lily" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "One of brian, daniel, bella, lily. Left out, they speak in the voice of whoever runs the test." - } - }, - "required": [ - "role" - ], - "type": "object" - }, - "maxItems": 6, - "minItems": 2, - "type": "array" - }, - "closingLine": { - "description": "What the lead says once everyone is done. Absent: no closing.", - "maxLength": 400, - "type": "string" - }, - "lead": { - "description": "The role that holds the script: it reads the agenda once everyone is in.", - "maxLength": 40, - "minLength": 1, - "type": "string" - }, - "name": { - "description": "What the test is called. Defaults to the title.", - "maxLength": 120, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "cast", - "lead" - ], - "type": "object" - }, - "signIn": { - "additionalProperties": false, - "description": "Ask Tada for a sign-in in a role it does not have. about_app lists the roles it has: asking for one of those returns the same person. Sent alone, nothing is tried.", - "properties": { - "email": { - "description": "Only when a sign-in was already made for Tada in the app: its address. Tada adopts it instead of making one.", - "type": "string" - }, - "environment": { - "description": "Which environment, by name, such as \"staging\". Leave it out unless Tada asks which one.", - "type": "string" - }, - "password": { - "description": "The password of the sign-in named by `email`. Tada seals it and never hands it back.", - "type": "string" - }, - "role": { - "description": "The role the app gives this person, in the app's own word: \"admin\", \"client\", \"staff\".", - "maxLength": 40, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "role" - ], - "type": "object" - }, - "steps": { - "description": "The steps of one test, in order. Leave out when sending `tests`, or when checking a feature again by name.", - "items": { - "additionalProperties": false, - "properties": { - "action": { - "enum": [ - "goto", - "click", - "fill", - "select", - "press", - "drag", - "upload", - "createInbox", - "awaitEmail", - "signUp", - "signal", - "await", - "waitForText", - "expect", - "scroll", - "hover", - "wait", - "caption", - "pause", - "resume" - ], - "type": "string" - }, - "key": { - "description": "press: key e.g. 'Enter'", - "type": "string" - }, - "link": { - "description": "awaitEmail: which link in the email to open as ${magicLink}: words on its button or link (\"Review and sign\"), or part of its address (\"/document/sign\"). Leave it out and Tada takes the sign-in, invitation or signing link itself. When set and no link matches, the step fails.", - "type": "string" - }, - "ms": { - "description": "wait/caption/scroll: milliseconds (scroll: animation duration); expect: max ms to wait for the condition (default 8000); awaitEmail: max ms to wait for the email (default 20000)", - "type": "number" - }, - "native": { - "description": "drag: force the synthetic HTML5 drag-and-drop path (real dragstart/dragover/drop events) for native `draggable=\"true\"` UIs. Auto-detected when the source is draggable; set true only if auto-detect misses. Leave unset for pointer-based libraries (dnd-kit, react-beautiful-dnd).", - "type": "boolean" - }, - "role": { - "description": "Who does this step, by the role the app gives them (admin, client, staff). Steps sharing a role are one person in their own browser, signed in with that role's sign-in. Every step naming one role: the check signs in as that person. Leave it off every step for a one-person test.", - "maxLength": 40, - "minLength": 1, - "type": "string" - }, - "selector": { - "description": "CSS selector for click/fill/hover/expect; select: combobox trigger; scroll: element to scroll to; drag: the source element to pick up; upload: the file field, or the control that opens the file picker", - "type": "string" - }, - "signedIn": { - "description": "expect: assert this page is past the sign-in: its address is not a sign-in address and no password box is showing. Use after opening a page only a signed-in person can see", - "type": "boolean" - }, - "text": { - "description": "click/hover/waitForText/expect: visible text to match (prefer over brittle CSS selectors); fill: label text; select: combobox label; caption: banner text; awaitEmail: subject substring to filter on; drag: visible text of the source to pick up; upload: the label of the file field, or the words on the button or drop area that takes the file", - "type": "string" - }, - "toSelector": { - "description": "drag: CSS selector of the drop target to drag the source ONTO", - "type": "string" - }, - "toText": { - "description": "drag: visible text of the drop target to drag the source ONTO", - "type": "string" - }, - "url": { - "description": "goto: URL to navigate to. May use ${magicLink} to open the link from the last awaitEmail.", - "type": "string" - }, - "urlIncludes": { - "description": "expect: assert the current URL contains this substring (e.g. \"/library\" to confirm a sign-in worked)", - "type": "string" - }, - "value": { - "description": "fill: text to type (use ${inbox} for the disposable inbox address); select: the option to pick from a dropdown/combobox; scroll: 'top' | 'bottom' | pixel offset; upload: the sample file to attach, by name, one of invoice.pdf, picture.png, photo.jpg, contacts.csv, notes.txt and letter.docx. Tada brings these files itself: a path or an address is refused", - "type": "string" - } - }, - "required": [ - "action" - ], - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "tests": { - "description": "Several tests of one feature Tada already knows, each with a name and its own steps. Send with `feature`. For one test, or a feature Tada does not know, send `steps`.", - "items": { - "additionalProperties": false, - "properties": { - "name": { - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "steps": { - "items": { - "$ref": "#/properties/steps/items" - }, - "minItems": 1, - "type": "array" - } - }, - "required": [ - "name", - "steps" - ], - "type": "object" - }, - "maxItems": 24, - "minItems": 1, - "type": "array" - }, - "ticket": { - "description": "The ticket id exactly as the tracker writes it, for example SOL-123. Lets Tada match the pull request that ships this feature.", - "maxLength": 60, - "minLength": 1, - "type": "string" - }, - "title": { - "description": "What this check is called, with `steps`. It names the feature when `feature` is left out. Not needed with `goal`.", - "maxLength": 200, - "minLength": 1, - "type": "string" - } - }, "type": "object" }, "name": "check_feature"
--- pinned/check_result +++ observed/check_result { - "description": "Read a check by its id: waiting, going, or its report (the result, what failed, what was not covered, and the links).\nUse it when check_feature stopped waiting before its check finished and gave you an id. Read only: it starts nothing and is not counted.", - "execution": { - "taskSupport": "forbidden" - }, + "description": "Old name for read_report; use read_report.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "id": { - "description": "The check's id, as verify_feature gave it.", - "type": "string" - } - }, - "required": [ - "id" - ], "type": "object" }, "name": "check_result"
+ tool added: check_test
+ tool added: connect_status
+ tool added: create_test
+ tool added: create_test_account
--- pinned/plan_feature +++ observed/plan_feature { - "description": "Old name, kept so existing setups keep working. It does what it always did. For new work use about_app.", - "execution": { - "taskSupport": "forbidden" - }, + "description": "Old name for plan_tests; use plan_tests.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "acceptanceCriteria": { - "description": "One criterion per entry, as the ticket states them.", - "items": { - "maxLength": 400, - "minLength": 1, - "type": "string" - }, - "maxItems": 30, - "type": "array" - }, - "changed": { - "additionalProperties": false, - "description": "The branch, and what you changed in a sentence or two.", - "properties": { - "branch": { - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "summary": { - "maxLength": 2000, - "minLength": 1, - "type": "string" - } - }, - "type": "object" - }, - "outOfScope": { - "description": "What this ticket deliberately does not do.", - "items": { - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "maxItems": 20, - "type": "array" - }, - "pages": { - "description": "The pages this feature adds or changes.", - "items": { - "additionalProperties": false, - "properties": { - "purpose": { - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "reachedBy": { - "description": "How a person gets there by clicking, for example \"Settings › Roles\".", - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "route": { - "description": "The path, for example /settings/roles.", - "maxLength": 300, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "route" - ], - "type": "object" - }, - "maxItems": 40, - "type": "array" - }, - "proposal": { - "additionalProperties": false, - "description": "Your proof, once plan_feature has told you the rules. Leave out on the first call.", - "properties": { - "access": { - "description": "permissions: who may see or do what once inside. sign_in: signing in and out. none: neither.", - "enum": [ - "permissions", - "sign_in", - "none" - ], - "type": "string" - }, - "area": { - "anyOf": [ - { - "maxLength": 80, - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "description": "The area of the app it lives in, as its sidebar names it (Companies). Pick one Tada already knows when it fits." - }, - "journeys": { - "items": { - "additionalProperties": false, - "properties": { - "as": { - "anyOf": [ - { - "maxLength": 40, - "type": "string" - }, - { - "type": "null" - } - ], - "description": "The role who does it; null for the app's default sign-in." - }, - "covers": { - "default": [], - "description": "The acceptance criteria it proves, by number.", - "items": { - "type": "integer" - }, - "maxItems": 30, - "type": "array" - }, - "kind": { - "enum": [ - "setup", - "invite", - "can", - "cant", - "revoke", - "direct_url", - "check" - ], - "type": "string" - }, - "name": { - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "needs": { - "default": [], - "description": "Tests of this feature that must pass first, by name.", - "items": { - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "maxItems": 10, - "type": "array" - }, - "says": { - "description": "What should happen and why.", - "maxLength": 400, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "name", - "as", - "kind", - "says" - ], - "type": "object" - }, - "maxItems": 24, - "minItems": 1, - "type": "array" - }, - "name": { - "description": "The feature, named exactly as the ticket names it.", - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "notBrowserCheckable": { - "items": { - "additionalProperties": false, - "properties": { - "covers": { - "default": [], - "items": { - "type": "integer" - }, - "maxItems": 30, - "type": "array" - }, - "what": { - "maxLength": 300, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "what" - ], - "type": "object" - }, - "maxItems": 10, - "type": "array" - }, - "ticketRef": { - "anyOf": [ - { - "maxLength": 60, - "type": "string" - }, - { - "type": "null" - } - ] - }, - "uses": { - "description": "Other features this feature needs first, by name.", - "items": { - "maxLength": 200, - "type": "string" - }, - "maxItems": 10, - "type": "array" - } - }, - "required": [ - "name", - "ticketRef", - "access", - "journeys", - "notBrowserCheckable", - "uses" - ], - "type": "object" - }, - "roles": { - "description": "Who uses it, and what each may and may not do.", - "items": { - "additionalProperties": false, - "properties": { - "can": { - "default": [], - "items": { - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "maxItems": 10, - "type": "array" - }, - "cannot": { - "default": [], - "items": { - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "maxItems": 10, - "type": "array" - }, - "name": { - "maxLength": 40, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "maxItems": 12, - "type": "array" - }, - "setup": { - "description": "What must exist first, for example \"a client named Acme Holding\".", - "items": { - "maxLength": 300, - "minLength": 1, - "type": "string" - }, - "maxItems": 20, - "type": "array" - }, - "text": { - "description": "The ticket description: what the feature should do and for whom.", - "maxLength": 20000, - "type": "string" - }, - "ticket": { - "description": "The ticket id as the tracker writes it, for example SOL-123.", - "maxLength": 60, - "minLength": 1, - "type": "string" - }, - "title": { - "description": "The ticket title, exactly as the tracker shows it.", - "maxLength": 200, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "title" - ], "type": "object" }, "name": "plan_feature"
+ tool added: plan_tests
+ tool added: read_report
--- pinned/record_demo +++ observed/record_demo { - "description": "Old name, kept so existing setups keep working. It does what it always did. For new work use check_feature.", - "execution": { - "taskSupport": "forbidden" - }, + "description": "Old name for create_test; use create_test.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "address": { - "description": "With `goal`: where to check, when it is not the app's usual address. An address on this machine (localhost) is reached through a temporary tunnel.", - "format": "uri", - "type": "string" - }, - "captureScale": { - "description": "Shoot at 2 ONLY when this film will be re-cut as marketing footage. A cut of 1x footage is capped at a 1.2x push-in forever, because the pixels for a closer look were never captured; 2 lifts that to 2.2x. It is a decision made at filming that cannot be taken later, and it costs four times the frame pixels, so leave it off for an ordinary check.", - "enum": [ - 1, - 2 - ], - "type": "number" - }, - "device": { - "description": "Viewport preset: 'desktop' (default), 'mobile', or 'tablet'. Use 'mobile' only for mobile-first features.", - "enum": [ - "desktop", - "mobile", - "tablet" - ], - "type": "string" - }, - "dryRun": { - "default": false, - "description": "DEPRECATED AND IGNORED. This does NOT keep the film local and does NOT skip the upload: every check happens on Tada and a pass is published to a public link. Filming on this machine no longer exists. If the app being checked must not be published, do not check it here.", - "type": "boolean" - }, - "feature": { - "description": "The feature this proves, named exactly as the ticket (for example \"Users, invitations and permissions\"). Omit it and the title names the feature.", - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "goal": { - "description": "What should work, in one or two sentences, with the result in it: \"A new customer can sign up and lands on their account page.\" Tada writes the test and checks it. Needs an app Tada knows (`repo`). Send the same sentence again after a fix: Tada keeps to the test it wrote for it. Leave out when sending `steps`.", - "maxLength": 240, - "minLength": 1, - "type": "string" - }, - "height": { - "description": "NOT SUPPORTED, and passing it refuses the check. Use `device`.", - "exclusiveMinimum": 0, - "type": "integer" - }, - "hideSelectors": { - "description": "CSS selectors for the app's own floating widgets to hide for the whole film: a chat bubble, a feedback tab, a cookie banner. Nothing is hidden unless you pass this. Each match, and everything inside it, is removed with display:none on every page of the check. A step that clicks, types into, hovers, drags, scrolls to or waits for anything inside a hidden element FAILS the check and names the selector, so hide only what the test never touches. Match the widget's container in the page itself: a selector cannot reach inside the widget's iframe or shadow root. Common containers (check them against your app, vendors change their markup): Intercom .intercom-app, .intercom-lightweight-app; HubSpot chat #hubspot-messages-iframe-container; Zendesk messaging iframe[title*=\"Zendesk\"], iframe[aria-label*=\"Zendesk\"]; Crisp #crisp-chatbox; Sentry user feedback #sentry-feedback; Userback .userback-button-container; Hotjar [class^=\"_hj\"]; OneTrust cookie banner #onetrust-consent-sdk; Cookiebot cookie banner #CybotCookiebotDialog.", - "items": { - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "maxItems": 20, - "type": "array" - }, - "initScript": { - "description": "JavaScript run in every page BEFORE the app's own scripts, to seed state the app reads at boot. Use it to force a locale or a feature flag, e.g. \"localStorage.setItem('lang','en')\" for an app that would otherwise render in another language.", - "type": "string" - }, - "journeys": { - "description": "For a feature planned with plan_feature: every test of the confirmed plan, named exactly as the plan names it, each with its steps. Send with `feature` set to the id plan_feature returned. The parameter keeps the name `journeys` so existing agent setups work; each entry is one test.", - "items": { - "additionalProperties": false, - "properties": { - "name": { - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "steps": { - "items": { - "$ref": "#/properties/steps/items" - }, - "minItems": 1, - "type": "array" - } - }, - "required": [ - "name", - "steps" - ], - "type": "object" - }, - "maxItems": 24, - "minItems": 1, - "type": "array" - }, - "repo": { - "description": "The app this proves, as \"owner/name\". Left out, Tada reads it from the git remote of the folder this server was started in, which is wrong whenever that is not the app.", - "type": "string" - }, - "role": { - "description": "With `goal`: the role to sign in as, in the app's own word, such as \"admin\" or \"client\". about_app lists the cast.", - "maxLength": 40, - "minLength": 1, - "type": "string" - }, - "room": { - "additionalProperties": false, - "description": "Save this as a test with several people from the app's cast, filmed like a video call (everyone joins and says hello, the lead reads the agenda, then each does their part). Every step needs a role. It is saved on the app and checked now. (The parameter keeps the name `room` so existing agent setups work.)", - "properties": { - "about": { - "description": "One line: what the test proves.", - "maxLength": 300, - "type": "string" - }, - "agenda": { - "description": "The chapters, in the order they happen.", - "items": { - "additionalProperties": false, - "properties": { - "from": { - "description": "The index (from 0) of the step the chapter starts on. That step must be this role's.", - "maximum": 199, - "minimum": 0, - "type": "integer" - }, - "line": { - "description": "What this person says as the chapter opens.", - "maxLength": 400, - "type": "string" - }, - "role": { - "description": "Whose part it is.", - "maxLength": 40, - "minLength": 1, - "type": "string" - }, - "title": { - "description": "The chapter, like \"Grace invites Ada as a client\".", - "maxLength": 120, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "title", - "role", - "from" - ], - "type": "object" - }, - "maxItems": 20, - "type": "array" - }, - "agendaLine": { - "description": "What the lead says once everyone is in. Defaults to the chapters read as a plan.", - "maxLength": 400, - "type": "string" - }, - "cast": { - "description": "Everyone in the cast, in the order they join; the admin always goes in first. Every role in the steps must be here; a role with no steps joins, says hello and stays.", - "items": { - "additionalProperties": false, - "properties": { - "character": { - "description": "One of the twelve cast characters: pip, juno, bea, moss, fern, tide, otto, nova, iris, lumi, ruby, sol. Left out, one nobody else has.", - "maxLength": 20, - "type": "string" - }, - "greeting": { - "description": "What they say as they join, like \"Hey, hi. I'm Grace, I run the team.\"", - "maxLength": 400, - "type": "string" - }, - "name": { - "description": "The person's name on screen, like \"Grace Hale\".", - "maxLength": 80, - "type": "string" - }, - "role": { - "description": "The role, exactly as the steps write it (step.role), like \"owner\".", - "maxLength": 40, - "minLength": 1, - "type": "string" - }, - "title": { - "description": "Their job, like \"Admin\".", - "maxLength": 60, - "type": "string" - }, - "voice": { - "anyOf": [ - { - "enum": [ - "brian", - "daniel", - "bella", - "lily" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "One of brian, daniel, bella, lily. Left out, they speak in the voice of whoever runs the test." - } - }, - "required": [ - "role" - ], - "type": "object" - }, - "maxItems": 6, - "minItems": 2, - "type": "array" - }, - "closingLine": { - "description": "What the lead says once everyone is done. Absent: no closing.", - "maxLength": 400, - "type": "string" - }, - "lead": { - "description": "The role that holds the script: it reads the agenda once everyone is in.", - "maxLength": 40, - "minLength": 1, - "type": "string" - }, - "name": { - "description": "What the test is called. Defaults to the title.", - "maxLength": 120, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "cast", - "lead" - ], - "type": "object" - }, - "steps": { - "description": "The steps of ONE test, in order. Leave out when sending `journeys` for a planned feature, or when checking a feature again by name.", - "items": { - "additionalProperties": false, - "properties": { - "action": { - "enum": [ - "goto", - "click", - "fill", - "select", - "press", - "drag", - "upload", - "createInbox", - "awaitEmail", - "signUp", - "signal", - "await", - "waitForText", - "expect", - "scroll", - "hover", - "wait", - "caption", - "pause", - "resume" - ], - "type": "string" - }, - "key": { - "description": "press: key e.g. 'Enter'", - "type": "string" - }, - "link": { - "description": "awaitEmail: which link in the email to open as ${magicLink}: words on its button or link (\"Review and sign\"), or part of its address (\"/document/sign\"). Leave it out and Tada takes the sign-in, invitation or signing link itself. When set and no link matches, the step fails.", - "type": "string" - }, - "ms": { - "description": "wait/caption/scroll: milliseconds (scroll: animation duration); expect: max ms to wait for the condition (default 8000); awaitEmail: max ms to wait for the email (default 20000)", - "type": "number" - }, - "native": { - "description": "drag: force the synthetic HTML5 drag-and-drop path (real dragstart/dragover/drop events) for native `draggable=\"true\"` UIs. Auto-detected when the source is draggable; set true only if auto-detect misses. Leave unset for pointer-based libraries (dnd-kit, react-beautiful-dnd).", - "type": "boolean" - }, - "role": { - "description": "Who does this step, by the role the app gives them (admin, client, staff). Steps sharing a role are one person in their own browser, signed in with that role's sign-in. Every step naming one role: the check signs in as that person. Leave it off every step for a one-person test.", - "maxLength": 40, - "minLength": 1, - "type": "string" - }, - "selector": { - "description": "CSS selector for click/fill/hover/expect; select: combobox trigger; scroll: element to scroll to; drag: the source element to pick up; upload: the file field, or the control that opens the file picker", - "type": "string" - }, - "signedIn": { - "description": "expect: assert this page is past the sign-in: its address is not a sign-in address and no password box is showing. Use after opening a page only a signed-in person can see", - "type": "boolean" - }, - "text": { - "description": "click/hover/waitForText/expect: visible text to match (prefer over brittle CSS selectors); fill: label text; select: combobox label; caption: banner text; awaitEmail: subject substring to filter on; drag: visible text of the source to pick up; upload: the label of the file field, or the words on the button or drop area that takes the file", - "type": "string" - }, - "toSelector": { - "description": "drag: CSS selector of the drop target to drag the source ONTO", - "type": "string" - }, - "toText": { - "description": "drag: visible text of the drop target to drag the source ONTO", - "type": "string" - }, - "url": { - "description": "goto: URL to navigate to. May use ${magicLink} to open the link from the last awaitEmail.", - "type": "string" - }, - "urlIncludes": { - "description": "expect: assert the current URL contains this substring (e.g. \"/library\" to confirm a sign-in worked)", - "type": "string" - }, - "value": { - "description": "fill: text to type (use ${inbox} for the disposable inbox address); select: the option to pick from a dropdown/combobox; scroll: 'top' | 'bottom' | pixel offset; upload: the sample file to attach, by name, one of invoice.pdf, picture.png, photo.jpg, contacts.csv, notes.txt and letter.docx. Tada brings these files itself: a path or an address is refused", - "type": "string" - } - }, - "required": [ - "action" - ], - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "storageState": { - "description": "NOT SUPPORTED, and passing it refuses the check rather than ignoring it. It names a file on YOUR machine and the check is driven by Tada's runner. To check a page behind a sign-in, sign in inside the test: see CHECK PAGES BEHIND A SIGN-IN in the tool description.", - "type": "string" - }, - "taskId": { - "description": "NOT SUPPORTED, and passing it refuses the check. Attach the film to a task from the dashboard.", - "format": "uuid", - "type": "string" - }, - "ticket": { - "description": "The ticket id exactly as the tracker writes it, for example SOL-123. Lets Tada match the pull request that ships this feature.", - "maxLength": 60, - "minLength": 1, - "type": "string" - }, - "title": { - "description": "What this check is called, with `steps`. It names the feature when `feature` is left out. Not needed with `goal`.", - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "width": { - "description": "NOT SUPPORTED, and passing it refuses the check. Use `device`.", - "exclusiveMinimum": 0, - "type": "integer" - } - }, "type": "object" }, "name": "record_demo"
--- pinned/setup_test_account +++ observed/setup_test_account { - "description": "Old name, kept so existing setups keep working. It does what it always did. For new work use check_feature with `signIn`.", - "execution": { - "taskSupport": "forbidden" - }, + "description": "Old name for create_test_account; use create_test_account.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "email": { - "description": "Only with `role`, and only when you ALREADY seeded a sign-in for Tada in the app: its address. Tada adopts it rather than making one.", - "type": "string" - }, - "environment": { - "description": "Only with `role`: which environment, by name (e.g. \"staging\"). Leave it out unless Tada asks which one.", - "type": "string" - }, - "password": { - "description": "The password of the sign-in named by `email`. Tada seals it and never hands it back.", - "type": "string" - }, - "repo": { - "description": "The app, as \"owner/name\" (e.g. \"acme/storefront\"). Must already be connected to Tada.", - "type": "string" - }, - "role": { - "description": "Ask for a cast member in this role instead of the app’s test account: the app’s own word for it, such as \"admin\", \"client\" or \"staff\".", - "type": "string" - } - }, - "required": [ - "repo" - ], "type": "object" }, "name": "setup_test_account"
--- pinned/tada_connect_status +++ observed/tada_connect_status { - "description": "Old name, kept so existing setups keep working. It does what it always did. For new work use about_app.", - "execution": { - "taskSupport": "forbidden" - }, + "description": "Old name for connect_status; use connect_status.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", - "properties": {}, "type": "object" }, "name": "tada_connect_status"
--- pinned/verify_feature +++ observed/verify_feature { - "description": "Old name, kept so existing setups keep working. It does what it always did. For new work use check_feature.", - "execution": { - "taskSupport": "forbidden" - }, + "description": "Old name for create_test; use create_test.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "address": { - "description": "With `goal`: where to check, when it is not the app's usual address. An address on this machine (localhost) is reached through a temporary tunnel.", - "format": "uri", - "type": "string" - }, - "captureScale": { - "description": "Shoot at 2 ONLY when this film will be re-cut as marketing footage. A cut of 1x footage is capped at a 1.2x push-in forever, because the pixels for a closer look were never captured; 2 lifts that to 2.2x. It is a decision made at filming that cannot be taken later, and it costs four times the frame pixels, so leave it off for an ordinary check.", - "enum": [ - 1, - 2 - ], - "type": "number" - }, - "device": { - "description": "Viewport preset: 'desktop' (default), 'mobile', or 'tablet'. Use 'mobile' only for mobile-first features.", - "enum": [ - "desktop", - "mobile", - "tablet" - ], - "type": "string" - }, - "dryRun": { - "default": false, - "description": "DEPRECATED AND IGNORED. This does NOT keep the film local and does NOT skip the upload: every check happens on Tada and a pass is published to a public link. Filming on this machine no longer exists. If the app being checked must not be published, do not check it here.", - "type": "boolean" - }, - "feature": { - "description": "The feature this proves, named exactly as the ticket (for example \"Users, invitations and permissions\"). Omit it and the title names the feature.", - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "goal": { - "description": "What should work, in one or two sentences, with the result in it: \"A new customer can sign up and lands on their account page.\" Tada writes the test and checks it. Needs an app Tada knows (`repo`). Send the same sentence again after a fix: Tada keeps to the test it wrote for it. Leave out when sending `steps`.", - "maxLength": 240, - "minLength": 1, - "type": "string" - }, - "height": { - "description": "NOT SUPPORTED, and passing it refuses the check. Use `device`.", - "exclusiveMinimum": 0, - "type": "integer" - }, - "hideSelectors": { - "description": "CSS selectors for the app's own floating widgets to hide for the whole film: a chat bubble, a feedback tab, a cookie banner. Nothing is hidden unless you pass this. Each match, and everything inside it, is removed with display:none on every page of the check. A step that clicks, types into, hovers, drags, scrolls to or waits for anything inside a hidden element FAILS the check and names the selector, so hide only what the test never touches. Match the widget's container in the page itself: a selector cannot reach inside the widget's iframe or shadow root. Common containers (check them against your app, vendors change their markup): Intercom .intercom-app, .intercom-lightweight-app; HubSpot chat #hubspot-messages-iframe-container; Zendesk messaging iframe[title*=\"Zendesk\"], iframe[aria-label*=\"Zendesk\"]; Crisp #crisp-chatbox; Sentry user feedback #sentry-feedback; Userback .userback-button-container; Hotjar [class^=\"_hj\"]; OneTrust cookie banner #onetrust-consent-sdk; Cookiebot cookie banner #CybotCookiebotDialog.", - "items": { - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "maxItems": 20, - "type": "array" - }, - "initScript": { - "description": "JavaScript run in every page BEFORE the app's own scripts, to seed state the app reads at boot. Use it to force a locale or a feature flag, e.g. \"localStorage.setItem('lang','en')\" for an app that would otherwise render in another language.", - "type": "string" - }, - "journeys": { - "description": "For a feature planned with plan_feature: every test of the confirmed plan, named exactly as the plan names it, each with its steps. Send with `feature` set to the id plan_feature returned. The parameter keeps the name `journeys` so existing agent setups work; each entry is one test.", - "items": { - "additionalProperties": false, - "properties": { - "name": { - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "steps": { - "items": { - "$ref": "#/properties/steps/items" - }, - "minItems": 1, - "type": "array" - } - }, - "required": [ - "name", - "steps" - ], - "type": "object" - }, - "maxItems": 24, - "minItems": 1, - "type": "array" - }, - "repo": { - "description": "The app this proves, as \"owner/name\". Left out, Tada reads it from the git remote of the folder this server was started in, which is wrong whenever that is not the app.", - "type": "string" - }, - "role": { - "description": "With `goal`: the role to sign in as, in the app's own word, such as \"admin\" or \"client\". about_app lists the cast.", - "maxLength": 40, - "minLength": 1, - "type": "string" - }, - "room": { - "additionalProperties": false, - "description": "Save this as a test with several people from the app's cast, filmed like a video call (everyone joins and says hello, the lead reads the agenda, then each does their part). Every step needs a role. It is saved on the app and checked now. (The parameter keeps the name `room` so existing agent setups work.)", - "properties": { - "about": { - "description": "One line: what the test proves.", - "maxLength": 300, - "type": "string" - }, - "agenda": { - "description": "The chapters, in the order they happen.", - "items": { - "additionalProperties": false, - "properties": { - "from": { - "description": "The index (from 0) of the step the chapter starts on. That step must be this role's.", - "maximum": 199, - "minimum": 0, - "type": "integer" - }, - "line": { - "description": "What this person says as the chapter opens.", - "maxLength": 400, - "type": "string" - }, - "role": { - "description": "Whose part it is.", - "maxLength": 40, - "minLength": 1, - "type": "string" - }, - "title": { - "description": "The chapter, like \"Grace invites Ada as a client\".", - "maxLength": 120, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "title", - "role", - "from" - ], - "type": "object" - }, - "maxItems": 20, - "type": "array" - }, - "agendaLine": { - "description": "What the lead says once everyone is in. Defaults to the chapters read as a plan.", - "maxLength": 400, - "type": "string" - }, - "cast": { - "description": "Everyone in the cast, in the order they join; the admin always goes in first. Every role in the steps must be here; a role with no steps joins, says hello and stays.", - "items": { - "additionalProperties": false, - "properties": { - "character": { - "description": "One of the twelve cast characters: pip, juno, bea, moss, fern, tide, otto, nova, iris, lumi, ruby, sol. Left out, one nobody else has.", - "maxLength": 20, - "type": "string" - }, - "greeting": { - "description": "What they say as they join, like \"Hey, hi. I'm Grace, I run the team.\"", - "maxLength": 400, - "type": "string" - }, - "name": { - "description": "The person's name on screen, like \"Grace Hale\".", - "maxLength": 80, - "type": "string" - }, - "role": { - "description": "The role, exactly as the steps write it (step.role), like \"owner\".", - "maxLength": 40, - "minLength": 1, - "type": "string" - }, - "title": { - "description": "Their job, like \"Admin\".", - "maxLength": 60, - "type": "string" - }, - "voice": { - "anyOf": [ - { - "enum": [ - "brian", - "daniel", - "bella", - "lily" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "One of brian, daniel, bella, lily. Left out, they speak in the voice of whoever runs the test." - } - }, - "required": [ - "role" - ], - "type": "object" - }, - "maxItems": 6, - "minItems": 2, - "type": "array" - }, - "closingLine": { - "description": "What the lead says once everyone is done. Absent: no closing.", - "maxLength": 400, - "type": "string" - }, - "lead": { - "description": "The role that holds the script: it reads the agenda once everyone is in.", - "maxLength": 40, - "minLength": 1, - "type": "string" - }, - "name": { - "description": "What the test is called. Defaults to the title.", - "maxLength": 120, - "minLength": 1, - "type": "string" - } - }, - "required": [ - "cast", - "lead" - ], - "type": "object" - }, - "steps": { - "description": "The steps of ONE test, in order. Leave out when sending `journeys` for a planned feature, or when checking a feature again by name.", - "items": { - "additionalProperties": false, - "properties": { - "action": { - "enum": [ - "goto", - "click", - "fill", - "select", - "press", - "drag", - "upload", - "createInbox", - "awaitEmail", - "signUp", - "signal", - "await", - "waitForText", - "expect", - "scroll", - "hover", - "wait", - "caption", - "pause", - "resume" - ], - "type": "string" - }, - "key": { - "description": "press: key e.g. 'Enter'", - "type": "string" - }, - "link": { - "description": "awaitEmail: which link in the email to open as ${magicLink}: words on its button or link (\"Review and sign\"), or part of its address (\"/document/sign\"). Leave it out and Tada takes the sign-in, invitation or signing link itself. When set and no link matches, the step fails.", - "type": "string" - }, - "ms": { - "description": "wait/caption/scroll: milliseconds (scroll: animation duration); expect: max ms to wait for the condition (default 8000); awaitEmail: max ms to wait for the email (default 20000)", - "type": "number" - }, - "native": { - "description": "drag: force the synthetic HTML5 drag-and-drop path (real dragstart/dragover/drop events) for native `draggable=\"true\"` UIs. Auto-detected when the source is draggable; set true only if auto-detect misses. Leave unset for pointer-based libraries (dnd-kit, react-beautiful-dnd).", - "type": "boolean" - }, - "role": { - "description": "Who does this step, by the role the app gives them (admin, client, staff). Steps sharing a role are one person in their own browser, signed in with that role's sign-in. Every step naming one role: the check signs in as that person. Leave it off every step for a one-person test.", - "maxLength": 40, - "minLength": 1, - "type": "string" - }, - "selector": { - "description": "CSS selector for click/fill/hover/expect; select: combobox trigger; scroll: element to scroll to; drag: the source element to pick up; upload: the file field, or the control that opens the file picker", - "type": "string" - }, - "signedIn": { - "description": "expect: assert this page is past the sign-in: its address is not a sign-in address and no password box is showing. Use after opening a page only a signed-in person can see", - "type": "boolean" - }, - "text": { - "description": "click/hover/waitForText/expect: visible text to match (prefer over brittle CSS selectors); fill: label text; select: combobox label; caption: banner text; awaitEmail: subject substring to filter on; drag: visible text of the source to pick up; upload: the label of the file field, or the words on the button or drop area that takes the file", - "type": "string" - }, - "toSelector": { - "description": "drag: CSS selector of the drop target to drag the source ONTO", - "type": "string" - }, - "toText": { - "description": "drag: visible text of the drop target to drag the source ONTO", - "type": "string" - }, - "url": { - "description": "goto: URL to navigate to. May use ${magicLink} to open the link from the last awaitEmail.", - "type": "string" - }, - "urlIncludes": { - "description": "expect: assert the current URL contains this substring (e.g. \"/library\" to confirm a sign-in worked)", - "type": "string" - }, - "value": { - "description": "fill: text to type (use ${inbox} for the disposable inbox address); select: the option to pick from a dropdown/combobox; scroll: 'top' | 'bottom' | pixel offset; upload: the sample file to attach, by name, one of invoice.pdf, picture.png, photo.jpg, contacts.csv, notes.txt and letter.docx. Tada brings these files itself: a path or an address is refused", - "type": "string" - } - }, - "required": [ - "action" - ], - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "storageState": { - "description": "NOT SUPPORTED, and passing it refuses the check rather than ignoring it. It names a file on YOUR machine and the check is driven by Tada's runner. To check a page behind a sign-in, sign in inside the test: see CHECK PAGES BEHIND A SIGN-IN in the tool description.", - "type": "string" - }, - "taskId": { - "description": "NOT SUPPORTED, and passing it refuses the check. Attach the film to a task from the dashboard.", - "format": "uuid", - "type": "string" - }, - "ticket": { - "description": "The ticket id exactly as the tracker writes it, for example SOL-123. Lets Tada match the pull request that ships this feature.", - "maxLength": 60, - "minLength": 1, - "type": "string" - }, - "title": { - "description": "What this check is called, with `steps`. It names the feature when `feature` is left out. Not needed with `goal`.", - "maxLength": 200, - "minLength": 1, - "type": "string" - }, - "width": { - "description": "NOT SUPPORTED, and passing it refuses the check. Use `device`.", - "exclusiveMinimum": 0, - "type": "integer" - } - }, "type": "object" }, "name": "verify_feature"
Changed 2026-10-07 13:17 UTC
7 descriptions rewritten, 1 tool added.
--- pinned/about_app +++ observed/about_app { - "description": "Everything Tada knows about an app, before you write a test for it: its features and the tests that already prove them, the cast (the roles Tada can sign in as, each with a sign-in), and the map of its screens.\nUSE THIS FIRST whenever you are about to verify a feature. It tells you whether the feature already exists (so you file under its name instead of inventing a twin), which roles already have a sign-in (so you do not ask for one Tada has, since asking creates it), and which addresses exist (so the test goes to real pages).\nThen write the tests yourself and send them with verify_feature. plan_feature is optional: use it only when you do not know what the feature should prove.\nIt reads an app Tada already knows. To check an app Tada does not know (on localhost, say), skip it and send steps to verify_feature.", + "description": "Read what Tada knows about an app before you check it: its features and their tests, its cast (the roles Tada can sign in as), the map of its screens, and what is still to connect.\nCall it first. It is free and changes nothing.\nLeave `repo` out and Tada reads the app from the git remote of this folder.", "execution": { "taskSupport": "forbidden" }, - "description": "The app, as \"owner/name\" (e.g. \"acme/storefront\"). Must already be connected to Tada.", + "description": "The app, as \"owner/name\". Left out, Tada reads it from the git remote of this folder.", "type": "string" } }, - "required": [ - "repo" - ], "type": "object" }, "name": "about_app"
+ tool added: check_feature
--- pinned/check_result +++ observed/check_result { - "description": "Read how a check went, by its id: waiting for a runner, running, passed or blocked, how many tests passed, the film's link, and the step that failed with its reason.\nUSE THIS when verify_feature stopped waiting before its check finished and gave you an id. Read only: it starts nothing and is not billed.", + "description": "Read a check by its id: waiting, going, or its report (the result, what failed, what was not covered, and the links).\nUse it when check_feature stopped waiting before its check finished and gave you an id. Read only: it starts nothing and is not counted.", "execution": { "taskSupport": "forbidden" },
--- pinned/plan_feature +++ observed/plan_feature { - "description": "Before verifying a feature you just built from a ticket, plan its proof. Send the ticket first: Tada answers with what it knows about the app and the rules a proof must meet.\nThen write the proof yourself and send it back as proposal. Tada holds it to those rules, names anything missing, and walks its pages before anything is checked.\nShow the plan to the developer and get their confirmation, then write each test's steps and call verify_feature with the returned feature and `journeys` (one entry per test).", + "description": "Old name, kept so existing setups keep working. It does what it always did. For new work use about_app.", "execution": { "taskSupport": "forbidden" },
--- pinned/record_demo +++ observed/record_demo { - "description": "DEPRECATED: renamed to `verify_feature`. Prefer `verify_feature`; this name still works so existing setups keep running.\nChecks a finished feature in a real browser and returns a verdict and one shareable film.", + "description": "Old name, kept so existing setups keep working. It does what it always did. For new work use check_feature.", "execution": { "taskSupport": "forbidden" }, + "address": { + "description": "With `goal`: where to check, when it is not the app's usual address. An address on this machine (localhost) is reached through a temporary tunnel.", + "format": "uri", + "type": "string" + }, "captureScale": { "description": "Shoot at 2 ONLY when this film will be re-cut as marketing footage. A cut of 1x footage is capped at a 1.2x push-in forever, because the pixels for a closer look were never captured; 2 lifts that to 2.2x. It is a decision made at filming that cannot be taken later, and it costs four times the frame pixels, so leave it off for an ordinary check.", "enum": [ + "goal": { + "description": "What should work, in one or two sentences, with the result in it: \"A new customer can sign up and lands on their account page.\" Tada writes the test and checks it. Needs an app Tada knows (`repo`). Send the same sentence again after a fix: Tada keeps to the test it wrote for it. Leave out when sending `steps`.", + "maxLength": 240, + "minLength": 1, + "type": "string" + }, "height": { "description": "NOT SUPPORTED, and passing it refuses the check. Use `device`.", "exclusiveMinimum": 0, + "role": { + "description": "With `goal`: the role to sign in as, in the app's own word, such as \"admin\" or \"client\". about_app lists the cast.", + "maxLength": 40, + "minLength": 1, + "type": "string" + }, "room": { "additionalProperties": false, "description": "Save this as a test with several people from the app's cast, filmed like a video call (everyone joins and says hello, the lead reads the agenda, then each does their part). Every step needs a role. It is saved on the app and checked now. (The parameter keeps the name `room` so existing agent setups work.)", - "description": "What this check is called, shown on the watch page. It names the feature when `feature` is left out.", + "description": "What this check is called, with `steps`. It names the feature when `feature` is left out. Not needed with `goal`.", "maxLength": 200, "minLength": 1, "type": "string" - "required": [ - "title" - ], "type": "object" }, "name": "record_demo"
--- pinned/setup_test_account +++ observed/setup_test_account { - "description": "Find out how this app signs in, so a test can reach pages only a signed-in person sees.\nUSE THIS when a test you are about to send to verify_feature needs someone signed in.\n\n⚠ YOU CAN CREATE THE ACCOUNT. YOU CANNOT READ ITS PASSWORD BACK. Creating one generates an email and a random password for a user that exists NOWHERE yet, so an agent key that mints one gains nothing: it holds a sign-in that has never been seeded in the app. Revealing the password of an account that by now DOES exist is a different thing, and it stays behind an interactive sign-in, in the dashboard.\n\nHOW IT WORKS: calling this creates the account if the app has none, and returns the existing one if it has. Someone then adds that user to the seed script, so it exists in every environment, including each pull request's own database, which is why a copied staging password would not work.\n\nONCE THE ACCOUNT EXISTS, DO TWO THINGS:\n1. Add the user to the seed, reading the password from `process.env.TADA_TEST_PASSWORD`, and tell the human to set that variable wherever their app seeds.\n2. Reference the values symbolically in every step: `fill Email with {{secret.TADA_TEST_EMAIL}}`, `fill Password with {{secret.TADA_TEST_PASSWORD}}`. NEVER paste the literal password into a step, a file, or a commit. A test is stored with its check, so a literal typed here would be saved in the clear; the substitution happens on the runner at the moment of typing.\n\nYou never need to read either value yourself. The runner substitutes them at the moment of typing, and the stored test keeps the placeholders.\nIt is safe to call more than once. An existing account is returned unchanged, never replaced.\n\nWITH A `role`, IT ASKS FOR A CAST MEMBER INSTEAD: one of Tada’s own people living in your app in that role (admin, client, staff), with a sign-in, so a test can act as that person.\nTada makes the sign-in and returns its email and password once. Where Tada already has an admin inside your app, Tada adds that person to your app itself, through your invite screen the way a person would, and the answer says so (planting). Where Tada has nobody inside yet, the answer says this first person is added from your side (the seed script, the invite screen, or an admin page).\nALREADY HAVE A SIGN-IN SEEDED FOR TADA? Pass `role` with `email` and `password` and Tada adopts it instead of making one. Tada seals the password and types it itself.\nA cast member is made once and reused by every check after it. Asking again for the same role returns the same one, with its status and what happens next.\nTada keeps each cast member's password and types it at every sign-in. Steps reference one as {{secret.TADA_<ROLE>_EMAIL}} and {{secret.TADA_<ROLE>_PASSWORD}}, or with \"role: <role>\" on a step.", + "description": "Old name, kept so existing setups keep working. It does what it always did. For new work use check_feature with `signIn`.", "execution": { "taskSupport": "forbidden" },
--- pinned/tada_connect_status +++ observed/tada_connect_status { - "description": "Check what this Tada account still needs connected before Tada can check your pull requests, and get the link to connect each one.\nOnly needed to check pull requests. Checking an app needs nothing connected, localhost included: go straight to verify_feature with steps.\nReturns a URL per provider. The user opens it and approves in a browser; call again to confirm it landed.", + "description": "Old name, kept so existing setups keep working. It does what it always did. For new work use about_app.", "execution": { "taskSupport": "forbidden" },
--- pinned/verify_feature +++ observed/verify_feature { - "description": "Check a feature you just built in a real browser, the way a QA engineer would. Tada is a QA for the app: it plays the people who use it, returns a verdict with a report, and a pass comes with one film you can share.\nUSE THIS whenever you are asked to check, verify or prove that a feature works, or to record a Loom, make a screencast, capture a demo video, do a walkthrough, or \"show\"/\"record\" a feature working. Prefer it over describing steps in text or asking the user to record anything.\nA check is one go at ONE feature. The tests of that feature are what you send: `steps` is one test, `journeys` is several. The check has one verdict, and a pass has one film with a chapter per test. Tada’s own browser walks the steps and films them; nothing renders on this machine.\n\nHOW TO MAKE A GREAT FILM (follow this playbook):\n• Open the app first, and sign in if it needs it, THEN one caption that greets the viewer and sets context (e.g. \"Hi, this is the new task list. Watch a task appear the moment it is added\"). A sentence spoken over a blank page or a sign-in form is about something the viewer cannot see yet; Tada holds such a caption until the page it is about is on screen.\n• Walk the happy path only: no edge cases, errors, or dead ends. Show the feature working.\n• Before each meaningful action, add a `caption` step that talks around it calmly, saying what it is for or what to notice (\"This keeps only what you sent\"), not a play-by-play like \"Now I click Save\". If the user has their own wording or tone, use theirs. ALWAYS include captions: a silent film is a bad film.\n• End on the visible result so the viewer sees the payoff (the new item on screen, the success toast, etc.).\n• Keep it tight: aim for ~30–90 seconds total.\n• Prefer `text` locators (visible labels) over brittle CSS `selector`s for click/hover, because they survive DOM and class changes.\n\nIT BECOMES THE FEATURE’S PROOF: name the feature with `feature` (or the title names it) and the app with `repo`. A test that passes and checks something is saved under that feature, and Tada replays it on every pull request from then on. So write it to be replayed: open the app by its path, and check the result with `expect`.\n\nVERIFY IT ACTUALLY WORKS (important, do not skip):\n• After each key action, add an `expect` step asserting the result really happened, e.g. after a sign-in click, `{ action: \"expect\", urlIncludes: \"/library\" }`; after creating an item, `{ action: \"expect\", text: \"My new item\" }`.\n• `expect` supports `text` (visible text appears), `urlIncludes` (URL contains a substring), or `selector` (element becomes visible).\n• If any `expect` does not hold, the check is Blocked and Tada publishes no film. It returns each step that failed so you can fix the steps and check again. This guarantees a published film shows the feature actually working, not a stuck or blank screen.\n\nSHOW THE WHOLE FEATURE, NOT ONE CLICK (archetype beats, docs/feature-archetypes.md):\n• A film of a feature is complete when a viewer could explain the feature back. Match the shape, then hit its beats:\n• FILTER/SEARCH: the unfiltered list WITH ITS COUNT → apply → the changed list and count → clear, count restored. The count is the narration.\n• CREATE: open → fill visibly → submit → THE NEW THING IN THE LIST (the beat thin films skip). EDIT: change one field → the change visible where a viewer would look. DELETE: gone from the list.\n• TABLE/LIST: say what a row is, open one, come back; sort a column if it sorts. SETTINGS: flip it, then SHOW THE CONSEQUENCE somewhere else, because a toggle never shown doing anything is a meaningless beat.\n• WIZARD: every step is a landmark: show it, say its job, advance; end somewhere real. EMPTY STATE: what a new account sees, and doing what it suggests.\n• HUMAN-ONLY (OAuth, \"Connect Railway\"): do not skip and never fake. `hover` the control and let the caption say what it does and what the person will see.\n\nPROVE A FIX LIKE A QA, NOT A SCRIPT (when the check answers \"does X work?\" or a bug report):\n• Do not stop at the single assertion. \"Click the thing, it works\" PROVES it but does not EARN BELIEF. Plan a short tour of the adjacent states a real tester would check, all of them WORKING states (still the happy path, not edge cases or errors).\n• Example: report: \"overdue items still show after filtering by Sent\". Filter Sent and `expect` the Sent-only result (the fix), THEN flip to a sibling filter (Overdue still lists them, All shows both), then back to Sent, showing the fix holds across the filter space, not just once.\n• OBSERVE every tour step with its own `expect`, never aimless clicking. Conscious, not chaotic: author the whole tour up front; do NOT improvise random interactions (an automated walk that wanders can trip an UNRELATED bug on camera, or go differently each time).\n• Keep the load-bearing checkpoint sacred: assert something that was FALSE before the fix and TRUE after. That single `expect` is the proof; the tour is the conviction around it, and it never replaces or weakens it.\n\nNARRATION (captions / voice-over):\n• Narration is controlled ENTIRELY by the USER'S TADA SETTINGS (their dashboard Captions + Voice-over + voice toggles). There is no agent override. Just write good captions; whether they're shown, spoken (and in which voice) is applied automatically from the account.\n\nDRAG-AND-DROP: use a `drag` step for a drag UI (kanban board, reorderable list). Source is `selector`/`text` (like click); drop target is `toSelector`/`toText`. Example: `{ action: \"drag\", text: \"My card\", toText: \"In Progress\" }` moves a card between columns. It tries a pointer drag first (dnd-kit / react-beautiful-dnd) and auto-switches to native HTML5 drag events for real `draggable=\"true\"` UIs; set `native: true` to force that. ALWAYS follow a `drag` with an `expect` asserting the item landed (e.g. `{ action: \"expect\", ... }`), because an unverified drag can silently do nothing and must not publish.\nATTACH A FILE: use an `upload` step where the feature needs a document or a picture. `text`/`selector` names the file field, or the button or drop area that takes the file (like click); `value` names the file. Example: `{ action: \"upload\", text: \"Attach invoice\", value: \"invoice.pdf\" }`. The file is always one of Tada's own sample files, named exactly: invoice.pdf, picture.png, photo.jpg, contacts.csv, notes.txt and letter.docx. A path on this machine or an address is refused, because the check happens on Tada, which cannot see this machine's files. ALWAYS follow an `upload` with an `expect` for what the app shows once it has the file (its name in a list, a preview, a confirmation).\nVIEWPORT: set `device` to \"desktop\" (default), \"mobile\", or \"tablet\" to match the feature. Use \"mobile\" only for mobile-first UIs.\n\nSEVERAL PEOPLE IN ONE TEST (an admin invites, a teammate accepts, a client sees the result):\n• Put `role` on every step: the role the app gives that person, such as \"admin\" or \"client\". Each role is one person from the app’s cast, in a browser of their own, signed in with that role’s sign-in. They act at the same time, and one film shows them side by side.\n• Each role signs in with its own sign-in. about_app lists the roles Tada has; setup_test_account with a `role` makes one. A role with no sign-in is someone the test brings in (an invite, a sign-up) and starts signed out. A role whose sign-in is still being added refuses the check before a browser opens, unbilled, and names the role.\n• Every person opens the app with their own `goto` first.\n• When one person must wait for another, the one who acts ends with `{ action: \"signal\", text: \"invited\", role: \"admin\" }` and the one who waits starts with `{ action: \"await\", text: \"invited\", role: \"staff\" }`. Every `await` needs a `signal` with the same text, or the check is refused before it starts.\n• Values are shared: the address an admin types as ${inbox} is the inbox the invited person reads with `awaitEmail` after their `await`.\n• Example: admin goto /team, createInbox, click \"Invite\", fill \"Email\" with ${inbox}, click \"Send\", signal invited. staff await invited, awaitEmail, goto ${magicLink}, expect \"Welcome\", signal joined. admin await joined, expect the new teammate in the list.\n• Every step naming the same one role is a one-person test done as that role.\n\nCHECK PAGES BEHIND A SIGN-IN (sign-in off camera): wrap the sign-in in `pause`/`resume`. The browser still performs those steps but no frames are filmed, so credentials never appear on screen and the film starts clean on the signed-in page. Pattern: `{pause}` → goto /login → fill email/password → click \"Sign in\" → goto the page → `{resume}` → caption + the feature.\n⚠ NEVER TYPE A REAL PASSWORD INTO A STEP. The test is stored with the check, so a literal credential would be written into our database in the clear. Type `{{secret.TADA_TEST_EMAIL}}` and `{{secret.TADA_TEST_PASSWORD}}` instead: the runner substitutes them from the app's test account at the moment it types, and the stored test keeps the placeholder. If the app has no test account yet, call `setup_test_account` once.\n\nTADA KEEPS THE TESTS, NEVER YOUR REPOSITORY. Do not write any file into this repository for Tada. When a check PASSES, Tada keeps the test it just verified as a test of its feature, inside Tada, and replays it against every future pull request, so a change that breaks this feature fails its check. Tell the user the feature is now guarded. The film shows the feature working once; the kept test re-proves it on every pull request.\n• A Blocked check keeps nothing, by design: a guard built from a test that did not work would lock in the breakage.\n• While a test is still being written, each go at it is a TRY: nothing is reported, no feature is marked failing, and it is not counted against the plan. The reply says which try it was. Fix the step or the code and send it again. The try that passes becomes the check, and from then on every run of it counts.\n• For a feature built from a ticket, call plan_feature first, show the developer the plan, then send every test of it here with `journeys` and the `feature` plan_feature returned.", + "description": "Old name, kept so existing setups keep working. It does what it always did. For new work use check_feature.", "execution": { "taskSupport": "forbidden" }, + "address": { + "description": "With `goal`: where to check, when it is not the app's usual address. An address on this machine (localhost) is reached through a temporary tunnel.", + "format": "uri", + "type": "string" + }, "captureScale": { "description": "Shoot at 2 ONLY when this film will be re-cut as marketing footage. A cut of 1x footage is capped at a 1.2x push-in forever, because the pixels for a closer look were never captured; 2 lifts that to 2.2x. It is a decision made at filming that cannot be taken later, and it costs four times the frame pixels, so leave it off for an ordinary check.", "enum": [ + "goal": { + "description": "What should work, in one or two sentences, with the result in it: \"A new customer can sign up and lands on their account page.\" Tada writes the test and checks it. Needs an app Tada knows (`repo`). Send the same sentence again after a fix: Tada keeps to the test it wrote for it. Leave out when sending `steps`.", + "maxLength": 240, + "minLength": 1, + "type": "string" + }, "height": { "description": "NOT SUPPORTED, and passing it refuses the check. Use `device`.", "exclusiveMinimum": 0, + "role": { + "description": "With `goal`: the role to sign in as, in the app's own word, such as \"admin\" or \"client\". about_app lists the cast.", + "maxLength": 40, + "minLength": 1, + "type": "string" + }, "room": { "additionalProperties": false, "description": "Save this as a test with several people from the app's cast, filmed like a video call (everyone joins and says hello, the lead reads the agenda, then each does their part). Every step needs a role. It is saved on the app and checked now. (The parameter keeps the name `room` so existing agent setups work.)", - "description": "What this check is called, shown on the watch page. It names the feature when `feature` is left out.", + "description": "What this check is called, with `steps`. It names the feature when `feature` is left out. Not needed with `goal`.", "maxLength": 200, "minLength": 1, "type": "string" - "required": [ - "title" - ], "type": "object" }, "name": "verify_feature"
Current tools
Show all 14 tool fingerprints
Watch this server yourself
If you run this server, put the proxy in front of it. It pins these exact fingerprints on first connect and stops the session if they move.
npx -y mcp-pin@0.2.3 -- <your tada-mcp command>
Or subscribe to this page's RSS feed to be told when it changes.
Badge
The badge states one fact about time and nothing else. It never claims a server is safe.
[](https://mcp-pin.gautamkhosla.com/servers/b0643be6f77c7df3.html)