@sealmetrics/mcp 0.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +149 -0
  2. package/dist/client.d.ts +45 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +136 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/errors.d.ts +10 -0
  7. package/dist/errors.d.ts.map +1 -0
  8. package/dist/errors.js +55 -0
  9. package/dist/errors.js.map +1 -0
  10. package/dist/index.d.ts +1 -6
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +122 -781
  13. package/dist/index.js.map +1 -0
  14. package/dist/resources/tracking-guide.d.ts +13 -0
  15. package/dist/resources/tracking-guide.d.ts.map +1 -0
  16. package/dist/resources/tracking-guide.js +479 -0
  17. package/dist/resources/tracking-guide.js.map +1 -0
  18. package/dist/tools/alerts.d.ts +5 -0
  19. package/dist/tools/alerts.d.ts.map +1 -0
  20. package/dist/tools/alerts.js +80 -0
  21. package/dist/tools/alerts.js.map +1 -0
  22. package/dist/tools/audience.d.ts +7 -0
  23. package/dist/tools/audience.d.ts.map +1 -0
  24. package/dist/tools/audience.js +146 -0
  25. package/dist/tools/audience.js.map +1 -0
  26. package/dist/tools/bots.d.ts +4 -0
  27. package/dist/tools/bots.d.ts.map +1 -0
  28. package/dist/tools/bots.js +52 -0
  29. package/dist/tools/bots.js.map +1 -0
  30. package/dist/tools/channels.d.ts +5 -0
  31. package/dist/tools/channels.d.ts.map +1 -0
  32. package/dist/tools/channels.js +88 -0
  33. package/dist/tools/channels.js.map +1 -0
  34. package/dist/tools/content.d.ts +3 -0
  35. package/dist/tools/content.d.ts.map +1 -0
  36. package/dist/tools/content.js +47 -0
  37. package/dist/tools/content.js.map +1 -0
  38. package/dist/tools/conversions.d.ts +6 -0
  39. package/dist/tools/conversions.d.ts.map +1 -0
  40. package/dist/tools/conversions.js +178 -0
  41. package/dist/tools/conversions.js.map +1 -0
  42. package/dist/tools/funnel.d.ts +3 -0
  43. package/dist/tools/funnel.d.ts.map +1 -0
  44. package/dist/tools/funnel.js +27 -0
  45. package/dist/tools/funnel.js.map +1 -0
  46. package/dist/tools/index.d.ts +16 -0
  47. package/dist/tools/index.d.ts.map +1 -0
  48. package/dist/tools/index.js +79 -0
  49. package/dist/tools/index.js.map +1 -0
  50. package/dist/tools/manage.d.ts +3 -0
  51. package/dist/tools/manage.d.ts.map +1 -0
  52. package/dist/tools/manage.js +55 -0
  53. package/dist/tools/manage.js.map +1 -0
  54. package/dist/tools/overview.d.ts +3 -0
  55. package/dist/tools/overview.d.ts.map +1 -0
  56. package/dist/tools/overview.js +26 -0
  57. package/dist/tools/overview.js.map +1 -0
  58. package/dist/tools/pages.d.ts +7 -0
  59. package/dist/tools/pages.d.ts.map +1 -0
  60. package/dist/tools/pages.js +207 -0
  61. package/dist/tools/pages.js.map +1 -0
  62. package/dist/tools/properties.d.ts +5 -0
  63. package/dist/tools/properties.d.ts.map +1 -0
  64. package/dist/tools/properties.js +107 -0
  65. package/dist/tools/properties.js.map +1 -0
  66. package/dist/tools/segments.d.ts +4 -0
  67. package/dist/tools/segments.d.ts.map +1 -0
  68. package/dist/tools/segments.js +49 -0
  69. package/dist/tools/segments.js.map +1 -0
  70. package/dist/tools/shared.d.ts +45 -0
  71. package/dist/tools/shared.d.ts.map +1 -0
  72. package/dist/tools/shared.js +139 -0
  73. package/dist/tools/shared.js.map +1 -0
  74. package/dist/tools/sites.d.ts +4 -0
  75. package/dist/tools/sites.d.ts.map +1 -0
  76. package/dist/tools/sites.js +36 -0
  77. package/dist/tools/sites.js.map +1 -0
  78. package/dist/tools/tracking.d.ts +3 -0
  79. package/dist/tools/tracking.d.ts.map +1 -0
  80. package/dist/tools/tracking.js +220 -0
  81. package/dist/tools/tracking.js.map +1 -0
  82. package/dist/tools/traffic.d.ts +10 -0
  83. package/dist/tools/traffic.d.ts.map +1 -0
  84. package/dist/tools/traffic.js +273 -0
  85. package/dist/tools/traffic.js.map +1 -0
  86. package/dist/tools/webhooks.d.ts +5 -0
  87. package/dist/tools/webhooks.d.ts.map +1 -0
  88. package/dist/tools/webhooks.js +101 -0
  89. package/dist/tools/webhooks.js.map +1 -0
  90. package/dist/types.d.ts +118 -0
  91. package/dist/types.d.ts.map +1 -0
  92. package/dist/types.js +22 -0
  93. package/dist/types.js.map +1 -0
  94. package/package.json +35 -27
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Static tracking guide exposed as an MCP resource.
3
+ * Claude reads this on connect and knows how to implement SealMetrics tracking
4
+ * without calling any tool.
5
+ *
6
+ * This is an operational playbook, not just an API reference.
7
+ * It tells the agent WHAT to do, HOW to decide, and WHERE to put things.
8
+ */
9
+ export declare const TRACKING_GUIDE_URI = "sealmetrics://tracking-guide";
10
+ export declare const TRACKING_GUIDE_NAME = "SealMetrics Tracking Guide";
11
+ export declare const TRACKING_GUIDE_DESCRIPTION = "Operational playbook for implementing SealMetrics tracking on any website: pixel installation, conversions, microconversions, content grouping, and framework-specific patterns.";
12
+ export declare const TRACKING_GUIDE_CONTENT = "# SealMetrics Tracking \u2014 Implementation Playbook\n\nYou are implementing SealMetrics analytics on a website. This guide tells you exactly what to do, step by step.\n\n---\n\n## Step 1: Get the pixel\n\nCall the `get_tracking_code` tool with the user's `site_id`. It returns:\n- `script_tag`: the exact `<script>` tag to insert (with the real site ID baked in)\n- `tracker_url`: the URL of the tracker JS file\n\nIf you don't know the site_id, call `list_sites` first to find it.\n\n---\n\n## Step 2: Find where to insert the script\n\nThe script tag goes in the `<head>` of every page, **once**. Where that is depends on the framework:\n\n| Framework | Where to put it |\n|-----------|----------------|\n| **Plain HTML** | Inside `<head>` in every `.html` file, or in a shared template/layout |\n| **Next.js (App Router)** | `app/layout.tsx` \u2014 use `import Script from 'next/script'` with `strategy=\"afterInteractive\"` |\n| **Next.js (Pages Router)** | `pages/_app.tsx` \u2014 use `<Script>` component |\n| **React (CRA/Vite)** | `index.html` inside `<head>` |\n| **Vue / Nuxt** | `nuxt.config.ts` head section, or `app.vue` |\n| **Angular** | `src/index.html` inside `<head>` |\n| **Astro** | Shared layout component `<head>` |\n| **WordPress** | `header.php` or via a \"header scripts\" plugin/setting |\n| **Shopify** | `theme.liquid` inside `<head>` |\n\n**How to find it**: Search for `</head>`, or look for the root layout/template file. Every framework has one place that wraps all pages \u2014 that's where the script goes.\n\n### Plain HTML\n```html\n<head>\n <!-- other tags... -->\n <script src=\"https://t.sealmetrics.com/t.js?id=SITE_ID\" defer></script>\n</head>\n```\n\n### Next.js (App Router)\n```tsx\n// app/layout.tsx\nimport Script from 'next/script';\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n return (\n <html lang=\"en\">\n <head />\n <body>\n {children}\n <Script src=\"https://t.sealmetrics.com/t.js?id=SITE_ID\" strategy=\"afterInteractive\" />\n </body>\n </html>\n );\n}\n```\n\n### Vue / Nuxt\n```ts\n// nuxt.config.ts\nexport default defineNuxtConfig({\n app: {\n head: {\n script: [{ src: 'https://t.sealmetrics.com/t.js?id=SITE_ID', defer: true }]\n }\n }\n});\n```\n\n**Once the script is in place, pageviews are tracked automatically** \u2014 on load, on SPA navigation (pushState, replaceState, popstate), on back/forward. You do NOT need to add manual pageview calls.\n\n---\n\n## Step 3: Identify what to track\n\nAnalyze the website code and identify trackeable user actions. Classify each one as a **conversion** or a **microconversion**.\n\n### What is a conversion?\n\nA conversion is a **business goal** \u2014 the main thing the site owner wants users to do. It typically has monetary value or represents a completed transaction.\n\n| What you see in the code | Conversion type | Has value? |\n|--------------------------|-----------------|------------|\n| Purchase/checkout completion, order confirmation page | `purchase` | Yes \u2014 the order total |\n| Subscription payment, plan upgrade | `purchase` | Yes \u2014 the subscription price |\n| Contact form, demo request, quote request | `lead` | No (use `0`) |\n| Account registration, signup form | `signup` | No (use `0`) |\n| Booking confirmation, appointment scheduled | `booking` | Yes if there's a price, otherwise `0` |\n\n**Rule of thumb**: If the business would pay money to make this action happen, it's a conversion.\n\n### What is a microconversion?\n\nA microconversion is a **step toward a conversion** or an **engagement signal**. It tells you how users interact with the site before converting.\n\n| What you see in the code | Microconversion type |\n|--------------------------|---------------------|\n| \"Add to cart\" button | `add_to_cart` |\n| \"Add to wishlist\" / \"Save for later\" | `add_to_wishlist` |\n| Checkout step buttons (shipping, payment...) | `begin_checkout`, `checkout_shipping`, `checkout_payment` |\n| Newsletter/email signup form | `newsletter_signup` |\n| PDF/resource download link | `download` |\n| Video play button | `video_play` |\n| Video ends | `video_complete` |\n| Pricing page visited or pricing toggle clicked | `pricing_view` |\n| Share/social buttons | `share` |\n| Search form used | `search` |\n| Filter/sort applied | `filter_applied` |\n| Tab/accordion clicked to reveal content | `content_expand` |\n| Chat widget opened | `chat_open` |\n| Scroll milestones (25%, 50%, 75%, 100%) | `scroll_25`, `scroll_50`, `scroll_75`, `scroll_100` |\n\n**Rule of thumb**: If it shows intent or engagement but isn't the final goal, it's a microconversion.\n\n---\n\n## Step 4: Implement conversions\n\n### Syntax\n```js\nsealmetrics.conv(type, amount, properties?)\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `type` | string | Yes | snake_case name for this conversion |\n| `amount` | number | Yes | Monetary value. Use `0` if no value (leads, signups) |\n| `properties` | object | No | Extra metadata (see \"Using properties\" below) |\n\n### Where to put it\n\nThe conversion call goes **at the moment the action succeeds**, not when the user clicks. For example:\n\n- **Form submission**: In the `onSubmit` handler, AFTER validation passes (or on the thank-you page)\n- **Purchase**: On the order confirmation/thank-you page, or in the success callback of the payment API\n- **Signup**: After the registration API call succeeds\n\n### Examples by pattern\n\n**Form submit (vanilla JS):**\n```js\ndocument.querySelector('#contact-form').addEventListener('submit', function(e) {\n // The form is about to submit \u2014 track the lead\n sealmetrics.conv('lead', 0, { form_name: 'contact', page: location.pathname });\n});\n```\n\n**Form submit (React):**\n```tsx\nconst handleSubmit = async (data: FormData) => {\n await api.submitContactForm(data);\n window.sealmetrics?.conv('lead', 0, { form_name: 'contact' });\n};\n```\n\n**Purchase (thank-you page):**\n```html\n<!-- This script runs on /order-confirmation or /thank-you -->\n<script>\n // Pull values from the page or from server-rendered data\n sealmetrics.conv('purchase', 149.99, {\n currency: 'EUR',\n payment_method: 'credit_card'\n });\n</script>\n```\n\n**Purchase (React, after payment API):**\n```tsx\nconst handlePayment = async () => {\n const result = await processPayment(cart);\n if (result.success) {\n window.sealmetrics?.conv('purchase', cart.total, {\n currency: cart.currency,\n payment_method: result.method\n });\n router.push('/thank-you');\n }\n};\n```\n\n**Signup (after API success):**\n```tsx\nconst handleRegister = async (formData: RegisterForm) => {\n const user = await api.register(formData);\n window.sealmetrics?.conv('signup', 0, { plan: formData.plan });\n router.push('/welcome');\n};\n```\n\n---\n\n## Step 5: Implement microconversions\n\n### Syntax\n```js\nsealmetrics.micro(type, properties?)\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `type` | string | Yes | snake_case name for this event |\n| `properties` | object | No | Extra metadata (see \"Using properties\" below) |\n\n### Where to put it\n\nMicroconversions go **on the user action** \u2014 usually in click handlers, submit handlers, or event listeners.\n\n### Examples by pattern\n\n**Add to cart (vanilla):**\n```js\ndocument.querySelectorAll('.add-to-cart').forEach(function(btn) {\n btn.addEventListener('click', function() {\n sealmetrics.micro('add_to_cart', {\n product_id: this.dataset.productId,\n product_name: this.dataset.productName,\n price: parseFloat(this.dataset.price)\n });\n });\n});\n```\n\n**Add to cart (React):**\n```tsx\nfunction AddToCartButton({ product }: { product: Product }) {\n const handleClick = () => {\n addToCart(product);\n window.sealmetrics?.micro('add_to_cart', {\n product_id: product.id,\n product_name: product.name,\n price: product.price\n });\n };\n return <button onClick={handleClick}>Add to Cart</button>;\n}\n```\n\n**Newsletter signup:**\n```js\ndocument.querySelector('#newsletter-form').addEventListener('submit', function() {\n sealmetrics.micro('newsletter_signup', {\n position: this.closest('footer') ? 'footer' : 'inline'\n });\n});\n```\n\n**Video engagement:**\n```js\nvar video = document.querySelector('video');\nvideo.addEventListener('play', function() {\n sealmetrics.micro('video_play', { video_id: this.dataset.videoId });\n});\nvideo.addEventListener('ended', function() {\n sealmetrics.micro('video_complete', { video_id: this.dataset.videoId });\n});\n```\n\n**Scroll depth tracking:**\n```js\nvar scrollTracked = {};\nwindow.addEventListener('scroll', function() {\n var pct = Math.round(window.scrollY / (document.body.scrollHeight - window.innerHeight) * 100);\n [25, 50, 75, 100].forEach(function(milestone) {\n if (pct >= milestone && !scrollTracked[milestone]) {\n scrollTracked[milestone] = true;\n sealmetrics.micro('scroll_' + milestone);\n }\n });\n});\n```\n\n**Checkout funnel steps (React):**\n```tsx\n// When user moves from step to step\nconst goToStep = (step: number) => {\n setCurrentStep(step);\n const stepNames: Record<number, string> = {\n 1: 'begin_checkout',\n 2: 'checkout_shipping',\n 3: 'checkout_payment',\n };\n if (stepNames[step]) {\n window.sealmetrics?.micro(stepNames[step], { items_count: cart.items.length });\n }\n};\n```\n\n---\n\n## Step 6: Set up content grouping\n\nContent grouping lets the site owner analyze metrics by section: \"how does the blog perform vs product pages?\"\n\n### When to use it\n\nUse content grouping when the site has **distinct sections** that serve different purposes. If the site is a single-purpose landing page, skip it.\n\n### How to decide groups\n\nLook at the URL structure and page purpose:\n\n| URL pattern | Group |\n|-------------|-------|\n| `/blog/*`, `/posts/*`, `/articles/*` | `blog` |\n| `/products/*`, `/shop/*`, `/item/*` | `product` |\n| `/category/*`, `/collections/*` | `category` |\n| `/cart`, `/checkout/*`, `/order/*` | `checkout` |\n| `/docs/*`, `/help/*`, `/faq` | `docs` |\n| `/dashboard/*`, `/app/*`, `/account/*` | `app` |\n| `/pricing`, `/plans` | `pricing` |\n| `/`, `/about`, `/features`, `/contact` | `landing` |\n\n### How to implement it\n\n**Option A \u2014 Static (via URL param in the script tag):**\n\nBest when you can set a different script tag per section (e.g., different templates, layouts).\n\n```html\n<!-- Blog layout -->\n<script src=\"https://t.sealmetrics.com/t.js?id=SITE_ID&group=blog\" defer></script>\n\n<!-- Product layout -->\n<script src=\"https://t.sealmetrics.com/t.js?id=SITE_ID&group=product\" defer></script>\n```\n\n**Option B \u2014 Dynamic (via JS, based on URL):**\n\nBest for SPAs or when you can't change the script tag per section.\n\n```js\n// Determine group from the current path\nfunction getContentGroup() {\n var path = location.pathname;\n if (path.startsWith('/blog') || path.startsWith('/posts')) return 'blog';\n if (path.startsWith('/products') || path.startsWith('/shop')) return 'product';\n if (path.startsWith('/cart') || path.startsWith('/checkout')) return 'checkout';\n if (path.startsWith('/docs') || path.startsWith('/help')) return 'docs';\n return 'landing';\n}\n\nsealmetrics({ group: getContentGroup() });\n```\n\n**Option C \u2014 Next.js (per layout segment):**\n```tsx\n// app/blog/layout.tsx\nimport Script from 'next/script';\nexport default function BlogLayout({ children }: { children: React.ReactNode }) {\n return (\n <>\n <Script src=\"https://t.sealmetrics.com/t.js?id=SITE_ID&group=blog\" strategy=\"afterInteractive\" />\n {children}\n </>\n );\n}\n```\nNote: if you use per-layout scripts with groups, remove the global script from the root layout to avoid double-tracking.\n\n---\n\n## Using properties effectively\n\nProperties are key-value metadata attached to conversions and microconversions. They power drill-down analysis in the dashboard.\n\n### What to include\n\n| Property | When to use | Example |\n|----------|------------|---------|\n| `currency` | Any monetary conversion | `'EUR'`, `'USD'` |\n| `payment_method` | Purchase conversions | `'credit_card'`, `'paypal'`, `'stripe'` |\n| `plan` | Signup/subscription conversions | `'free'`, `'pro'`, `'enterprise'` |\n| `product_id` | Product-related events | `'SKU-123'` |\n| `product_name` | Product-related events | `'Wireless Headphones'` |\n| `price` | Add-to-cart, wishlist | `49.99` |\n| `category` | Product events | `'electronics'`, `'shoes'` |\n| `form_name` | Lead/form conversions | `'contact'`, `'demo_request'`, `'quote'` |\n| `position` | UI element interactions | `'header'`, `'footer'`, `'popup'`, `'sidebar'` |\n| `video_id` | Video events | `'intro-video'`, `'product-demo'` |\n| `search_term` | Search events | The user's search query |\n\n### What NOT to include\n\n- **Order IDs, user IDs, transaction IDs** \u2014 these are high-cardinality and don't help analysis. Never put them in the type name either.\n- **Email addresses, names, or PII** \u2014 SealMetrics is privacy-first.\n- **Timestamps** \u2014 the system already records when events happen.\n- **Page URLs** \u2014 already tracked automatically.\n\n### Property values\n\n- Keep values **low-cardinality** when possible: `'credit_card'` not `'visa_ending_4242'`\n- Use **consistent values**: always `'credit_card'`, never sometimes `'cc'` and sometimes `'Credit Card'`\n- **Numbers** should be numbers, not strings: `price: 49.99`, not `price: '49.99'`\n\n---\n\n## Naming conventions\n\n- **snake_case** always: `add_to_cart`, not `addToCart` or `Add To Cart`\n- **Descriptive**: `begin_checkout` not `step2`, `newsletter_signup` not `nl`\n- **Stable**: once you name a type, don't rename it \u2014 it breaks historical data\n- **No IDs in the type name**: `sealmetrics.conv('purchase', 99)`, not `sealmetrics.conv('purchase_order_12345', 99)`\n\n---\n\n## TypeScript type declarations\n\nIf the project uses TypeScript, add this declaration so `window.sealmetrics` doesn't show type errors:\n\n```ts\n// types/sealmetrics.d.ts (or at the top of a component file)\ndeclare global {\n interface Window {\n sealmetrics?: {\n (options?: { group?: string }): void;\n conv: (type: string, amount: number, properties?: Record<string, unknown>) => void;\n micro: (type: string, properties?: Record<string, unknown>) => void;\n };\n }\n}\n```\n\nThen call via `window.sealmetrics?.conv(...)` instead of `sealmetrics.conv(...)`.\n\n---\n\n## Complete implementation checklist\n\nUse this to verify you haven't missed anything:\n\n1. **Pixel installed**: Script tag is in the `<head>` (or root layout) of every page \u2014 ONE time only\n2. **Pageviews working**: Automatic, no code needed. Verify with `?debug=1`\n3. **Conversions identified**: Every business goal (purchase, lead, signup) has a `sealmetrics.conv()` call\n4. **Conversion placement**: Each conversion fires at the right moment (after success, not on click)\n5. **Conversion values**: Purchases have the real `amount`, leads/signups use `0`\n6. **Microconversions identified**: Funnel steps and engagement signals have `sealmetrics.micro()` calls\n7. **Properties added**: Events carry useful metadata (currency, product_id, form_name, etc.)\n8. **Content grouping**: If the site has distinct sections, groups are assigned\n9. **Naming**: All types are snake_case, descriptive, and stable\n10. **No PII**: No emails, user IDs, or personal data in properties\n\n---\n\n## Debugging\n\nAdd `?debug=1` to any page URL to see all tracking events in the browser console:\n\n```\nhttps://yoursite.com/products?debug=1\n```\n\nShows: session ID, account ID, event type, payload, and any errors.\n\n---\n\n## Technical notes\n\n- **No cookies, no localStorage** \u2014 GDPR-friendly, no consent banner needed\n- **SPA support is automatic** \u2014 History API (pushState/replaceState/popstate) is intercepted\n- **Uses sendBeacon** \u2014 non-blocking, guaranteed delivery even on tab close\n- **Three equivalent globals**: `sealmetrics`, `sm`, `_sm` \u2014 use `sealmetrics` in new code\n- **Script loads async** with `defer` \u2014 never blocks page rendering\n";
13
+ //# sourceMappingURL=tracking-guide.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracking-guide.d.ts","sourceRoot":"","sources":["../../src/resources/tracking-guide.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,iCAAiC,CAAC;AACjE,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,0BAA0B,qLAC6I,CAAC;AAErL,eAAO,MAAM,sBAAsB,gigBAkdlC,CAAC"}
@@ -0,0 +1,479 @@
1
+ /**
2
+ * Static tracking guide exposed as an MCP resource.
3
+ * Claude reads this on connect and knows how to implement SealMetrics tracking
4
+ * without calling any tool.
5
+ *
6
+ * This is an operational playbook, not just an API reference.
7
+ * It tells the agent WHAT to do, HOW to decide, and WHERE to put things.
8
+ */
9
+ export const TRACKING_GUIDE_URI = "sealmetrics://tracking-guide";
10
+ export const TRACKING_GUIDE_NAME = "SealMetrics Tracking Guide";
11
+ export const TRACKING_GUIDE_DESCRIPTION = "Operational playbook for implementing SealMetrics tracking on any website: pixel installation, conversions, microconversions, content grouping, and framework-specific patterns.";
12
+ export const TRACKING_GUIDE_CONTENT = `# SealMetrics Tracking — Implementation Playbook
13
+
14
+ You are implementing SealMetrics analytics on a website. This guide tells you exactly what to do, step by step.
15
+
16
+ ---
17
+
18
+ ## Step 1: Get the pixel
19
+
20
+ Call the \`get_tracking_code\` tool with the user's \`site_id\`. It returns:
21
+ - \`script_tag\`: the exact \`<script>\` tag to insert (with the real site ID baked in)
22
+ - \`tracker_url\`: the URL of the tracker JS file
23
+
24
+ If you don't know the site_id, call \`list_sites\` first to find it.
25
+
26
+ ---
27
+
28
+ ## Step 2: Find where to insert the script
29
+
30
+ The script tag goes in the \`<head>\` of every page, **once**. Where that is depends on the framework:
31
+
32
+ | Framework | Where to put it |
33
+ |-----------|----------------|
34
+ | **Plain HTML** | Inside \`<head>\` in every \`.html\` file, or in a shared template/layout |
35
+ | **Next.js (App Router)** | \`app/layout.tsx\` — use \`import Script from 'next/script'\` with \`strategy="afterInteractive"\` |
36
+ | **Next.js (Pages Router)** | \`pages/_app.tsx\` — use \`<Script>\` component |
37
+ | **React (CRA/Vite)** | \`index.html\` inside \`<head>\` |
38
+ | **Vue / Nuxt** | \`nuxt.config.ts\` head section, or \`app.vue\` |
39
+ | **Angular** | \`src/index.html\` inside \`<head>\` |
40
+ | **Astro** | Shared layout component \`<head>\` |
41
+ | **WordPress** | \`header.php\` or via a "header scripts" plugin/setting |
42
+ | **Shopify** | \`theme.liquid\` inside \`<head>\` |
43
+
44
+ **How to find it**: Search for \`</head>\`, or look for the root layout/template file. Every framework has one place that wraps all pages — that's where the script goes.
45
+
46
+ ### Plain HTML
47
+ \`\`\`html
48
+ <head>
49
+ <!-- other tags... -->
50
+ <script src="https://t.sealmetrics.com/t.js?id=SITE_ID" defer></script>
51
+ </head>
52
+ \`\`\`
53
+
54
+ ### Next.js (App Router)
55
+ \`\`\`tsx
56
+ // app/layout.tsx
57
+ import Script from 'next/script';
58
+
59
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
60
+ return (
61
+ <html lang="en">
62
+ <head />
63
+ <body>
64
+ {children}
65
+ <Script src="https://t.sealmetrics.com/t.js?id=SITE_ID" strategy="afterInteractive" />
66
+ </body>
67
+ </html>
68
+ );
69
+ }
70
+ \`\`\`
71
+
72
+ ### Vue / Nuxt
73
+ \`\`\`ts
74
+ // nuxt.config.ts
75
+ export default defineNuxtConfig({
76
+ app: {
77
+ head: {
78
+ script: [{ src: 'https://t.sealmetrics.com/t.js?id=SITE_ID', defer: true }]
79
+ }
80
+ }
81
+ });
82
+ \`\`\`
83
+
84
+ **Once the script is in place, pageviews are tracked automatically** — on load, on SPA navigation (pushState, replaceState, popstate), on back/forward. You do NOT need to add manual pageview calls.
85
+
86
+ ---
87
+
88
+ ## Step 3: Identify what to track
89
+
90
+ Analyze the website code and identify trackeable user actions. Classify each one as a **conversion** or a **microconversion**.
91
+
92
+ ### What is a conversion?
93
+
94
+ A conversion is a **business goal** — the main thing the site owner wants users to do. It typically has monetary value or represents a completed transaction.
95
+
96
+ | What you see in the code | Conversion type | Has value? |
97
+ |--------------------------|-----------------|------------|
98
+ | Purchase/checkout completion, order confirmation page | \`purchase\` | Yes — the order total |
99
+ | Subscription payment, plan upgrade | \`purchase\` | Yes — the subscription price |
100
+ | Contact form, demo request, quote request | \`lead\` | No (use \`0\`) |
101
+ | Account registration, signup form | \`signup\` | No (use \`0\`) |
102
+ | Booking confirmation, appointment scheduled | \`booking\` | Yes if there's a price, otherwise \`0\` |
103
+
104
+ **Rule of thumb**: If the business would pay money to make this action happen, it's a conversion.
105
+
106
+ ### What is a microconversion?
107
+
108
+ A microconversion is a **step toward a conversion** or an **engagement signal**. It tells you how users interact with the site before converting.
109
+
110
+ | What you see in the code | Microconversion type |
111
+ |--------------------------|---------------------|
112
+ | "Add to cart" button | \`add_to_cart\` |
113
+ | "Add to wishlist" / "Save for later" | \`add_to_wishlist\` |
114
+ | Checkout step buttons (shipping, payment...) | \`begin_checkout\`, \`checkout_shipping\`, \`checkout_payment\` |
115
+ | Newsletter/email signup form | \`newsletter_signup\` |
116
+ | PDF/resource download link | \`download\` |
117
+ | Video play button | \`video_play\` |
118
+ | Video ends | \`video_complete\` |
119
+ | Pricing page visited or pricing toggle clicked | \`pricing_view\` |
120
+ | Share/social buttons | \`share\` |
121
+ | Search form used | \`search\` |
122
+ | Filter/sort applied | \`filter_applied\` |
123
+ | Tab/accordion clicked to reveal content | \`content_expand\` |
124
+ | Chat widget opened | \`chat_open\` |
125
+ | Scroll milestones (25%, 50%, 75%, 100%) | \`scroll_25\`, \`scroll_50\`, \`scroll_75\`, \`scroll_100\` |
126
+
127
+ **Rule of thumb**: If it shows intent or engagement but isn't the final goal, it's a microconversion.
128
+
129
+ ---
130
+
131
+ ## Step 4: Implement conversions
132
+
133
+ ### Syntax
134
+ \`\`\`js
135
+ sealmetrics.conv(type, amount, properties?)
136
+ \`\`\`
137
+
138
+ | Parameter | Type | Required | Description |
139
+ |-----------|------|----------|-------------|
140
+ | \`type\` | string | Yes | snake_case name for this conversion |
141
+ | \`amount\` | number | Yes | Monetary value. Use \`0\` if no value (leads, signups) |
142
+ | \`properties\` | object | No | Extra metadata (see "Using properties" below) |
143
+
144
+ ### Where to put it
145
+
146
+ The conversion call goes **at the moment the action succeeds**, not when the user clicks. For example:
147
+
148
+ - **Form submission**: In the \`onSubmit\` handler, AFTER validation passes (or on the thank-you page)
149
+ - **Purchase**: On the order confirmation/thank-you page, or in the success callback of the payment API
150
+ - **Signup**: After the registration API call succeeds
151
+
152
+ ### Examples by pattern
153
+
154
+ **Form submit (vanilla JS):**
155
+ \`\`\`js
156
+ document.querySelector('#contact-form').addEventListener('submit', function(e) {
157
+ // The form is about to submit — track the lead
158
+ sealmetrics.conv('lead', 0, { form_name: 'contact', page: location.pathname });
159
+ });
160
+ \`\`\`
161
+
162
+ **Form submit (React):**
163
+ \`\`\`tsx
164
+ const handleSubmit = async (data: FormData) => {
165
+ await api.submitContactForm(data);
166
+ window.sealmetrics?.conv('lead', 0, { form_name: 'contact' });
167
+ };
168
+ \`\`\`
169
+
170
+ **Purchase (thank-you page):**
171
+ \`\`\`html
172
+ <!-- This script runs on /order-confirmation or /thank-you -->
173
+ <script>
174
+ // Pull values from the page or from server-rendered data
175
+ sealmetrics.conv('purchase', 149.99, {
176
+ currency: 'EUR',
177
+ payment_method: 'credit_card'
178
+ });
179
+ </script>
180
+ \`\`\`
181
+
182
+ **Purchase (React, after payment API):**
183
+ \`\`\`tsx
184
+ const handlePayment = async () => {
185
+ const result = await processPayment(cart);
186
+ if (result.success) {
187
+ window.sealmetrics?.conv('purchase', cart.total, {
188
+ currency: cart.currency,
189
+ payment_method: result.method
190
+ });
191
+ router.push('/thank-you');
192
+ }
193
+ };
194
+ \`\`\`
195
+
196
+ **Signup (after API success):**
197
+ \`\`\`tsx
198
+ const handleRegister = async (formData: RegisterForm) => {
199
+ const user = await api.register(formData);
200
+ window.sealmetrics?.conv('signup', 0, { plan: formData.plan });
201
+ router.push('/welcome');
202
+ };
203
+ \`\`\`
204
+
205
+ ---
206
+
207
+ ## Step 5: Implement microconversions
208
+
209
+ ### Syntax
210
+ \`\`\`js
211
+ sealmetrics.micro(type, properties?)
212
+ \`\`\`
213
+
214
+ | Parameter | Type | Required | Description |
215
+ |-----------|------|----------|-------------|
216
+ | \`type\` | string | Yes | snake_case name for this event |
217
+ | \`properties\` | object | No | Extra metadata (see "Using properties" below) |
218
+
219
+ ### Where to put it
220
+
221
+ Microconversions go **on the user action** — usually in click handlers, submit handlers, or event listeners.
222
+
223
+ ### Examples by pattern
224
+
225
+ **Add to cart (vanilla):**
226
+ \`\`\`js
227
+ document.querySelectorAll('.add-to-cart').forEach(function(btn) {
228
+ btn.addEventListener('click', function() {
229
+ sealmetrics.micro('add_to_cart', {
230
+ product_id: this.dataset.productId,
231
+ product_name: this.dataset.productName,
232
+ price: parseFloat(this.dataset.price)
233
+ });
234
+ });
235
+ });
236
+ \`\`\`
237
+
238
+ **Add to cart (React):**
239
+ \`\`\`tsx
240
+ function AddToCartButton({ product }: { product: Product }) {
241
+ const handleClick = () => {
242
+ addToCart(product);
243
+ window.sealmetrics?.micro('add_to_cart', {
244
+ product_id: product.id,
245
+ product_name: product.name,
246
+ price: product.price
247
+ });
248
+ };
249
+ return <button onClick={handleClick}>Add to Cart</button>;
250
+ }
251
+ \`\`\`
252
+
253
+ **Newsletter signup:**
254
+ \`\`\`js
255
+ document.querySelector('#newsletter-form').addEventListener('submit', function() {
256
+ sealmetrics.micro('newsletter_signup', {
257
+ position: this.closest('footer') ? 'footer' : 'inline'
258
+ });
259
+ });
260
+ \`\`\`
261
+
262
+ **Video engagement:**
263
+ \`\`\`js
264
+ var video = document.querySelector('video');
265
+ video.addEventListener('play', function() {
266
+ sealmetrics.micro('video_play', { video_id: this.dataset.videoId });
267
+ });
268
+ video.addEventListener('ended', function() {
269
+ sealmetrics.micro('video_complete', { video_id: this.dataset.videoId });
270
+ });
271
+ \`\`\`
272
+
273
+ **Scroll depth tracking:**
274
+ \`\`\`js
275
+ var scrollTracked = {};
276
+ window.addEventListener('scroll', function() {
277
+ var pct = Math.round(window.scrollY / (document.body.scrollHeight - window.innerHeight) * 100);
278
+ [25, 50, 75, 100].forEach(function(milestone) {
279
+ if (pct >= milestone && !scrollTracked[milestone]) {
280
+ scrollTracked[milestone] = true;
281
+ sealmetrics.micro('scroll_' + milestone);
282
+ }
283
+ });
284
+ });
285
+ \`\`\`
286
+
287
+ **Checkout funnel steps (React):**
288
+ \`\`\`tsx
289
+ // When user moves from step to step
290
+ const goToStep = (step: number) => {
291
+ setCurrentStep(step);
292
+ const stepNames: Record<number, string> = {
293
+ 1: 'begin_checkout',
294
+ 2: 'checkout_shipping',
295
+ 3: 'checkout_payment',
296
+ };
297
+ if (stepNames[step]) {
298
+ window.sealmetrics?.micro(stepNames[step], { items_count: cart.items.length });
299
+ }
300
+ };
301
+ \`\`\`
302
+
303
+ ---
304
+
305
+ ## Step 6: Set up content grouping
306
+
307
+ Content grouping lets the site owner analyze metrics by section: "how does the blog perform vs product pages?"
308
+
309
+ ### When to use it
310
+
311
+ Use content grouping when the site has **distinct sections** that serve different purposes. If the site is a single-purpose landing page, skip it.
312
+
313
+ ### How to decide groups
314
+
315
+ Look at the URL structure and page purpose:
316
+
317
+ | URL pattern | Group |
318
+ |-------------|-------|
319
+ | \`/blog/*\`, \`/posts/*\`, \`/articles/*\` | \`blog\` |
320
+ | \`/products/*\`, \`/shop/*\`, \`/item/*\` | \`product\` |
321
+ | \`/category/*\`, \`/collections/*\` | \`category\` |
322
+ | \`/cart\`, \`/checkout/*\`, \`/order/*\` | \`checkout\` |
323
+ | \`/docs/*\`, \`/help/*\`, \`/faq\` | \`docs\` |
324
+ | \`/dashboard/*\`, \`/app/*\`, \`/account/*\` | \`app\` |
325
+ | \`/pricing\`, \`/plans\` | \`pricing\` |
326
+ | \`/\`, \`/about\`, \`/features\`, \`/contact\` | \`landing\` |
327
+
328
+ ### How to implement it
329
+
330
+ **Option A — Static (via URL param in the script tag):**
331
+
332
+ Best when you can set a different script tag per section (e.g., different templates, layouts).
333
+
334
+ \`\`\`html
335
+ <!-- Blog layout -->
336
+ <script src="https://t.sealmetrics.com/t.js?id=SITE_ID&group=blog" defer></script>
337
+
338
+ <!-- Product layout -->
339
+ <script src="https://t.sealmetrics.com/t.js?id=SITE_ID&group=product" defer></script>
340
+ \`\`\`
341
+
342
+ **Option B — Dynamic (via JS, based on URL):**
343
+
344
+ Best for SPAs or when you can't change the script tag per section.
345
+
346
+ \`\`\`js
347
+ // Determine group from the current path
348
+ function getContentGroup() {
349
+ var path = location.pathname;
350
+ if (path.startsWith('/blog') || path.startsWith('/posts')) return 'blog';
351
+ if (path.startsWith('/products') || path.startsWith('/shop')) return 'product';
352
+ if (path.startsWith('/cart') || path.startsWith('/checkout')) return 'checkout';
353
+ if (path.startsWith('/docs') || path.startsWith('/help')) return 'docs';
354
+ return 'landing';
355
+ }
356
+
357
+ sealmetrics({ group: getContentGroup() });
358
+ \`\`\`
359
+
360
+ **Option C — Next.js (per layout segment):**
361
+ \`\`\`tsx
362
+ // app/blog/layout.tsx
363
+ import Script from 'next/script';
364
+ export default function BlogLayout({ children }: { children: React.ReactNode }) {
365
+ return (
366
+ <>
367
+ <Script src="https://t.sealmetrics.com/t.js?id=SITE_ID&group=blog" strategy="afterInteractive" />
368
+ {children}
369
+ </>
370
+ );
371
+ }
372
+ \`\`\`
373
+ Note: if you use per-layout scripts with groups, remove the global script from the root layout to avoid double-tracking.
374
+
375
+ ---
376
+
377
+ ## Using properties effectively
378
+
379
+ Properties are key-value metadata attached to conversions and microconversions. They power drill-down analysis in the dashboard.
380
+
381
+ ### What to include
382
+
383
+ | Property | When to use | Example |
384
+ |----------|------------|---------|
385
+ | \`currency\` | Any monetary conversion | \`'EUR'\`, \`'USD'\` |
386
+ | \`payment_method\` | Purchase conversions | \`'credit_card'\`, \`'paypal'\`, \`'stripe'\` |
387
+ | \`plan\` | Signup/subscription conversions | \`'free'\`, \`'pro'\`, \`'enterprise'\` |
388
+ | \`product_id\` | Product-related events | \`'SKU-123'\` |
389
+ | \`product_name\` | Product-related events | \`'Wireless Headphones'\` |
390
+ | \`price\` | Add-to-cart, wishlist | \`49.99\` |
391
+ | \`category\` | Product events | \`'electronics'\`, \`'shoes'\` |
392
+ | \`form_name\` | Lead/form conversions | \`'contact'\`, \`'demo_request'\`, \`'quote'\` |
393
+ | \`position\` | UI element interactions | \`'header'\`, \`'footer'\`, \`'popup'\`, \`'sidebar'\` |
394
+ | \`video_id\` | Video events | \`'intro-video'\`, \`'product-demo'\` |
395
+ | \`search_term\` | Search events | The user's search query |
396
+
397
+ ### What NOT to include
398
+
399
+ - **Order IDs, user IDs, transaction IDs** — these are high-cardinality and don't help analysis. Never put them in the type name either.
400
+ - **Email addresses, names, or PII** — SealMetrics is privacy-first.
401
+ - **Timestamps** — the system already records when events happen.
402
+ - **Page URLs** — already tracked automatically.
403
+
404
+ ### Property values
405
+
406
+ - Keep values **low-cardinality** when possible: \`'credit_card'\` not \`'visa_ending_4242'\`
407
+ - Use **consistent values**: always \`'credit_card'\`, never sometimes \`'cc'\` and sometimes \`'Credit Card'\`
408
+ - **Numbers** should be numbers, not strings: \`price: 49.99\`, not \`price: '49.99'\`
409
+
410
+ ---
411
+
412
+ ## Naming conventions
413
+
414
+ - **snake_case** always: \`add_to_cart\`, not \`addToCart\` or \`Add To Cart\`
415
+ - **Descriptive**: \`begin_checkout\` not \`step2\`, \`newsletter_signup\` not \`nl\`
416
+ - **Stable**: once you name a type, don't rename it — it breaks historical data
417
+ - **No IDs in the type name**: \`sealmetrics.conv('purchase', 99)\`, not \`sealmetrics.conv('purchase_order_12345', 99)\`
418
+
419
+ ---
420
+
421
+ ## TypeScript type declarations
422
+
423
+ If the project uses TypeScript, add this declaration so \`window.sealmetrics\` doesn't show type errors:
424
+
425
+ \`\`\`ts
426
+ // types/sealmetrics.d.ts (or at the top of a component file)
427
+ declare global {
428
+ interface Window {
429
+ sealmetrics?: {
430
+ (options?: { group?: string }): void;
431
+ conv: (type: string, amount: number, properties?: Record<string, unknown>) => void;
432
+ micro: (type: string, properties?: Record<string, unknown>) => void;
433
+ };
434
+ }
435
+ }
436
+ \`\`\`
437
+
438
+ Then call via \`window.sealmetrics?.conv(...)\` instead of \`sealmetrics.conv(...)\`.
439
+
440
+ ---
441
+
442
+ ## Complete implementation checklist
443
+
444
+ Use this to verify you haven't missed anything:
445
+
446
+ 1. **Pixel installed**: Script tag is in the \`<head>\` (or root layout) of every page — ONE time only
447
+ 2. **Pageviews working**: Automatic, no code needed. Verify with \`?debug=1\`
448
+ 3. **Conversions identified**: Every business goal (purchase, lead, signup) has a \`sealmetrics.conv()\` call
449
+ 4. **Conversion placement**: Each conversion fires at the right moment (after success, not on click)
450
+ 5. **Conversion values**: Purchases have the real \`amount\`, leads/signups use \`0\`
451
+ 6. **Microconversions identified**: Funnel steps and engagement signals have \`sealmetrics.micro()\` calls
452
+ 7. **Properties added**: Events carry useful metadata (currency, product_id, form_name, etc.)
453
+ 8. **Content grouping**: If the site has distinct sections, groups are assigned
454
+ 9. **Naming**: All types are snake_case, descriptive, and stable
455
+ 10. **No PII**: No emails, user IDs, or personal data in properties
456
+
457
+ ---
458
+
459
+ ## Debugging
460
+
461
+ Add \`?debug=1\` to any page URL to see all tracking events in the browser console:
462
+
463
+ \`\`\`
464
+ https://yoursite.com/products?debug=1
465
+ \`\`\`
466
+
467
+ Shows: session ID, account ID, event type, payload, and any errors.
468
+
469
+ ---
470
+
471
+ ## Technical notes
472
+
473
+ - **No cookies, no localStorage** — GDPR-friendly, no consent banner needed
474
+ - **SPA support is automatic** — History API (pushState/replaceState/popstate) is intercepted
475
+ - **Uses sendBeacon** — non-blocking, guaranteed delivery even on tab close
476
+ - **Three equivalent globals**: \`sealmetrics\`, \`sm\`, \`_sm\` — use \`sealmetrics\` in new code
477
+ - **Script loads async** with \`defer\` — never blocks page rendering
478
+ `;
479
+ //# sourceMappingURL=tracking-guide.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracking-guide.js","sourceRoot":"","sources":["../../src/resources/tracking-guide.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,8BAA8B,CAAC;AACjE,MAAM,CAAC,MAAM,mBAAmB,GAAG,4BAA4B,CAAC;AAChE,MAAM,CAAC,MAAM,0BAA0B,GACrC,kLAAkL,CAAC;AAErL,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkdrC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { ToolDef } from "./index.js";
2
+ export declare const listAlertsTool: ToolDef;
3
+ export declare const getAlertHistoryTool: ToolDef;
4
+ export declare const getAlertStatsTool: ToolDef;
5
+ //# sourceMappingURL=alerts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../../src/tools/alerts.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,eAAO,MAAM,cAAc,EAAE,OAuB5B,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,OAqCjC,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,OAkB/B,CAAC"}
@@ -0,0 +1,80 @@
1
+ import { LIMIT_SCHEMA, resolveSiteId } from "./shared.js";
2
+ export const listAlertsTool = {
3
+ name: "list_alerts",
4
+ description: "List alert rules configured for a site. Shows rule name, metric, condition, threshold, and status (active/paused). Alerts monitor metrics and trigger notifications when thresholds are crossed.",
5
+ inputSchema: {
6
+ type: "object",
7
+ properties: {
8
+ site_id: {
9
+ type: "string",
10
+ description: "Site ID (account_id). Optional if SEALMETRICS_SITE_ID env var is set.",
11
+ },
12
+ include_inactive: {
13
+ type: "boolean",
14
+ description: "Include inactive/paused alert rules (default: false).",
15
+ },
16
+ },
17
+ },
18
+ handler: async (client, args) => {
19
+ return client.requestDirect("/alerts/rules", {
20
+ account_id: resolveSiteId(args),
21
+ include_inactive: args.include_inactive != null ? String(args.include_inactive) : undefined,
22
+ });
23
+ },
24
+ };
25
+ export const getAlertHistoryTool = {
26
+ name: "get_alert_history",
27
+ description: "Get history of triggered alerts: when they fired, current status (active/resolved/acknowledged), and which rule triggered them. Useful for reviewing past incidents.",
28
+ inputSchema: {
29
+ type: "object",
30
+ properties: {
31
+ site_id: {
32
+ type: "string",
33
+ description: "Site ID (account_id). Optional if SEALMETRICS_SITE_ID env var is set.",
34
+ },
35
+ rule_id: {
36
+ type: "number",
37
+ description: "Filter by specific alert rule ID.",
38
+ },
39
+ status: {
40
+ type: "string",
41
+ description: "Filter by alert status.",
42
+ enum: ["active", "resolved", "acknowledged"],
43
+ },
44
+ limit: LIMIT_SCHEMA,
45
+ offset: {
46
+ type: "number",
47
+ description: "Offset for pagination (default: 0).",
48
+ default: 0,
49
+ },
50
+ },
51
+ },
52
+ handler: async (client, args) => {
53
+ return client.requestDirect("/alerts/history", {
54
+ account_id: resolveSiteId(args),
55
+ rule_id: args.rule_id != null ? String(args.rule_id) : undefined,
56
+ status: args.status,
57
+ limit: String(args.limit ?? 50),
58
+ offset: args.offset != null ? String(args.offset) : undefined,
59
+ });
60
+ },
61
+ };
62
+ export const getAlertStatsTool = {
63
+ name: "get_alert_stats",
64
+ description: "Get alert statistics for a site: total rules, active alerts, resolved count, and acknowledgement rate. Provides a quick health overview of the alerting system.",
65
+ inputSchema: {
66
+ type: "object",
67
+ properties: {
68
+ site_id: {
69
+ type: "string",
70
+ description: "Site ID (account_id). Optional if SEALMETRICS_SITE_ID env var is set.",
71
+ },
72
+ },
73
+ },
74
+ handler: async (client, args) => {
75
+ return client.requestDirect("/alerts/stats", {
76
+ account_id: resolveSiteId(args),
77
+ });
78
+ },
79
+ };
80
+ //# sourceMappingURL=alerts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.js","sourceRoot":"","sources":["../../src/tools/alerts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG1D,MAAM,CAAC,MAAM,cAAc,GAAY;IACrC,IAAI,EAAE,aAAa;IACnB,WAAW,EACT,kMAAkM;IACpM,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,uEAAuE;aACrF;YACD,gBAAgB,EAAE;gBAChB,IAAI,EAAE,SAAS;gBACf,WAAW,EAAE,uDAAuD;aACrE;SACF;KACF;IACD,OAAO,EAAE,KAAK,EAAE,MAAyB,EAAE,IAA6B,EAAE,EAAE;QAC1E,OAAO,MAAM,CAAC,aAAa,CAAU,eAAe,EAAE;YACpD,UAAU,EAAE,aAAa,CAAC,IAAI,CAAC;YAC/B,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAAS;SAC5F,CAAC,CAAC;IACL,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAY;IAC1C,IAAI,EAAE,mBAAmB;IACzB,WAAW,EACT,sKAAsK;IACxK,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,uEAAuE;aACrF;YACD,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,mCAAmC;aACjD;YACD,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,yBAAyB;gBACtC,IAAI,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,cAAc,CAAC;aAC7C;YACD,KAAK,EAAE,YAAY;YACnB,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,qCAAqC;gBAClD,OAAO,EAAE,CAAC;aACX;SACF;KACF;IACD,OAAO,EAAE,KAAK,EAAE,MAAyB,EAAE,IAA6B,EAAE,EAAE;QAC1E,OAAO,MAAM,CAAC,aAAa,CAAU,iBAAiB,EAAE;YACtD,UAAU,EAAE,aAAa,CAAC,IAAI,CAAC;YAC/B,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;YAChE,MAAM,EAAE,IAAI,CAAC,MAA4B;YACzC,KAAK,EAAE,MAAM,CAAE,IAAI,CAAC,KAAgB,IAAI,EAAE,CAAC;YAC3C,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAgB,CAAC,CAAC,CAAC,CAAC,SAAS;SACxE,CAAC,CAAC;IACL,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAY;IACxC,IAAI,EAAE,iBAAiB;IACvB,WAAW,EACT,iKAAiK;IACnK,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,uEAAuE;aACrF;SACF;KACF;IACD,OAAO,EAAE,KAAK,EAAE,MAAyB,EAAE,IAA6B,EAAE,EAAE;QAC1E,OAAO,MAAM,CAAC,aAAa,CAAU,eAAe,EAAE;YACpD,UAAU,EAAE,aAAa,CAAC,IAAI,CAAC;SAChC,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { ToolDef } from "./index.js";
2
+ export declare const getCountriesTool: ToolDef;
3
+ export declare const getDevicesTool: ToolDef;
4
+ export declare const getBrowsersTool: ToolDef;
5
+ export declare const getOperatingSystemsTool: ToolDef;
6
+ export declare const getDeviceTypesTool: ToolDef;
7
+ //# sourceMappingURL=audience.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audience.d.ts","sourceRoot":"","sources":["../../src/tools/audience.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,eAAO,MAAM,gBAAgB,EAAE,OAmC9B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,OAsB5B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,OA6B7B,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,OA6BrC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,OA6BhC,CAAC"}