@sealmetrics/mcp 0.1.0 → 1.3.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.
- package/README.md +173 -0
- package/dist/client.d.ts +68 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +225 -0
- package/dist/client.js.map +1 -0
- package/dist/embedded.d.ts +29 -0
- package/dist/embedded.d.ts.map +1 -0
- package/dist/embedded.js +37 -0
- package/dist/embedded.js.map +1 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +55 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +1 -6
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3809 -780
- package/dist/index.js.map +1 -0
- package/dist/resources/tracking-guide.d.ts +13 -0
- package/dist/resources/tracking-guide.d.ts.map +1 -0
- package/dist/resources/tracking-guide.js +479 -0
- package/dist/resources/tracking-guide.js.map +1 -0
- package/dist/sealmetrics.mcpb +0 -0
- package/dist/server.d.ts +38 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +132 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/alerts.d.ts +5 -0
- package/dist/tools/alerts.d.ts.map +1 -0
- package/dist/tools/alerts.js +80 -0
- package/dist/tools/alerts.js.map +1 -0
- package/dist/tools/audience.d.ts +7 -0
- package/dist/tools/audience.d.ts.map +1 -0
- package/dist/tools/audience.js +146 -0
- package/dist/tools/audience.js.map +1 -0
- package/dist/tools/bots.d.ts +4 -0
- package/dist/tools/bots.d.ts.map +1 -0
- package/dist/tools/bots.js +52 -0
- package/dist/tools/bots.js.map +1 -0
- package/dist/tools/channels.d.ts +5 -0
- package/dist/tools/channels.d.ts.map +1 -0
- package/dist/tools/channels.js +88 -0
- package/dist/tools/channels.js.map +1 -0
- package/dist/tools/content.d.ts +3 -0
- package/dist/tools/content.d.ts.map +1 -0
- package/dist/tools/content.js +47 -0
- package/dist/tools/content.js.map +1 -0
- package/dist/tools/conversions.d.ts +9 -0
- package/dist/tools/conversions.d.ts.map +1 -0
- package/dist/tools/conversions.js +427 -0
- package/dist/tools/conversions.js.map +1 -0
- package/dist/tools/funnel.d.ts +3 -0
- package/dist/tools/funnel.d.ts.map +1 -0
- package/dist/tools/funnel.js +27 -0
- package/dist/tools/funnel.js.map +1 -0
- package/dist/tools/index.d.ts +16 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +83 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/overview.d.ts +3 -0
- package/dist/tools/overview.d.ts.map +1 -0
- package/dist/tools/overview.js +26 -0
- package/dist/tools/overview.js.map +1 -0
- package/dist/tools/pages.d.ts +7 -0
- package/dist/tools/pages.d.ts.map +1 -0
- package/dist/tools/pages.js +307 -0
- package/dist/tools/pages.js.map +1 -0
- package/dist/tools/properties.d.ts +5 -0
- package/dist/tools/properties.d.ts.map +1 -0
- package/dist/tools/properties.js +107 -0
- package/dist/tools/properties.js.map +1 -0
- package/dist/tools/segments.d.ts +4 -0
- package/dist/tools/segments.d.ts.map +1 -0
- package/dist/tools/segments.js +49 -0
- package/dist/tools/segments.js.map +1 -0
- package/dist/tools/setup.d.ts +49 -0
- package/dist/tools/setup.d.ts.map +1 -0
- package/dist/tools/setup.js +347 -0
- package/dist/tools/setup.js.map +1 -0
- package/dist/tools/shared.d.ts +33 -0
- package/dist/tools/shared.d.ts.map +1 -0
- package/dist/tools/shared.js +40 -0
- package/dist/tools/shared.js.map +1 -0
- package/dist/tools/sites.d.ts +4 -0
- package/dist/tools/sites.d.ts.map +1 -0
- package/dist/tools/sites.js +36 -0
- package/dist/tools/sites.js.map +1 -0
- package/dist/tools/tracking.d.ts +3 -0
- package/dist/tools/tracking.d.ts.map +1 -0
- package/dist/tools/tracking.js +220 -0
- package/dist/tools/tracking.js.map +1 -0
- package/dist/tools/traffic.d.ts +10 -0
- package/dist/tools/traffic.d.ts.map +1 -0
- package/dist/tools/traffic.js +273 -0
- package/dist/tools/traffic.js.map +1 -0
- package/dist/tools/webhooks.d.ts +5 -0
- package/dist/tools/webhooks.d.ts.map +1 -0
- package/dist/tools/webhooks.js +101 -0
- package/dist/tools/webhooks.js.map +1 -0
- package/dist/types.d.ts +118 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -0
- package/package.json +44 -27
package/README.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# @sealmetrics/mcp
|
|
2
|
+
|
|
3
|
+
MCP server for querying [SealMetrics](https://sealmetrics.com) analytics from Claude Code, Claude Desktop, or any MCP-compatible client.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
### 1. Get an API key
|
|
8
|
+
|
|
9
|
+
Go to **Settings > API Tokens** in your SealMetrics dashboard and generate a new API key.
|
|
10
|
+
|
|
11
|
+
### 2. Configure your MCP client
|
|
12
|
+
|
|
13
|
+
Add to your project's `.mcp.json` or `~/.claude/settings.json`:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"mcpServers": {
|
|
18
|
+
"sealmetrics": {
|
|
19
|
+
"command": "npx",
|
|
20
|
+
"args": ["-y", "@sealmetrics/mcp"],
|
|
21
|
+
"env": {
|
|
22
|
+
"SEALMETRICS_API_KEY": "sm_xxxx...",
|
|
23
|
+
"SEALMETRICS_SITE_ID": "my-site"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Environment variables
|
|
31
|
+
|
|
32
|
+
| Variable | Required | Description |
|
|
33
|
+
|----------|----------|-------------|
|
|
34
|
+
| `SEALMETRICS_API_KEY` | Yes | Your API key (starts with `sm_`) |
|
|
35
|
+
| `SEALMETRICS_SITE_ID` | No | Default site ID (skips needing to pass `site_id` to every tool) |
|
|
36
|
+
| `SEALMETRICS_BASE_URL` | No | API base URL (default: `https://my.sealmetrics.com/api/v1`) |
|
|
37
|
+
|
|
38
|
+
## Available tools
|
|
39
|
+
|
|
40
|
+
### list_sites
|
|
41
|
+
|
|
42
|
+
List all sites accessible with your API key.
|
|
43
|
+
|
|
44
|
+
### get_overview
|
|
45
|
+
|
|
46
|
+
Dashboard KPIs: pageviews, entrances, bounce rate, conversions, revenue. Includes time series data and optional period comparison.
|
|
47
|
+
|
|
48
|
+
**Parameters:** `site_id`, `period`, `compare`
|
|
49
|
+
|
|
50
|
+
### get_traffic_sources
|
|
51
|
+
|
|
52
|
+
Traffic broken down by source (utm_source): google, facebook, direct, etc.
|
|
53
|
+
|
|
54
|
+
**Parameters:** `site_id`, `period`, `compare`, `limit`, `sort_by`, `sort_order`
|
|
55
|
+
|
|
56
|
+
### get_traffic_mediums
|
|
57
|
+
|
|
58
|
+
Traffic broken down by medium (utm_medium): organic, cpc, email, referral, etc.
|
|
59
|
+
|
|
60
|
+
**Parameters:** `site_id`, `period`, `compare`, `limit`, `sort_by`, `sort_order`
|
|
61
|
+
|
|
62
|
+
### get_campaigns
|
|
63
|
+
|
|
64
|
+
Campaign performance (utm_campaign) with entrances, conversions, and revenue.
|
|
65
|
+
|
|
66
|
+
**Parameters:** `site_id`, `period`, `compare`, `limit`, `sort_by`, `sort_order`, `utm_source`, `utm_medium`
|
|
67
|
+
|
|
68
|
+
### get_pages
|
|
69
|
+
|
|
70
|
+
Metrics per page URL path: pageviews, entrances, conversions.
|
|
71
|
+
|
|
72
|
+
**Parameters:** `site_id`, `period`, `compare`, `limit`, `sort_by`, `sort_order`, `path_filter`, multi-value filters `country`, `device_type`, `browser`, `os`, `channel_group`, `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, and `include` (`device|browser|os|channel_group`).
|
|
73
|
+
|
|
74
|
+
> **Breaking change in 1.2.0**: `country` is now `array of string` (was `string`). Pass `country: ["ES"]` instead of `country: "ES"`.
|
|
75
|
+
|
|
76
|
+
### get_landing_pages
|
|
77
|
+
|
|
78
|
+
Landing page performance: entrances, bounce rate, conversions.
|
|
79
|
+
|
|
80
|
+
**Parameters:** `site_id`, `period`, `compare`, `limit`, `sort_by`, `sort_order`, `path_filter`, plus the same multi-value filter set and `include` parameter as `get_pages` (same breaking change on `country`).
|
|
81
|
+
|
|
82
|
+
### get_conversions
|
|
83
|
+
|
|
84
|
+
Aggregated conversions by type (purchase, signup) with count, revenue, and average order value.
|
|
85
|
+
|
|
86
|
+
**Parameters:** `site_id`, `period`, `compare`, `limit`, `sort_by`, `sort_order`, `utm_source`, `utm_medium`, `country`
|
|
87
|
+
|
|
88
|
+
For per-event detail use `get_conversions_raw`. For per-product analysis use `get_conversion_items_raw`.
|
|
89
|
+
|
|
90
|
+
### get_microconversions
|
|
91
|
+
|
|
92
|
+
Aggregated microconversions (add_to_cart, newsletter_signup, etc.) by type with counts.
|
|
93
|
+
|
|
94
|
+
**Parameters:** `site_id`, `period`, `compare`, `limit`, `sort_by`, `sort_order`, `conversion_type`
|
|
95
|
+
|
|
96
|
+
For per-event detail use `get_microconversions_raw`.
|
|
97
|
+
|
|
98
|
+
### get_conversions_raw
|
|
99
|
+
|
|
100
|
+
Raw conversion rows (one per event) with `timestamp_utc`, `timestamp_local`, full attribution. Date range capped at 31 days; `limit` capped at 100 (default 10) to control LLM token usage.
|
|
101
|
+
|
|
102
|
+
**Parameters:** `site_id`, `period` or (`start_date` + `end_date`), `page`, `limit` (1–100), `include_properties` (default `false`), and multi-value filters `conversion_type`, `country`, `device_type`, `browser`, `os`, `channel_group`, `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content`.
|
|
103
|
+
|
|
104
|
+
When `include_properties=false` the custom `properties` map is stripped from each row to keep responses compact.
|
|
105
|
+
|
|
106
|
+
### get_microconversions_raw
|
|
107
|
+
|
|
108
|
+
Same shape as `get_conversions_raw` but for `/stats/microconversions/raw` (no `amount`/`clid`).
|
|
109
|
+
|
|
110
|
+
### get_conversion_items_raw
|
|
111
|
+
|
|
112
|
+
One row per item inside a conversion (e.g. one row per product in a purchase). `properties` is **always** included — that's where `product_id`, `sku`, `price`, `quantity` live.
|
|
113
|
+
|
|
114
|
+
**Parameters:** `site_id`, `period` or (`start_date` + `end_date`), `page`, `limit` (1–100), and the same multi-value filter set as the other raw tools. Note: this tool does **not** accept `include_properties`.
|
|
115
|
+
|
|
116
|
+
### get_countries
|
|
117
|
+
|
|
118
|
+
Traffic by country with entrances, conversions, and revenue.
|
|
119
|
+
|
|
120
|
+
**Parameters:** `site_id`, `period`, `compare`, `limit`, `sort_by`, `sort_order`
|
|
121
|
+
|
|
122
|
+
### get_devices
|
|
123
|
+
|
|
124
|
+
Device type (desktop/mobile/tablet), browser, and OS breakdown in a single call.
|
|
125
|
+
|
|
126
|
+
**Parameters:** `site_id`, `period`, `compare`
|
|
127
|
+
|
|
128
|
+
### get_funnel
|
|
129
|
+
|
|
130
|
+
Funnel analysis with step-by-step conversion rates and dropoff.
|
|
131
|
+
|
|
132
|
+
**Parameters:** `site_id`, `period`, `country`
|
|
133
|
+
|
|
134
|
+
### get_tracking_code
|
|
135
|
+
|
|
136
|
+
Get the tracking pixel code for a site and the full JavaScript API reference for implementing pageviews, conversions, microconversions, and content grouping. Use this when you need to install tracking on a website.
|
|
137
|
+
|
|
138
|
+
**Parameters:** `site_id`
|
|
139
|
+
|
|
140
|
+
## Common parameters
|
|
141
|
+
|
|
142
|
+
| Parameter | Values | Default | Description |
|
|
143
|
+
|-----------|--------|---------|-------------|
|
|
144
|
+
| `site_id` | string | `$SEALMETRICS_SITE_ID` | Site to query |
|
|
145
|
+
| `period` | `today`, `yesterday`, `7d`, `30d`, `90d`, `this_month`, `last_month`, `this_year`, etc. | `30d` | Time period |
|
|
146
|
+
| `compare` | `previous`, `yoy` | none | Comparison mode |
|
|
147
|
+
| `limit` | 1-100 | 20 | Max rows returned |
|
|
148
|
+
| `page` | number | 1 | Page number for paginated results |
|
|
149
|
+
| `sort_by` | varies per tool | varies | Sort field |
|
|
150
|
+
| `sort_order` | `asc`, `desc` | `desc` | Sort direction |
|
|
151
|
+
|
|
152
|
+
## Example usage
|
|
153
|
+
|
|
154
|
+
Once configured, you can ask Claude:
|
|
155
|
+
|
|
156
|
+
- "Show me an overview of my site for the last 7 days"
|
|
157
|
+
- "What are the top traffic sources this month?"
|
|
158
|
+
- "Compare this month's conversions with last month"
|
|
159
|
+
- "Which landing pages have the highest bounce rate?"
|
|
160
|
+
- "Show me revenue by country for Q1"
|
|
161
|
+
|
|
162
|
+
## Development
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
npm install
|
|
166
|
+
npm run build # Compile TypeScript
|
|
167
|
+
npm test # Run tests
|
|
168
|
+
npm run dev # Run with tsx (dev mode)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** Raw JSON response from the API before envelope unwrapping. */
|
|
2
|
+
export interface RawAPIResponse {
|
|
3
|
+
success: boolean;
|
|
4
|
+
data: unknown;
|
|
5
|
+
total?: number;
|
|
6
|
+
page?: number;
|
|
7
|
+
page_size?: number;
|
|
8
|
+
has_next?: boolean;
|
|
9
|
+
has_prev?: boolean;
|
|
10
|
+
comparison?: unknown;
|
|
11
|
+
totals?: unknown;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
export declare class SealMetricsClient {
|
|
15
|
+
/**
|
|
16
|
+
* Read-only api_key (X-API-Key). Optional + mutable so the server can start in
|
|
17
|
+
* setup-only mode without a key (RF-3202) and adopt the key returned by
|
|
18
|
+
* provision_site at runtime to enable the read-only tools (RF-3202b). Held in
|
|
19
|
+
* memory only — never echoed to the model or logs (VAL-3201).
|
|
20
|
+
*/
|
|
21
|
+
private apiKey;
|
|
22
|
+
private readonly baseUrl;
|
|
23
|
+
constructor(apiKey: string | undefined, baseUrl: string);
|
|
24
|
+
/** True once a read-only api_key is available (env at boot or post-provision). */
|
|
25
|
+
hasApiKey(): boolean;
|
|
26
|
+
/** Return the in-memory api_key (for setup-core verify). Never logged by callers. */
|
|
27
|
+
getApiKey(): string | undefined;
|
|
28
|
+
/** Adopt the api_key returned by provision_site so read-only tools work (RF-3202b). */
|
|
29
|
+
setApiKey(apiKey: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* Authenticated POST (RF-3201). The client was GET-only in v1.2.0; the
|
|
32
|
+
* write-path needs it. NOT auto-retried — POST is not idempotent (a blind retry
|
|
33
|
+
* of /provision could create duplicate accounts). Authenticates with the
|
|
34
|
+
* publishable provision key (`provisionKey`) for provisioning, or X-API-Key
|
|
35
|
+
* otherwise. On 2xx returns the parsed JSON; on non-2xx / network throws
|
|
36
|
+
* SealMetricsAPIError carrying the status code for the caller to map.
|
|
37
|
+
*/
|
|
38
|
+
post<T>(path: string, body: unknown, opts?: {
|
|
39
|
+
provisionKey?: string;
|
|
40
|
+
}): Promise<T>;
|
|
41
|
+
/**
|
|
42
|
+
* Send an authenticated GET request. Unwraps the API envelope
|
|
43
|
+
* and returns just the `data` field.
|
|
44
|
+
*/
|
|
45
|
+
request<T>(path: string, params?: Record<string, string | string[] | undefined>): Promise<T>;
|
|
46
|
+
/**
|
|
47
|
+
* Send an authenticated GET request and return the full JSON body
|
|
48
|
+
* without unwrapping. Use this for endpoints that don't use the
|
|
49
|
+
* standard APIResponse envelope (e.g. alerts, webhooks).
|
|
50
|
+
*/
|
|
51
|
+
requestDirect<T>(path: string, params?: Record<string, string | string[] | undefined>): Promise<T>;
|
|
52
|
+
/**
|
|
53
|
+
* Send an authenticated GET request and return the full response
|
|
54
|
+
* including pagination fields (total, page, has_next, etc.).
|
|
55
|
+
* Use this for paginated endpoints.
|
|
56
|
+
*/
|
|
57
|
+
requestPaginated<T>(path: string, params?: Record<string, string | string[] | undefined>): Promise<{
|
|
58
|
+
data: T[];
|
|
59
|
+
total: number;
|
|
60
|
+
page: number;
|
|
61
|
+
page_size: number;
|
|
62
|
+
has_next: boolean;
|
|
63
|
+
comparison?: unknown;
|
|
64
|
+
totals?: unknown;
|
|
65
|
+
}>;
|
|
66
|
+
private requestRaw;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAMA,iEAAiE;AACjE,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IAEd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,qBAAa,iBAAiB;IAC5B;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM;IAKvD,kFAAkF;IAClF,SAAS,IAAI,OAAO;IAIpB,qFAAqF;IACrF,SAAS,IAAI,MAAM,GAAG,SAAS;IAI/B,uFAAuF;IACvF,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B;;;;;;;OAOG;IACG,IAAI,CAAC,CAAC,EACV,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,GAC/B,OAAO,CAAC,CAAC,CAAC;IA0Cb;;;OAGG;IACG,OAAO,CAAC,CAAC,EACb,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,OAAO,CAAC,CAAC,CAAC;IAKb;;;;OAIG;IACG,aAAa,CAAC,CAAC,EACnB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,OAAO,CAAC,CAAC,CAAC;IAKb;;;;OAIG;IACG,gBAAgB,CAAC,CAAC,EACtB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,OAAO,CAAC;QACT,IAAI,EAAE,CAAC,EAAE,CAAC;QACV,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,OAAO,CAAC;QAClB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;YAsBY,UAAU;CAwHzB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { mapHttpError, SealMetricsAPIError } from "./errors.js";
|
|
2
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
3
|
+
const MAX_RETRIES = 2;
|
|
4
|
+
const RETRY_BASE_MS = 1_000;
|
|
5
|
+
export class SealMetricsClient {
|
|
6
|
+
/**
|
|
7
|
+
* Read-only api_key (X-API-Key). Optional + mutable so the server can start in
|
|
8
|
+
* setup-only mode without a key (RF-3202) and adopt the key returned by
|
|
9
|
+
* provision_site at runtime to enable the read-only tools (RF-3202b). Held in
|
|
10
|
+
* memory only — never echoed to the model or logs (VAL-3201).
|
|
11
|
+
*/
|
|
12
|
+
apiKey;
|
|
13
|
+
baseUrl;
|
|
14
|
+
constructor(apiKey, baseUrl) {
|
|
15
|
+
this.apiKey = apiKey;
|
|
16
|
+
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
17
|
+
}
|
|
18
|
+
/** True once a read-only api_key is available (env at boot or post-provision). */
|
|
19
|
+
hasApiKey() {
|
|
20
|
+
return typeof this.apiKey === "string" && this.apiKey.length > 0;
|
|
21
|
+
}
|
|
22
|
+
/** Return the in-memory api_key (for setup-core verify). Never logged by callers. */
|
|
23
|
+
getApiKey() {
|
|
24
|
+
return this.apiKey;
|
|
25
|
+
}
|
|
26
|
+
/** Adopt the api_key returned by provision_site so read-only tools work (RF-3202b). */
|
|
27
|
+
setApiKey(apiKey) {
|
|
28
|
+
this.apiKey = apiKey;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Authenticated POST (RF-3201). The client was GET-only in v1.2.0; the
|
|
32
|
+
* write-path needs it. NOT auto-retried — POST is not idempotent (a blind retry
|
|
33
|
+
* of /provision could create duplicate accounts). Authenticates with the
|
|
34
|
+
* publishable provision key (`provisionKey`) for provisioning, or X-API-Key
|
|
35
|
+
* otherwise. On 2xx returns the parsed JSON; on non-2xx / network throws
|
|
36
|
+
* SealMetricsAPIError carrying the status code for the caller to map.
|
|
37
|
+
*/
|
|
38
|
+
async post(path, body, opts) {
|
|
39
|
+
const url = `${this.baseUrl}${path}`;
|
|
40
|
+
const headers = {
|
|
41
|
+
"Content-Type": "application/json",
|
|
42
|
+
Accept: "application/json",
|
|
43
|
+
};
|
|
44
|
+
if (opts?.provisionKey) {
|
|
45
|
+
headers["X-Provision-Key"] = opts.provisionKey;
|
|
46
|
+
}
|
|
47
|
+
else if (this.apiKey) {
|
|
48
|
+
headers["X-API-Key"] = this.apiKey;
|
|
49
|
+
}
|
|
50
|
+
const controller = new AbortController();
|
|
51
|
+
const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
|
|
52
|
+
let response;
|
|
53
|
+
try {
|
|
54
|
+
response = await fetch(url, {
|
|
55
|
+
method: "POST",
|
|
56
|
+
headers,
|
|
57
|
+
body: JSON.stringify(body),
|
|
58
|
+
signal: controller.signal,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
63
|
+
throw new SealMetricsAPIError(0, "Request timed out after 30 seconds.");
|
|
64
|
+
}
|
|
65
|
+
throw new SealMetricsAPIError(0, error instanceof Error ? error.message : "Network error");
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
clearTimeout(timeout);
|
|
69
|
+
}
|
|
70
|
+
const text = await response.text();
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
throw mapHttpError(response.status, text);
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
return JSON.parse(text);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
throw new SealMetricsAPIError(response.status, "API returned invalid JSON.", text.slice(0, 500));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Send an authenticated GET request. Unwraps the API envelope
|
|
83
|
+
* and returns just the `data` field.
|
|
84
|
+
*/
|
|
85
|
+
async request(path, params) {
|
|
86
|
+
const raw = await this.requestRaw(path, params);
|
|
87
|
+
return raw.data;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Send an authenticated GET request and return the full JSON body
|
|
91
|
+
* without unwrapping. Use this for endpoints that don't use the
|
|
92
|
+
* standard APIResponse envelope (e.g. alerts, webhooks).
|
|
93
|
+
*/
|
|
94
|
+
async requestDirect(path, params) {
|
|
95
|
+
const raw = await this.requestRaw(path, params);
|
|
96
|
+
return raw;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Send an authenticated GET request and return the full response
|
|
100
|
+
* including pagination fields (total, page, has_next, etc.).
|
|
101
|
+
* Use this for paginated endpoints.
|
|
102
|
+
*/
|
|
103
|
+
async requestPaginated(path, params) {
|
|
104
|
+
const raw = await this.requestRaw(path, params);
|
|
105
|
+
const result = {
|
|
106
|
+
data: raw.data,
|
|
107
|
+
total: raw.total ?? 0,
|
|
108
|
+
page: raw.page ?? 1,
|
|
109
|
+
page_size: raw.page_size ?? 0,
|
|
110
|
+
has_next: raw.has_next ?? false,
|
|
111
|
+
};
|
|
112
|
+
if (raw.comparison != null)
|
|
113
|
+
result.comparison = raw.comparison;
|
|
114
|
+
if (raw.totals != null)
|
|
115
|
+
result.totals = raw.totals;
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
async requestRaw(path, params) {
|
|
119
|
+
// Defense in depth (RF-3202/RF-3602): without an api_key the read-only tools
|
|
120
|
+
// are never registered, but if one is somehow invoked, fail with a clear
|
|
121
|
+
// AUTH_REQUIRED instead of sending an unauthenticated request.
|
|
122
|
+
if (!this.apiKey) {
|
|
123
|
+
throw new SealMetricsAPIError(401, "AUTH_REQUIRED: a SealMetrics api_key is required for data tools. " +
|
|
124
|
+
"Provision a site first (provision_site) or set SEALMETRICS_API_KEY.");
|
|
125
|
+
}
|
|
126
|
+
const apiKey = this.apiKey;
|
|
127
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
128
|
+
if (params) {
|
|
129
|
+
for (const [key, value] of Object.entries(params)) {
|
|
130
|
+
if (value === undefined)
|
|
131
|
+
continue;
|
|
132
|
+
if (Array.isArray(value)) {
|
|
133
|
+
// Multi-value: append each entry as a repeated query param
|
|
134
|
+
// (FastAPI deserializes `?k=a&k=b` into a list[str]).
|
|
135
|
+
for (const item of value) {
|
|
136
|
+
if (item !== undefined && item !== "") {
|
|
137
|
+
url.searchParams.append(key, item);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else if (value !== "") {
|
|
142
|
+
url.searchParams.set(key, value);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
let lastError;
|
|
147
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
148
|
+
if (attempt > 0) {
|
|
149
|
+
const delay = RETRY_BASE_MS * Math.pow(2, attempt - 1);
|
|
150
|
+
await sleep(delay);
|
|
151
|
+
}
|
|
152
|
+
const controller = new AbortController();
|
|
153
|
+
const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
|
|
154
|
+
try {
|
|
155
|
+
const response = await fetch(url.toString(), {
|
|
156
|
+
method: "GET",
|
|
157
|
+
headers: {
|
|
158
|
+
"X-API-Key": apiKey,
|
|
159
|
+
Accept: "application/json",
|
|
160
|
+
},
|
|
161
|
+
signal: controller.signal,
|
|
162
|
+
});
|
|
163
|
+
if (response.ok) {
|
|
164
|
+
const text = await response.text();
|
|
165
|
+
try {
|
|
166
|
+
return JSON.parse(text);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
throw new SealMetricsAPIError(response.status, "API returned invalid JSON.", text.slice(0, 500));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const body = await response.text();
|
|
173
|
+
// Retry on 429 (respect Retry-After) or 5xx
|
|
174
|
+
if (response.status === 429 || response.status >= 500) {
|
|
175
|
+
if (response.status === 429) {
|
|
176
|
+
const retryAfter = parseRetryAfter(response.headers.get("Retry-After"));
|
|
177
|
+
if (retryAfter > 0) {
|
|
178
|
+
await sleep(retryAfter);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
lastError = mapHttpError(response.status, body);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
// Non-retryable error
|
|
185
|
+
const siteIdRaw = params?.["site_id"] ?? params?.["account_id"] ?? undefined;
|
|
186
|
+
const siteId = typeof siteIdRaw === "string" ? siteIdRaw : undefined;
|
|
187
|
+
throw mapHttpError(response.status, body, siteId);
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
if (error instanceof SealMetricsAPIError) {
|
|
191
|
+
throw error;
|
|
192
|
+
}
|
|
193
|
+
if (error instanceof Error &&
|
|
194
|
+
error.name === "AbortError") {
|
|
195
|
+
lastError = new SealMetricsAPIError(0, "Request timed out after 30 seconds.");
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
lastError =
|
|
199
|
+
error instanceof Error
|
|
200
|
+
? error
|
|
201
|
+
: new Error("Unknown error occurred");
|
|
202
|
+
// Network errors are retryable
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
finally {
|
|
206
|
+
clearTimeout(timeout);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
throw (lastError ??
|
|
210
|
+
new SealMetricsAPIError(0, "Request failed after retries."));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function sleep(ms) {
|
|
214
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
215
|
+
}
|
|
216
|
+
function parseRetryAfter(header) {
|
|
217
|
+
if (!header)
|
|
218
|
+
return 0;
|
|
219
|
+
const seconds = parseInt(header, 10);
|
|
220
|
+
if (!isNaN(seconds) && seconds > 0 && seconds <= 120) {
|
|
221
|
+
return seconds * 1000;
|
|
222
|
+
}
|
|
223
|
+
return 0;
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEhE,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,aAAa,GAAG,KAAK,CAAC;AAiB5B,MAAM,OAAO,iBAAiB;IAC5B;;;;;OAKG;IACK,MAAM,CAAqB;IAClB,OAAO,CAAS;IAEjC,YAAY,MAA0B,EAAE,OAAe;QACrD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,kFAAkF;IAClF,SAAS;QACP,OAAO,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACnE,CAAC;IAED,qFAAqF;IACrF,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,uFAAuF;IACvF,SAAS,CAAC,MAAc;QACtB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,IAAI,CACR,IAAY,EACZ,IAAa,EACb,IAAgC;QAEhC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,CAAC;QACrC,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,kBAAkB;YAClC,MAAM,EAAE,kBAAkB;SAC3B,CAAC;QACF,IAAI,IAAI,EAAE,YAAY,EAAE,CAAC;YACvB,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC;QACjD,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACvB,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QACrC,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,kBAAkB,CAAC,CAAC;QACzE,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC1B,MAAM,EAAE,MAAM;gBACd,OAAO;gBACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;gBAC1B,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC1D,MAAM,IAAI,mBAAmB,CAAC,CAAC,EAAE,qCAAqC,CAAC,CAAC;YAC1E,CAAC;YACD,MAAM,IAAI,mBAAmB,CAAC,CAAC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;QAC7F,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,YAAY,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5C,CAAC;QACD,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,mBAAmB,CAAC,QAAQ,CAAC,MAAM,EAAE,4BAA4B,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACnG,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO,CACX,IAAY,EACZ,MAAsD;QAEtD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAChD,OAAO,GAAG,CAAC,IAAS,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,aAAa,CACjB,IAAY,EACZ,MAAsD;QAEtD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAChD,OAAO,GAAmB,CAAC;IAC7B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,gBAAgB,CACpB,IAAY,EACZ,MAAsD;QAUtD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAChD,MAAM,MAAM,GAQR;YACF,IAAI,EAAE,GAAG,CAAC,IAAW;YACrB,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;YACrB,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC;YACnB,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,CAAC;YAC7B,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,KAAK;SAChC,CAAC;QACF,IAAI,GAAG,CAAC,UAAU,IAAI,IAAI;YAAE,MAAM,CAAC,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC;QAC/D,IAAI,GAAG,CAAC,MAAM,IAAI,IAAI;YAAE,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QACnD,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,KAAK,CAAC,UAAU,CACtB,IAAY,EACZ,MAAsD;QAEtD,6EAA6E;QAC7E,yEAAyE;QACzE,+DAA+D;QAC/D,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,IAAI,mBAAmB,CAC3B,GAAG,EACH,mEAAmE;gBACjE,qEAAqE,CACxE,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,CAAC,CAAC;QAC9C,IAAI,MAAM,EAAE,CAAC;YACX,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClD,IAAI,KAAK,KAAK,SAAS;oBAAE,SAAS;gBAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;oBACzB,2DAA2D;oBAC3D,sDAAsD;oBACtD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;wBACzB,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;4BACtC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;wBACrC,CAAC;oBACH,CAAC;gBACH,CAAC;qBAAM,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;oBACxB,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;gBACnC,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,SAA4B,CAAC;QAEjC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;YACxD,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;gBAChB,MAAM,KAAK,GAAG,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;gBACvD,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;YACrB,CAAC;YAED,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;YACzC,MAAM,OAAO,GAAG,UAAU,CACxB,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EACxB,kBAAkB,CACnB,CAAC;YAEF,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;oBAC3C,MAAM,EAAE,KAAK;oBACb,OAAO,EAAE;wBACP,WAAW,EAAE,MAAM;wBACnB,MAAM,EAAE,kBAAkB;qBAC3B;oBACD,MAAM,EAAE,UAAU,CAAC,MAAM;iBAC1B,CAAC,CAAC;gBAEH,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;oBAChB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;oBACnC,IAAI,CAAC;wBACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAmB,CAAC;oBAC5C,CAAC;oBAAC,MAAM,CAAC;wBACP,MAAM,IAAI,mBAAmB,CAC3B,QAAQ,CAAC,MAAM,EACf,4BAA4B,EAC5B,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CACnB,CAAC;oBACJ,CAAC;gBACH,CAAC;gBAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAEnC,4CAA4C;gBAC5C,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;oBACtD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;wBAC5B,MAAM,UAAU,GAAG,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;wBACxE,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;4BACnB,MAAM,KAAK,CAAC,UAAU,CAAC,CAAC;wBAC1B,CAAC;oBACH,CAAC;oBACD,SAAS,GAAG,YAAY,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;oBAChD,SAAS;gBACX,CAAC;gBAED,sBAAsB;gBACtB,MAAM,SAAS,GACb,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,MAAM,EAAE,CAAC,YAAY,CAAC,IAAI,SAAS,CAAC;gBAC7D,MAAM,MAAM,GACV,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;gBACxD,MAAM,YAAY,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YACpD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,mBAAmB,EAAE,CAAC;oBACzC,MAAM,KAAK,CAAC;gBACd,CAAC;gBACD,IACE,KAAK,YAAY,KAAK;oBACtB,KAAK,CAAC,IAAI,KAAK,YAAY,EAC3B,CAAC;oBACD,SAAS,GAAG,IAAI,mBAAmB,CACjC,CAAC,EACD,qCAAqC,CACtC,CAAC;oBACF,SAAS;gBACX,CAAC;gBACD,SAAS;oBACP,KAAK,YAAY,KAAK;wBACpB,CAAC,CAAC,KAAK;wBACP,CAAC,CAAC,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;gBAC1C,+BAA+B;gBAC/B,SAAS;YACX,CAAC;oBAAS,CAAC;gBACT,YAAY,CAAC,OAAO,CAAC,CAAC;YACxB,CAAC;QACH,CAAC;QAED,MAAM,CACJ,SAAS;YACT,IAAI,mBAAmB,CAAC,CAAC,EAAE,+BAA+B,CAAC,CAC5D,CAAC;IACJ,CAAC;CACF;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,eAAe,CAAC,MAAqB;IAC5C,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC;IACtB,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACrC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO,IAAI,GAAG,EAAE,CAAC;QACrD,OAAO,OAAO,GAAG,IAAI,CAAC;IACxB,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile-time constants for the MCP write-path.
|
|
3
|
+
*
|
|
4
|
+
* The provision key is **publishable** (Fase 1, RF-202 / RF-1001 / VAL-3301): it
|
|
5
|
+
* only grants attribution + rate-limited access to POST /provision and is
|
|
6
|
+
* revocable per channel without republishing. It is NEVER a TOKEN_SECRET/
|
|
7
|
+
* API_KEY_SECRET.
|
|
8
|
+
*
|
|
9
|
+
* This is the **dedicated `mcp`-channel key** (RF-3205 / PRE-2), distinct from the
|
|
10
|
+
* CLI's `npx` key and the `pk_provision_fallback`. A separate key lets us revoke
|
|
11
|
+
* the chat-provisioning channel on its own:
|
|
12
|
+
* UPDATE provision_keys SET is_active=FALSE WHERE channel='mcp'; (VAL-3601)
|
|
13
|
+
* without touching the CLI. Override with SEALMETRICS_PROVISION_KEY when targeting
|
|
14
|
+
* local/PRE (the PROD key doesn't exist there — use `pk_provision_fallback`).
|
|
15
|
+
*/
|
|
16
|
+
export declare const EMBEDDED_PROVISION_KEY = "pk_mcp_69bb5a1ec96f2cc4d5dd77be";
|
|
17
|
+
/** Publishable fallback key seeded in non-prod DBs (local/PRE) but NOT in PROD. */
|
|
18
|
+
export declare const FALLBACK_PROVISION_KEY = "pk_provision_fallback";
|
|
19
|
+
/** Attribution channel sent as `install_source` on /provision (RF-3205 / RF-3604). */
|
|
20
|
+
export declare const INSTALL_SOURCE = "mcp";
|
|
21
|
+
/** True for local/PRE base URLs, where the fallback provision key is auto-used. */
|
|
22
|
+
export declare function isNonProdTarget(baseUrl: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the provision key to embed in /provision requests. Precedence:
|
|
25
|
+
* explicit env (SEALMETRICS_PROVISION_KEY) > local/PRE auto-fallback > embedded
|
|
26
|
+
* PROD mcp-channel key (mirrors the CLI's resolveProvisionKey).
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveProvisionKey(baseUrl: string): string;
|
|
29
|
+
//# sourceMappingURL=embedded.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedded.d.ts","sourceRoot":"","sources":["../src/embedded.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,sBAAsB,oCAAoC,CAAC;AAExE,mFAAmF;AACnF,eAAO,MAAM,sBAAsB,0BAA0B,CAAC;AAE9D,sFAAsF;AACtF,eAAO,MAAM,cAAc,QAAQ,CAAC;AAEpC,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAI3D"}
|
package/dist/embedded.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile-time constants for the MCP write-path.
|
|
3
|
+
*
|
|
4
|
+
* The provision key is **publishable** (Fase 1, RF-202 / RF-1001 / VAL-3301): it
|
|
5
|
+
* only grants attribution + rate-limited access to POST /provision and is
|
|
6
|
+
* revocable per channel without republishing. It is NEVER a TOKEN_SECRET/
|
|
7
|
+
* API_KEY_SECRET.
|
|
8
|
+
*
|
|
9
|
+
* This is the **dedicated `mcp`-channel key** (RF-3205 / PRE-2), distinct from the
|
|
10
|
+
* CLI's `npx` key and the `pk_provision_fallback`. A separate key lets us revoke
|
|
11
|
+
* the chat-provisioning channel on its own:
|
|
12
|
+
* UPDATE provision_keys SET is_active=FALSE WHERE channel='mcp'; (VAL-3601)
|
|
13
|
+
* without touching the CLI. Override with SEALMETRICS_PROVISION_KEY when targeting
|
|
14
|
+
* local/PRE (the PROD key doesn't exist there — use `pk_provision_fallback`).
|
|
15
|
+
*/
|
|
16
|
+
export const EMBEDDED_PROVISION_KEY = "pk_mcp_69bb5a1ec96f2cc4d5dd77be";
|
|
17
|
+
/** Publishable fallback key seeded in non-prod DBs (local/PRE) but NOT in PROD. */
|
|
18
|
+
export const FALLBACK_PROVISION_KEY = "pk_provision_fallback";
|
|
19
|
+
/** Attribution channel sent as `install_source` on /provision (RF-3205 / RF-3604). */
|
|
20
|
+
export const INSTALL_SOURCE = "mcp";
|
|
21
|
+
/** True for local/PRE base URLs, where the fallback provision key is auto-used. */
|
|
22
|
+
export function isNonProdTarget(baseUrl) {
|
|
23
|
+
return /localhost|127\.0\.0\.1|pre\.sealmetrics\.com/.test(baseUrl);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the provision key to embed in /provision requests. Precedence:
|
|
27
|
+
* explicit env (SEALMETRICS_PROVISION_KEY) > local/PRE auto-fallback > embedded
|
|
28
|
+
* PROD mcp-channel key (mirrors the CLI's resolveProvisionKey).
|
|
29
|
+
*/
|
|
30
|
+
export function resolveProvisionKey(baseUrl) {
|
|
31
|
+
if (process.env.SEALMETRICS_PROVISION_KEY)
|
|
32
|
+
return process.env.SEALMETRICS_PROVISION_KEY;
|
|
33
|
+
if (isNonProdTarget(baseUrl))
|
|
34
|
+
return FALLBACK_PROVISION_KEY;
|
|
35
|
+
return EMBEDDED_PROVISION_KEY;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=embedded.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedded.js","sourceRoot":"","sources":["../src/embedded.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,iCAAiC,CAAC;AAExE,mFAAmF;AACnF,MAAM,CAAC,MAAM,sBAAsB,GAAG,uBAAuB,CAAC;AAE9D,sFAAsF;AACtF,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAC;AAEpC,mFAAmF;AACnF,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO,8CAA8C,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACtE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAe;IACjD,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC;IACxF,IAAI,eAAe,CAAC,OAAO,CAAC;QAAE,OAAO,sBAAsB,CAAC;IAC5D,OAAO,sBAAsB,CAAC;AAChC,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** SealMetrics API error with user-friendly message. */
|
|
2
|
+
export declare class SealMetricsAPIError extends Error {
|
|
3
|
+
readonly statusCode: number;
|
|
4
|
+
readonly userMessage: string;
|
|
5
|
+
readonly detail?: string | undefined;
|
|
6
|
+
constructor(statusCode: number, userMessage: string, detail?: string | undefined);
|
|
7
|
+
}
|
|
8
|
+
/** Map HTTP status codes to user-friendly error messages. */
|
|
9
|
+
export declare function mapHttpError(status: number, body: string, siteId?: string): SealMetricsAPIError;
|
|
10
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,qBAAa,mBAAoB,SAAQ,KAAK;aAE1B,UAAU,EAAE,MAAM;aAClB,WAAW,EAAE,MAAM;aACnB,MAAM,CAAC,EAAE,MAAM;gBAFf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,YAAA;CAKlC;AAED,6DAA6D;AAC7D,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,GACd,mBAAmB,CAkDrB"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** SealMetrics API error with user-friendly message. */
|
|
2
|
+
export class SealMetricsAPIError extends Error {
|
|
3
|
+
statusCode;
|
|
4
|
+
userMessage;
|
|
5
|
+
detail;
|
|
6
|
+
constructor(statusCode, userMessage, detail) {
|
|
7
|
+
super(userMessage);
|
|
8
|
+
this.statusCode = statusCode;
|
|
9
|
+
this.userMessage = userMessage;
|
|
10
|
+
this.detail = detail;
|
|
11
|
+
this.name = "SealMetricsAPIError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** Map HTTP status codes to user-friendly error messages. */
|
|
15
|
+
export function mapHttpError(status, body, siteId) {
|
|
16
|
+
switch (status) {
|
|
17
|
+
case 401:
|
|
18
|
+
return new SealMetricsAPIError(401, "Invalid API key. Generate one at Settings > API Tokens in your SealMetrics dashboard.", body);
|
|
19
|
+
case 403:
|
|
20
|
+
return new SealMetricsAPIError(403, siteId
|
|
21
|
+
? `Access denied to site "${siteId}". Your API key may not have access to this site.`
|
|
22
|
+
: "Access denied. Your API key does not have the required permissions.", body);
|
|
23
|
+
case 404:
|
|
24
|
+
return new SealMetricsAPIError(404, siteId
|
|
25
|
+
? `Site "${siteId}" not found. Use list_sites to see available sites.`
|
|
26
|
+
: "Resource not found.", body);
|
|
27
|
+
case 422:
|
|
28
|
+
return new SealMetricsAPIError(422, `Invalid request parameters: ${extractValidationMessage(body)}`, body);
|
|
29
|
+
case 429:
|
|
30
|
+
return new SealMetricsAPIError(429, "Rate limit exceeded. Please wait a moment before retrying.", body);
|
|
31
|
+
default:
|
|
32
|
+
if (status >= 500) {
|
|
33
|
+
return new SealMetricsAPIError(status, "SealMetrics API is temporarily unavailable. Please try again later.", body);
|
|
34
|
+
}
|
|
35
|
+
return new SealMetricsAPIError(status, `API request failed with status ${status}.`, body);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function extractValidationMessage(body) {
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(body);
|
|
41
|
+
if (parsed.detail) {
|
|
42
|
+
if (Array.isArray(parsed.detail)) {
|
|
43
|
+
return parsed.detail
|
|
44
|
+
.map((d) => `${d.loc?.join(".") ?? "field"}: ${d.msg ?? "invalid"}`)
|
|
45
|
+
.join("; ");
|
|
46
|
+
}
|
|
47
|
+
return String(parsed.detail);
|
|
48
|
+
}
|
|
49
|
+
return body;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return body;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAE1B;IACA;IACA;IAHlB,YACkB,UAAkB,EAClB,WAAmB,EACnB,MAAe;QAE/B,KAAK,CAAC,WAAW,CAAC,CAAC;QAJH,eAAU,GAAV,UAAU,CAAQ;QAClB,gBAAW,GAAX,WAAW,CAAQ;QACnB,WAAM,GAAN,MAAM,CAAS;QAG/B,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED,6DAA6D;AAC7D,MAAM,UAAU,YAAY,CAC1B,MAAc,EACd,IAAY,EACZ,MAAe;IAEf,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,GAAG;YACN,OAAO,IAAI,mBAAmB,CAC5B,GAAG,EACH,uFAAuF,EACvF,IAAI,CACL,CAAC;QACJ,KAAK,GAAG;YACN,OAAO,IAAI,mBAAmB,CAC5B,GAAG,EACH,MAAM;gBACJ,CAAC,CAAC,0BAA0B,MAAM,mDAAmD;gBACrF,CAAC,CAAC,qEAAqE,EACzE,IAAI,CACL,CAAC;QACJ,KAAK,GAAG;YACN,OAAO,IAAI,mBAAmB,CAC5B,GAAG,EACH,MAAM;gBACJ,CAAC,CAAC,SAAS,MAAM,qDAAqD;gBACtE,CAAC,CAAC,qBAAqB,EACzB,IAAI,CACL,CAAC;QACJ,KAAK,GAAG;YACN,OAAO,IAAI,mBAAmB,CAC5B,GAAG,EACH,+BAA+B,wBAAwB,CAAC,IAAI,CAAC,EAAE,EAC/D,IAAI,CACL,CAAC;QACJ,KAAK,GAAG;YACN,OAAO,IAAI,mBAAmB,CAC5B,GAAG,EACH,4DAA4D,EAC5D,IAAI,CACL,CAAC;QACJ;YACE,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;gBAClB,OAAO,IAAI,mBAAmB,CAC5B,MAAM,EACN,qEAAqE,EACrE,IAAI,CACL,CAAC;YACJ,CAAC;YACD,OAAO,IAAI,mBAAmB,CAC5B,MAAM,EACN,kCAAkC,MAAM,GAAG,EAC3C,IAAI,CACL,CAAC;IACN,CAAC;AACH,CAAC;AAED,SAAS,wBAAwB,CAAC,IAAY;IAC5C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;gBACjC,OAAO,MAAM,CAAC,MAAM;qBACjB,GAAG,CACF,CAAC,CAAmC,EAAE,EAAE,CACtC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,OAAO,KAAK,CAAC,CAAC,GAAG,IAAI,SAAS,EAAE,CAC1D;qBACA,IAAI,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC;YACD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* SealMetrics MCP Server
|
|
4
|
-
*
|
|
5
|
-
* A Model Context Protocol server that provides access to SealMetrics analytics data.
|
|
6
|
-
* Allows AI assistants to query traffic, conversions, sales, and generate tracking pixels.
|
|
7
|
-
*/
|
|
8
2
|
export {};
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|