SSWorld MCP server
2026년 10월 19일 커밋AI 에이전트가 WebGPU와 레이 트레이싱 기반 전역 조명을 사용하여 3D 지리적 장면을 작성, 컴파일, 렌더링 및 검사할 수 있습니다.
다른 이름 DataArche/SSWorld
- 스타
- 5
- Tools
- 13
- 다운로드, 30일
- —
- 최신 버전
- —
- 마지막 커밋
- 2026년 10월 19일
- 라이선스
- Apache-2.0
SSWorld MCP란?
SSWorld은(는) MCP server입니다. AI 에이전트가 WebGPU와 레이 트레이싱 기반 전역 조명을 사용하여 3D 지리적 장면을 작성, 컴파일, 렌더링 및 검사할 수 있습니다. 13개 도구를 제공합니다. 예: ssworld_catalog, ssworld_project_list, ssworld_project_create.
프로젝트 README에서 생성된 요약
SSWorld MCP server 정보
SSWorld는 AI 에이전트를 통해 3D 지리적 장면을 생성하고 검사할 수 있는 환경을 제공합니다. 이 MCP server를 사용하면 개발자는 Claude Code, Cursor 또는 Codex와 같은 에이전트를 활용하여 QML 스타일의 장면 기술 언어인 SSDL로 복잡한 환경을 작성, 컴파일 및 렌더링할 수 있습니다. 시스템은 SSEngine WebGPU 런타임에서 실행되며, 브라우저 기반 미리보기 내에서 실시간 레이 트레이싱 전역 조명, 대기 하늘 및 그림자를 제공합니다.
사용자는 npm을 통해 서버를 로컬에 설치하여 서버와 선택한 에이전트 앱을 연결하고 필요한 엔진을 다운로드함으로써 서버와 상호작용합니다. 단순한 장면 생성기와 달리, 이 서버는 에이전트가 작업물을 실제로 확인할 수 있는 피드백 루프를 제공합니다. 에이전트는 ssworld_capture_frame 도구를 통해 휘도 백분위수, 색상 커버리지, 카메라 포즈 편차를 포함한 시각적 데이터를 수신합니다. 이를 통해 AI는 하늘 색상이 잘못 지정된 것과 같은 문제를 감지하고 사람의 개입 없이 자율적으로 수정할 수 있습니다.
활용 사례
- 1자동 시각적 디버깅에이전트가 렌더링된 프레임을 검사하여 색상 오류를 감지하고 장면 코드를 자동으로 수정할 수 있습니다.
- 2절차적 도시 생성개발자는 에이전트에게 OpenStreetMap 데이터를 사용하여 전체 거리 경관을 재구축하도록 지시할 수 있습니다.
- 3지리적 장면 고정경도와 위도를 사용하여 전 세계 지형 위에 정밀하게 배치된 3D 모델을 생성합니다.
- 4실시간 조명 조정에이전트가 엔진 클락을 조작하여 다양한 시간대를 시뮬레이션하고 조명 변화를 관찰할 수 있습니다.
프로젝트의 README를 바탕으로 작성되었습니다. 모든 활용 사례는 해당 내용의 인용문과 대조하여 확인합니다.
SSWorld MCP 도구 13
README에서 감지됨ssworld_catalogssworld_project_listssworld_project_createssworld_source_patchssworld_source_batchssworld_scene_inspectssworld_compilessworld_previewssworld_capture_framessworld_environment_readssworld_geo_readssworld_geometry_read
| ssworld_catalog | — | |
| ssworld_project_list | — | |
| ssworld_project_create | — | |
| ssworld_source_patch | — | |
| ssworld_source_batch | — | |
| ssworld_scene_inspect | — | |
| ssworld_compile | — | |
| ssworld_preview | — | |
| ssworld_capture_frame | — | |
| ssworld_environment_read | — | |
| ssworld_geo_read | — | |
| ssworld_geometry_read | — |
도구 13개 모두 보기도구 수 줄이기
ssworld_engine_status
| ssworld_engine_status | — |
자동 검사
2026년 11월 14일에 실행됨전체 보고서 →아직 인증되지 않았습니다. 서버는 80점 이상의 점수, 심각한 결함 없음, 그리고 검사에 충분한 데이터를 갖추어야 합니다. 검사 방식 · 점수를 높이기 위해 수정할 항목
- 소스 저장소 연결 및 접근 가능dataarche/ssworld is public and reachablegithub.com/dataarche/ssworld ↗통과
- 패키지가 이 저장소를 소스로 명시검사되지 않음
- 빌드 출처(Sigstore 증명)검사되지 않음
- 오픈 소스 라이선스 (OSI)Apache-2.0spdx.org/licenses/Apache-2.0.html ↗통과
- 최근 1년 내 커밋 또는 릴리스Last commit or release 2026-10-19github.com/dataarche/ssworld ↗통과
- OpenSSF Scorecard검사되지 않음
- 패키지 이름이 인기 패키지와 유사하지 않음검사되지 않음
- 패키지 및 해당 종속성에 중요한 보안 권고 없음검사되지 않음
- 모델에 대한 숨겨진 지침 없음13 tool descriptions and the README read; no override phrases, credential paths or hidden characters통과
- 파괴적인 도구에 라벨 표시됨No tool named like delete, drop or exec통과
- 원격 엔드포인트가 MCP 핸드셰이크에 응답검사되지 않음
README
GitHub에서 보기SSWorld — WebGPU ray tracing and global illumination (GI)
Agents can't see what they render. SSWorld closes the loop.
An MCP server that lets any MCP-capable agent app (Claude Code, Codex, Hermes, Cursor …) author, compile, render and inspect 3D geographic scenes — written in SSDL, a QML-like scene description language — on the SSEngine WebGPU runtime. The picture above is Lumen Gallery: an interactive architectural courtyard with sunlit timber vaults, metal reflections and ray-traced global illumination (GI). Its English interface lets visitors compare GI on/off, switch between 16:00 and 22:00, and explore three views. Lumen Gallery is this project's showcase name; it is not an integration of Unreal Engine Lumen.
Why this is not another scene generator
- A real engine, not a preview. Deferred rendering, atmospheric sky, shadows, post-processing, glTF assets, GPU instancing. Scenes stand on a real globe: terrain, imagery layers, 3D Tiles and GeoJSON, anchored by longitude and latitude. 60 fps with hundreds of thousands of instanced objects.
- Ray-traced GI in the browser. WebGPU software ray tracing adds indirect light and reflections to the scene. Compare it against raster lighting without changing the camera or materials; the gallery also uses the engine clock to move the sun and transition to night.
- The agent gets the frame back.
ssworld_capture_framerenders through the engine and returns luma percentiles, exposure tails, colour coverage per 3×3 region, top colours, the camera pose it actually used and the deviation from the one requested — plus a receipt binding the frame to the source digest and the answering page. An agent can tell that its sky came out orange-brown, and fix it, without a human looking. - Traps that cost a real session become compile errors. Every failure below was first hit on real
hardware, where it showed up as a blank page with nothing in the log. Each one now names the node
and the member at compile time: a sky tinted through
rayleighScattering, aLatheradius of 0, anormalMapon geometry without tangents, aheightslist counted as cells instead of corners.
Examples
| Lumen Gallery | GuanlanRoadTemplate0924 | SkylineGarden0919 | DaxiongHall0924 |
|---|---|---|---|
| An English interactive gallery: ray-traced GI on/off, sunlit architecture, reflections, furnished close-ups and an engine-clock day/night transition. | Real streets rebuilt from OpenStreetMap by a reusable pipeline: point it at another bbox and it builds that site. | An original procedural city with traffic, signals, a train, park life, fountains and a day–night cycle. | A Chinese temple hall — double eaves, bracket sets, marble terrace, incense smoke — with a cinematic camera tour. |
Start with Lumen Gallery: its editable SSDL, English interface and about 6.7 MB of model and
texture assets are included, with no asset-generation step. The other three examples carry sources
and generators and are each under 400 KB. The gallery includes CC0 Poly Haven textures; Guanlan
fetches © OpenStreetMap contributors' street data under
ODbL. See examples/ for setup and credits.
Install (one command)
npx -y ssworld-mcp install
This installs the package globally, registers the ssworld MCP server with every agent app it finds
(--client=claude,codex,hermes,cursor,dsh to choose), and downloads the pinned engine (≈54 MB) into ~/.ssworld/engine/.
Requirements: Node.js ≥ 22, npm, a WebGPU browser (Chrome/Edge) to look at previews.
Or install as a native plugin
Claude Code and Codex both read plugin manifests straight out of this repository, so one command registers the MCP server and the SSDL authoring skill with no global npm install. The pinned engine still downloads on the first preview.
# Claude Code
/plugin marketplace add DataArche/SSWorld
/plugin install ssworld@ssworld
# Codex
codex plugin marketplace add DataArche/SSWorld
codex plugin add ssworld --marketplace ssworld
Hermes, Cursor and DSH have no plugin package that can carry an MCP server — each registers servers
in its own config — so ssworld-mcp install is the native path there and writes exactly what that
host reads: mcp_servers: in ~/.hermes/config.yaml, mcpServers in ~/.cursor/mcp.json, and an
@deepseek-ai/dsh-mcp-client row in DSH's user patch layer ~/.dsh/cordis.patch.yml. Hermes and DSH
also get the skill copied into their own skills/ directory, which is the whole registration.
npx -y ssworld-mcp install --client=hermes,dsh
Manual registration for any other client:
{ "mcpServers": { "ssworld": { "command": "node", "args": ["<npm root -g>/ssworld-mcp/bin/ssworld-mcp.mjs"] } } }
Tools
| tool | purpose |
|---|---|
ssworld_catalog |
component index / one contract / components: [...] in one call (detail: compact hoists shared members); catalog_digest + if_digest for cache hits; units and rotation conventions annotated per member |
ssworld_project_list |
projects in ~/.ssworld/projects |
ssworld_project_create |
new runnable project (anchored at lon/lat/height), compiled |
ssworld_source_read / ssworld_source_write |
bounded reads (max_chars, offset/limit with has_more/next_offset; mode: metadata for digests, sizes and compile staleness without text; node: "<id>" for one block; file: "*" for all files) and whole-file writes of .ssdl sources; editing the files on disk with any tool also works (ssworld_compile rebuilds from disk) but bypasses the digest lock |
ssworld_source_patch |
exact-span edit (old_string → new_string, uniqueness checked, replace_all opt-in) with the same digest guard |
ssworld_source_batch |
atomic multi-edit: text patches and node-level {node_id, set, unset} across files; all validated in memory before anything is written; validate: compile + rollback restores the previous sources on a failed compile |
ssworld_scene_inspect |
compiled-scene facts without rendering: budget ratio, subtrees by size, leaf children by type, nodes per source file, primitive extent, the requested camera (lookAt-derived heading/pitch); render stats honestly unavailable |
ssworld_compile |
SSDL 0.3 compiler with real diagnostics, node_count, budget usage and logic (declared properties, states, host calls); catalog/runtime mismatches are compile errors (Label without a font, material animations on a Group/Model, undeclared host calls) |
ssworld_preview |
starts the local preview server, returns http://127.0.0.1:8880/projects/<name>/index.html, whether the page is open, page.clients (every browser syncing the page: id, visibility, canvas, user agent) and a structured next (open_webgpu_viewer / capture_frame / bring_page_to_front) |
ssworld_capture_frame |
screenshot of the open preview through the engine (saveImage2Base64); stats with luma percentiles, exposure tails, colour-class coverage overall and per 3×3 region, top colours; runtime errors deduplicated and mapped to scene.ssdl:line:column; camera pose with `source: scene |
ssworld_environment_read |
ask the engine what it actually received for the environment: the adopted sun's direction read back from the native sun and converted to azimuth/elevation at the anchor (with the deviation from the scene's request), which DirectionalLight drives the atmosphere, and every environment component's live native values. The probe lives in the project's index.html, which is project-owned, so a page created before 0.9.8 answers page_probe_unavailable and the error names the handler to paste in |
ssworld_geo_read |
ask the engine what it holds for the geographic layers: whether the terrain provider loaded, anchor_above_terrain_m (how far the scene's local z: 0 sits above the ground under the anchor, because terrain moves the ground and not the scene), the imagery stack in real draw order with each layer's native_index, and every Tileset / GeoJsonLayer's readiness, extent and feature count. Same project-owned probe as the environment read, so an older index.html answers page_probe_unavailable |
ssworld_geometry_read |
measure the scene from the engine instead of a screenshot: every geometry node's and Model's dimensions (object axes, metres), world position/rotation/scale composed through the live scene graph, axis-aligned world_bounds (tight or an envelope), ground contact (on / buried / above z = 0, and a Box is centred on its position so z: 0 buries half of it), mesh facts the engine measured (vertex/triangle counts, closed, manifold, area_m2 / volume_m3); Groups and GeoAnchors as the union of their descendants, Instances as the native union, the scene as its whole extent; overlaps: true lists intersecting boxes with the overlap volume, ids narrows, detail: brief trims. Same project-owned probe, so an older index.html answers page_probe_unavailable |
ssworld_logic_read / ssworld_logic_write |
read the open page's scene logic (and whether the scene module actually mounted: runtime.state plus errors mapped to scene.ssdl:line) without a screenshot, or set declared properties in one transaction ({set: {p: 0.4, pace: 0.0025}}) to put a game into a situation before capturing; a refused value rolls the whole set back and names the failing binding |
ssworld_engine_status |
engine pair installed? (install: true to download) |
How big a scene, measured
Budgets are per-project values in showcase.manifest.json; the defaults and the ceilings come from
src/ssdl/tools/probe_engine_limits.py (which asks every facade for its own capabilities()) and
src/ssdl/tools/probe_scale_limits.py (which raises the budgets and builds a scene past them on real
hardware). Defaults: 4096 native objects / 4096 material shells / 32 distinct images / 4096 bindings
/ 2048 handlers / 256 timers / 256 timelines / 1024 Group locators / 1024 Prefabs / 524288 instance
rows. Four of those are the engine's own and a manifest cannot raise them (resolveBudgets clamps
distinct images, timers, timelines and Group locators), because a scene past them compiles and then
fails to mount.
What actually runs out, in order:
- Not rendering. 6401 native objects with 409 600 instance rows across 200 prefabs compiled in 1.2 s, mounted in 4.0 s and ran at 61 fps with no runtime errors.
ssworld_capture_framefirst. The offscreen readback dies of a wasm "memory access out of bounds" somewhere between 4001 native objects (captures,render_verified, ~21k distinct colours) and 5001 (does not). Past roughly 4000 nodes a scene runs and cannot be screenshotted, which is whynative_objectssits just under that and why instancing — 409 600 rows for 200 native objects — is the way to build something large. The capture timeout is now the caller's to set: the page used to give the readback a fixed 8 s whatevertimeout_mssaid, so a scene big enough to be worth a screenshot always timed out.- Bindings used to be the hard wall, and are not any more.
AnimationFacade.captureBatchrefuses a batch of more than 64 items and refuses it whole, so a scene whose first frame carried 65 bindings failed to mount with a receipt that said "evaluated 65, committed 0, failed 65" and named nothing — while the shipped budget said 256. The runtime now flushes bindings in chunks of 64 and restores the chunks that already landed if a later one fails, so the flush stays all-or-nothing to the author. Measured after the fix: 1500 bindings + 600 handlers + 914 locators mount in 2.2 s at 44 fps.
Three engine ceilings have no budget dimension and are compile errors of their own:
scene_depth_exceeded (SceneGraphFacade.max_depth, 16 levels of nesting), model_budget
(ModelFacade.max_model_instances, 256 Model mounts, 1 GiB of distinct glb bytes — use one as a Prefab source instead of a copy
per placement) and texture_budget (MaterialFacade.max_texture_bytes_total, 64 MiB of GPU texture
memory: each distinct image resized to a power of two, block-compressed with its mips -- about 1 MiB per
1024² image, whatever the file size -- and no side above 4096 px).
Authoring reference
Colours are authored as sRGB
#rrggbb is read as sRGB — the value a colour picker gives you. The runtime converts it to linear before handing it to
the engine, so the frame shows the colour that was picked and reading the value back returns the same hex. Before 0.9.8
that conversion was missing and every flat colour rendered roughly three times too bright (#16260f came out as a light
green). Native materials keep 8 bits of linear light per channel, so very dark colours quantise: #16260f reads back
as #16260d, and anything below about #0a0a0a collapses towards black. Verified on real hardware with
src/ssdl/tools/probe_color_space.py (headless Chromium + WebGPU, frames captured through the engine).
What the compiler refuses
Several failures that used to surface only as a dead page are now compile errors naming the node and the member:
sky_scattering_refused—SkyAtmosphere'srayleighScattering,mieScattering,mieAbsorption,otherAbsorptionandskyLuminanceFactorare normalised direction vectors, not colours (their magnitude lives in the neighbouring*Scale). Writing a "neutral-looking" vector raises red and turns the whole sky orange-brown. Tint through the sun'slightColorinstead; the error text carries each member's real engine default.cloud_kilometres_expected—VolumetricCloud'slayerBottomAltitude,layerHeight,tracingStartMaxDistance,tracingMaxDistanceandshadowTracingDistanceare in kilometres, the unit UE uses for this component, while the rest of SSDL is in metres. Metre values do not merely misplace the deck: the ray-march sample count is fixed (min(96 x viewSampleCountScale, 768)) and does not grow with the tracing distance, so a ray stretched a thousandfold aliases the cloud noise into vertical white streaks across the sky. A deck islayerBottomAltitude: 1.8; layerHeight: 0.5.mesh_degenerate— a radius of 0 in aLatheprofile, exactly repeated adjacent points, aTubeorSweeppath that doubles back or repeats a point, two identical adjacentLoftrings, aMeshface with zero area, a ring with no area. These producedGeometryFacade.createMesh: triangle is degenerateand took the entire scene module down without naming a node. (A vertical firstTubesegment is fine: the parallel-transport frame switches reference axis at|tangent.z| >= 0.9.)mesh_invalid— a parameter outside its range or a shape the generator cannot build: aSweepprofile or anExtrudedPolygonoutline that crosses itself, ataper/bevelthat eats an edge (the message names the edge), aMeshface index out of range, aRooffootprint that crosses itself or whose skeleton cannot be closed (the message names the edge to simplify),ridgeon a polygonRooforgables/lowEdgeon a quadrilateral one,Capsulewithheight <= 2 * radiusor a segment count not divisible by four,sampleswithoutsmooth: "catmullrom",capon a closedLoft.placement_invalid—Instancesmembers that belong to another placement mode, aringwithout a positiveradius,along_pathwith both or neither ofstep/count, more than 512 instances in one batch, orfaceCenter/alignToPathnext to an explicit rotation member.spawnable_invalid— apragma spawnablecomponent that cannot be one: a root that is not a singleGroup, apropertyoutsidereal/bool/string/length/degrees/durationor one without a default (the default is what the fragment's budget and mesh limits are checked against), a scene singleton (Camera,Environment,Globe, a light that owns the sun, a geographic layer), aKeyHandlerorPointerHandler(both claim the whole window), or aModel(it loads asynchronously, andapi.scene.spawnreturns a live handle in the same turn). The entryscene.ssdlitself cannot be spawnable.spawn_budget/spawn_parameter_invalid/spawn_parameter_immutable/spawn_handle_unknown/spawn_placement_invalid— runtime refusals fromapi.scene.spawn_budgetnames the dimension and shows static + already-spawned + this copy against the manifest limit, and nothing is built.spawn_parameter_immutablemeans the parameter feeds acreate_onlymember, so changing it isdispose+spawn, notset.spawn_replay_failed— a hot reload replayed the spawn log into the new generation and one entry no longer fits the component as it was edited. It does not stop the mount; the entries are inssworld_logic_readunderdynamic.spawn_failures, and the objects they named are simply gone.runtime_unsupported—Label(no font),sunAzimuth/sunElevationwithoutatmosphereSunLight: true, and the members a light may not write in the mode it is in.multiple_writer/environment_duplicate— anEnvironmentowns the sun direction, so aDirectionalLightthat also pinssunAzimuth/sunElevation, or aSunSkybeside it, is refused rather than left to a last-writer-of-the-frame race; and the scene-globalEnvironmentslot takes exactly one claimant.sky_light_capture_owned—SkyLight.realTimeCapture: falsebeside anEnvironment. TheEnvironmentowns the sky light capture and keeps it running, so thefalsewould be a no-op; it is refused with the reason instead.material_requires_tangent— anormalMapon geometry without tangents (onlyHeightField/Lathe/Tube/Lofthave them).value_out_of_range—emissiveColoroutside its[r, g, b]0..16 range.
List values may span lines since 0.9.8: a newline after sections: [, one ring per line and comments inside the
brackets all compile. A property still ends at a newline or ;, so this only applies inside [ ].
Procedural geometry
Four parametric generators compile to constant parameters and are tessellated by the runtime (MeshData/v1, ccw outward,
≤ 65535 vertices per node, mesh_budget beyond): HeightField { width; depth; columns; rows; heights }
(columns/rows count cells, heights counts the (columns+1)*(rows+1) corners around them — row-major from -depth/2),
Lathe { profile: [[radius, 0, height] …]; segments; closed }, Tube { path; radius; segments; closed },
Loft { sections: [[ring] …]; cap }. ssworld_compile reports their cost as usage.mesh. There are no per-vertex
functions: arbitrary meshes are managed assets (Model).
Geographic layers
Every scene already stands on the globe at its manifest anchor; four components let it reach the globe
itself, all of them direct children of Scene with no transform of their own (geographic longitude/latitude
and the scene's local ENU metres are two different worlds, and SSDL does not pretend otherwise):
Globe { id: earth; terrain: "default"; lighting: false }
ImageryLayer { id: base; source: "https://<your tile service>/{z}/{x}/{y}.png"; webMercator: true }
Tileset { id: city; source: "https://<host>/tileset.json"; offset: [0, 0, -12] }
GeoJsonLayer { id: parks; source: "assets/parks.geojson"; geometry: "polygon"; fillColor: "#2e7d32" }
Globe— terrain, ground colour, opacity and lighting; at most one, and the last scene to write it hands the globe back as it found it on unload.ImageryLayer—kind: "xyz" | "wms" | "arcgis" | "single". Draw order is declaration order, the first layer being the base map;visibleandalphacan be bound and animated by scene logic. At most 8.Tileset— 3D Tiles, or a Gaussian splat set withsplat: true.offset/rotation/scaletransform the tileset's own root (offset.zis the height correction most datasets need). This engine has nomaximumScreenSpaceError;geometricErrorScale(0.2–12) is the LOD dial. At most 4.GeoJsonLayer— one layer draws one kind of feature (geometry: "polygon" | "line" | "point"). The document is a managedassets/*.geojsonwhose geometry is checked againstgeometryat compile time, or a remoteurlthe page fetches — a cross-origin server withoutAccess-Control-Allow-Originreportsgeojson_fetch_failedinstead of leaving a silently empty layer. Polygons are filled: withextrudeHeightFieldthey become solid blocks, without it a flat plane. At most 8.
Terrain moves the ground, not the scene: local z: 0 stays at the anchor's ellipsoid height, so a scene built
flat can end up buried in a hillside the moment terrain loads. ssworld_geo_read answers with
anchor_above_terrain_m, the imagery stack in real draw order and each layer's readiness. No basemap ships
with this server — a tile service carries terms and often a key, and neither is ours to accept for you;
"template": "geo" lays out the scene with the ImageryLayer line commented out, waiting for a URL.
SSWorld MCP 질문
SSWorld MCP는 어떤 도구를 제공하나요?
13개 도구를 제공합니다. 예: ssworld_catalog, ssworld_project_list, ssworld_project_create, ssworld_source_patch, ssworld_source_batch.
SSWorld로 에이전트가 무엇을 할 수 있나요?
에이전트는 SSEngine WebGPU 런타임에서 SSDL로 작성된 3D 지리적 장면을 작성, 컴파일, 렌더링 및 검사할 수 있습니다.
에이전트가 렌더링 결과가 올바른지 어떻게 알 수 있나요?
에이전트는 capture frame 도구를 사용하여 휘도 백분위수, 노출 테일, 색상 커버리지 및 카메라 포즈 데이터를 수신함으로써 시각적 출력을 검증할 수 있습니다.
SSWorld는 실제 지리 데이터를 지원하나요?
네, 경도와 위도를 기준으로 장면을 고정할 수 있으며 지형, 이미지 레이어, 3D Tiles 및 GeoJSON을 포함할 수 있습니다.
설치를 위한 시스템 요구 사항은 무엇인가요?
Node.js 버전 22 이상, npm, 그리고 미리보기를 확인하기 위한 Chrome 또는 Edge와 같은 WebGPU 호환 브라우저가 필요합니다.
