Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions knowledge_base/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,60 @@ def read_knowledge_file(filename: str) -> dict:
return {"filename": filename, "content": path.read_text()}


# Maximum size for a single knowledge-base file. Generous enough for full reference docs,
# small enough that nothing here is being used as bulk storage.
_MAX_FILE_SIZE_BYTES = 200_000


@register_mcp_tool()
def write_knowledge_file(filename: str, content: str, mode: str = "overwrite") -> dict:
"""Create or update a knowledge-base file. Use this to record durable, reactor-specific facts (cluster layout, calibration history, hardware quirks, hard-won workarounds) that future sessions should be able to find via list_knowledge_files. Filename must end in .md and contain only the basename (no path separators). Content should start with YAML frontmatter including a `description:` field — that field is what list_knowledge_files surfaces, so make it specific. Mode 'overwrite' replaces existing content; 'append' adds to the end.

Do NOT use this for transient session notes, secrets, generic Pioreactor docs (those belong in the skill or upstream docs), or anything that's not durably useful for THIS reactor."""
if not filename.endswith(".md"):
return {"error": "Filename must end in .md"}
if ".." in filename or "/" in filename or "\\" in filename:
return {"error": "Invalid filename — basename only, no path separators"}
if not filename.replace("-", "").replace("_", "").replace(".", "").isalnum():
return {"error": "Filename should contain only letters, digits, hyphens, underscores, and the .md extension"}
if mode not in ("overwrite", "append"):
return {"error": "Mode must be 'overwrite' or 'append'"}
if len(content.encode("utf-8")) > _MAX_FILE_SIZE_BYTES:
return {"error": f"Content exceeds max size ({_MAX_FILE_SIZE_BYTES} bytes); split into multiple files"}

KNOWLEDGE_DIR.mkdir(parents=True, exist_ok=True)
path = KNOWLEDGE_DIR / filename

existed = path.exists()
if mode == "append" and existed:
existing = path.read_text()
# Ensure exactly one blank line separator
sep = "" if existing.endswith("\n\n") else ("\n" if existing.endswith("\n") else "\n\n")
new_content = existing + sep + content
if len(new_content.encode("utf-8")) > _MAX_FILE_SIZE_BYTES:
return {"error": f"Appended content would exceed max size ({_MAX_FILE_SIZE_BYTES} bytes)"}
path.write_text(new_content)
else:
path.write_text(content)

return {
"filename": filename,
"action": ("appended_to" if mode == "append" and existed else ("updated" if existed else "created")),
"size_bytes": path.stat().st_size,
}


@register_mcp_tool()
def delete_knowledge_file(filename: str) -> dict:
"""Delete a knowledge-base file. Use sparingly — only when the file is genuinely obsolete (e.g. it described hardware that's been removed, or its content has been merged into another file). For corrections, prefer write_knowledge_file with mode='overwrite'."""
if not filename.endswith(".md"):
return {"error": "Filename must end in .md"}
if ".." in filename or "/" in filename or "\\" in filename:
return {"error": "Invalid filename"}
path = KNOWLEDGE_DIR / filename
if not path.exists():
return {"error": f"File {filename} not found"}
path.unlink()
return {"filename": filename, "action": "deleted"}


2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

setup(
name="pioreactor-knowledge-base",
version="0.1.0",
version="0.2.0",
license="MIT",
description="MCP tools for accessing domain knowledge files on a Pioreactor",
long_description=open("README.md").read(),
Expand Down