diff --git a/tutorials/signals-mcp/conclusion.md b/tutorials/signals-mcp/conclusion.md new file mode 100644 index 000000000..0ba2b103d --- /dev/null +++ b/tutorials/signals-mcp/conclusion.md @@ -0,0 +1,35 @@ +--- +title: "Conclusions and next steps from the conversational Signals tutorial" +position: 5 +sidebar_label: "Conclusion" +description: "Recap of managing Signals conversationally in the Snowplow Assistant or with the Snowplow MCP server, with next steps for services and interventions." +keywords: ["snowplow assistant", "snowplow mcp", "signals services", "interventions", "next steps"] +date: "2026-08-04" +--- + +In this tutorial you managed a complete Signals workflow conversationally, in the Snowplow Assistant or in your own MCP-connected assistant: + +* Opened the assistant in Console, or installed the Snowplow plugin and authenticated with your Console account +* Defined a stream attribute group with three session metrics, saved as a draft +* Tested the definitions and published the group +* Verified the configuration in Console, and the live values in the Snowplow Inspector +* Added an attribute and published the new version, without leaving the conversation + +## Beyond attribute groups + +Attribute groups are one part of [Signals](/docs/signals/introduction/). The same conversational approach applies to: + +* [Services](/docs/signals/applications/services/): bundle attribute groups behind a single name for your applications to query +* [Interventions](/docs/signals/interventions/): define rules that push an action to your application the moment a user's attributes meet your criteria +* [Attribute keys](/docs/signals/attributes/attribute-keys/): create custom keys to aggregate by, such as an account or tenant ID +* [Agentic contexts](/docs/signals/agentic-contexts/): maintain a rolling log of each user's recent events to ground your own AI agents in live behavior + +Both routes reach beyond Signals into the rest of Snowplow Console, from data structures and tracking plans to pipeline health and failed events. See [Snowplow Assistant](/docs/llms-support/console-agent/) and [Snowplow MCP server](/docs/llms-support/snowplow-mcp/) for what each one covers. + +## Next steps + +Some directions to take from here: + +* Follow the [interventions tutorial](/tutorials/signals-interventions/start) to react to attribute changes in an ecommerce app, then try recreating its configuration conversationally +* Use the [Python SDK](/tutorials/python-tracking-and-signals/introduction) when you want Signals definitions in version control, with the assistant helping you write them +* Ask your assistant to explore what's already in your account: "list my attribute groups and summarize what each one calculates" is a useful audit prompt diff --git a/tutorials/signals-mcp/connect-the-mcp-server.md b/tutorials/signals-mcp/connect-the-mcp-server.md new file mode 100644 index 000000000..502d3edbe --- /dev/null +++ b/tutorials/signals-mcp/connect-the-mcp-server.md @@ -0,0 +1,124 @@ +--- +title: "Open the Snowplow Assistant or connect the MCP server" +position: 2 +sidebar_label: "Connect your assistant" +description: "Start a conversation with the Snowplow Assistant in Console, or install the Snowplow MCP server in your own AI assistant, authenticate with OAuth, and check the connection." +keywords: ["snowplow assistant", "snowplow mcp install", "claude code plugin", "mcp authentication", "console api key", "mcp-remote"] +date: "2026-08-04" +--- + +Both routes end in the same place: a conversation that can read and change your Signals configuration on your behalf. Pick the one that fits where you want to work. + +## Use the Snowplow Assistant + +The [Snowplow Assistant](/docs/llms-support/console-agent/) is built into [Snowplow Console](https://console.snowplowanalytics.com) as a chat interface, so there's nothing to install and no credentials to configure. It authenticates with your current Console session and operates with your existing Console permissions, and it asks you to confirm any action that changes your configuration before it proceeds. + +Log in to Console, open the assistant, and you're ready to start prompting. If the chat interface doesn't appear, an administrator can enable the assistant for your organization from the **Settings** section of Console: see [Snowplow Assistant](/docs/llms-support/console-agent/) for what it covers and how it handles your data. + +On this route you can skip straight to [defining attributes](/tutorials/signals-mcp/define-attributes-conversationally). The rest of this page connects the MCP server to your own assistant instead. + +## Install the Snowplow plugin + +The Snowplow MCP server is a remote server, hosted by Snowplow: there's no server for you to deploy or maintain. Connecting your assistant to it means pointing your MCP client at the server URL and authenticating with your Snowplow Console account. + +The Snowplow plugin bundles the MCP server with six skills, including the `signals` skill that guides the assistant through Signals workflows like the one in this tutorial. The skills are loaded on demand: when you ask about attribute groups, the assistant automatically engages the `signals` skill, so there's no slash command to run. The plugin source lives in the [`snowplow/skills` repository](https://github.com/snowplow/skills). + +### Install in any assistant + +Whichever assistant you use, the recommended way to install is the vendor-neutral [open-plugin](https://github.com/vercel-labs/plugins) CLI, which installs the MCP server and the skills together: + +```bash +npx plugins add snowplow/skills +``` + +The CLI detects which agent tools you have on your machine and installs the plugin to all of them. To install for a single one, name it with `--target`: + +```bash +npx plugins add snowplow/skills --target claude-code +``` + +### Install in Claude Code + +Claude Code also has a native plugin marketplace, which reads the plugin definition straight from the repository and updates it automatically when the repository changes. Run these two commands inside Claude Code: + +```text +/plugin marketplace add snowplow/skills +/plugin install snowplow@snowplow +``` + +### Other MCP clients + +Any MCP-capable client can connect to the server directly. For example, using [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) in a JSON-based client configuration such as Claude Desktop or Cursor: + +```json +{ + "mcpServers": { + "snowplow-mcp": { + "command": "npx", + "args": [ + "-y", + "mcp-remote", + "https://console.snowplowanalytics.com/api/agent/mcp", + "3334", + "--static-oauth-client-info", + "{\"client_id\":\"NxCcdyu13Cr4umnIYw70evvUyRXRvyWf\"}" + ] + } + } +} +``` + +See [Snowplow MCP server](/docs/llms-support/snowplow-mcp/) for tested configurations for Claude.ai, Claude Desktop, Claude Code, Codex, and Cursor. You won't get the bundled skills this way, but all the Signals tools work the same. + +## Authenticate with your Console account + +Use OAuth. It's the simpler path and the one to start with: there's nothing to configure, because every installation route above is already set up for it. The first time your assistant calls a Snowplow tool, a browser window opens for you to log in to [Snowplow Console](https://console.snowplowanalytics.com). The assistant then operates with the same permissions as your user account, and can only access your organization's resources. + +For most people that's the whole of authentication, and you can go straight to checking the connection. + +### Optional: authenticate with API keys + +OAuth tokens expire, which means occasional re-authentication prompts. If you'd rather have a connection that keeps working without a browser, authenticate with a Console API key instead. You'll need three values: + +1. Your organization ID, from the **Manage organization** page in Console settings +2. An API key ID, which you get when you [create an API key in Console](/docs/account-management/#create-an-api-key) +3. The API key itself, which Console shows only once, at creation + +Then pass them as headers in your MCP client configuration: + +```json +{ + "mcpServers": { + "snowplow-mcp": { + "command": "npx", + "args": [ + "-y", + "mcp-remote", + "https://console.snowplowanalytics.com/api/agent/mcp", + "--header", + "X-Org-Id:", + "--header", + "X-Api-Key-Id:", + "--header", + "X-Api-Key:" + ] + } + } +} +``` + +:::warning[API keys use admin permissions by default] +By default, API keys are created with all permissions, which may be broader than you intend for an assistant. If you want the assistant to operate with your user account's more limited permissions, stay with OAuth. Store the key securely, and never paste credentials into the assistant's chat. +::: + +See [Connect to Snowplow Signals](/docs/signals/connection/) for the full list of connection credentials and where to find each one. + +## Check the connection + +Start a new session in your assistant and ask it something that requires a Snowplow tool call: + +```text +Which Snowplow organization am I connected to? +``` + +The assistant should call the `get_organization` tool and reply with your organization's name and ID. With OAuth, this first call is what triggers the browser login. Confirm the organization ID matches the one in Console before continuing: it's worth being certain the assistant is pointed at the right account before you start creating resources. diff --git a/tutorials/signals-mcp/define-attributes-conversationally.md b/tutorials/signals-mcp/define-attributes-conversationally.md new file mode 100644 index 000000000..eeca1b3a9 --- /dev/null +++ b/tutorials/signals-mcp/define-attributes-conversationally.md @@ -0,0 +1,115 @@ +--- +title: "Define attributes conversationally" +position: 3 +sidebar_label: "Define attributes" +description: "Prompt the Snowplow Assistant or your own AI assistant to create a Signals stream attribute group, test it against warehouse data, and publish it." +keywords: ["signals attribute group", "snowplow assistant", "conversational configuration", "test attributes", "publish signals"] +date: "2026-08-04" +--- + +With your assistant ready, you can define your first attribute group by describing it. You'll create a stream [attribute group](/docs/signals/attributes/attribute-groups/) that calculates three session metrics from page view events, the same ones as in the [Signals quick start](/tutorials/signals-quickstart/define-attribute-group): + +* How many page views occurred in the last 15 minutes, per session +* The last seen browser name, per session +* The first seen page referrer, per session + +Send the prompts on this page in the assistant you chose on the previous page. They're the same in the Snowplow Assistant and in an MCP-connected assistant, and so are the resources they create. + +:::note[Transcripts are representative] +The transcript excerpts on this page come from a real Claude Code session with the MCP server, condensed for readability. Assistant output varies between runs and between assistants, so the wording you see, and even the steps your assistant takes, will differ. What stays the same are the resources it creates, and those are what you'll verify in Console in the next step. +::: + +## Describe the attribute group + +Write your prompt as a goal, with the details the assistant needs: the group name, the attribute key to aggregate against, and what each attribute should calculate. Asking the assistant not to publish keeps you in control of the review. + +```text +Create a stream attribute group called tut_mcp_session_metrics, keyed on +domain_sessionid, with three attributes calculated from page_view events: + +1. page_view_count: how many page views in the last 15 minutes +2. most_recent_browser: the last seen browser name, from the YAUAA context +3. first_referrer: the first seen referrer host, ignoring empty referrers + +Save it as a draft, don't publish it yet. +``` + +The assistant works out the rest: which aggregation each attribute needs, the attribute types, and the exact event and entity references. A representative response: + +```text +Assistant: +[calls signals_list_attribute_keys] +[calls signals_create_attribute_group] + +Created tut_mcp_session_metrics version 1 as a draft, keyed on the built-in +domain_sessionid attribute key: + +- page_view_count (int32): counter of page_view events over a 15-minute period +- most_recent_browser (string): last aggregation on the agentName property of + the yauaa_context entity +- first_referrer (string): first aggregation on the refr_urlhost atomic field, + with a criteria filter excluding events where page_referrer is empty + +It's not published, so Signals isn't calculating anything yet. +``` + +The `most_recent_browser` attribute relies on the [YAUAA enrichment](/docs/pipeline/enrichments/available-enrichments/yauaa-enrichment/), which attaches a `yauaa_context` entity to each event. If it's not enabled on your pipeline, ask the assistant to check: it can list your enrichment configurations too. + +## Review the draft + +New attribute groups are saved as drafts: definitions in the registry that aren't deployed to any compute engine. Nothing is calculated, and nothing reaches your applications, until you publish. + +Before moving on, review what was actually created rather than trusting the summary. Ask the assistant to show the full definition: + +```text +Show me the full definition of tut_mcp_session_metrics. +``` + +The assistant returns the stored configuration. Check that: + +* The attribute key is `domain_sessionid` +* `page_view_count` uses a `counter` aggregation with a 15-minute period (`PT15M`) +* `most_recent_browser` uses a `last` aggregation on the `yauaa_context` entity's `agentName` property +* `first_referrer` uses a `first` aggregation on `refr_urlhost`, with a criteria filter on `page_referrer` + +You'll confirm the same details in Snowplow Console on the next page. If anything is off, say so in plain language, for example "the period should be 15 minutes, not 15 days", and ask the assistant to update the draft. + +## Test against warehouse data + +Before publishing, you can test what the group would calculate. Testing runs the attribute definitions against recent events in your warehouse's atomic events table and returns sample results, the same calculation as [previewing an attribute group](/tutorials/signals-quickstart/define-attribute-group#test-the-attribute-definitions) in Console. + +```text +Test tut_mcp_session_metrics against recent events before we publish. +``` + +The result is a small table: one row per `domain_sessionid` value found in the sample, with a column for each of the three attributes. Use it to sanity-check the definitions, and iterate conversationally if something looks wrong: + +* If `most_recent_browser` is always null, the YAUAA enrichment probably isn't enabled on your pipeline +* If `first_referrer` is mostly null, your traffic may be mostly direct, so try a test visit that arrives via a link +* If no rows come back at all, there were no page view events in the sampled window, so check that your tracking is sending events + +Adjust the draft as many times as you like: ask for a different period, another filter, or an extra attribute, and test again. + +:::note[Testing requires a warehouse connection] +Signals runs test queries against your warehouse rather than the live stream, so testing attribute definitions requires a warehouse destination connected to your organization. Without one, publish the draft and verify it against live events instead: the next page checks the values Signals calculates. +::: + +## Publish the attribute group + +Once you're satisfied with the definition, publish it: + +```text +Looks good. Publish tut_mcp_session_metrics. +``` + +```text +Assistant: +[calls signals_publish] + +Published tut_mcp_session_metrics version 1. Signals is now calculating the +three attributes from your live event stream. +``` + +Publishing deploys the configuration to the Signals streaming engine. From this moment on, every page view event flowing through your pipeline updates the three attributes for its session. Stream attribute groups only process events from the publish time onwards, unless you configured a [backfill](/docs/signals/attributes/attribute-groups/#backfill-attributes) — see the [configuration workflow](/docs/signals/attributes/#configuration-workflow) for how the lifecycle fits together. + +The assistant says it's published. Next, you'll confirm that independently in Console, and watch the attribute values change in real time. diff --git a/tutorials/signals-mcp/images/console-attribute-group-detail.png b/tutorials/signals-mcp/images/console-attribute-group-detail.png new file mode 100644 index 000000000..2d40caa7e Binary files /dev/null and b/tutorials/signals-mcp/images/console-attribute-group-detail.png differ diff --git a/tutorials/signals-mcp/images/console-attribute-group-four-attributes.png b/tutorials/signals-mcp/images/console-attribute-group-four-attributes.png new file mode 100644 index 000000000..f7bf2a801 Binary files /dev/null and b/tutorials/signals-mcp/images/console-attribute-group-four-attributes.png differ diff --git a/tutorials/signals-mcp/images/console-attribute-groups-list.png b/tutorials/signals-mcp/images/console-attribute-groups-list.png new file mode 100644 index 000000000..982f43f08 Binary files /dev/null and b/tutorials/signals-mcp/images/console-attribute-groups-list.png differ diff --git a/tutorials/signals-mcp/introduction.md b/tutorials/signals-mcp/introduction.md new file mode 100644 index 000000000..1ade6fdb0 --- /dev/null +++ b/tutorials/signals-mcp/introduction.md @@ -0,0 +1,42 @@ +--- +title: "Manage Snowplow Signals conversationally with the Snowplow Assistant or the MCP server" +position: 1 +sidebar_label: "Introduction" +description: "Manage Snowplow Signals conversationally, either with the Snowplow Assistant in Console or with your own MCP-connected assistant: define, test, publish, and verify a real-time attribute group." +keywords: ["snowplow assistant", "snowplow mcp", "model context protocol", "ai assistant", "snowplow signals", "attribute groups"] +date: "2026-08-04" +--- + +You can manage [Signals](/docs/signals/introduction/) by describing what you want in plain language and letting an AI assistant make the API calls for you: define, test, publish, and query real-time attributes conversationally. There are two routes, and this tutorial works with either: + +* The [Snowplow Assistant](/docs/llms-support/console-agent/) built into Snowplow Console, where there's nothing to install +* The [Snowplow MCP server](/docs/llms-support/snowplow-mcp/), which connects the AI assistant you already work in to your Snowplow account + +The prompts are the same either way. In this tutorial you'll build the same attribute group as the [Signals quick start](/tutorials/signals-quickstart/start), conversationally instead of through the Console UI. You'll: + +* Open the Snowplow Assistant in Console, or connect the MCP server to your own assistant +* Define a stream attribute group that calculates three session metrics from page view events +* Test the definition, then publish it to Signals +* Verify the results in Snowplow Console, and watch the live attribute values in the Snowplow Inspector +* Ask for a change, and publish the new version it creates + +The assistant handles the mechanics, but you stay in control: Signals saves new attribute groups as drafts, so nothing is calculated until you review the definition and publish it. Every step ends with a check you can make yourself in Console or in the [Snowplow Inspector](/docs/testing/snowplow-inspector/), rather than relying on the assistant's own description of what it did. + +The transcripts in this tutorial come from a [Claude Code](https://claude.com/product/claude-code) session, because Snowplow ships a plugin that bundles the MCP server with Signals-aware skills. + +This tutorial should take around 20 minutes to complete. + +## Prerequisites + +This tutorial assumes that you have: + +* A Snowplow account with a running pipeline, and page view tracking on a web application, so Signals has events to calculate from +* [Signals enabled](/docs/signals/setup/) on your account +* Either the [Snowplow Assistant](/docs/llms-support/console-agent/) enabled on your organization, or an MCP-capable AI assistant such as Claude Code, Claude Desktop, or Cursor +* The [Snowplow Inspector](/docs/testing/snowplow-inspector/) browser extension, to check the calculated attribute values + +:::note[You need a Snowplow account and pipeline] +Signals computes attributes from real events flowing through your pipeline, so you need a Snowplow account with a running pipeline and Signals enabled. + +If you don't have one, you can deploy and use a [Snowplow free trial](https://snowplow.io/get-started/snowplow-free-trial) to follow along. +::: diff --git a/tutorials/signals-mcp/meta.json b/tutorials/signals-mcp/meta.json new file mode 100644 index 000000000..ea5d563ac --- /dev/null +++ b/tutorials/signals-mcp/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Manage Signals with AI assistants using the Snowplow MCP server", + "description": "Connect the Snowplow MCP server to your AI coding assistant and define, test, and publish Signals attribute groups conversationally.", + "label": "Signals implementation", + "useCase": "Real-time personalization", + "technologies": ["MCP", "Claude Code"], + "snowplowTech": ["Signals", "Console"] +} diff --git a/tutorials/signals-mcp/verify-and-refine.md b/tutorials/signals-mcp/verify-and-refine.md new file mode 100644 index 000000000..ed097080d --- /dev/null +++ b/tutorials/signals-mcp/verify-and-refine.md @@ -0,0 +1,100 @@ +--- +title: "Verify and refine" +position: 4 +sidebar_label: "Verify and refine" +description: "Confirm the conversationally created attribute group in Snowplow Console, watch live attribute values in the Snowplow Inspector, then add an attribute and publish the new version." +keywords: ["verify attribute group", "snowplow console", "snowplow inspector", "attribute values", "attribute group versions"] +date: "2026-08-04" +--- + +The assistant reports success, but the real test is whether the configuration exists in your Snowplow account and produces values. In this step you'll verify both, then make your first conversational edit. + +## Confirm the group in Console + +In [Snowplow Console](https://console.snowplowanalytics.com), go to **Signals** > **Attribute groups**. You should see `tut_mcp_session_metrics` in the list, at version 1, with a published status. + +![The attribute groups list in Console, showing tut_mcp_session_metrics as a published stream group keyed on domain_sessionid with three attributes](./images/console-attribute-groups-list.png) + +Open it and check the definition against what you reviewed in the previous step: + +* Attribute key: `domain_sessionid` +* `page_view_count`: counter of `page_view` events, 15-minute period +* `most_recent_browser`: last `agentName` from the `yauaa_context` entity +* `first_referrer`: first `refr_urlhost`, filtered to non-empty referrers + +![The attribute group detail page, showing the domain_sessionid attribute key and the three attribute definitions with their aggregations and properties](./images/console-attribute-group-detail.png) + +This is the same screen you'd have used to build the group by hand in the [quick start](/tutorials/signals-quickstart/define-attribute-group). Whether a group was created through the Console UI, the Python SDK, or an AI assistant, it lands in the same registry, and Console is always the neutral place to audit what's deployed. + +## Watch the attribute values in Snowplow Inspector + +The configuration is right, but that doesn't prove Signals is calculating anything. To see the real values, use the [Snowplow Inspector](/docs/testing/snowplow-inspector/) browser extension, which reads them from the same Profiles Store your applications will query. + +First, connect the extension to Signals. Open the [Signals integration](/docs/testing/snowplow-inspector/signals-integration/) by logging in to Snowplow Console through the extension: it then detects the Signals instances for every organization you have access to. For full functionality, add API credentials for each organization in the extension's options, which you reach from your browser's extensions menu by selecting **Options** for Snowplow Inspector. + +Once connected, the Inspector uses the Signals API to discover your attribute keys and attribute group definitions, and matches them against the events it observes in the debugger. Because `tut_mcp_session_metrics` is keyed on `domain_sessionid`, the extension collects the `domain_sessionid` values from your page views and requests attributes for each one, so there's no session ID for you to copy by hand. + +Now generate some data. With the Inspector open, browse your tracked website and view a few pages, then switch to the **Attributes** tab in the column of tabs down the left-hand side of the extension. You should see `tut_mcp_session_metrics` listed, with the three attributes and the values calculated for your session: + +* `page_view_count` matching the number of pages you viewed in the last 15 minutes +* `most_recent_browser` showing your browser, such as `Chrome` +* `first_referrer` showing the host you arrived from, or empty if you navigated directly + +Each attribute group in the list shows its organization, environment, version, and source in the top-right, so you can confirm you're looking at the version you just published. Click any attribute row to expand the JSON definition behind it, and click again to collapse it. + +While you stay on the **Attributes** tab, new events refresh the values automatically, with a final refresh five seconds after the events stop, to allow for processing latency. View another page and watch `page_view_count` increase. If the values still look stale, use the refresh button at the top to request them again. + +You can also ask the assistant for the same lookup: give it a session ID from the Inspector's **Events** tab and ask what the current attribute values are, and it reads them from the Profiles Store for you. Reading the values in the Inspector first keeps the verification independent of the assistant that made the change. + +:::note[No values yet?] +Stream attribute groups only calculate from events that arrive after you publish, so events from before that point won't appear. If the list stays empty, check that the extension is observing your page views on the **Events** tab first, and that you added API credentials for the right organization in the extension's options. +::: + +## Add an attribute and publish a new version + +Attribute groups rarely stay finished. Ask the assistant to extend the group: + +```text +Add a most_recent_page_title attribute to tut_mcp_session_metrics: the last +seen page title for the session. +``` + +```text +Assistant: +[calls signals_get_attribute_group] +[calls signals_create_attribute_group] + +Created tut_mcp_session_metrics version 2 as a draft, with the three existing +attributes plus most_recent_page_title (string, last aggregation on the +page_title atomic field). Version 1 is still published and calculating. +``` + +You can't change a published version: once a group is live, editing it produces the next version rather than altering the one in production. So the assistant carries the existing definition over, adds your new attribute, and saves the result as version 2 in draft. Version 1 keeps running untouched while you review, exactly as when you created the group. + +Publish the new version once you're happy with it: + +```text +Looks good. Publish version 2. +``` + +```text +Assistant: +[calls signals_publish] + +Published tut_mcp_session_metrics version 2. Version 1 is still published, so +both versions are calculating now. +``` + +Publishing version 2 doesn't retire version 1. Each version is a separate deployment with its own values in the Profiles Store, and consumers ask for a version by name, as in `tut_mcp_session_metrics_v2:page_view_count`. That's the point of versioning: applications reading version 1 carry on working while you test version 2. Version 2's `page_view_count` starts from zero for each session, because a stream group only counts events that arrive after it's published. Once nothing reads the old version any more, ask the assistant to unpublish it. + +:::note[Versioning is how you edit] +Every attribute group starts at `v1`, and the version increments each time you edit and publish a published group, so nothing in production changes underneath its consumers. Bear this in mind when you name a version in a [service](/docs/signals/applications/services/) or a retrieval call: adding an attribute means there's a newer version to point at. See [versioning](/docs/signals/attributes/attribute-groups/#versioning) for how versions behave. +::: + +Verify the change the same way as before. The new version's page in Console lists four attributes: + +![The attribute group detail page for the new version, with most_recent_page_title added as a fourth attribute](./images/console-attribute-group-four-attributes.png) + +Back in the Inspector, the **Attributes** tab fetches the highest version of each group by default, so after viewing another page you'll see `most_recent_page_title` alongside the original three, with the version shown in the group's top-right corner. If you want to compare, switch to version 1 from the same listing, which appears as an option now that the group has more than one version. + +You've now completed the full loop: defined, tested, published, verified, and extended a Signals attribute group without leaving your assistant, with every change confirmed in Console or in the Inspector.