mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 02:44:02 +00:00
refactor: name the view tool show_in_gods_eye_view
The tool and its panel now say "Show in God's Eye View" rather than "show on the globe", in tool names, descriptions, server instructions and docs.
This commit is contained in:
1 parent
216a75ac71
commit
47ef168815
12 files changed
+48
-44
No files matched your search
@@ -1,11 +1,11 @@
|
||||
# God's Eye View Current State
|
||||
|
||||
## The globe in conversations — October 2, 2026
|
||||
## God's Eye View in conversations — October 2, 2026
|
||||
|
||||
Tool answers that can be seen on the globe include a view: camera, layers,
|
||||
Tool answers that can be shown in God's Eye View include a view: camera, layers,
|
||||
style, map, marks and an aircraft or satellite to follow, written in the
|
||||
share-link format (`gods-eye-view/view`). `show_on_globe` shows a view as the
|
||||
live globe inside clients that display MCP Apps, and as a link everywhere.
|
||||
share-link format (`gods-eye-view/view`). `show_in_gods_eye_view` shows a view as live
|
||||
God's Eye View inside clients that display MCP Apps, and as a link everywhere.
|
||||
`?embed=1` shows the app as the globe alone and takes new views from the page
|
||||
that frames it. MCP leads with the tools that find what to show. See
|
||||
[tools and the MCP server](TOOLS.md).
|
||||
|
||||
+3
-3
@@ -104,9 +104,9 @@ tilted camera over an area looks at its center from behind; camera fields
|
||||
given with an area override its framing. `resolveViewArguments` turns them
|
||||
into a view.
|
||||
|
||||
### The globe panel
|
||||
### The God's Eye View panel
|
||||
|
||||
`show_on_globe` names an MCP Apps view (`io.modelcontextprotocol/ui`):
|
||||
`show_in_gods_eye_view` names an MCP Apps view (`io.modelcontextprotocol/ui`):
|
||||
`_meta.ui.resourceUri` is `ui://gods-eye-view/globe`, a `text/html;profile=mcp-app`
|
||||
resource from `createGlobePanelResource({ appUrl })` in
|
||||
`src/tools/globePanel.js`. Clients that display apps render it inside the
|
||||
@@ -250,4 +250,4 @@ public feeds the sources already use.
|
||||
| `situation_brief` | `weather` | Weather, earthquakes, fires, aircraft, ships and cyclones for an area, by section |
|
||||
| `military_awareness` | `military` | Military and other aircraft, ships and military installations within 250 km of a point, by section |
|
||||
| `get_hud_caption` | `weather`, `summary` | The app's heads-up display caption for an area |
|
||||
| `show_on_globe` | `app` | A view on the globe: the live globe panel in clients with MCP Apps, and a link everywhere; takes another answer's view or an area or camera, layers, style, map, marks and something to follow |
|
||||
| `show_in_gods_eye_view` | `app` | A view in God's Eye View: the live panel in clients with MCP Apps, and a link everywhere; takes another answer's view or an area or camera, layers, style, map, marks and something to follow |
|
||||
@@ -14,9 +14,9 @@ const INSTRUCTIONS =
|
||||
"Tools answer questions from God's Eye View's live public data. Location " +
|
||||
'tools take an area: a place name, a bbox, or lat/lon with radius_km. ' +
|
||||
'Results are capped; check truncated and total before concluding there is nothing more. ' +
|
||||
'Answers that can be seen on the globe include data.view; to show one, call ' +
|
||||
'show_on_globe with that view, adding layers, style, a camera or marks as ' +
|
||||
'needed. It shows the live globe where the client displays apps and ' +
|
||||
"Answers that can be shown in God's Eye View include data.view; to show one, call " +
|
||||
'show_in_gods_eye_view with that view, adding layers, style, a camera or marks as ' +
|
||||
"needed. It shows live God's Eye View where the client displays apps and " +
|
||||
'returns a link everywhere.';
|
||||
|
||||
/** Construct the local MCP server for Core's tools. */
|
||||
|
||||
@@ -220,7 +220,7 @@ test('Realtime sessions carry supplied tools, and the standalone voice adds the
|
||||
);
|
||||
assert.ok(names.includes('get_weather'));
|
||||
assert.ok(names.includes('military_awareness'));
|
||||
assert.ok(!names.includes('show_on_globe'));
|
||||
assert.ok(!names.includes('show_in_gods_eye_view'));
|
||||
assert.ok(!names.includes('aircraft_in_area'));
|
||||
assert.ok(!names.includes('get_weather_map'));
|
||||
// The action of the same name answers satellite passes.
|
||||
|
||||
@@ -4,10 +4,10 @@ import { coreTools, toFunctionOutput, toFunctionTools } from './index.js';
|
||||
|
||||
test('catalog tools become independent function records', () => {
|
||||
const records = toFunctionTools(coreTools, {
|
||||
exclude: ['show_on_globe'],
|
||||
exclude: ['show_in_gods_eye_view'],
|
||||
});
|
||||
assert.equal(records.length, coreTools.length - 1);
|
||||
assert.ok(!records.some((tool) => tool.name === 'show_on_globe'));
|
||||
assert.ok(!records.some((tool) => tool.name === 'show_in_gods_eye_view'));
|
||||
const weather = records.find((tool) => tool.name === 'get_weather');
|
||||
const source = coreTools.find((tool) => tool.name === 'get_weather');
|
||||
assert.deepEqual(Object.keys(weather), [
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* The globe panel: an MCP Apps view (`io.modelcontextprotocol/ui`) that shows
|
||||
* The God's Eye View panel: an MCP Apps view (`io.modelcontextprotocol/ui`) that shows
|
||||
* God's Eye View inside a conversation. The panel is a small page that loads
|
||||
* the app in embed mode and sends it each view the show_on_globe tool
|
||||
* the app in embed mode and sends it each view the show_in_gods_eye_view tool
|
||||
* returns; see docs/TOOLS.md.
|
||||
*/
|
||||
|
||||
@@ -33,7 +33,7 @@ function panelHtml(appOrigin) {
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="status">Waiting for a view of the globe…</div>
|
||||
<div id="status">Waiting for a view…</div>
|
||||
<button id="open" type="button" hidden>Open in God's Eye View</button>
|
||||
<script>
|
||||
(() => {
|
||||
@@ -82,7 +82,7 @@ function panelHtml(appOrigin) {
|
||||
frame.allow = 'fullscreen';
|
||||
frame.src = embedUrl(url);
|
||||
document.body.appendChild(frame);
|
||||
status.textContent = 'Loading the globe…';
|
||||
status.textContent = "Loading God's Eye View…";
|
||||
return;
|
||||
}
|
||||
if (frameReady) postView(view);
|
||||
@@ -148,7 +148,7 @@ export function createGlobePanelResource({ appUrl }) {
|
||||
uri: GLOBE_PANEL_URI,
|
||||
name: 'globe',
|
||||
title: "God's Eye View globe",
|
||||
description: 'The live globe, showing the view a tool returns.',
|
||||
description: "Live God's Eye View, showing the view a tool returns.",
|
||||
mimeType: MCP_APP_MIME_TYPE,
|
||||
text: panelHtml(appOrigin),
|
||||
_meta: {
|
||||
|
||||
@@ -23,12 +23,12 @@ test('the globe panel is an MCP Apps resource that may frame only the app', () =
|
||||
assert.match(resource.text, /protocolVersion: "2026-01-26"/);
|
||||
});
|
||||
|
||||
test('show_on_globe names the panel and shows a view another answer returned', async () => {
|
||||
test('show_in_gods_eye_view names the panel and shows a view another answer returned', async () => {
|
||||
const catalog = composeCatalog({
|
||||
tools: coreTools,
|
||||
services: { app: { baseUrl: 'http://localhost:5173/' } },
|
||||
});
|
||||
assert.deepEqual(catalog.get('show_on_globe').ui, {
|
||||
assert.deepEqual(catalog.get('show_in_gods_eye_view').ui, {
|
||||
resourceUri: GLOBE_PANEL_URI,
|
||||
});
|
||||
const earlier = {
|
||||
@@ -46,7 +46,7 @@ test('show_on_globe names the panel and shows a view another answer returned', a
|
||||
annotations: [],
|
||||
url: 'http://localhost:5173/#v=2',
|
||||
};
|
||||
const shown = await catalog.call('show_on_globe', {
|
||||
const shown = await catalog.call('show_in_gods_eye_view', {
|
||||
view: earlier,
|
||||
layers: ['ais-live-vessels', 'military'],
|
||||
style: 'thermal',
|
||||
@@ -55,7 +55,7 @@ test('show_on_globe names the panel and shows a view another answer returned', a
|
||||
assert.deepEqual(shown.data.view.layers, ['ais-live-vessels', 'military']);
|
||||
assert.equal(shown.data.view.style, 'thermal');
|
||||
await assert.rejects(
|
||||
catalog.call('show_on_globe', { view: { camera: { lat: 'x' } } }),
|
||||
catalog.call('show_in_gods_eye_view', { view: { camera: { lat: 'x' } } }),
|
||||
(error) => error.code === 'invalid_arguments',
|
||||
);
|
||||
});
|
||||
+2
-2
@@ -40,7 +40,7 @@ import {
|
||||
getTransitVehicles,
|
||||
} from './queries/mobility.js';
|
||||
import { placesNearby, planRoute, searchPlaces } from './queries/places.js';
|
||||
import { showOnGlobe } from './queries/share.js';
|
||||
import { showInGodsEyeView } from './queries/share.js';
|
||||
import { findAlprCameras } from './queries/surveillance.js';
|
||||
import {
|
||||
findCctvCameras,
|
||||
@@ -121,5 +121,5 @@ export const coreTools = Object.freeze([
|
||||
situationBrief,
|
||||
militaryAwareness,
|
||||
getHudCaption,
|
||||
showOnGlobe,
|
||||
showInGodsEyeView,
|
||||
]);
|
||||
@@ -77,16 +77,16 @@ function appBase(services) {
|
||||
}
|
||||
}
|
||||
|
||||
export const showOnGlobe = defineTool({
|
||||
name: 'show_on_globe',
|
||||
title: 'Show on the globe',
|
||||
export const showInGodsEyeView = defineTool({
|
||||
name: 'show_in_gods_eye_view',
|
||||
title: "Show in God's Eye View",
|
||||
description:
|
||||
"Shows a view of God's Eye View's globe: in clients that display apps, " +
|
||||
'the live globe in the conversation; everywhere, a link that opens it. ' +
|
||||
'Pass the view another answer returned, or describe one: an area framed from above ' +
|
||||
'or a camera position, with chosen data layers, visual style and map, ' +
|
||||
'optionally following an aircraft or satellite and with marks drawn on ' +
|
||||
'the globe.',
|
||||
"Shows a view in God's Eye View: in clients that display apps, the live " +
|
||||
"God's Eye View globe in the conversation; everywhere, a link that opens " +
|
||||
'it. Pass the view another answer returned, or describe one: an area ' +
|
||||
'framed from above or a camera position, with chosen data layers, visual ' +
|
||||
'style and map, optionally following an aircraft or satellite and with ' +
|
||||
'marks drawn on the map.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: VIEW_ARGUMENTS,
|
||||
|
||||
@@ -7,7 +7,7 @@ test('links open the app looking straight down on the area', async () => {
|
||||
tools: coreTools,
|
||||
services: { app: { baseUrl: 'https://maps.example/view?x=1' } },
|
||||
});
|
||||
const result = await catalog.call('show_on_globe', {
|
||||
const result = await catalog.call('show_in_gods_eye_view', {
|
||||
area: { lat: 30.2672, lon: -97.7431, radius_km: 5 },
|
||||
});
|
||||
const url = new URL(result.data.url);
|
||||
@@ -35,11 +35,11 @@ test('altitude is bounded for tiny and planet-sized areas', async () => {
|
||||
tools: coreTools,
|
||||
services: { app: { baseUrl: 'http://localhost:4173/' } },
|
||||
});
|
||||
const tiny = await catalog.call('show_on_globe', {
|
||||
const tiny = await catalog.call('show_in_gods_eye_view', {
|
||||
area: { lat: 0, lon: 0, radius_km: 0.1 },
|
||||
});
|
||||
assert.equal(tiny.data.view.camera.altitude_m, 500);
|
||||
const world = await catalog.call('show_on_globe', {
|
||||
const world = await catalog.call('show_in_gods_eye_view', {
|
||||
area: { bbox: [-180, -85, 180, 85] },
|
||||
});
|
||||
assert.equal(world.data.view.camera.altitude_m, 15000000);
|
||||
@@ -48,7 +48,7 @@ test('altitude is bounded for tiny and planet-sized areas', async () => {
|
||||
services: { app: { baseUrl: 'not a url' } },
|
||||
});
|
||||
await assert.rejects(
|
||||
broken.call('show_on_globe', {
|
||||
broken.call('show_in_gods_eye_view', {
|
||||
area: { lat: 0, lon: 0, radius_km: 1 },
|
||||
}),
|
||||
(error) => error.code === 'unavailable',
|
||||
@@ -61,7 +61,7 @@ test('links turn on the requested layers with the app share-link codec', async (
|
||||
tools: coreTools,
|
||||
services: { app: { baseUrl: 'http://localhost:4173/' } },
|
||||
});
|
||||
const result = await catalog.call('show_on_globe', {
|
||||
const result = await catalog.call('show_in_gods_eye_view', {
|
||||
area: { lat: 30.27, lon: -97.74, radius_km: 10 },
|
||||
layers: ['earthquakes', 'flights', 'flights'],
|
||||
});
|
||||
@@ -72,7 +72,7 @@ test('links turn on the requested layers with the app share-link codec', async (
|
||||
'flights',
|
||||
]);
|
||||
await assert.rejects(
|
||||
catalog.call('show_on_globe', {
|
||||
catalog.call('show_in_gods_eye_view', {
|
||||
area: { lat: 0, lon: 0, radius_km: 1 },
|
||||
layers: ['not-a-layer'],
|
||||
}),
|
||||
@@ -86,7 +86,7 @@ test('links carry a camera, style, map and an entity to follow', async () => {
|
||||
tools: coreTools,
|
||||
services: { app: { baseUrl: 'http://localhost:4173/' } },
|
||||
});
|
||||
const result = await catalog.call('show_on_globe', {
|
||||
const result = await catalog.call('show_in_gods_eye_view', {
|
||||
area: { lat: 25, lon: 121, radius_km: 200 },
|
||||
camera: { pitch_deg: -40, heading_deg: 350 },
|
||||
layers: ['ais-live-vessels'],
|
||||
@@ -103,13 +103,13 @@ test('links carry a camera, style, map and an entity to follow', async () => {
|
||||
id: 'ae1234',
|
||||
});
|
||||
assert.equal(result.data.view.camera.pitch_deg, -40);
|
||||
const camera = await catalog.call('show_on_globe', {
|
||||
const camera = await catalog.call('show_in_gods_eye_view', {
|
||||
camera: { lat: 48.8584, lon: 2.2945, altitude_m: 1200, pitch_deg: -30 },
|
||||
});
|
||||
assert.equal(camera.data.view.camera.altitude_m, 1200);
|
||||
assert.match(camera.summary, /^Open 48\.858, 2\.295 in God's Eye View: /);
|
||||
await assert.rejects(
|
||||
catalog.call('show_on_globe', { layers: ['flights'] }),
|
||||
catalog.call('show_in_gods_eye_view', { layers: ['flights'] }),
|
||||
/Give a view, an area, or a camera with lat and lon/,
|
||||
);
|
||||
});
|
||||
@@ -15,7 +15,7 @@ const mcpOff = Object.freeze({ mcp: false });
|
||||
|
||||
export const TOOL_SURFACES = Object.freeze({
|
||||
// Voice runs inside the app, so a link to it adds nothing.
|
||||
show_on_globe: voiceOff,
|
||||
show_in_gods_eye_view: voiceOff,
|
||||
// Voice has an app action of the same name.
|
||||
next_satellite_pass: voiceOff,
|
||||
// Voice receives text only, so tools that answer with an image are off.
|
||||
|
||||
@@ -20,7 +20,11 @@ test('each surface offers the tools the table does not turn off', () => {
|
||||
assert.deepEqual(offered, expected, surface);
|
||||
}
|
||||
const mcp = names(toolsForSurface(coreTools, 'mcp'));
|
||||
for (const name of ['show_on_globe', 'situation_brief', 'aircraft_in_area'])
|
||||
for (const name of [
|
||||
'show_in_gods_eye_view',
|
||||
'situation_brief',
|
||||
'aircraft_in_area',
|
||||
])
|
||||
assert.ok(mcp.includes(name), name);
|
||||
for (const name of [
|
||||
'search_places',
|
||||
|
||||
Reference in new issue
Block a user