feat: lead the MCP tool list with what the globe shows

MCP no longer lists place search, routing, plain weather and wind, the
regional brief, the HUD caption, radio, bike share and transit. Assistants
already cover these or they add little without the globe; voice keeps them,
and tools that combine others still reach them.
This commit is contained in:
Sameh Khamis committed 2026-10-02 15:21:36 -07:00
1 parent d6473b6049
commit af8856cedf
3 files changed
+43 -13

No files matched your search

+2 -2
View File
@@ -4,7 +4,7 @@ import { PassThrough } from 'node:stream';
import { createLocalMcpServer } from '../../server/mcp/server.js';
import { createApiFetch } from '../../server/mcp/services.js';
import { parseArgs, serveStdio } from '../../server/mcp/stdio.js';
import { coreTools } from './index.js';
import { coreTools, toolsForSurface } from './index.js';
const usgs = {
type: 'FeatureCollection',
@@ -70,7 +70,7 @@ test('the stdio server answers newline-delimited requests using only its data so
// Every Core tool's services are composed locally.
assert.deepEqual(
byId.get(2).result.tools.map((tool) => tool.name),
coreTools.map((tool) => tool.name),
toolsForSurface(coreTools, 'mcp').map((tool) => tool.name),
);
assert.equal(
byId.get(3).result.content[0].text,
+16 -2
View File
@@ -2,6 +2,8 @@
* Which surfaces offer each tool. A tool is on every surface unless this
* table turns it off; edit an entry to turn a tool on or off for MCP, voice,
* or both. Applications can pass overrides when they compose a surface.
* Tools off a surface stay reachable from tools that combine others, such as
* situation_brief using get_weather.
*/
import { ToolError } from './catalog.js';
@@ -9,6 +11,7 @@ import { ToolError } from './catalog.js';
export const SURFACES = Object.freeze(['mcp', 'voice']);
const voiceOff = Object.freeze({ voice: false });
const mcpOff = Object.freeze({ mcp: false });
export const TOOL_SURFACES = Object.freeze({
// Voice runs inside the app, so a link to it adds nothing.
@@ -19,8 +22,8 @@ export const TOOL_SURFACES = Object.freeze({
get_weather_map: voiceOff,
get_recent_imagery: voiceOff,
get_cctv_snapshot: voiceOff,
// Voice shows the HUD caption in the app itself.
get_hud_caption: voiceOff,
// The app shows its HUD caption itself, and assistants write their own.
get_hud_caption: Object.freeze({ voice: false, mcp: false }),
// Voice answers these from the loaded layers with analyst_query.
aircraft_in_area: voiceOff,
vessels_in_area: voiceOff,
@@ -30,6 +33,17 @@ export const TOOL_SURFACES = Object.freeze({
get_active_fires: voiceOff,
find_infrastructure: voiceOff,
satellites_overhead: voiceOff,
// MCP leads with what the globe shows; assistants already search places,
// plan routes and report weather, and these layers add little in a chat.
search_places: mcpOff,
places_nearby: mcpOff,
plan_route: mcpOff,
get_weather: mcpOff,
get_wind: mcpOff,
get_regional_brief: mcpOff,
find_radio_stations: mcpOff,
get_bike_share: mcpOff,
get_transit_vehicles: mcpOff,
});
/**
+25 -9
View File
@@ -11,17 +11,32 @@ import {
const names = (tools) => tools.map((tool) => tool.name);
test('MCP offers every tool and voice leaves out the table entries', () => {
assert.deepEqual(names(toolsForSurface(coreTools, 'mcp')), names(coreTools));
test('each surface offers the tools the table does not turn off', () => {
for (const surface of ['mcp', 'voice']) {
const offered = names(toolsForSurface(coreTools, surface));
const expected = names(coreTools).filter(
(name) => TOOL_SURFACES[name]?.[surface] !== false,
);
assert.deepEqual(offered, expected, surface);
}
const mcp = names(toolsForSurface(coreTools, 'mcp'));
for (const name of [
'open_in_gods_eye_view',
'situation_brief',
'aircraft_in_area',
])
assert.ok(mcp.includes(name), name);
for (const name of [
'search_places',
'plan_route',
'get_weather',
'get_hud_caption',
])
assert.ok(!mcp.includes(name), name);
const voice = names(toolsForSurface(coreTools, 'voice'));
assert.equal(
voice.length,
coreTools.length - Object.keys(TOOL_SURFACES).length,
);
for (const name of Object.keys(TOOL_SURFACES))
assert.ok(!voice.includes(name), name);
assert.ok(voice.includes('get_weather'));
assert.ok(voice.includes('military_awareness'));
assert.ok(!voice.includes('aircraft_in_area'));
});
test('overrides turn a tool on or off for one surface', () => {
@@ -44,7 +59,8 @@ test('every table entry names a core tool and a known surface', () => {
const names = new Set(coreTools.map((tool) => tool.name));
for (const [name, entry] of Object.entries(TOOL_SURFACES)) {
assert.ok(names.has(name), name);
assert.deepEqual(Object.keys(entry), ['voice']);
for (const key of Object.keys(entry))
assert.ok(['voice', 'mcp'].includes(key), `${name}: ${key}`);
}
});