Koskamo

Sarkoa MCP

Connect AI agents to Sarkoa to manage geospatial data, including uploading files, creating and styling custom maps, querying vector layers and raster catalogs, managing shares, and handling webhooks via a hosted Model Context Protocol server.

Endpoint and access

OAuth sign-in: the first time you connect, your client opens the vendor's own authorization prompt in a browser.

Sarkoa MCP logo, by SarkoaVendor evidenceStreamable HTTPOAuth sign-inEndpoint checked 2026-11-06Operated by Sarkoa
You need:Create an API key under Profile → API & Integrations in the web app
MCP endpoint (Streamable HTTP)
https://mcp.sarkoa.com/mcp
Vendor documentation

Checks and signals

Each line is separate evidence, not a trust score.

  • Passed Endpoint answered the live check ·
  • Passed Official MCP Registry namespace is tied to sarkoa.com ·
  • Passed Endpoint is on the registry namespace domain ·
  • Availability 1 of 1 checks passed in 30 days

What you can do with it

  • Upload and organize geospatial files
  • Create and style custom maps
  • Query vector layers and raster data
  • Manage file shares and webhooks

Source and maintenance

Vendor evidenceRegistry 1.1.0

Protocol and capabilities

Server
sarkoa 1.10.0
MCP protocol negotiated in check
2025-03-26
Tools
Sign-in required
Resources
Sign-in required
Resource templates
Sign-in required
Prompts
Not advertised
Server-advertised capabilities
{
  "tools": {
    "listChanged": false
  },
  "resources": {
    "subscribe": false,
    "listChanged": false
  }
}

Server-provided instructions

Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.

Sarkoa MCP: live storage tools for your organization. Authenticate with Authorization: Bearer <API_KEY>. Best practices: 1. Prefer tool results over inventing tiler hosts, R2 URLs, API paths, or share tokens. 2. Call list_files once per folder (omit parent_id for the library root) or read sarkoa://files for root; reuse file IDs. Use list_folders when picking a destination. 3. Prefer get_file_status for one file. While processing, poll no faster than ~5s; back off on retries. BETTER: if the caller controls an HTTPS endpoint, create_webhook (asset.processing.succeeded / .failed) and stop polling; test_webhook verifies the receiver first. The signing secret is returned once, on create: surface it to the user immediately. 4. Do not re-call get_download_url or upload_presign for the same work unless the signed URL expired or failed. 5. On HTTP 429, wait for Retry-After (or several seconds) before retrying; never tight-loop API calls. 6. Uploads: upload_presign → HTTP PUT to storage → upload_complete (never send file bytes to this MCP endpoint). An upload_complete warning processor_trigger_deferred means the file IS registered and will be processed automatically: poll it, do not re-upload. A repeat upload_complete for the same upload returns deduplicated: true. To change a file and keep its id, see 19. 7. Before map styling, call inspect_asset for each layer. Use its fields, bands, statistics, and supported style modes rather than guessing. For categorized vector colours, call classify_vector_style (distinct palette by default; optional shuffle/seed). Raster styles: 20 and 21. 8. Read sarkoa://schemas/map/v1 and sarkoa://schemas/map/v1/example (or call scaffold_map) before authoring. Prefer slug layerIds (e.g. layer-smoke); unique per map. Use create_map/replace_map dry_run=true for persistence preflight. On validation failure, read errors[].path from the structured tool error JSON. Use get_map revisions as expected_revision for edits. 8b. VECTOR STYLING beyond colour, on layer.vectorStyle (both optional, omitted when unset): (a) sizeBy scales POINT radius / LINE width by a numeric attribute — {attribute, mode: proportional|graduated, domain:[min,max], range:[minPx,maxPx], transform: sqrt|linear, nullSize}; use sqrt for magnitudes so area, not radius, reads as the value. Graduated takes breaks[] + sizes[]: breaks are class EDGES, ONE LONGER than sizes (six sizes, seven breaks). Graduated COLOUR mode has the same +1 rule: {attribute, method, classCount, breaks (classCount+1), colors (classCount), nullColor}. Every mode requires all five paint keys (fillColor, strokeColor, fillOpacity, strokeOpacity, strokeWidth); parsing is all-or-nothing and the error names the key. This replaces pre-generating circle polygons. (b) strokeDash: solid | dashed | dotted | dash-dot, LINE layers only (polygon outlines and sized points stay solid). 3D blocks: item 18. 9. delete_file, revoke_share, and delete_map require confirm=true. Deleting a non-empty folder also needs recursive=true. Creating or publishing public, password, or invited_emails maps requires confirm_publish=true, as do clone_map of a public or invited_emails map and update_map adding a layer (addLayer, replaceLayers, updateLayer with a new assetId) to a public or password map (what those maps show: 22). Use setAccess to change who can open a map. 10. TIME-AWARE DATA - decide this before uploading, because it changes the upload call: (a) Same area imaged/measured repeatedly (satellite frames, monthly rasters, daily snapshots) -> a SEQUENTIAL SERIES: create_series once, then series_append_presign -> HTTP PUT -> upload_complete per frame (pass the returned appendRequestId + sequentialId). Add the container id as a SINGLE map layer - never one layer per frame, which stacks and hides everything below the top. TWO FRAMES COUNTS: a before/after pair for a single event is a series, not two layers. Give each frame ONE instant (captured_at); a period keeps several frames live at once, which for opaque imagery looks like a frozen scrubber. Call series_health before publishing. A raster uploaded outside a series is STATIC: it renders, but shows the same pixels at every point on the timeline and nothing errors, so a map can look time-aware while its imagery is frozen. (b) One vector file whose features carry dates -> a TEMPORAL VECTOR. Upload normally, but pass timestamp_field to upload_complete for a point in time, or timestamp_start_field + timestamp_end_field for an interval. Use an INTERVAL when a feature should persist once it appears (a perimeter, a closure, ground disturbed from a date onward) - a point-in-time feature blinks for one step and vanishes. On a one-playhead map an interval shows from its start up to, not including, its end, so back-to-back intervals should end exactly where the next begins. (c) One raster whose PIXEL VALUES encode a date (e.g. year of first change) -> keep it static and add a map interaction slider with action rasterThreshold. Needs a single-band measurement raster with band statistics. (d) A status RELATIVE to a per-feature date (too early / on time / too late around each place's own peak, deadline or forecast) -> ONE feature per place, not one per status. Store the date as Unix seconds in a numeric column, give every feature an interval spanning the whole timeline (timestamp_start_field + timestamp_end_field), and colour it with vectorStyle mode relativeTime {attribute, unit, thresholds: inner edges of (selected time - feature time), colors: thresholds + 1, labels}. Shape the timeline with spec.temporal: initial now, step day, labelFormat date, timeZone, autoplay, loop (see sarkoa://schemas/map/v1). For data that is a TYPICAL YEAR (a climatology), set cycle annual: labels drop the year and the map opens on today's date in the data's year every year, with no re-dating. Polish for any time map: spec.temporal.markers labels dates on the slider (a holiday weekend, a deadline); spec.temporal.controls hides scrubber controls that do not apply (modeToggle, followLatest, fineTune; all visible unless set hidden); set_layer_popup field_formats date turns a Unix-time column into a readable date and relative_status names the feature's class at the selected date; get_embed_code time opens a link on a date. Do not mix (a)/(b) with (c) on one map: two different time controls is worse than either alone. If the data has no time dimension, ignore all of this - a static layer is correct. 11. READING DATA, not just serving it: query_features runs a bbox / attribute query server-side and returns GeoJSON — use it to answer questions about a layer instead of downloading the file. Call inspect_asset first for field names. It reads the original GeoJSON upload, so it does not apply to rasters or other vector formats. For a raster, sample_raster returns the values it stores at one lng/lat (every band, nodata as a status; scale and offset are listed, never applied), so ask it what is at a place instead of reading colours off a tile. zonal_stats summarises a raster inside a polygon and sample_series reads a value per frame of a series (item 39). For imagery, stac_search finds rasters by bbox, time range, and collection. 12. A MAP NOBODY CAN READ IS NOT FINISHED. After adding layers, set_layer_popup (which fields a click shows) and set_layer_legend (what the colours mean) — without them a viewer clicks a feature and sees nothing, and a choropleth is decoration. Then get_embed_code for a paste-ready iframe; it warns when the map is not public, which is the most common embed surprise. 12b. OPEN ON ONE STORY. Every layer is visible when the map opens unless you set visible: false, and polygons and rasters cover what is drawn under them, so a map of several measures over the same places opens as a stack where only the top one can be read. Leave visible the one layer the map is about (one area-filling layer; points or lines on top of it are fine) and set visible: false on alternative measures, context and auxiliary layers. They stay in the layers panel for visitors to switch on. spec.layers runs bottom to top, so put the visible one last. create_map / replace_map / update_map return warnings[].code overlapping_visible_layers when two filled layers over the same area are both visible. 13. When a file is Failed, retry_processing with dry_run=true first: it returns the failure diagnosis so you can tell whether a retry can help. A malformed source fails the same way again. An Active orbit, point cloud, splat or 3D Tiles file made before a processing fix (e.g. content3d.heightsVerified false) is redone with retry_processing reprocess=true; its current version stays live until the new one succeeds. Before a large upload, check_upload_quota so a quota error arrives before the bytes, not after. 14. Collaboration is readable and writable: list_map_comments picks up reviewer feedback, post_map_comment leaves a note anchored to a point (replies inherit the parent anchor), resolve_map_comment closes the thread once you have acted. 15. PRINTING: no tool returns a PDF, because the user's browser makes it in the print composer. Write a PrintSpecV1 (sarkoa://schemas/print/v1; every field optional), call preview_print_layout (view access) or create_print_layout (edit access, saved), fix what report and warnings flag (legend_truncated, scale_varies, capped paper or DPI), then give the user composer_url to open and click Download PDF. 16. PUBLIC DIRECTORY (maps.sarkoa.com/explore): making a map public does NOT list it, and listing it is the user's decision, never a side effect of yours. get_map_listing shows whether it is listed, what blocks it (eligibility.failures), the terms and guidelines to confirm, and the categories. Call set_map_listing only when the user explicitly asks to list (or, with listed=false, to unlist) the map; confirm_guidelines carries the user's own answer, never yours. When the terms need accepting, an API key cannot do it: the user lists the map once in the map builder. 17. 3D CONTENT AND SATELLITES are asset kinds of their own (content_kind on list_files and inspect_asset): orbit (OMM JSON, OMM CSV, TLE/3LE), pointcloud (LAS/LAZ/COPC/PLY, served as COPC), splat (SPZ/SPLAT/KSPLAT/SOG/splat PLY, served as SPZ) and tileset (.3tz/.3dtiles/zipped tileset, served as .3tz). Upload them like any file; pass content_kind to upload_presign and upload_complete for .json, .csv, .ply and .zip files. A layer is still {layerId, assetId}: style it with the block for its kind, never vectorStyle/rasterStyle (a block on the wrong kind fails validation). orbitStyle {colorBy: altitude|layer, color?, staleAfterHours? 1..720, pointSize? 2..24 px, opacity? 0.1..1, outlineColor?, outlineWidth? 0..6 px, selectedColor?, showOrbitPath?, pathColor?}; pointCloudStyle {colorBy: rgb|elevation|intensity|classification, pointSize 1..10, edl, hiddenClasses?, maxScreenSpaceError? 1..64}; splatStyle {placement?: {lon, lat, height, heading, scale}}, and a splat with no placement from the file or the layer is not drawn; tilesetStyle {opacity 0..1, heightOffset?, maxScreenSpaceError? 1..64}. Open 3D content tilted (preferred_map_mode 3d) and orbit maps on the globe; scaffold_map does both when its layers carry content_kind. Orbit positions are SGP4 estimates that drift by kilometres a day: keep a layer current by uploading a fresh file. render_tile_preview and query_features do not apply to these kinds. 18. 3D: vectorStyle.extrude raises POLYGONS into 3D blocks: {heightAttribute | constantHeight, baseAttribute?, unit: m|ft|floors, floorHeight?} with exactly one of heightAttribute or constantHeight; colours still come from the mode, fillOpacity applies to the whole layer, and the blocks only read as 3D when the map opens tilted (preferred_map_mode 3d). spec.terrain {layerId, exaggeration?, heightUnit?} turns one single-band measurement raster layer of the map (a DEM: inspect_asset shows rasterProductKind measurement, one band) into the 3D terrain, merged over the global terrain; exaggeration defaults to 1 (true scale) with a layer DEM. heightUnit (m, ft, us-ft) says what the DEM values are in when its coordinate system does not; a unit stated in the file wins. 19. UPDATE A FILE IN PLACE, KEEP ITS ID: to change the data behind a map (a daily refresh, a corrected file), replace it instead of uploading a new file: upload_presign with replace_asset_id (the file id) -> HTTP PUT -> upload_complete with the same replace_asset_id and the returned objectKey. Map layers and their styles keep working with no re-pointing and no delete. The file stays Active on its current version until the new one succeeds; poll get_file_status and read replacement (processing | succeeded | failed with previous_version_live: true, i.e. the map still shows the old version: fix the file and replace again, retry_processing does not apply). Same kind only (vector for vector, raster for raster; any format within it). Not for a series or its frames (append a frame), a folder, or a file still processing. A succeeded replacement may carry warnings (replacement_field_missing / replacement_band_missing) naming map layers whose style or popup uses a field or band the new version lacks: update those layers. 20. Raster profiles (create_raster_profile, set_raster_profile_default) do not style custom maps; layer.rasterStyle in the map document does (profiles only reach tiles with SARKOA_RASTER_CANONICAL_MODE on, off by default). A time series cannot have profiles: set layer.rasterStyle on the series layer and it applies to every frame. 21. RASTER COLOUR RAMPS: layer.rasterStyle mode continuous colours the raw values of a single-band raster that inspect_asset reports as raster.productKind measurement. Other rasters ignore it: inspect_asset lists continuous in raster.suggestedStyleModes only for measurement rasters, and a save that sets it on another raster returns a continuous_style_not_rendered warning. It takes EITHER ramp with vmin/vmax (sequential: viridis|cividis|inferno|magma|plasma|turbo|ylorrd|ylgnbu; single hue, light to dark so low values fade into the basemap: grayscale|reds|blues|greens|oranges|purples; diverging, with vmin/vmax centred on the midpoint: rdbu|rdylgn|rdylbu|brbg|spectral; reverse: true runs the ramp high to low, e.g. rdbu reversed puts red at the high end; mode palette takes the same names and reverse), OR your own stops: [{value, color}], 2 to 64 entries, color #RRGGBB, value an absolute data value, strictly increasing - e.g. a diverging ramp centred on zero [{value:-5,color:"#2166AC"},{value:0,color:"#F7F7F7"},{value:5,color:"#B2182B"}]. Never both, and reverse is for named ramps only (reverse the stop colours instead). With stops, vmin/vmax are the first and last stop values: omit them or give exactly those. The viewer blends linearly between stops (two stops a small step apart make a hard edge); values below the first stop are transparent and above the last take the last colour; a threshold hides values outside its range without moving a stop. The legend draws the stops. Do not bake colours into a uint8 COG to get a ramp of your own. For band math (NDVI and other indices) use rasterStyle mode expression (item 39). 22. A PUBLIC OR PASSWORD MAP SHOWS ITS OWN LAYERS to everyone who can open it (drawn, with popups and search), whatever each file's access: do not set files public or call create_share_link to make a map work, since those expose the file outside the map (its own page, downloads). replace_map takes access from spec.access, so a document without it makes the map organization-only (access_mode_changed warning). A save lists the layers it newly exposes (map_exposes_layers warning). Visitors get no download of them, but what the map draws reaches their browser: a vector layer's tiles, with their attributes, can be saved in full, so keep a layer whose features must stay private off public and password maps. 23. Use whoami to verify connectivity, not on every turn. After MCP deploys, reload the MCP host so new tools appear. 24. ROUTE FLY-THROUGHS: a map can carry up to 10 camera paths (spec.cameraPaths) that the viewer flies along over terrain with play / pause / scrub and stops: a hike, a river, a road. Upload the route as a LINE layer, add it to the map, then set_camera_path {map_id, layer_id, feature_id?, name, expected_revision}: the app resolves and simplifies the geometry, which is stored in the map (viewers need no access to the layer). Tune speed, height_above_ground, follow_distance and look_ahead; stops pause at metres from the start (the result reports lengthM). Open the map in 3D with terrain. remove_camera_path deletes one. 25. ANIMATED FLOW (layer.flow) draws motion ON TOP of the layer's own style: keep vectorStyle / rasterStyle, which the legend, popups, search and print still use. (a) LINE layers: comets move along each line in the order of its points (reverse: true flips it). speed is {mode: constant}, or one number per line {mode: attribute, attribute, domain: [low, high], transform: sqrt|linear, range: [slowest, fastest] factors, default [0.3, 2.5]}, or one number per point {mode: perVertex, attribute: an array property holding one value per vertex, every part in order}. color "style" follows the vectorStyle colours, or give one #rrggbb (white over coloured lines reads like Windy). speedPx, spacingPx and trailPx are screen pixels at speed factor 1 (trailPx at most 0.4 x spacingPx); glow suits dark basemaps; baseOpacity dims the line while comets run. For perVertex, pass tile_exclude_properties [the array property] to upload_complete so the array stays out of the tiles (a replacement keeps it). (b) WIND / CURRENT rasters: upload east and north components as a two-band GeoTIFF (names like u10/v10, uo/vo, eastward_wind are detected) or a NetCDF with variable_names [u, v]; vector_field {u_band, v_band}, {speed_band, direction_band, direction_convention: from|to} or false overrides detection. On a model's own Lambert, polar stereographic or rotated-pole grid (HRRR, NAM, WRF, CORDEX), names like UGRD or u10 do not say whether the components follow the grid or true north: detection then keeps an ordinary raster, and vector_field must add relative_to "grid" (the usual case for a model's native output) or "earth". A model's curvilinear grid (2-D lat/lon per cell: ROMS, WRF, regional NEMO) works with variable_names when u and v share the same coordinates; a staggered pair (ROMS u/v, WRF U/V) is refused, so name the cell-centre pair (ROMS u_eastward/v_northward, WRF U10/V10). inspect_asset then reports raster.vectorField (bands u, v, speed; units; scale); colour the speed with rasterStyle continuous on band 3 and set flow {density, refSpeed, speedPx, trailPx, width, color, opacity, glow}. Not together with visualization. Say the motion shows direction and relative speed, never a measured speed, unless the field's own units say so. Viewers pause it with a button and hold it still under reduced motion. 26. WEATHER MODEL FILES (GRIB, NETCDF) AND KML TIME: GRIB (.grib2/.grb2/.grb: GFS, HRRR, ECMWF) and NetCDF upload like any raster. A file holding one field or variable, or just an east/north wind pair at one level and one valid time, needs no options: a GFS or HRRR UGRD/VGRD file becomes a wind field ready for layer.flow (25b), and HRRR's grid-relative winds are turned to true north from the file's own flag. Any other file makes upload_complete answer 422 (code grib_bands_required with bands [{element, level, validTime, description}], or netcdf_variable_required with variables): call upload_complete again for the same object with grib_bands [{element, level, validTime?}] (1 to 4 fields, ONE valid time; UGRD and VGRD at 10-HTGL is 10 m wind) or netcdfVariable / variable_names. A forecast with several times is a SERIES, one upload per valid time (10a). KML <TimeStamp> and <TimeSpan> become the time (an instant, or an interval from begin to end) when temporal_mode is auto and no timestamp field is named. 27. 3D MODELS (glTF 2.0) are tileset assets: upload a .glb, or a .zip holding exactly one .gltf or .glb with its .bin and texture files and no tileset.json (pass content_kind tileset for the .zip; a bare .gltf is refused, zip it with its files). A model has no location of its own: inspect_asset reports content3d.georeferenced false and localBounds, and its layer draws nothing (needs placement) until tilesetStyle.placement {lon, lat, height (metres in the map's terrain datum: the ground height there), heading (degrees clockwise from north; 0 points the model's front, glTF -Z, north), scale (metres per model unit; glTF units are metres, so 1 is its true size)} stands the bottom-centre of its local bounds on that point. heightOffset adds to height. A placement on a georeferenced tileset is ignored (save warning tileset_placement_ignored). Open the map tilted (preferred_map_mode 3d) and centred on the placement. TERRAIN SHADING: spec.basemap.overlays.hillshade: 'under' shades the streets basemap; absent = off. 'over' also shades data and basemap features underneath, including water. Satellite imagery never draws hillshade. 28. SATELLITE BASEMAP: spec.basemap.basemapId satellite-basemap draws Sentinel-2 imagery of the whole world (our own summer mosaics over most of the United States, southern Canada and Europe, the ESA WorldCover 2021 composite elsewhere) with the streets map's labels, roads and boundaries over it; basemap_overlays still apply. mapTheme colours the streets basemap only, and the theme id satellite is a colour scheme (shown as Earth tones), not imagery. Where satellite is not switched on yet the map shows streets. Imagery tiles are not counted in the tile limit and carry their own credit: do not add one to layer attribution. 29. KEYFRAME TOURS (story mode): a map can carry up to 10 tours (spec.tours) of up to 50 stops; the viewer flies from stop to stop and holds at each, showing its title and plain-text body, and visitors play it or step with previous / next and the arrow keys. set_tour {map_id, title, stops: [{center_lng, center_lat, zoom, bearing?, pitch?, mode?, title, body?, duration_ms?, hold_ms?, visible_layers?, time?}], autoplay?, loop?, expected_revision} adds or replaces one (remove_tour deletes one). A stop can show only some layers (visible_layers) and move the time slider (time). Write each stop as a short caption a person would read in a few seconds; tilt only with 3D terrain or extruded buildings. Link to a stop with ?tour=<id>&stop=<n> (n from 1); an embed's page drives it with postMessage {type: 'sarkoa:tour', action: 'goToStop', stop: n}. 30. VISUALIZATION v2 (layer.visualization on vector layers; kinds arc, trips, hexbin, heatmap, column, path for static lines, scatter for points). colorBy colours arc, trips, column, path and scatter by an attribute with EXACTLY the keys of a categorized or graduated vectorStyle: {mode: categorized, attribute, classes [{value, color, label?, enabled?}], otherColor} or {mode: graduated, attribute, breaks (class EDGES, one more than colors), colors, nullColor, normalizeBy?}; classify_vector_style output works as is (its paint keys are dropped with a warning). It replaces sourceColor/targetColor, color and the column palette; arcs keep their direction by fading the origin end (sourceOpacity, default 0.25 with colorBy). trips and path take widthField (square root). trips.head {show, radius 2-20 px, color?} adds a clickable marker at each trip's current position, and its popup follows it. altitude {mode: ground|absolute|relative (required), source: geometry|field, field?, unit: m|ft, scale 0.1-100, base?} draws trips, path and scatter at height, from [lon, lat, z] coordinates or a property (one number per feature, or one per vertex of a line); on the flat map absolute heights start at base, else the layer's lowest altitude. Readers tilt the map to see heights; visualizations are hidden in 3D terrain view and below zoom 10 on the globe. Give the layer a popup so points and trip heads show their altitude. 31. MEASURING ON 3D CONTENT needs no map field: in the hosted viewer the measure tool places points on point clouds, 3D models and terrain at their true heights (slant distance and height change for a path; areas stay horizontal), and Show profile cuts a cross-section through a point cloud (a corridor 1 to 20 m wide along the path) or an elevation profile from the map's terrain. Its CSV export follows the map's download settings, per layer: a visitor can save a cross-section only when the map allows downloads and that point cloud is downloadable. 32. CLICKABLE LEGENDS: in the hosted viewer a reader can click a categorized or graduated legend row (vectorStyle, or a visualization's colorBy) to hide that class, and Show all to reset; hidden classes AND with spec.interactions filters, and ?hide=<layerId>:<key>|<key> in the URL shares the view (keys: =<value>, ~other, ~null, #<class index>, ~nodata). It is on by default; set spec.legends.interactive false (setLegends) to keep a legend read-only. It needs a visible legend panel (spec.legends.panel visible or collapsed). Give classes readable labels: the row label is what the reader clicks. 33. CONTOUR LINES: layer.contours {interval (required, in unit), unit m|ft (default m), majorEvery (0 or 2..50, default 5: every Nth line thicker and labelled), color #rrggbb, width, majorWidth, opacity, labels} draws contour lines over a SINGLE-BAND MEASUREMENT raster (an elevation model: inspect_asset shows rasterProductKind measurement and one band); on any other layer the save fails with style_block_wrong_kind. Heights are read in metres (the unit the file's CRS states, else spec.terrain.heightUnit). The same block at spec.terrain.contours draws lines from the map's terrain surface (the terrain layer over the global terrain, or the global terrain alone) in 2D and 3D. Lines are made in the viewer from tiles already served: nothing is reprocessed. Zoomed out, the spacing widens by majorEvery so lines stay readable. The legend says "Contours every 20 m". 34. VISUALIZATION DATA: visualizations and line flow read a column copy that processing writes beside every vector upload, so any vector format works (Shapefile, GeoPackage, CSV, FlatGeobuf, KML, GeoParquet, not only GeoJSON) up to 10 million points for hexbin, heatmap, column and scatter, 1 million arcs, and 2 million vertices for trips, path and line flow. Phones with little memory get lower limits and the viewer says so instead of drawing part of a layer, so keep layers near those sizes off maps meant for phones, or aggregate first. A file uploaded before the copy existed reads its original GeoJSON (32 MB; other formats show a message) until a replacement of the same file (19) rebuilds it. Viewers without download rights get coordinates rounded to 5 decimals, as from tiles. 35. 3D TILES COLOUR BY PROPERTY: tilesetStyle.colorBy takes the visualization colorBy block (categorized {attribute, classes, otherColor} or graduated {attribute, breaks (classCount+1), colors, nullColor}) and colours each feature of a 3D Tiles layer by a property of its metadata (EXT_structural_metadata property tables, or B3DM/I3DM batch tables). inspect_asset's content3d.featureProperties lists the property names, types and sample values (field-stats shape, so classify like a vector); files processed before it existed need retry_processing to get it. tilesetStyle.blend: replace (default, the class colour alone) or tint (multiplies the mesh's own colour and texture). Limit: features marked only in textures (most photogrammetry) keep their own colours. The legend lists the classes. 36. COMPARE (spec.compare): two synced maps of one map, cut by a draggable divider (mode swipe, position 0..1) or next to each other (mode side-by-side; phones get a horizontal divider and stacked panes). left / right {layerIds?, time?}: a listed layer draws on that side only, layers listed on neither side draw on both, a layer on both lists is an error, and the sides must differ. For BEFORE / AFTER imagery use ONE series layer and give each side a time (left.time 2019-07-01, right.time 2024-07-01): each side shows its own frame; two separate static rasters on the two lists also work but are not a series. leftLabel / rightLabel (1..60 chars) name the sides; autoOpen opens it on load; deep link ?compare=0.5 (or on / off). Set it with update_map {op: setCompare, compare: {...} | null}; a replace that leaves compare out keeps it. Viewers need ENABLE_MAP_COMPARE (or ?mapPreview=compare) until it is on by default. 37. VISUALIZATIONS IN 3D need no map field: where the viewer has it on (ENABLE_VISUALIZATION_3D), every visualization kind and line flow also draws in 3D terrain view and on the globe below zoom 10 instead of being hidden. In terrain view lines and points lie on the hills, altitude relative is height above the ground under each vertex and absolute is above sea level (times the terrain exaggeration); hexbins, columns and heatmaps draw as MapLibre extrusions and heatmaps, so their heights step instead of easing. Do not split or flatten a layer to work around the old hiding. 38. TIMELINE STEPS, SCALES AND MOTION: spec.temporal.step also takes month and year (labels read "October 2026" / "2026"; ?t= links are YYYY-MM / YYYY). A map whose only time-aware layers are visualizations with timeField (hexbin, heatmap, column, scatter) gets a timeline from their data. hexbin and column take domain [min, max] to hold colours (and hexbin heights) steady across timeline windows, else they rescale to what is in view, and scale linear|log|quantile for the palette. trips.loopStartTime (ISO-8601) starts each loop later than the data's first time. Line flow widthPerVertex {attribute (one number per point), domain, range [px, px], transform sqrt|linear, nullWidth} widens comets along a line, replacing width. Opposite-direction arcs between the same two places are tilted apart automatically. A panBounds spanning every longitude limits nothing and is cleared with a warning. 39. RASTER ANALYTICS AND BAND MATH: an expression is band math on the RAW stored values (no scale or offset): numbers, b1..b64, + - * /, comparisons (1 or 0), abs sqrt log log10 exp pow min max clamp, nd(a,b) = (a-b)/(a+b) and where(c,a,b); nodata in any band it reads gives nodata. NDVI on Sentinel-2 is nd(b8,b4). zonal_stats {asset_id, geometry, bands | expr, percentiles, histogram_bins, all_touched, min_resolution} answers count, mean, min, max, sum, stddev inside a Polygon or MultiPolygon; past 1,048,576 pixels it reads an overview and says approximate=true (min_resolution in metres refuses instead). sample_series {series_id, point | geometry, from, to, bands | expr, percentiles, limit, cursor} reads every frame of a series in one call: the point form for a value over time at a place (InSAR displacement at a building), the geometry form for a zonal summary per frame (a field's NDVI each season, capped at 262,144 pixels a frame); page with cursor. Points and geometries go in tool arguments, never in URLs. sample_raster takes expr too (bands as b1.. only); zonal_stats and sample_series also accept the band names inspect_asset lists. To SHOW an index on a map, layer.rasterStyle mode expression {expr, vmin, vmax, palette, opacity?} renders it on the tiler, stretched from vmin to vmax and coloured by palette (rdylgn suits NDVI from -0.2 to 0.9, rdbu a diverging field such as InSAR centred on 0; any named ramp of item 21 works, and reverse: true flips it); the raster modes are bands, palette, continuous, categorical and expression. On a published map, visitors can draw an area with the measure tool and chart a series layer through time (mean per frame with a +/- 1 standard deviation band) only when spec.analysis.areaTimeSeries.enabled is true: it is off by default, every chart reads the series and is billed to the map's organization tile quota, and members of that organization can always use it.

Tools

Sign-in required

No tool definitions available.

Prompts

Not advertised

No prompt definitions available.

Resources

Sign-in required

No resource definitions available.

90-day uptime

A lightweight connection check about every six hours, sampled on hosts with many listings. It shows the server answered, not that its tools work.

Too few checks for a percentage yet (1 of 1 answered).

  1. 2026-11-07: 1 of 1 checks answered

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
352 ms
95th percentile latency
352 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 352 ms · metadata changed · refuses unauthenticated tools/list (HTTP 401)

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http sarkoa-mcp https://mcp.sarkoa.com/mcp

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add sarkoa-mcp --url https://mcp.sarkoa.com/mcp

Cursor

Merge this server into .cursor/mcp.json in your project, then enable sarkoa-mcp in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "sarkoa-mcp": {
      "url": "https://mcp.sarkoa.com/mcp"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "sarkoa-mcp": {
      "type": "streamableHttp",
      "url": "https://mcp.sarkoa.com/mcp",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start sarkoa-mcp and inspect its tools.
VS Code configuration
{
  "servers": {
    "sarkoa-mcp": {
      "type": "http",
      "url": "https://mcp.sarkoa.com/mcp"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http sarkoa-mcp https://mcp.sarkoa.com/mcp

Claude Desktop

Open Claude Settings > Connectors > Add custom connector, enter this server URL and enable it for the conversation. This is a manual remote-connector handoff, not a desktop JSON configuration.
Claude Desktop configuration
https://mcp.sarkoa.com/mcp

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "sarkoa-mcp": {
      "serverUrl": "https://mcp.sarkoa.com/mcp"
    }
  }
}

Let your agent find its tools

Find servers like Sarkoa MCP from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.

Do you run Sarkoa MCP?

Prove ownership with a DNS record, a well-known file, or your Official MCP Registry entry, and your edits publish immediately. Proofs are re-checked weekly; admins can still hide or remove a listing.

Claim this listing