sceneview-mcp 3.6.2 → 3.6.5
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 +39 -0
- package/dist/analyze-project.js +500 -0
- package/dist/auth.js +84 -0
- package/dist/billing.js +137 -0
- package/dist/convert-platform.js +302 -0
- package/dist/debug-issue.js +2 -2
- package/dist/explain-api.js +245 -0
- package/dist/extra-guides.js +1 -1
- package/dist/generate-animation.js +576 -0
- package/dist/generate-environment.js +483 -0
- package/dist/generate-gesture.js +532 -0
- package/dist/generate-physics.js +570 -0
- package/dist/generate-scene.js +4 -4
- package/dist/generated/llms-txt.js +6 -0
- package/dist/guides.js +8 -8
- package/dist/index.js +54 -1111
- package/dist/migration.js +2 -2
- package/dist/optimize-scene.js +173 -0
- package/dist/platform-setup.js +11 -11
- package/dist/samples.js +64 -64
- package/dist/search-models.js +214 -0
- package/dist/telemetry.js +120 -0
- package/dist/tiers.js +100 -0
- package/dist/tools/definitions.js +467 -0
- package/dist/tools/handler.js +791 -0
- package/dist/tools/index.js +18 -0
- package/dist/tools/types.js +8 -0
- package/dist/validator.js +1 -1
- package/llms.txt +24 -1
- package/package.json +9 -20
package/dist/index.js
CHANGED
|
@@ -1,52 +1,42 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* stdio entrypoint for the `sceneview-mcp` npm package.
|
|
4
|
+
*
|
|
5
|
+
* This file used to be a 1 200+ line monolith that defined every tool,
|
|
6
|
+
* its handler, and the stdio transport all at once. The tool definitions
|
|
7
|
+
* and handler logic now live in `./tools/`, so this file is a thin
|
|
8
|
+
* adapter that wires the library into the MCP stdio server plus the
|
|
9
|
+
* two MCP resources (`sceneview://api`, `sceneview://known-issues`) and
|
|
10
|
+
* the pro-tier access / billing checks.
|
|
11
|
+
*
|
|
12
|
+
* IMPORTANT: the runtime behaviour must stay identical to v3.6.2 for
|
|
13
|
+
* existing npm consumers. Do not reorder checks, do not change content
|
|
14
|
+
* strings, do not touch disclaimers.
|
|
15
|
+
*/
|
|
2
16
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
17
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
18
|
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
-
import { readFileSync } from "fs";
|
|
6
|
-
import { dirname, resolve } from "path";
|
|
7
|
-
import { fileURLToPath } from "url";
|
|
8
|
-
import { getSample, SAMPLE_IDS, SAMPLES } from "./samples.js";
|
|
9
|
-
import { validateCode, formatValidationReport } from "./validator.js";
|
|
10
|
-
import { MIGRATION_GUIDE } from "./migration.js";
|
|
11
19
|
import { fetchKnownIssues } from "./issues.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
function withDisclaimer(content) {
|
|
26
|
-
if (content.length === 0)
|
|
27
|
-
return content;
|
|
28
|
-
const last = content[content.length - 1];
|
|
29
|
-
return [
|
|
30
|
-
...content.slice(0, -1),
|
|
31
|
-
{ ...last, text: last.text + DISCLAIMER },
|
|
32
|
-
];
|
|
33
|
-
}
|
|
34
|
-
let API_DOCS;
|
|
35
|
-
try {
|
|
36
|
-
API_DOCS = readFileSync(resolve(__dirname, "../llms.txt"), "utf-8");
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
API_DOCS = "SceneView API docs not found. Run `npm run prepare` to bundle llms.txt.";
|
|
40
|
-
}
|
|
41
|
-
const NODE_SECTIONS = parseNodeSections(API_DOCS);
|
|
42
|
-
const server = new Server({ name: "sceneview-mcp", version: "3.6.1" }, { capabilities: { resources: {}, tools: {} } });
|
|
20
|
+
import { checkToolAccess, filterToolsForTier, createAccessDeniedResponse } from "./auth.js";
|
|
21
|
+
import { recordUsage, getConfiguredApiKey } from "./billing.js";
|
|
22
|
+
import { recordClientInit, recordToolCall } from "./telemetry.js";
|
|
23
|
+
import { getToolTier } from "./tiers.js";
|
|
24
|
+
import { API_DOCS, TOOL_DEFINITIONS, dispatchTool, } from "./tools/index.js";
|
|
25
|
+
const server = new Server({ name: "sceneview-mcp", version: "3.6.5" }, { capabilities: { resources: {}, tools: {} } });
|
|
26
|
+
// ─── Telemetry (anonymous, opt-out via SCENEVIEW_TELEMETRY=0) ────────────────
|
|
27
|
+
//
|
|
28
|
+
// Fire once when the client finishes the handshake. See `telemetry.ts` and
|
|
29
|
+
// `PRIVACY.md` for what's collected and how to opt out.
|
|
30
|
+
server.oninitialized = () => {
|
|
31
|
+
recordClientInit(server.getClientVersion());
|
|
32
|
+
};
|
|
43
33
|
// ─── Resources ───────────────────────────────────────────────────────────────
|
|
44
34
|
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
45
35
|
resources: [
|
|
46
36
|
{
|
|
47
37
|
uri: "sceneview://api",
|
|
48
38
|
name: "SceneView API Reference",
|
|
49
|
-
description: "Complete SceneView 3.6.
|
|
39
|
+
description: "Complete SceneView 3.6.2 API — SceneView, ARSceneView, SceneScope DSL, ARSceneScope DSL, node types, resource loading, camera, gestures, math types, threading rules, and common patterns. Read this before writing any SceneView code.",
|
|
50
40
|
mimeType: "text/markdown",
|
|
51
41
|
},
|
|
52
42
|
{
|
|
@@ -74,1081 +64,34 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
74
64
|
}
|
|
75
65
|
});
|
|
76
66
|
// ─── Tools ───────────────────────────────────────────────────────────────────
|
|
77
|
-
server.setRequestHandler(ListToolsRequestSchema, async () =>
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
scenario: {
|
|
86
|
-
type: "string",
|
|
87
|
-
enum: SAMPLE_IDS,
|
|
88
|
-
description: `The scenario to fetch:\n${SAMPLE_IDS.map((id) => `- "${id}": ${SAMPLES[id].description}`).join("\n")}`,
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
required: ["scenario"],
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
name: "list_samples",
|
|
96
|
-
description: "Lists all available SceneView code samples with their IDs, descriptions, and tags. Use this to find the right sample before calling `get_sample`, or to show the user what SceneView can do.",
|
|
97
|
-
inputSchema: {
|
|
98
|
-
type: "object",
|
|
99
|
-
properties: {
|
|
100
|
-
tag: {
|
|
101
|
-
type: "string",
|
|
102
|
-
description: "Optional tag to filter by (e.g. \"ar\", \"3d\", \"ios\", \"swift\", \"anchor\", \"geometry\", \"animation\", \"video\", \"lighting\"). Omit to list all samples.",
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
required: [],
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
name: "get_setup",
|
|
110
|
-
description: "Returns the Gradle dependency and AndroidManifest snippet required to use SceneView in an Android project.",
|
|
111
|
-
inputSchema: {
|
|
112
|
-
type: "object",
|
|
113
|
-
properties: {
|
|
114
|
-
type: {
|
|
115
|
-
type: "string",
|
|
116
|
-
enum: ["3d", "ar"],
|
|
117
|
-
description: '"3d" for 3D-only scenes. "ar" for augmented reality (includes 3D).',
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
required: ["type"],
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
name: "validate_code",
|
|
125
|
-
description: "Checks a Kotlin or Swift SceneView snippet for common mistakes. For Kotlin: threading violations, wrong destroy order, missing null-checks, LightNode trailing-lambda bug, deprecated 2.x APIs. For Swift: missing @MainActor, async/await patterns, missing imports, RealityKit mistakes. Language is auto-detected. Always call this before presenting generated SceneView code to the user.",
|
|
126
|
-
inputSchema: {
|
|
127
|
-
type: "object",
|
|
128
|
-
properties: {
|
|
129
|
-
code: {
|
|
130
|
-
type: "string",
|
|
131
|
-
description: "The Kotlin or Swift source code to validate (composable function, SwiftUI view, class, or file). Language is auto-detected.",
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
required: ["code"],
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
name: "get_migration_guide",
|
|
139
|
-
description: "Returns the full SceneView 2.x → 3.0 migration guide. Use this when a user reports code that worked in 2.x but breaks in 3.0, or when helping someone upgrade.",
|
|
140
|
-
inputSchema: {
|
|
141
|
-
type: "object",
|
|
142
|
-
properties: {},
|
|
143
|
-
required: [],
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
name: "get_node_reference",
|
|
148
|
-
description: "Returns the full API reference for a specific SceneView node type or composable — parameters, types, and a usage example — parsed directly from the official llms.txt. Use this when you need the exact signature or options for a node (e.g. ModelNode, LightNode, ARScene). If the requested type is not found, the response lists all available types.",
|
|
149
|
-
inputSchema: {
|
|
150
|
-
type: "object",
|
|
151
|
-
properties: {
|
|
152
|
-
nodeType: {
|
|
153
|
-
type: "string",
|
|
154
|
-
description: 'The node type or composable name to look up, e.g. "ModelNode", "LightNode", "ARScene", "AnchorNode". Case-insensitive.',
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
required: ["nodeType"],
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
name: "get_platform_roadmap",
|
|
162
|
-
description: "Returns the SceneView multi-platform roadmap — current Android support status, planned iOS/KMP/web targets, and timeline. Use this when the user asks about cross-platform support, iOS, Kotlin Multiplatform, or future plans.",
|
|
163
|
-
inputSchema: {
|
|
164
|
-
type: "object",
|
|
165
|
-
properties: {},
|
|
166
|
-
required: [],
|
|
167
|
-
},
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
name: "get_best_practices",
|
|
171
|
-
description: "Returns SceneView performance and architecture best practices — memory management, model optimization, threading rules, Compose integration patterns, and common anti-patterns. Use this when the user asks about performance, optimization, best practices, or architecture.",
|
|
172
|
-
inputSchema: {
|
|
173
|
-
type: "object",
|
|
174
|
-
properties: {
|
|
175
|
-
category: {
|
|
176
|
-
type: "string",
|
|
177
|
-
enum: ["all", "performance", "architecture", "memory", "threading"],
|
|
178
|
-
description: 'Category to filter by. "all" returns everything. Defaults to "all" if omitted.',
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
required: [],
|
|
182
|
-
},
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
name: "get_ar_setup",
|
|
186
|
-
description: "Returns detailed AR setup instructions — AndroidManifest permissions and features, Gradle dependencies, ARCore session configuration options (depth, light estimation, instant placement, plane detection, image tracking, cloud anchors), and a complete working AR starter template. More detailed than `get_setup` for AR-specific configuration.",
|
|
187
|
-
inputSchema: {
|
|
188
|
-
type: "object",
|
|
189
|
-
properties: {},
|
|
190
|
-
required: [],
|
|
191
|
-
},
|
|
192
|
-
},
|
|
193
|
-
{
|
|
194
|
-
name: "get_troubleshooting",
|
|
195
|
-
description: "Returns the SceneView troubleshooting guide — common crashes (SIGABRT, model not showing), build failures, AR issues (drift, overexposure, image detection), and performance problems. Use this when a user reports something not working, a crash, or unexpected behavior.",
|
|
196
|
-
inputSchema: {
|
|
197
|
-
type: "object",
|
|
198
|
-
properties: {},
|
|
199
|
-
required: [],
|
|
200
|
-
},
|
|
201
|
-
},
|
|
202
|
-
{
|
|
203
|
-
name: "get_ios_setup",
|
|
204
|
-
description: "Returns the complete iOS setup guide for SceneViewSwift — SPM dependency, Package.swift example, minimum platform versions, Info.plist entries for AR (camera permission), and basic SwiftUI integration code. Use this when a user wants to set up SceneView for iOS, macOS, or visionOS.",
|
|
205
|
-
inputSchema: {
|
|
206
|
-
type: "object",
|
|
207
|
-
properties: {
|
|
208
|
-
type: {
|
|
209
|
-
type: "string",
|
|
210
|
-
enum: ["3d", "ar"],
|
|
211
|
-
description: '"3d" for 3D-only scenes. "ar" for augmented reality (requires iOS, not macOS/visionOS via this path).',
|
|
212
|
-
},
|
|
213
|
-
},
|
|
214
|
-
required: ["type"],
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
name: "get_web_setup",
|
|
219
|
-
description: "Returns the complete Web setup guide for SceneView Web — npm install, Kotlin/JS Gradle config, HTML canvas setup, and basic Filament.js integration code. SceneView Web uses the same Filament rendering engine as Android, compiled to WebAssembly. Use this when a user wants to set up SceneView for browsers.",
|
|
220
|
-
inputSchema: {
|
|
221
|
-
type: "object",
|
|
222
|
-
properties: {},
|
|
223
|
-
},
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
name: "render_3d_preview",
|
|
227
|
-
description: "Generates an interactive 3D preview link. Accepts a model URL, a SceneView code snippet, or both. Returns a URL to sceneview.github.io/preview that renders the model in the browser with orbit controls, AR support, and sharing. For model URLs: embeds a model-viewer link directly. For code snippets: shows the 3D preview with the code in a companion panel. Use this when you want to show a 3D model to the user — paste the link in your response and they can click to see it live.",
|
|
228
|
-
inputSchema: {
|
|
229
|
-
type: "object",
|
|
230
|
-
properties: {
|
|
231
|
-
modelUrl: {
|
|
232
|
-
type: "string",
|
|
233
|
-
description: "Public URL to a .glb or .gltf model file. Must be HTTPS and CORS-enabled. If omitted, a default model is used.",
|
|
234
|
-
},
|
|
235
|
-
codeSnippet: {
|
|
236
|
-
type: "string",
|
|
237
|
-
description: "SceneView code snippet (Kotlin or Swift) to display alongside the 3D preview in a companion panel. Useful when showing generated code together with a live preview.",
|
|
238
|
-
},
|
|
239
|
-
autoRotate: {
|
|
240
|
-
type: "boolean",
|
|
241
|
-
description: "Auto-rotate the model (default: true).",
|
|
242
|
-
},
|
|
243
|
-
ar: {
|
|
244
|
-
type: "boolean",
|
|
245
|
-
description: "Enable AR mode on supported devices (default: true).",
|
|
246
|
-
},
|
|
247
|
-
title: {
|
|
248
|
-
type: "string",
|
|
249
|
-
description: "Custom title shown above the preview.",
|
|
250
|
-
},
|
|
251
|
-
},
|
|
252
|
-
required: [],
|
|
253
|
-
},
|
|
254
|
-
},
|
|
255
|
-
{
|
|
256
|
-
name: "create_3d_artifact",
|
|
257
|
-
description: 'Generates a complete, self-contained HTML page with interactive 3D content that Claude can render as an artifact. Returns valid HTML using model-viewer (Google\'s web component for 3D). Use this when the user asks to "show", "preview", "visualize" 3D models, create 3D charts/dashboards, or view products in 360°. The HTML works standalone in any browser, supports AR on mobile, and includes orbit controls. Types: "model-viewer" for 3D model viewing, "chart-3d" for 3D data visualization (bar charts with perspective), "scene" for rich 3D scenes with lighting, "product-360" for product turntables with hotspot annotations.',
|
|
258
|
-
inputSchema: {
|
|
259
|
-
type: "object",
|
|
260
|
-
properties: {
|
|
261
|
-
type: {
|
|
262
|
-
type: "string",
|
|
263
|
-
enum: ["model-viewer", "chart-3d", "scene", "product-360", "geometry"],
|
|
264
|
-
description: '"model-viewer": interactive 3D model viewer with orbit + AR. "chart-3d": 3D bar chart for data visualization. "scene": rich 3D scene with lighting. "product-360": product turntable with hotspot annotations. "geometry": procedural 3D shapes (cubes, spheres, cylinders, planes, lines) — Claude can DRAW in 3D! Use this when the user asks to draw, build, or visualize 3D shapes.',
|
|
265
|
-
},
|
|
266
|
-
modelUrl: {
|
|
267
|
-
type: "string",
|
|
268
|
-
description: "Public HTTPS URL to a .glb or .gltf model. If omitted, a default model is used. Not needed for chart-3d type.",
|
|
269
|
-
},
|
|
270
|
-
title: {
|
|
271
|
-
type: "string",
|
|
272
|
-
description: "Title displayed on the artifact. Defaults to a sensible name per type.",
|
|
273
|
-
},
|
|
274
|
-
data: {
|
|
275
|
-
type: "array",
|
|
276
|
-
items: {
|
|
277
|
-
type: "object",
|
|
278
|
-
properties: {
|
|
279
|
-
label: { type: "string", description: "Data point label (e.g. 'Q1 2025')" },
|
|
280
|
-
value: { type: "number", description: "Numeric value" },
|
|
281
|
-
color: { type: "string", description: "Optional hex color (e.g. '#4285F4'). Auto-assigned if omitted." },
|
|
282
|
-
},
|
|
283
|
-
required: ["label", "value"],
|
|
284
|
-
},
|
|
285
|
-
description: 'Data array for chart-3d type. Each item has {label, value, color?}. Required for "chart-3d", ignored for other types.',
|
|
286
|
-
},
|
|
287
|
-
options: {
|
|
288
|
-
type: "object",
|
|
289
|
-
properties: {
|
|
290
|
-
autoRotate: { type: "boolean", description: "Auto-rotate the model (default: true)" },
|
|
291
|
-
ar: { type: "boolean", description: "Enable AR on mobile devices (default: true)" },
|
|
292
|
-
backgroundColor: { type: "string", description: "Background color as hex (default: '#1a1a2e')" },
|
|
293
|
-
cameraOrbit: { type: "string", description: "Camera orbit string (default: '0deg 75deg 105%')" },
|
|
294
|
-
},
|
|
295
|
-
description: "Visual options for the 3D artifact.",
|
|
296
|
-
},
|
|
297
|
-
hotspots: {
|
|
298
|
-
type: "array",
|
|
299
|
-
items: {
|
|
300
|
-
type: "object",
|
|
301
|
-
properties: {
|
|
302
|
-
position: { type: "string", description: 'Hotspot 3D position, e.g. "0.5 1.2 0.3"' },
|
|
303
|
-
normal: { type: "string", description: 'Hotspot surface normal, e.g. "0 1 0"' },
|
|
304
|
-
label: { type: "string", description: "Hotspot label" },
|
|
305
|
-
description: { type: "string", description: "Hotspot description" },
|
|
306
|
-
},
|
|
307
|
-
required: ["position", "normal", "label"],
|
|
308
|
-
},
|
|
309
|
-
description: "Annotation hotspots for product-360 type. Each has position, normal, label, and optional description.",
|
|
310
|
-
},
|
|
311
|
-
shapes: {
|
|
312
|
-
type: "array",
|
|
313
|
-
items: {
|
|
314
|
-
type: "object",
|
|
315
|
-
properties: {
|
|
316
|
-
type: {
|
|
317
|
-
type: "string",
|
|
318
|
-
enum: ["cube", "sphere", "cylinder", "plane", "line"],
|
|
319
|
-
description: 'Shape type: "cube", "sphere", "cylinder", "plane" (flat surface), or "line" (thin cylinder connecting points).',
|
|
320
|
-
},
|
|
321
|
-
position: {
|
|
322
|
-
type: "array",
|
|
323
|
-
items: { type: "number" },
|
|
324
|
-
description: "Position [x, y, z] in world space. Y is up. Default: [0, 0, 0].",
|
|
325
|
-
},
|
|
326
|
-
scale: {
|
|
327
|
-
type: "array",
|
|
328
|
-
items: { type: "number" },
|
|
329
|
-
description: "Scale [x, y, z]. For cube: edge sizes. For sphere: diameters. For line: [length, thickness, thickness]. Default: [1, 1, 1].",
|
|
330
|
-
},
|
|
331
|
-
color: {
|
|
332
|
-
type: "array",
|
|
333
|
-
items: { type: "number" },
|
|
334
|
-
description: "Color [r, g, b] in 0-1 range. E.g. [1, 0, 0] for red, [0.2, 0.5, 1] for sky blue. Default: [0.8, 0.8, 0.8].",
|
|
335
|
-
},
|
|
336
|
-
metallic: {
|
|
337
|
-
type: "number",
|
|
338
|
-
description: "Metallic factor 0-1. 0 = plastic/matte, 1 = metal. Default: 0.",
|
|
339
|
-
},
|
|
340
|
-
roughness: {
|
|
341
|
-
type: "number",
|
|
342
|
-
description: "Roughness factor 0-1. 0 = mirror/glossy, 1 = rough/matte. Default: 0.5.",
|
|
343
|
-
},
|
|
344
|
-
},
|
|
345
|
-
required: ["type"],
|
|
346
|
-
},
|
|
347
|
-
description: 'Array of procedural 3D shapes for "geometry" type. Each shape has type, position, scale, color, metallic, roughness. Required for "geometry" type. Example: [{type:"cube",position:[0,0.5,0],scale:[1,1,1],color:[1,0,0]},{type:"sphere",position:[0,1.8,0],scale:[0.6,0.6,0.6],color:[0,0,1]}]',
|
|
348
|
-
},
|
|
349
|
-
},
|
|
350
|
-
required: ["type"],
|
|
351
|
-
},
|
|
352
|
-
},
|
|
353
|
-
{
|
|
354
|
-
name: "get_platform_setup",
|
|
355
|
-
description: "Returns the complete setup guide for any SceneView-supported platform: Android, iOS, Web, Flutter, React Native, Desktop, or Android TV. Includes dependencies, manifest/permissions, minimum SDK, and a working starter template. Replaces `get_setup`, `get_ios_setup`, and `get_web_setup` with a single unified tool. Use this when a user wants to set up SceneView on any platform.",
|
|
356
|
-
inputSchema: {
|
|
357
|
-
type: "object",
|
|
358
|
-
properties: {
|
|
359
|
-
platform: {
|
|
360
|
-
type: "string",
|
|
361
|
-
enum: PLATFORM_IDS,
|
|
362
|
-
description: `Target platform:\n${PLATFORM_IDS.map((p) => `- "${p}"`).join("\n")}`,
|
|
363
|
-
},
|
|
364
|
-
type: {
|
|
365
|
-
type: "string",
|
|
366
|
-
enum: ["3d", "ar"],
|
|
367
|
-
description: '"3d" for 3D-only scenes. "ar" for augmented reality. Some platforms only support 3D.',
|
|
368
|
-
},
|
|
369
|
-
},
|
|
370
|
-
required: ["platform", "type"],
|
|
371
|
-
},
|
|
372
|
-
},
|
|
373
|
-
{
|
|
374
|
-
name: "migrate_code",
|
|
375
|
-
description: "Automatically migrates SceneView 2.x Kotlin code to 3.x. Applies known renames (SceneView→Scene, ArSceneView→ARScene), replaces deprecated APIs (loadModelAsync→rememberModelInstance, Engine.create→rememberEngine), fixes LightNode trailing-lambda bug, removes Sceneform imports, and more. Returns the migrated code with a detailed changelog. Use this when a user has 2.x code that needs updating, or when you detect 2.x patterns in their code.",
|
|
376
|
-
inputSchema: {
|
|
377
|
-
type: "object",
|
|
378
|
-
properties: {
|
|
379
|
-
code: {
|
|
380
|
-
type: "string",
|
|
381
|
-
description: "The SceneView 2.x Kotlin code to migrate. Can be a snippet, a function, or a full file.",
|
|
382
|
-
},
|
|
383
|
-
},
|
|
384
|
-
required: ["code"],
|
|
385
|
-
},
|
|
386
|
-
},
|
|
387
|
-
{
|
|
388
|
-
name: "debug_issue",
|
|
389
|
-
description: 'Returns a targeted debugging guide for a specific SceneView issue. Categories: "model-not-showing" (invisible models), "ar-not-working" (AR camera/planes), "crash" (SIGABRT/native), "performance" (low FPS/memory), "build-error" (Gradle/dependency), "black-screen" (no rendering), "lighting" (dark/bright/shadows), "gestures" (touch/drag), "ios" (Swift/RealityKit). You can provide a category directly, or describe the problem and it will be auto-detected. Use this when a user reports something not working.',
|
|
390
|
-
inputSchema: {
|
|
391
|
-
type: "object",
|
|
392
|
-
properties: {
|
|
393
|
-
category: {
|
|
394
|
-
type: "string",
|
|
395
|
-
enum: DEBUG_CATEGORIES,
|
|
396
|
-
description: `The issue category. If omitted, provide \`description\` for auto-detection.\n${DEBUG_CATEGORIES.map((c) => `- "${c}"`).join("\n")}`,
|
|
397
|
-
},
|
|
398
|
-
description: {
|
|
399
|
-
type: "string",
|
|
400
|
-
description: 'Free-text description of the issue (e.g., "my model is not showing", "app crashes on destroy"). Used for auto-detection when category is omitted.',
|
|
401
|
-
},
|
|
402
|
-
},
|
|
403
|
-
required: [],
|
|
404
|
-
},
|
|
405
|
-
},
|
|
406
|
-
{
|
|
407
|
-
name: "generate_scene",
|
|
408
|
-
description: 'Generates a complete, compilable Scene{} or ARScene{} Kotlin composable from a natural language description. Parses 40+ object types (furniture, vehicles, animals, food, buildings, nature), quantities ("two chairs", "3 spheres"), environment (indoor/outdoor/dark), and mode (3D or AR). Returns working Kotlin code with proper engine setup, model loading, lighting, and ground plane. Use this when a user says "build me a scene with..." or describes a 3D scene they want to create.',
|
|
409
|
-
inputSchema: {
|
|
410
|
-
type: "object",
|
|
411
|
-
properties: {
|
|
412
|
-
description: {
|
|
413
|
-
type: "string",
|
|
414
|
-
description: 'Natural language description of the desired 3D scene. Examples: "a room with a table and two chairs", "AR scene with a robot on the floor", "outdoor scene with three trees and a car", "dark room with a sphere and a cube", "a dog and a cat in a garden", "house with a fence and flowers".',
|
|
415
|
-
},
|
|
416
|
-
},
|
|
417
|
-
required: ["description"],
|
|
418
|
-
},
|
|
419
|
-
},
|
|
420
|
-
{
|
|
421
|
-
name: "list_platforms",
|
|
422
|
-
description: "Returns all platforms supported by SceneView with their renderer, framework, status, and version. Use this to answer questions about what platforms SceneView supports, or to show cross-platform capabilities.",
|
|
423
|
-
inputSchema: {
|
|
424
|
-
type: "object",
|
|
425
|
-
properties: {},
|
|
426
|
-
required: [],
|
|
427
|
-
},
|
|
428
|
-
},
|
|
429
|
-
{
|
|
430
|
-
name: "get_animation_guide",
|
|
431
|
-
description: "Returns a comprehensive guide for animating 3D models in SceneView — playing embedded glTF animations, Spring physics animations (KMP core), Compose property animations (animateFloatAsState, InfiniteTransition), SmoothTransform for smooth following, and AR animated models. Includes compilable Kotlin code samples. Use this when a user asks about animation, motion, springs, smooth movement, or how to play model animations.",
|
|
432
|
-
inputSchema: {
|
|
433
|
-
type: "object",
|
|
434
|
-
properties: {},
|
|
435
|
-
required: [],
|
|
436
|
-
},
|
|
437
|
-
},
|
|
438
|
-
{
|
|
439
|
-
name: "get_gesture_guide",
|
|
440
|
-
description: "Returns a comprehensive guide for adding gestures to 3D objects in SceneView — isEditable for one-line pinch-to-scale/drag-to-rotate/tap-to-select, custom onTouchEvent handlers, AR tap-to-place, drag-to-rotate with sensitivity, pinch-to-scale with limits, multi-model selection, and HitResultNode surface cursor. Includes compilable Kotlin code samples. Use this when a user asks about touch, gestures, interaction, drag, pinch, tap, or editing 3D objects.",
|
|
441
|
-
inputSchema: {
|
|
442
|
-
type: "object",
|
|
443
|
-
properties: {},
|
|
444
|
-
required: [],
|
|
445
|
-
},
|
|
446
|
-
},
|
|
447
|
-
{
|
|
448
|
-
name: "get_performance_tips",
|
|
449
|
-
description: "Returns a comprehensive performance optimization guide for SceneView — polygon budgets per device tier, LOD, texture compression (KTX2/Basis Universal), mesh compression (Draco/Meshopt), engine reuse, per-frame allocation avoidance, frustum culling, instancing, lighting optimization, post-processing costs, and profiling with Systrace and Android GPU Inspector. Includes code samples and CLI commands. Use this when a user asks about performance, optimization, FPS, memory, profiling, or slow rendering.",
|
|
450
|
-
inputSchema: {
|
|
451
|
-
type: "object",
|
|
452
|
-
properties: {},
|
|
453
|
-
required: [],
|
|
454
|
-
},
|
|
455
|
-
},
|
|
456
|
-
{
|
|
457
|
-
name: "get_material_guide",
|
|
458
|
-
description: "Returns a comprehensive guide for PBR materials in SceneView — baseColor, metallic, roughness, reflectance, emissive, clearCoat, normal maps. Includes recipes for common materials (glass, chrome, gold, rubber, car paint), code samples for modifying materials on ModelNode, texture setup, and environment lighting requirements. Use this when a user asks about materials, textures, colors, shaders, appearance, or why their model looks wrong (flat, dark, too shiny).",
|
|
459
|
-
inputSchema: {
|
|
460
|
-
type: "object",
|
|
461
|
-
properties: {},
|
|
462
|
-
required: [],
|
|
463
|
-
},
|
|
464
|
-
},
|
|
465
|
-
{
|
|
466
|
-
name: "get_collision_guide",
|
|
467
|
-
description: "Returns a comprehensive guide for collision detection, hit testing, and physics in SceneView — node tapping (onTouchEvent), AR surface hit testing (frame.hitTest), ray-box/ray-sphere intersection (KMP core), bounding boxes, and basic rigid body physics. Use this when a user asks about tapping 3D objects, collision detection, physics simulation, ray casting, or hit testing.",
|
|
468
|
-
inputSchema: {
|
|
469
|
-
type: "object",
|
|
470
|
-
properties: {},
|
|
471
|
-
required: [],
|
|
472
|
-
},
|
|
473
|
-
},
|
|
474
|
-
{
|
|
475
|
-
name: "get_model_optimization_guide",
|
|
476
|
-
description: "Returns a complete guide for optimizing 3D models for SceneView — polygon budgets per device tier, file size targets, Draco/Meshopt mesh compression, KTX2 texture compression, the recommended optimization pipeline (gltf-transform CLI), texture sizing rules, LOD strategies, and quick wins. Use this when a user asks about model optimization, file size, load times, polygon count, texture compression, or preparing models for mobile.",
|
|
477
|
-
inputSchema: {
|
|
478
|
-
type: "object",
|
|
479
|
-
properties: {},
|
|
480
|
-
required: [],
|
|
481
|
-
},
|
|
482
|
-
},
|
|
483
|
-
{
|
|
484
|
-
name: "get_web_rendering_guide",
|
|
485
|
-
description: "Returns a comprehensive guide for SceneView Web (Filament.js WASM) — architecture, quick start (sceneview.js npm or Kotlin/JS), IBL environment lighting (critical for PBR quality), rendering quality settings (SSAO, bloom, TAA), camera exposure tuning, Filament.js vs model-viewer comparison, and web performance tips. Use this when a user asks about web 3D rendering, Filament.js, browser viewing, WebGL, or wants to display 3D models in a web page.",
|
|
486
|
-
inputSchema: {
|
|
487
|
-
type: "object",
|
|
488
|
-
properties: {},
|
|
489
|
-
required: [],
|
|
490
|
-
},
|
|
491
|
-
},
|
|
492
|
-
],
|
|
493
|
-
}));
|
|
494
|
-
// ─── Tool handlers ────────────────────────────────────────────────────────────
|
|
67
|
+
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);
|
|
73
|
+
return { tools };
|
|
74
|
+
});
|
|
495
75
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
isError: true,
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
const isIos = sample.language === "swift";
|
|
513
|
-
const depBlock = isIos
|
|
514
|
-
? [
|
|
515
|
-
`**SPM dependency:**`,
|
|
516
|
-
`\`\`\`swift`,
|
|
517
|
-
`.package(url: "${sample.spmDependency ?? sample.dependency}", from: "3.6.1")`,
|
|
518
|
-
`\`\`\``,
|
|
519
|
-
]
|
|
520
|
-
: [
|
|
521
|
-
`**Gradle dependency:**`,
|
|
522
|
-
`\`\`\`kotlin`,
|
|
523
|
-
`implementation("${sample.dependency}")`,
|
|
524
|
-
`\`\`\``,
|
|
525
|
-
];
|
|
526
|
-
const codeLang = isIos ? "swift" : "kotlin";
|
|
527
|
-
const codeLabel = isIos ? "**Swift (SwiftUI):**" : "**Kotlin (Jetpack Compose):**";
|
|
528
|
-
return {
|
|
529
|
-
content: withDisclaimer([
|
|
530
|
-
{
|
|
531
|
-
type: "text",
|
|
532
|
-
text: [
|
|
533
|
-
`## ${sample.title}`,
|
|
534
|
-
``,
|
|
535
|
-
`**Tags:** ${sample.tags.join(", ")}`,
|
|
536
|
-
``,
|
|
537
|
-
...depBlock,
|
|
538
|
-
``,
|
|
539
|
-
codeLabel,
|
|
540
|
-
`\`\`\`${codeLang}`,
|
|
541
|
-
sample.code,
|
|
542
|
-
`\`\`\``,
|
|
543
|
-
``,
|
|
544
|
-
`**Prompt that generates this:**`,
|
|
545
|
-
`> ${sample.prompt}`,
|
|
546
|
-
].join("\n"),
|
|
547
|
-
},
|
|
548
|
-
]),
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
// ── list_samples ──────────────────────────────────────────────────────────
|
|
552
|
-
case "list_samples": {
|
|
553
|
-
const filterTag = request.params.arguments?.tag;
|
|
554
|
-
const entries = Object.values(SAMPLES).filter((s) => !filterTag || s.tags.includes(filterTag));
|
|
555
|
-
if (entries.length === 0) {
|
|
556
|
-
return {
|
|
557
|
-
content: [
|
|
558
|
-
{
|
|
559
|
-
type: "text",
|
|
560
|
-
text: `No samples found with tag "${filterTag}". Available tags: 3d, ar, model, geometry, animation, camera, environment, anchor, plane-detection, image-tracking, cloud-anchor, point-cloud, placement, gestures, physics, sky, fog, lines, text, reflection, post-processing, ios, swift, video, lighting`,
|
|
561
|
-
},
|
|
562
|
-
],
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
const header = filterTag
|
|
566
|
-
? `## SceneView samples tagged \`${filterTag}\` (${entries.length})\n`
|
|
567
|
-
: `## All SceneView samples (${entries.length})\n`;
|
|
568
|
-
const rows = entries
|
|
569
|
-
.map((s) => {
|
|
570
|
-
const depLabel = s.language === "swift" ? "*SPM:*" : "*Dependency:*";
|
|
571
|
-
return `### \`${s.id}\`\n**${s.title}**${s.language === "swift" ? " (Swift/iOS)" : ""}\n${s.description}\n*Tags:* ${s.tags.join(", ")}\n${depLabel} \`${s.dependency}\`\n\nCall \`get_sample("${s.id}")\` for the full code.`;
|
|
572
|
-
})
|
|
573
|
-
.join("\n\n---\n\n");
|
|
574
|
-
return { content: withDisclaimer([{ type: "text", text: header + rows }]) };
|
|
575
|
-
}
|
|
576
|
-
// ── get_setup ─────────────────────────────────────────────────────────────
|
|
577
|
-
case "get_setup": {
|
|
578
|
-
const type = request.params.arguments?.type;
|
|
579
|
-
if (type === "3d") {
|
|
580
|
-
return {
|
|
581
|
-
content: withDisclaimer([
|
|
582
|
-
{
|
|
583
|
-
type: "text",
|
|
584
|
-
text: [
|
|
585
|
-
`## SceneView — 3D setup`,
|
|
586
|
-
``,
|
|
587
|
-
`### build.gradle.kts`,
|
|
588
|
-
`\`\`\`kotlin`,
|
|
589
|
-
`dependencies {`,
|
|
590
|
-
` implementation("io.github.sceneview:sceneview:3.6.0")`,
|
|
591
|
-
`}`,
|
|
592
|
-
`\`\`\``,
|
|
593
|
-
``,
|
|
594
|
-
`No manifest changes required for 3D-only scenes.`,
|
|
595
|
-
].join("\n"),
|
|
596
|
-
},
|
|
597
|
-
]),
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
if (type === "ar") {
|
|
601
|
-
return {
|
|
602
|
-
content: withDisclaimer([
|
|
603
|
-
{
|
|
604
|
-
type: "text",
|
|
605
|
-
text: [
|
|
606
|
-
`## SceneView — AR setup`,
|
|
607
|
-
``,
|
|
608
|
-
`### build.gradle.kts`,
|
|
609
|
-
`\`\`\`kotlin`,
|
|
610
|
-
`dependencies {`,
|
|
611
|
-
` implementation("io.github.sceneview:arsceneview:3.6.0")`,
|
|
612
|
-
`}`,
|
|
613
|
-
`\`\`\``,
|
|
614
|
-
``,
|
|
615
|
-
`### AndroidManifest.xml`,
|
|
616
|
-
`\`\`\`xml`,
|
|
617
|
-
`<uses-permission android:name="android.permission.CAMERA" />`,
|
|
618
|
-
`<uses-feature android:name="android.hardware.camera.ar" android:required="true" />`,
|
|
619
|
-
`<application>`,
|
|
620
|
-
` <meta-data android:name="com.google.ar.core" android:value="required" />`,
|
|
621
|
-
`</application>`,
|
|
622
|
-
`\`\`\``,
|
|
623
|
-
].join("\n"),
|
|
624
|
-
},
|
|
625
|
-
]),
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
return {
|
|
629
|
-
content: [{ type: "text", text: `Unknown type "${type}". Use "3d" or "ar".` }],
|
|
630
|
-
isError: true,
|
|
631
|
-
};
|
|
632
|
-
}
|
|
633
|
-
// ── validate_code ─────────────────────────────────────────────────────────
|
|
634
|
-
case "validate_code": {
|
|
635
|
-
const code = request.params.arguments?.code;
|
|
636
|
-
if (!code || typeof code !== "string") {
|
|
637
|
-
return {
|
|
638
|
-
content: [{ type: "text", text: "Missing required parameter: `code`" }],
|
|
639
|
-
isError: true,
|
|
640
|
-
};
|
|
641
|
-
}
|
|
642
|
-
const issues = validateCode(code);
|
|
643
|
-
const report = formatValidationReport(issues);
|
|
644
|
-
return { content: withDisclaimer([{ type: "text", text: report }]) };
|
|
645
|
-
}
|
|
646
|
-
// ── get_migration_guide ───────────────────────────────────────────────────
|
|
647
|
-
case "get_migration_guide": {
|
|
648
|
-
return { content: withDisclaimer([{ type: "text", text: MIGRATION_GUIDE }]) };
|
|
649
|
-
}
|
|
650
|
-
// ── get_node_reference ────────────────────────────────────────────────────
|
|
651
|
-
case "get_node_reference": {
|
|
652
|
-
const nodeType = request.params.arguments?.nodeType;
|
|
653
|
-
if (!nodeType || typeof nodeType !== "string") {
|
|
654
|
-
return {
|
|
655
|
-
content: [{ type: "text", text: "Missing required parameter: `nodeType`" }],
|
|
656
|
-
isError: true,
|
|
657
|
-
};
|
|
658
|
-
}
|
|
659
|
-
const section = findNodeSection(NODE_SECTIONS, nodeType);
|
|
660
|
-
if (!section) {
|
|
661
|
-
const available = listNodeTypes(NODE_SECTIONS).join(", ");
|
|
662
|
-
return {
|
|
663
|
-
content: [
|
|
664
|
-
{
|
|
665
|
-
type: "text",
|
|
666
|
-
text: [
|
|
667
|
-
`No reference found for node type \`${nodeType}\`.`,
|
|
668
|
-
``,
|
|
669
|
-
`**Available node types:**`,
|
|
670
|
-
available,
|
|
671
|
-
].join("\n"),
|
|
672
|
-
},
|
|
673
|
-
],
|
|
674
|
-
};
|
|
675
|
-
}
|
|
676
|
-
return {
|
|
677
|
-
content: withDisclaimer([
|
|
678
|
-
{
|
|
679
|
-
type: "text",
|
|
680
|
-
text: [
|
|
681
|
-
`## \`${section.name}\` — API Reference`,
|
|
682
|
-
``,
|
|
683
|
-
section.content,
|
|
684
|
-
].join("\n"),
|
|
685
|
-
},
|
|
686
|
-
]),
|
|
687
|
-
};
|
|
688
|
-
}
|
|
689
|
-
// ── get_platform_roadmap ────────────────────────────────────────────────
|
|
690
|
-
case "get_platform_roadmap": {
|
|
691
|
-
return { content: withDisclaimer([{ type: "text", text: PLATFORM_ROADMAP }]) };
|
|
692
|
-
}
|
|
693
|
-
// ── get_best_practices ───────────────────────────────────────────────────
|
|
694
|
-
case "get_best_practices": {
|
|
695
|
-
const category = request.params.arguments?.category || "all";
|
|
696
|
-
const text = BEST_PRACTICES[category] ?? BEST_PRACTICES["all"];
|
|
697
|
-
return { content: withDisclaimer([{ type: "text", text }]) };
|
|
698
|
-
}
|
|
699
|
-
// ── get_ar_setup ─────────────────────────────────────────────────────────
|
|
700
|
-
case "get_ar_setup": {
|
|
701
|
-
return { content: withDisclaimer([{ type: "text", text: AR_SETUP_GUIDE }]) };
|
|
702
|
-
}
|
|
703
|
-
// ── get_troubleshooting ──────────────────────────────────────────────────
|
|
704
|
-
case "get_troubleshooting": {
|
|
705
|
-
return { content: withDisclaimer([{ type: "text", text: TROUBLESHOOTING_GUIDE }]) };
|
|
706
|
-
}
|
|
707
|
-
// ── get_ios_setup ─────────────────────────────────────────────────────────
|
|
708
|
-
case "get_ios_setup": {
|
|
709
|
-
const iosType = request.params.arguments?.type;
|
|
710
|
-
if (iosType === "3d") {
|
|
711
|
-
return {
|
|
712
|
-
content: withDisclaimer([
|
|
713
|
-
{
|
|
714
|
-
type: "text",
|
|
715
|
-
text: [
|
|
716
|
-
`## SceneViewSwift — iOS/macOS/visionOS 3D Setup`,
|
|
717
|
-
``,
|
|
718
|
-
`### 1. Add SPM Dependency`,
|
|
719
|
-
``,
|
|
720
|
-
`In Xcode: **File → Add Package Dependencies** → paste:`,
|
|
721
|
-
`\`\`\``,
|
|
722
|
-
`https://github.com/sceneview/sceneview`,
|
|
723
|
-
`\`\`\``,
|
|
724
|
-
`Set version rule to **"from: 3.6.0"**.`,
|
|
725
|
-
``,
|
|
726
|
-
`Or in Package.swift:`,
|
|
727
|
-
`\`\`\`swift`,
|
|
728
|
-
`// swift-tools-version: 5.10`,
|
|
729
|
-
`import PackageDescription`,
|
|
730
|
-
``,
|
|
731
|
-
`let package = Package(`,
|
|
732
|
-
` name: "MyApp",`,
|
|
733
|
-
` platforms: [.iOS(.v17), .macOS(.v14), .visionOS(.v1)],`,
|
|
734
|
-
` dependencies: [`,
|
|
735
|
-
` .package(url: "https://github.com/sceneview/sceneview", from: "3.6.1")`,
|
|
736
|
-
` ],`,
|
|
737
|
-
` targets: [`,
|
|
738
|
-
` .executableTarget(`,
|
|
739
|
-
` name: "MyApp",`,
|
|
740
|
-
` dependencies: [`,
|
|
741
|
-
` .product(name: "SceneViewSwift", package: "sceneview")`,
|
|
742
|
-
` ]`,
|
|
743
|
-
` )`,
|
|
744
|
-
` ]`,
|
|
745
|
-
`)`,
|
|
746
|
-
`\`\`\``,
|
|
747
|
-
``,
|
|
748
|
-
`### 2. Minimum Platform Versions`,
|
|
749
|
-
``,
|
|
750
|
-
`| Platform | Minimum Version |`,
|
|
751
|
-
`|----------|-----------------|`,
|
|
752
|
-
`| iOS | 17.0 |`,
|
|
753
|
-
`| macOS | 14.0 |`,
|
|
754
|
-
`| visionOS | 1.0 |`,
|
|
755
|
-
``,
|
|
756
|
-
`### 3. Basic SwiftUI Integration`,
|
|
757
|
-
``,
|
|
758
|
-
`\`\`\`swift`,
|
|
759
|
-
`import SwiftUI`,
|
|
760
|
-
`import SceneViewSwift`,
|
|
761
|
-
`import RealityKit`,
|
|
762
|
-
``,
|
|
763
|
-
`struct ContentView: View {`,
|
|
764
|
-
` @State private var model: ModelNode?`,
|
|
765
|
-
``,
|
|
766
|
-
` var body: some View {`,
|
|
767
|
-
` SceneView { root in`,
|
|
768
|
-
` if let model {`,
|
|
769
|
-
` root.addChild(model.entity)`,
|
|
770
|
-
` }`,
|
|
771
|
-
` }`,
|
|
772
|
-
` .cameraControls(.orbit)`,
|
|
773
|
-
` .task {`,
|
|
774
|
-
` model = try? await ModelNode.load("models/car.usdz")`,
|
|
775
|
-
` model?.scaleToUnits(1.0)`,
|
|
776
|
-
` }`,
|
|
777
|
-
` }`,
|
|
778
|
-
`}`,
|
|
779
|
-
`\`\`\``,
|
|
780
|
-
``,
|
|
781
|
-
`### 4. Model Formats`,
|
|
782
|
-
``,
|
|
783
|
-
`| Format | Support |`,
|
|
784
|
-
`|--------|---------|`,
|
|
785
|
-
`| USDZ | Native — recommended for iOS |`,
|
|
786
|
-
`| .reality | Native — RealityKit format |`,
|
|
787
|
-
`| glTF/GLB | Planned via GLTFKit2 |`,
|
|
788
|
-
``,
|
|
789
|
-
`No manifest or permission changes needed for 3D-only scenes.`,
|
|
790
|
-
].join("\n"),
|
|
791
|
-
},
|
|
792
|
-
]),
|
|
793
|
-
};
|
|
794
|
-
}
|
|
795
|
-
if (iosType === "ar") {
|
|
796
|
-
return {
|
|
797
|
-
content: withDisclaimer([
|
|
798
|
-
{
|
|
799
|
-
type: "text",
|
|
800
|
-
text: [
|
|
801
|
-
`## SceneViewSwift — iOS AR Setup`,
|
|
802
|
-
``,
|
|
803
|
-
`### 1. Add SPM Dependency`,
|
|
804
|
-
``,
|
|
805
|
-
`\`\`\`swift`,
|
|
806
|
-
`.package(url: "https://github.com/sceneview/sceneview", from: "3.6.1")`,
|
|
807
|
-
`\`\`\``,
|
|
808
|
-
``,
|
|
809
|
-
`### 2. Minimum Platform`,
|
|
810
|
-
``,
|
|
811
|
-
`AR requires **iOS 17.0+** (ARKit + RealityKit). macOS and visionOS use different AR APIs.`,
|
|
812
|
-
``,
|
|
813
|
-
`### 3. Info.plist — Camera Permission`,
|
|
814
|
-
``,
|
|
815
|
-
`Add to your Info.plist (required for AR camera access):`,
|
|
816
|
-
`\`\`\`xml`,
|
|
817
|
-
`<key>NSCameraUsageDescription</key>`,
|
|
818
|
-
`<string>This app uses the camera for augmented reality.</string>`,
|
|
819
|
-
`\`\`\``,
|
|
820
|
-
``,
|
|
821
|
-
`### 4. Basic AR Integration`,
|
|
822
|
-
``,
|
|
823
|
-
`\`\`\`swift`,
|
|
824
|
-
`import SwiftUI`,
|
|
825
|
-
`import SceneViewSwift`,
|
|
826
|
-
`import RealityKit`,
|
|
827
|
-
``,
|
|
828
|
-
`struct ARContentView: View {`,
|
|
829
|
-
` @State private var model: ModelNode?`,
|
|
830
|
-
``,
|
|
831
|
-
` var body: some View {`,
|
|
832
|
-
` ARSceneView(`,
|
|
833
|
-
` planeDetection: .horizontal,`,
|
|
834
|
-
` showCoachingOverlay: true,`,
|
|
835
|
-
` onTapOnPlane: { position, arView in`,
|
|
836
|
-
` guard let model else { return }`,
|
|
837
|
-
` let anchor = AnchorNode.world(position: position)`,
|
|
838
|
-
` let clone = model.entity.clone(recursive: true)`,
|
|
839
|
-
` clone.scale = .init(repeating: 0.3)`,
|
|
840
|
-
` anchor.add(clone)`,
|
|
841
|
-
` arView.scene.addAnchor(anchor.entity)`,
|
|
842
|
-
` }`,
|
|
843
|
-
` )`,
|
|
844
|
-
` .edgesIgnoringSafeArea(.all)`,
|
|
845
|
-
` .task {`,
|
|
846
|
-
` model = try? await ModelNode.load("models/robot.usdz")`,
|
|
847
|
-
` }`,
|
|
848
|
-
` }`,
|
|
849
|
-
`}`,
|
|
850
|
-
`\`\`\``,
|
|
851
|
-
``,
|
|
852
|
-
`### 5. AR Configuration Options`,
|
|
853
|
-
``,
|
|
854
|
-
`| Parameter | Options | Default |`,
|
|
855
|
-
`|-----------|---------|---------|`,
|
|
856
|
-
`| \`planeDetection\` | \`.none\`, \`.horizontal\`, \`.vertical\`, \`.both\` | \`.horizontal\` |`,
|
|
857
|
-
`| \`showPlaneOverlay\` | \`true\` / \`false\` | \`true\` |`,
|
|
858
|
-
`| \`showCoachingOverlay\` | \`true\` / \`false\` | \`true\` |`,
|
|
859
|
-
`| \`imageTrackingDatabase\` | \`Set<ARReferenceImage>\` | \`nil\` |`,
|
|
860
|
-
``,
|
|
861
|
-
`### 6. Image Tracking`,
|
|
862
|
-
``,
|
|
863
|
-
`\`\`\`swift`,
|
|
864
|
-
`let images = AugmentedImageNode.createImageDatabase([`,
|
|
865
|
-
` AugmentedImageNode.ReferenceImage(`,
|
|
866
|
-
` name: "poster",`,
|
|
867
|
-
` image: UIImage(named: "poster_ref")!,`,
|
|
868
|
-
` physicalWidth: 0.3 // meters`,
|
|
869
|
-
` )`,
|
|
870
|
-
`])`,
|
|
871
|
-
``,
|
|
872
|
-
`ARSceneView(`,
|
|
873
|
-
` imageTrackingDatabase: images,`,
|
|
874
|
-
` onImageDetected: { name, anchor, arView in`,
|
|
875
|
-
` let cube = GeometryNode.cube(size: 0.1, color: .blue)`,
|
|
876
|
-
` anchor.add(cube.entity)`,
|
|
877
|
-
` arView.scene.addAnchor(anchor.entity)`,
|
|
878
|
-
` }`,
|
|
879
|
-
`)`,
|
|
880
|
-
`\`\`\``,
|
|
881
|
-
].join("\n"),
|
|
882
|
-
},
|
|
883
|
-
]),
|
|
884
|
-
};
|
|
885
|
-
}
|
|
886
|
-
return {
|
|
887
|
-
content: [{ type: "text", text: `Unknown type "${iosType}". Use "3d" or "ar".` }],
|
|
888
|
-
isError: true,
|
|
889
|
-
};
|
|
890
|
-
}
|
|
891
|
-
// ── get_web_setup ────────────────────────────────────────────────────────
|
|
892
|
-
case "get_web_setup": {
|
|
893
|
-
return {
|
|
894
|
-
content: withDisclaimer([
|
|
895
|
-
{
|
|
896
|
-
type: "text",
|
|
897
|
-
text: [
|
|
898
|
-
`## SceneView Web — Browser 3D Setup`,
|
|
899
|
-
``,
|
|
900
|
-
`SceneView Web uses **Filament.js** — the same rendering engine as Android, compiled to WebAssembly (WebGL2).`,
|
|
901
|
-
``,
|
|
902
|
-
`### 1. Install`,
|
|
903
|
-
``,
|
|
904
|
-
`\`\`\`bash`,
|
|
905
|
-
`npm install @sceneview/sceneview-web`,
|
|
906
|
-
`\`\`\``,
|
|
907
|
-
``,
|
|
908
|
-
`Or in a Kotlin/JS Gradle project:`,
|
|
909
|
-
`\`\`\`kotlin`,
|
|
910
|
-
`// build.gradle.kts`,
|
|
911
|
-
`kotlin {`,
|
|
912
|
-
` js(IR) { browser(); binaries.executable() }`,
|
|
913
|
-
` sourceSets {`,
|
|
914
|
-
` jsMain.dependencies {`,
|
|
915
|
-
` implementation("@sceneview/sceneview-web")`,
|
|
916
|
-
` // or: implementation(project(":sceneview-web"))`,
|
|
917
|
-
` }`,
|
|
918
|
-
` }`,
|
|
919
|
-
`}`,
|
|
920
|
-
`\`\`\``,
|
|
921
|
-
``,
|
|
922
|
-
`### 2. HTML`,
|
|
923
|
-
``,
|
|
924
|
-
`\`\`\`html`,
|
|
925
|
-
`<canvas id="scene-canvas" style="width:100%;height:100vh"></canvas>`,
|
|
926
|
-
`<script src="your-app.js"></script>`,
|
|
927
|
-
`\`\`\``,
|
|
928
|
-
``,
|
|
929
|
-
`### 3. Kotlin/JS Code`,
|
|
930
|
-
``,
|
|
931
|
-
`\`\`\`kotlin`,
|
|
932
|
-
`import io.github.sceneview.web.SceneView`,
|
|
933
|
-
`import kotlinx.browser.document`,
|
|
934
|
-
`import org.w3c.dom.HTMLCanvasElement`,
|
|
935
|
-
``,
|
|
936
|
-
`fun main() {`,
|
|
937
|
-
` val canvas = document.getElementById("scene-canvas") as HTMLCanvasElement`,
|
|
938
|
-
` canvas.width = canvas.clientWidth`,
|
|
939
|
-
` canvas.height = canvas.clientHeight`,
|
|
940
|
-
``,
|
|
941
|
-
` SceneView.create(`,
|
|
942
|
-
` canvas = canvas,`,
|
|
943
|
-
` configure = {`,
|
|
944
|
-
` camera {`,
|
|
945
|
-
` eye(0.0, 1.5, 5.0)`,
|
|
946
|
-
` target(0.0, 0.0, 0.0)`,
|
|
947
|
-
` fov(45.0)`,
|
|
948
|
-
` }`,
|
|
949
|
-
` light {`,
|
|
950
|
-
` directional()`,
|
|
951
|
-
` intensity(100_000.0)`,
|
|
952
|
-
` }`,
|
|
953
|
-
` model("models/DamagedHelmet.glb")`,
|
|
954
|
-
` autoRotate()`,
|
|
955
|
-
` },`,
|
|
956
|
-
` onReady = { sceneView ->`,
|
|
957
|
-
` sceneView.startRendering()`,
|
|
958
|
-
` }`,
|
|
959
|
-
` )`,
|
|
960
|
-
`}`,
|
|
961
|
-
`\`\`\``,
|
|
962
|
-
``,
|
|
963
|
-
`### 4. Features`,
|
|
964
|
-
``,
|
|
965
|
-
`- Same Filament PBR renderer as Android (WASM)`,
|
|
966
|
-
`- glTF 2.0 / GLB model loading`,
|
|
967
|
-
`- IBL environment lighting (KTX)`,
|
|
968
|
-
`- Orbit camera with mouse/touch/pinch controls`,
|
|
969
|
-
`- Auto-rotation`,
|
|
970
|
-
`- Directional, point, and spot lights`,
|
|
971
|
-
``,
|
|
972
|
-
`### 5. Limitations`,
|
|
973
|
-
``,
|
|
974
|
-
`- No AR (requires native sensors)`,
|
|
975
|
-
`- WebGL2 required (~95% of browsers)`,
|
|
976
|
-
`- glTF/GLB format only (same as Android)`,
|
|
977
|
-
].join("\n"),
|
|
978
|
-
},
|
|
979
|
-
]),
|
|
980
|
-
};
|
|
981
|
-
}
|
|
982
|
-
// ── render_3d_preview ──────────────────────────────────────────────────
|
|
983
|
-
case "render_3d_preview": {
|
|
984
|
-
const modelUrl = request.params.arguments?.modelUrl;
|
|
985
|
-
const codeSnippet = request.params.arguments?.codeSnippet;
|
|
986
|
-
const autoRotate = request.params.arguments?.autoRotate;
|
|
987
|
-
const ar = request.params.arguments?.ar;
|
|
988
|
-
const title = request.params.arguments?.title;
|
|
989
|
-
const validationError = validatePreviewInput(modelUrl, codeSnippet);
|
|
990
|
-
if (validationError) {
|
|
991
|
-
return {
|
|
992
|
-
content: [{ type: "text", text: `Error: ${validationError}` }],
|
|
993
|
-
isError: true,
|
|
994
|
-
};
|
|
995
|
-
}
|
|
996
|
-
const result = buildPreviewUrl({ modelUrl, codeSnippet, autoRotate, ar, title });
|
|
997
|
-
const text = formatPreviewResponse(result);
|
|
998
|
-
return { content: withDisclaimer([{ type: "text", text }]) };
|
|
999
|
-
}
|
|
1000
|
-
// ── create_3d_artifact ───────────────────────────────────────────────────
|
|
1001
|
-
case "create_3d_artifact": {
|
|
1002
|
-
const artifactInput = {
|
|
1003
|
-
type: request.params.arguments?.type,
|
|
1004
|
-
modelUrl: request.params.arguments?.modelUrl,
|
|
1005
|
-
title: request.params.arguments?.title,
|
|
1006
|
-
data: request.params.arguments?.data,
|
|
1007
|
-
options: request.params.arguments?.options,
|
|
1008
|
-
hotspots: request.params.arguments?.hotspots,
|
|
1009
|
-
shapes: request.params.arguments?.shapes,
|
|
1010
|
-
};
|
|
1011
|
-
const validationError = validateArtifactInput(artifactInput);
|
|
1012
|
-
if (validationError) {
|
|
1013
|
-
return {
|
|
1014
|
-
content: [{ type: "text", text: `Error: ${validationError}` }],
|
|
1015
|
-
isError: true,
|
|
1016
|
-
};
|
|
1017
|
-
}
|
|
1018
|
-
const result = generateArtifact(artifactInput);
|
|
1019
|
-
const text = formatArtifactResponse(result);
|
|
1020
|
-
return { content: withDisclaimer([{ type: "text", text }]) };
|
|
1021
|
-
}
|
|
1022
|
-
// ── get_platform_setup ─────────────────────────────────────────────────
|
|
1023
|
-
case "get_platform_setup": {
|
|
1024
|
-
const platform = request.params.arguments?.platform;
|
|
1025
|
-
const setupType = request.params.arguments?.type;
|
|
1026
|
-
if (!platform || !setupType) {
|
|
1027
|
-
return {
|
|
1028
|
-
content: [{ type: "text", text: "Missing required parameters: `platform` and `type`." }],
|
|
1029
|
-
isError: true,
|
|
1030
|
-
};
|
|
1031
|
-
}
|
|
1032
|
-
const guide = getPlatformSetup(platform, setupType);
|
|
1033
|
-
return { content: withDisclaimer([{ type: "text", text: guide }]) };
|
|
1034
|
-
}
|
|
1035
|
-
// ── migrate_code ─────────────────────────────────────────────────────────
|
|
1036
|
-
case "migrate_code": {
|
|
1037
|
-
const code = request.params.arguments?.code;
|
|
1038
|
-
if (!code || typeof code !== "string") {
|
|
1039
|
-
return {
|
|
1040
|
-
content: [{ type: "text", text: "Missing required parameter: `code`." }],
|
|
1041
|
-
isError: true,
|
|
1042
|
-
};
|
|
1043
|
-
}
|
|
1044
|
-
const migrationResult = migrateCode(code);
|
|
1045
|
-
const migrationReport = formatMigrationResult(migrationResult);
|
|
1046
|
-
return { content: withDisclaimer([{ type: "text", text: migrationReport }]) };
|
|
1047
|
-
}
|
|
1048
|
-
// ── debug_issue ──────────────────────────────────────────────────────────
|
|
1049
|
-
case "debug_issue": {
|
|
1050
|
-
let category = request.params.arguments?.category;
|
|
1051
|
-
const desc = request.params.arguments?.description;
|
|
1052
|
-
if (!category && desc) {
|
|
1053
|
-
category = autoDetectIssue(desc) ?? undefined;
|
|
1054
|
-
}
|
|
1055
|
-
if (!category) {
|
|
1056
|
-
return {
|
|
1057
|
-
content: [{
|
|
1058
|
-
type: "text",
|
|
1059
|
-
text: `Please provide a \`category\` or \`description\`.\n\nAvailable categories: ${DEBUG_CATEGORIES.join(", ")}`,
|
|
1060
|
-
}],
|
|
1061
|
-
isError: true,
|
|
1062
|
-
};
|
|
1063
|
-
}
|
|
1064
|
-
const debugGuide = getDebugGuide(category);
|
|
1065
|
-
const prefix = desc && autoDetectIssue(desc) === category
|
|
1066
|
-
? `> Auto-detected category: **${category}** from your description.\n\n`
|
|
1067
|
-
: "";
|
|
1068
|
-
return { content: withDisclaimer([{ type: "text", text: prefix + debugGuide }]) };
|
|
1069
|
-
}
|
|
1070
|
-
// ── generate_scene ───────────────────────────────────────────────────────
|
|
1071
|
-
case "generate_scene": {
|
|
1072
|
-
const sceneDesc = request.params.arguments?.description;
|
|
1073
|
-
if (!sceneDesc || typeof sceneDesc !== "string") {
|
|
1074
|
-
return {
|
|
1075
|
-
content: [{ type: "text", text: "Missing required parameter: `description`." }],
|
|
1076
|
-
isError: true,
|
|
1077
|
-
};
|
|
1078
|
-
}
|
|
1079
|
-
const sceneResult = generateScene(sceneDesc);
|
|
1080
|
-
const sceneReport = formatGeneratedScene(sceneResult);
|
|
1081
|
-
return { content: withDisclaimer([{ type: "text", text: sceneReport }]) };
|
|
1082
|
-
}
|
|
1083
|
-
// ── list_platforms ────────────────────────────────────────────────────────
|
|
1084
|
-
case "list_platforms": {
|
|
1085
|
-
const platforms = [
|
|
1086
|
-
{ platform: "Android", renderer: "Filament", framework: "Jetpack Compose", status: "Stable", version: "3.6.1", dependency: "io.github.sceneview:sceneview:3.6.0", features: ["3D", "AR (ARCore)", "Model loading (GLB/glTF)", "Geometry nodes", "Physics", "Gestures"] },
|
|
1087
|
-
{ platform: "Android TV", renderer: "Filament", framework: "Compose TV", status: "Alpha", version: "3.6.1", dependency: "io.github.sceneview:sceneview:3.6.0", features: ["3D", "D-pad controls", "Auto-rotation", "Model loading"] },
|
|
1088
|
-
{ platform: "Android XR", renderer: "Jetpack XR SceneCore", framework: "Compose XR", status: "Planned", version: "-", dependency: "-", features: ["Spatial computing", "Hand tracking", "Passthrough"] },
|
|
1089
|
-
{ platform: "iOS", renderer: "RealityKit", framework: "SwiftUI", status: "Alpha", version: "3.6.1", dependency: "SceneViewSwift (SPM)", features: ["3D", "AR (ARKit)", "16 node types", "USDZ models"] },
|
|
1090
|
-
{ platform: "macOS", renderer: "RealityKit", framework: "SwiftUI", status: "Alpha", version: "3.6.1", dependency: "SceneViewSwift (SPM)", features: ["3D", "Orbit camera", "USDZ models"] },
|
|
1091
|
-
{ platform: "visionOS", renderer: "RealityKit", framework: "SwiftUI", status: "Alpha", version: "3.6.1", dependency: "SceneViewSwift (SPM)", features: ["3D", "Immersive spaces", "Hand tracking (planned)"] },
|
|
1092
|
-
{ platform: "Web", renderer: "Filament.js (WASM)", framework: "Kotlin/JS", status: "Alpha", version: "3.6.1", dependency: "@sceneview/sceneview-web", features: ["3D", "WebXR AR/VR", "GLB models", "WebGL2"] },
|
|
1093
|
-
{ platform: "Desktop", renderer: "Software / Filament JNI", framework: "Compose Desktop", status: "Alpha", version: "3.6.1", dependency: "sceneview-desktop (local)", features: ["3D", "Software renderer", "Wireframe"] },
|
|
1094
|
-
{ platform: "Flutter", renderer: "Filament / RealityKit", framework: "PlatformView", status: "Alpha", version: "3.6.1", dependency: "flutter pub: sceneview", features: ["3D", "AR", "Android + iOS bridge"] },
|
|
1095
|
-
];
|
|
1096
|
-
const lines = [
|
|
1097
|
-
"## SceneView Supported Platforms\n",
|
|
1098
|
-
"| Platform | Renderer | Framework | Status | Version |",
|
|
1099
|
-
"|----------|----------|-----------|--------|---------|",
|
|
1100
|
-
...platforms.map(p => `| ${p.platform} | ${p.renderer} | ${p.framework} | ${p.status} | ${p.version} |`),
|
|
1101
|
-
"",
|
|
1102
|
-
"### Platform Details\n",
|
|
1103
|
-
...platforms.map(p => [
|
|
1104
|
-
`**${p.platform}** (${p.status})`,
|
|
1105
|
-
`- Renderer: ${p.renderer}`,
|
|
1106
|
-
`- Framework: ${p.framework}`,
|
|
1107
|
-
`- Dependency: \`${p.dependency}\``,
|
|
1108
|
-
`- Features: ${p.features.join(", ")}`,
|
|
1109
|
-
"",
|
|
1110
|
-
].join("\n")),
|
|
1111
|
-
"### Architecture",
|
|
1112
|
-
"",
|
|
1113
|
-
"SceneView uses **native renderers per platform**: Filament on Android/Web/Desktop, RealityKit on Apple (iOS/macOS/visionOS).",
|
|
1114
|
-
"KMP `sceneview-core` shares logic (math, collision, geometry, animations) across all platforms.",
|
|
1115
|
-
];
|
|
1116
|
-
return { content: withDisclaimer([{ type: "text", text: lines.join("\n") }]) };
|
|
1117
|
-
}
|
|
1118
|
-
// ── get_animation_guide ─────────────────────────────────────────────────
|
|
1119
|
-
case "get_animation_guide": {
|
|
1120
|
-
return { content: withDisclaimer([{ type: "text", text: ANIMATION_GUIDE }]) };
|
|
1121
|
-
}
|
|
1122
|
-
// ── get_gesture_guide ────────────────────────────────────────────────────
|
|
1123
|
-
case "get_gesture_guide": {
|
|
1124
|
-
return { content: withDisclaimer([{ type: "text", text: GESTURE_GUIDE }]) };
|
|
1125
|
-
}
|
|
1126
|
-
// ── get_performance_tips ─────────────────────────────────────────────────
|
|
1127
|
-
case "get_performance_tips": {
|
|
1128
|
-
return { content: withDisclaimer([{ type: "text", text: PERFORMANCE_TIPS }]) };
|
|
1129
|
-
}
|
|
1130
|
-
// ── get_material_guide ───────────────────────────────────────────────────
|
|
1131
|
-
case "get_material_guide": {
|
|
1132
|
-
return { content: withDisclaimer([{ type: "text", text: MATERIAL_GUIDE }]) };
|
|
1133
|
-
}
|
|
1134
|
-
// ── get_collision_guide ──────────────────────────────────────────────────
|
|
1135
|
-
case "get_collision_guide": {
|
|
1136
|
-
return { content: withDisclaimer([{ type: "text", text: COLLISION_GUIDE }]) };
|
|
1137
|
-
}
|
|
1138
|
-
// ── get_model_optimization_guide ─────────────────────────────────────────
|
|
1139
|
-
case "get_model_optimization_guide": {
|
|
1140
|
-
return { content: withDisclaimer([{ type: "text", text: MODEL_OPTIMIZATION_GUIDE }]) };
|
|
1141
|
-
}
|
|
1142
|
-
// ── get_web_rendering_guide ──────────────────────────────────────────────
|
|
1143
|
-
case "get_web_rendering_guide": {
|
|
1144
|
-
return { content: withDisclaimer([{ type: "text", text: WEB_RENDERING_GUIDE }]) };
|
|
1145
|
-
}
|
|
1146
|
-
default:
|
|
1147
|
-
return {
|
|
1148
|
-
content: [{ type: "text", text: `Unknown tool: ${request.params.name}` }],
|
|
1149
|
-
isError: true,
|
|
1150
|
-
};
|
|
76
|
+
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
|
+
}
|
|
82
|
+
// Record anonymous telemetry (fire-and-forget, non-blocking, opt-out via
|
|
83
|
+
// SCENEVIEW_TELEMETRY=0). See `telemetry.ts` and `PRIVACY.md`.
|
|
84
|
+
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(() => { });
|
|
1151
89
|
}
|
|
90
|
+
// The dispatcher returns the narrower SceneView `ToolResult` shape, which
|
|
91
|
+
// structurally matches the MCP SDK's `CallToolResult` but TS can't prove
|
|
92
|
+
// it (the SDK's zod-derived type has additional optional members).
|
|
93
|
+
const result = await dispatchTool(toolName, request.params.arguments);
|
|
94
|
+
return result;
|
|
1152
95
|
});
|
|
1153
96
|
const transport = new StdioServerTransport();
|
|
1154
97
|
await server.connect(transport);
|