#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';
const API_KEY = process.env.FANTASYPROS_API_KEY;
if (!API_KEY) {
throw new Error('FANTASYPROS_API_KEY environment variable is required');
}
class FantasyProsServer {
server;
axiosInstance;
constructor() {
this.server = new Server({
name: 'fantasypros-server',
version: '0.1.0',
}, {
capabilities: {
tools: {},
},
});
this.axiosInstance = axios.create({
baseURL: 'https://api.fantasypros.com/public/v2',
headers: {
'x-api-key': API_KEY,
},
});
this.setupToolHandlers();
this.server.onerror = (error) => console.error('[MCP Error]', error);
process.on('SIGINT', async () => {
await this.server.close();
process.exit(0);
});
}
setupToolHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'get_sport_news',
description: 'Get news for a specific sport',
inputSchema: {
type: 'object',
properties: {
sport: {
type: 'string',
enum: ['nfl', 'mlb', 'nba', 'nhl'],
description: 'Sport to get news for',
},
limit: {
type: 'number',
description: 'Number of news items to return (max 25)',
minimum: 1,
maximum: 25,
},
category: {
type: 'string',
enum: ['injury', 'recap', 'transaction', 'rumor', 'breaking'],
description: 'Type of news to show',
},
},
required: ['sport'],
},
},
{
name: 'get_players',
description: 'Get player information for a specific sport',
inputSchema: {
type: 'object',
properties: {
sport: {
type: 'string',
enum: ['nfl', 'mlb', 'nba', 'nhl'],
description: 'Sport to get players for',
},
playerId: {
type: 'string',
description: 'Filter by specific player ID',
},
},
required: ['sport'],
},
},
{
name: 'get_rankings',
description: 'Get consensus rankings for a sport',
inputSchema: {
type: 'object',
properties: {
sport: {
type: 'string',
enum: ['nfl', 'nba'],
description: 'Sport to get rankings for',
},
position: {
type: 'string',
description: 'Position to filter by',
},
scoring: {
type: 'string',
enum: ['STD', 'PPR', 'HALF'],
description: 'Scoring type (for NFL)',
},
},
required: ['sport'],
},
},
{
name: 'get_projections',
description: 'Get player projections for a sport',
inputSchema: {
type: 'object',
properties: {
sport: {
type: 'string',
enum: ['nfl', 'mlb', 'nba'],
description: 'Sport to get projections for',
},
season: {
type: 'string',
description: 'Season year',
},
week: {
type: 'string',
description: 'Week number (for NFL)',
},
position: {
type: 'string',
description: 'Position to filter by',
},
},
required: ['sport', 'season'],
},
},
{
name: 'get_all_news',
description: 'Get all news from FantasyPros',
inputSchema: {
type: 'object',
properties: {
limit: {
type: 'number',
description: 'Number of news items to return (max 25)',
minimum: 1,
maximum: 25,
},
category: {
type: 'string',
enum: ['injury', 'recap', 'transaction', 'rumor', 'breaking'],
description: 'Type of news items to show',
},
},
},
},
],
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
switch (request.params.name) {
case 'get_sport_news':
return await this.getNews(request.params.arguments);
case 'get_players':
return await this.getPlayers(request.params.arguments);
case 'get_rankings':
return await this.getRankings(request.params.arguments);
case 'get_projections':
return await this.getProjections(request.params.arguments);
case 'get_all_news':
return await this.getAllNews(request.params.arguments);
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
}
}
catch (error) {
if (axios.isAxiosError(error)) {
return {
content: [
{
type: 'text',
text: `FantasyPros API error: ${error.response?.data?.message || error.message}`,
},
],
isError: true,
};
}
throw error;
}
});
}
async getNews(args) {
const { sport, limit = 25, category } = args;
const params = { limit };
if (category)
params.category = category;
const response = await this.axiosInstance.get(`/${sport}/news`, { params });
return {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
}
async getPlayers(args) {
const { sport, playerId } = args;
const params = {};
if (playerId)
params.player = playerId;
const response = await this.axiosInstance.get(`/${sport}/players`, { params });
return {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
}
async getRankings(args) {
const { sport, position = 'ALL', scoring = 'STD' } = args;
const season = new Date().getFullYear().toString();
const params = {
position,
scoring,
};
const response = await this.axiosInstance.get(`/${sport}/${season}/consensus-rankings`, { params });
return {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
}
async getProjections(args) {
const { sport, season, week, position } = args;
const params = {};
if (week)
params.week = week;
if (position)
params.position = position;
const response = await this.axiosInstance.get(`/${sport}/${season}/projections`, { params });
return {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
}
async getAllNews(args) {
const { limit = 25, category } = args;
const params = { limit };
if (category)
params.category = category;
const response = await this.axiosInstance.get('/json/all/news', { params });
return {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('FantasyPros MCP server running on stdio');
}
}
const server = new FantasyProsServer();
server.run().catch(console.error);