Metadata-Version: 2.5
Name: lorehub-mcp
Version: 0.1.0
Summary: MCP server that lets AI agents read the Meta Areas of a LoreHub dialogue
Project-URL: Homepage, https://lorehub.app
Project-URL: Documentation, https://lorehub.app/mcp
Author: Lostwood Games
License-Expression: MIT
License-File: LICENSE
Keywords: ai-agents,dialogue,game-development,lorehub,mcp,model-context-protocol,narrative
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Games/Entertainment
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: mcp<3,>=2.3
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Description-Content-Type: text/markdown

# LoreHub MCP

<!-- mcp-name: io.github.lostwood-games/lorehub-mcp -->

An MCP server that lets AI agents (Claude, Cursor and any other MCP client) read the **Meta Areas** of a [LoreHub](https://lorehub.app) dialogue.

A director draws Meta Areas on the Dialogue Builder canvas: a shot, a location, a mood, with a note, a priority and meta rules such as `shot = close_up`. With this server, your agent reads those areas and the nodes inside them, and turns them into references, shot lists or mood boards. It only reads: it never changes your world.

More on the [LoreHub MCP page](https://lorehub.app/mcp) and in the [Meta Areas guide](https://lorehub.app/meta-areas-guide).

## Install

You need a LoreHub account with at least Viewer access to the world, and [uv](https://docs.astral.sh/uv/getting-started/installation/). Nothing else to install: `uvx` downloads and runs the server.

```bash
uvx lorehub-mcp
```

Or with pip: `pip install lorehub-mcp`, then run `lorehub-mcp`.

### Claude Code

```bash
claude mcp add lorehub --env LOREHUB_LOGIN=you@example.com --env LOREHUB_PASSWORD=your-password -- uvx lorehub-mcp
```

### Claude Desktop

Settings → Developer → Edit Config, then add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "lorehub": {
      "command": "uvx",
      "args": ["lorehub-mcp"],
      "env": {
        "LOREHUB_LOGIN": "you@example.com",
        "LOREHUB_PASSWORD": "your-password"
      }
    }
  }
}
```

### Cursor

Add the same block to `~/.cursor/mcp.json` (all projects) or `.cursor/mcp.json` (one project):

```json
{
  "mcpServers": {
    "lorehub": {
      "command": "uvx",
      "args": ["lorehub-mcp"],
      "env": {
        "LOREHUB_LOGIN": "you@example.com",
        "LOREHUB_PASSWORD": "your-password"
      }
    }
  }
}
```

Other MCP clients work the same way: run `uvx lorehub-mcp` over stdio with the two environment variables.

## Find your world and dialogue ids

You don't need to look them up yourself. Ask the agent, for example *"List my LoreHub worlds, then find the dialogue Tom at Grandpa's and give me its Meta Areas"*, and it calls `list_worlds`, `list_dialogues` and `get_dialogue_meta` in turn.

To copy them by hand: open the dialogue in the Dialogue Builder. The address looks like `app.lorehub.app/<owner>/<world>/game/<story id>?dialogue=<dialogue id>`, so the dialogue id is after `?dialogue=`. The world id comes from `list_worlds`.

## Tools

| Tool | What it returns |
| --- | --- |
| `list_worlds()` | The worlds your account can open, with their ids and your role. |
| `list_dialogues(world_id, game_id?, name?)` | Stories and their dialogues with ids. `name` filters by dialogue name. |
| `get_dialogue_meta(world_id, dialogue_id, area?, include_nodes=true)` | Each Meta Area's title, note, priority and meta rules as `field name: value`, the nodes inside it, and the nodes where areas overlap (higher priority first). `area` filters by title text or area id. |

Example output of `get_dialogue_meta`:

```json
{
  "dialogue": "Tom at Grandpa's",
  "dialogue_id": "…",
  "areas": [
    {"title": "Grandpa's house: wide", "priority": 1,
     "rules": {"shot": "wide", "background": "grandpas_house"},
     "nodes": ["dlg.n_01", "dlg.n_04"], "id": "…"},
    {"title": "Close-up: Tom is crying", "note": "Tears, low light", "priority": 2,
     "rules": {"shot": "close_up", "background": "grandpas_house", "tom_emotion": "crying"},
     "nodes": ["dlg.n_04"], "id": "…"}
  ],
  "overlaps": [{"node": "dlg.n_04", "areas": ["Close-up: Tom is crying (P2)", "Grandpa's house: wide (P1)"]}]
}
```

## Configuration

| Variable | Default | |
| --- | --- | --- |
| `LOREHUB_LOGIN`, `LOREHUB_PASSWORD` | | Your LoreHub user name or email and password. The server signs in, and signs in again when the token expires. |
| `LOREHUB_TOKEN` | | A LoreHub JWT instead of a login and password. It lasts 8 hours. |
| `LOREHUB_API_URL` | `https://api.lorehub.app` | |
| `LOREHUB_TIMEOUT` | `30` | Seconds per request. |
| `LOREHUB_LOG_LEVEL` | `INFO` | Logs go to stderr. |
| `LOREHUB_NODE_WIDTH`, `LOREHUB_NODE_HEADER_HEIGHT` | `200`, `32` | The canvas node card size. Leave them as they are. |

## How it works

The server makes read-only GET calls to the LoreHub API with your account's token: the dialogue's Meta Areas, its name and story, the story's meta schema (to turn rule ids into field names) and the node positions. A node is inside an area when the middle of its header is, the same rule the Dialogue Builder uses.

Not yet: nodes come back as full ids, without their line text or speaker.

## License

MIT
