# Directory Structure
```
├── .editorconfig
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode
│ └── settings.json
├── .windsurfrules
├── Dockerfile
├── package.json
├── pnpm-lock.yaml
├── README.md
├── smithery.yaml
├── src
│ └── index.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
```
build
node_modules
.git
.DS_Store
```
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
```
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
[*.{js,ts,json}]
indent_style = space
indent_size = 2
```
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
```
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"bracketSpacing": true,
"arrowParens": "avoid",
"plugins": ["prettier-plugin-organize-imports"]
}
```
--------------------------------------------------------------------------------
/.windsurfrules:
--------------------------------------------------------------------------------
```
This project is an MCP (Model Context Protocol) server that communicates with the OP.GG Esports API. The server provides a tool to fetch upcoming League of Legends match schedules from OP.GG Esports and returns formatted match information including match name, league, status, score, and scheduled time.
When analyzing this project, focus on TypeScript patterns, error handling approaches, and GraphQL queries. The project uses node-fetch for API requests and follows a Promise-based async/await pattern throughout the codebase.
## Code Conventions for TypeScript
- Prefer interfaces over type aliases for object types
- Use readonly modifier for immutable properties
- Use const assertions for literal values
- Use mapped types and utility types when appropriate
- Avoid any, use unknown when type cannot be determined
- Organize imports alphabetically and by groups (built-in, external, internal)
- Use Arrow functions for callbacks
- Prefer template literals over string concatenation
- Use optional chaining and nullish coalescing where appropriate
- Avoid type assertions when possible
- Prefer function declarations over function expressions
- Use enums for values that represent a specific set of choices
## Documentation Guidelines
- Add descriptive headers to all code files with purpose and author information
- Each function should have a detailed comment block explaining its purpose, parameters, and return values
- Use JSDoc style comments for TypeScript code documentation
- Include examples in comments for complex logic or non-obvious implementations
- Document all public interfaces and exported functions thoroughly
- When writing complex algorithms, include comments explaining the approach
- Add section headers as comments to organize large files (e.g. "// === API Handlers ===")
- Use inline comments to explain "why" rather than "what" for non-obvious code
- Document edge cases and error handling strategies in comments
- Keep comments up-to-date when changing functionality
- Include versioning information in file headers when making significant changes
- Add TODO comments with ticket numbers for incomplete implementations
- Add context and background information for workarounds or unusual approaches
- Maintain a consistent commenting style throughout the codebase
- Use descriptive variable and function names to reduce the need for obvious comments
## Code Structure and Refactoring Guidelines
- Refactor files that exceed 200 lines of code into multiple files or classes
- Each class should have a single responsibility (follow the Single Responsibility Principle)
- Extract reusable logic into utility functions or service classes
- Group related functionality into modules or namespaces
- Use dependency injection for better testability and loose coupling
- Keep public APIs minimal - expose only what is necessary
- Consider breaking large functions (>50 lines) into smaller, more focused functions
- Prefer composition over inheritance when designing class relationships
- Use feature-based directory structure for complex applications
- Keep nesting to a minimum (maximum 3-4 levels of nesting)
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# OP.GG Esports MCP Server
[](https://smithery.ai/server/@opgginc/esports-mcp)
The OP.GG Esports MCP Server is a Model Context Protocol implementation that seamlessly connects OP.GG Esports data with AI agents and platforms. This server enables AI agents to retrieve upcoming League of Legends match schedules and information via function calling.
## Overview
This MCP server provides AI agents with access to OP.GG Esports data through a standardized interface. Built on TypeScript and Node.js, it connects directly to the OP.GG Esports GraphQL API and formats the data in a way that's easily consumable by AI models and agent frameworks.
## Features
The OP.GG Esports MCP Server currently supports the following tools:
- **get-lol-matches**: Fetch and format upcoming League of Legends match schedules from OP.GG Esports
- Returns match name, league, status, score, scheduled time, and a direct link to the match
- Formats the data in a clean, structured format for AI consumption
## Installation
### Installing via Smithery
To install OP.GG Esports MCP for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@opgginc/esports-mcp):
```bash
npx -y @smithery/cli install @opgginc/esports-mcp --client claude
```
### Using npm/pnpm
```bash
# Install dependencies
pnpm install
# Build the project
pnpm build
```
### Running the server
#### Using pnpm
```bash
# Start the MCP server on stdio
pnpm start
```
#### Using Node.js directly
```bash
# Start using Node.js
node dist/index.js
```
#### Using npx
```bash
# Run directly with npx
npx -y @opgg/esports-mcp
```
### Adding to MCP configuration
To add this server to your MCP configuration (e.g., Windsurf's mcp_config.json), add the following entry:
```json
{
"mcpServers": {
"opgg-esports": {
"command": "node",
"args": ["/path/to/esports-mcp/dist/index.js"]
}
}
}
```
Alternatively, you can use the npm package if published:
```json
{
"mcpServers": {
"opgg-esports": {
"command": "npx",
"args": ["-y", "@opgg/esports-mcp"]
}
}
}
```
## Usage
The OP.GG Esports MCP Server can be used with any MCP-compatible client. Here are some examples:
### Listing available tools
```json
{ "type": "list_tools" }
```
Response:
```json
{
"tools": [
{
"name": "get-lol-matches",
"description": "Get upcoming LoL match schedules from OP.GG Esports"
}
]
}
```
### Fetching upcoming match schedules
```json
{
"type": "tool_call",
"tool_call": {
"name": "get-lol-matches"
}
}
```
Response:
```json
{
"content": [
{
"type": "text",
"text": "Upcoming match schedules:\n\nMatch: Team A vs Team B\nLeague: LCK\nStatus: SCHEDULED\nScore: 0 - 0\nScheduled at: 4/6/2025, 7:00:00 PM\nDetails: https://esports.op.gg/matches/12345\n---\n..."
}
]
}
```
## License
This project is licensed under the MIT License - see the LICENSE file for details.
## Related Links
- [Model Context Protocol](https://modelcontextprotocol.com)
- [OP.GG Esports](https://esports.op.gg)
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"target": "ES2020",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"strict": true,
"outDir": "./build",
"rootDir": "./src",
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "build"]
}
```
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
```json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
}
```
--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------
```yaml
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
startCommand:
type: stdio
configSchema:
# JSON Schema defining the configuration options for the MCP.
{}
commandFunction:
# A JS function that produces the CLI command based on the given config to start the MCP on stdio.
|-
(config) => ({ command: 'node', args: ['build/index.js'] })
exampleConfig: {}
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
FROM node:lts-alpine
# Install pnpm globally
RUN npm install -g pnpm
# Set working directory
WORKDIR /app
# Copy package files and lockfile
COPY package.json pnpm-lock.yaml ./
# Install dependencies
RUN pnpm install --frozen-lockfile
# Copy the rest of the source code
COPY . .
# Build the project
RUN pnpm build
# Expose any required port if necessary (none specified in this MCP)
# Run the MCP server; entry point is build/index.js
CMD [ "node", "build/index.js" ]
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "opgg-mcp-esports",
"version": "1.0.0",
"description": "OP.GG Esports MCP Server - LoL Match Schedule Search Tool",
"main": "./build/index.js",
"type": "module",
"bin": {
"opgg-mcp-esports": "./build/index.js"
},
"scripts": {
"test": "npx @modelcontextprotocol/inspector node ./build/index.js",
"build": "tsc && chmod 755 build/index.js"
},
"files": [
"build"
],
"keywords": [
"opgg",
"esports",
"lol"
],
"author": "",
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.8.0",
"node-fetch": "^3.3.2"
},
"devDependencies": {
"@types/node": "^22.14.0",
"prettier": "^3.5.3",
"prettier-plugin-organize-imports": "^4.1.0",
"typescript": "^5.8.2"
}
}
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import fetch from 'node-fetch';
const GRAPHQL_ENDPOINT = 'https://esports.op.gg/matches/graphql';
const UPCOMING_MATCHES_QUERY = `
query MCPListUpcomingMatches {
upcomingMatches {
id
name
status
awayScore
homeScore
scheduledAt
numberOfGames
tournament {
serie {
league {
shortName
}
}
}
}
}
`;
async function fetchUpcomingMatches() {
try {
const response = await fetch(GRAPHQL_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': 'MCP',
},
body: JSON.stringify({
query: UPCOMING_MATCHES_QUERY,
}),
});
if (!response.ok) {
throw new Error(`API request failed: ${response.status} ${response.statusText}`);
}
const data = (await response.json()) as any;
if (data.errors) {
throw new Error(`GraphQL error: ${JSON.stringify(data.errors)}`);
}
return data.data.upcomingMatches;
} catch (error) {
console.error('Error calling OP.GG Esports API:', error);
throw error;
}
}
interface MatchInfo {
id: number;
name: string;
status: string;
homeScore: number;
awayScore: number;
scheduledAt: string | number | Date;
league: string;
numberOfGames?: number;
}
const server = new McpServer({
name: 'opgg-mcp-esports',
version: '1.0.0',
});
server.tool('get-lol-matches', 'Get upcoming LoL match schedules from OP.GG Esports', async () => {
try {
// Fetch match schedules
const matches = await fetchUpcomingMatches();
// Format results
const formattedMatches = matches.map((match: any): MatchInfo => {
const league = match.tournament?.serie?.league || {};
return {
id: match.id,
name: match.name,
status: match.status?.toUpperCase(),
awayScore: match.awayScore,
homeScore: match.homeScore,
scheduledAt: match.scheduledAt,
numberOfGames: match.numberOfGames,
league: league.shortName || 'Unknown',
};
});
return {
content: [
{
type: 'text',
text: `Upcoming match schedules:\n\n${formattedMatches
.map(
(match: MatchInfo) =>
`Match: ${match.name}\nLeague: ${match.league}\nStatus: ${match.status}\nScore: ${match.homeScore} - ${match.awayScore}\nScheduled at: ${new Date(match.scheduledAt).toLocaleString()}\nDetails: https://esports.op.gg/matches/${match.id}\n---`
)
.join('\n')}`,
},
],
};
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error fetching match schedules: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch(error => {
console.error('Fatal error in main():', error);
process.exit(1);
});
```