Skip to main content
Glama
README.md
<div align="center">

# KotorMCP

[![Version](https://img.shields.io/github/v/release/th3w1zard1/PyKotor?label=KotorMCP)](https://github.com/OpenKotOR/PyKotor/releases)
[![License](https://img.shields.io/github/license/th3w1zard1/PyKotor)](https://github.com/OpenKotOR/PyKotor/blob/main/LICENSE)
[![Python](https://img.shields.io/badge/python-3.8%2B-blue)](https://www.python.org/downloads/)
[![MCP](https://img.shields.io/badge/MCP-Protocol-blueviolet)](https://modelcontextprotocol.io)

**Model Context Protocol server for Knights of the Old Republic game resources**

</div>

A Model Context Protocol (MCP) server that exposes context-rich tools for AI agents to interact with Knights of the Old Republic (KOTOR) and Knights of the Old Republic II: The Sith Lords (TSL) game installations. This server provides intelligent resource discovery, installation management, and game data inspection capabilities tailored for automated analysis and debugging workflows.

## Features

- **Installation Detection & Management**
  - Automatic detection of KOTOR 1 and KOTOR 2 installations
  - Environment variable support (`K1_PATH`, `K2_PATH`, `TSL_PATH`, etc.)
  - Default path discovery (Windows registry, common install locations)
  - Installation caching for performance

- **Resource Discovery & Inspection**
  - List resources from all locations (override, modules, chitin, streams)
  - Filter by resource type, name patterns, and module scope
  - Deep resource summarization (GFF structures, 2DA tables, TLK strings)
  - Resource metadata extraction (size, location, type)

- **Journal & Plot Analysis**
  - Comprehensive journal entry overview (`global.jrl`)
  - Plot category organization
  - Quest entry enumeration
  - Cross-references with game scripts and dialogs

- **AI-Optimized Workflows**
  - Context-rich responses designed for LLM consumption
  - Structured JSON output for programmatic access
  - Efficient resource scanning with configurable limits
  - Installation-aware resource resolution

## Installation Guide

### Prerequisites

- Python 3.8+
- A valid KOTOR 1 or KOTOR 2 installation
- MCP-compatible client (Claude Desktop, Cursor, etc.)

### Quick Start

#### Using uv (Recommended)

```bash
# End users: run with --refresh for latest (no install needed)
uvx --refresh kotormcp

# Developers: run from local source with --with-editable
uvx --with-editable Libraries/PyKotor --with-editable Tools/KotorMCP kotormcp
uv run --directory Tools/KotorMCP/src --module kotormcp

# Or install editable
uv pip install -e Tools/KotorMCP
```

#### Using pip

```bash
pip install kotormcp
# Or from source: pip install -e Tools/KotorMCP
```

### Configuration

#### Claude Desktop

Add the following to your Claude Desktop configuration file:

**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`  
**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`  
**Linux:** `~/.config/Claude/claude_desktop_config.json`

```json
{
  "mcpServers": {
    "kotormcp": {
      "command": "uvx",
      "args": [
        "--refresh",
        "--from",
        "kotormcp @ git+https://github.com/th3w1zard1/KotorMCP.git",
        "kotormcp"
      ],
      "env": {
        "K1_PATH": "C:\\Program Files\\steamapps\\common\\swkotor",
        "K2_PATH": "C:\\Program Files\\steamapps\\common\\Knights of the Old Republic II"
      }
    }
  }
}
```

#### Cursor / VS Code (Claude Dev Extension)

Add to your MCP configuration:

```json
{
  "mcpServers": {
    "kotormcp": {
      "command": "uvx",
      "args": [
        "--refresh",
        "--from",
        "kotormcp @ git+https://github.com/th3w1zard1/KotorMCP.git",
        "kotormcp"
      ],
      "env": {
        "K1_PATH": "C:\\Program Files\\steamapps\\common\\swkotor",
        "K2_PATH": "C:\\Program Files\\steamapps\\common\\Knights of the Old Republic II"
      }
    }
  }
}
```

#### Environment Variables

The server automatically detects installations using these environment variables (in order of precedence):

**KOTOR 1:**

- `K1_PATH`
- `KOTOR_PATH`
- `KOTOR1_PATH`

**KOTOR 2:**

- `K2_PATH`
- `TSL_PATH`
- `KOTOR2_PATH`
- `K1_PATH` (fallback)

If environment variables are not set, the server will attempt to find installations using default paths (Windows registry, common install locations).

## Usage Guide

The server provides a growing set of installation and discovery tools for interacting with KOTOR installations:

### 1. `detectInstallations`

Detect available KOTOR installations and their paths.

**Parameters:** None

**Response:**

```json
{
  "K1": [
    {
      "path": "C:\\Program Files\\LucasArts\\SWKotOR",
      "exists": true,
      "label": "default"
    }
  ],
  "K2": [
    {
      "path": "C:\\Program Files\\LucasArts\\SWKotOR2",
      "exists": true,
      "label": "env"
    }
  ]
}
```

**Example:**

```typescript
use_mcp_tool({
  server_name: "kotormcp",
  tool_name: "detectInstallations",
  arguments: {}
})
```

### 2. `loadInstallation`

Load and cache a KOTOR installation for subsequent operations.

**Parameters:**

- `game` (string, required): Game identifier (`"k1"`, `"k2"`, `"tsl"`, `"kotori"`, `"kotor2"`)
- `path` (string, optional): Explicit installation path (overrides environment variables)

**Response:**

```json
{
  "game": "K1",
  "path": "C:\\Program Files\\LucasArts\\SWKotOR"
}
```

**Example:**

```typescript
use_mcp_tool({
  server_name: "kotormcp",
  tool_name: "loadInstallation",
  arguments: {
    game: "k1",
    path: "C:\\Program Files\\LucasArts\\SWKotOR"
  }
})
```

### `openInstallation`

Build or reuse a compacted in-memory installation snapshot and return a snapshot handle for follow-up queries.

**Parameters:**

- `game` (string, required): Game identifier (`"k1"`, `"k2"`, `"tsl"`, `"kotori"`, `"kotor2"`)
- `path` (string, optional): Explicit installation path (overrides environment variables)
- `refresh` (boolean, optional): Force the snapshot to rebuild instead of reusing the cached snapshot for the same game/path

**Response:**

```json
{
  "snapshotId": "2d6bf3f5108b4f498cb0d7147d6e31b9",
  "cached": false,
  "game": "K1",
  "path": "C:\\Program Files\\LucasArts\\SWKotOR",
  "policy": "default",
  "resourceCount": 84217,
  "omittedPayloadCount": 19042
}
```

### `getInstallationSnapshot`

Page through a snapshot handle returned by `openInstallation` and optionally include compacted per-resource documents.

**Parameters:**

- `snapshotId` (string, required): Snapshot handle returned by `openInstallation`
- `resourceTypes` (array of strings, optional): Filter by resource type (for example `NSS`, `DLG`, `TLK`)
- `resrefQuery` (string, optional): Filter by resref or filename substring
- `sourceQuery` (string, optional): Filter by source or container path substring
- `includeData` (boolean, optional): Return compacted per-resource documents instead of metadata-only summaries
- `limit` (number, optional): Maximum number of results (default: 50)
- `offset` (number, optional): Skip the first N filtered results (default: 0)

**Response:**

```json
{
  "snapshotId": "2d6bf3f5108b4f498cb0d7147d6e31b9",
  "total": 1,
  "offset": 0,
  "limit": 50,
  "nextOffset": null,
  "includeData": true,
  "items": [
    {
      "resource": "hello.nss",
      "restype": "NSS",
      "encoding": "text",
      "data": "void main() {}\n"
    }
  ]
}
```

### `getInstallationGraph`

Page through canonical dependency edges extracted from a snapshot handle returned by `openInstallation`.

**Parameters:**

- `snapshotId` (string, required): Snapshot handle returned by `openInstallation`
- `edgeKinds` (array of strings, optional): Filter by edge kind such as `script`, `conversation`, or `template_resref`
- `targetTypes` (array of strings, optional): Filter by target resource type such as `NSS` or `DLG`
- `query` (string, optional): Filter by target name, source resource, or field path substring
- `sourceQuery` (string, optional): Filter by source document path or resource path substring
- `limit` (number, optional): Maximum number of results (default: 50)
- `offset` (number, optional): Skip the first N filtered results (default: 0)

**Response:**

```json
{
  "snapshotId": "2d6bf3f5108b4f498cb0d7147d6e31b9",
  "total": 3,
  "offset": 0,
  "limit": 50,
  "nextOffset": null,
  "items": [
    {
      "sourceDocumentPath": "Override/fixture.utp.json",
      "edgeKind": "script",
      "targetName": "open_script",
      "targetRestypes": ["NCS", "NSS"],
      "targetResolved": true,
      "targetDocumentPaths": ["Override/open_script.nss.json"],
      "fieldPath": "OnOpen"
    }
  ]
}
```

### 3. `listResources`

List resources from the active installation with filtering options.

**Parameters:**

- `game` (string, required): Game identifier
- `location` (string, optional): Resource location filter (`"all"`, `"override"`, `"modules"`, `"chitin"`, `"streams"`) - default: `"all"`
- `moduleFilter` (string, optional): Filter by module name (e.g., `"001ebo"`)
- `resourceTypes` (string, optional): Comma-separated resource type extensions (e.g., `"gff,dlg,jrl"`)
- `resrefQuery` (string, optional): Filter resources by name pattern (case-insensitive)
- `limit` (number, optional): Maximum number of results (default: 50)

**Response:**

```json
{
  "count": 25,
  "items": [
    {
      "resref": "global",
      "restype": "JRL",
      "source": "override",
      "size": 12345,
      "module": null
    }
  ],
  "truncated": false
}
```

**Example:**

```typescript
use_mcp_tool({
  server_name: "kotormcp",
  tool_name: "listResources",
  arguments: {
    game: "k1",
    location: "override",
    resourceTypes: "jrl,gff",
    resrefQuery: "global",
    limit: 10
  }
})
```

### 4. `describeResource`

Get detailed information about a specific resource, including structured data for GFF files, 2DA tables, and TLK files.

**Parameters:**

- `game` (string, required): Game identifier
- `resref` (string, required): Resource name (without extension)
- `restype` (string, required): Resource type extension (e.g., `"jrl"`, `"gff"`, `"2da"`, `"tlk"`)
- `order` (array, optional): Search location priority order (default: `["override", "custom_folders", "modules", "chitin"]`)

**Response:**

```json
{
  "resref": "global",
  "restype": "JRL",
  "source": "override",
  "size": 12345,
  "summary": {
    "type": "JRL",
    "categories": 5,
    "entries": 42,
    "structure": "..."
  }
}
```

**Example:**

```typescript
use_mcp_tool({
  server_name: "kotormcp",
  tool_name: "describeResource",
  arguments: {
    game: "k1",
    resref: "global",
    restype: "jrl"
  }
})
```

### 5. `journalOverview`

Get a comprehensive overview of journal entries and plot categories from `global.jrl`.

**Parameters:**

- `game` (string, required): Game identifier

**Response:**

```json
{
  "count": 5,
  "categories": [
    {
      "id": 0,
      "name": "Main Quest",
      "entries": [
        {
          "id": 0,
          "title": "Escape from Taris",
          "text": "You must escape from the planet Taris..."
        }
      ]
    }
  ]
}
```

**Example:**

```typescript
use_mcp_tool({
  server_name: "kotormcp",
  tool_name: "journalOverview",
  arguments: {
    game: "k1"
  }
})
```

## Common Workflows

### Finding All Resources That Modify Plot Points

```typescript
// 1. Load installation
await loadInstallation({ game: "k1" });

// 2. List all journal-related resources
const resources = await listResources({
  game: "k1",
  resourceTypes: "jrl,gff,dlg",
  resrefQuery: "global",
  limit: 100
});

// 3. Get journal overview
const journal = await journalOverview({ game: "k1" });

// 4. Search for scripts that reference plot points
const scripts = await listResources({
  game: "k1",
  resourceTypes: "ncs",
  resrefQuery: "plot",
  limit: 50
});
```

### Investigating Module Structure

```typescript
// 1. List all resources in a specific module
const moduleResources = await listResources({
  game: "k1",
  location: "modules",
  moduleFilter: "001ebo",
  limit: 200
});

// 2. Get detailed information about key resources
const area = await describeResource({
  game: "k1",
  resref: "001ebo",
  restype: "are"
});

const git = await describeResource({
  game: "k1",
  resref: "001ebo",
  restype: "git"
});
```

### Debugging Missing Resources

```typescript
// 1. Check all locations for a resource
const resource = await describeResource({
  game: "k1",
  resref: "missing_resource",
  restype: "gff",
  order: ["override", "modules", "chitin", "streams"]
});

// 2. List similar resources
const similar = await listResources({
  game: "k1",
  resrefQuery: "missing",
  limit: 20
});
```

## Architecture

KotorMCP is built on the [Model Context Protocol](https://modelcontextprotocol.io) specification and uses the official Python MCP SDK. The server:

- **Caches installations** for performance across multiple tool calls
- **Resolves resources** using the same logic as `PyKotorCLI` and PyKotor's `Installation` class
- **Provides structured summaries** optimized for LLM consumption
- **Follows established patterns** from engine reimplementations (reone, xoreos, kotor.js)

### Implementation Notes

- Resource scanning logic mirrors `scripts/investigate_module_structure.py`
- Journal summarization follows the structure documented in `vendor/xoreos/src/engines/nwn2/journal.cpp`
- Resource resolution uses PyKotor's `Installation` class with configurable search order
- GFF structure summarization provides hierarchical field overviews

## Development

### Building from Source

```bash
# Clone the repository
git clone https://github.com/OpenKotOR/PyKotor.git
cd PyKotor

# Install dependencies
uv pip install -e Tools/KotorMCP

# Run the server
python -m kotormcp.server
```

### Project Structure

```
Tools/KotorMCP/
├── src/
│   └── kotormcp/
│       ├── __init__.py
│       └── server.py          # Main MCP server implementation
├── pyproject.toml             # Project metadata and dependencies
├── requirements.txt            # Pip-compatible requirements
└── README.md                  # This file
```

### Dependencies

- `mcp>=0.1.1` - Model Context Protocol Python SDK
- `pykotor>=2.3.2` - PyKotor core library for KOTOR file format support

## Contributing

Contributions are welcome! Please:

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Submit a pull request

For bug reports or feature requests, please open an issue on [GitHub](https://github.com/OpenKotOR/PyKotor/issues).

## License

This project is part of the PyKotor ecosystem and is licensed under the LGPL-3.0-or-later License. See the main repository [LICENSE](https://github.com/OpenKotOR/PyKotor/blob/main/LICENSE) file for details.

## Related Projects

- **[PyKotor](https://github.com/OpenKotOR/PyKotor)** - Core library for KOTOR file formats
- **[PyKotorCLI](https://github.com/OpenKotOR/PyKotor/tree/main/Tools/PyKotorCLI)** - Command-line interface for KOTOR resources
- **[Holocron Toolset](https://github.com/OpenKotOR/PyKotor/tree/main/Tools/HolocronToolset)** - GUI editor for KOTOR files

## Acknowledgments

- Built on the [Model Context Protocol](https://modelcontextprotocol.io) specification
- Uses the [PyKotor](https://github.com/OpenKotOR/PyKotor) library for game file format support
- Inspired by engine reimplementations: [reone](https://github.com/reone-project/reone), [xoreos](https://github.com/xoreos/xoreos), [kotor.js](https://github.com/jakubg1/kotor.js)

---

<div align="center">

**Made with ❤️ for the KOTOR modding community**

[Report Bug](https://github.com/OpenKotOR/PyKotor/issues) · [Request Feature](https://github.com/OpenKotOR/PyKotor/issues) · [Documentation](https://github.com/OpenKotOR/PyKotor/wiki)

</div>

TDQS

A3.6/5.0

Scored across 28 tools

Disambiguation4/5

Most tools have distinct purposes, targeting specific resource types or actions. Some overlap exists between describe and list tools (e.g., kotor_describe_resource_refs vs kotor_list_references) but descriptions help differentiate. Overall clear boundaries.

Naming Consistency3/5

Majority use 'kotor_' prefix with snake_case verb_noun pattern, but several tools (detectInstallations, listResources, etc.) lack the prefix, breaking consistency. The pattern is still readable but uneven.

Tool Count3/5

28 tools is on the high side for a single server, but justified by the complexity of KotOR modding/analysis. Some tools could be merged (e.g., read vs describe), but still within reasonable bounds.

Completeness4/5

The tool surface covers installation management, resource reading/searching, module analysis, and reference tracing comprehensively. Missing are write/modify tools (except extract), but the server appears read-only focused. Minor gaps like batch operations.

Maintenance

ActivityStale
ResponsivenessSyncing