Report unsupported military track history in get_aircraft_track

The standalone military source no longer has getTrack, so the MCP
get_aircraft_track tool threw a TypeError and MCP advised a retry. It
now returns an `unsupported` ToolError when the selected feed has no
getTrack, while custom sources that supply history still work. Tests
cover the default composition through the catalog and MCP.

Also drop the unused epoch import and update the CURRENT-STATE track
sections to describe local trails and 503 history_unavailable.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Opus 5.5 committed 2026-10-23 12:26:02 +01:00
1 parent 98af799cb4
commit 730ba57073
4 files changed
+73 -10

No files matched your search

+13 -6
View File
@@ -333,13 +333,20 @@ export const getAircraftTrack = defineTool({
requires: ['aircraft'],
async run(args, { services, signal }) {
const icao24 = args.icao24.toLowerCase();
const source = feedFor(services, args.military);
// History is optional: the standalone military source has none.
if (typeof source.getTrack !== 'function')
throw new ToolError(
'unsupported',
args.military
? 'Track history is not available for the military aircraft feed'
: 'Track history is not available for the aircraft feed',
);
// A track server answers 404 for an aircraft it has no positions for.
const track = await feedFor(services, args.military)
.getTrack(icao24, { signal })
.catch((error) => {
if (error?.status === 404) return { records: [] };
throw error;
});
const track = await source.getTrack(icao24, { signal }).catch((error) => {
if (error?.status === 404) return { records: [] };
throw error;
});
const points = [...track.records].sort(
(a, b) => a.observedAtMs - b.observedAtMs,
);
+57
View File
@@ -3,6 +3,8 @@ import test from 'node:test';
import { composeCatalog, coreTools } from '../index.js';
import { airlineDesignator } from './aviation.js';
import { LiveSourceError } from '../../sources/live/contract.js';
import { createMcpServer } from '../mcp/index.js';
import { createToolServices } from '../services.js';
const record = (id, latitude, longitude, extra = {}) => ({
id,
@@ -242,6 +244,61 @@ test('tracks are ordered and thinned to 200 points', async () => {
);
});
test('military track history is unsupported by the default composition and kept for custom sources', async () => {
const requests = [];
const services = createToolServices({
fetchImpl: async (url) => {
requests.push(String(url));
throw new Error('no request expected');
},
appUrl: 'http://localhost:5173',
});
const catalog = composeCatalog({ tools: coreTools, services });
await assert.rejects(
catalog.call('get_aircraft_track', { icao24: 'AE0001', military: true }),
(error) =>
error.name === 'ToolError' &&
error.code === 'unsupported' &&
/military/.test(error.message),
);
const server = createMcpServer({ catalog, name: 'test', version: '1.0.0' });
const { result } = await server.handle({
jsonrpc: '2.0',
id: 1,
method: 'tools/call',
params: {
name: 'get_aircraft_track',
arguments: { icao24: 'ae0001', military: true },
},
});
assert.equal(result.isError, true);
assert.equal(result.structuredContent.error, 'unsupported');
assert.doesNotMatch(result.content[0].text, /try again|later/i);
assert.deepEqual(requests, []);
const track = [
{
latitude: 37.6,
longitude: -122.3,
observedAtMs: Date.UTC(2026, 0, 1),
baroAltitudeM: 3000,
onGround: false,
},
];
const custom = feed(military, { track });
const withHistory = composeCatalog({
tools: coreTools,
services: { aircraft: feed(civil), military: custom },
});
const kept = await withHistory.call('get_aircraft_track', {
icao24: 'AE0001',
military: true,
});
assert.equal(custom.calls[0], 'ae0001');
assert.equal(kept.data.total, 1);
assert.equal(kept.data.view.follow.kind, 'military_aircraft');
});
test('aircraft info combines type and route lookups', async () => {
const aircraft = feed(civil, {
enrichment: {