Thank you for your interest in contributing! This guide covers dev setup, testing, and how to add a new language parser or LLM provider.
git clone https://github.com/sairam0424/trelix
cd trelix
# Create virtualenv
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# Install in editable mode with all dev + optional deps
make install-dev
# equivalent: pip install -e ".[bge-code,plaid,lance,serve,dev]"
# Optional extras:
# [bge-code] β BGE-Code embeddings (requires torch, transformers)
# [plaid] β Plaid financial data integration
# [lance] β LanceDB vector store for large-scale deployments
# [serve] β REST API server (FastAPI + Uvicorn)
# [knowledge-graph] β Knowledge graph + visualization (pyvis>=0.3.2, networkx>=3.3.0)
# [graph-viz] β Alias for [knowledge-graph]
# [watch] β Multi-repo file watching (watchfiles)
# [dev] β testing, linting, type-checking (always included)
# Standard dev setup (no graph visualization)
pip install -e ".[local,dev]"
# Include graph visualization (pyvis + networkx)
pip install -e ".[local,dev,knowledge-graph]"
# Copy environment template
cp .env.example .env
# Edit .env β at minimum set TRELIX_EMBEDDER_PROVIDER=localmake test # unit + MCP: 3,114 unit + 102 MCP = 3,216 tests (no coverage)
make test-fast # unit tests only, 3,114 (no API calls, fast)
make test-cov # unit tests with the coverage report
make lint # ruff check + ruff format (auto-formats before diff-check, cross-platform safe)
make format # ruff format
make typecheck # mypyNote on CI checks: The ruff format step runs as part of linting β files are auto-formatted before the diff-check, ensuring cross-platform consistency (Windows CRLF vs Unix LF).
# Unit tests only β no credentials needed
# Collects 3,114 of 3,220 and deselects the 106 integration tests.
pytest -m "not integration"
# Live integration tests β require Azure or AWS credentials (104 tests)
pytest tests/integration/
# tests/integration/test_llm_e2e.py covers Azure + Bedrock chat and embeddings;
# individual tests skip gracefully when the relevant credentials are absentYou do not apply the integration marker by hand: tests/integration/conftest.py
applies it to every test in that directory, so a new file there is credential-gated on
arrival. The marker is registered in pyproject.toml under [tool.pytest.ini_options] markers, and addopts carries --strict-markers so a typo becomes a collection error.
Both matter β while the marker was unregistered, -m "not integration" matched nothing
and quietly ran the entire suite including the live Azure/Bedrock tests.
main β stable releases only (do not push directly)
ββ develop β integration branch β open PRs here
ββ feature/<name> β your work
- Fork the repo and create a branch from
develop:git checkout -b feature/my-feature develop - Make your changes with tests
- Open a PR targeting
develop(notmain)
The graph module lives at src/trelix/graph/ and is organized as:
| File | Responsibility |
|---|---|
code_graph.py |
CodeGraph β NetworkX MultiDiGraph over SQLite edge tables |
community.py |
Community detection (Louvain/Girvan-Newman) |
persistence.py |
Save/load community + centrality to graph_metadata table |
concepts.py |
LLM semantic concept extraction (crash-safe) |
builder.py |
GraphBuilder β orchestrates the full pipeline |
visualizer.py |
Pyvis HTML export (requires trelix[knowledge-graph]) |
search.py |
BFS graph_search function (4th retrieval leg) |
Tests live in tests/unit/test_graph_*.py. All graph tests can run without pyvis or any LLM configured.
Opt-in config keys (all default to off β zero impact when disabled):
| Key | Default | Env var |
|---|---|---|
graph_search_enabled |
False |
TRELIX_RETRIEVAL_GRAPH_SEARCH_ENABLED=true |
graph_search_depth |
2 |
β |
graph_search_max_results |
15 |
β |
Adding a new graph algorithm:
- Add the implementation to the most appropriate existing file or create a new file under
src/trelix/graph/ - Expose it through
GraphBuilderinbuilder.pyso the pipeline can call it - If it requires a new optional dependency, add an extras group to
pyproject.tomland document it here - Write tests in
tests/unit/test_graph_<name>.pyβ mock any LLM calls; do not require pyvis
The retrieval enhancement modules live at src/trelix/retrieval/ and are organized as:
| File | Responsibility |
|---|---|
query_expansion.py |
HyDEExpander (synthetic snippet embedding), MultiQueryExpander (N-variant recall) |
flare.py |
FLARELoop β confidence-gated re-retrieval, _contains_uncertainty phrase check |
telemetry.py |
TelemetryWriter β crash-safe per-query latency/intent recorder |
All three modules are crash-safe (return empty/original on any failure) and gated by config flags.
Opt-in config keys (all default to off β zero impact when disabled):
| Key | Default | Env var |
|---|---|---|
query_expansion_enabled |
False |
TRELIX_QUERY_EXPANSION_ENABLED=true |
flare_enabled |
False |
TRELIX_FLARE_ENABLED=true |
telemetry_enabled |
False |
TRELIX_TELEMETRY_ENABLED=true |
Adding a new query enhancement:
- Add the implementation under
src/trelix/retrieval/ - Ensure any failure path returns the original query or empty results β never raises
- Gate the feature with a config flag defaulting to
False - Write tests in
tests/unit/test_retrieval_<name>.pyβ mock any LLM calls
The evaluation harness lives at src/trelix/eval/ and is organized as:
| File | Responsibility |
|---|---|
ndcg.py |
Pure-Python ndcg_at_k, recall_at_k, mrr β no pandas dependency |
harness.py |
EvalHarness.run(golden_path) β reads JSONL, retrieves, returns aggregate metrics |
Usage:
trelix eval --golden .trelix/golden.jsonlGolden file format (one line per query):
{"query": "how does auth work", "relevant_files": ["src/auth.py"]}Adding new metrics:
- Add the pure-Python metric function to
src/trelix/eval/ndcg.py - Wire it into
EvalHarness.run()insrc/trelix/eval/harness.py - Write tests in
tests/unit/test_eval_<name>.pyβ no LLM calls required for metric functions
The agent module lives at src/trelix/agent/ and implements a ReAct (Reason + Act) loop over the trelix retrieval stack:
| File | Responsibility |
|---|---|
actions.py |
ActionType enum, AgentAction, Observation, Turn dataclasses |
history.py |
TurnHistory, HistoryCompressor (token-budget context trimming) |
tools.py |
OpenAI function-calling tool schemas for 4 actions |
loop.py |
AgentLoop orchestrator β ReAct ThoughtβActionβObservation cycle |
All agent tests live in tests/unit/test_agent_*.py. No LLM calls are needed β TrelixChatClient is mocked throughout the test suite.
The analysis module lives at src/trelix/analysis/ and provides static program analysis on top of the indexed codebase:
| File | Responsibility |
|---|---|
defuse.py |
DataFlowExtractor β tree-sitter def-use chain extraction (crash-safe) |
taint.py |
TaintAnalyzer β Semgrep CLI wrapper (requires trelix[taint]) |
To use taint analysis, install the optional extra:
pip install -e ".[taint]"Tests that exercise TaintAnalyzer mock the subprocess call, so the full test suite runs without Semgrep installed.
SparseEmbedder produces {token_id: weight} SPLADE-Code vectors and requires the optional extra:
pip install -e ".[sparse]"SparseStore is a SQLite inverted index β no external service or vector database is needed. Tests run without torch: SparseEmbedder returns {} automatically when _TORCH_AVAILABLE=False, so the sparse test suite passes in any environment.
- Create
src/trelix/indexing/parser/extractors/<language>.py - Subclass
BaseParserfromsrc/trelix/indexing/parser/base.py - Implement
parse(source: str, file_id: int) -> ParseResult - Register in
src/trelix/indexing/parser/registry.py: addLanguage.YOURLANG: YourParser() - Add file extensions to
EXTENSION_MAPinsrc/trelix/indexing/walker.py - Add
Language.YOURLANGtoWalkerConfig.languagesdefault list insrc/trelix/core/config.py - Write tests in
tests/unit/test_parser_<language>.pywith fixture files
trelix ships with built-in support for multiple embedding backends:
- Local embeddings (
local) β Uses transformers library (default, no API keys needed) - BGE-Code-v1 (
bge-code) β BAAI General Embedding for code. Experimental: the wrapper pools with CLS while BAAI publishespooling_mode_lasttoken: true, so retrieval quality is unverified (see docs/PROVIDERS.md) - Nomic CodeRankEmbed (
nomic-code) β Open-source embeddings specialized for code ranking - Azure OpenAI Embeddings β Enterprise deployment via Azure; set
TRELIX_EMBEDDER_PROVIDER=azure - Bedrock β AWS-hosted embeddings via Bedrock
To use BGE-Code or CodeRank embeddings, install the optional extra:
pip install -e ".[bge-code]"
# Then set TRELIX_EMBEDDER_PROVIDER=bge-code in .envtrelix uses a provider-agnostic TrelixChatClient ABC (src/trelix/llm/client.py). All five built-in backends (OpenAIBackend, AnthropicBackend, BedrockBackend, VertexBackend, LiteLLMBackend) implement the same three methods: complete(), stream(), and tool_call(). Adding a new provider requires zero changes to business logic (chunker, synthesizer, planner, graph_rag).
- Create
src/trelix/llm/providers/<name>_backend.py - Subclass
TrelixChatClientand implementcomplete(),stream(),tool_call(). Incomplete(), translate the provider's stop field through a table insrc/trelix/llm/finish_reasons.py(normalise, orclassify_chat_choicefor an OpenAI-shapedchoices[0]): a value nobody has classified, or a missing one, isunknown, neverstop, because providers return refusals, filtered replies and truncations as ordinary successful responses. Add the provider's values totests/unit/test_llm_finish_reason_matrix.py. The "not configured" placeholder a backend returns when it has no credentials keepsfinish_reason="stop"and is recognised bymodel == UNCONFIGURED_MODEL, so check that, not the finish reason - Add a
case "<name>":branch tosrc/trelix/llm/factory.py(build_chat_client()) - Add credential fields to
LLMConfiginsrc/trelix/core/config.py - Add
"<name>"to theLiteraltype ofLLMConfig.provider - Add an optional dep group to
pyproject.tomlif the provider SDK is not already a dependency - Write unit tests in
tests/unit/test_llm_<name>_backend.pyβ mock the provider SDK, no real API calls
No changes are needed in chunker.py, synthesizer.py, planner/agent.py, or graph_rag.py.
FederatedRetriever (src/trelix/federation/retriever.py) ships with a SHA-256 keyed, thread-safe TTL cache:
from trelix.federation.retriever import FederatedRetriever
# Default: 120-second TTL
fed = FederatedRetriever(registry)
# Custom TTL
fed = FederatedRetriever(registry, cache_ttl=300.0)
# Disable caching entirely
fed = FederatedRetriever(registry, cache_ttl=0)
# Inspect cache performance
stats = fed.cache_stats() # -> {"hits": int, "misses": int, "size": int}
# Invalidate all cached results
fed.clear_cache()The cache achieves ~90% hit rate for typical debugging sessions. Set cache_ttl=0 in tests to avoid stale results across test cases.
MultiRepoWatcher (src/trelix/indexing/multi_watcher.py) drives a single watchfiles.awatch() call over all repos registered in RepoRegistry. A SHA-256 hash guard prevents re-indexing unchanged files; deleted files are removed from both SQLite and the vector store.
# Install the watch extra
pip install -e ".[watch]"
# Watch all registered repos
trelix watch-allTo integrate programmatically:
from trelix.indexing.multi_watcher import MultiRepoWatcher
watcher = MultiRepoWatcher(registry=repo_registry, indexer=indexer)
await watcher.run() # streams per-repo stats; graceful on KeyboardInterruptGitHubPRClient (src/trelix/review/github.py) fetches PR diffs via the GitHub REST API and posts batched review comments back. Requires the GITHUB_TOKEN environment variable.
# Review a PR diff locally
trelix review --pr owner/repo#42
# Review and post findings as a GitHub review
trelix review --pr owner/repo#42 --post-commentsAll seven GitHub file status values (added, modified, removed, renamed, copied, changed, unchanged) are handled. PRs with more than 3,000 files emit a truncation warning.
To call DiffReviewer directly with a raw unified diff string (skipping file I/O):
from trelix.review.diff import DiffReviewer
reviewer = DiffReviewer(llm_client=client)
findings = await reviewer.review(diff_text=raw_unified_diff)parse_pr_ref("owner/repo#42") is the canonical helper for parsing --pr argument values.
- Python 3.12+ type hints everywhere
- Line length: 100 chars (ruff enforced)
- No mutable default arguments
- New objects, never mutate in-place
- Functions > 50 lines should be split
Use the GitHub issue templates:
- Bug report β include Python version, OS, trelix version, minimal reproduction
- Feature request β describe the use case, not just the solution
Open a GitHub Discussion for questions.
trelix follows Semantic Versioning 2.0.0. Read the current version from the tree rather than from this sentence:
python -c "import trelix; print(trelix.__version__)"This section deliberately no longer carries a copy of the number. It said "Current version: 2.12.0" while v3.0.0, v3.0.1, v3.1.0, v3.1.1 and v3.1.2 shipped β five releases of rot inside the very section that tells you to keep versions in sync. The doc-stamp grep in the release checklist below would now catch it, but a prose stamp no release step has to touch is better deleted than monitored.
- CLI commands and flags:
trelix index,trelix search,trelix ask,trelix query,trelix stats,trelix watch,trelix update-index,trelix migrate-vectorsand all documented flags - Python API:
IndexConfig,EmbedderConfig,LLMConfig,Indexer,Retriever,TrelixChatClient,ChatMessage,ChatResponse,ToolCallResponse,build_chat_client,BaseEmbedder,make_embedder - Sub-package interfaces:
TrelixRetriever(trelix-langchain),TrelixIndexRetriever(trelix-llama-index), MCP tool signatures (trelix-mcp) β note:search_codereturn type changed to{results, next_cursor, total_available}envelope in v2.4.0 (see Breaking Changes in CHANGELOG) - Environment variable names: all
TRELIX_*env vars documented in.env.example
- Removing or renaming a public class, method, or CLI flag
- Changing a method signature in an incompatible way
- Changing the SQLite schema in a way that requires re-indexing
- Removing a previously supported Python version
CLI command renames (e.g. trelix graph β trelix call-graph in v2.0.0) are breaking changes and must be documented under a ### Breaking Changes heading in CHANGELOG.md for the relevant release, alongside a migration note showing the old and new invocation.
- Deprecated features are marked with
DeprecationWarningand noted in the CHANGELOG - The grace period is minimum 2 minor versions and minimum 3 months, whichever lands later, with removal only on a major bump. docs/BACKWARDS_COMPATIBILITY.md is authoritative β go there for the reasoning and the current deprecation table
- The CLI will print a deprecation notice on first use of deprecated flags
This file previously said "at least one minor version", contradicting the policy doc's "2 minor versions". The stricter number won: trelix shipped eight minor releases in the 30 days from v2.4.0 to v2.12.0, so a one-minor grace period can be over in days.
- Supported: Python 3.12, 3.13, 3.14
- Dropped versions are announced one minor release in advance
Eleven files carry the release version, in twelve stamps β server.json carries it
twice. All twelve are gated. Bump them together. Missing one ships a package whose --version disagrees with
its metadata, or a Helm chart that advertises one version and deploys another; neither is
hypothetical. helm/trelix/values.yaml's image.tag sat on 2.12.0 while Chart.yaml
advertised appVersion: 3.1.2, and both adapters sat at 2.4.0 while core reached
3.1.2 β which skip-existing: true turned into a silent 2-of-4 publish on every tag.
| # | Site | Key | Gated by verify-version |
|---|---|---|---|
| 1 | pyproject.toml |
[project] version |
yes |
| 2 | src/trelix/__init__.py |
__version__ |
yes |
| 3 | packages/trelix-mcp/pyproject.toml |
[project] version |
yes |
| 4 | packages/trelix-mcp/src/trelix_mcp/__init__.py |
__version__ |
yes |
| 5 | helm/trelix/Chart.yaml |
appVersion: |
yes |
| 6 | helm/trelix/values.yaml |
image.tag |
yes |
| 7 | packages/trelix-mcp/server.json |
version and packages[0].version |
yes (both) |
| 8 | packages/trelix-langchain/pyproject.toml |
[project] version |
yes |
| 9 | packages/trelix-langchain/src/trelix_langchain/__init__.py |
__version__ |
yes |
| 10 | packages/trelix-llama-index/pyproject.toml |
[project] version |
yes |
| 11 | packages/trelix-llama-index/src/trelix_llama_index/__init__.py |
__version__ |
yes |
.github/workflows/release.yml's verify-version job now fails the release if a v*
tag disagrees with any of those stamps, which is what turns a missed bump from a silent
mis-publish into a red build. It runs twelve check calls β one per stamp above, with no
exceptions. Site 4 used to be one: it was documented as "verify it by hand until that check
is added", which is the same silent-mis-publish risk as the adapters had, so it is now
checked like the rest.
Sites 4 and 8β11 are newly gated.
docs/BACKWARDS_COMPATIBILITY.md
has always put all three integration packages on the core version; both adapters sat at
2.4.0 anyway, across the seventeen releases that shipped after it, and nothing in CI
noticed. Read the jump as a
re-alignment to that line rather than as adapter change:
git diff v2.4.0..HEAD -- 'packages/trelix-*/src/*/retriever.py' is 13 insertions and 3
deletions, all
type annotations, so 3.1.2 adds no feature and breaks nothing the adapters exposed at
2.4.0.
Two more files hold the number without being sites of their own:
packages/trelix-langchain/tests/test_retriever.py and
packages/trelix-llama-index/tests/test_retriever.py each assert their own package's
__version__. Bump those literals too β but they are assertions on sites 9 and 11, not
independent stamps, so they stay out of the table and out of the count. Skipping them
turns a suite red rather than shipping anything wrong, and release.yml's test job now
runs both adapter suites, so that red arrives on the tag and not only in ci.yml (which
never fires on one).
Two things that are not version sites, and must not be bumped with them:
helm/trelix/Chart.yamlhas bothversion:(the chart's own version, independent of trelix; the comment in that file says when it moves) andappVersion:(which tracks trelix). OnlyappVersionis a version stamp.- Both adapters'
dependencies = ["trelix>=3.0.0", ...]. Lockstep governs the version stamp β identity β not the dependency floor, which is a compatibility contract that moves only when an import demands it. Eachpyproject.toml:35records why it reads3.0.0: "the lowest published core verified to expose every name used here." An adapter stamped3.1.2that declarestrelix>=3.0.0is saying something true. Raising the floor to match a release is the mistake CHANGELOG v2.7.1 already reverted ("Unjustified dependency-floor bumps reverted") on these same two packages, where it had been raised on an unverified assumption about API usage.
Both directions of that grep fail silently, which is how image.tag stayed on 2.12.0
across five releases:
- Grepping for the new version lists only the sites already bumped. A stale site
produces no line, and a missing line is not a signal you will notice. Reproduced on a
tree with
values.yamlandserver.jsonleft at2.12.0: a grep for the new version over the five previously-listed files printed five clean hits and exited0. - Grepping for the previous version cannot see a site that skipped a release. The
same tree, grepped for the previous version, returned zero hits β the stale sites
read
2.12.0, not the previous version. A site stranded on 2.x is structurally invisible to this check β which is whyverify-version, not any grep in this checklist, is the actual gate. Every site that had really drifted was of that class:image.tag, both ofserver.json's fields, and both adapters at2.4.0.
So print the value each site actually holds and collapse them:
python - <<'PY'
import json, re, tomllib
from pathlib import Path
def toml(p): return tomllib.load(open(p, "rb"))["project"]["version"]
def rx(p, pat): return re.search(pat, Path(p).read_text(), re.M).group(1)
sj = json.load(open("packages/trelix-mcp/server.json"))
for label, got in [
("pyproject.toml", toml("pyproject.toml")),
("src/trelix/__init__.py", rx("src/trelix/__init__.py", r'__version__ = "([^"]+)"')),
("trelix-mcp/pyproject.toml", toml("packages/trelix-mcp/pyproject.toml")),
("trelix_mcp/__init__.py", rx("packages/trelix-mcp/src/trelix_mcp/__init__.py", r'__version__ = "([^"]+)"')),
("Chart.yaml appVersion", rx("helm/trelix/Chart.yaml", r'^appVersion:\s*"?([^"\s]+)')),
("values.yaml image.tag", rx("helm/trelix/values.yaml", r'^\s+tag:\s*"?([^"\s]+)')),
("server.json version", sj["version"]),
("server.json packages[0]", sj["packages"][0]["version"]),
("trelix-langchain/pyproject.toml", toml("packages/trelix-langchain/pyproject.toml")),
("trelix_langchain/__init__.py", rx("packages/trelix-langchain/src/trelix_langchain/__init__.py", r'__version__ = "([^"]+)"')),
("trelix-llama-index/pyproject.toml", toml("packages/trelix-llama-index/pyproject.toml")),
("trelix_llama_index/__init__.py", rx("packages/trelix-llama-index/src/trelix_llama_index/__init__.py", r'__version__ = "([^"]+)"')),
]:
print(f"{got:<10} {label}")
PYTwelve lines out, all the same version. Pipe it through | awk '{print $1}' | sort -u
and you should get exactly one line β more than one means a stale site, named rather
than merely absent. This mirrors verify-version's own extraction site for site, so a
clean local run predicts a green tag. tests/unit/test_release_version_gate.py asserts the
same agreement in CI, which is the part that catches drift before a tag exists at all β
the gate itself can only fail once someone has cut one.
Doc stamps rot every release, and they rot to arbitrary old versions β this file's own
"Current version" line reached 2.12.0-vs-3.1.2, five releases behind β so grepping
for the previous version misses exactly the worst cases. Grep for the assertions that
must name the current version, whatever number they currently hold:
grep -rnE '[Cc]urrent version|trelix(-mcp|-langchain|-llama-index)?==|^\*\*Version|image: .*trelix:' \
docs/*.md *.md packages/*/README.md \
| grep -v '^CHANGELOG.md' \
| grep -viE "new in|fixed in|added in|since v|what's new|removed in|deprecated in"That returns 15 readable lines β several of which are this section quoting its own
pattern β rather than the 251 that "every semver-shaped token that isn't the current
version" produces over the same files. It catches the stamps a previous-version grep
cannot: docs/CLI_REFERENCE.md's **Version:** header, the adapter == install pins in
docs/FAQ.md's pin-your-requirements answer, a Helm image: β¦trelix: tag. The pattern
finds those pins by package name, not by number, so it keeps working across bumps β naming
the number here would only rot this sentence. docs/LANGCHAIN_LLAMAINDEX_GUIDE.md no
longer has any: its install lines are deliberately unpinned, since a version hardcoded into
a doc's own install command is what rotted them last time.
CHANGELOG.md is excluded on purpose β it is an append-only historical record, never a
stamp to bump.
packages/*/README.md is in scope because each distribution's pyproject.toml sets
readme = "README.md", making those files the PyPI long description β the project page
readers copy commands from. The glob was docs/*.md *.md until this release, which never
descended into packages/, and that blind spot is exactly where the worst rot was:
packages/trelix-mcp/README.md hard-pinned trelix-mcp==2.12.0 in seven places, including
the primary command under its own ## Install heading, while the package shipped 3.1.2.
A grep with a blind spot reads as coverage. tests/unit/test_readme_install_commands.py
now asserts the same properties in CI, so this does not depend on anyone remembering to run
the grep.
Read every hit before editing. A blind sed over docs/ will silently rewrite
"New in v3.0.0" and the shipped-version table in ROADMAP.md, turning accurate
history into a false claim.
Every stamp above is a property of the source tree. Once the tag is pushed,
.github/workflows/verify-release.yml runs scripts/verify_release.py for you
automatically β it waits for release.yml's Release workflow AND
docker-publish.yml's Docker Publish workflow to both go green for that tag,
then runs every check and posts the same PASS/FAIL summary. Watch the "Verify
Release" run in the Actions tab before announcing the release. The manual command
remains available for ad-hoc re-verification β e.g. after fixing a bug in the
script itself, or re-checking an older release:
python scripts/verify_release.py --version X.Y.Z (confirm every check passes)
β it verifies the published artifacts themselves (PyPI, the Docker images, the
Helm chart at the tag, the GitHub Release binaries), which is a different claim
than "the stamps agreed with the tag."
A green "Verify Release" run is not the end of a release: nothing in CI updates the MCP
registry, so that entry only moves when someone runs the publish by hand. That is how the
io.github.sairam0424/trelix entry came to read 0.5.1 when PyPI already had 3.4.2.
Once PyPI shows the new trelix-mcp version and "Verify Release" is green, publish the
same version to the registry. It has to come after PyPI, because the registry checks the
package named in server.json against PyPI, where it looks for the mcp-name: line that
packages/trelix-mcp/README.md carries (keep it).
cd packages/trelix-mcp
mcp-publisher validate # server.json against the registry schema; both of its version fields are site 7 above
mcp-publisher login github # GitHub device flow, as the account that owns the io.github.sairam0424 namespace
mcp-publisher publish # publishes ./server.json
curl -s 'https://registry.modelcontextprotocol.io/v0/servers?search=io.github.sairam0424/trelix' | python -m json.toolmcp-publisher installs with brew install mcp-publisher. The curl is the check, not
the publish: its output must name the version you just released, in both
servers[0].server.version and servers[0].server.packages[0].version. An old version
there means the publish did not land.
trelix ships three integration packages. To work on them:
# Install a sub-package in editable mode
pip install -e packages/trelix-mcp/
pip install -e packages/trelix-langchain/
pip install -e packages/trelix-llama-index/
# Run tests for a specific package
python -m pytest packages/trelix-mcp/tests/ --override-ini="testpaths=packages" -v
python -m pytest packages/trelix-langchain/tests/ --override-ini="testpaths=packages" -v
python -m pytest packages/trelix-llama-index/tests/ --override-ini="testpaths=packages" -vEach package has its own pyproject.toml and tests/ directory. The src/ layout mirrors the main package.