# Directory Structure
```
├── .gitignore
├── .npmignore
├── package-lock.json
├── package.json
├── README.md
├── src
│ ├── constants.ts
│ ├── index.ts
│ ├── tools
│ │ ├── index.ts
│ │ └── match.ts
│ ├── types.ts
│ └── utils.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Build output
dist/
build/
# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
.DS_Store
# Environment variables
.env
.env.local
.env.*.local
# Logs
logs/
*.log
```
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
```
# Source files
src/
*.ts
# Development files
.git/
.gitignore
.npmignore
.editorconfig
.eslintrc
.prettierrc
tsconfig.json
# Test files
__tests__/
*.test.ts
*.spec.ts
# Documentation
docs/
*.md
!README.md
# Build files
dist/*.map
# IDE files
.idea/
.vscode/
*.swp
*.swo
# OS files
.DS_Store
Thumbs.db
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# TFT MCP Server
This is a Model Context Protocol (MCP) server for Team Fight Tactics (TFT) that provides access to TFT game data through various tools.
## Features
- Get match history for a summoner
- Get detailed information about specific TFT matches
## Prerequisites
- Node.js (v14 or higher)
- npm or yarn
- Riot Games API Key (for accessing TFT data) - Get it from [Riot Games Developer Portal](https://developer.riotgames.com/)
- Note: For development, you can use a temporary API key that expires in 24 hours
- For production use, you'll need to apply for a permanent personal API key at [Riot's Application Portal](https://developer.riotgames.com/app-type)
- Your Game Name, accessed from your Riot game console
- Your Name Tagline, accessed from your Riot game console, which is usually followed/shown right after your Game Name. For example: `NA1`
## Usage
1. Configure the MCP server in your Claude Desktop config file:
### MacOS
Location: `~/Library/Application Support/Claude/claude_desktop_config.json`
### Windows
Location: `%APPDATA%/Claude/claude_desktop_config.json`
Add the following configuration:
```json
{
"mcpServers": {
"tft-mcp": {
"command": "npx",
"args": [
"mcp-server-tft",
"--apiKey",
"<YOUR_RIOT_API_KEY>",
"--gameName",
"<YOUR_GAME_NAME>",
"--tagLine",
"<YOUR_TAG_LINE>"
]
}
}
}
```
2. The server will run on stdio and provide the following tools:
### tft_match_history
Get TFT match history for the current player.
Parameters:
- `count` (optional): Number of matches to retrieve. Defaults to 20
- `start` (optional): Start index for pagination. Defaults to 0
### tft_match_details
Get detailed information about a specific TFT match.
Parameters:
- `matchId` (required): The match ID to get details for
## Development
The project is written in TypeScript and uses the Model Context Protocol SDK. To modify the code:
1. Make changes in the `src` directory
2. Run `npm run build` to compile
3. Run `npm start` with the required parameters to test changes
## License
MIT
```
--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------
```typescript
export const RIOT_ACCOUNT_API =
"https://americas.api.riotgames.com/riot/account/v1";
export const RIOT_API_BASE = "https://americas.api.riotgames.com";
```
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
```typescript
export interface Arguments {
apiKey: string;
gameName: string;
tagLine: string;
[x: string]: unknown;
}
let _args: Arguments;
export function setArgs(newArgs: Arguments) {
_args = newArgs;
}
export function getArgs(): Arguments {
return _args;
}
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"target": "ES2020",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
```
--------------------------------------------------------------------------------
/src/tools/index.ts:
--------------------------------------------------------------------------------
```typescript
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
// Define tools using the Zod schemas
export const TFT_TOOLS: Tool[] = [
{
name: "tft_match_history",
description: "Get TFT match history for the current player",
inputSchema: {
type: "object",
properties: {
count: {
type: "number",
default: 20,
description: "Number of matches to retrieve (default: 20)"
},
start: {
type: "number",
default: 0,
description: "Start index (default: 0)"
}
}
}
},
{
name: "tft_match_details",
description: "Get detailed information about a specific TFT match",
inputSchema: {
type: "object",
properties: {
matchId: {
type: "string",
description: "The match ID to get details for"
}
},
required: ["matchId"]
}
}
];
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "mcp-server-tft",
"version": "0.1.3",
"description": "MCP Server for Team Fight Tactics (TFT)",
"main": "dist/index.js",
"type": "module",
"bin": {
"mcp-server-tft": "./dist/index.js"
},
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"prepublishOnly": "npm run build"
},
"keywords": [
"tft",
"riot",
"riot-games",
"mcp",
"model-context-protocol",
"game-data"
],
"author": "Your Name",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/GeLi2001/tft-mcp-server.git"
},
"bugs": {
"url": "https://github.com/GeLi2001/tft-mcp-server/issues"
},
"homepage": "https://github.com/GeLi2001/tft-mcp-server#readme",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.8.0",
"node-fetch": "^3.3.2",
"cheerio": "^1.0.0-rc.12",
"yargs": "^17.7.2",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/node": "^20.11.19",
"@types/yargs": "^17.0.32",
"typescript": "^5.3.3"
},
"engines": {
"node": ">=14.0.0"
}
}
```
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
```typescript
import fetch from "node-fetch";
import { RIOT_ACCOUNT_API } from "./constants.js";
import { getArgs } from "./types.js";
export let CURRENT_PUUID: string | null = null;
export async function fetchWithErrorHandling(url: string, options: any = {}) {
try {
const headers = {
"X-Riot-Token": getArgs().apiKey,
...options.headers
};
const response = await fetch(url, { ...options, headers });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response;
} catch (error) {
throw new Error(
`Failed to fetch: ${
error instanceof Error ? error.message : String(error)
}`
);
}
}
export async function initializePUUID() {
try {
const args = getArgs();
const url = `${RIOT_ACCOUNT_API}/accounts/by-riot-id/${encodeURIComponent(
args.gameName
)}/${encodeURIComponent(args.tagLine)}`;
const response = await fetchWithErrorHandling(url);
const data = (await response.json()) as { puuid: string };
CURRENT_PUUID = data.puuid;
} catch (error) {
console.error("Failed to initialize PUUID:", error);
process.exit(1);
}
}
```
--------------------------------------------------------------------------------
/src/tools/match.ts:
--------------------------------------------------------------------------------
```typescript
import { RIOT_API_BASE } from "../constants.js";
import { CURRENT_PUUID, fetchWithErrorHandling } from "../utils.js";
// Tool handlers
export async function handleTftMatchHistory(params: any) {
const { count = 20, start = 0 } = params;
const url = `${RIOT_API_BASE}/tft/match/v1/matches/by-puuid/${CURRENT_PUUID}/ids?start=${start}&count=${count}`;
try {
const response = await fetchWithErrorHandling(url);
const matchIds = await response.json();
return {
content: [
{
type: "text",
text: JSON.stringify({ matchIds }, null, 2)
}
],
isError: false
};
} catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify(
{
error: error instanceof Error ? error.message : String(error)
},
null,
2
)
}
],
isError: true
};
}
}
export async function handleTftMatchDetails(params: any) {
const { matchId } = params;
const url = `${RIOT_API_BASE}/tft/match/v1/matches/${matchId}`;
try {
const response = await fetchWithErrorHandling(url);
const data = await response.json();
return {
content: [
{
type: "text",
text: JSON.stringify(data, null, 2)
}
],
isError: false
};
} catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify(
{
error: error instanceof Error ? error.message : String(error)
},
null,
2
)
}
],
isError: true
};
}
}
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
#!/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 yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { TFT_TOOLS } from "./tools/index.js";
import { handleTftMatchDetails, handleTftMatchHistory } from "./tools/match.js";
import { setArgs } from "./types.js";
import { initializePUUID } from "./utils.js";
// Parse command line arguments
interface Arguments {
apiKey: string;
gameName: string;
tagLine: string;
[x: string]: unknown;
}
const getArgs = async (): Promise<Arguments> => {
return yargs(hideBin(process.argv))
.option("apiKey", {
alias: "k",
type: "string",
description: "Riot API Key",
demandOption: true
})
.option("gameName", {
alias: "n",
type: "string",
description: "Summoner Name",
demandOption: true
})
.option("tagLine", {
alias: "t",
type: "string",
description: "Name Tagline",
demandOption: true
})
.help()
.parseAsync();
};
// Server setup
const server = new Server(
{
name: "tft",
version: "0.1.0"
},
{
capabilities: {
tools: {}
}
}
);
// Set up request handlers
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: TFT_TOOLS
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
switch (request.params.name) {
case "tft_match_history": {
return await handleTftMatchHistory(request.params.arguments);
}
case "tft_match_details": {
return await handleTftMatchDetails(request.params.arguments);
}
default:
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${request.params.name}`
);
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${
error instanceof Error ? error.message : String(error)
}`
}
],
isError: true
};
}
});
// Modify runServer to initialize args and PUUID first
async function runServer() {
const parsedArgs = await getArgs();
setArgs(parsedArgs);
await initializePUUID();
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("TFT MCP Server running on stdio");
}
runServer().catch((error) => {
console.error("Fatal error running server:", error);
process.exit(1);
});
```