splera-mcp 0.1.3 → 0.1.4

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sohaib Thiab
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 CHANGED
@@ -7,8 +7,8 @@ A/B testing for vibe coders, exposed as a Model Context Protocol (MCP) server. Y
7
7
  Three native tools your agent gets once installed: `create_experiment`, `get_results`, `ship_winner`. The agent calls them, edits your code to wire the SDK and variant, and reports stats back in plain English. No dashboard tab, no copy-paste, no test infrastructure to learn.
8
8
 
9
9
  - npm: [`splera-mcp`](https://www.npmjs.com/package/splera-mcp)
10
- - Web docs: [splera.app/docs/mcp](https://splera.app/docs/mcp)
11
- - Issues: [github.com/sohaibt/splera/issues](https://github.com/sohaibt/splera/issues)
10
+ - Web docs: [splera.ai/docs/mcp](https://splera.ai/docs/mcp)
11
+ - Issues: [github.com/sohaibt/splera-mcp/issues](https://github.com/sohaibt/splera-mcp/issues)
12
12
 
13
13
  ---
14
14
 
@@ -30,7 +30,7 @@ The agent picks the right tool, makes the call, and either edits your code to in
30
30
 
31
31
  ### 1. Generate an API key
32
32
 
33
- Go to [splera.app/settings](https://splera.app/settings), click **Generate new key**, name it something like `claude-code-macbook`. Copy the key from the one-time modal. It starts with `splera_live_`. You will not see the full key again. If you close the modal without copying, revoke and regenerate.
33
+ Go to [splera.ai/settings](https://splera.ai/settings), click **Generate new key**, name it something like `claude-code-macbook`. Copy the key from the one-time modal. It starts with `splera_live_`. You will not see the full key again. If you close the modal without copying, revoke and regenerate.
34
34
 
35
35
  ### 2. Add the config to your agent
36
36
 
@@ -100,7 +100,7 @@ Restart Zed.
100
100
  curl -H "Authorization: Bearer splera_live_your_key_here" https://splera.app/api/me
101
101
  ```
102
102
 
103
- `200 OK` with a JSON body means the key works. `401` means the key is bad or revoked, regenerate at [splera.app/settings](https://splera.app/settings).
103
+ `200 OK` with a JSON body means the key works. `401` means the key is bad or revoked, regenerate at [splera.ai/settings](https://splera.ai/settings).
104
104
 
105
105
  ### Option B: check the agent's MCP panel after restart
106
106
 
@@ -121,17 +121,17 @@ If those lines are missing, the MCP isn't loading. Check your config file path a
121
121
 
122
122
  The MCP config isn't being read. Check that (1) you edited the right file for your agent (see install table above), (2) the JSON is valid, no trailing commas, (3) you restarted the agent after editing.
123
123
 
124
- ### `SPLERA_API_KEY not set`
124
+ ### `SPLERA_API_KEY not set` / `SPLERA_API_KEY_MISSING` on every call
125
125
 
126
- The `env` block in your MCP config is missing or malformed. The key must start with `splera_live_`. Re-check the examples above.
126
+ The server starts without a key (so your agent still lists the tools), but every call fails until the key is set. The `env` block in your MCP config is missing or malformed. The key must start with `splera_live_`. Re-check the examples above.
127
127
 
128
128
  ### 401 / `SPLERA_INVALID_API_KEY` on every call
129
129
 
130
- The key is wrong, revoked, or truncated during copy. Go to [splera.app/settings](https://splera.app/settings), revoke the existing key, generate a new one, update the config, restart the agent.
130
+ The key is wrong, revoked, or truncated during copy. Go to [splera.ai/settings](https://splera.ai/settings), revoke the existing key, generate a new one, update the config, restart the agent.
131
131
 
132
132
  ### `SPLERA_PLAN_LIMIT_EXCEEDED` on create_experiment
133
133
 
134
- You've hit your plan's monthly experiment cap. Options: [upgrade](https://splera.app/pricing) or ship / stop an existing experiment to free a slot. The error `hint` field tells you which plan and how many slots are used.
134
+ You've hit your plan's monthly experiment cap. Options: [upgrade](https://splera.ai/pricing) or ship / stop an existing experiment to free a slot. The error `hint` field tells you which plan and how many slots are used.
135
135
 
136
136
  ### `get_results` says NOT_READY forever
137
137
 
@@ -145,7 +145,7 @@ The SDK isn't reaching the page being tested. Verify the script tag is in place:
145
145
 
146
146
  `data-api-url` is required: without it the SDK tracks nothing. If your experiment was created with splera-mcp 0.1.1, its install code was missing this attribute and used a callback form of `getVariant` that never runs. Re-copy the snippet from the experiment page on the dashboard.
147
147
 
148
- Also confirm the page is receiving real traffic. The dashboard shows live exposure counts at [splera.app/experiments/:id](https://splera.app).
148
+ Also confirm the page is receiving real traffic. The dashboard shows live exposure counts at [splera.ai/experiments/:id](https://splera.ai).
149
149
 
150
150
  ### Agent doesn't use the Splera tool even when I ask
151
151
 
@@ -196,15 +196,15 @@ Ship the winning variant to 100% of traffic. The SDK flips server-side within 30
196
196
  - `splera-mcp` makes HTTPS calls to `splera.app` only. No local telemetry, no third-party analytics from this package.
197
197
  - Your API key stays on your machine in your agent's MCP config. It's never logged server-side.
198
198
  - Keys are stored as `sha256` hashes in our database. Even if our DB were compromised, the raw keys are not recoverable.
199
- - Revoke any key instantly at [splera.app/settings](https://splera.app/settings). Revocation takes effect within 60 seconds.
199
+ - Revoke any key instantly at [splera.ai/settings](https://splera.ai/settings). Revocation takes effect within 60 seconds.
200
200
 
201
201
  ---
202
202
 
203
203
  ## Links
204
204
 
205
- - [Settings / API keys](https://splera.app/settings) — generate, name, revoke
206
- - [Pricing](https://splera.app/pricing) — plan limits and upgrade
207
- - [Web docs](https://splera.app/docs/mcp) — longer-form setup guide with screenshots
205
+ - [Settings / API keys](https://splera.ai/settings) — generate, name, revoke
206
+ - [Pricing](https://splera.ai/pricing) — plan limits and upgrade
207
+ - [Web docs](https://splera.ai/docs/mcp) — longer-form setup guide with screenshots
208
208
  - [GitHub issues](https://github.com/sohaibt/splera/issues) — bug reports and feature requests
209
209
 
210
- Built by [Splera](https://splera.app). A/B testing for vibe coders.
210
+ Built by [Splera](https://splera.ai). A/B testing for vibe coders.
package/dist/apiClient.js CHANGED
@@ -13,6 +13,9 @@ const { apiKey, apiUrl } = loadConfig();
13
13
  * so tool handlers never see raw HTTP failures.
14
14
  */
15
15
  export async function apiFetch(path, init = {}) {
16
+ if (!apiKey) {
17
+ throw new SpleraError('SPLERA_API_KEY_MISSING', 'SPLERA_API_KEY is not set.', HINTS.SPLERA_API_KEY_MISSING);
18
+ }
16
19
  const res = await fetch(`${apiUrl}${path}`, {
17
20
  ...init,
18
21
  headers: {
package/dist/config.d.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  /**
2
2
  * Env var parsing + validation. Loaded once at startup.
3
3
  *
4
- * Fails fast (process.exit(1)) on missing or malformed env vars so the
5
- * user learns the problem in the MCP host's server-logs panel instead of
6
- * getting a 30-second stuck agent or silent auth failures downstream.
4
+ * A missing SPLERA_API_KEY does NOT exit: the server still starts and lists
5
+ * its tools (directories like Glama and MCP inspectors introspect without a
6
+ * key), logs the fix to the host's server-logs panel, and every tool call
7
+ * returns SPLERA_API_KEY_MISSING with the same fix (apiClient.ts).
8
+ * A malformed SPLERA_API_URL still fails fast (process.exit(1)) — that is a
9
+ * developer override, never set by directories.
7
10
  */
8
11
  export interface Config {
9
12
  apiKey: string;
@@ -17,4 +20,4 @@ export declare function loadConfig(): Config;
17
20
  * Pre-release / build metadata is NOT supported — we don't ship those for MCP.
18
21
  */
19
22
  export declare function isBelow(current: string, minimum: string): boolean;
20
- export declare const CURRENT_MCP_VERSION = "0.1.3";
23
+ export declare const CURRENT_MCP_VERSION = "0.1.4";
package/dist/config.js CHANGED
@@ -1,19 +1,22 @@
1
1
  /**
2
2
  * Env var parsing + validation. Loaded once at startup.
3
3
  *
4
- * Fails fast (process.exit(1)) on missing or malformed env vars so the
5
- * user learns the problem in the MCP host's server-logs panel instead of
6
- * getting a 30-second stuck agent or silent auth failures downstream.
4
+ * A missing SPLERA_API_KEY does NOT exit: the server still starts and lists
5
+ * its tools (directories like Glama and MCP inspectors introspect without a
6
+ * key), logs the fix to the host's server-logs panel, and every tool call
7
+ * returns SPLERA_API_KEY_MISSING with the same fix (apiClient.ts).
8
+ * A malformed SPLERA_API_URL still fails fast (process.exit(1)) — that is a
9
+ * developer override, never set by directories.
7
10
  */
8
11
  export function loadConfig() {
9
12
  const apiKey = process.env.SPLERA_API_KEY;
10
13
  if (!apiKey) {
11
14
  console.error('splera-mcp: SPLERA_API_KEY not set.\n' +
12
- 'Generate a key at https://splera.app/settings.\n' +
15
+ 'Generate a key at https://splera.ai/settings.\n' +
13
16
  'Your MCP config env block should look like:\n' +
14
17
  ' { "splera": { "command": "npx", "args": ["-y", "splera-mcp@latest"], ' +
15
- '"env": { "SPLERA_API_KEY": "splera_live_..." } } }');
16
- process.exit(1);
18
+ '"env": { "SPLERA_API_KEY": "splera_live_..." } } }\n' +
19
+ 'Starting anyway: tools will list, but every call fails until the key is set.');
17
20
  }
18
21
  const rawUrl = process.env.SPLERA_API_URL ?? 'https://splera.app';
19
22
  let parsed;
@@ -30,7 +33,7 @@ export function loadConfig() {
30
33
  'Plain http is only allowed for localhost/127.0.0.1 (local dev).');
31
34
  process.exit(1);
32
35
  }
33
- return { apiKey, apiUrl: parsed.origin };
36
+ return { apiKey: apiKey ?? '', apiUrl: parsed.origin };
34
37
  }
35
38
  /**
36
39
  * Semver comparison used by the X-Splera-MCP-Min-Version guard in apiClient.
@@ -51,4 +54,4 @@ export function isBelow(current, minimum) {
51
54
  }
52
55
  return false;
53
56
  }
54
- export const CURRENT_MCP_VERSION = '0.1.3';
57
+ export const CURRENT_MCP_VERSION = '0.1.4';
package/dist/errors.js CHANGED
@@ -23,15 +23,16 @@ export class SpleraError extends Error {
23
23
  * Keep them actionable — every hint ends in a thing the user can DO.
24
24
  */
25
25
  export const HINTS = {
26
- SPLERA_INVALID_API_KEY: 'Check SPLERA_API_KEY or regenerate at https://splera.app/settings.',
27
- SPLERA_PLAN_LIMIT_EXCEEDED: 'Upgrade at https://splera.app/pricing, or ship/stop an existing experiment to free a slot.',
26
+ SPLERA_API_KEY_MISSING: 'Set SPLERA_API_KEY in the env block of your MCP config (generate a key at https://splera.ai/settings), then restart your agent.',
27
+ SPLERA_INVALID_API_KEY: 'Check SPLERA_API_KEY or regenerate at https://splera.ai/settings.',
28
+ SPLERA_PLAN_LIMIT_EXCEEDED: 'Upgrade at https://splera.ai/pricing, or ship/stop an existing experiment to free a slot.',
28
29
  SPLERA_DOMAIN_NOT_ALLOWED: 'This experiment is locked to specific domains. Add the requesting origin at the experiment\'s /settings.',
29
- SPLERA_EXPERIMENT_NOT_FOUND: 'Experiment ID not found. Confirm the UUID from create_experiment.',
30
+ SPLERA_EXPERIMENT_NOT_FOUND: 'Experiment not found — it may have been deleted. Confirm the UUID from create_experiment.',
30
31
  SPLERA_VALIDATION_ERROR: 'Check the tool input — see server message for details.',
31
32
  SPLERA_MCP_VERSION_TOO_OLD: 'splera-mcp client is below the minimum supported version. Run: npm update -g splera-mcp',
32
- SPLERA_HTTP_ERROR: 'Transient Splera API error. Retry in a moment, or check https://splera.app for status.',
33
+ SPLERA_HTTP_ERROR: 'Transient Splera API error. Retry in a moment, or check https://splera.ai for status.',
33
34
  SPLERA_UNKNOWN_TOOL: 'Unknown tool name. Expected one of: create_experiment, get_results, ship_winner.',
34
- SPLERA_JWT_REQUIRED: 'This action requires a browser session, not an API key. Sign in at https://splera.app/settings.',
35
+ SPLERA_JWT_REQUIRED: 'This action requires a browser session, not an API key. Sign in at https://splera.ai/settings.',
35
36
  };
36
37
  /**
37
38
  * Maps an HTTP status (+ optional server-returned code) to a SPLERA_* code
package/dist/index.js CHANGED
@@ -15,8 +15,10 @@ console.error(`splera-mcp: connecting to ${apiUrl}`);
15
15
  // Install verification with a 5s timeout. Slow networks should not hang
16
16
  // the MCP host — hosts typically kill a server that doesn't respond in ~10s.
17
17
  // On timeout or network error: soft-fail (warn + continue). The first real
18
- // tool call will re-surface the error with full context. On 401: hard-fail
19
- // with regenerate guidance.
18
+ // tool call will re-surface the error with full context. A missing or
19
+ // rejected key warns but does NOT exit, so the server still answers
20
+ // tools/list (directories like Glama introspect without a key); every tool
21
+ // call then returns SPLERA_API_KEY_MISSING / SPLERA_INVALID_API_KEY.
20
22
  const VERIFY_TIMEOUT_MS = 5_000;
21
23
  try {
22
24
  const ctrl = new AbortController();
@@ -30,13 +32,17 @@ try {
30
32
  catch (err) {
31
33
  const msg = err.message ?? String(err);
32
34
  const code = err.code ?? '';
33
- if (code === 'SPLERA_INVALID_API_KEY') {
34
- console.error('splera-mcp: API key rejected (401).\n' +
35
- 'Regenerate at https://splera.app/settings or verify with:\n' +
35
+ if (code === 'SPLERA_API_KEY_MISSING') {
36
+ // config.ts already logged the fix.
37
+ }
38
+ else if (code === 'SPLERA_INVALID_API_KEY') {
39
+ console.error('splera-mcp: API key rejected (401). Tools will list, but every call fails until it is fixed.\n' +
40
+ 'Regenerate at https://splera.ai/settings or verify with:\n' +
36
41
  ' curl -H "Authorization: Bearer $SPLERA_API_KEY" https://splera.app/api/me');
37
- process.exit(1);
38
42
  }
39
- console.error(`splera-mcp: startup verification skipped (${msg}). Will retry on first tool call.`);
43
+ else {
44
+ console.error(`splera-mcp: startup verification skipped (${msg}). Will retry on first tool call.`);
45
+ }
40
46
  }
41
47
  const tools = [createExperimentTool, getResultsTool, shipWinnerTool];
42
48
  const server = new Server({ name: 'splera-mcp', version: CURRENT_MCP_VERSION }, { capabilities: { tools: {} } });
@@ -117,7 +117,7 @@ export const createExperimentTool = {
117
117
  });
118
118
  const exp = res.experiment;
119
119
  const primaryMetric = metrics.find((m) => m.is_primary) ?? metrics[0];
120
- const resultsUrl = `https://splera.app/experiments/${exp.id}`;
120
+ const resultsUrl = `https://splera.ai/experiments/${exp.id}`;
121
121
  // Human-readable text block. The install snippet is the "aha moment" —
122
122
  // agents paste it verbatim, so keep the formatting stable.
123
123
  const variantLines = variants
@@ -134,7 +134,7 @@ export const createExperimentTool = {
134
134
  `View live results: ${resultsUrl}`;
135
135
  // Structured JSON block. CRITICAL: agents parse this, not the prose.
136
136
  // `apiUrl` from config isn't substituted into results_url because the
137
- // customer-facing dashboard is always splera.app — apiUrl may be an
137
+ // customer-facing dashboard is always splera.ai — apiUrl may be an
138
138
  // internal/api subdomain.
139
139
  const jsonBlock = {
140
140
  experiment_id: exp.id,
@@ -77,7 +77,7 @@ export const getResultsTool = {
77
77
  name: 'get_results',
78
78
  description: 'Get current results for an experiment — variant stats, leader, ship-readiness, and any guard-window alerts. ' +
79
79
  'Use this to check if an experiment is ready to ship. Safe to call once per minute during active experiments. ' +
80
- 'For long-horizon monitoring, direct the user to https://splera.app/experiments/:id.',
80
+ 'For long-horizon monitoring, direct the user to https://splera.ai/experiments/:id.',
81
81
  inputSchema: {
82
82
  type: 'object',
83
83
  required: ['experiment_id'],
@@ -94,7 +94,7 @@ export const getResultsTool = {
94
94
  const readiness = computeShipReadiness(res);
95
95
  const nextCheckAt = computeNextCheckAt(readiness.state, readiness.sub);
96
96
  const leader = res.variants.find((v) => v.variant_id === res.leading_variant_id);
97
- const resultsUrl = `https://splera.app/experiments/${res.experiment_id}`;
97
+ const resultsUrl = `https://splera.ai/experiments/${res.experiment_id}`;
98
98
  // Short-id fallback since the API doesn't echo the experiment name back.
99
99
  const experimentDisplayName = `Experiment ${res.experiment_id.slice(0, 8)}`;
100
100
  // ── Build text output, shape varies by readiness state ─────────────────
@@ -38,7 +38,7 @@ export const shipWinnerTool = {
38
38
  // The API does not return this composite directly.
39
39
  const guardWindowEndsAt = new Date(new Date(exp.shipped_at).getTime() + exp.guard_window_days * DAY_MS).toISOString();
40
40
  const guardWindowEndsDate = guardWindowEndsAt.slice(0, 10); // YYYY-MM-DD
41
- const resultsUrl = `https://splera.app/experiments/${exp.id}`;
41
+ const resultsUrl = `https://splera.ai/experiments/${exp.id}`;
42
42
  const text = `Shipped ${winningLabel} to 100% of traffic.\n` +
43
43
  `SDK will refresh within 30 seconds worldwide — no redeploy needed.\n\n` +
44
44
  `Guard window: ${exp.guard_window_days} days (until ${guardWindowEndsDate}).\n` +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "splera-mcp",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "mcpName": "io.github.sohaibt/splera",
5
5
  "description": "Model Context Protocol (MCP) server for Splera — A/B testing for vibe coders. Create experiments, check results, and ship winners directly from your AI coding agent.",
6
6
  "type": "module",