sceneview-mcp 3.6.4 → 4.0.0-beta.1

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 CHANGED
@@ -18,26 +18,40 @@ Connect it to Claude, Cursor, Windsurf, or any MCP client. The assistant gets 26
18
18
 
19
19
  ---
20
20
 
21
- ## 🚀 Pro Products
21
+ ## 🚀 Hosted-first mode (v4 beta)
22
22
 
23
- | Product | Price | Description |
24
- |---------|-------|-------------|
25
- | [MCP Creator Kit](https://buy.polar.sh/polar_cl_tb87ROB9Xn0c5aohdn3NvkTINDF1xjW5zpkg70UwmcF) | €29 | Everything to create your own MCP server — template, CLI, docs, examples |
26
- | [SceneView Pro Starter Kit](https://buy.polar.sh/polar_cl_tb87ROB9Xn0c5aohdn3NvkTINDF1xjW5zpkg70UwmcF) | €49 | Complete Android 3D + AR app template — 4 screens, ready to customize |
27
- | [SceneView MCP Pro](https://buy.polar.sh/polar_cl_tb87ROB9Xn0c5aohdn3NvkTINDF1xjW5zpkg70UwmcF) | €9.99/mo | Premium MCP tools and priority support |
23
+ Starting with **v4.0.0-beta.1**, `sceneview-mcp` is a **lite stdio package**: free tools run locally (no network round-trip) and Pro tools are transparently forwarded to the hosted gateway at **https://sceneview-mcp.mcp-tools-lab.workers.dev/mcp**.
28
24
 
29
- [Sponsor on GitHub](https://github.com/sponsors/sceneview) — Help us build the future of 3D/AR development
25
+ | What | Where |
26
+ |---|---|
27
+ | 17 free tools (samples, guides, validator, search, analyze) | Local, zero network |
28
+ | 36+ Pro tools (AR, multi-platform, scene gen, artifacts, packages) | Forwarded to the gateway — Bearer auth + Stripe-metered |
29
+ | Auth, metering, Stripe webhooks, API-key provisioning | Gateway (Cloudflare Workers + D1 + KV) |
30
+
31
+ **Pricing** (subscribe at https://sceneview-mcp.mcp-tools-lab.workers.dev/pricing):
32
+
33
+ | Plan | Price | Use case |
34
+ |---|---|---|
35
+ | Free | 0 € | Samples, guides, validator — no signup |
36
+ | Pro | 19 €/mo or 190 €/yr | Individual devs, full Pro tool access |
37
+ | Team | 49 €/mo or 490 €/yr | Teams with higher rate limits |
38
+
39
+ After subscribing, you'll receive a `sv_live_…` API key. Set it via the `SCENEVIEW_API_KEY` env var in your MCP client config (see snippets below) to unlock Pro tools.
40
+
41
+ ⭐ [Sponsor on GitHub](https://github.com/sponsors/sceneview) — help keep the free tier free.
30
42
 
31
43
  ---
32
44
 
33
- ## Quick start
45
+ ## Quick start (free tier)
34
46
 
35
47
  **One command -- no install required:**
36
48
 
37
49
  ```bash
38
- npx sceneview-mcp
50
+ npx sceneview-mcp@beta
39
51
  ```
40
52
 
53
+ On startup you'll see a `[sceneview-mcp] v4.0.0-beta.1 — LITE (free tools only)` banner on stderr. Set `SCENEVIEW_API_KEY` to flip into `HOSTED` mode.
54
+
41
55
  > **Anonymous telemetry** is enabled on the free tier (MCP client name/version and tool names — no personal data, no prompt content). Opt out with `SCENEVIEW_TELEMETRY=0`. See [PRIVACY.md](./PRIVACY.md#telemetry-free-tier) for the full payload shape.
42
56
 
43
57
  ### Claude Desktop
package/dist/index.js CHANGED
@@ -17,12 +17,33 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
17
17
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
18
18
  import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
19
19
  import { fetchKnownIssues } from "./issues.js";
20
- import { checkToolAccess, filterToolsForTier, createAccessDeniedResponse } from "./auth.js";
21
- import { recordUsage, getConfiguredApiKey } from "./billing.js";
22
20
  import { recordClientInit, recordToolCall } from "./telemetry.js";
23
- import { getToolTier } from "./tiers.js";
21
+ import { isProTool, getToolTier } from "./tiers.js";
22
+ import { dispatchProxyToolCall, isProxyConfigured, DEFAULT_PRICING_URL, } from "./proxy.js";
24
23
  import { API_DOCS, TOOL_DEFINITIONS, dispatchTool, } from "./tools/index.js";
25
- const server = new Server({ name: "sceneview-mcp", version: "3.6.4" }, { capabilities: { resources: {}, tools: {} } });
24
+ // ─── v4 lite-mode startup banner ─────────────────────────────────────────────
25
+ //
26
+ // MCP servers must keep stdout clean for JSON-RPC, so we log to stderr.
27
+ // Claude Desktop surfaces this in the server's "Logs" panel. The banner
28
+ // tells the user which mode they're in (hosted vs free) and where to
29
+ // upgrade, without blocking the transport handshake.
30
+ const PACKAGE_VERSION = "4.0.0-beta.1";
31
+ function logStartupBanner() {
32
+ if (process.env.SCENEVIEW_MCP_QUIET === "1")
33
+ return;
34
+ const proxied = isProxyConfigured();
35
+ const mode = proxied ? "HOSTED (Pro tools → gateway)" : "LITE (free tools only)";
36
+ const lines = [
37
+ `[sceneview-mcp] v${PACKAGE_VERSION} — ${mode}`,
38
+ proxied
39
+ ? `[sceneview-mcp] Pro tool calls will be forwarded to the hosted gateway.`
40
+ : `[sceneview-mcp] Set SCENEVIEW_API_KEY to unlock 36+ Pro tools — ${DEFAULT_PRICING_URL}`,
41
+ ];
42
+ for (const line of lines)
43
+ process.stderr.write(`${line}\n`);
44
+ }
45
+ logStartupBanner();
46
+ const server = new Server({ name: "sceneview-mcp", version: PACKAGE_VERSION }, { capabilities: { resources: {}, tools: {} } });
26
47
  // ─── Telemetry (anonymous, opt-out via SCENEVIEW_TELEMETRY=0) ────────────────
27
48
  //
28
49
  // Fire once when the client finishes the handshake. See `telemetry.ts` and
@@ -65,32 +86,41 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
65
86
  });
66
87
  // ─── Tools ───────────────────────────────────────────────────────────────────
67
88
  server.setRequestHandler(ListToolsRequestSchema, async () => {
68
- // filterToolsForTier has a looser parameter type (index signature) than
69
- // our strict ToolDefinition. The cast is safe: ToolDefinition is a
70
- // superset of { name, description, inputSchema } and filterToolsForTier
71
- // only reads `name` and `description`.
72
- const tools = await filterToolsForTier(TOOL_DEFINITIONS);
89
+ // v4 lite mode: we trust the gateway to enforce Pro access at call time,
90
+ // so listing is purely cosmetic here. If no API key is set we still prefix
91
+ // Pro tool descriptions with "[PRO]" so the AI knows an upgrade is needed
92
+ // and surfaces the upsell in its responses; with a key we expose the full
93
+ // list unmodified.
94
+ const unlocked = isProxyConfigured();
95
+ const tools = TOOL_DEFINITIONS.map((tool) => {
96
+ if (unlocked || !isProTool(tool.name))
97
+ return tool;
98
+ return { ...tool, description: `[PRO] ${tool.description}` };
99
+ });
73
100
  return { tools };
74
101
  });
75
102
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
76
103
  const toolName = request.params.name;
77
- // ── Pro tier access check ──────────────────────────────────────────────────
78
- const access = await checkToolAccess(toolName);
79
- if (!access.allowed) {
80
- return createAccessDeniedResponse(toolName, access.message);
81
- }
104
+ const args = request.params.arguments;
82
105
  // Record anonymous telemetry (fire-and-forget, non-blocking, opt-out via
83
106
  // SCENEVIEW_TELEMETRY=0). See `telemetry.ts` and `PRIVACY.md`.
84
107
  recordToolCall(toolName, getToolTier(toolName));
85
- // Record usage for billing (async, fire-and-forget)
86
- const apiKey = getConfiguredApiKey();
87
- if (apiKey) {
88
- recordUsage(apiKey, toolName).catch(() => { });
108
+ // ── v4 lite-mode routing ─────────────────────────────────────────────────
109
+ //
110
+ // Free tools execute locally, same as 3.6.x. Pro tools are forwarded to
111
+ // the hosted gateway at sceneview-mcp.mcp-tools-lab.workers.dev/mcp —
112
+ // that's where auth, metering, and Stripe live. If no API key is set,
113
+ // `dispatchProxyToolCall` returns a friendly stub that points at the
114
+ // pricing page (handles the upsell itself, no separate denied-response
115
+ // step needed).
116
+ if (isProTool(toolName)) {
117
+ const result = await dispatchProxyToolCall(toolName, args);
118
+ return result;
89
119
  }
90
120
  // The dispatcher returns the narrower SceneView `ToolResult` shape, which
91
121
  // structurally matches the MCP SDK's `CallToolResult` but TS can't prove
92
122
  // it (the SDK's zod-derived type has additional optional members).
93
- const result = await dispatchTool(toolName, request.params.arguments);
123
+ const result = await dispatchTool(toolName, args);
94
124
  return result;
95
125
  });
96
126
  const transport = new StdioServerTransport();
package/dist/proxy.js ADDED
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Proxy helper for the v4 "lite" npm package.
3
+ *
4
+ * In v4 the stdio package keeps serving free-tier tools locally (no
5
+ * network round-trip), but Pro-tier tools are forwarded as JSON-RPC
6
+ * `tools/call` requests to the hosted gateway. This keeps the install
7
+ * footprint small for free users while letting paying customers hit a
8
+ * metered, auth'd backend.
9
+ *
10
+ * The gateway lives at
11
+ * https://sceneview-mcp.mcp-tools-lab.workers.dev/mcp
12
+ * (Cloudflare Workers, post-Stripe-first pivot). Override with
13
+ * `SCENEVIEW_MCP_URL` if you run a self-hosted fork or the staging
14
+ * worker.
15
+ *
16
+ * This module is intentionally dependency-free: it relies on the
17
+ * global `fetch` that Node 18+ exposes and on nothing else from the
18
+ * package.
19
+ */
20
+ /** Default URL of the hosted gateway (post-Stripe-first, Apr 2026). */
21
+ export const DEFAULT_GATEWAY_URL = "https://sceneview-mcp.mcp-tools-lab.workers.dev/mcp";
22
+ /** Public pricing/signup page shown in stubs when no API key is set. */
23
+ export const DEFAULT_PRICING_URL = "https://sceneview-mcp.mcp-tools-lab.workers.dev/pricing";
24
+ // Minimal JSON-RPC 2.0 request id, auto-incremented per process.
25
+ let nextRpcId = 1;
26
+ /**
27
+ * Forwards a Pro-tier tool call to the hosted gateway and returns its
28
+ * `ToolResult`. Network, HTTP, and JSON-RPC errors are converted into
29
+ * a user-visible error block so the MCP client surfaces them in Claude.
30
+ *
31
+ * When no API key is configured at all, returns a helpful stub that
32
+ * points at the pricing page instead of trying to call the gateway.
33
+ * The stub is the user's first touch point with the Pro upsell — it
34
+ * shows up verbatim in the Claude UI.
35
+ */
36
+ export async function dispatchProxyToolCall(toolName, args, options = {}) {
37
+ const apiKey = options.apiKey ?? process.env.SCENEVIEW_API_KEY;
38
+ if (!apiKey) {
39
+ return {
40
+ content: [
41
+ {
42
+ type: "text",
43
+ text: `## 🔒 Pro feature\n\n` +
44
+ `\`${toolName}\` is a SceneView MCP Pro tool. ` +
45
+ `Set \`SCENEVIEW_API_KEY\` to an API key from ` +
46
+ `${DEFAULT_PRICING_URL} to unlock it.\n\n` +
47
+ `Pro unlocks 36+ premium tools: AR, multi-platform setup, ` +
48
+ `scene generation, 3D artifacts, and the Automotive / Gaming ` +
49
+ `/ Healthcare / Interior packages.`,
50
+ },
51
+ ],
52
+ isError: true,
53
+ };
54
+ }
55
+ const gatewayUrl = options.gatewayUrl ??
56
+ process.env.SCENEVIEW_MCP_URL ??
57
+ DEFAULT_GATEWAY_URL;
58
+ const fetchImpl = options.fetchImpl ?? fetch;
59
+ const requestBody = {
60
+ jsonrpc: "2.0",
61
+ id: nextRpcId++,
62
+ method: "tools/call",
63
+ params: { name: toolName, arguments: args ?? {} },
64
+ };
65
+ let response;
66
+ try {
67
+ response = await fetchImpl(gatewayUrl, {
68
+ method: "POST",
69
+ headers: {
70
+ authorization: `Bearer ${apiKey}`,
71
+ "content-type": "application/json",
72
+ accept: "application/json",
73
+ },
74
+ body: JSON.stringify(requestBody),
75
+ });
76
+ }
77
+ catch (err) {
78
+ const detail = err instanceof Error ? err.message : String(err);
79
+ return {
80
+ content: [
81
+ {
82
+ type: "text",
83
+ text: `Failed to reach SceneView MCP gateway (${gatewayUrl}): ${detail}.\n\n` +
84
+ `The gateway may be temporarily down. Try again in a few seconds, ` +
85
+ `or check status at ${DEFAULT_PRICING_URL}.`,
86
+ },
87
+ ],
88
+ isError: true,
89
+ };
90
+ }
91
+ const text = await response.text().catch(() => "");
92
+ if (response.status === 401 || response.status === 403) {
93
+ return {
94
+ content: [
95
+ {
96
+ type: "text",
97
+ text: `## 🔑 Invalid or expired API key\n\n` +
98
+ `The gateway rejected your \`SCENEVIEW_API_KEY\` (HTTP ${response.status}).\n\n` +
99
+ `- If you just subscribed, make sure you copied the full key from ` +
100
+ `the Stripe success page.\n` +
101
+ `- If your subscription was cancelled, reactivate it at ${DEFAULT_PRICING_URL}.\n\n` +
102
+ (text ? `Gateway response: ${text}` : ""),
103
+ },
104
+ ],
105
+ isError: true,
106
+ };
107
+ }
108
+ if (response.status === 429) {
109
+ return {
110
+ content: [
111
+ {
112
+ type: "text",
113
+ text: `## ⏳ Rate limited\n\n` +
114
+ `You've hit the rate limit for \`${toolName}\` (HTTP 429). ` +
115
+ `Wait a few seconds and retry, or upgrade your tier at ` +
116
+ `${DEFAULT_PRICING_URL}.\n\n` +
117
+ (text ? `Gateway response: ${text}` : ""),
118
+ },
119
+ ],
120
+ isError: true,
121
+ };
122
+ }
123
+ if (!response.ok) {
124
+ return {
125
+ content: [
126
+ {
127
+ type: "text",
128
+ text: `Gateway HTTP ${response.status} while calling ${toolName}. ` +
129
+ (text || "No response body."),
130
+ },
131
+ ],
132
+ isError: true,
133
+ };
134
+ }
135
+ let parsed;
136
+ try {
137
+ parsed = JSON.parse(text);
138
+ }
139
+ catch {
140
+ return {
141
+ content: [
142
+ {
143
+ type: "text",
144
+ text: `Gateway returned non-JSON response: ${text}`,
145
+ },
146
+ ],
147
+ isError: true,
148
+ };
149
+ }
150
+ if (parsed.error) {
151
+ return {
152
+ content: [
153
+ {
154
+ type: "text",
155
+ text: parsed.error.message ??
156
+ `Gateway error while calling ${toolName}.`,
157
+ },
158
+ ],
159
+ isError: true,
160
+ };
161
+ }
162
+ const result = parsed.result;
163
+ return {
164
+ content: result?.content ?? [{ type: "text", text: "" }],
165
+ isError: result?.isError ?? false,
166
+ };
167
+ }
168
+ /**
169
+ * Returns true when proxy mode is active: an API key is configured
170
+ * (either via `SCENEVIEW_API_KEY` env var or an explicit override).
171
+ */
172
+ export function isProxyConfigured(apiKey) {
173
+ if (apiKey && apiKey.length > 0)
174
+ return true;
175
+ return !!process.env.SCENEVIEW_API_KEY;
176
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sceneview-mcp",
3
- "version": "3.6.4",
3
+ "version": "4.0.0-beta.1",
4
4
  "mcpName": "io.github.sceneview/mcp",
5
5
  "description": "MCP server for SceneView — cross-platform 3D & AR SDK for Android and iOS. Give Claude the full SceneView SDK so it writes correct, compilable code.",
6
6
  "keywords": [