@clarvivo/mcp 0.1.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/LICENSE +21 -0
- package/README.md +229 -0
- package/dist/api.d.ts +68 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +85 -0
- package/dist/api.js.map +1 -0
- package/dist/frameworks.d.ts +21 -0
- package/dist/frameworks.d.ts.map +1 -0
- package/dist/frameworks.js +202 -0
- package/dist/frameworks.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +51 -0
- package/dist/server.js.map +1 -0
- package/dist/tools.d.ts +45 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +198 -0
- package/dist/tools.js.map +1 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Clarvivo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# `clarvivo-mcp`
|
|
2
|
+
|
|
3
|
+
Install Clarvivo analytics without leaving your coding agent. The server detects the app framework, reuses or creates the right Clarvivo project, and returns the exact file and code edit. It can then verify real traffic and return compact analytics summaries.
|
|
4
|
+
|
|
5
|
+
## The 60-second flow
|
|
6
|
+
|
|
7
|
+
1. Create an API token at [app.clarvivo.com/dashboard/settings?tab=api-tokens](https://app.clarvivo.com/dashboard/settings?tab=api-tokens).
|
|
8
|
+
2. Add this MCP server to your coding client with `CLARVIVO_API_TOKEN` in its environment.
|
|
9
|
+
3. Ask the agent: **“Set up Clarvivo analytics for `example.com`.”**
|
|
10
|
+
4. The agent detects the framework, calls `setup_analytics`, makes the returned edit, and deploys through your normal workflow.
|
|
11
|
+
5. Visit the deployed site, then ask the agent to verify the installation.
|
|
12
|
+
|
|
13
|
+
The tracker observes `history.pushState`, `history.replaceState`, and `popstate`, so client-side SPA route changes work automatically. Do not add router hooks.
|
|
14
|
+
|
|
15
|
+
## Tools
|
|
16
|
+
|
|
17
|
+
| Tool | Purpose |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| `setup_analytics` | Detect the framework, reuse a matching-domain project or create one, and return the exact installation edit. |
|
|
20
|
+
| `get_install_snippet` | Get framework-specific instructions for an existing API key or project. |
|
|
21
|
+
| `verify_installation` | Prove the deployed snippet works from a proof-of-life event count, without reading analytics. |
|
|
22
|
+
| `list_projects` | List accessible projects and their public tracking API keys. |
|
|
23
|
+
| `get_stats` | Return a small summary for 1–90 days, capped to the top five pages and referrers. |
|
|
24
|
+
|
|
25
|
+
`setup_analytics` supports Next.js App Router, Next.js Pages Router, Vite (React/Vue/Svelte), Create React App, Astro, SvelteKit, Nuxt, Remix / React Router v7, Gatsby, Docusaurus, and static HTML.
|
|
26
|
+
|
|
27
|
+
## Configuration
|
|
28
|
+
|
|
29
|
+
Replace `clv_live_REPLACE_ME` below. Tokens are secrets; keep personal config files out of source control. The tracking API key returned by tools is intentionally public because it ships in page source.
|
|
30
|
+
|
|
31
|
+
For local development or self-hosting, add `CLARVIVO_BASE_URL` (for example `http://127.0.0.1:5000`) beside the token.
|
|
32
|
+
|
|
33
|
+
### Claude Code
|
|
34
|
+
|
|
35
|
+
The official CLI accepts `--env` before the `--` separator. This user-scoped command works from every project:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
claude mcp add clarvivo --scope user \
|
|
39
|
+
--env CLARVIVO_API_TOKEN=clv_live_REPLACE_ME \
|
|
40
|
+
-- npx -y @clarvivo/mcp
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Check it with `claude mcp get clarvivo`. See [Claude Code MCP documentation](https://docs.anthropic.com/en/docs/claude-code/mcp).
|
|
44
|
+
|
|
45
|
+
### Cursor
|
|
46
|
+
|
|
47
|
+
Create `.cursor/mcp.json` in a project, or `~/.cursor/mcp.json` globally:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"mcpServers": {
|
|
52
|
+
"clarvivo": {
|
|
53
|
+
"command": "npx",
|
|
54
|
+
"args": ["-y", "clarvivo-mcp"],
|
|
55
|
+
"env": {
|
|
56
|
+
"CLARVIVO_API_TOKEN": "clv_live_REPLACE_ME"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
One-click install with a placeholder token (review and replace it in Cursor before starting the server):
|
|
64
|
+
|
|
65
|
+
[Add Clarvivo to Cursor](cursor://anysphere.cursor-deeplink/mcp/install?name=clarvivo&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBjbGFydml2by9tY3AiXSwiZW52Ijp7IkNMQVJWSVZPX0FQSV9UT0tFTiI6ImNsdl9saXZlX1JFUExBQ0VfTUUifX0)
|
|
66
|
+
|
|
67
|
+
Cursor requires the single-server config to be JSON-stringified and base64-encoded in the `config` parameter. See [Cursor MCP configuration](https://cursor.com/docs/mcp) and [Cursor install links](https://cursor.com/docs/mcp/install-links).
|
|
68
|
+
|
|
69
|
+
### Codex CLI
|
|
70
|
+
|
|
71
|
+
Add this to `~/.codex/config.toml`:
|
|
72
|
+
|
|
73
|
+
```toml
|
|
74
|
+
[mcp_servers.clarvivo]
|
|
75
|
+
command = "npx"
|
|
76
|
+
args = ["-y", "clarvivo-mcp"]
|
|
77
|
+
|
|
78
|
+
[mcp_servers.clarvivo.env]
|
|
79
|
+
CLARVIVO_API_TOKEN = "clv_live_REPLACE_ME"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The equivalent command is:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
codex mcp add clarvivo \
|
|
86
|
+
--env CLARVIVO_API_TOKEN=clv_live_REPLACE_ME \
|
|
87
|
+
-- npx -y @clarvivo/mcp
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
This was checked against the installed `codex mcp add --help` and the [official OpenAI Codex MCP documentation](https://developers.openai.com/codex/mcp/).
|
|
91
|
+
|
|
92
|
+
### Windsurf
|
|
93
|
+
|
|
94
|
+
Open `~/.codeium/windsurf/mcp_config.json` and add:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"mcpServers": {
|
|
99
|
+
"clarvivo": {
|
|
100
|
+
"command": "npx",
|
|
101
|
+
"args": ["-y", "clarvivo-mcp"],
|
|
102
|
+
"env": {
|
|
103
|
+
"CLARVIVO_API_TOKEN": "clv_live_REPLACE_ME"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Refresh the MCP list after saving. See [Windsurf’s MCP documentation](https://docs.windsurf.com/windsurf/cascade/mcp).
|
|
111
|
+
|
|
112
|
+
### Cline
|
|
113
|
+
|
|
114
|
+
Open Cline’s MCP Servers settings, choose **Configure MCP Servers**, and add this entry to `cline_mcp_settings.json` (normally under `~/.cline/data/settings/`):
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"mcpServers": {
|
|
119
|
+
"clarvivo": {
|
|
120
|
+
"command": "npx",
|
|
121
|
+
"args": ["-y", "clarvivo-mcp"],
|
|
122
|
+
"env": {
|
|
123
|
+
"CLARVIVO_API_TOKEN": "clv_live_REPLACE_ME"
|
|
124
|
+
},
|
|
125
|
+
"disabled": false
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
See [Cline configuration locations](https://docs.cline.bot/getting-started/config).
|
|
132
|
+
|
|
133
|
+
### Visual Studio Code / GitHub Copilot
|
|
134
|
+
|
|
135
|
+
Create `.vscode/mcp.json`:
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"servers": {
|
|
140
|
+
"clarvivo": {
|
|
141
|
+
"type": "stdio",
|
|
142
|
+
"command": "npx",
|
|
143
|
+
"args": ["-y", "clarvivo-mcp"],
|
|
144
|
+
"env": {
|
|
145
|
+
"CLARVIVO_API_TOKEN": "clv_live_REPLACE_ME"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
One-click install with a placeholder token:
|
|
153
|
+
|
|
154
|
+
[Install Clarvivo in VS Code](vscode:mcp/install?%7B%22name%22%3A%22clarvivo%22%2C%22type%22%3A%22stdio%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40clarvivo%2Fmcp%22%5D%2C%22env%22%3A%7B%22CLARVIVO_API_TOKEN%22%3A%22clv_live_REPLACE_ME%22%7D%7D)
|
|
155
|
+
|
|
156
|
+
VS Code’s URI uses URL-encoded JSON rather than Cursor’s base64 encoding. See the [VS Code MCP developer guide](https://code.visualstudio.com/api/extension-guides/ai/mcp) and [configuration reference](https://code.visualstudio.com/docs/agents/reference/mcp-configuration).
|
|
157
|
+
|
|
158
|
+
### Zed
|
|
159
|
+
|
|
160
|
+
Open your Zed settings file and merge:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"context_servers": {
|
|
165
|
+
"clarvivo": {
|
|
166
|
+
"command": "npx",
|
|
167
|
+
"args": ["-y", "clarvivo-mcp"],
|
|
168
|
+
"env": {
|
|
169
|
+
"CLARVIVO_API_TOKEN": "clv_live_REPLACE_ME"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
See [MCP in Zed](https://zed.dev/docs/ai/mcp).
|
|
177
|
+
|
|
178
|
+
### Generic MCP client
|
|
179
|
+
|
|
180
|
+
For clients using the common `mcpServers` shape:
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"mcpServers": {
|
|
185
|
+
"clarvivo": {
|
|
186
|
+
"command": "npx",
|
|
187
|
+
"args": ["-y", "clarvivo-mcp"],
|
|
188
|
+
"env": {
|
|
189
|
+
"CLARVIVO_API_TOKEN": "clv_live_REPLACE_ME"
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Node.js 20 or newer is required.
|
|
197
|
+
|
|
198
|
+
## Authentication and billing errors
|
|
199
|
+
|
|
200
|
+
Every tool checks for `CLARVIVO_API_TOKEN` before doing work. If it is missing, the tool returns the token URL and a copy-pasteable environment variable name. The bearer token is never included in tool output or logs.
|
|
201
|
+
|
|
202
|
+
Clarvivo has no free tier. If project creation returns `PROJECT_LIMIT_REACHED`, HTTP 402, or HTTP 403, the tool stops immediately and links to [billing](https://app.clarvivo.com/dashboard/billing). It does not retry or claim the project was created.
|
|
203
|
+
|
|
204
|
+
## Development
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
cd mcp
|
|
208
|
+
npm install
|
|
209
|
+
npm run build
|
|
210
|
+
npm test
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Set `CLARVIVO_BASE_URL=http://127.0.0.1:5000` when using a local Clarvivo server. Tests mock HTTP and never call production.
|
|
214
|
+
|
|
215
|
+
## Publishing checklist
|
|
216
|
+
|
|
217
|
+
No submission happens automatically. Complete these after the package and public repository are ready:
|
|
218
|
+
|
|
219
|
+
- [ ] **npm:** publish `clarvivo-mcp` publicly, confirm the `bin` is executable, and test a clean `npx -y @clarvivo/mcp` MCP initialize handshake. Use npm provenance/2FA and inspect `npm pack --dry-run` first.
|
|
220
|
+
- [ ] **Official MCP Registry:** choose and add a verified `mcpName` to `package.json` (a GitHub-authenticated `io.github.<owner>/...` name or DNS-authenticated Clarvivo namespace), publish the matching npm version first, create `server.json` with stdio transport and required `CLARVIVO_API_TOKEN`, then run `mcp-publisher login`, `mcp-publisher validate`, and `mcp-publisher publish`. The registry stores metadata, not the package. Follow the [official registry quickstart](https://modelcontextprotocol.io/registry/quickstart).
|
|
221
|
+
- [ ] **Smithery:** authenticate with the Smithery CLI and publish a local MCP bundle (`.mcpb`) under the Clarvivo namespace, or publish a hosted URL if an HTTP transport is added later: `smithery mcp publish <bundle.mcpb> -n clarvivo/clarvivo`. Smithery’s current publisher accepts hosted URLs or MCP bundles; see [Smithery CLI publishing](https://www.npmjs.com/package/@smithery/cli).
|
|
222
|
+
- [ ] **mcp.so:** use the [server submission form](https://mcp.so/submit?type=server). It currently requires a public repository URL and name; the paid path is optional and advertises immediate publishing.
|
|
223
|
+
- [ ] **PulseMCP:** use the site’s **Submit** flow and provide the public repository, npm install command, five-tool inventory, auth variable, license, and maintainer contact. Re-check the form at submission time because PulseMCP does not publish a stable submission schema.
|
|
224
|
+
- [ ] **Glama:** click **Add Server** in the [Glama registry](https://glama.ai/mcp/servers), submit the public GitHub repository, and add `glama.json` if organization ownership/metadata verification requests it. Confirm Glama can inspect all five tools.
|
|
225
|
+
- [ ] **Cursor Marketplace:** package the MCP config as an Agent Plugin or Cursor Plugin with a valid `plugin.json` / `.cursor-plugin/plugin.json`, public Git repository, README, and optional committed logo; declare the token variable in the manifest schema, then submit the repository at [cursor.com/marketplace/publish](https://cursor.com/marketplace/publish). Use the [official submission checklist](https://cursor.com/docs/reference/plugins).
|
|
226
|
+
- [ ] **awesome-mcp-servers:** first ensure the Glama listing exists, then fork [punkpeye/awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers), add one alphabetized line in the appropriate category with repository link and concise description, and open a PR following its [contribution guide](https://github.com/punkpeye/awesome-mcp-servers/blob/main/CONTRIBUTING.md).
|
|
227
|
+
- [ ] **Additional discovery:** submit the same canonical metadata to `mcpservers.org`, `MCP Central`, and `mcp-marketplace.io`; keep every listing pointed at the npm package and one canonical repository so versions and security notices do not drift.
|
|
228
|
+
|
|
229
|
+
For every listing, use the same name, description, repository, license, token variable, Node requirement, five-tool inventory, and install snippets from this README.
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export interface ClarvivoProject {
|
|
2
|
+
id: number | string;
|
|
3
|
+
name: string;
|
|
4
|
+
domain: string;
|
|
5
|
+
apiKey: string;
|
|
6
|
+
[key: string]: unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface AnalyticsRow {
|
|
9
|
+
date?: string;
|
|
10
|
+
pageviews?: number | string;
|
|
11
|
+
visitors?: number | string;
|
|
12
|
+
uniqueVisitors?: number | string;
|
|
13
|
+
sessions?: number | string;
|
|
14
|
+
topPages?: unknown;
|
|
15
|
+
pageDetails?: unknown;
|
|
16
|
+
topReferrers?: unknown;
|
|
17
|
+
trafficChannels?: unknown;
|
|
18
|
+
[key: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
export interface RealtimeSnapshot {
|
|
21
|
+
count?: number;
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
}
|
|
24
|
+
export interface InstallStatus {
|
|
25
|
+
installed: boolean;
|
|
26
|
+
eventsReceived: number;
|
|
27
|
+
lastEventAt: string | null;
|
|
28
|
+
locked: boolean;
|
|
29
|
+
upgradeUrl?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface ClarvivoApi {
|
|
32
|
+
listProjects(): Promise<ClarvivoProject[]>;
|
|
33
|
+
createProject(input: {
|
|
34
|
+
name: string;
|
|
35
|
+
domain: string;
|
|
36
|
+
}): Promise<ClarvivoProject>;
|
|
37
|
+
getInstallStatus(projectId: number | string): Promise<InstallStatus>;
|
|
38
|
+
getAnalytics(projectId: number | string, days: number): Promise<AnalyticsRow[]>;
|
|
39
|
+
getRealtime(projectId: number | string): Promise<RealtimeSnapshot>;
|
|
40
|
+
}
|
|
41
|
+
export declare class ClarvivoApiError extends Error {
|
|
42
|
+
readonly status: number;
|
|
43
|
+
readonly code?: string | undefined;
|
|
44
|
+
constructor(message: string, status: number, code?: string | undefined);
|
|
45
|
+
}
|
|
46
|
+
export declare const DEFAULT_BASE_URL = "https://app.clarvivo.com";
|
|
47
|
+
export declare function tokenSetupMessage(baseUrl?: string): string;
|
|
48
|
+
export declare function planLimitMessage(baseUrl?: string): string;
|
|
49
|
+
export declare function payToReadMessage(baseUrl?: string): string;
|
|
50
|
+
export declare function requireToken(env?: NodeJS.ProcessEnv): string;
|
|
51
|
+
export declare class HttpClarvivoApi implements ClarvivoApi {
|
|
52
|
+
private readonly fetchImpl;
|
|
53
|
+
private readonly baseUrl;
|
|
54
|
+
private readonly token;
|
|
55
|
+
constructor(env?: NodeJS.ProcessEnv, fetchImpl?: typeof fetch);
|
|
56
|
+
private request;
|
|
57
|
+
listProjects(): Promise<ClarvivoProject[]>;
|
|
58
|
+
createProject(input: {
|
|
59
|
+
name: string;
|
|
60
|
+
domain: string;
|
|
61
|
+
}): Promise<ClarvivoProject>;
|
|
62
|
+
getInstallStatus(projectId: number | string): Promise<InstallStatus>;
|
|
63
|
+
getAnalytics(projectId: number | string, days: number): Promise<AnalyticsRow[]>;
|
|
64
|
+
getRealtime(projectId: number | string): Promise<RealtimeSnapshot>;
|
|
65
|
+
}
|
|
66
|
+
export declare function isPlanLimit(error: unknown): boolean;
|
|
67
|
+
export declare function isAnalyticsLockout(error: unknown): boolean;
|
|
68
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export class ClarvivoApiError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
code;
|
|
4
|
+
constructor(message, status, code) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.status = status;
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.name = "ClarvivoApiError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export const DEFAULT_BASE_URL = "https://app.clarvivo.com";
|
|
12
|
+
export function tokenSetupMessage(baseUrl = process.env.CLARVIVO_BASE_URL || DEFAULT_BASE_URL) {
|
|
13
|
+
return `Clarvivo needs an API token. Open ${baseUrl.replace(/\/$/, "")}/dashboard/settings?tab=api-tokens, create a token, then set CLARVIVO_API_TOKEN=clv_live_<your-token> in this MCP server's environment and restart the client.`;
|
|
14
|
+
}
|
|
15
|
+
export function planLimitMessage(baseUrl = process.env.CLARVIVO_BASE_URL || DEFAULT_BASE_URL) {
|
|
16
|
+
return `Your Clarvivo plan cannot create another project. Open ${baseUrl.replace(/\/$/, "")}/dashboard/billing — checkout takes about 30 seconds — then re-run setup_analytics.`;
|
|
17
|
+
}
|
|
18
|
+
export function payToReadMessage(baseUrl = process.env.CLARVIVO_BASE_URL || DEFAULT_BASE_URL) {
|
|
19
|
+
return `Pay $1 to read your Clarvivo data. Open ${baseUrl.replace(/\/$/, "")}/dashboard/billing, then re-run get_stats.`;
|
|
20
|
+
}
|
|
21
|
+
export function requireToken(env = process.env) {
|
|
22
|
+
const token = env.CLARVIVO_API_TOKEN?.trim();
|
|
23
|
+
if (!token)
|
|
24
|
+
throw new Error(tokenSetupMessage(env.CLARVIVO_BASE_URL || DEFAULT_BASE_URL));
|
|
25
|
+
return token;
|
|
26
|
+
}
|
|
27
|
+
export class HttpClarvivoApi {
|
|
28
|
+
fetchImpl;
|
|
29
|
+
baseUrl;
|
|
30
|
+
token;
|
|
31
|
+
constructor(env = process.env, fetchImpl = fetch) {
|
|
32
|
+
this.fetchImpl = fetchImpl;
|
|
33
|
+
this.baseUrl = (env.CLARVIVO_BASE_URL || DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
34
|
+
this.token = requireToken(env);
|
|
35
|
+
}
|
|
36
|
+
async request(pathname, init = {}) {
|
|
37
|
+
const response = await this.fetchImpl(`${this.baseUrl}${pathname}`, {
|
|
38
|
+
...init,
|
|
39
|
+
headers: {
|
|
40
|
+
Accept: "application/json",
|
|
41
|
+
...(init.body ? { "Content-Type": "application/json" } : {}),
|
|
42
|
+
...init.headers,
|
|
43
|
+
Authorization: `Bearer ${this.token}`,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
const text = await response.text();
|
|
47
|
+
let body;
|
|
48
|
+
try {
|
|
49
|
+
body = text ? JSON.parse(text) : undefined;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
body = text;
|
|
53
|
+
}
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
const object = body && typeof body === "object" ? body : {};
|
|
56
|
+
throw new ClarvivoApiError(typeof object.message === "string" ? object.message : `Clarvivo API request failed (${response.status}).`, response.status, typeof object.code === "string" ? object.code : undefined);
|
|
57
|
+
}
|
|
58
|
+
return body;
|
|
59
|
+
}
|
|
60
|
+
listProjects() {
|
|
61
|
+
return this.request("/api/projects");
|
|
62
|
+
}
|
|
63
|
+
createProject(input) {
|
|
64
|
+
return this.request("/api/projects", { method: "POST", body: JSON.stringify(input) });
|
|
65
|
+
}
|
|
66
|
+
getInstallStatus(projectId) {
|
|
67
|
+
return this.request(`/api/projects/${encodeURIComponent(String(projectId))}/install-status`);
|
|
68
|
+
}
|
|
69
|
+
getAnalytics(projectId, days) {
|
|
70
|
+
return this.request(`/api/projects/${encodeURIComponent(String(projectId))}/analytics?days=${days}`);
|
|
71
|
+
}
|
|
72
|
+
getRealtime(projectId) {
|
|
73
|
+
return this.request(`/api/projects/${encodeURIComponent(String(projectId))}/realtime/active`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function isPlanLimit(error) {
|
|
77
|
+
return error instanceof ClarvivoApiError && (error.code === "PROJECT_LIMIT_REACHED" ||
|
|
78
|
+
error.code === "PAYMENT_REQUIRED_FOR_MORE_PROJECTS" ||
|
|
79
|
+
error.status === 402 ||
|
|
80
|
+
error.status === 403);
|
|
81
|
+
}
|
|
82
|
+
export function isAnalyticsLockout(error) {
|
|
83
|
+
return error instanceof ClarvivoApiError && error.status === 402;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const frameworkIds: readonly ["next-app", "next-pages", "vite", "create-react-app", "astro", "sveltekit", "nuxt", "remix", "gatsby", "docusaurus", "static-html"];
|
|
2
|
+
export type FrameworkId = (typeof frameworkIds)[number];
|
|
3
|
+
export interface FrameworkDetection {
|
|
4
|
+
framework: FrameworkId;
|
|
5
|
+
confidence: "high" | "medium";
|
|
6
|
+
evidence: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface InstallInstructions {
|
|
9
|
+
framework: FrameworkId;
|
|
10
|
+
frameworkName: string;
|
|
11
|
+
file: string;
|
|
12
|
+
placement: string;
|
|
13
|
+
code: string;
|
|
14
|
+
snippet: string;
|
|
15
|
+
spaRouteTracking: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function normalizeFramework(value: string): FrameworkId;
|
|
18
|
+
export declare function detectFramework(root?: string): FrameworkDetection | undefined;
|
|
19
|
+
export declare function trackingSnippet(apiKey: string, baseUrl?: string): string;
|
|
20
|
+
export declare function getInstallInstructions(framework: FrameworkId, apiKey: string, baseUrl?: string, root?: string): InstallInstructions;
|
|
21
|
+
//# sourceMappingURL=frameworks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frameworks.d.ts","sourceRoot":"","sources":["../src/frameworks.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,YAAY,+IAYf,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,WAAW,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC9B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,WAAW,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AA+CD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAS7D;AA4BD,wBAAgB,eAAe,CAAC,IAAI,SAAgB,GAAG,kBAAkB,GAAG,SAAS,CA6DpF;AAmBD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,SAA6B,GAAG,MAAM,CAE5F;AAED,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,WAAW,EACtB,MAAM,EAAE,MAAM,EACd,OAAO,SAA6B,EACpC,IAAI,SAAgB,GACnB,mBAAmB,CAoCrB"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export const frameworkIds = [
|
|
4
|
+
"next-app",
|
|
5
|
+
"next-pages",
|
|
6
|
+
"vite",
|
|
7
|
+
"create-react-app",
|
|
8
|
+
"astro",
|
|
9
|
+
"sveltekit",
|
|
10
|
+
"nuxt",
|
|
11
|
+
"remix",
|
|
12
|
+
"gatsby",
|
|
13
|
+
"docusaurus",
|
|
14
|
+
"static-html",
|
|
15
|
+
];
|
|
16
|
+
const names = {
|
|
17
|
+
"next-app": "Next.js App Router",
|
|
18
|
+
"next-pages": "Next.js Pages Router",
|
|
19
|
+
vite: "Vite (React, Vue, or Svelte)",
|
|
20
|
+
"create-react-app": "Create React App",
|
|
21
|
+
astro: "Astro",
|
|
22
|
+
sveltekit: "SvelteKit",
|
|
23
|
+
nuxt: "Nuxt",
|
|
24
|
+
remix: "Remix / React Router v7",
|
|
25
|
+
gatsby: "Gatsby",
|
|
26
|
+
docusaurus: "Docusaurus",
|
|
27
|
+
"static-html": "Plain static HTML",
|
|
28
|
+
};
|
|
29
|
+
const aliases = {
|
|
30
|
+
"next-app": "next-app",
|
|
31
|
+
"next.js-app-router": "next-app",
|
|
32
|
+
"nextjs-app": "next-app",
|
|
33
|
+
"next-app-router": "next-app",
|
|
34
|
+
"next-pages": "next-pages",
|
|
35
|
+
"next.js-pages-router": "next-pages",
|
|
36
|
+
"nextjs-pages": "next-pages",
|
|
37
|
+
"next-pages-router": "next-pages",
|
|
38
|
+
vite: "vite",
|
|
39
|
+
react: "vite",
|
|
40
|
+
vue: "vite",
|
|
41
|
+
"vite-react": "vite",
|
|
42
|
+
"vite-vue": "vite",
|
|
43
|
+
"vite-svelte": "vite",
|
|
44
|
+
cra: "create-react-app",
|
|
45
|
+
"create-react-app": "create-react-app",
|
|
46
|
+
astro: "astro",
|
|
47
|
+
sveltekit: "sveltekit",
|
|
48
|
+
"svelte-kit": "sveltekit",
|
|
49
|
+
nuxt: "nuxt",
|
|
50
|
+
remix: "remix",
|
|
51
|
+
"react-router": "remix",
|
|
52
|
+
"react-router-v7": "remix",
|
|
53
|
+
gatsby: "gatsby",
|
|
54
|
+
docusaurus: "docusaurus",
|
|
55
|
+
html: "static-html",
|
|
56
|
+
static: "static-html",
|
|
57
|
+
"static-html": "static-html",
|
|
58
|
+
};
|
|
59
|
+
export function normalizeFramework(value) {
|
|
60
|
+
const normalized = value.trim().toLowerCase().replace(/[_\s]+/g, "-");
|
|
61
|
+
const framework = aliases[normalized];
|
|
62
|
+
if (!framework) {
|
|
63
|
+
throw new Error(`Unsupported framework "${value}". Use one of: ${frameworkIds.join(", ")}.`);
|
|
64
|
+
}
|
|
65
|
+
return framework;
|
|
66
|
+
}
|
|
67
|
+
function exists(root, relative) {
|
|
68
|
+
return fs.existsSync(path.join(root, relative));
|
|
69
|
+
}
|
|
70
|
+
function firstExisting(root, candidates) {
|
|
71
|
+
return candidates.find((candidate) => exists(root, candidate));
|
|
72
|
+
}
|
|
73
|
+
function readPackage(root) {
|
|
74
|
+
try {
|
|
75
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
76
|
+
return { ...pkg.dependencies, ...pkg.devDependencies };
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return {};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function hasConfig(root, basename) {
|
|
83
|
+
return ["js", "mjs", "cjs", "ts", "mts", "cts"].some((ext) => exists(root, `${basename}.${ext}`));
|
|
84
|
+
}
|
|
85
|
+
export function detectFramework(root = process.cwd()) {
|
|
86
|
+
const deps = readPackage(root);
|
|
87
|
+
const evidence = [];
|
|
88
|
+
if (deps.next || hasConfig(root, "next.config")) {
|
|
89
|
+
if (deps.next)
|
|
90
|
+
evidence.push("package.json includes next");
|
|
91
|
+
if (hasConfig(root, "next.config"))
|
|
92
|
+
evidence.push("next.config.* exists");
|
|
93
|
+
const appMarker = firstExisting(root, [
|
|
94
|
+
"app/layout.tsx", "app/layout.jsx", "app/layout.js", "app/layout.ts",
|
|
95
|
+
"src/app/layout.tsx", "src/app/layout.jsx", "src/app/layout.js", "src/app/layout.ts",
|
|
96
|
+
]);
|
|
97
|
+
if (appMarker) {
|
|
98
|
+
return { framework: "next-app", confidence: "high", evidence: [...evidence, `${appMarker} exists`] };
|
|
99
|
+
}
|
|
100
|
+
const pagesMarker = firstExisting(root, [
|
|
101
|
+
"pages/_app.tsx", "pages/_app.jsx", "pages/_app.js", "pages/index.tsx", "pages/index.jsx", "pages/index.js",
|
|
102
|
+
"src/pages/_app.tsx", "src/pages/_app.jsx", "src/pages/_app.js", "src/pages/index.tsx", "src/pages/index.jsx", "src/pages/index.js",
|
|
103
|
+
]);
|
|
104
|
+
return {
|
|
105
|
+
framework: "next-pages",
|
|
106
|
+
confidence: pagesMarker ? "high" : "medium",
|
|
107
|
+
evidence: pagesMarker ? [...evidence, `${pagesMarker} exists`] : evidence,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
if (deps["@sveltejs/kit"] || (exists(root, "svelte.config.js") && exists(root, "src/app.html"))) {
|
|
111
|
+
return {
|
|
112
|
+
framework: "sveltekit",
|
|
113
|
+
confidence: "high",
|
|
114
|
+
evidence: deps["@sveltejs/kit"] ? ["package.json includes @sveltejs/kit"] : ["svelte.config.js and src/app.html exist"],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (deps.astro || hasConfig(root, "astro.config")) {
|
|
118
|
+
return { framework: "astro", confidence: "high", evidence: [deps.astro ? "package.json includes astro" : "astro.config.* exists"] };
|
|
119
|
+
}
|
|
120
|
+
if (deps.nuxt || hasConfig(root, "nuxt.config")) {
|
|
121
|
+
return { framework: "nuxt", confidence: "high", evidence: [deps.nuxt ? "package.json includes nuxt" : "nuxt.config.* exists"] };
|
|
122
|
+
}
|
|
123
|
+
if (deps["@docusaurus/core"] || hasConfig(root, "docusaurus.config")) {
|
|
124
|
+
return { framework: "docusaurus", confidence: "high", evidence: [deps["@docusaurus/core"] ? "package.json includes @docusaurus/core" : "docusaurus.config.* exists"] };
|
|
125
|
+
}
|
|
126
|
+
if (deps.gatsby || hasConfig(root, "gatsby.config")) {
|
|
127
|
+
return { framework: "gatsby", confidence: "high", evidence: [deps.gatsby ? "package.json includes gatsby" : "gatsby.config.* exists"] };
|
|
128
|
+
}
|
|
129
|
+
if (deps["@remix-run/react"] || deps["@react-router/dev"] || hasConfig(root, "react-router.config") || hasConfig(root, "remix.config")) {
|
|
130
|
+
return {
|
|
131
|
+
framework: "remix",
|
|
132
|
+
confidence: "high",
|
|
133
|
+
evidence: [deps["@remix-run/react"] ? "package.json includes @remix-run/react" : deps["@react-router/dev"] ? "package.json includes @react-router/dev" : "Remix/React Router config exists"],
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (deps["react-scripts"]) {
|
|
137
|
+
return { framework: "create-react-app", confidence: "high", evidence: ["package.json includes react-scripts"] };
|
|
138
|
+
}
|
|
139
|
+
if (deps.vite || hasConfig(root, "vite.config")) {
|
|
140
|
+
return { framework: "vite", confidence: "high", evidence: [deps.vite ? "package.json includes vite" : "vite.config.* exists"] };
|
|
141
|
+
}
|
|
142
|
+
if (exists(root, "index.html")) {
|
|
143
|
+
return { framework: "static-html", confidence: "medium", evidence: ["index.html exists and no supported framework marker was found"] };
|
|
144
|
+
}
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
function preferredFile(framework, root) {
|
|
148
|
+
const choices = {
|
|
149
|
+
"next-app": ["app/layout.tsx", "src/app/layout.tsx", "app/layout.jsx", "src/app/layout.jsx", "app/layout.js", "src/app/layout.js"],
|
|
150
|
+
"next-pages": ["pages/_app.tsx", "src/pages/_app.tsx", "pages/_app.jsx", "src/pages/_app.jsx", "pages/_app.js", "src/pages/_app.js"],
|
|
151
|
+
vite: ["index.html"],
|
|
152
|
+
"create-react-app": ["public/index.html"],
|
|
153
|
+
astro: ["src/layouts/Layout.astro", "src/layouts/BaseLayout.astro", "src/pages/index.astro"],
|
|
154
|
+
sveltekit: ["src/app.html"],
|
|
155
|
+
nuxt: ["nuxt.config.ts", "nuxt.config.js"],
|
|
156
|
+
remix: ["app/root.tsx", "app/root.jsx", "app/root.js"],
|
|
157
|
+
gatsby: ["gatsby-ssr.tsx", "gatsby-ssr.jsx", "gatsby-ssr.js"],
|
|
158
|
+
docusaurus: ["docusaurus.config.ts", "docusaurus.config.js"],
|
|
159
|
+
"static-html": ["index.html"],
|
|
160
|
+
};
|
|
161
|
+
return firstExisting(root, choices[framework]) ?? choices[framework][0];
|
|
162
|
+
}
|
|
163
|
+
export function trackingSnippet(apiKey, baseUrl = "https://app.clarvivo.com") {
|
|
164
|
+
return `<script defer src="${baseUrl.replace(/\/$/, "")}/js/${apiKey}/analytics.js"></script>`;
|
|
165
|
+
}
|
|
166
|
+
export function getInstallInstructions(framework, apiKey, baseUrl = "https://app.clarvivo.com", root = process.cwd()) {
|
|
167
|
+
const src = `${baseUrl.replace(/\/$/, "")}/js/${apiKey}/analytics.js`;
|
|
168
|
+
const snippet = trackingSnippet(apiKey, baseUrl);
|
|
169
|
+
const file = preferredFile(framework, root);
|
|
170
|
+
const common = {
|
|
171
|
+
framework,
|
|
172
|
+
frameworkName: names[framework],
|
|
173
|
+
file,
|
|
174
|
+
snippet,
|
|
175
|
+
spaRouteTracking: "No router hook is needed: the Clarvivo tracker automatically observes pushState, replaceState, and popstate route changes.",
|
|
176
|
+
};
|
|
177
|
+
switch (framework) {
|
|
178
|
+
case "next-app":
|
|
179
|
+
return { ...common, placement: "Import Script, then render it inside the root layout's <body>.", code: `import Script from "next/script";\n\n// Inside <body>, after {children}:\n<Script src="${src}" strategy="afterInteractive" />` };
|
|
180
|
+
case "next-pages":
|
|
181
|
+
return { ...common, placement: "Import Script in the custom App and render it after <Component />. Create the file if it does not exist.", code: `import type { AppProps } from "next/app";\nimport Script from "next/script";\n\nexport default function App({ Component, pageProps }: AppProps) {\n return (\n <>\n <Component {...pageProps} />\n <Script src="${src}" strategy="afterInteractive" />\n </>\n );\n}` };
|
|
182
|
+
case "vite":
|
|
183
|
+
return { ...common, placement: "Add this immediately before </head> in the root index.html.", code: snippet };
|
|
184
|
+
case "create-react-app":
|
|
185
|
+
return { ...common, placement: "Add this immediately before </head> in public/index.html.", code: snippet };
|
|
186
|
+
case "astro":
|
|
187
|
+
return { ...common, placement: "Add this inside <head> in the shared layout used by every page.", code: snippet };
|
|
188
|
+
case "sveltekit":
|
|
189
|
+
return { ...common, placement: "Add this inside <head>, directly after %sveltekit.head%.", code: `%sveltekit.head%\n ${snippet}` };
|
|
190
|
+
case "nuxt":
|
|
191
|
+
return { ...common, placement: "Merge this app.head entry into defineNuxtConfig (preserve existing config keys).", code: `export default defineNuxtConfig({\n app: {\n head: {\n script: [{ src: "${src}", defer: true }],\n },\n },\n});` };
|
|
192
|
+
case "remix":
|
|
193
|
+
return { ...common, placement: "Add this script inside <head> in the root document returned by app/root.tsx.", code: snippet };
|
|
194
|
+
case "gatsby":
|
|
195
|
+
return { ...common, placement: "Add this SSR hook (merge it with an existing onRenderBody export if present).", code: `import React from "react";\n\nexport const onRenderBody = ({ setHeadComponents }) => {\n setHeadComponents([\n <script key="clarvivo" defer src="${src}" />,\n ]);\n};` };
|
|
196
|
+
case "docusaurus":
|
|
197
|
+
return { ...common, placement: "Merge this scripts entry into the exported Docusaurus config.", code: `scripts: [\n { src: "${src}", defer: true },\n],` };
|
|
198
|
+
case "static-html":
|
|
199
|
+
return { ...common, placement: "Add this immediately before </head> on every page (or in the shared HTML template).", code: snippet };
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=frameworks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frameworks.js","sourceRoot":"","sources":["../src/frameworks.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { createServer } from "./server.js";
|
|
4
|
+
const server = createServer();
|
|
5
|
+
await server.connect(new StdioServerTransport());
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;AAC9B,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,wBAAgB,YAAY,IAAI,SAAS,CAoDxC"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { createToolHandlers } from "./tools.js";
|
|
4
|
+
export function createServer() {
|
|
5
|
+
const server = new McpServer({ name: "clarvivo", version: "0.1.0" });
|
|
6
|
+
const handlers = createToolHandlers();
|
|
7
|
+
const projectId = z.union([z.number(), z.string()]).describe("Clarvivo project ID");
|
|
8
|
+
server.registerTool("setup_analytics", {
|
|
9
|
+
title: "Set up Clarvivo analytics",
|
|
10
|
+
description: "Detect this repository's framework, reuse or create the Clarvivo project for a domain, and return the exact file and code edit. Use this first when asked to add analytics.",
|
|
11
|
+
inputSchema: {
|
|
12
|
+
name: z.string().min(1).optional().describe("Project name; defaults to a readable name derived from domain"),
|
|
13
|
+
domain: z.string().min(1).describe("Production hostname, for example example.com"),
|
|
14
|
+
framework: z.string().min(1).optional().describe("Optional override such as next-app, next-pages, vite, astro, sveltekit, nuxt, remix, gatsby, docusaurus, or static-html"),
|
|
15
|
+
},
|
|
16
|
+
annotations: { idempotentHint: true },
|
|
17
|
+
}, handlers.setupAnalytics);
|
|
18
|
+
server.registerTool("get_install_snippet", {
|
|
19
|
+
title: "Get framework install instructions",
|
|
20
|
+
description: "Return the exact tracking snippet, target file, placement, and framework-specific code for an existing Clarvivo API key or project.",
|
|
21
|
+
inputSchema: {
|
|
22
|
+
apiKey: z.string().min(1).optional().describe("Public Clarvivo tracking API key"),
|
|
23
|
+
projectId: projectId.optional(),
|
|
24
|
+
framework: z.string().min(1).describe("Target framework"),
|
|
25
|
+
},
|
|
26
|
+
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
27
|
+
}, handlers.getInstallSnippet);
|
|
28
|
+
server.registerTool("verify_installation", {
|
|
29
|
+
title: "Verify analytics installation",
|
|
30
|
+
description: "Check Clarvivo's proof-of-life receipt to confirm that traffic from the deployed site has arrived, without reading analytics or sending synthetic events.",
|
|
31
|
+
inputSchema: { projectId },
|
|
32
|
+
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
33
|
+
}, handlers.verifyInstallation);
|
|
34
|
+
server.registerTool("list_projects", {
|
|
35
|
+
title: "List Clarvivo projects",
|
|
36
|
+
description: "List accessible Clarvivo projects with their IDs, domains, and public tracking API keys.",
|
|
37
|
+
inputSchema: {},
|
|
38
|
+
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
39
|
+
}, handlers.listProjects);
|
|
40
|
+
server.registerTool("get_stats", {
|
|
41
|
+
title: "Get compact traffic stats",
|
|
42
|
+
description: "Return a small pre-summarised traffic overview, active visitor count, and top five pages/referrers instead of raw daily analytics rows.",
|
|
43
|
+
inputSchema: {
|
|
44
|
+
projectId,
|
|
45
|
+
days: z.number().int().min(1).max(90).default(7).describe("Reporting window from 1 to 90 days"),
|
|
46
|
+
},
|
|
47
|
+
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
48
|
+
}, handlers.getStats);
|
|
49
|
+
return server;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type AnalyticsRow, type ClarvivoApi } from "./api.js";
|
|
2
|
+
export interface ToolContext {
|
|
3
|
+
env?: NodeJS.ProcessEnv;
|
|
4
|
+
cwd?: string;
|
|
5
|
+
api?: ClarvivoApi;
|
|
6
|
+
}
|
|
7
|
+
type ToolSuccess = {
|
|
8
|
+
content: Array<{
|
|
9
|
+
type: "text";
|
|
10
|
+
text: string;
|
|
11
|
+
}>;
|
|
12
|
+
structuredContent: Record<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
type ToolFailure = {
|
|
15
|
+
content: Array<{
|
|
16
|
+
type: "text";
|
|
17
|
+
text: string;
|
|
18
|
+
}>;
|
|
19
|
+
isError: true;
|
|
20
|
+
};
|
|
21
|
+
export type ToolResult = ToolSuccess | ToolFailure;
|
|
22
|
+
export declare function summarizeStats(rows: AnalyticsRow[], realtimeCount: number, days: number): Record<string, unknown>;
|
|
23
|
+
export declare function createToolHandlers(context?: ToolContext): {
|
|
24
|
+
setupAnalytics: (input: {
|
|
25
|
+
name?: string;
|
|
26
|
+
domain: string;
|
|
27
|
+
framework?: string;
|
|
28
|
+
}) => Promise<ToolResult>;
|
|
29
|
+
getInstallSnippet: (input: {
|
|
30
|
+
apiKey?: string;
|
|
31
|
+
projectId?: number | string;
|
|
32
|
+
framework: string;
|
|
33
|
+
}) => Promise<ToolResult>;
|
|
34
|
+
verifyInstallation: (input: {
|
|
35
|
+
projectId: number | string;
|
|
36
|
+
}) => Promise<ToolResult>;
|
|
37
|
+
listProjects: () => Promise<ToolResult>;
|
|
38
|
+
getStats: (input: {
|
|
39
|
+
projectId: number | string;
|
|
40
|
+
days?: number;
|
|
41
|
+
}) => Promise<ToolResult>;
|
|
42
|
+
};
|
|
43
|
+
export declare function workingDirectoryLabel(cwd?: string): string;
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,WAAW,EAOjB,MAAM,UAAU,CAAC;AAQlB,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,WAAW,CAAC;CACnB;AAED,KAAK,WAAW,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC;AAClH,KAAK,WAAW,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,EAAE,IAAI,CAAA;CAAE,CAAC;AACrF,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,WAAW,CAAC;AAgFnD,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAuBjH;AAED,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,WAAgB;4BAK1B;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAG,OAAO,CAAC,UAAU,CAAC;+BAwBxE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,KAAG,OAAO,CAAC,UAAU,CAAC;gCAmBxF;QAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,KAAG,OAAO,CAAC,UAAU,CAAC;wBAyB9D,OAAO,CAAC,UAAU,CAAC;sBASnB;QAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAG,OAAO,CAAC,UAAU,CAAC;EAW9F;AAED,wBAAgB,qBAAqB,CAAC,GAAG,SAAgB,GAAG,MAAM,CAEjE"}
|
package/dist/tools.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { HttpClarvivoApi, isAnalyticsLockout, isPlanLimit, payToReadMessage, planLimitMessage, requireToken, } from "./api.js";
|
|
3
|
+
import { detectFramework, getInstallInstructions, normalizeFramework, } from "./frameworks.js";
|
|
4
|
+
function success(data) {
|
|
5
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }], structuredContent: data };
|
|
6
|
+
}
|
|
7
|
+
function failure(error, env, mode) {
|
|
8
|
+
const message = mode === "project-limit" && isPlanLimit(error)
|
|
9
|
+
? planLimitMessage(env.CLARVIVO_BASE_URL)
|
|
10
|
+
: mode === "analytics-lockout" && isAnalyticsLockout(error)
|
|
11
|
+
? payToReadMessage(env.CLARVIVO_BASE_URL)
|
|
12
|
+
: error instanceof Error ? error.message : "Clarvivo request failed.";
|
|
13
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
14
|
+
}
|
|
15
|
+
function canonicalDomain(input) {
|
|
16
|
+
const value = input.trim();
|
|
17
|
+
if (!value)
|
|
18
|
+
throw new Error("domain is required.");
|
|
19
|
+
try {
|
|
20
|
+
const url = new URL(value.includes("://") ? value : `https://${value}`);
|
|
21
|
+
return url.hostname.toLowerCase().replace(/^www\./, "").replace(/\.$/, "");
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
throw new Error(`"${input}" is not a valid domain. Use a hostname such as example.com.`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function defaultProjectName(domain) {
|
|
28
|
+
return domain.split(".")[0].replace(/[-_]+/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
|
|
29
|
+
}
|
|
30
|
+
function resolveApi(context) {
|
|
31
|
+
requireToken(context.env ?? process.env);
|
|
32
|
+
return context.api ?? new HttpClarvivoApi(context.env ?? process.env);
|
|
33
|
+
}
|
|
34
|
+
function baseUrl(context) {
|
|
35
|
+
return (context.env?.CLARVIVO_BASE_URL || process.env.CLARVIVO_BASE_URL || "https://app.clarvivo.com").replace(/\/$/, "");
|
|
36
|
+
}
|
|
37
|
+
function projectMatchesDomain(projectDomain, domain) {
|
|
38
|
+
try {
|
|
39
|
+
return canonicalDomain(projectDomain) === domain;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return projectDomain.trim().toLowerCase() === domain;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function chooseFramework(explicit, cwd) {
|
|
46
|
+
if (explicit)
|
|
47
|
+
return { framework: normalizeFramework(explicit) };
|
|
48
|
+
const detection = detectFramework(cwd);
|
|
49
|
+
if (!detection) {
|
|
50
|
+
throw new Error("Could not detect the framework. Pass framework explicitly (for example: next-app, vite, astro, or static-html). No project was created.");
|
|
51
|
+
}
|
|
52
|
+
return { framework: detection.framework, detection };
|
|
53
|
+
}
|
|
54
|
+
function number(value) {
|
|
55
|
+
const parsed = typeof value === "number" ? value : Number(value ?? 0);
|
|
56
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
57
|
+
}
|
|
58
|
+
function array(value) {
|
|
59
|
+
return Array.isArray(value) ? value.filter((item) => Boolean(item) && typeof item === "object") : [];
|
|
60
|
+
}
|
|
61
|
+
function topEntries(rows, keys, labelKeys, valueKeys) {
|
|
62
|
+
const totals = new Map();
|
|
63
|
+
for (const row of rows) {
|
|
64
|
+
for (const key of keys) {
|
|
65
|
+
for (const item of array(row[key])) {
|
|
66
|
+
const name = labelKeys.map((candidate) => item[candidate]).find((value) => typeof value === "string");
|
|
67
|
+
if (!name)
|
|
68
|
+
continue;
|
|
69
|
+
const count = valueKeys.map((candidate) => item[candidate]).find((value) => value !== undefined);
|
|
70
|
+
totals.set(name, (totals.get(name) ?? 0) + number(count));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return [...totals.entries()].map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count).slice(0, 5);
|
|
75
|
+
}
|
|
76
|
+
export function summarizeStats(rows, realtimeCount, days) {
|
|
77
|
+
const totals = rows.reduce((acc, row) => ({
|
|
78
|
+
pageviews: acc.pageviews + number(row.pageviews),
|
|
79
|
+
uniqueVisitors: acc.uniqueVisitors + number(row.uniqueVisitors ?? row.visitors),
|
|
80
|
+
sessions: acc.sessions + number(row.sessions),
|
|
81
|
+
}), { pageviews: 0, uniqueVisitors: 0, sessions: 0 });
|
|
82
|
+
const channels = new Map();
|
|
83
|
+
for (const row of rows) {
|
|
84
|
+
if (!row.trafficChannels || typeof row.trafficChannels !== "object" || Array.isArray(row.trafficChannels))
|
|
85
|
+
continue;
|
|
86
|
+
for (const [name, count] of Object.entries(row.trafficChannels)) {
|
|
87
|
+
channels.set(name, (channels.get(name) ?? 0) + number(count));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
days,
|
|
92
|
+
...totals,
|
|
93
|
+
activeNow: realtimeCount,
|
|
94
|
+
topPages: topEntries(rows, ["topPages", "pageDetails"], ["path", "page"], ["views", "pageviews"]),
|
|
95
|
+
topReferrers: topEntries(rows, ["topReferrers"], ["source", "referrer"], ["visitors", "visits", "count"]),
|
|
96
|
+
trafficChannels: [...channels.entries()].map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count).slice(0, 6),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
export function createToolHandlers(context = {}) {
|
|
100
|
+
const env = context.env ?? process.env;
|
|
101
|
+
const cwd = context.cwd ?? process.cwd();
|
|
102
|
+
return {
|
|
103
|
+
setupAnalytics: async (input) => {
|
|
104
|
+
try {
|
|
105
|
+
const api = resolveApi(context);
|
|
106
|
+
const domain = canonicalDomain(input.domain);
|
|
107
|
+
const selected = chooseFramework(input.framework, cwd);
|
|
108
|
+
const projects = await api.listProjects();
|
|
109
|
+
let project = projects.find((candidate) => projectMatchesDomain(candidate.domain, domain));
|
|
110
|
+
const reused = Boolean(project);
|
|
111
|
+
if (!project) {
|
|
112
|
+
project = await api.createProject({ name: input.name?.trim() || defaultProjectName(domain), domain });
|
|
113
|
+
}
|
|
114
|
+
const install = getInstallInstructions(selected.framework, project.apiKey, baseUrl(context), cwd);
|
|
115
|
+
return success({
|
|
116
|
+
project: { id: project.id, name: project.name, domain: project.domain, apiKey: project.apiKey },
|
|
117
|
+
reusedExistingProject: reused,
|
|
118
|
+
detection: selected.detection,
|
|
119
|
+
install,
|
|
120
|
+
nextStep: `Edit ${install.file} exactly as described, deploy the site, then run verify_installation with projectId ${project.id}.`,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
return failure(error, env, "project-limit");
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
getInstallSnippet: async (input) => {
|
|
128
|
+
try {
|
|
129
|
+
const api = resolveApi(context);
|
|
130
|
+
let apiKey = input.apiKey?.trim();
|
|
131
|
+
let projectId = input.projectId;
|
|
132
|
+
if (!apiKey) {
|
|
133
|
+
if (projectId === undefined)
|
|
134
|
+
throw new Error("Provide apiKey or projectId.");
|
|
135
|
+
const project = (await api.listProjects()).find((candidate) => String(candidate.id) === String(projectId));
|
|
136
|
+
if (!project)
|
|
137
|
+
throw new Error(`Project ${projectId} was not found.`);
|
|
138
|
+
apiKey = project.apiKey;
|
|
139
|
+
projectId = project.id;
|
|
140
|
+
}
|
|
141
|
+
const framework = normalizeFramework(input.framework);
|
|
142
|
+
return success({ projectId, apiKey, install: getInstallInstructions(framework, apiKey, baseUrl(context), cwd) });
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
return failure(error, env);
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
verifyInstallation: async (input) => {
|
|
149
|
+
try {
|
|
150
|
+
const api = resolveApi(context);
|
|
151
|
+
const status = await api.getInstallStatus(input.projectId);
|
|
152
|
+
const count = number(status.eventsReceived);
|
|
153
|
+
const upgradeUrl = status.upgradeUrl || `${baseUrl(context)}/dashboard/billing`;
|
|
154
|
+
const baseMessage = status.installed
|
|
155
|
+
? `Your tracking is live. ${count.toLocaleString("en-US")} pageviews recorded.`
|
|
156
|
+
: "No real traffic has arrived yet. Deploy the snippet, visit the live site, navigate to another route, wait up to 90 seconds, then run this check again.";
|
|
157
|
+
return success({
|
|
158
|
+
projectId: input.projectId,
|
|
159
|
+
installed: status.installed,
|
|
160
|
+
eventsReceived: count,
|
|
161
|
+
lastEventAt: status.lastEventAt,
|
|
162
|
+
locked: status.locked,
|
|
163
|
+
...(status.locked ? { upgradeUrl } : {}),
|
|
164
|
+
message: status.locked
|
|
165
|
+
? `${baseMessage} Pay $1 to read your data: ${upgradeUrl}`
|
|
166
|
+
: baseMessage,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
return failure(error, env, "analytics-lockout");
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
listProjects: async () => {
|
|
174
|
+
try {
|
|
175
|
+
const projects = await resolveApi(context).listProjects();
|
|
176
|
+
return success({ projects: projects.map(({ id, name, domain, apiKey }) => ({ id, name, domain, apiKey })), count: projects.length });
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
return failure(error, env);
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
getStats: async (input) => {
|
|
183
|
+
try {
|
|
184
|
+
const api = resolveApi(context);
|
|
185
|
+
const days = Math.max(1, Math.min(90, Math.round(input.days ?? 7)));
|
|
186
|
+
const [rows, realtime] = await Promise.all([api.getAnalytics(input.projectId, days), api.getRealtime(input.projectId)]);
|
|
187
|
+
return success({ projectId: input.projectId, ...summarizeStats(rows, number(realtime.count), days) });
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
return failure(error, env, "analytics-lockout");
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
export function workingDirectoryLabel(cwd = process.cwd()) {
|
|
196
|
+
return path.basename(cwd) || cwd;
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@clarvivo/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Install Clarvivo analytics from your coding agent, then query compact traffic stats.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"clarvivo-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=20"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.json",
|
|
20
|
+
"prepack": "npm run build",
|
|
21
|
+
"test": "vitest run"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"clarvivo",
|
|
25
|
+
"analytics",
|
|
26
|
+
"mcp",
|
|
27
|
+
"model-context-protocol"
|
|
28
|
+
],
|
|
29
|
+
"homepage": "https://app.clarvivo.com",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/cracked-collective/clarvivo-mcp.git"
|
|
33
|
+
},
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
37
|
+
"zod": "^3.25.76"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^22.18.0",
|
|
41
|
+
"typescript": "^5.9.2",
|
|
42
|
+
"vitest": "^3.2.4"
|
|
43
|
+
},
|
|
44
|
+
"mcpName": "io.github.cracked-collective/clarvivo-mcp"
|
|
45
|
+
}
|