MCP (AI agents)
Connect Claude, Cursor, and other AI agents to your Sarkoa organization, using the same API keys as the CLI.
Agents get the whole library, not a read-only view of it: organizing and uploading files, authoring and styling custom maps, running time series, leaving and resolving review comments, registering webhooks, and querying your vector layers and raster catalogue directly. That last part is the one people underestimate. An agent that can call query_features answers questions about your data instead of only drawing it.
Set up
- Create an API key under Profile β API & Integrations in the web app (see Authentication).
- Add Sarkoa as a remote MCP server in your AI app.
Server URL: https://mcp.sarkoa.com/mcp
Claude Code / Claude Desktop
{
"mcpServers": {
"sarkoa": {
"url": "https://mcp.sarkoa.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_TOKEN"
}
}
}
}
Cursor
Use the same JSON in your MCP settings (mcp.json): set url to the server URL above and put your API key in headers.Authorization.
Confirm it works
Ask the agent to run whoami, then list_files. You should see your account and the files in your organization.
Official MCP Registry
Sarkoa is published as com.sarkoa/mcp on the official MCP Registry. Clients and directories that ingest the registry can discover the same Streamable HTTP endpoint and Bearer API-key auth described above.
What agents can do
Fifty tools, grouped by what they act on. Every tool takes the organization from the API key,
so organization_id is only needed when a key spans several organizations.
Identity
| Tool | What it does |
|---|---|
whoami |
Confirm the API key and show email, organization, scopes, and auth method |
Files and folders
| Tool | What it does |
|---|---|
list_files |
List files and folders in one location (parent_id optional; library root if omitted) |
list_folders |
List all folders (id, name, parent) so you can pick a destination |
create_folder |
Create a folder (parent_id optional) |
move_file |
Move a file or folder (folder_ref, or root=true for the library root) |
rename_file |
Change a file or folder's display name. The stored object is untouched |
get_file_status |
Show status and details for one file |
get_view_url |
Open the file in the Sarkoa web viewer |
get_download_url |
Get a temporary link to download the original file |
delete_file |
Delete a file (the agent must confirm). A folder that still has contents also needs recursive=true |
retry_processing |
Requeue a file whose optimization failed. Call with dry_run=true first to read the failure report: a malformed source fails the same way again |
check_upload_quota |
Ask whether an upload of a given size would exceed the plan limit, before transferring the bytes |
inspect_asset |
Inspect fields, bands, statistics, and style options for a file before styling, including sizeChannel and strokeDashSupported |
Uploads
| Tool | What it does |
|---|---|
upload_presign |
Start an upload and return where to send the file |
upload_part_url |
For large files: get the next upload chunk URL |
upload_complete |
Finish the upload so Sarkoa can optimize the file (optional parent_id to place it in a folder) |
Sharing
| Tool | What it does |
|---|---|
create_share_link |
Create a public /share/β¦ link (optional expiry / password on Essential+) |
list_shares |
List share records for a file |
revoke_share |
Revoke a share (the agent must confirm) |
Maps
| Tool | What it does |
|---|---|
list_maps |
List custom maps in your organization |
get_map |
Load a full map document (layers, styles, access, chrome) |
get_map_preview |
Return viewer and editor links for a map |
get_embed_code |
Return a ready-to-paste iframe plus the raw embed URL, and say so when the map is not public yet |
validate_map |
Check a map document without saving |
scaffold_map |
Build a ready-to-edit map document stub (does not save). Layers may include attribution_text / attribution_url. Optional preferred_map_mode (2d/3d) and preferred_projection (mercator/globe) |
create_map |
Create a map from a full document, or a minimal title + layer list (same attribution and display fields as scaffold_map) |
replace_map |
Replace a map with a full desired-state document. Omission deletes: a per-layer key the document leaves out is removed |
update_map |
Apply typed map operations (add/update/remove layers, set styles, setView for display defaults, setAccess for who can open the map) |
clone_map |
Duplicate a map |
delete_map |
Delete a map (the agent must confirm) |
Styling a layer
| Tool | What it does |
|---|---|
classify_vector_style |
Build categorized class colours (distinct by default, or Category 10 / colour-blind-safe). Optional map_id + layer_id applies the style; shuffle recolours. Keeps existing sizeBy and strokeDash |
set_layer_popup |
Choose which attributes a click shows, and which is the heading. Without it, clicking a feature shows nothing |
set_layer_legend |
Enable and label the legend for a raster layer. Vector legends come from the style itself, so this returns an error on a vector layer |
render_tile_preview |
Fetch one actual rendered raster tile as an image, to check a style instead of assuming it worked |
Map review
| Tool | What it does |
|---|---|
list_map_comments |
List review comments on a map, with open and resolved counts |
post_map_comment |
Post a comment anchored to a point, or reply to a thread (a reply inherits its parent's anchor) |
resolve_map_comment |
Mark a comment resolved, or reopen it |
delete_map_comment |
Delete a comment (the agent must confirm) |
Time series
| Tool | What it does |
|---|---|
list_series |
List time series with entry counts and time bounds |
create_series |
Create a series container to hold ordered frames |
series_append_presign |
Start adding one frame; returns a signed upload payload to PUT the bytes to |
list_series_entries |
List the frames in a series with timestamps, status, and format |
update_series_entry |
Change a frame's timing without re-uploading it |
series_health |
Check whether a series will actually animate, and why not |
Asking questions of the data
| Tool | What it does |
|---|---|
query_features |
Filter a hosted vector layer by bounding box and attributes and get GeoJSON back, without downloading the file. See Feature query |
stac_search |
Search the raster catalogue by bounding box, time range, and collection |
Webhooks
| Tool | What it does |
|---|---|
list_webhooks |
List endpoints and the event types available to subscribe to. Signing secrets are never returned |
create_webhook |
Register an HTTPS endpoint. Returns the signing secret exactly once, so surface it to the user immediately |
test_webhook |
Send a synthetic signed delivery now and report what the endpoint returned |
delete_webhook |
Remove an endpoint and its pending deliveries (the agent must confirm) |
Agents can also read sarkoa://files (file list), sarkoa://maps (map list), sarkoa://maps/{id} (one map), sarkoa://schemas/map/v1 (map document schema), and sarkoa://schemas/map/v1/example (minimal valid document).
Creating and styling maps
Maps use a versioned map document (sarkoa.com/maps/v1). A document describes the camera, basemap, access settings, and every layer style (fill, outline, categorized/graduated vector styles, raster palettes, popups, legends, and more). Layer titles you set appear in the layers panel; legend titles work for simple styles as well as categorized and graduated ones. Layer attribution_text / attribution_url (or layers[].attribution) set the cartographic credit in the bottom-right map control. preferred_map_mode (2d or 3d) and preferred_projection (mercator or globe) control how the map opens; visitors can still switch in the viewer.
Typical agent flow:
list_files(or reuse known file IDs).scaffold_map(or readsarkoa://schemas/map/v1/example) for a starting document.inspect_assetfor each layer you plan to style. For categorized vector layers, callclassify_vector_styleso classes get distinct colours (or passshuffle=trueto recolour). For point size or line width by a number, setvectorStyle.sizeByusinginspect_asset.sizeChannel. For dashed lines, setstrokeDashwhenstrokeDashSupportedis true.validate_mapwith your document, thencreate_map(full document or minimal{ title, layers }). Usedry_run=trueon create/replace when you want persistence checks (including duplicatelayerIdwithin the map) without saving.- Share
get_map_previewlinks with the user. - For follow-up edits, call
get_map, keep the returned revision, and pass it asexpected_revisiontoreplace_maporupdate_map.
If validation fails, the tool returns structured JSON with errors[].path so the agent can fix the right field. Public, password-protected, or email-invite maps require confirm_publish=true. Deleting a map requires confirm=true. If a tool listed on this page does not show up in your AI app, reload the MCP connection there.
Uploading files
Uploads go straight from the agent (or your machine) to Sarkoa storage.
- The agent calls
upload_presignwith the filename and size. - It uploads the file to the link that call returns (large files may use several chunk URLs via
upload_part_url). - It calls
upload_completeso Sarkoa registers the file and starts optimization. Passparent_idto place the file in a folder.
If your AI app cannot upload to those links, use the web app or CLI instead, then have the agent work with the file by name or ID.
Call check_upload_quota before a large upload. Otherwise the bytes transfer first and the quota error arrives at the end, after the wait.
Waiting for processing without polling
The obvious loop after upload_complete is to call get_file_status until it changes. That works, and for one file it is fine. It is the wrong shape for a batch: it burns request quota and adds latency equal to the poll interval.
If the agent (or whatever it is automating) controls an HTTPS endpoint, register it once instead:
create_webhookwithasset.processing.succeededandasset.processing.failed. The signing secret comes back exactly once, so hand it to the user immediately.test_webhookto confirm the receiver actually answers before you depend on it.- Stop polling. Sarkoa posts a signed event, usually within a minute.
Full event catalogue, payload shape, and signature verification: Webhooks.
When polling really is the only option, poll no faster than about every five seconds and back off on retries.
Asking questions about the data
Styling is not the only thing an agent can do with a hosted layer. Two tools read the data itself:
query_featuresfilters a vector layer by bounding box and attributes and returns GeoJSON, server-side, with no download. Runinspect_assetfirst to see the attribute names.stac_searchfinds rasters by bounding box, time range, and collection.
Both are covered in Feature query, including the size limits and what happens when a source is not queryable.
Checking your own work
An agent that styles a raster and reports success has not actually verified anything. render_tile_preview fetches one real rendered tile and returns it as an image, so the model can look at the result before telling you it is done.
Tips for reliable agents
These habits help agents work smoothly with your files and maps:
- Use the links and file IDs returned by MCP tools. Do not ask the agent to invent viewer, download, or share URLs.
- List one folder at a time (
list_fileswithparent_id, or omit it for the library root). Uselist_folderswhen choosing a destination. Reuse those IDs instead of listing again. - Check one file with
get_file_status. While a file is still processing, wait several seconds between checks. - Request a download or upload link only when you need a new one (for example if a previous link expired).
- If the server asks the agent to slow down, wait before trying again.
- For uploads, follow the three-step flow above: get an upload link, send the file to that link, then call
upload_complete. Do not send file bytes through the MCP connection. - Before styling a map layer, call
inspect_assetand use its fields, bands, and statistics. For categorized colours, callclassify_vector_styleinstead of inventing hex values. Size points or lines withsizeBy; dash line layers withstrokeDash. Unrecognised style keys are dropped with a warning rather than applied. - After
get_map, pass the revision into later edits so concurrent changes are not overwritten. - Only delete a file, revoke a share, or publish a map (public, password, or email invite) when you explicitly ask for it. The agent must confirm before those tools run. Deleting a folder that still has contents also needs
recursive=true. - Use
whoamito confirm the connection works, not on every message. - If validation fails, have the agent read
errors[].pathin the tool error JSON instead of guessing. When a call succeeds but returnswarnings, read those too: they list the parts of the request that could not be used and were dropped. The codes are listed under Map documents. 11b. Argument names are checked. An argument a tool does not recognise is rejected with an error naming the closest match, and nothing is written. Either spelling works (title_fieldortitleField). - Ask for an embed with
get_embed_coderather than assembling a URL. A hand-built embed URL is the most common way an agent produces a frame that silently shows nothing. - Diagnose before retrying.
retry_processingwithdry_run=truereads the failure report; a malformed source will fail the same way on a second attempt. - Prefer a webhook over a polling loop for batches, and verify the receiver with
test_webhookbefore relying on it. - When a webhook is created, pass the signing secret straight to the user. It is returned once and cannot be retrieved later, only rotated.
When an agent connects, the MCP server shares the same guidance. You can also install the optional Skills pack so hosts that load skill files pick it up.
Related
- Authentication
- CLI
- Skills (AI agents)
- Feature query:
query_featuresandstac_searchin detail - Webhooks: the alternative to polling for status
- Map documents: the JSON the map tools read and write
- API reference
- Downloads