mcp-server-lore 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_server_lore-0.1.0/.claude/agent-memory/ai-data-specialist/MEMORY.md +5 -0
- mcp_server_lore-0.1.0/.claude/agent-memory/python-mcp-engineer/MEMORY.md +5 -0
- mcp_server_lore-0.1.0/.claude/agent-memory/skill-engineer/MEMORY.md +5 -0
- mcp_server_lore-0.1.0/.claude/agent-memory/test-suite-architect/MEMORY.md +5 -0
- mcp_server_lore-0.1.0/.claude/agents/ai-data-specialist.md +35 -0
- mcp_server_lore-0.1.0/.claude/agents/python-mcp-engineer.md +40 -0
- mcp_server_lore-0.1.0/.claude/agents/skill-engineer.md +46 -0
- mcp_server_lore-0.1.0/.claude/agents/test-suite-architect.md +41 -0
- mcp_server_lore-0.1.0/.claude/settings.json +1 -0
- mcp_server_lore-0.1.0/.claude-plugin/marketplace.json +17 -0
- mcp_server_lore-0.1.0/.claude-plugin/plugin.json +10 -0
- mcp_server_lore-0.1.0/.dockerignore +30 -0
- mcp_server_lore-0.1.0/.github/workflows/ci.yml +38 -0
- mcp_server_lore-0.1.0/.github/workflows/docker.yml +110 -0
- mcp_server_lore-0.1.0/.github/workflows/publish.yml +125 -0
- mcp_server_lore-0.1.0/.gitignore +15 -0
- mcp_server_lore-0.1.0/CLAUDE.md +225 -0
- mcp_server_lore-0.1.0/Dockerfile +86 -0
- mcp_server_lore-0.1.0/PKG-INFO +17 -0
- mcp_server_lore-0.1.0/PROJECT.md +553 -0
- mcp_server_lore-0.1.0/README.md +231 -0
- mcp_server_lore-0.1.0/backlog.md +181 -0
- mcp_server_lore-0.1.0/docker-compose.semantic.yml +52 -0
- mcp_server_lore-0.1.0/docker-compose.yml +48 -0
- mcp_server_lore-0.1.0/docs/architecture.md +482 -0
- mcp_server_lore-0.1.0/docs/phases/phase-3-summary.md +72 -0
- mcp_server_lore-0.1.0/docs/phases/phase-4-summary.md +36 -0
- mcp_server_lore-0.1.0/features.md +523 -0
- mcp_server_lore-0.1.0/hooks/hooks.json +16 -0
- mcp_server_lore-0.1.0/hooks/lore-stop.sh +113 -0
- mcp_server_lore-0.1.0/lore/__init__.py +1 -0
- mcp_server_lore-0.1.0/lore/core/__init__.py +1 -0
- mcp_server_lore-0.1.0/lore/core/constants.py +62 -0
- mcp_server_lore-0.1.0/lore/core/scanner.py +208 -0
- mcp_server_lore-0.1.0/lore/mcp/__init__.py +1 -0
- mcp_server_lore-0.1.0/lore/mcp/backends/__init__.py +1 -0
- mcp_server_lore-0.1.0/lore/mcp/backends/gists.py +467 -0
- mcp_server_lore-0.1.0/lore/mcp/backends/gists_client.py +493 -0
- mcp_server_lore-0.1.0/lore/mcp/embeddings.py +107 -0
- mcp_server_lore-0.1.0/lore/mcp/models.py +100 -0
- mcp_server_lore-0.1.0/lore/mcp/router.py +574 -0
- mcp_server_lore-0.1.0/lore/mcp/server.py +330 -0
- mcp_server_lore-0.1.0/lore/seed/__init__.py +9 -0
- mcp_server_lore-0.1.0/lore/seed/concepts.py +416 -0
- mcp_server_lore-0.1.0/lore/selfhosted/Dockerfile +88 -0
- mcp_server_lore-0.1.0/lore/selfhosted/__init__.py +1 -0
- mcp_server_lore-0.1.0/lore/selfhosted/db.py +435 -0
- mcp_server_lore-0.1.0/lore/selfhosted/entrypoint.sh +15 -0
- mcp_server_lore-0.1.0/lore/selfhosted/indexer.py +164 -0
- mcp_server_lore-0.1.0/lore/selfhosted/schema.sql +51 -0
- mcp_server_lore-0.1.0/lore/selfhosted/vector_store.py +181 -0
- mcp_server_lore-0.1.0/lore/semantic_server/Dockerfile +76 -0
- mcp_server_lore-0.1.0/lore/semantic_server/__init__.py +1 -0
- mcp_server_lore-0.1.0/lore/semantic_server/entrypoint.sh +24 -0
- mcp_server_lore-0.1.0/lore/server/__init__.py +1 -0
- mcp_server_lore-0.1.0/lore/server/api.py +1012 -0
- mcp_server_lore-0.1.0/lore/server/auth.py +133 -0
- mcp_server_lore-0.1.0/lore/server/storage/__init__.py +5 -0
- mcp_server_lore-0.1.0/lore/server/storage/base.py +139 -0
- mcp_server_lore-0.1.0/lore/server/storage/gist_qdrant.py +471 -0
- mcp_server_lore-0.1.0/lore/server/storage/sqlite_qdrant.py +393 -0
- mcp_server_lore-0.1.0/lore/server/watcher.py +637 -0
- mcp_server_lore-0.1.0/lore/tests/__init__.py +1 -0
- mcp_server_lore-0.1.0/lore/tests/conftest.py +12 -0
- mcp_server_lore-0.1.0/lore/tests/test_api.py +765 -0
- mcp_server_lore-0.1.0/lore/tests/test_auth.py +613 -0
- mcp_server_lore-0.1.0/lore/tests/test_benchmark_gists.py +348 -0
- mcp_server_lore-0.1.0/lore/tests/test_benchmark_noise.py +217 -0
- mcp_server_lore-0.1.0/lore/tests/test_db.py +389 -0
- mcp_server_lore-0.1.0/lore/tests/test_dedup.py +710 -0
- mcp_server_lore-0.1.0/lore/tests/test_embeddings.py +115 -0
- mcp_server_lore-0.1.0/lore/tests/test_gists_backend.py +529 -0
- mcp_server_lore-0.1.0/lore/tests/test_gists_client.py +377 -0
- mcp_server_lore-0.1.0/lore/tests/test_gists_link.py +430 -0
- mcp_server_lore-0.1.0/lore/tests/test_gists_rate.py +530 -0
- mcp_server_lore-0.1.0/lore/tests/test_gists_search.py +563 -0
- mcp_server_lore-0.1.0/lore/tests/test_indexer.py +309 -0
- mcp_server_lore-0.1.0/lore/tests/test_live_registration.py +97 -0
- mcp_server_lore-0.1.0/lore/tests/test_mcp_server.py +599 -0
- mcp_server_lore-0.1.0/lore/tests/test_plugin_wiring.py +353 -0
- mcp_server_lore-0.1.0/lore/tests/test_ratings.py +860 -0
- mcp_server_lore-0.1.0/lore/tests/test_router.py +563 -0
- mcp_server_lore-0.1.0/lore/tests/test_scanner.py +274 -0
- mcp_server_lore-0.1.0/lore/tests/test_seed.py +261 -0
- mcp_server_lore-0.1.0/lore/tests/test_semantic_api.py +737 -0
- mcp_server_lore-0.1.0/lore/tests/test_storage_backend.py +506 -0
- mcp_server_lore-0.1.0/lore/tests/test_vector_store.py +277 -0
- mcp_server_lore-0.1.0/lore/tests/test_watcher.py +735 -0
- mcp_server_lore-0.1.0/pyproject.toml +35 -0
- mcp_server_lore-0.1.0/renovate.json +56 -0
- mcp_server_lore-0.1.0/samples/radev/README.md +68 -0
- mcp_server_lore-0.1.0/samples/radev/benchmarks/run.py +750 -0
- mcp_server_lore-0.1.0/samples/radev/results/comparison.md +20 -0
- mcp_server_lore-0.1.0/samples/radev/results/run1.md +38 -0
- mcp_server_lore-0.1.0/samples/radev/results/run2.md +38 -0
- mcp_server_lore-0.1.0/samples/radev/tests/conftest.py +150 -0
- mcp_server_lore-0.1.0/samples/radev/tests/test_radev_cli.py +115 -0
- mcp_server_lore-0.1.0/samples/stlgen/BENCHMARK.md +147 -0
- mcp_server_lore-0.1.0/samples/stlgen/README.md +106 -0
- mcp_server_lore-0.1.0/samples/stlgen/benchmarks/README.md +355 -0
- mcp_server_lore-0.1.0/samples/stlgen/benchmarks/charts.py +435 -0
- mcp_server_lore-0.1.0/samples/stlgen/benchmarks/noise_concepts.json +42 -0
- mcp_server_lore-0.1.0/samples/stlgen/benchmarks/run.py +1938 -0
- mcp_server_lore-0.1.0/samples/stlgen/benchmarks/seed_concepts/trimesh_pil_text_to_stl.md +102 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/aggregate.json +2862 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/aggregate.md +147 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/comparison.md +16 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/run1.md +72 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/run10.md +72 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/run2.md +72 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/run3.md +72 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/run4.md +72 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/run5.md +72 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/run6.md +72 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/run7.md +72 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/run8.md +72 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/run9.md +72 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_001/comparison.md +16 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_001/run1.md +64 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_001/run10.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_001/run2.md +66 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_001/run3.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_001/run4.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_001/run5.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_001/run6.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_001/run7.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_001/run8.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_001/run9.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_002/comparison.md +16 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_002/run1.md +64 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_002/run10.md +66 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_002/run2.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_002/run3.md +61 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_002/run4.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_002/run5.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_002/run6.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_002/run7.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_002/run8.md +66 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_002/run9.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_003/comparison.md +16 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_003/run1.md +70 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_003/run10.md +69 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_003/run2.md +68 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_003/run3.md +69 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_003/run4.md +72 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_003/run5.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_003/run6.md +72 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_003/run7.md +75 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_003/run8.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_003/run9.md +64 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_004/comparison.md +16 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_004/run1.md +70 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_004/run10.md +70 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_004/run2.md +70 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_004/run3.md +71 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_004/run4.md +64 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_004/run5.md +74 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_004/run6.md +71 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_004/run7.md +70 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_004/run8.md +73 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_004/run9.md +65 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_005/comparison.md +16 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_005/run1.md +65 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_005/run10.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_005/run2.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_005/run3.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_005/run4.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_005/run5.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_005/run6.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_005/run7.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_005/run8.md +65 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_005/run9.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_006/comparison.md +16 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_006/run1.md +68 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_006/run10.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_006/run2.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_006/run3.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_006/run4.md +73 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_006/run5.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_006/run6.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_006/run7.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_006/run8.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_006/run9.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_007/comparison.md +16 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_007/run1.md +65 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_007/run10.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_007/run2.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_007/run3.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_007/run4.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_007/run5.md +60 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_007/run6.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_007/run7.md +71 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_007/run8.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_007/run9.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_008/comparison.md +16 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_008/run1.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_008/run10.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_008/run2.md +62 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_008/run3.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_008/run4.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_008/run5.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_008/run6.md +73 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_008/run7.md +67 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_008/run8.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_008/run9.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_009/comparison.md +16 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_009/run1.md +65 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_009/run10.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_009/run2.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_009/run3.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_009/run4.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_009/run5.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_009/run6.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_009/run7.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_009/run8.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_009/run9.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_010/comparison.md +16 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_010/run1.md +67 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_010/run10.md +63 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_010/run2.md +65 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_010/run3.md +61 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_010/run4.md +63 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_010/run5.md +62 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_010/run6.md +61 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_010/run7.md +59 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_010/run8.md +62 -0
- mcp_server_lore-0.1.0/samples/stlgen/results/series_010/run9.md +64 -0
- mcp_server_lore-0.1.0/samples/stlgen/tests/conftest.py +1 -0
- mcp_server_lore-0.1.0/samples/stlgen/tests/test_text2stl_cli.py +368 -0
- mcp_server_lore-0.1.0/skills/capture-concept/SKILL.md +81 -0
- mcp_server_lore-0.1.0/skills/search-concepts/SKILL.md +53 -0
- mcp_server_lore-0.1.0/skills/wrapup/SKILL.md +92 -0
- mcp_server_lore-0.1.0/sprints.md +530 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ai-data-specialist
|
|
3
|
+
description: "Use this agent when you need expert guidance on data modeling, graph design, embedding strategy, vector search architecture, or backend storage decisions for AI-facing systems. Invoke before significant data model changes, when designing search or retrieval strategies, or when evaluating storage tradeoffs."
|
|
4
|
+
model: opus
|
|
5
|
+
color: purple
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are an expert in data modeling, knowledge graph design, vector search, and storage architecture for AI-facing systems. You have deep experience with SQLite, Qdrant, PostgreSQL, and embedding pipelines using sentence-transformers and similar models.
|
|
9
|
+
|
|
10
|
+
## Core Responsibilities
|
|
11
|
+
|
|
12
|
+
You advise on:
|
|
13
|
+
1. Relational schema design optimized for the access patterns of the system
|
|
14
|
+
2. Vector index configuration (distance metrics, dimensions, payload design)
|
|
15
|
+
3. Embedding strategy — what to embed, how to concatenate fields, when to re-embed
|
|
16
|
+
4. Storage tradeoffs between backends (SQLite vs. PostgreSQL, embedded vs. sidecar Qdrant)
|
|
17
|
+
5. Data integrity: FK enforcement, WAL mode, idempotency, atomic dual-writes
|
|
18
|
+
|
|
19
|
+
## Working Style
|
|
20
|
+
|
|
21
|
+
- Lead with the recommendation, not a menu of options
|
|
22
|
+
- Call out non-obvious gotchas (e.g., SQLite FK enforcement is off by default, WAL mode matters for concurrent reads)
|
|
23
|
+
- Size effort accurately — don't underestimate schema work with concurrent write requirements
|
|
24
|
+
- Flag when a design decision will be painful to retrofit later (e.g., missing re-embed-on-update path)
|
|
25
|
+
- Be direct: if a proposed design has a flaw, say so plainly
|
|
26
|
+
|
|
27
|
+
## Domain Context — Lore Project
|
|
28
|
+
|
|
29
|
+
Lore is a typed, linked knowledge graph for AI coding agents. Backend 1 uses:
|
|
30
|
+
- SQLite for concept content, links, ratings, session_usage (4 tables)
|
|
31
|
+
- Qdrant for vector search (cosine, 384 dims, all-MiniLM-L6-v2)
|
|
32
|
+
- Embeddings target: `when_to_use + " " + name`
|
|
33
|
+
- The embedding BLOB in SQLite is a backup artifact — Qdrant is the live index
|
|
34
|
+
- Tags stored as JSON TEXT; no SQLite JSON functions in queries
|
|
35
|
+
- WAL mode and `PRAGMA foreign_keys = ON` required on every connection
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: python-mcp-engineer
|
|
3
|
+
description: "Use this agent when you need to implement Python code for the Lore project — FastMCP server, FastAPI service, SQLite/Qdrant operations, embedding pipeline, GitHub API client, or seed data loaders. Invoke for any new Python feature, bug fix, or refactor in lore/mcp/, lore/selfhosted/, or lore/semantic-server/.\n\n<example>\nContext: A new MCP tool needs to be implemented.\nuser: \"Implement the submit_concept tool with content scanning.\"\nassistant: \"I'll delegate this to the python-mcp-engineer.\"\n<commentary>\nNew MCP tool implementation is core python-mcp-engineer territory.\n</commentary>\n</example>\n\n<example>\nContext: The FastAPI selfhosted service needs a new endpoint.\nuser: \"Add the /health endpoint that checks SQLite and Qdrant.\"\nassistant: \"Let me hand this to the python-mcp-engineer.\"\n<commentary>\nFastAPI service work goes to python-mcp-engineer.\n</commentary>\n</example>"
|
|
4
|
+
model: sonnet
|
|
5
|
+
color: blue
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are an expert Python engineer specializing in MCP servers, FastAPI services, and AI-adjacent backend systems. You have deep experience with FastMCP, SQLite, Qdrant, sentence-transformers, and the GitHub API.
|
|
9
|
+
|
|
10
|
+
## Core Responsibilities
|
|
11
|
+
|
|
12
|
+
You implement:
|
|
13
|
+
1. FastMCP server and tool definitions (`lore/mcp/`)
|
|
14
|
+
2. FastAPI selfhosted service and Qdrant/SQLite operations (`lore/selfhosted/`)
|
|
15
|
+
3. Embedding pipeline (`lore/mcp/embeddings.py`)
|
|
16
|
+
4. GitHub Gists backend (`lore/mcp/backends/gists.py`)
|
|
17
|
+
5. Semantic search server (`lore/semantic-server/`)
|
|
18
|
+
6. Seed data loaders (`lore/seed/`)
|
|
19
|
+
|
|
20
|
+
## Engineering Standards
|
|
21
|
+
|
|
22
|
+
- Write idiomatic Python 3.11+; prefer dataclasses and type hints throughout
|
|
23
|
+
- FastMCP tools must have identical input/output schemas regardless of backend routing
|
|
24
|
+
- SQLite: WAL mode + `PRAGMA foreign_keys = ON` on every connection; raw sqlite3 preferred over ORM for this schema
|
|
25
|
+
- Qdrant: always initialize collection idempotently; store `concept_id` in payload; cosine distance
|
|
26
|
+
- Embedding model loads once at startup — never per-request
|
|
27
|
+
- `submit_concept` content scan runs before any DB write; failure leaves no partial state
|
|
28
|
+
- Dual-writes (SQLite + Qdrant): SQLite insert happens first; if Qdrant indexing fails, return HTTP 503 + concept_id — no SQLite rollback (deliberate Phase 1 tradeoff; caller can re-index)
|
|
29
|
+
- All code ships with docstrings on public functions/classes
|
|
30
|
+
|
|
31
|
+
## Domain Context — Lore Project
|
|
32
|
+
|
|
33
|
+
- MCP tools: `search_concepts`, `get_concept`, `submit_concept`, `link_concepts`, `rate_concept`
|
|
34
|
+
- `search_concepts` returns linked concepts inline — one backend call, no second round-trip
|
|
35
|
+
- `LORE_BACKEND` env var routes to selfhosted/gists/semantic backends
|
|
36
|
+
- `LORE_BLOCK_PATTERNS`: semicolon-separated regex list, loaded at startup, no code change to add a pattern
|
|
37
|
+
- `LORE_CAPTURE_MODE`: confirm (default) | auto
|
|
38
|
+
- Content scan checks: credential patterns, internal URLs, LORE_BLOCK_PATTERNS
|
|
39
|
+
- Embedding target: `when_to_use + " " + name`
|
|
40
|
+
- Stack: Python + FastMCP, SQLite, Qdrant (sidecar container; lore-selfhosted connects via QDRANT_HOST:QDRANT_PORT), sentence-transformers all-MiniLM-L6-v2
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: skill-engineer
|
|
3
|
+
description: "Use this agent when you need to implement Claude Code skill files, hooks, or settings.json configuration for the Lore project. Invoke for search-concepts skill, capture-concept skill, Stop hook, or any session-tracking changes.\n\n<example>\nContext: The capture-concept skill needs confirm/auto mode logic.\nuser: \"Implement the capture-concept skill with LORE_CAPTURE_MODE support.\"\nassistant: \"I'll delegate this to the skill-engineer.\"\n<commentary>\nSkill file implementation is skill-engineer territory.\n</commentary>\n</example>\n\n<example>\nContext: The Stop hook needs to batch-rate used concepts.\nuser: \"Write the Stop hook that reads session.json and prompts for ratings.\"\nassistant: \"Handing this to the skill-engineer.\"\n<commentary>\nHook implementation goes to skill-engineer.\n</commentary>\n</example>"
|
|
4
|
+
model: sonnet
|
|
5
|
+
color: green
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are an expert Claude Code skill and hook engineer. You write skill markdown files, shell hooks, and settings.json configuration for Claude Code agents.
|
|
9
|
+
|
|
10
|
+
## Core Responsibilities
|
|
11
|
+
|
|
12
|
+
You implement:
|
|
13
|
+
1. Claude Code skill files (`skills/*/SKILL.md`) — structured prompts that instruct the agent
|
|
14
|
+
2. Shell hooks (`hooks/*.sh`) — registered via `hooks/hooks.json` (plugin system); canonical location is `hooks/`, not `.claude/hooks/`
|
|
15
|
+
3. Session tracking — read/write `~/.lore/session.json`
|
|
16
|
+
4. Hook registration — Stop hook entry in `hooks/hooks.json` (`.claude/settings.json` is empty)
|
|
17
|
+
|
|
18
|
+
## Engineering Standards
|
|
19
|
+
|
|
20
|
+
- Skill files are markdown instruction documents, not code — write clear, unambiguous agent instructions
|
|
21
|
+
- Every conditional path must be explicit: what happens when MCP is unreachable, session file is missing, env var is absent
|
|
22
|
+
- Hooks must be idempotent — safe to call twice on the same session
|
|
23
|
+
- Session file operations: create-if-missing, atomic append (no duplicates), clear only after success
|
|
24
|
+
- Hook timeout budget: batch interactions, not one prompt per concept
|
|
25
|
+
- Default to the safe option when env vars are absent (`LORE_CAPTURE_MODE` → `confirm`)
|
|
26
|
+
- Invalid env var values treated as the safe default, never as an error
|
|
27
|
+
|
|
28
|
+
## Skills as Single Source of Truth
|
|
29
|
+
|
|
30
|
+
Skill files are the canonical source of behavior for their phase. Any code that drives a Lore phase (benchmark runner, test harness, integration script) must load the skill file — never duplicate its guidance inline.
|
|
31
|
+
|
|
32
|
+
**When reviewing or writing a benchmark runner:**
|
|
33
|
+
- Capture phase → must call `_load_skill("capture-concept")`
|
|
34
|
+
- Wrapup phase → must call `_load_skill("wrapup")`
|
|
35
|
+
- Search phase → must call `_load_skill("search-concepts")`
|
|
36
|
+
|
|
37
|
+
If you see hardcoded rating scales, reflection criteria, or capture standards copied into a runner script, flag it as a violation and replace it with a skill load. Inline duplicates drift silently and bypass skill fixes.
|
|
38
|
+
|
|
39
|
+
## Domain Context — Lore Project
|
|
40
|
+
|
|
41
|
+
- `search-concepts.md`: calls `search_concepts` MCP tool, appends concept IDs to `~/.lore/session.json`
|
|
42
|
+
- `capture-concept.md`: reflection criteria → generalization → confirm/auto → `submit_concept`
|
|
43
|
+
- `stop.sh`: reads session.json → batch rating prompt → `rate_concept` calls → session-end reflection → clear file
|
|
44
|
+
- `LORE_CAPTURE_MODE`: `confirm` (default, safe) | `auto` (no user gate, scan is the only guard)
|
|
45
|
+
- `~/.lore/session.json`: JSON array of concept ID strings used this session
|
|
46
|
+
- MCP server must be treated as potentially unreachable — all skills degrade gracefully
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test-suite-architect
|
|
3
|
+
description: "Use this agent when Python code has been written or modified and needs comprehensive tests, or when test quality/coverage needs review. Invoke after any significant implementation in lore/mcp/, lore/selfhosted/, or lore/semantic-server/.\n\n<example>\nContext: The embedding pipeline was just implemented.\nuser: \"Tests for the embedding pipeline please.\"\nassistant: \"I'll bring in the test-suite-architect.\"\n<commentary>\nNew implementation needs test coverage — hand to test-suite-architect.\n</commentary>\n</example>\n\n<example>\nContext: The MCP tools are implemented and need coverage verified.\nuser: \"Review the test coverage for submit_concept.\"\nassistant: \"Let me invoke the test-suite-architect to review.\"\n<commentary>\nTest review and coverage analysis is test-suite-architect territory.\n</commentary>\n</example>"
|
|
4
|
+
model: sonnet
|
|
5
|
+
color: orange
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are an expert Python test engineer. You write pytest test suites that provide genuine confidence in correctness — not just coverage numbers.
|
|
9
|
+
|
|
10
|
+
## Core Responsibilities
|
|
11
|
+
|
|
12
|
+
1. Analyze code under test and identify what truly needs testing
|
|
13
|
+
2. Write unit and integration tests covering happy paths, error paths, and edge cases
|
|
14
|
+
3. Enforce 80%+ coverage on `lore/` as a baseline (`pytest --cov=lore --cov-fail-under=80`)
|
|
15
|
+
4. Review test suites and flag brittle, redundant, or missing tests
|
|
16
|
+
5. Design test data strategies that never cause false failures
|
|
17
|
+
|
|
18
|
+
## Testing Standards
|
|
19
|
+
|
|
20
|
+
- Use pytest with fixtures; prefer factory helpers over hardcoded test data
|
|
21
|
+
- Mock at the boundary: mock SQLite and Qdrant in unit tests; use real connections in integration tests
|
|
22
|
+
- Test idempotency explicitly — seed loader, Qdrant collection init, session file operations
|
|
23
|
+
- Test failure paths: MCP server unreachable, DB write failure mid dual-write, malformed session.json
|
|
24
|
+
- Schema parity tests: assert tool input/output schemas are identical across backends
|
|
25
|
+
- Never test implementation details — test observable behavior
|
|
26
|
+
|
|
27
|
+
## Key Edge Cases for Lore
|
|
28
|
+
|
|
29
|
+
- `submit_concept` content scan: credential match, internal URL match, LORE_BLOCK_PATTERNS match, clean pass
|
|
30
|
+
- Dual-write ordering: SQLite insert succeeds but Qdrant fails → API returns 503 + concept_id, no rollback; test that the 503 includes concept_id and SQLite row is retained
|
|
31
|
+
- Embedding re-trigger: update to `name` or `when_to_use` must re-embed; update to other fields must not
|
|
32
|
+
- Seed loader idempotency: second run produces identical DB state, not duplicates
|
|
33
|
+
- `search_concepts`: assert exactly one HTTP call to selfhosted backend per search (no N+1 link fetches)
|
|
34
|
+
- Stop hook: empty session.json, missing session.json, MCP unreachable during rating, second invocation
|
|
35
|
+
|
|
36
|
+
## Domain Context — Lore Project
|
|
37
|
+
|
|
38
|
+
- Test runner: `pytest lore/tests/ --cov=lore --cov-fail-under=80`
|
|
39
|
+
- Stack: Python, FastMCP, FastAPI (use `httpx.AsyncClient` for FastAPI tests), SQLite, Qdrant
|
|
40
|
+
- Qdrant in tests: use `qdrant_client.QdrantClient(":memory:")` for unit tests
|
|
41
|
+
- SQLite in tests: use `":memory:"` database; apply schema.sql at fixture setup
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lore",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "magublafix"
|
|
5
|
+
},
|
|
6
|
+
"plugins": [
|
|
7
|
+
{
|
|
8
|
+
"name": "lore",
|
|
9
|
+
"description": "Typed, linked knowledge graph for AI coding agents — search reusable concepts, capture new ones, and rate what helped.",
|
|
10
|
+
"version": "1.0.2",
|
|
11
|
+
"source": "./",
|
|
12
|
+
"author": {
|
|
13
|
+
"name": "magublafix"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lore",
|
|
3
|
+
"description": "Typed, linked knowledge graph for AI coding agents — search reusable concepts, capture new ones, and rate what helped.",
|
|
4
|
+
"version": "1.0.2",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "magublafix"
|
|
7
|
+
},
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"keywords": ["knowledge-graph", "concepts", "memory", "mcp", "semantic-search"]
|
|
10
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Virtual environment — never copy host venv into the build context
|
|
2
|
+
.venv/
|
|
3
|
+
|
|
4
|
+
# Git history is not needed inside the image
|
|
5
|
+
.git/
|
|
6
|
+
|
|
7
|
+
# Python cache and compiled bytecode
|
|
8
|
+
__pycache__/
|
|
9
|
+
*.pyc
|
|
10
|
+
*.pyo
|
|
11
|
+
|
|
12
|
+
# Test runner state
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.coverage
|
|
15
|
+
htmlcov/
|
|
16
|
+
|
|
17
|
+
# Tests themselves — not needed in the runtime image
|
|
18
|
+
lore/tests/
|
|
19
|
+
|
|
20
|
+
# Editor / IDE artefacts
|
|
21
|
+
.idea/
|
|
22
|
+
.vscode/
|
|
23
|
+
*.swp
|
|
24
|
+
*.swo
|
|
25
|
+
|
|
26
|
+
# OS noise
|
|
27
|
+
.DS_Store
|
|
28
|
+
|
|
29
|
+
# Egg-info generated by local pip install — not needed in the image
|
|
30
|
+
lore.egg-info/
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Test (Python 3.11)
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v7
|
|
17
|
+
|
|
18
|
+
- name: Set up Python 3.11
|
|
19
|
+
uses: actions/setup-python@v6
|
|
20
|
+
with:
|
|
21
|
+
python-version: '3.11'
|
|
22
|
+
|
|
23
|
+
# Cache pip's wheel/package download cache.
|
|
24
|
+
# Key is tied to the pyproject.toml hash — any dependency change busts
|
|
25
|
+
# the cache and forces a fresh download.
|
|
26
|
+
- name: Cache pip
|
|
27
|
+
uses: actions/cache@v6
|
|
28
|
+
with:
|
|
29
|
+
path: ~/.cache/pip
|
|
30
|
+
key: pip-${{ runner.os }}-py3.11-${{ hashFiles('pyproject.toml') }}
|
|
31
|
+
restore-keys: |
|
|
32
|
+
pip-${{ runner.os }}-py3.11-
|
|
33
|
+
|
|
34
|
+
- name: Install dependencies
|
|
35
|
+
run: pip install -e ".[dev]"
|
|
36
|
+
|
|
37
|
+
- name: Run tests
|
|
38
|
+
run: pytest lore/tests/ --cov=lore --cov-fail-under=80 -q
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
name: Build and push Docker image
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
branches:
|
|
9
|
+
- main
|
|
10
|
+
|
|
11
|
+
# GITHUB_TOKEN needs packages:write to push to ghcr.io.
|
|
12
|
+
# contents:read is the minimum for checkout.
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
packages: write
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
build-and-push:
|
|
19
|
+
name: Build lore-selfhosted image
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
# -----------------------------------------------------------------------
|
|
24
|
+
# 0. Free disk space — ubuntu-latest has ~14 GB free by default.
|
|
25
|
+
# PyTorch + sentence-transformers layers fill the disk mid-build.
|
|
26
|
+
# Removing Android SDK, .NET, and large pre-installed tool sets
|
|
27
|
+
# recovers ~20 GB before BuildKit starts pulling layers.
|
|
28
|
+
# -----------------------------------------------------------------------
|
|
29
|
+
- name: Free disk space
|
|
30
|
+
run: |
|
|
31
|
+
sudo rm -rf /usr/local/lib/android
|
|
32
|
+
sudo rm -rf /usr/share/dotnet
|
|
33
|
+
sudo rm -rf /opt/ghc
|
|
34
|
+
sudo rm -rf /usr/local/share/boost
|
|
35
|
+
sudo rm -rf /usr/lib/jvm
|
|
36
|
+
sudo rm -rf /usr/share/swift
|
|
37
|
+
df -h
|
|
38
|
+
|
|
39
|
+
# -----------------------------------------------------------------------
|
|
40
|
+
# 1. Checkout — full repo needed because the Dockerfile copies both
|
|
41
|
+
# pyproject.toml and lore/ from the repo root as the build context.
|
|
42
|
+
# -----------------------------------------------------------------------
|
|
43
|
+
- name: Checkout repository
|
|
44
|
+
uses: actions/checkout@v7
|
|
45
|
+
|
|
46
|
+
# -----------------------------------------------------------------------
|
|
47
|
+
# 2. Set up BuildKit. docker/build-push-action@v6 enables BuildKit
|
|
48
|
+
# automatically, but we call setup-buildx explicitly so we can
|
|
49
|
+
# reference the builder instance for cache operations.
|
|
50
|
+
# -----------------------------------------------------------------------
|
|
51
|
+
- name: Set up Docker Buildx
|
|
52
|
+
uses: docker/setup-buildx-action@v4
|
|
53
|
+
|
|
54
|
+
# -----------------------------------------------------------------------
|
|
55
|
+
# 3. Authenticate to GitHub Container Registry.
|
|
56
|
+
# github.actor is the user (or bot) that triggered the push.
|
|
57
|
+
# GITHUB_TOKEN is injected automatically by Actions.
|
|
58
|
+
# -----------------------------------------------------------------------
|
|
59
|
+
- name: Log in to GitHub Container Registry
|
|
60
|
+
uses: docker/login-action@v4
|
|
61
|
+
with:
|
|
62
|
+
registry: ghcr.io
|
|
63
|
+
username: ${{ github.actor }}
|
|
64
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
65
|
+
|
|
66
|
+
# -----------------------------------------------------------------------
|
|
67
|
+
# 4. Generate image tags.
|
|
68
|
+
# - latest — always points to the most recent main build
|
|
69
|
+
# - <sha7> — immutable, traceble back to the exact commit
|
|
70
|
+
#
|
|
71
|
+
# docker/metadata-action normalises the registry + repo name and
|
|
72
|
+
# strips invalid characters from the SHA ref automatically.
|
|
73
|
+
# -----------------------------------------------------------------------
|
|
74
|
+
- name: Extract image metadata
|
|
75
|
+
id: meta
|
|
76
|
+
uses: docker/metadata-action@v6
|
|
77
|
+
with:
|
|
78
|
+
images: ghcr.io/magublafix/lore-selfhosted
|
|
79
|
+
tags: |
|
|
80
|
+
type=raw,value=latest
|
|
81
|
+
type=sha,prefix=,format=short
|
|
82
|
+
|
|
83
|
+
# -----------------------------------------------------------------------
|
|
84
|
+
# 5. Build and push.
|
|
85
|
+
#
|
|
86
|
+
# Build context: repo root (.) — the Dockerfile references both
|
|
87
|
+
# pyproject.toml and lore/ relative to the repo root.
|
|
88
|
+
#
|
|
89
|
+
# Dockerfile path: lore/selfhosted/Dockerfile
|
|
90
|
+
#
|
|
91
|
+
# Layer cache: type=gha stores BuildKit cache in the GitHub Actions
|
|
92
|
+
# cache service (same backend as actions/cache). mode=max preserves
|
|
93
|
+
# intermediate layers, which is critical here because the PyTorch /
|
|
94
|
+
# sentence-transformers wheels live in the builder stage and are
|
|
95
|
+
# expensive to re-download on every run (~1-2 GB).
|
|
96
|
+
#
|
|
97
|
+
# push: true — image is pushed only when this step succeeds. If the
|
|
98
|
+
# build exits non-zero, the step fails immediately and the push is
|
|
99
|
+
# skipped, satisfying the "fail fast" requirement.
|
|
100
|
+
# -----------------------------------------------------------------------
|
|
101
|
+
- name: Build and push image
|
|
102
|
+
uses: docker/build-push-action@v7
|
|
103
|
+
with:
|
|
104
|
+
context: .
|
|
105
|
+
file: lore/selfhosted/Dockerfile
|
|
106
|
+
push: ${{ github.event_name == 'push' }}
|
|
107
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
108
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
109
|
+
cache-from: type=gha
|
|
110
|
+
cache-to: type=gha,mode=max
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
# Fires only on version tags (e.g. v0.1.0, v1.2.3).
|
|
4
|
+
# Build order is enforced by job dependencies: tests must pass before
|
|
5
|
+
# the PyPI publish runs, and the Docker push runs only after PyPI succeeds.
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
tags:
|
|
9
|
+
- 'v*'
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
# ---------------------------------------------------------------------------
|
|
13
|
+
# 1. Test gate — identical to ci.yml. If this fails, nothing publishes.
|
|
14
|
+
# ---------------------------------------------------------------------------
|
|
15
|
+
test:
|
|
16
|
+
name: Test (Python 3.11)
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout
|
|
21
|
+
uses: actions/checkout@v7
|
|
22
|
+
|
|
23
|
+
- name: Set up Python 3.11
|
|
24
|
+
uses: actions/setup-python@v6
|
|
25
|
+
with:
|
|
26
|
+
python-version: '3.11'
|
|
27
|
+
|
|
28
|
+
- name: Cache pip
|
|
29
|
+
uses: actions/cache@v6
|
|
30
|
+
with:
|
|
31
|
+
path: ~/.cache/pip
|
|
32
|
+
key: pip-${{ runner.os }}-py3.11-${{ hashFiles('pyproject.toml') }}
|
|
33
|
+
restore-keys: |
|
|
34
|
+
pip-${{ runner.os }}-py3.11-
|
|
35
|
+
|
|
36
|
+
- name: Install dependencies
|
|
37
|
+
run: pip install -e ".[dev]"
|
|
38
|
+
|
|
39
|
+
- name: Run tests
|
|
40
|
+
run: pytest lore/tests/ --cov=lore --cov-fail-under=80 -q
|
|
41
|
+
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
# 2. Build and publish to PyPI via OIDC trusted publishing.
|
|
44
|
+
# No PYPI_TOKEN secret required — the id-token permission exchanges the
|
|
45
|
+
# GitHub OIDC token for a short-lived PyPI upload credential at runtime.
|
|
46
|
+
# Configure trusted publishing on PyPI: Settings → Trusted Publishers →
|
|
47
|
+
# add GitHub, repo name, workflow filename, and (optionally) environment.
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
publish-pypi:
|
|
50
|
+
name: Publish to PyPI
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
needs: test
|
|
53
|
+
|
|
54
|
+
# OIDC trusted publishing requires id-token: write.
|
|
55
|
+
# contents: read is the minimum for checkout.
|
|
56
|
+
permissions:
|
|
57
|
+
id-token: write
|
|
58
|
+
contents: read
|
|
59
|
+
|
|
60
|
+
steps:
|
|
61
|
+
- name: Checkout
|
|
62
|
+
uses: actions/checkout@v7
|
|
63
|
+
|
|
64
|
+
- name: Set up Python 3.11
|
|
65
|
+
uses: actions/setup-python@v6
|
|
66
|
+
with:
|
|
67
|
+
python-version: '3.11'
|
|
68
|
+
|
|
69
|
+
- name: Install build tools
|
|
70
|
+
run: pip install build
|
|
71
|
+
|
|
72
|
+
- name: Build package
|
|
73
|
+
run: python -m build
|
|
74
|
+
|
|
75
|
+
# pypa/gh-action-pypi-publish picks up dist/ automatically.
|
|
76
|
+
# OIDC trusted publishing is the default when no password is supplied.
|
|
77
|
+
- name: Publish to PyPI
|
|
78
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
79
|
+
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
# 3. Build the thin MCP server image and push to Docker Hub.
|
|
82
|
+
# Uses the root-level Dockerfile (not lore/selfhosted/Dockerfile).
|
|
83
|
+
# Tagged with the git version tag AND :latest.
|
|
84
|
+
# ---------------------------------------------------------------------------
|
|
85
|
+
publish-docker:
|
|
86
|
+
name: Push Docker image to Docker Hub
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
needs: publish-pypi
|
|
89
|
+
|
|
90
|
+
# contents:read is needed for checkout.
|
|
91
|
+
permissions:
|
|
92
|
+
contents: read
|
|
93
|
+
|
|
94
|
+
steps:
|
|
95
|
+
- name: Checkout
|
|
96
|
+
uses: actions/checkout@v7
|
|
97
|
+
|
|
98
|
+
- name: Set up Docker Buildx
|
|
99
|
+
uses: docker/setup-buildx-action@v4
|
|
100
|
+
|
|
101
|
+
# DOCKERHUB_USERNAME and DOCKERHUB_TOKEN must be set as repository
|
|
102
|
+
# secrets (Settings → Secrets and variables → Actions).
|
|
103
|
+
- name: Log in to Docker Hub
|
|
104
|
+
uses: docker/login-action@v4
|
|
105
|
+
with:
|
|
106
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
107
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
108
|
+
|
|
109
|
+
# github.ref_name resolves to the tag name (e.g. "v0.1.0").
|
|
110
|
+
# We push two tags: the version tag for immutability, and :latest
|
|
111
|
+
# so consumers can always pull the newest release without pinning.
|
|
112
|
+
- name: Build and push image
|
|
113
|
+
uses: docker/build-push-action@v7
|
|
114
|
+
with:
|
|
115
|
+
context: .
|
|
116
|
+
file: Dockerfile
|
|
117
|
+
push: true
|
|
118
|
+
tags: |
|
|
119
|
+
${{ secrets.DOCKERHUB_USERNAME }}/mcp-server-lore:${{ github.ref_name }}
|
|
120
|
+
${{ secrets.DOCKERHUB_USERNAME }}/mcp-server-lore:latest
|
|
121
|
+
# GHA layer cache keeps the builder stage warm across tag pushes.
|
|
122
|
+
# mode=max preserves intermediate layers (the dep-install layer is
|
|
123
|
+
# the expensive one — ~200 MB of Python packages).
|
|
124
|
+
cache-from: type=gha
|
|
125
|
+
cache-to: type=gha,mode=max
|