[project] name = "world-model-mcp" version = "0.16.3" description = "MCP world-model memory server for coding agents; ships the etch-verify offline audit-chain verifier CLI" authors = [ {name = "SaravananJaichandar"} ] readme = "README.md" requires-python = ">=3.11" license = {text = "MIT"} keywords = [ "mcp", "ai", "code-assistant", "knowledge-graph", "claude", "audit-log", "audit-chain", "verifier", "signed-audit", "etch-verify", ] classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", ] dependencies = [ # mcp: pinned to <1.29.0 (bumped 2026-07-30) after 1.29.0 and 2.0.0 # both landed on 2026-07-28 and dropped the `Server.list_tools()` # decorator API this server uses at world_model_server/server.py:58. # CI failed on a fresh 2.0.0 install with: # AttributeError: 'Server' object has no attribute 'list_tools' # Bump this pin deliberately in a follow-up PR after migrating # server.py to the newer SDK's tool-registration surface (likely # FastMCP or an explicit register-tool call). Current server code # verified working against mcp 1.25.0 locally. "mcp>=1.0.0,<1.29.0", "anthropic>=0.40.0", "pydantic>=2.0.0", "python-dotenv>=1.0.0", "aiosqlite>=0.19.0", "rich>=13.0.0", # v0.13 tamper-evident audit log signing. # Ed25519 (via cryptography) + SLH-DSA-SHA2-128f-simple (via # liboqs-python, which reads the same PQClean C reference implementation # that the pqclean npm TypeScript verifier reads — so signatures # cross-verify byte-for-byte across languages). # # Requires liboqs installed as a system dependency: # macOS: brew install liboqs # Debian/Ubuntu: apt install liboqs-dev "cryptography>=42.0.0", "liboqs-python>=0.15.0", # v0.15.5: streaming JSON parser for etch-verify. Verifiers on # chains large enough to OOM json.load (verified 2026-07-24 on a # 760MB manifest exceeding 3GB after Python dict expansion) parse # via ijson so memory usage stays O(single row + compact row-hash # lookup) instead of O(manifest size). Ships a pure-Python # fallback backend so no system deps are required. "ijson>=3.2", ] [project.optional-dependencies] dev = [ "pytest>=8.0.0", "pytest-asyncio>=0.23.0", # v0.15 test-infra: coverage reporting, property-based testing, hang # detection, and order randomization. Added ahead of the pin_annotation # feature landing so its test surface (unit / integration / security / # e2e / property-based) has the right instrumentation from day one. "pytest-cov>=5.0.0", "hypothesis>=6.100", "pytest-timeout>=2.3.0", "pytest-randomly>=3.15.0", "black>=24.0.0", "ruff>=0.3.0", "mypy>=1.8.0", "httpx>=0.27.0", # PYSEC-2026-3447 fix: setuptools < 83.0.0 has a MANIFEST.in exclusion # bypass on macOS APFS/HFS+ due to NFC/NFD Unicode normalization mismatch, # which could silently pack maintainer-excluded files into a published # sdist. We build with hatchling (not setuptools), but setuptools is a # transitive dep in most resolved environments and our release builds # happen on macOS APFS. Pin to the fixed version. "setuptools>=83.0.0", ] http = [ "uvicorn>=0.30.0", "starlette>=0.40.0", ] hermes = [ "ruamel.yaml>=0.17", ] continue = [ "ruamel.yaml>=0.17", ] openai = [ "openai>=1.0", ] [project.scripts] world-model = "world_model_server.cli:main" etch-verify = "world_model_server.etch_verify:main" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["world_model_server"] [tool.hatch.build.targets.wheel.force-include] "world_model_server/adapters/cursor/hooks.json" = "world_model_server/adapters/cursor/hooks.json" "world_model_server/adapters/cursor/mcp.json" = "world_model_server/adapters/cursor/mcp.json" "world_model_server/adapters/pi/index.ts" = "world_model_server/adapters/pi/index.ts" "world_model_server/adapters/pi/package.json" = "world_model_server/adapters/pi/package.json" "world_model_server/adapters/codex/config.toml" = "world_model_server/adapters/codex/config.toml" "world_model_server/adapters/codex/hooks_snippet.toml" = "world_model_server/adapters/codex/hooks_snippet.toml" "world_model_server/adapters/openclaw/openclaw.json" = "world_model_server/adapters/openclaw/openclaw.json" "world_model_server/adapters/copilot/mcp.json" = "world_model_server/adapters/copilot/mcp.json" "world_model_server/adapters/cline/mcp.json" = "world_model_server/adapters/cline/mcp.json" "world_model_server/adapters/windsurf/mcp_config.json" = "world_model_server/adapters/windsurf/mcp_config.json" "world_model_server/adapters/hermes/config-snippet.yaml" = "world_model_server/adapters/hermes/config-snippet.yaml" "world_model_server/hermes_memory_provider/__init__.py" = "world_model_server/hermes_memory_provider/__init__.py" "world_model_server/hermes_memory_provider/plugin.yaml" = "world_model_server/hermes_memory_provider/plugin.yaml" "world_model_server/hermes_memory_provider/README.md" = "world_model_server/hermes_memory_provider/README.md" "world_model_server/adapters/continue_/world-model.yaml" = "world_model_server/adapters/continue_/world-model.yaml" "world_model_server/hooks/world-model-capture.js" = "world_model_server/hooks/world-model-capture.js" "world_model_server/hooks/world-model-inject.js" = "world_model_server/hooks/world-model-inject.js" "world_model_server/hooks/world-model-session.js" = "world_model_server/hooks/world-model-session.js" "world_model_server/hooks/world-model-validate.js" = "world_model_server/hooks/world-model-validate.js" "scripts/demo_seed.py" = "scripts/demo_seed.py" # (v0.14: dropped the _embedded_token.py force-include. The old GitHub # Issues telemetry sink was replaced by an unauthenticated etch.systems # endpoint that rate-limits by install_id server-side. No PAT needed.) [tool.black] line-length = 100 target-version = ["py311"] [tool.ruff] line-length = 100 target-version = "py311" [tool.ruff.lint] select = ["E", "F", "I", "N", "W", "UP"] ignore = ["E501"] [tool.mypy] python_version = "3.11" strict = true warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true [tool.pytest.ini_options] asyncio_mode = "auto" testpaths = ["tests"] # Hard 5-minute per-test timeout to catch hangs (individual tests can # override with @pytest.mark.timeout(N) for tighter bounds). timeout = 300 markers = [ "timeout: mark a test with a specific timeout in seconds", ]