odds-api-mcp-server 1.1.1 → 1.2.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.
- package/dist/index.js +56 -0
- package/package.json +1 -1
- package/src/index.test.ts +56 -2
- package/src/index.ts +71 -0
package/dist/index.js
CHANGED
|
@@ -319,6 +319,62 @@ const tools = [
|
|
|
319
319
|
return jsonResponse(await apiRequest("/odds/updated", { since, bookmaker, sport }));
|
|
320
320
|
},
|
|
321
321
|
},
|
|
322
|
+
// ── Dropping Odds ───────────────────────────────────────────────
|
|
323
|
+
{
|
|
324
|
+
name: "get_dropping_odds",
|
|
325
|
+
description: "Get odds that have dropped the most from opening, based on sharp bookmaker data. Useful for tracking where sharp money is moving. Updated every ~10 seconds. Only available on paid plans.",
|
|
326
|
+
inputSchema: {
|
|
327
|
+
type: "object",
|
|
328
|
+
properties: {
|
|
329
|
+
sport: {
|
|
330
|
+
type: "string",
|
|
331
|
+
description: "Sport slug to filter by (e.g., 'football', 'basketball')",
|
|
332
|
+
},
|
|
333
|
+
league: {
|
|
334
|
+
type: "string",
|
|
335
|
+
description: "League slug to filter by (e.g., 'england-premier-league'). Requires sport to also be set.",
|
|
336
|
+
},
|
|
337
|
+
market: {
|
|
338
|
+
type: "string",
|
|
339
|
+
description: "Market type to filter by: 'ML', 'Spread', or 'Totals'",
|
|
340
|
+
},
|
|
341
|
+
timeWindow: {
|
|
342
|
+
type: "string",
|
|
343
|
+
description: "Time window for drop calculation and sorting: 'opening', '12h', '24h', '48h' (default: 'opening')",
|
|
344
|
+
},
|
|
345
|
+
minDrop: {
|
|
346
|
+
type: "number",
|
|
347
|
+
description: "Minimum drop percentage threshold (default: 0)",
|
|
348
|
+
},
|
|
349
|
+
limit: {
|
|
350
|
+
type: "number",
|
|
351
|
+
description: "Results per page, 1-200 (default: 50)",
|
|
352
|
+
},
|
|
353
|
+
page: {
|
|
354
|
+
type: "number",
|
|
355
|
+
description: "Page number, 1-indexed (default: 1)",
|
|
356
|
+
},
|
|
357
|
+
includeEventDetails: {
|
|
358
|
+
type: "boolean",
|
|
359
|
+
description: "Include expanded event details (home, away, date, sport, league) in response",
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
required: [],
|
|
363
|
+
},
|
|
364
|
+
async handler(args) {
|
|
365
|
+
const { sport, league, market, timeWindow, minDrop, limit, page, includeEventDetails } = args;
|
|
366
|
+
return jsonResponse(await apiRequest("/dropping-odds", {
|
|
367
|
+
sport,
|
|
368
|
+
league,
|
|
369
|
+
market,
|
|
370
|
+
timeWindow,
|
|
371
|
+
minDrop,
|
|
372
|
+
limit,
|
|
373
|
+
page,
|
|
374
|
+
includeEventDetails: includeEventDetails || undefined,
|
|
375
|
+
}));
|
|
376
|
+
},
|
|
377
|
+
},
|
|
322
378
|
// ── Historical ──────────────────────────────────────────────────
|
|
323
379
|
{
|
|
324
380
|
name: "get_historical_events",
|
package/package.json
CHANGED
package/src/index.test.ts
CHANGED
|
@@ -63,6 +63,7 @@ describe("Tool Registry", () => {
|
|
|
63
63
|
"get_multi_odds",
|
|
64
64
|
"get_odds_movements",
|
|
65
65
|
"get_updated_odds",
|
|
66
|
+
"get_dropping_odds",
|
|
66
67
|
"get_historical_events",
|
|
67
68
|
"get_historical_odds",
|
|
68
69
|
"get_value_bets",
|
|
@@ -72,8 +73,8 @@ describe("Tool Registry", () => {
|
|
|
72
73
|
"get_documentation",
|
|
73
74
|
];
|
|
74
75
|
|
|
75
|
-
it("has all
|
|
76
|
-
expect(tools).toHaveLength(
|
|
76
|
+
it("has all 22 tools registered", () => {
|
|
77
|
+
expect(tools).toHaveLength(22);
|
|
77
78
|
});
|
|
78
79
|
|
|
79
80
|
it("has no duplicate tool names", () => {
|
|
@@ -134,6 +135,7 @@ describe("Tool Schemas - Required Parameters", () => {
|
|
|
134
135
|
["get_multi_odds", ["eventIds", "bookmakers"]],
|
|
135
136
|
["get_odds_movements", ["eventId", "bookmaker", "market"]],
|
|
136
137
|
["get_updated_odds", ["since", "bookmaker", "sport"]],
|
|
138
|
+
["get_dropping_odds", []],
|
|
137
139
|
["get_historical_events", ["sport", "league", "from", "to"]],
|
|
138
140
|
["get_historical_odds", ["eventId", "bookmakers"]],
|
|
139
141
|
["get_value_bets", ["bookmaker"]],
|
|
@@ -459,6 +461,58 @@ describe("Tool Handlers", () => {
|
|
|
459
461
|
expect(calledUrl.searchParams.get("since")).toBe(String(since));
|
|
460
462
|
});
|
|
461
463
|
|
|
464
|
+
it("get_dropping_odds passes all optional params", async () => {
|
|
465
|
+
const fetchMock = mockFetchJson([]);
|
|
466
|
+
globalThis.fetch = fetchMock;
|
|
467
|
+
|
|
468
|
+
await toolMap.get("get_dropping_odds")!.handler({
|
|
469
|
+
sport: "football",
|
|
470
|
+
league: "england-premier-league",
|
|
471
|
+
market: "ML",
|
|
472
|
+
timeWindow: "12h",
|
|
473
|
+
minDrop: 5,
|
|
474
|
+
limit: 100,
|
|
475
|
+
page: 2,
|
|
476
|
+
includeEventDetails: true,
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
const calledUrl = new URL(fetchMock.mock.calls[0][0]);
|
|
480
|
+
expect(calledUrl.pathname).toBe("/v3/dropping-odds");
|
|
481
|
+
expect(calledUrl.searchParams.get("sport")).toBe("football");
|
|
482
|
+
expect(calledUrl.searchParams.get("league")).toBe("england-premier-league");
|
|
483
|
+
expect(calledUrl.searchParams.get("market")).toBe("ML");
|
|
484
|
+
expect(calledUrl.searchParams.get("timeWindow")).toBe("12h");
|
|
485
|
+
expect(calledUrl.searchParams.get("minDrop")).toBe("5");
|
|
486
|
+
expect(calledUrl.searchParams.get("limit")).toBe("100");
|
|
487
|
+
expect(calledUrl.searchParams.get("page")).toBe("2");
|
|
488
|
+
expect(calledUrl.searchParams.get("includeEventDetails")).toBe("true");
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
it("get_dropping_odds sends includeEventDetails only when true", async () => {
|
|
492
|
+
const fetchMock = mockFetchJson([]);
|
|
493
|
+
globalThis.fetch = fetchMock;
|
|
494
|
+
|
|
495
|
+
await toolMap.get("get_dropping_odds")!.handler({ includeEventDetails: false });
|
|
496
|
+
const url = new URL(fetchMock.mock.calls[0][0]);
|
|
497
|
+
expect(url.searchParams.has("includeEventDetails")).toBe(false);
|
|
498
|
+
|
|
499
|
+
await toolMap.get("get_dropping_odds")!.handler({});
|
|
500
|
+
const url2 = new URL(fetchMock.mock.calls[1][0]);
|
|
501
|
+
expect(url2.searchParams.has("includeEventDetails")).toBe(false);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it("get_dropping_odds works with no params", async () => {
|
|
505
|
+
const fetchMock = mockFetchJson([]);
|
|
506
|
+
globalThis.fetch = fetchMock;
|
|
507
|
+
|
|
508
|
+
await toolMap.get("get_dropping_odds")!.handler({});
|
|
509
|
+
|
|
510
|
+
const calledUrl = new URL(fetchMock.mock.calls[0][0]);
|
|
511
|
+
expect(calledUrl.pathname).toBe("/v3/dropping-odds");
|
|
512
|
+
expect(calledUrl.searchParams.has("sport")).toBe(false);
|
|
513
|
+
expect(calledUrl.searchParams.has("league")).toBe(false);
|
|
514
|
+
});
|
|
515
|
+
|
|
462
516
|
it("get_historical_events passes all required params", async () => {
|
|
463
517
|
const fetchMock = mockFetchJson([]);
|
|
464
518
|
globalThis.fetch = fetchMock;
|
package/src/index.ts
CHANGED
|
@@ -408,6 +408,77 @@ const tools: ToolDefinition[] = [
|
|
|
408
408
|
},
|
|
409
409
|
},
|
|
410
410
|
|
|
411
|
+
// ── Dropping Odds ───────────────────────────────────────────────
|
|
412
|
+
|
|
413
|
+
{
|
|
414
|
+
name: "get_dropping_odds",
|
|
415
|
+
description:
|
|
416
|
+
"Get odds that have dropped the most from opening, based on sharp bookmaker data. Useful for tracking where sharp money is moving. Updated every ~10 seconds. Only available on paid plans.",
|
|
417
|
+
inputSchema: {
|
|
418
|
+
type: "object",
|
|
419
|
+
properties: {
|
|
420
|
+
sport: {
|
|
421
|
+
type: "string",
|
|
422
|
+
description: "Sport slug to filter by (e.g., 'football', 'basketball')",
|
|
423
|
+
},
|
|
424
|
+
league: {
|
|
425
|
+
type: "string",
|
|
426
|
+
description: "League slug to filter by (e.g., 'england-premier-league'). Requires sport to also be set.",
|
|
427
|
+
},
|
|
428
|
+
market: {
|
|
429
|
+
type: "string",
|
|
430
|
+
description: "Market type to filter by: 'ML', 'Spread', or 'Totals'",
|
|
431
|
+
},
|
|
432
|
+
timeWindow: {
|
|
433
|
+
type: "string",
|
|
434
|
+
description: "Time window for drop calculation and sorting: 'opening', '12h', '24h', '48h' (default: 'opening')",
|
|
435
|
+
},
|
|
436
|
+
minDrop: {
|
|
437
|
+
type: "number",
|
|
438
|
+
description: "Minimum drop percentage threshold (default: 0)",
|
|
439
|
+
},
|
|
440
|
+
limit: {
|
|
441
|
+
type: "number",
|
|
442
|
+
description: "Results per page, 1-200 (default: 50)",
|
|
443
|
+
},
|
|
444
|
+
page: {
|
|
445
|
+
type: "number",
|
|
446
|
+
description: "Page number, 1-indexed (default: 1)",
|
|
447
|
+
},
|
|
448
|
+
includeEventDetails: {
|
|
449
|
+
type: "boolean",
|
|
450
|
+
description: "Include expanded event details (home, away, date, sport, league) in response",
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
required: [],
|
|
454
|
+
},
|
|
455
|
+
async handler(args) {
|
|
456
|
+
const { sport, league, market, timeWindow, minDrop, limit, page, includeEventDetails } =
|
|
457
|
+
args as {
|
|
458
|
+
sport?: string;
|
|
459
|
+
league?: string;
|
|
460
|
+
market?: string;
|
|
461
|
+
timeWindow?: string;
|
|
462
|
+
minDrop?: number;
|
|
463
|
+
limit?: number;
|
|
464
|
+
page?: number;
|
|
465
|
+
includeEventDetails?: boolean;
|
|
466
|
+
};
|
|
467
|
+
return jsonResponse(
|
|
468
|
+
await apiRequest("/dropping-odds", {
|
|
469
|
+
sport,
|
|
470
|
+
league,
|
|
471
|
+
market,
|
|
472
|
+
timeWindow,
|
|
473
|
+
minDrop,
|
|
474
|
+
limit,
|
|
475
|
+
page,
|
|
476
|
+
includeEventDetails: includeEventDetails || undefined,
|
|
477
|
+
}),
|
|
478
|
+
);
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
|
|
411
482
|
// ── Historical ──────────────────────────────────────────────────
|
|
412
483
|
|
|
413
484
|
{
|