@striderlabs/mcp-gasbuddy 1.0.0

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.
@@ -0,0 +1,31 @@
1
+ const esbuild = require('esbuild');
2
+
3
+ const watch = process.argv.includes('--watch');
4
+
5
+ const buildOptions = {
6
+ entryPoints: ['src/index.ts'],
7
+ bundle: true,
8
+ platform: 'node',
9
+ target: 'node18',
10
+ outfile: 'dist/index.js',
11
+ format: 'cjs',
12
+ sourcemap: true,
13
+ external: [],
14
+ banner: {
15
+ js: '#!/usr/bin/env node',
16
+ },
17
+ define: {
18
+ 'process.env.NODE_ENV': '"production"',
19
+ },
20
+ };
21
+
22
+ if (watch) {
23
+ esbuild.context(buildOptions).then(ctx => {
24
+ ctx.watch();
25
+ console.log('Watching for changes...');
26
+ });
27
+ } else {
28
+ esbuild.build(buildOptions).then(() => {
29
+ console.log('Build complete: dist/index.js');
30
+ }).catch(() => process.exit(1));
31
+ }
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@striderlabs/mcp-gasbuddy",
3
+ "version": "1.0.0",
4
+ "description": "MCP connector for GasBuddy fuel prices and station finder",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "mcp-gasbuddy": "dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "node esbuild.config.js",
11
+ "dev": "node esbuild.config.js --watch",
12
+ "start": "node dist/index.js"
13
+ },
14
+ "keywords": ["mcp", "gasbuddy", "fuel", "gas", "prices", "stations"],
15
+ "author": "Strider Labs",
16
+ "license": "MIT",
17
+ "dependencies": {
18
+ "@modelcontextprotocol/sdk": "^1.0.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^20.0.0",
22
+ "esbuild": "^0.20.0",
23
+ "typescript": "^5.0.0"
24
+ }
25
+ }
package/src/index.ts ADDED
@@ -0,0 +1,636 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import {
6
+ CallToolRequestSchema,
7
+ ListToolsRequestSchema,
8
+ Tool,
9
+ } from "@modelcontextprotocol/sdk/types.js";
10
+
11
+ // ---- Types ----
12
+
13
+ interface GasBuddyConfig {
14
+ apiKey: string;
15
+ baseUrl: string;
16
+ }
17
+
18
+ interface Station {
19
+ id: string;
20
+ name: string;
21
+ brand: string;
22
+ address: string;
23
+ city: string;
24
+ state: string;
25
+ zip: string;
26
+ latitude: number;
27
+ longitude: number;
28
+ phone?: string;
29
+ distance?: number;
30
+ amenities: string[];
31
+ prices: FuelPrice[];
32
+ rating?: number;
33
+ reviewCount?: number;
34
+ }
35
+
36
+ interface FuelPrice {
37
+ grade: string;
38
+ price: number;
39
+ currency: string;
40
+ reportedAt: string;
41
+ reportedBy?: string;
42
+ confidence: number;
43
+ }
44
+
45
+ interface PriceReport {
46
+ stationId: string;
47
+ grade: string;
48
+ price: number;
49
+ latitude?: number;
50
+ longitude?: number;
51
+ }
52
+
53
+ interface PriceTrend {
54
+ date: string;
55
+ averagePrice: number;
56
+ minPrice: number;
57
+ maxPrice: number;
58
+ sampleCount: number;
59
+ }
60
+
61
+ interface Deal {
62
+ id: string;
63
+ stationId: string;
64
+ stationName: string;
65
+ type: string;
66
+ description: string;
67
+ discount: number;
68
+ discountType: "cents_per_gallon" | "percent" | "fixed";
69
+ validUntil?: string;
70
+ requirements?: string;
71
+ }
72
+
73
+ interface PayWithGasBuddy {
74
+ enrolled: boolean;
75
+ savingsPerGallon: number;
76
+ totalSaved: number;
77
+ cardNumber?: string;
78
+ cardStatus?: string;
79
+ linkedPayment?: string;
80
+ participatingStations: number;
81
+ }
82
+
83
+ interface TripCostEstimate {
84
+ distance: number;
85
+ distanceUnit: string;
86
+ fuelEfficiency: number;
87
+ fuelEfficiencyUnit: string;
88
+ gallonsNeeded: number;
89
+ estimatedCost: number;
90
+ cheapestCost: number;
91
+ averageCost: number;
92
+ currency: string;
93
+ cheapestStation?: Station;
94
+ routeStations?: Station[];
95
+ }
96
+
97
+ // ---- API Client ----
98
+
99
+ class GasBuddyClient {
100
+ private config: GasBuddyConfig;
101
+
102
+ constructor(config: GasBuddyConfig) {
103
+ this.config = config;
104
+ }
105
+
106
+ private async request<T>(
107
+ endpoint: string,
108
+ method: string = "GET",
109
+ body?: unknown
110
+ ): Promise<T> {
111
+ const url = `${this.config.baseUrl}${endpoint}`;
112
+ const headers: Record<string, string> = {
113
+ "Content-Type": "application/json",
114
+ "X-API-Key": this.config.apiKey,
115
+ "User-Agent": "mcp-gasbuddy/1.0.0",
116
+ };
117
+
118
+ const options: RequestInit = {
119
+ method,
120
+ headers,
121
+ };
122
+
123
+ if (body) {
124
+ options.body = JSON.stringify(body);
125
+ }
126
+
127
+ const response = await fetch(url, options);
128
+
129
+ if (!response.ok) {
130
+ const errorText = await response.text();
131
+ throw new Error(
132
+ `GasBuddy API error ${response.status}: ${errorText}`
133
+ );
134
+ }
135
+
136
+ return response.json() as Promise<T>;
137
+ }
138
+
139
+ async searchStations(params: {
140
+ latitude?: number;
141
+ longitude?: number;
142
+ address?: string;
143
+ radius?: number;
144
+ fuel?: string;
145
+ limit?: number;
146
+ minRating?: number;
147
+ amenities?: string[];
148
+ }): Promise<{ stations: Station[]; total: number }> {
149
+ const query = new URLSearchParams();
150
+ if (params.latitude !== undefined) query.set("lat", params.latitude.toString());
151
+ if (params.longitude !== undefined) query.set("lng", params.longitude.toString());
152
+ if (params.address) query.set("address", params.address);
153
+ if (params.radius !== undefined) query.set("radius", params.radius.toString());
154
+ if (params.fuel) query.set("fuel", params.fuel);
155
+ if (params.limit !== undefined) query.set("limit", params.limit.toString());
156
+ if (params.minRating !== undefined) query.set("minRating", params.minRating.toString());
157
+ if (params.amenities?.length) query.set("amenities", params.amenities.join(","));
158
+
159
+ return this.request<{ stations: Station[]; total: number }>(
160
+ `/v1/stations/search?${query}`
161
+ );
162
+ }
163
+
164
+ async getStationDetails(stationId: string): Promise<Station> {
165
+ return this.request<Station>(`/v1/stations/${stationId}`);
166
+ }
167
+
168
+ async getPrices(params: {
169
+ stationId?: string;
170
+ latitude?: number;
171
+ longitude?: number;
172
+ radius?: number;
173
+ grade?: string;
174
+ }): Promise<{ prices: FuelPrice[]; stationId?: string }> {
175
+ const query = new URLSearchParams();
176
+ if (params.stationId) query.set("stationId", params.stationId);
177
+ if (params.latitude !== undefined) query.set("lat", params.latitude.toString());
178
+ if (params.longitude !== undefined) query.set("lng", params.longitude.toString());
179
+ if (params.radius !== undefined) query.set("radius", params.radius.toString());
180
+ if (params.grade) query.set("grade", params.grade);
181
+
182
+ return this.request<{ prices: FuelPrice[]; stationId?: string }>(
183
+ `/v1/prices?${query}`
184
+ );
185
+ }
186
+
187
+ async reportPrice(report: PriceReport): Promise<{ success: boolean; message: string; reportId: string }> {
188
+ return this.request<{ success: boolean; message: string; reportId: string }>(
189
+ "/v1/prices/report",
190
+ "POST",
191
+ report
192
+ );
193
+ }
194
+
195
+ async getPriceTrends(params: {
196
+ latitude?: number;
197
+ longitude?: number;
198
+ stationId?: string;
199
+ grade?: string;
200
+ days?: number;
201
+ region?: string;
202
+ }): Promise<{ trends: PriceTrend[]; grade: string; region: string }> {
203
+ const query = new URLSearchParams();
204
+ if (params.latitude !== undefined) query.set("lat", params.latitude.toString());
205
+ if (params.longitude !== undefined) query.set("lng", params.longitude.toString());
206
+ if (params.stationId) query.set("stationId", params.stationId);
207
+ if (params.grade) query.set("grade", params.grade);
208
+ if (params.days !== undefined) query.set("days", params.days.toString());
209
+ if (params.region) query.set("region", params.region);
210
+
211
+ return this.request<{ trends: PriceTrend[]; grade: string; region: string }>(
212
+ `/v1/prices/trends?${query}`
213
+ );
214
+ }
215
+
216
+ async getDeals(params: {
217
+ latitude?: number;
218
+ longitude?: number;
219
+ radius?: number;
220
+ type?: string;
221
+ }): Promise<{ deals: Deal[] }> {
222
+ const query = new URLSearchParams();
223
+ if (params.latitude !== undefined) query.set("lat", params.latitude.toString());
224
+ if (params.longitude !== undefined) query.set("lng", params.longitude.toString());
225
+ if (params.radius !== undefined) query.set("radius", params.radius.toString());
226
+ if (params.type) query.set("type", params.type);
227
+
228
+ return this.request<{ deals: Deal[] }>(`/v1/deals?${query}`);
229
+ }
230
+
231
+ async getPayWithGasBuddy(userId: string): Promise<PayWithGasBuddy> {
232
+ return this.request<PayWithGasBuddy>(`/v1/pay-with-gasbuddy/${userId}`);
233
+ }
234
+
235
+ async findCheapest(params: {
236
+ latitude: number;
237
+ longitude: number;
238
+ radius?: number;
239
+ grade?: string;
240
+ limit?: number;
241
+ }): Promise<{ stations: Station[] }> {
242
+ const query = new URLSearchParams({
243
+ lat: params.latitude.toString(),
244
+ lng: params.longitude.toString(),
245
+ sort: "price_asc",
246
+ });
247
+ if (params.radius !== undefined) query.set("radius", params.radius.toString());
248
+ if (params.grade) query.set("grade", params.grade);
249
+ if (params.limit !== undefined) query.set("limit", params.limit.toString());
250
+
251
+ return this.request<{ stations: Station[] }>(
252
+ `/v1/stations/search?${query}`
253
+ );
254
+ }
255
+
256
+ async getFavorites(userId: string): Promise<{ stations: Station[] }> {
257
+ return this.request<{ stations: Station[] }>(
258
+ `/v1/users/${userId}/favorites`
259
+ );
260
+ }
261
+
262
+ async getTripCost(params: {
263
+ originLatitude: number;
264
+ originLongitude: number;
265
+ destLatitude: number;
266
+ destLongitude: number;
267
+ fuelEfficiency: number;
268
+ fuelEfficiencyUnit?: string;
269
+ grade?: string;
270
+ tankSize?: number;
271
+ }): Promise<TripCostEstimate> {
272
+ return this.request<TripCostEstimate>("/v1/trip/cost", "POST", params);
273
+ }
274
+ }
275
+
276
+ // ---- Tool Definitions ----
277
+
278
+ const TOOLS: Tool[] = [
279
+ {
280
+ name: "search_stations",
281
+ description:
282
+ "Find gas stations near a location. Supports searching by coordinates or address with optional filters for fuel type, amenities, and rating.",
283
+ inputSchema: {
284
+ type: "object",
285
+ properties: {
286
+ latitude: { type: "number", description: "Latitude for location-based search" },
287
+ longitude: { type: "number", description: "Longitude for location-based search" },
288
+ address: { type: "string", description: "Address or city/state for text-based search" },
289
+ radius: { type: "number", description: "Search radius in miles (default: 5)" },
290
+ fuel: {
291
+ type: "string",
292
+ enum: ["regular", "midgrade", "premium", "diesel", "e85", "uls"],
293
+ description: "Filter by fuel type",
294
+ },
295
+ limit: { type: "number", description: "Max results to return (default: 20)" },
296
+ minRating: { type: "number", description: "Minimum station rating (1-5)" },
297
+ amenities: {
298
+ type: "array",
299
+ items: { type: "string" },
300
+ description: "Required amenities (e.g. car_wash, restrooms, atm, air_pump)",
301
+ },
302
+ },
303
+ },
304
+ },
305
+ {
306
+ name: "get_station_details",
307
+ description:
308
+ "Get detailed information about a specific gas station including amenities, current prices, ratings, and contact info.",
309
+ inputSchema: {
310
+ type: "object",
311
+ required: ["station_id"],
312
+ properties: {
313
+ station_id: { type: "string", description: "The station's unique identifier" },
314
+ },
315
+ },
316
+ },
317
+ {
318
+ name: "get_prices",
319
+ description:
320
+ "Get current fuel prices for a station or area. Returns prices by grade with reporting confidence and timestamps.",
321
+ inputSchema: {
322
+ type: "object",
323
+ properties: {
324
+ station_id: { type: "string", description: "Station ID for station-specific prices" },
325
+ latitude: { type: "number", description: "Latitude for area prices" },
326
+ longitude: { type: "number", description: "Longitude for area prices" },
327
+ radius: { type: "number", description: "Radius in miles for area prices" },
328
+ grade: {
329
+ type: "string",
330
+ enum: ["regular", "midgrade", "premium", "diesel", "e85"],
331
+ description: "Filter by fuel grade",
332
+ },
333
+ },
334
+ },
335
+ },
336
+ {
337
+ name: "report_price",
338
+ description:
339
+ "Report a current fuel price at a gas station. Helps keep the community database accurate.",
340
+ inputSchema: {
341
+ type: "object",
342
+ required: ["station_id", "grade", "price"],
343
+ properties: {
344
+ station_id: { type: "string", description: "Station ID where price was observed" },
345
+ grade: {
346
+ type: "string",
347
+ enum: ["regular", "midgrade", "premium", "diesel", "e85"],
348
+ description: "Fuel grade",
349
+ },
350
+ price: { type: "number", description: "Price per gallon in USD" },
351
+ latitude: { type: "number", description: "Reporter latitude for verification" },
352
+ longitude: { type: "number", description: "Reporter longitude for verification" },
353
+ },
354
+ },
355
+ },
356
+ {
357
+ name: "get_price_trends",
358
+ description:
359
+ "Get historical fuel price trends for an area or station. Useful for analyzing price patterns over time.",
360
+ inputSchema: {
361
+ type: "object",
362
+ properties: {
363
+ station_id: { type: "string", description: "Station ID for station-specific trends" },
364
+ latitude: { type: "number", description: "Latitude for regional trends" },
365
+ longitude: { type: "number", description: "Longitude for regional trends" },
366
+ grade: {
367
+ type: "string",
368
+ enum: ["regular", "midgrade", "premium", "diesel", "e85"],
369
+ description: "Fuel grade (default: regular)",
370
+ },
371
+ days: { type: "number", description: "Number of days of history (default: 30, max: 365)" },
372
+ region: { type: "string", description: "Region code (e.g. CA, TX, US) for national/state trends" },
373
+ },
374
+ },
375
+ },
376
+ {
377
+ name: "get_deals",
378
+ description:
379
+ "Find current fuel deals and discounts near a location. Includes loyalty discounts, app offers, and credit card deals.",
380
+ inputSchema: {
381
+ type: "object",
382
+ properties: {
383
+ latitude: { type: "number", description: "Latitude" },
384
+ longitude: { type: "number", description: "Longitude" },
385
+ radius: { type: "number", description: "Search radius in miles (default: 10)" },
386
+ type: {
387
+ type: "string",
388
+ enum: ["loyalty", "credit_card", "app_offer", "cashback", "coupon"],
389
+ description: "Filter by deal type",
390
+ },
391
+ },
392
+ },
393
+ },
394
+ {
395
+ name: "get_pay_with_gasbuddy",
396
+ description:
397
+ "Get information about the Pay With GasBuddy savings card, including enrollment status, savings per gallon, and total savings.",
398
+ inputSchema: {
399
+ type: "object",
400
+ required: ["user_id"],
401
+ properties: {
402
+ user_id: { type: "string", description: "GasBuddy user ID" },
403
+ },
404
+ },
405
+ },
406
+ {
407
+ name: "find_cheapest",
408
+ description:
409
+ "Find the cheapest gas stations within a radius, sorted by price. Great for finding the best deal nearby.",
410
+ inputSchema: {
411
+ type: "object",
412
+ required: ["latitude", "longitude"],
413
+ properties: {
414
+ latitude: { type: "number", description: "Center point latitude" },
415
+ longitude: { type: "number", description: "Center point longitude" },
416
+ radius: { type: "number", description: "Search radius in miles (default: 10)" },
417
+ grade: {
418
+ type: "string",
419
+ enum: ["regular", "midgrade", "premium", "diesel", "e85"],
420
+ description: "Fuel grade to compare (default: regular)",
421
+ },
422
+ limit: { type: "number", description: "Number of results (default: 10)" },
423
+ },
424
+ },
425
+ },
426
+ {
427
+ name: "get_favorites",
428
+ description:
429
+ "Get a user's saved favorite gas stations with current prices.",
430
+ inputSchema: {
431
+ type: "object",
432
+ required: ["user_id"],
433
+ properties: {
434
+ user_id: { type: "string", description: "GasBuddy user ID" },
435
+ },
436
+ },
437
+ },
438
+ {
439
+ name: "get_trip_cost",
440
+ description:
441
+ "Calculate estimated fuel cost for a road trip based on route distance, vehicle fuel efficiency, and current gas prices along the route.",
442
+ inputSchema: {
443
+ type: "object",
444
+ required: [
445
+ "origin_latitude",
446
+ "origin_longitude",
447
+ "dest_latitude",
448
+ "dest_longitude",
449
+ "fuel_efficiency",
450
+ ],
451
+ properties: {
452
+ origin_latitude: { type: "number", description: "Trip start latitude" },
453
+ origin_longitude: { type: "number", description: "Trip start longitude" },
454
+ dest_latitude: { type: "number", description: "Trip destination latitude" },
455
+ dest_longitude: { type: "number", description: "Trip destination longitude" },
456
+ fuel_efficiency: { type: "number", description: "Vehicle fuel efficiency (MPG or L/100km)" },
457
+ fuel_efficiency_unit: {
458
+ type: "string",
459
+ enum: ["mpg", "l_per_100km"],
460
+ description: "Unit for fuel efficiency (default: mpg)",
461
+ },
462
+ grade: {
463
+ type: "string",
464
+ enum: ["regular", "midgrade", "premium", "diesel", "e85"],
465
+ description: "Fuel grade (default: regular)",
466
+ },
467
+ tank_size: { type: "number", description: "Tank size in gallons for stop planning" },
468
+ },
469
+ },
470
+ },
471
+ ];
472
+
473
+ // ---- Server ----
474
+
475
+ const API_KEY = process.env.GASBUDDY_API_KEY || "";
476
+ const BASE_URL = process.env.GASBUDDY_BASE_URL || "https://api.gasbuddy.com";
477
+ const USER_ID = process.env.GASBUDDY_USER_ID || "";
478
+
479
+ const client = new GasBuddyClient({ apiKey: API_KEY, baseUrl: BASE_URL });
480
+
481
+ const server = new Server(
482
+ { name: "@striderlabs/mcp-gasbuddy", version: "1.0.0" },
483
+ { capabilities: { tools: {} } }
484
+ );
485
+
486
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
487
+
488
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
489
+ const { name, arguments: args = {} } = request.params;
490
+
491
+ try {
492
+ switch (name) {
493
+ case "search_stations": {
494
+ const result = await client.searchStations({
495
+ latitude: args.latitude as number | undefined,
496
+ longitude: args.longitude as number | undefined,
497
+ address: args.address as string | undefined,
498
+ radius: args.radius as number | undefined,
499
+ fuel: args.fuel as string | undefined,
500
+ limit: args.limit as number | undefined,
501
+ minRating: args.minRating as number | undefined,
502
+ amenities: args.amenities as string[] | undefined,
503
+ });
504
+ return {
505
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
506
+ };
507
+ }
508
+
509
+ case "get_station_details": {
510
+ const result = await client.getStationDetails(args.station_id as string);
511
+ return {
512
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
513
+ };
514
+ }
515
+
516
+ case "get_prices": {
517
+ const result = await client.getPrices({
518
+ stationId: args.station_id as string | undefined,
519
+ latitude: args.latitude as number | undefined,
520
+ longitude: args.longitude as number | undefined,
521
+ radius: args.radius as number | undefined,
522
+ grade: args.grade as string | undefined,
523
+ });
524
+ return {
525
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
526
+ };
527
+ }
528
+
529
+ case "report_price": {
530
+ const result = await client.reportPrice({
531
+ stationId: args.station_id as string,
532
+ grade: args.grade as string,
533
+ price: args.price as number,
534
+ latitude: args.latitude as number | undefined,
535
+ longitude: args.longitude as number | undefined,
536
+ });
537
+ return {
538
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
539
+ };
540
+ }
541
+
542
+ case "get_price_trends": {
543
+ const result = await client.getPriceTrends({
544
+ stationId: args.station_id as string | undefined,
545
+ latitude: args.latitude as number | undefined,
546
+ longitude: args.longitude as number | undefined,
547
+ grade: args.grade as string | undefined,
548
+ days: args.days as number | undefined,
549
+ region: args.region as string | undefined,
550
+ });
551
+ return {
552
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
553
+ };
554
+ }
555
+
556
+ case "get_deals": {
557
+ const result = await client.getDeals({
558
+ latitude: args.latitude as number | undefined,
559
+ longitude: args.longitude as number | undefined,
560
+ radius: args.radius as number | undefined,
561
+ type: args.type as string | undefined,
562
+ });
563
+ return {
564
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
565
+ };
566
+ }
567
+
568
+ case "get_pay_with_gasbuddy": {
569
+ const userId = (args.user_id as string) || USER_ID;
570
+ if (!userId) throw new Error("user_id is required (or set GASBUDDY_USER_ID env var)");
571
+ const result = await client.getPayWithGasBuddy(userId);
572
+ return {
573
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
574
+ };
575
+ }
576
+
577
+ case "find_cheapest": {
578
+ const result = await client.findCheapest({
579
+ latitude: args.latitude as number,
580
+ longitude: args.longitude as number,
581
+ radius: args.radius as number | undefined,
582
+ grade: args.grade as string | undefined,
583
+ limit: args.limit as number | undefined,
584
+ });
585
+ return {
586
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
587
+ };
588
+ }
589
+
590
+ case "get_favorites": {
591
+ const userId = (args.user_id as string) || USER_ID;
592
+ if (!userId) throw new Error("user_id is required (or set GASBUDDY_USER_ID env var)");
593
+ const result = await client.getFavorites(userId);
594
+ return {
595
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
596
+ };
597
+ }
598
+
599
+ case "get_trip_cost": {
600
+ const result = await client.getTripCost({
601
+ originLatitude: args.origin_latitude as number,
602
+ originLongitude: args.origin_longitude as number,
603
+ destLatitude: args.dest_latitude as number,
604
+ destLongitude: args.dest_longitude as number,
605
+ fuelEfficiency: args.fuel_efficiency as number,
606
+ fuelEfficiencyUnit: args.fuel_efficiency_unit as string | undefined,
607
+ grade: args.grade as string | undefined,
608
+ tankSize: args.tank_size as number | undefined,
609
+ });
610
+ return {
611
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
612
+ };
613
+ }
614
+
615
+ default:
616
+ throw new Error(`Unknown tool: ${name}`);
617
+ }
618
+ } catch (error) {
619
+ const message = error instanceof Error ? error.message : String(error);
620
+ return {
621
+ content: [{ type: "text", text: `Error: ${message}` }],
622
+ isError: true,
623
+ };
624
+ }
625
+ });
626
+
627
+ async function main() {
628
+ const transport = new StdioServerTransport();
629
+ await server.connect(transport);
630
+ console.error("@striderlabs/mcp-gasbuddy server running on stdio");
631
+ }
632
+
633
+ main().catch((err) => {
634
+ console.error("Fatal error:", err);
635
+ process.exit(1);
636
+ });