@showmeonmap/mcp 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -4
- package/dist/fetchNoRedirect.js +16 -0
- package/dist/generalizeGeometry.js +232 -0
- package/dist/http.js +2 -0
- package/dist/index.js +2 -1
- package/dist/nextActions.js +340 -0
- package/dist/serverInstructions.js +34 -0
- package/dist/sessionStoreD1.js +28 -8
- package/dist/tools.js +774 -107
- package/dist/version.js +3 -0
- package/dist/viewer.js +117 -0
- package/package.json +1 -1
package/dist/version.js
ADDED
package/dist/viewer.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP App map viewer — server-side surface.
|
|
3
|
+
*
|
|
4
|
+
* When `createServer` receives `viewer` opts (the hosted HTTP server only),
|
|
5
|
+
* it registers VIEWER_URI as an MCP App resource, tags VIEWER_TOOLS with
|
|
6
|
+
* `_meta.ui.resourceUri`, and attaches a hidden `(map_id, cursor, opId)`
|
|
7
|
+
* pointer under VIEW_META_KEY to their results so the card in Claude loads
|
|
8
|
+
* that exact version. Without `viewer` (stdio / npm builds) nothing changes.
|
|
9
|
+
*
|
|
10
|
+
* This package must not import from the main app's `src/`; VIEW_META_KEY is
|
|
11
|
+
* duplicated in `src/mcp-viewer/viewData.ts` and a unit test pins the two
|
|
12
|
+
* literals equal.
|
|
13
|
+
*/
|
|
14
|
+
export const VIEWER_URI = 'ui://showmeonmap/map-viewer.html';
|
|
15
|
+
export const VIEW_META_KEY = 'showmeonmap/view';
|
|
16
|
+
export const MCP_APP_MIME = 'text/html;profile=mcp-app';
|
|
17
|
+
export const VIEWER_TOOLS = new Set([
|
|
18
|
+
'build_map', 'mutate_map', 'add_layer', 'add_fusion_layer', 'filter_layer', 'focus_area',
|
|
19
|
+
'run_analysis', 'remove_layer', 'clear_filters', 'rename_layer', 'restyle_layer',
|
|
20
|
+
'set_camera', 'set_time', 'add_annotation', 'remove_annotation', 'undo', 'redo', 'run_workflow',
|
|
21
|
+
]);
|
|
22
|
+
/** Canonical origin — a trailing slash or path must never leak into asset
|
|
23
|
+
* URLs (`//mcp-viewer/…`) or CSP entries. */
|
|
24
|
+
function normalizeOrigin(assetOrigin) {
|
|
25
|
+
return new URL(assetOrigin).origin;
|
|
26
|
+
}
|
|
27
|
+
export const VIEWER_UNAVAILABLE_TEXT = "Map viewer unavailable — open the map from the link in Claude's reply.";
|
|
28
|
+
/** Tiny shell: the real viewer is a hashed, immutable bundle on assetOrigin.
|
|
29
|
+
* Any manifest/bundle failure renders a visible fallback instead of a blank
|
|
30
|
+
* card (a rejected top-level await is otherwise silent). */
|
|
31
|
+
export function viewerShellHtml(assetOrigin) {
|
|
32
|
+
// `<` escaped so the inlined literal can never close the <script> element.
|
|
33
|
+
// The fallback box carries its own background + text colour (#222 on #f2f2f2,
|
|
34
|
+
// ~14:1) so its contrast never depends on the host's light/dark card.
|
|
35
|
+
const o = JSON.stringify(normalizeOrigin(assetOrigin)).replace(/</g, '\\u003c');
|
|
36
|
+
const msg = JSON.stringify(VIEWER_UNAVAILABLE_TEXT).replace(/</g, '\\u003c');
|
|
37
|
+
return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
38
|
+
<style>html,body,#smo-viewer{margin:0;height:100%;background:transparent}</style></head>
|
|
39
|
+
<body><div id="smo-viewer"></div><script type="module">
|
|
40
|
+
const O=${o};
|
|
41
|
+
try{
|
|
42
|
+
const res=await fetch(O+'/mcp-viewer/manifest.json',{cache:'no-store'});
|
|
43
|
+
if(!res.ok)throw new Error('manifest HTTP '+res.status);
|
|
44
|
+
const m=await res.json();
|
|
45
|
+
window.__SMO_ASSET_ORIGIN__=O;window.__SMO_MANIFEST__=m;
|
|
46
|
+
const l=document.createElement('link');l.rel='stylesheet';l.href=O+'/mcp-viewer/'+m.css;document.head.append(l);
|
|
47
|
+
await import(O+'/mcp-viewer/'+m.js);
|
|
48
|
+
}catch(e){
|
|
49
|
+
console.error('[smo-viewer] failed to load viewer',e);
|
|
50
|
+
const el=document.getElementById('smo-viewer');
|
|
51
|
+
if(el){const p=document.createElement('p');p.style.cssText='margin:16px;padding:12px 16px;border-radius:8px;background:#f2f2f2;color:#222;font:14px/1.4 system-ui,-apple-system,sans-serif';p.textContent=${msg};el.replaceChildren(p);}
|
|
52
|
+
}
|
|
53
|
+
</script></body></html>`;
|
|
54
|
+
}
|
|
55
|
+
export function viewerResourceMeta(v) {
|
|
56
|
+
const uniq = (xs) => [...new Set(xs)];
|
|
57
|
+
const origin = normalizeOrigin(v.assetOrigin);
|
|
58
|
+
return {
|
|
59
|
+
ui: {
|
|
60
|
+
prefersBorder: true,
|
|
61
|
+
csp: {
|
|
62
|
+
connectDomains: uniq([origin, ...v.connectDomains]),
|
|
63
|
+
resourceDomains: uniq([origin, ...v.resourceDomains]),
|
|
64
|
+
...(v.frameDomains.length ? { frameDomains: uniq(v.frameDomains) } : {}),
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** UTF-8 byte length of a string — the budget is bytes, and `.length` counts
|
|
70
|
+
* UTF-16 units (a CJK place name is 1 unit but 3 bytes). */
|
|
71
|
+
export function utf8Bytes(s) {
|
|
72
|
+
let n = 0;
|
|
73
|
+
for (let i = 0; i < s.length; i++) {
|
|
74
|
+
const c = s.charCodeAt(i);
|
|
75
|
+
if (c < 0x80)
|
|
76
|
+
n += 1;
|
|
77
|
+
else if (c < 0x800)
|
|
78
|
+
n += 2;
|
|
79
|
+
else if (c >= 0xd800 && c <= 0xdbff) {
|
|
80
|
+
n += 4;
|
|
81
|
+
i++;
|
|
82
|
+
} // surrogate pair
|
|
83
|
+
else
|
|
84
|
+
n += 3;
|
|
85
|
+
}
|
|
86
|
+
return n;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Split a plan's layers so their inline data fits `budgetBytes`, keeping
|
|
90
|
+
* order. A layer whose own JSON exceeds the budget → `oversize` (data
|
|
91
|
+
* stripped, id kept — it can never ride in a card result); layers that fit
|
|
92
|
+
* individually but not in the remaining budget → `deferred` (data stripped;
|
|
93
|
+
* the card fetches them one by one with `layer_ids`).
|
|
94
|
+
*/
|
|
95
|
+
export function splitLayersByBudget(layers, budgetBytes) {
|
|
96
|
+
const inline = [];
|
|
97
|
+
const deferred = [];
|
|
98
|
+
const oversize = [];
|
|
99
|
+
let used = 0;
|
|
100
|
+
for (const layer of layers) {
|
|
101
|
+
const size = utf8Bytes(JSON.stringify(layer));
|
|
102
|
+
const strip = () => ({ ...layer, source: layer.source ? { ...layer.source, data: undefined } : layer.source });
|
|
103
|
+
if (size > budgetBytes) {
|
|
104
|
+
oversize.push(layer.id);
|
|
105
|
+
inline.push(strip());
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (used + size > budgetBytes) {
|
|
109
|
+
deferred.push(layer.id);
|
|
110
|
+
inline.push(strip());
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
used += size;
|
|
114
|
+
inline.push(layer);
|
|
115
|
+
}
|
|
116
|
+
return { inline, deferred, oversize };
|
|
117
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@showmeonmap/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"mcpName": "com.showmeonmap/maps",
|
|
5
5
|
"description": "MCP server for ShowMeOnMap \u2014 geospatial visualization tools for any MCP-speaking agent (Claude Desktop, Cursor, Zed, ChatGPT Apps SDK)",
|
|
6
6
|
"type": "module",
|