@gotza02/sequential-thinking 2026.2.39 → 2026.2.41

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.
Files changed (38) hide show
  1. package/README.md +7 -4
  2. package/SYSTEM_INSTRUCTION.md +25 -50
  3. package/dist/tools/sports/core/base.d.ts +169 -0
  4. package/dist/tools/sports/core/base.js +289 -0
  5. package/dist/tools/sports/core/cache.d.ts +106 -0
  6. package/dist/tools/sports/core/cache.js +305 -0
  7. package/dist/tools/sports/core/constants.d.ts +179 -0
  8. package/dist/tools/sports/core/constants.js +149 -0
  9. package/dist/tools/sports/core/types.d.ts +379 -0
  10. package/dist/tools/sports/core/types.js +5 -0
  11. package/dist/tools/sports/index.d.ts +34 -0
  12. package/dist/tools/sports/index.js +50 -0
  13. package/dist/tools/sports/providers/api.d.ts +73 -0
  14. package/dist/tools/sports/providers/api.js +517 -0
  15. package/dist/tools/sports/providers/scraper.d.ts +66 -0
  16. package/dist/tools/sports/providers/scraper.js +186 -0
  17. package/dist/tools/sports/providers/search.d.ts +54 -0
  18. package/dist/tools/sports/providers/search.js +224 -0
  19. package/dist/tools/sports/tools/betting.d.ts +6 -0
  20. package/dist/tools/sports/tools/betting.js +251 -0
  21. package/dist/tools/sports/tools/league.d.ts +11 -0
  22. package/dist/tools/sports/tools/league.js +12 -0
  23. package/dist/tools/sports/tools/live.d.ts +9 -0
  24. package/dist/tools/sports/tools/live.js +235 -0
  25. package/dist/tools/sports/tools/match.d.ts +6 -0
  26. package/dist/tools/sports/tools/match.js +323 -0
  27. package/dist/tools/sports/tools/player.d.ts +6 -0
  28. package/dist/tools/sports/tools/player.js +152 -0
  29. package/dist/tools/sports/tools/team.d.ts +6 -0
  30. package/dist/tools/sports/tools/team.js +370 -0
  31. package/dist/tools/sports/utils/calculator.d.ts +69 -0
  32. package/dist/tools/sports/utils/calculator.js +156 -0
  33. package/dist/tools/sports/utils/formatter.d.ts +57 -0
  34. package/dist/tools/sports/utils/formatter.js +206 -0
  35. package/dist/tools/sports.d.ts +7 -0
  36. package/dist/tools/sports.js +27 -6
  37. package/package.json +1 -1
  38. package/system_instruction.md +155 -0
@@ -0,0 +1,251 @@
1
+ /**
2
+ * SPORTS MODULE TOOLS - Betting Tools
3
+ * Tools for betting intelligence and odds comparison
4
+ */
5
+ import { z } from 'zod';
6
+ import { getSearchProvider } from '../providers/search.js';
7
+ import { calculateValue, calculateKelly, calculateRecommendedStake, oddsToProbability } from '../utils/calculator.js';
8
+ import { formatOdds } from '../utils/formatter.js';
9
+ import { BETTING } from '../core/constants.js';
10
+ // ============= Helper Functions =============
11
+ /**
12
+ * Get league ID from league name
13
+ */
14
+ function getLeagueId(leagueName) {
15
+ const normalized = leagueName.toLowerCase();
16
+ for (const [key, value] of Object.entries({ ...BETTING })) {
17
+ // Skip non-league entries
18
+ if (typeof value !== 'object' || value === null || !('name' in value)) {
19
+ continue;
20
+ }
21
+ }
22
+ return undefined;
23
+ }
24
+ /**
25
+ * Search for odds by match
26
+ */
27
+ async function searchOdds(homeTeam, awayTeam) {
28
+ const searchQuery = `${homeTeam} vs ${awayTeam} odds betting`;
29
+ const searchProvider = getSearchProvider();
30
+ try {
31
+ const results = await searchProvider.search(searchQuery, undefined, 5);
32
+ let output = `## 💰 Odds: ${homeTeam} vs ${awayTeam}\n\n`;
33
+ output += `### Available Odds\n\n`;
34
+ const items = results.map(r => `- [${r.title}](${r.url}): ${r.snippet}`).join('\n');
35
+ output += items;
36
+ output += `\n\n*Click links to view current odds from bookmakers.*`;
37
+ return output;
38
+ }
39
+ catch (error) {
40
+ return `Error searching for odds: ${error instanceof Error ? error.message : String(error)}`;
41
+ }
42
+ }
43
+ // ============= Tool Registration =============
44
+ export function registerBettingTools(server) {
45
+ /**
46
+ * Tool 1: odds_comparison
47
+ * Compare betting odds across multiple bookmakers
48
+ */
49
+ server.tool('odds_comparison', `Compare betting odds across multiple bookmakers.
50
+ Shows: Home Win, Draw, Away Win, Asian Handicap, Over/Under.`, {
51
+ matchId: z.string().optional().describe('API match ID if known'),
52
+ homeTeam: z.string().optional().describe('Home team name (for search fallback)'),
53
+ awayTeam: z.string().optional().describe('Away team name (for search fallback)'),
54
+ }, async ({ matchId, homeTeam, awayTeam }) => {
55
+ const errors = [];
56
+ let output = '';
57
+ if (homeTeam && awayTeam) {
58
+ output = await searchOdds(homeTeam, awayTeam);
59
+ }
60
+ else {
61
+ errors.push('Either matchId OR both homeTeam and awayTeam must be provided');
62
+ }
63
+ return {
64
+ content: [{ type: 'text', text: output || `Error: ${errors.join(', ')}` }],
65
+ };
66
+ });
67
+ /**
68
+ * Tool 2: value_bet_calculator
69
+ * Calculate betting value using Kelly Criterion
70
+ */
71
+ server.tool('value_bet_calculator', `Calculate betting value and recommended stake.
72
+ Shows: Fair odds, market odds, value %, confidence, Kelly stake.`, {
73
+ odds: z.number().describe('Decimal odds offered by bookmaker'),
74
+ probability: z.number().describe('Your estimated probability (0-1, e.g., 0.60 = 60%)'),
75
+ bankroll: z.number().optional().describe('Total bankroll (default: 1000)'),
76
+ kellyVariant: z.enum(['full', 'half', 'quarter']).optional().default('half')
77
+ .describe('Kelly Criterion variant'),
78
+ }, async ({ odds, probability, bankroll, kellyVariant }) => {
79
+ if (odds <= 1) {
80
+ return {
81
+ content: [{ type: 'text', text: 'Error: Odds must be greater than 1.00' }],
82
+ isError: true,
83
+ };
84
+ }
85
+ if (probability < 0 || probability > 1) {
86
+ return {
87
+ content: [{ type: 'text', text: 'Error: Probability must be between 0 and 1' }],
88
+ isError: true,
89
+ };
90
+ }
91
+ const value = calculateValue(odds, probability);
92
+ const kelly = calculateKelly(odds, probability, kellyVariant);
93
+ const recommendedStake = calculateRecommendedStake(odds, probability, bankroll || BETTING.DEFAULT_BANKROLL, kellyVariant);
94
+ const fairOdds = 1 / probability;
95
+ const impliedProb = oddsToProbability(odds);
96
+ let output = `## 💰 Value Bet Analysis\n\n`;
97
+ output += `| Metric | Value |\n`;
98
+ output += `|--------|-------|\n`;
99
+ output += `| Market Odds | ${formatOdds(odds)} |\n`;
100
+ output += `| Fair Odds | ${fairOdds.toFixed(2)} |\n`;
101
+ output += `| Your Probability | ${(probability * 100).toFixed(1)}% |\n`;
102
+ output += `| Implied Probability | ${(impliedProb * 100).toFixed(1)}% |\n`;
103
+ output += `| **Value** | **${value > 0 ? '+' : ''}${(value * 100).toFixed(1)}%** |\n`;
104
+ output += `\n### Kelly Criterion (${kellyVariant})\n\n`;
105
+ output += `| Metric | Value |\n`;
106
+ output += `|--------|-------|\n`;
107
+ output += `| Kelly Fraction | ${(kelly * 100).toFixed(2)}% |\n`;
108
+ output += `| Recommended Stake | ${recommendedStake.toFixed(2)} units |\n`;
109
+ output += `| Based on Bankroll | ${bankroll || BETTING.DEFAULT_BANKROLL} units |\n`;
110
+ // Verdict
111
+ output += `\n### Verdict\n\n`;
112
+ if (value < -0.05) {
113
+ output += `❌ **Negative Value** - This bet is not worth taking.\n`;
114
+ }
115
+ else if (value < 0) {
116
+ output += `⚠️ **Low Value** - Minimal edge, consider skipping.\n`;
117
+ }
118
+ else if (value < BETTING.HIGH_VALUE_THRESHOLD) {
119
+ output += `✅ **Value Bet** - Positive edge detected.\n`;
120
+ }
121
+ else {
122
+ output += `🔥 **HIGH VALUE** - Strong value opportunity!\n`;
123
+ }
124
+ // Risk warning
125
+ if (kelly > 0.05) {
126
+ output += `\n⚠️ **Warning:** High stake recommendation. Consider using half or quarter Kelly.\n`;
127
+ }
128
+ return {
129
+ content: [{ type: 'text', text: output }],
130
+ };
131
+ });
132
+ /**
133
+ * Tool 3: odds_converter
134
+ * Convert between decimal, fractional, and American odds
135
+ */
136
+ server.tool('odds_converter', `Convert between decimal, fractional, and American odds formats.`, {
137
+ odds: z.number().describe('Odds value to convert'),
138
+ fromFormat: z.enum(['decimal', 'fractional', 'american'])
139
+ .describe('Input format'),
140
+ toFormat: z.enum(['decimal', 'fractional', 'american', 'all'])
141
+ .optional().default('all')
142
+ .describe('Output format'),
143
+ }, async ({ odds, fromFormat, toFormat }) => {
144
+ // Fallback for Zod default value issue
145
+ const targetFormat = toFormat || 'all';
146
+ let decimalOdds;
147
+ // Convert input to decimal
148
+ switch (fromFormat) {
149
+ case 'decimal':
150
+ decimalOdds = odds;
151
+ break;
152
+ case 'american':
153
+ if (odds > 0) {
154
+ decimalOdds = (odds / 100) + 1;
155
+ }
156
+ else {
157
+ decimalOdds = (100 / Math.abs(odds)) + 1;
158
+ }
159
+ break;
160
+ case 'fractional':
161
+ // Parse fractional odds like "5/2" or "5-2"
162
+ const oddsStr = odds.toString();
163
+ const parts = oddsStr.split(/[-/]/);
164
+ if (parts.length === 2) {
165
+ decimalOdds = (parseFloat(parts[0]) / parseFloat(parts[1])) + 1;
166
+ }
167
+ else {
168
+ decimalOdds = odds;
169
+ }
170
+ break;
171
+ }
172
+ if (isNaN(decimalOdds) || decimalOdds < 1) {
173
+ return {
174
+ content: [{ type: 'text', text: 'Error: Invalid odds value' }],
175
+ isError: true,
176
+ };
177
+ }
178
+ let output = `## 📊 Odds Conversion\n\n`;
179
+ output += `**Input:** ${odds} (${fromFormat})\n\n`;
180
+ if (targetFormat === 'all') {
181
+ const american = decimalOdds >= 2
182
+ ? Math.round((decimalOdds - 1) * 100)
183
+ : Math.round(-100 / (decimalOdds - 1));
184
+ const fractional = decimalOdds - 1;
185
+ const impliedProb = oddsToProbability(decimalOdds);
186
+ output += `| Format | Odds |\n`;
187
+ output += `|--------|------|\n`;
188
+ output += `| Decimal | ${decimalOdds.toFixed(2)} |\n`;
189
+ output += `| American | ${american > 0 ? '+' : ''}${american} |\n`;
190
+ output += `| Fractional | ${fractional.toFixed(2)}/1 |\n`;
191
+ output += `| Implied Prob | ${(impliedProb * 100).toFixed(1)}% |\n`;
192
+ }
193
+ else {
194
+ // Convert to specific format
195
+ switch (toFormat) {
196
+ case 'decimal':
197
+ output += `**Result:** ${decimalOdds.toFixed(2)}`;
198
+ break;
199
+ case 'american':
200
+ const american = decimalOdds >= 2
201
+ ? Math.round((decimalOdds - 1) * 100)
202
+ : Math.round(-100 / (decimalOdds - 1));
203
+ output += `**Result:** ${american > 0 ? '+' : ''}${american}`;
204
+ break;
205
+ case 'fractional':
206
+ const fractional = decimalOdds - 1;
207
+ output += `**Result:** ${fractional.toFixed(2)}/1`;
208
+ break;
209
+ }
210
+ }
211
+ return {
212
+ content: [{ type: 'text', text: output }],
213
+ };
214
+ });
215
+ /**
216
+ * Tool 4: probability_calculator
217
+ * Calculate probabilities and implied odds
218
+ */
219
+ server.tool('probability_calculator', `Calculate implied probability from odds and vice versa.
220
+ Useful for converting between odds and probabilities.`, {
221
+ odds: z.number().optional().describe('Decimal odds to convert to probability'),
222
+ probability: z.number().optional().describe('Probability (0-1) to convert to odds'),
223
+ }, async ({ odds, probability }) => {
224
+ let output = `## 📊 Probability Calculator\n\n`;
225
+ if (odds) {
226
+ const impliedProb = oddsToProbability(odds);
227
+ const american = odds >= 2
228
+ ? Math.round((odds - 1) * 100)
229
+ : Math.round(-100 / (odds - 1));
230
+ output += `### From Odds: ${formatOdds(odds)}\n\n`;
231
+ output += `- **Implied Probability:** ${(impliedProb * 100).toFixed(1)}%\n`;
232
+ output += `- **American Odds:** ${american > 0 ? '+' : ''}${american}\n`;
233
+ output += `- **Break-even Rate:** ${(impliedProb * 100).toFixed(1)}%\n`;
234
+ }
235
+ if (probability) {
236
+ const fairOdds = 1 / probability;
237
+ const american = fairOdds >= 2
238
+ ? Math.round((fairOdds - 1) * 100)
239
+ : Math.round(-100 / (fairOdds - 1));
240
+ output += `\n### From Probability: ${(probability * 100).toFixed(1)}%\n\n`;
241
+ output += `- **Fair Odds:** ${fairOdds.toFixed(2)}\n`;
242
+ output += `- **American Odds:** ${american > 0 ? '+' : ''}${american}\n`;
243
+ }
244
+ if (!odds && !probability) {
245
+ output += `Please provide either odds or probability.`;
246
+ }
247
+ return {
248
+ content: [{ type: 'text', text: output }],
249
+ };
250
+ });
251
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * SPORTS MODULE TOOLS - League Tools
3
+ * Tools for league standings and statistics
4
+ *
5
+ * This file will contain:
6
+ * - get_league_standings
7
+ * - get_top_scorers
8
+ *
9
+ * Status: Placeholder for Phase 2 implementation
10
+ */
11
+ export {};
@@ -0,0 +1,12 @@
1
+ /**
2
+ * SPORTS MODULE TOOLS - League Tools
3
+ * Tools for league standings and statistics
4
+ *
5
+ * This file will contain:
6
+ * - get_league_standings
7
+ * - get_top_scorers
8
+ *
9
+ * Status: Placeholder for Phase 2 implementation
10
+ */
11
+ export {};
12
+ // Placeholder - Tools will be implemented in Phase 2
@@ -0,0 +1,9 @@
1
+ /**
2
+ * SPORTS MODULE TOOLS - Live Tools
3
+ * Tools for live match data and alerts
4
+ */
5
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
6
+ /**
7
+ * Register live and alert tools
8
+ */
9
+ export declare function registerLiveTools(server: McpServer): void;
@@ -0,0 +1,235 @@
1
+ /**
2
+ * SPORTS MODULE TOOLS - Live Tools
3
+ * Tools for live match data and alerts
4
+ */
5
+ import { z } from 'zod';
6
+ import { getSearchProvider } from '../providers/search.js';
7
+ // In-memory watchlist storage
8
+ const watchlist = [];
9
+ /**
10
+ * Register live and alert tools
11
+ */
12
+ export function registerLiveTools(server) {
13
+ /**
14
+ * Tool 1: watchlist_add
15
+ * Add an item to the watchlist for monitoring
16
+ */
17
+ server.tool('watchlist_add', `Add an item to the sports watchlist.
18
+ Monitors: matches, odds changes, team news, injuries.
19
+
20
+ Can be called with:
21
+ 1. type + target + condition (simple mode)
22
+ 2. matchId + homeTeam + awayTeam + alertCondition (match mode)`, {
23
+ // Simple mode
24
+ type: z.enum(['match', 'odds', 'news', 'injury']).optional().describe('Type of alert'),
25
+ target: z.string().optional().describe('Target to monitor (team name, match ID, etc.)'),
26
+ condition: z.string().optional().describe('Alert condition (e.g., "odds < 2.00", "injury news")'),
27
+ // Match mode
28
+ matchId: z.string().optional().describe('Match ID for match monitoring'),
29
+ homeTeam: z.string().optional().describe('Home team name'),
30
+ awayTeam: z.string().optional().describe('Away team name'),
31
+ alertCondition: z.string().optional().describe('Alert condition for match'),
32
+ }, async ({ type, target, condition, matchId, homeTeam, awayTeam, alertCondition }) => {
33
+ const id = `watch_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
34
+ // Handle match mode (matchId, homeTeam, awayTeam provided)
35
+ let itemType = type;
36
+ let itemTarget = target;
37
+ let itemCondition = condition;
38
+ if (matchId && (homeTeam || awayTeam)) {
39
+ // Match mode: derive type/target from match parameters
40
+ itemType = 'match';
41
+ itemTarget = matchId;
42
+ if (!itemCondition) {
43
+ itemCondition = alertCondition || 'monitor';
44
+ }
45
+ }
46
+ else {
47
+ // Simple mode: use provided values
48
+ if (!itemType)
49
+ itemType = 'match';
50
+ if (!itemTarget)
51
+ itemTarget = 'unknown';
52
+ if (!itemCondition)
53
+ itemCondition = 'monitor';
54
+ }
55
+ const item = {
56
+ id,
57
+ type: itemType,
58
+ target: itemTarget,
59
+ condition: itemCondition,
60
+ createdAt: new Date().toISOString(),
61
+ triggered: false,
62
+ };
63
+ watchlist.push(item);
64
+ let output = `## 📋 Added to Watchlist\n\n`;
65
+ output += `**ID:** ${id}\n`;
66
+ output += `**Type:** ${itemType}\n`;
67
+ output += `**Target:** ${itemTarget}\n`;
68
+ if (itemCondition)
69
+ output += `**Condition:** ${itemCondition}\n`;
70
+ if (homeTeam && awayTeam) {
71
+ output += `**Match:** ${homeTeam} vs ${awayTeam}\n`;
72
+ }
73
+ output += `**Created:** ${item.createdAt}\n`;
74
+ output += `\n\nTotal watchlist items: ${watchlist.length}`;
75
+ return {
76
+ content: [{ type: 'text', text: output }],
77
+ };
78
+ });
79
+ /**
80
+ * Tool 2: watchlist_list
81
+ * List all items in the watchlist
82
+ */
83
+ server.tool('watchlist_list', `List all items in the sports watchlist.`, {}, async () => {
84
+ if (watchlist.length === 0) {
85
+ return {
86
+ content: [{ type: 'text', text: '## 📋 Watchlist\n\nNo items in watchlist.' }],
87
+ };
88
+ }
89
+ let output = `## 📋 Watchlist (${watchlist.length} items)\n\n`;
90
+ for (const item of watchlist) {
91
+ const status = item.triggered ? '✅' : '👀';
92
+ output += `${status} **${item.id}**\n`;
93
+ output += `- Type: ${item.type}\n`;
94
+ output += `- Target: ${item.target}\n`;
95
+ if (item.condition)
96
+ output += `- Condition: ${item.condition}\n`;
97
+ output += `- Created: ${item.createdAt}\n\n`;
98
+ }
99
+ return {
100
+ content: [{ type: 'text', text: output }],
101
+ };
102
+ });
103
+ /**
104
+ * Tool 3: watchlist_remove
105
+ * Remove an item from the watchlist
106
+ */
107
+ server.tool('watchlist_remove', `Remove an item from the sports watchlist.`, {
108
+ id: z.string().describe('Watchlist item ID to remove'),
109
+ }, async ({ id }) => {
110
+ const index = watchlist.findIndex(item => item.id === id);
111
+ if (index === -1) {
112
+ return {
113
+ content: [{ type: 'text', text: `Error: Watchlist item "${id}" not found.` }],
114
+ isError: true,
115
+ };
116
+ }
117
+ const removed = watchlist.splice(index, 1)[0];
118
+ let output = `## 📋 Removed from Watchlist\n\n`;
119
+ output += `**ID:** ${removed.id}\n`;
120
+ output += `**Type:** ${removed.type}\n`;
121
+ output += `**Target:** ${removed.target}\n`;
122
+ output += `\n\nRemaining items: ${watchlist.length}`;
123
+ return {
124
+ content: [{ type: 'text', text: output }],
125
+ };
126
+ });
127
+ /**
128
+ * Tool 4: watchlist_clear
129
+ * Clear all items from the watchlist
130
+ */
131
+ server.tool('watchlist_clear', `Clear all items from the sports watchlist.`, {}, async () => {
132
+ const count = watchlist.length;
133
+ watchlist.length = 0;
134
+ return {
135
+ content: [{ type: 'text', text: `## 📋 Watchlist Cleared\n\nRemoved ${count} item(s).` }],
136
+ };
137
+ });
138
+ /**
139
+ * Tool 5: check_alerts
140
+ * Check watchlist for triggered alerts
141
+ */
142
+ server.tool('check_alerts', `Check watchlist for triggered alerts based on current data.`, {}, async () => {
143
+ let output = `## 🔔 Alert Check Results\n\n`;
144
+ if (watchlist.length === 0) {
145
+ output += 'No items in watchlist.';
146
+ return {
147
+ content: [{ type: 'text', text: output }],
148
+ };
149
+ }
150
+ const searchProvider = getSearchProvider();
151
+ let triggeredCount = 0;
152
+ for (const item of watchlist) {
153
+ let triggered = false;
154
+ let message = '';
155
+ // Check based on type
156
+ try {
157
+ if (item.type === 'news' || item.type === 'injury') {
158
+ const searchQuery = `${item.target} ${item.type === 'injury' ? 'injury news' : 'news'} today`;
159
+ const results = await searchProvider.search(searchQuery, undefined, 1);
160
+ if (results.length > 0) {
161
+ triggered = true;
162
+ message = `New ${item.type} found for ${item.target}`;
163
+ }
164
+ }
165
+ else if (item.type === 'odds') {
166
+ const searchQuery = `${item.target} odds`;
167
+ const results = await searchProvider.search(searchQuery, undefined, 1);
168
+ if (results.length > 0) {
169
+ // Would need to parse actual odds to check condition
170
+ triggered = true;
171
+ message = `Odds data available for ${item.target}`;
172
+ }
173
+ }
174
+ }
175
+ catch (error) {
176
+ // Continue checking other items
177
+ }
178
+ if (triggered) {
179
+ triggeredCount++;
180
+ output += `🔔 **${item.id}**: ${message}\n`;
181
+ }
182
+ }
183
+ if (triggeredCount === 0) {
184
+ output += 'No new alerts. All conditions normal.';
185
+ }
186
+ else {
187
+ output += `\n\n**Total Alerts:** ${triggeredCount}`;
188
+ }
189
+ return {
190
+ content: [{ type: 'text', text: output }],
191
+ };
192
+ });
193
+ /**
194
+ * Tool 6: transfer_news
195
+ * Get latest transfer rumors and confirmed deals
196
+ */
197
+ server.tool('transfer_news', `Get latest transfer rumors and confirmed deals.`, {
198
+ team: z.string().optional().describe('Filter by team'),
199
+ player: z.string().optional().describe('Filter by player'),
200
+ league: z.string().optional().describe('Filter by league'),
201
+ }, async ({ team, player, league }) => {
202
+ let searchQuery = 'football transfer news rumors';
203
+ if (team)
204
+ searchQuery += ` ${team}`;
205
+ if (player)
206
+ searchQuery += ` ${player}`;
207
+ if (league)
208
+ searchQuery += ` ${league}`;
209
+ searchQuery += ` ${new Date().toLocaleDateString('en-US', { month: 'long', year: 'numeric' })}`;
210
+ try {
211
+ const searchProvider = getSearchProvider();
212
+ const results = await searchProvider.search(searchQuery, undefined, 10);
213
+ let output = `## 💸 Transfer News\n\n`;
214
+ if (results.length > 0) {
215
+ const items = results.map(r => `- [${r.title}](${r.url}): ${r.snippet}`).join('\n\n');
216
+ output += items;
217
+ }
218
+ else {
219
+ output += 'No transfer news found.';
220
+ }
221
+ return {
222
+ content: [{ type: 'text', text: output }],
223
+ };
224
+ }
225
+ catch (error) {
226
+ return {
227
+ content: [{
228
+ type: 'text',
229
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`
230
+ }],
231
+ isError: true,
232
+ };
233
+ }
234
+ });
235
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * SPORTS MODULE TOOLS - Match Tools
3
+ * Tools for match analysis and live scores
4
+ */
5
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
6
+ export declare function registerMatchTools(server: McpServer): void;