easy-spec-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| EASYSPEC_PROJECT | No | Optional path to project. If omitted, tools fall back to this value, and live_* tools can auto-target the single connected side panel with neither. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| list_flowsA | List flows in a project (id, name, folder, step count). |
| list_scenariosA | List scenarios in a project (id, name, folder, composed flow count). |
| list_page_objectsC | List page objects in a project (name, folder, locator count). |
| get_variablesA | Get all global variables (Variables tab) for a project. |
| get_flowA | Get a flow's full definition, including every step. |
| get_scenarioA | Get a scenario's full definition (the flows it composes and their params). |
| get_page_objectB | Get a page object's locators and scopes. |
| create_flowA | Create a new, empty flow. |
| delete_flowB | Delete a flow (its .flow.json, .flow.ts, and flowData.ts). |
| set_flow_metaB | Update a flow's name, folder, description, preconditions, and/or notes. |
| add_stepA | Add a step to a flow, at the end by default. Use parentPath to insert into a Conditional/Repeat/Iterate step's own nested body. |
| add_multiple_stepsA | Add a whole ordered batch of steps to a flow in one call/one undo entry -- same placement rules as add_step (end of the array by default, parentPath for a Conditional/Repeat/Iterate body), but avoids an add_step-per-step round trip when recording several steps at once, e.g. from a live exploration session where each live_run_step action gets a matching recorded step. |
| update_stepB | Patch fields on an existing step. |
| remove_stepC | Remove a step by its path. |
| copy_stepsA | Copies a contiguous run of steps [from, to] (inclusive, 0-based indices within sourceParentPath's own array) from one flow into another flow -- or the same flow, for a reorder-via-copy. Deep-cloned (nested Conditional/Repeat/Iterate bodies come along intact). One undo entry, on the target flow only -- the source is never modified. Splitting a flow into two, or reusing a chunk of steps another flow already has, is one call instead of reading every step and re-adding each one by hand. |
| set_flow_datasetA | Add or replace a named flow-data dataset (a set of param values a flow can run with -- the Flow editor's "Flow Data" tab). |
| remove_flow_datasetC | Remove a named flow-data dataset. |
| set_state_varC | Add or replace a flow-scoped state variable (a |
| remove_state_varC | Remove a flow-scoped state variable. |
| set_output_fieldA | Add or replace one of a flow's output-data fields (what it returns to a scenario that composes it). |
| remove_output_fieldB | Remove one of a flow's output-data fields. |
| create_page_objectB | Create a new, empty page object. |
| delete_page_objectA | Delete a page object (fails if any flow step still binds to it via its own persistPageObject usage -- check get_flow first). |
| set_locatorA | Add or replace a locator on a page object. |
| remove_locatorC | Remove a locator from a page object. |
| set_scopeC | Add or replace a SCOPE on a page object -- a root-element locator other locators on the same page object can be nested under (see set_method_scope), so their own selectors only need to be unique within it instead of the whole page. Useful for a reusable section (a modal, a repeated table row) -- a page object already models either a full page or a section; scopes are what make the section case actually robust. |
| remove_scopeA | Remove a scope from a page object. Fails if any locator on the same page object is still assigned to it -- unassign with set_method_scope first (pass a null/omitted scope). |
| set_method_scopeA | Nest an existing locator under one of its OWN page object's scopes (create the scope first with set_scope) -- generated code then reads it off that scope's own element instead of the whole page. Omit/pass an empty scope to unassign, going back to page-wide. |
| list_utilsA | List shared utility functions in a project (name, groupName, folder, param count). |
| get_utilB | Get a shared utility function's full definition (groupName + params + body). A step calling it uses |
| create_utilA | Create a new, empty shared utility function (no params, empty body). Lands in the given group, defaulting to "custom-utils" if omitted -- see list_util_groups/create_util_group. |
| set_util_bodyA | Set a shared utility function's body -- plain TypeScript statements, no wrapping |
| set_util_paramsB | Set a shared utility function's ordered parameter names (bare identifiers, e.g. ["sql", "params"]). |
| delete_utilA | Delete a shared utility function. Does NOT check whether any flow step still calls it (via kind: groupName, method: name) -- check list_flows/get_flow first (same caveat as delete_page_object: this is a bare delete, no reference cascade). |
| list_util_groupsA | List util groups in a project (name, folder, member count). "custom-utils" always exists as the default group, even before it's ever been explicitly used. |
| get_util_groupA | Get a util group's metadata plus its member utils' names/params. |
| create_util_groupA | Create a new, empty util group. Groups are also created automatically the first time a util is placed in one (create_util's groupName, or set_util_group_name) -- this is only needed to make an empty one exist in advance. |
| set_util_group_nameA | Move an existing util into a (possibly brand-new) group -- e.g. moving "connectDb" out of "custom-utils" into a new "db-helpers" group. Rewrites every referencing step's own |
| delete_util_groupA | Delete a util group. Does NOT destroy its member utils -- each is moved to "custom-utils" instead (their params/body are fully meaningful standalone, so this never silently discards content). Cascades every affected step's own |
| create_scenarioC | Create a new, empty scenario. |
| delete_scenarioC | Delete a scenario. |
| add_flow_to_scenarioA | Append a flow OR another already-saved scenario to a scenario's composed sequence, as a RUN CONFIG -- the exact same dataset-tokens/raw-YAML pair live_replay_flow's own |
| remove_flow_from_scenarioA | Remove one of a scenario's composed flows by its index. |
| set_scenario_datasetB | Add or replace a named scenario-data dataset (a set of scenario-level param values, referenceable from any flow entry's own params/postprocess expressions) -- mirrors set_flow_dataset one level up. |
| remove_scenario_datasetC | Remove a named scenario-data dataset. |
| list_suitesA | List every suite entry in a project: id, folder path, which scenario it points at (plus that scenario's own name, for convenience), and its own saved run params (datasetTokens/useYaml/yaml, same shape add_flow_to_scenario stores -- absent means the scenario's own default dataset). A suite entry is a SAVED RUN, not a bare reference -- the same scenarioId can appear more than once, at different paths or even the same one, each with its own independent params. |
| add_to_suiteA | Place an existing scenario at a suite folder path (e.g. "regression/checkout", or "" for the root) as a SAVED RUN, not a bare reference -- |
| remove_from_suiteA | Remove one suite placement by its entry id (see list_suites). Only removes that placement -- the underlying scenario, and any of its OTHER placements, are untouched. |
| move_suite_entryB | Move one suite placement (by its entry id, see list_suites) to a different folder path. |
| set_variableC | Set a global variable (Variables tab). |
| unset_variableC | Remove a global variable. |
| live_statusA | List which projects currently have a side panel connected to this bridge (by folder name -- call this to see what's available to target, e.g. before omitting |
| connect_panelA | Establish trust with a specific side panel by its session name -- the only way to do so, since a new connection starts with no claim at all. Pass the exact session name shown in the target panel's own UI (e.g. "calm-yak-806409"); on a match the panel moves that connection from unverified to connected. Returns immediately either way -- call live_status or check the panel UI afterward to confirm it actually took. |
| live_replay_flowA | Replay a single flow live, in the side panel's own connected browser tab (real chrome.debugger, the user's real session/login) -- requires the side panel open and connected to this project. Targets whichever tab is pinned in the side panel's header, or the browser's actual active tab if none is pinned. |
| live_run_step_rangeA | Runs a contiguous [from, to] slice of an already-SAVED flow's own top-level steps live, as a real tracked Run (shows up in the Runs tab with live per-step status dots, same as live_replay_flow/live_start_run) -- for re-verifying just the steps you just added/changed instead of a full live_replay_flow from step 0 every time. Context is seeded the same way a fresh replay's first iteration would be (the chosen dataset/params -- or the flow's own first/default dataset if neither is given -- plus state-var defaults), since a mid-flow slice has no prior-steps history to inherit values from otherwise: if the steps in range reference a param/state-var, pass a |
| live_pick_elementA | Starts the element picker on the side panel's target tab (the pinned tab, or the active tab if none is pinned) and waits (up to 2 minutes) for the user to click something, returning the resulting locator expression -- the same picker the step editor's "Pick element" button uses, generated by the real Playwright engine (Locator.generateLocatorString()), ready to use as-is with set_locator/set_scope or a step's own |
| live_run_stepA | Runs one plain step (locator/page/assert/variable -- not a Conditional/Repeat/Iterate) live against the side panel's target tab (the pinned tab, or the active tab if none is pinned), the same way a step's own Play button would, and returns its result: |
| live_run_multiple_stepsA | Runs a whole ordered sequence of plain steps (locator/page/assert/variable -- not Conditional/Repeat/Iterate) live against the side panel's target tab in one call, sharing one variable context across them (so a |
| live_snapshotA | Accessibility (ARIA) snapshot of the side panel's target tab (the pinned tab, or the active tab if none is pinned) -- a text tree of roles/names/values (the same format Playwright's own tooling uses), the fastest way to "see" the page well enough to find a selector for live_run_step/add_step without a screenshot. Requires the side panel open and connected. |
| live_screenshotA | JPEG screenshot of the side panel's target tab (the pinned tab, or the active tab if none is pinned). Prefer live_snapshot for finding a selector to act on -- reach for this when the question is genuinely visual (layout, colors, whether something rendered). Requires the side panel open and connected. |
| live_start_runA | Starts a flow running live on the side panel's target tab (the pinned tab, or the active tab if none is pinned) and returns immediately with a |
| live_list_runsA | Lists runs on the connected side panel: |
| live_get_runA | Full detail for one run started via live_start_run (or live_replay_flow) by its |
| live_cancel_runA | Cancels a still-running run by its |
| live_replay_scenarioA | Replay a whole scenario live -- runs its composed flows strictly sequentially, in the side panel's own connected browser tab (real chrome.debugger, the user's real session/login), stopping at the first flow that fails, exactly like a single flow stops at its first failing step. A flow entry whose flow declares output (see add_flow_to_scenario) automatically has its captured |
| live_start_scenario_runA | Starts a scenario running live on the side panel's target tab and returns immediately with a |
| live_list_scenario_runsA | Lists scenario runs on the connected side panel: |
| live_get_scenario_runA | Full detail for one scenario run started via live_start_scenario_run (or live_replay_scenario) by its |
| live_cancel_scenario_runA | Cancels a still-running scenario run by its |
| live_run_suiteA | Runs a suite (or a single suite entry) live, in parallel, across |
| live_list_suite_runsA | Lists suite runs on the connected side panel: |
| live_get_suite_runA | Full detail for one suite run started via live_run_suite, by its |
| live_cancel_suite_runA | Cancels a still-running suite run by its |
| live_detachA | Detaches chrome.debugger from the side panel's target tab right away, instead of waiting out the ~20s idle timeout every other live_* call leaves it attached for (see live_snapshot/ live_run_step/live_pick_element -- they keep the tab attached across a quick back-and-forth burst of calls rather than detaching after each individual one, since detaching mid-exploration was closing transient page UI like an open dropdown). Call this when done with a live exploration/interaction sequence, so Chrome's "being debugged" banner does not linger on the tab longer than necessary. |
| live_list_tabsA | Lists every open tab in the side panel's browser window (id, title, url, which one is currently active, which one -- if any -- is pinned as the target for every other live_* call). Call this before live_switch_tab to get a valid tabId. |
| live_switch_tabA | Pins a specific already-open tab (by id, see live_list_tabs) as the target for every other live_* call, same as picking it from the side panel's own "Target" menu. Omit |
| live_new_tabA | Opens a new browser tab and immediately pins it as the target for every other live_* call (same as live_switch_tab right after). Optionally navigate it straight to |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 77 tools
Most tools are clearly separated by entity type (flow, scenario, page object, util, suite) and action (list, get, create, delete, set, live_*). A few closely related live-execution tools could require careful reading, but descriptions generally make each one's purpose distinct.
The naming largely follows a consistent snake_case verb_noun pattern such as list_flows, create_page_object, set_util_body, and live_get_run. Minor deviations like add_to_suite and connect_panel are still readable and do not seriously break the overall convention.
With 77 tools, the surface is extremely large and far outside the typical well-scoped range. Even for a complex automation domain, this many distinct endpoints creates a heavy cognitive and discovery burden for an agent.
The toolkit covers a broad lifecycle: create/list/get/update/delete for flows, scenarios, page objects, and utils, plus live execution, run inspection, and suite management. Some possible lifecycle gaps exist, such as no explicit suite-entry removal tool, but the domain is otherwise richly covered.