MCP Server API¶
The MCP server module exposes corpus search as tools for LLM clients via the Model Context Protocol (MCP).
Overview¶
The MCP server allows LLMs like Claude to search the IF Craft Corpus directly during conversations. It provides tools for searching, browsing, and retrieving craft guidance.
Quick Start¶
Running the Server¶
# Using uvx (recommended)
uvx ifcraftcorpus-mcp
# Using Python directly
python -m ifcraftcorpus.mcp_server
Claude Desktop Configuration¶
Add to your Claude Desktop config file (claude_desktop_config.json):
Verbose Logging¶
Set LOG_LEVEL (for example INFO, DEBUG) or VERBOSE=1 before
launching the MCP server to emit detailed logs to stderr without
interfering with stdio transport. The same variables work for the CLI
(ifcraftcorpus) and the Docker image.
Available Tools¶
search_corpus¶
Search the IF Craft Corpus for writing guidance.
@mcp.tool
def search_corpus(
query: str,
cluster: str | None = None,
limit: int = 5,
) -> list[dict]
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
query |
str |
required | Search query describing what guidance you need |
cluster |
str \| None |
None |
Topic cluster to filter by |
limit |
int |
5 |
Maximum results (1-20) |
Valid Clusters:
narrative-structureprose-and-languagegenre-conventionsaudience-and-accessworld-and-settingemotional-designscope-and-planningcraft-foundationsagent-designgame-design
Returns: List of dicts with keys: source, title, cluster, content, topics.
Example Queries:
"dialogue subtext""branching narrative""pacing action scenes""horror atmosphere"
get_document¶
Get a specific document from the corpus by name.
Parameters:
| Parameter | Type | Description |
|---|---|---|
name |
str |
Document name (e.g., "dialogue_craft") |
Returns: Full document with title, summary, cluster, topics, and all sections. Returns None if not found.
list_documents¶
List all documents in the corpus.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
cluster |
str \| None |
None |
Optional cluster to filter by |
Returns: List of dicts with keys: name, title, cluster, topics.
list_clusters¶
List all topic clusters with document counts.
Returns: List of dicts with keys: name, document_count.
corpus_stats¶
Get statistics about the corpus.
Returns: Dict with keys:
document_count- Total number of documentscluster_count- Number of clustersclusters- List of cluster namessemantic_search_available- Whether embeddings are loaded
Functions¶
run_server¶
def run_server(
transport: Literal["stdio", "http"] = "stdio",
host: str = "127.0.0.1",
port: int = 8000,
) -> None
Run the MCP server with the specified transport.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
transport |
str |
"stdio" |
Transport protocol ("stdio" or "http") |
host |
str |
"127.0.0.1" |
Host for HTTP transport |
port |
int |
8000 |
Port for HTTP transport |
Example:
from ifcraftcorpus.mcp_server import run_server
# Run with stdio (default, for CLI clients)
run_server()
# Run as HTTP server
run_server(transport="http", host="0.0.0.0", port=8080)
get_corpus¶
Get or create the global Corpus instance used by the server.
Returns: The shared Corpus instance.
Server Configuration¶
The FastMCP Instance¶
from fastmcp import FastMCP
mcp = FastMCP(
name="IF Craft Corpus",
instructions="""
This server provides access to the Interactive Fiction Craft Corpus,
a curated knowledge base for writing interactive fiction. Use the tools
to search for craft guidance on topics like narrative structure, dialogue,
branching, prose style, and genre conventions.
""",
)
HTTP Transport¶
For web-based integrations, run the server in HTTP mode:
# Start HTTP server
python -c "from ifcraftcorpus.mcp_server import run_server; run_server(transport='http', port=8080)"
The server will be available at http://localhost:8080.
Programmatic Usage¶
You can also use the MCP tools programmatically:
from ifcraftcorpus.mcp_server import search_corpus, get_document, list_clusters
# Search for content
results = search_corpus("dialogue techniques", limit=3)
for r in results:
print(f"{r['source']}: {r['content'][:100]}...")
# Get a specific document
doc = get_document("dialogue_craft")
if doc:
print(doc["title"])
# List clusters
clusters = list_clusters()
for c in clusters:
print(f"{c['name']}: {c['document_count']} docs")