#
tokens: 3084/50000 11/11 files
lines: off (toggle) GitHub
raw markdown copy
# 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);
});

```