MatrixHub is the API and registry for Matrix apps, tracking health and LKG metadata.


Matrix Guardian coordinates probes, writes /status to MatrixHub, and asks matrix-ai for plans.


Matrix System 1.0 turns a static registry into an alive, policy-governed, self-healing platform (observe → plan → approve/execute → audit).


⚡ Matrix CLI
*The command-line interface for **Matrix Hub** — search, inspect, install, run, probe MCP servers, manage remotes, check connectivity, and safely uninstall.*
[](https://pypi.org/project/matrix-cli/)
[](https://pypi.org/project/matrix-cli/)
[](https://github.com/agent-matrix/matrix-cli)
[](https://agent-matrix.github.io/matrix-cli/)
[](./LICENSE)
Requires **Python 3.11+** and **matrix-python-sdk ≥ 0.1.9**.
🌍 Why Matrix CLI
Matrix CLI gets you from **discovery → install → run → interact** with agents, tools, and MCP servers — fast. Built to be secure by default, delightful for developers, and friendly for automation w


, tools, and MCP servers — fast. Built to be secure by default, delightful for developers, and friendly for automation worldwide.
🚀 What’s new in v0.1.6
Aligned with **matrix-python-sdk 0.1.9** (backwards-compatible refactor) and introduces a faster way to talk to your agents.
• **✨ New:** matrix do <alias> <prompt> — the quickest way to interact with a running agent.
• **Smarter runner discovery:** Auto-materializes runner.json from embedded b64/URL/object, embedded manifests (v1/v2), on-disk search, or inference (server.py, package.json). Synthesizes connector runners if an MCP URL is present.
• **Safer materialization:** Writes only under your target; supports base64 files, git/http artifacts; robust logging.
• **Faster environment prep:** Python: fresh venv + upgraded pip/setuptools/wh


iles, git/http artifacts; robust logging.
• **Faster environment prep:** Python: fresh venv + upgraded pip/setuptools/wheel, then requirements.txt or editable pyproject.toml / setup.py. Node: auto-detects **pnpm > yarn > npm**.
• **Connector-aware run (attach mode):** If runner.json is a connector with an MCP SSE URL, matrix run attaches (no local process). matrix stop becomes a no-op (clears the lock).
• **Better MCP probing & calls:** Tolerates /sse vs /messages/, clearer errors, --json for scripts.
• **Idempotent installs:** Re-install same alias with --force --no-prompt without surprises.
Tip: export MATRIX_SDK_DEBUG=1 for verbose installer logs.
🎬 A 5-Minute End-to-End Demo
Experience the full lifecycle with a Watsonx agent — from search to results.
0) Setup
Create a local .env with y


nd Demo
Experience the full lifecycle with a Watsonx agent — from search to results.
0) Setup
Create a local .env with your credentials:
1) 🔍 Discover
2) 📦 Install
3) 🚀 Run
4) ✨ Ask with **matrix do**
5) ⚙ Advanced call
6) 📋 Manage & clean up
📦 Install
Optional extras
⚙ Configuration
The CLI reads, in order: **environment variables**, ~/.config/matrix/cli.toml (optional), then built-ins.
Environment
Optional TOML (~/.config/matrix/cli.toml)
🏁 Quick start
**Demo GIFs**
🔍 Search tips
Useful filters:
• --type {agent|tool|mcp_server}
• --mode {keyword|semantic|hybrid}
• --capabilities rag,sql
• --frameworks langchain,autogen
• --providers openai,anthropic
• --with-snippets
• --certified (registered/certified only)
• --json for programmatic output
• --exact to fetch a specific ID
Examples:
If


• --certified (registered/certified only)
• --json for programmatic output
• --exact to fetch a specific ID
Examples:
If the public Hub is unreachable, some operations try a **local dev Hub** once and tell you.
🧩 Install behavior (safer by design)
• Accepts name, name@ver, ns:name, ns:name@ver.
• If ns missing, prefers **mcp_server**.
• If @version missing, picks **latest** (stable > pre-release).
• Uses a small cache under ~/.matrix/cache/resolve.json (per-hub, short TTL).
• **No absolute paths sent to the Hub** — the CLI sends a safe <alias>/<version> label, then **materializes locally**.
• Preflight checks ensure your local target is **writable** before network calls.
Examples:
▶ Run, interact, and probe
matrix run <alias> prints a click-ready **URL** and **Health** link, plus a logs h


Examples:
▶ Run, interact, and probe
matrix run <alias> prints a click-ready **URL** and **Health** link, plus a logs hint.
🔗 Connector mode (attach to a remote/local MCP)
If you already have an MCP server listening (e.g. on http://127.0.0.1:6289/sse), **attach** to it without starting a local process by using a **connector runner**:
~/.matrix/runners/<alias>/<version>/runner.json:
Then:
In connector mode, matrix stop simply clears the lock (no local process to kill).
🧪 MCP utilities (SSE/WS)
Probe and call tools on MCP servers.
Notes:
• SSE works with mcp>=1.13.1 (installed via the mcp extra).
• WebSocket URLs (ws:///wss://) require the websockets package.
• If a call fails, the CLI helps by listing tools and tolerates /sse vs /messages/ endpoints.
🧭 Process management
matrix ps shows a


ails, the CLI helps by listing tools and tolerates /sse vs /messages/ endpoints.
🧭 Process management
matrix ps shows a **URL** column built from the runner’s port and endpoint (default /messages/).
Copy the URL directly into:
Script-friendly output:
Other commands:
🌐 Hub health & TLS
TLS policy:
• Respects REQUESTS_CA_BUNDLE / SSL_CERT_FILE.
• Tries OS trust (when available).
• Falls back to certifi.
• Never throws on network errors in health checks — returns a structured status with exit codes.
🧹 Safe uninstall
Remove one or many aliases, and optionally purge local files.
Safety features:
• Only purges targets under ~/.matrix/runners by default.
• Skips deleting files still referenced by other aliases.
• --force-files allows deleting outside the safe path (⚠ **dangerous**; off by defaul


till referenced by other aliases.
• --force-files allows deleting outside the safe path (⚠ **dangerous**; off by default).
• --stopped-only to avoid touching running aliases.
Exit codes: **0** success, **2** partial/failed.
🧰 Scripting & CI examples
🐞 Troubleshooting
• **“Missing 'mcp' package”** — Install the optional extra: pip install "matrix-cli[mcp]" (and pip install websockets for WS).
• **TLS / certificate errors** — Set SSL_CERT_FILE or REQUESTS_CA_BUNDLE to your CA bundle.
• **Alias not found when probing** — Use the alias shown by matrix ps (case-insensitive), or pass --url directly.
• **Connector mode shows PID=0** — Expected in attach mode; ensure the remote server is running.
🛠 Development
🌍 About MatrixHub
MatrixHub aims to be the **pip of agents & MCP servers** — a secure,


server is running.
🛠 Development
🌍 About MatrixHub
MatrixHub aims to be the **pip of agents & MCP servers** — a secure, open, and developer-friendly registry and runtime that scales from personal laptops to global enterprises. If you’re building agents, tools, or MCP services, Matrix CLI gets you from idea to running in seconds.
📄 License
Apache License 2.0
✉ Feedback
Issues and PRs welcome! If you hit rough edges with install/probing/health, the new **connector** flow, or ps --plain/--json and uninstall, please open an issue with your command, output, and environment.
• GitHub: https://github.com/agent-matrix/matrix-cli
• PyPI: https://pypi.org/project/matrix-cli/


<!-- docs/changelog.md -->
Changelog
v0.1.3
• **Connector runner** supported end‑to‑end (pid=0; URL only)
• MCP client: one‑retry tolerance between /sse and /messages/
• Better error messages: show advertised tool names on call failure
• Runtime: stop is a no‑op for connectors; doctor acknowledges remote URL
v0.1.2
• matrix connection (human/JSON; exit code 0/2)
• matrix mcp (probe/call; SSE; WS optional)
• Safer installs (no absolute paths leaked to the Hub)
• matrix ps shows URL; --plain & --json
• matrix uninstall with optional --purge
• TLS hardening


do — One‑shot MCP Call
A fast, zero‑JSON way to call an MCP tool exposed by a running server. You give an **alias** (or a full **URL**) and optional **text** or a **file path**; do selects a sensible tool and builds the arguments for you.
Prefer do for quick ad‑hoc prompts. If you need structured input, use matrix mcp call with --args, --kv, --text, or --wizard.
Synopsis
**Positional arguments**
• TARGET  — Optional alias or catalog name you installed earlier (same as --alias).
• TEXT    — Optional plain‑text input mapped to the tool’s default input field.
**Options**
• --alias ALIAS  — Alias shown by matrix ps. Auto‑discovers host/port/endpoint.
• --url URL      — Full SSE/WebSocket endpoint (e.g., http://127.0.0.1:52305/messages/).
• --in PATH      — Path‑like input. If the schema define


SE/WebSocket endpoint (e.g., http://127.0.0.1:52305/messages/).
• --in PATH      — Path‑like input. If the schema defines path/file fields, it maps there; otherwise it uses the default input key.
• --timeout SEC  — Connect/read timeout (default: 10.0).
• --json         — Emit structured JSON instead of human text.
Exit codes: 0 success · 2 user/guidance error · 130 interrupted
What do infers for you
URL / endpoint (from alias)
• Reuses the same discovery helpers as matrix mcp:
• Finds the running row for the alias via matrix_sdk.runtime.status().
• If you did **not** provide an endpoint, it reads <target>/runner.json and falls back to "/messages/" when unspecified.
• Builds http://{host}:{port}/{endpoint}/ and picks **SSE** automatically for HTTP(S) URLs.
• If you pass --url, it is used **


ds http://{host}:{port}/{endpoint}/ and picks **SSE** automatically for HTTP(S) URLs.
• If you pass --url, it is used **as‑is**. Many MCP servers require a **trailing slash** (e.g., /sse/).
Tool selection
• Picks a *default* tool by name preference: **default → main → run → chat**.
• If none match, uses the **first** tool exposed by the server.
Input mapping
• If you provided **TEXT**, do maps it to a default input key inferred from the tool’s input schema:
1. x-default-input
2. first of query | prompt | text | input | message
3. a single **required** string property
4. the only **string** property
• If you provided **--in PATH**, do prefers common path keys (path, file, filepath, filename, input_path); otherwise it maps PATH to the default input key (above).
• If the schema requires struc


filepath, filename, input_path); otherwise it maps PATH to the default input key (above).
• If the schema requires structured input and do can’t infer it safely, it will guide you to use matrix mcp call --wizard.
Output rendering
• Prints text content blocks directly. Non‑text blocks are shown as - <type>: <repr>. Use --json for machine‑readable output.
Examples
1) Quick chat‑style prompt (alias)
2) Using TARGET positional instead of --alias
3) Explicit URL (note the trailing slash)
4) File path mapping
5) JSON output (pipe into jq)
Troubleshooting
**“Provide --url OR --alias (optionally with --port).”**
You didn’t supply a way to connect. Add --alias <name> (preferred) or --url http://host:port/path/.
**404 or route error**
If you passed --url without a trailing slash (e.g., /sse), many s


r --url http://host:port/path/.
**404 or route error**
If you passed --url without a trailing slash (e.g., /sse), many servers expect /sse/.
**“Multiple inputs or no clear default input detected.”**
The tool needs structured arguments. Use the guided mode:
**“No tools exposed by the server.”**
Verify the server is healthy and actually exposes tools. Try:
**Unsupported URL scheme**
WebSocket requires ws:// or wss://. HTTP(S) uses SSE by default.
When to use do vs mcp call
• Use **do** for quick one‑shot prompts where a single text or file path is enough.
• Use **mcp call** when you need full control over arguments (--args JSON, --kv, --text, or interactive --wizard).
See also
• mcp command
• quickstart
• matrix ps, matrix run, matrix logs, matrix stop


<!-- docs/index.md -->
Matrix CLI (v0.1.3)
**MatrixHub** is building the *pip of agents & MCP servers* — search, install, run, and connect.
The **Matrix CLI** is a thin, fast UX over the Matrix Python SDK. It works with public and private Matrix Hubs.
Why MatrixHub?
• **Discoverability** — Search a global catalog of agents, tools, and MCP servers.
• **One‑line installs** — Clean install plans; local materialization stays on your machine.
• **Runtime built‑in** — Start/stop processes, tail logs, and health‑check.
• **MCP‑native** — Probe and call tools over SSE/WS with zero boilerplate.
• **Enterprise‑grade TLS** — Honors corporate CAs via SSL_CERT_FILE / REQUESTS_CA_BUNDLE.
CLI highlights
• matrix search — rich filters, JSON output for scripting
• matrix install — safe planning; no absolut


E.
CLI highlights
• matrix search — rich filters, JSON output for scripting
• matrix install — safe planning; no absolute paths sent to the Hub
• matrix run, matrix ps, matrix logs, matrix stop
• matrix mcp probe / matrix mcp call — attach to **local** or **remote** MCP
• matrix connection — Hub health (human + JSON)
• matrix uninstall — safe, scriptable removal
Example (end‑to‑end)
Environment variables
Project links
• Website: https://matrixhub.io
• CLI: published as matrix-cli (PyPI/pipx)
• SDK: matrix-python-sdk


<!-- docs/install.md -->
Install
**Requirements**: Python **3.11+**.
Recommended
Virtualenv
Optional extras
Upgrading
Using local wheels (offline/dev)
If you built local wheels (e.g. the SDK: matrix-python-sdk-0.1.6), install them before the CLI:


MCP (Probe & Call)
Matrix CLI speaks **Model Context Protocol** (MCP) over **SSE** and optionally WebSocket.
Two ways to attach
• **Process runner** — CLI starts a local server from runner.json (type python/node).
• **Connector runner** — CLI records a **remote** url (pid=0) and does **not** spawn a process.
Both are supported and non‑breaking.
Probe
CLI will automatically retry **once** if /sse vs /messages/ is mismatched.
**Gotcha:** When you pass --url, the CLI uses it **as‑is**. Many MCP servers require a **trailing slash** (e.g., /sse/). If you see 404s or route errors, add the trailing /.
Call a tool
**Tip:** If a call fails, the CLI prints the tool list advertised by the server (e.g., chat).
Argument forms (choose one)
Pick any of these equivalent ways to pass inputs to a tool. Thes


by the server (e.g., chat).
Argument forms (choose one)
Pick any of these equivalent ways to pass inputs to a tool. These are especially handy for simple chat‑style tools that expect a query string.
**Simplest (let the CLI infer the argument name):**
**KV style (no JSON):**
**Proper JSON (quote the whole object):**
**With URL (note the trailing slash):**
**From stdin:**
**Bash/WSL tip:** Wrap JSON in **single quotes** so you don’t have to escape the inner double quotes.
Example: connector runner
Create ~/.matrix/runners/watsonx-chat/0.1.0/runner.json:
Then:


<!-- docs/quickstart.md -->
Quickstart
1) Check the Hub
2) Search the catalog
3) Install safely
4) Run + inspect
5) MCP probe & call
By alias (auto‑discovers URL/endpoint)
By explicit URL
**Tip (bash/WSL):** Wrap JSON in **single quotes** so you don’t have to escape the inner double quotes.
6) Stop & uninstall


<!-- docs/runtime.md -->
Runtime (run / ps / logs / stop)
Run
• For **process** runners, CLI launches the server and writes a lock with real PID/port.
• For **connector** runners, CLI writes a lock with pid=0, port=null, url=<runner.url>.
List
Logs
Health
• Process: probes /health at http://127.0.0.1:<port>/health.
• Connector: returns **ok** with a message like *configured remote SSE url …* (may perform a short HEAD).
Stop
• Process: sends SIGTERM and removes the lock.
• Connector: **no‑op** (removes the lock only).


<!-- docs/troubleshooting.md -->
Troubleshooting
"ExceptionGroup: unhandled errors in a TaskGroup"
Usually means the CLI couldn’t establish an MCP session.
**Checklist**
1. **Is the server running?**
• Probe the URL directly: curl -I http://127.0.0.1:6288/sse (look for 200 OK).
• Try matrix mcp probe --url ... --json and inspect the error.
2. **Endpoint mismatch**
• Your server exposes /sse, older code might use /messages/.
• The CLI retries once with the alternate path automatically.
3. **Tool name mismatch**
• Server exposes chat, but you called watsonx-chat.
• The CLI now prints tool names on failures.
4. **Stale lock**
• If matrix run says locked, run matrix stop <alias> or remove ~/.matrix/state/<alias>/runner.lock.json.
5. **TLS / corporate CA**
• Set SSL_CERT_FILE *or* REQUESTS_CA_B


as> or remove ~/.matrix/state/<alias>/runner.lock.json.
5. **TLS / corporate CA**
• Set SSL_CERT_FILE *or* REQUESTS_CA_BUNDLE to your CA bundle.
Gateway 401 during install
Set MCP_GATEWAY_TOKEN or pass --gw-token in your PoC scripts.
Local wheels
Ensure your virtualenv resolves your dev wheels first, e.g. install matrix-python-sdk-*.whl before matrix-cli.


Matrix Python SDK
[](https://pypi.org/project/matrix-python-sdk/)
[](https://pypi.org/project/matrix-python-sdk/)
[](https://github.com/agent-matrix/matrix-python-sdk/actions/workflows/ci.yml)
[](https://github.com/agent-matrix/matrix-python-sdk/blob/master/LICENSE)
**matrix-python-sdk** is the official Python SDK for the Matrix Hub — the open catalog and installer for **agents**, **tools**, and **MCP servers**.
Built for teams that need **fast discovery**, **reproducible installs**, and **safe runtime** operations at scale.
What’s new in 0.1.9
A focused, compatibility-preserving refresh.
• **Modular installer** with legacy import preserved:
Internals split into installer/core.py, runner_schema.py, runner_discovery.py, envs.py, util.py.
• **Runner discovery** restored to legacy strategy or


/core.py, runner_schema.py, runner_discovery.py, envs.py, util.py.
• **Runner discovery** restored to legacy strategy order with modern safeguards:
b64 → URL → object → embedded manifest → file → shallow search → manifest URL (opt-in) → infer by structure → **connector** fallback.
• **Connector synthesis** (attach mode): auto-generates minimal
{"type":"connector","url":"…/sse"} when an MCP/SSE endpoint is found. Gate via MATRIX_SDK_ENABLE_CONNECTOR=1 (default **on**).
• **Windows venv reliability**: venv creation retries without symlinks when required.
• **Shared utilities**: env toggles, timeouts, runner search depth, safe FS checks in installer/util.py.
• **Docs**: updated install/usage/API pages; clearer examples.
Requires Python **3.11–3.12**.
Why teams choose this SDK
• **One interfac


ted install/usage/API pages; clearer examples.
Requires Python **3.11–3.12**.
Why teams choose this SDK
• **One interface** for a messy ecosystem — consistent search/install across agents, tools, and MCP servers.
• **Reproducible installs** — Hub-backed plans, adapters, and lockfiles you can ship to CI and prod.
• **Production guardrails** — safe archive extraction, Git host allow-lists, ETag-aware caching, typed models.
• **Performance at scale** — lean client, server-side indexing/scoring, normalized params to maximize cache hits.
Install
Python **3.11+** supported.
Quickstart
1) Search
Prefer the high-level helper for resilience and typed results:
2) Install
3) Run locally (no daemon)
Connector / attach mode (new in 0.1.6)
If runner.json has {"type":"connector","url":"http://127.0.0.1:6


ally (no daemon)
Connector / attach mode (new in 0.1.6)
If runner.json has {"type":"connector","url":"http://127.0.0.1:6288/sse"}, runtime.start(...) **does not** start a process. Instead it stores the URL in the lock (with pid=0). Use the URL directly with your MCP client.
4) Bulk register (optional)
Register servers (ZIP/dir/Git) into an MCP Gateway with concurrency, idempotency keys, and capability probing.
API surface (snapshot)
• matrix_sdk.client.MatrixClient: search, entity, install, list_remotes, add_remote, delete_remote, trigger_ingest
• matrix_sdk.search: search, search_try_modes, SearchOptions
• matrix_sdk.installer.LocalInstaller: plan, materialize, prepare_env, build
• matrix_sdk.runtime: start, stop, status, tail_logs, doctor, log_path
*(Lock files now include an optional ur


nv, build
• matrix_sdk.runtime: start, stop, status, tail_logs, doctor, log_path
*(Lock files now include an optional url for connector runners; stop() is a no-op for connectors.)*
• matrix_sdk.bulk.* (optional): discovery, gateway client, registrar
Pydantic models (v1/v2 compatible) in matrix_sdk.schemas: SearchItem, SearchResponse, EntityDetail, InstallOutcome, etc.
Reliability, Security, Performance
• **Reliability**: strict error types, small safe retries, idempotent bulk writes, optional ETag cache.
• **Security**: safe ZIP/TAR extraction, Git host allow-lists, deny-by-default where sensible.
• **Performance**: minimal client overhead, normalized search params, server-side scoring and indexing.
Links
• Docs: see docs/ (MkDocs) — Usage, API Reference, Bulk
• Source: https://github.com/


r-side scoring and indexing.
Links
• Docs: see docs/ (MkDocs) — Usage, API Reference, Bulk
• Source: https://github.com/agent-matrix/matrix-python-sdk
• Matrix Hub: https://github.com/agent-matrix/matrix-hub
• License: Apache 2.0
Contributing
We welcome issues and PRs. Please read **CONTRIBUTING.md** before submitting changes.
Join us in shaping a fast, safe, and open ecosystem for AI agents, tools, and MCP servers.


API Reference
SDK version: **0.1.9**
Scope: Hub catalog client • local installer (files, artifacts, env, runner discovery) • lightweight runtime
This SDK provides three pillars:
1. **MatrixClient** — search the Hub, read entities, request install plans.
2. **LocalInstaller** — materialize a plan locally (files, artifacts, env, runner.json).
3. **runtime** — start/stop/list/tail/doctor local MCP servers (no daemon).
New in **0.1.9**
* The installer has been refactored into a proper subpackage: matrix_sdk/installer/.
* Backwards-compatible import: **from matrix_sdk.installer import LocalInstaller still works**.
* More robust runner discovery (URLs, embedded manifests, shallow search, structural inference).
* Environment prep parity with the legacy installer (Python venv + pip indices, Node P


ow search, structural inference).
* Environment prep parity with the legacy installer (Python venv + pip indices, Node PM autodetect).
* New/expanded env toggles (see **Environment variables**).
MatrixClient
**Location**: matrix_sdk.client.MatrixClient
**Errors**: raises matrix_sdk.client.MatrixError(status, detail) on non-2xx.
**Notes**
• entity(id) URL-encodes id internally.
• install(id, target) calls Hub /catalog/install and returns the Hub payload (plan/results/etc).
• Treat type="any" (or None) as “no filter”.
Deep links
**Location**: matrix_sdk.deep_link
**Errors**: InvalidMatrixUri for bad/unsupported URIs.
Rules:
• Only matrix://install is supported.
• id is **required**; alias must match ^[a-z0-9][a-z0-9._-]{0,63}$.
LocalInstaller
**Location**: matrix_sdk.installer.LocalInstaller


quired**; alias must match ^[a-z0-9][a-z0-9._-]{0,63}$.
LocalInstaller
**Location**: matrix_sdk.installer.LocalInstaller
**Data classes**: BuildReport, EnvReport, BuildResult
**Other errors**: matrix_sdk.manifest.ManifestResolutionError, ArchiveFetchError, GitFetchError.
Public dataclasses (exact fields)
Lifecycle
1. **plan(id, target)** → ask Hub for an install plan
*Sends a server-safe target label by default (e.g. alias/version). Use MATRIX_INSTALL_SEND_ABS_TARGET=true to send the absolute path.*
2. **materialize(outcome, target)** → write declared files, fetch artifacts, ensure/validate runner.json
*Scans plan.files, results[].files, and top-level files. Supports content (text) and content_b64 (binary).*
3. **prepare_env(target, runner)** → create .venv (Python) and/or run pnpm|yarn|np


ent (text) and content_b64 (binary).*
3. **prepare_env(target, runner)** → create .venv (Python) and/or run pnpm|yarn|npm install (Node)
*Python: upgrades pip/setuptools/wheel; respects pip index envs; installs from runner.python.requirements, requirements.txt, or local project (pyproject.toml / setup.py) with optional editable mode. Node: auto-detects package manager from lockfiles unless overridden.*
4. **build(id, …)** → runs all three steps and returns BuildResult.
Methods (signatures & behavior)
Runner discovery (strategy order)
The installer uses a deterministic chain and stops at the first valid result:
1. plan.runner_b64 → decode → validate → write
2. plan.runner_url (relative resolved against provenance) → fetch JSON → validate → write
3. plan.runner (object) → validate → write
4.


url (relative resolved against provenance) → fetch JSON → validate → write
3. plan.runner (object) → validate → write
4. Embedded manifest (v2): manifest.runner → validate → write
5. Embedded manifest (v1): extract server URL → synthesize **connector** runner (/sse) → write
6. Local file: runner_file or runner.json
7. Shallow BFS for runner.json (depth = MATRIX_SDK_RUNNER_SEARCH_DEPTH, default **2**)
8. Manifest URL (plan.manifest_url or provenance.source_url), when MATRIX_SDK_ALLOW_MANIFEST_FETCH=true: fetch → parse → synthesize connector → write
9. Structural inference: infer minimal python/node runner from files (e.g., server.py, package.json) → write
10. Last-ditch: if *any* MCP/SSE URL hint is present, synthesize a minimal connector
A valid **process** runner must include "entry" and


f *any* MCP/SSE URL hint is present, synthesize a minimal connector
A valid **process** runner must include "entry" and "type": "python"|"node".
A valid **connector** runner must include "type": "connector" and "url".
runtime
**Location**: matrix_sdk.runtime
**State/Logs**: ~/.matrix/state/<alias>/runner.lock.json, ~/.matrix/logs/<alias>.log
**Model**: LockInfo(pid:int, port:int|None, alias:str, target:str, started_at:float, runner_path:str)
**Requirements**
• runner.json must define at least: {"type": "python"|"node", "entry": "…"}
• Python runners: venv Python must exist (created during prepare_env).
Artifact fetchers
HTTP archives
**Location**: matrix_sdk.archivefetch
**API**:
• Safe ZIP/TAR extraction (no path traversal).
• Optional flatten of GitHub-style single-folder archives.
Git r


**API**:
• Safe ZIP/TAR extraction (no path traversal).
• Optional flatten of GitHub-style single-folder archives.
Git repos
**Location**: matrix_sdk.gitfetch
**API**:
• HTTPS by default; host allow-list enforced (deny-by-default if none given).
• Shallow clone, optional sparse subdir, optional LFS, optional commit verification.
Schemas (Pydantic)
**Location**: matrix_sdk.schemas
Models mirror Hub responses; unknown fields are allowed.
• SearchItem, SearchResponse
• EntityDetail (extra fields allowed; includes manifest-ish + computed fields)
• InstallStepResult, InstallOutcome
• MatrixAPIError (generic error wrapper)
Exceptions
• matrix_sdk.client.MatrixError(status, detail) — Hub non-2xx.
• matrix_sdk.deep_link.InvalidMatrixUri — deep-link parse/validation errors.
• matrix_sdk.manifest.Ma


il) — Hub non-2xx.
• matrix_sdk.deep_link.InvalidMatrixUri — deep-link parse/validation errors.
• matrix_sdk.manifest.ManifestResolutionError — manifest/artifact integrity/host violations.
• matrix_sdk.archivefetch.ArchiveFetchError — download/unpack errors.
• matrix_sdk.gitfetch.GitFetchError — git materialization errors.
Environment variables
General
| Variable                         | Meaning                                                                           | Default   |
| MATRIX_SDK_DEBUG               | Enables verbose DEBUG logging for installer/runtime/archivefetch.                 | off       |
| MATRIX_HOME                    | Base dir for ~/.matrix (state/logs).                                            | user home |
| MATRIX_INSTALL_SEND_ABS_TARGET | Send absolute ins


ate/logs).                                            | user home |
| MATRIX_INSTALL_SEND_ABS_TARGET | Send absolute install path to Hub during plan() (instead of server-safe label). | off       |
Installer — runner discovery
| Variable                          | Meaning                                                             | Default     |
| MATRIX_SDK_RUNNER_SEARCH_DEPTH  | Depth for shallow runner.json search.                             | 2         |
| MATRIX_SDK_ALLOW_MANIFEST_FETCH | Permit fetching a remote manifest to synthesize a connector runner. | 1         |
| MATRIX_SDK_MANIFEST_DOMAINS     | CSV allow-list of domains allowed for remote manifest fetches.      | (allow all) |
| MATRIX_SDK_ENABLE_CONNECTOR     | Allow synthesizing connector runners from URL hints.


      | (allow all) |
| MATRIX_SDK_ENABLE_CONNECTOR     | Allow synthesizing connector runners from URL hints.                | 1         |
| MATRIX_SDK_HTTP_TIMEOUT         | Network timeout (seconds) for installer fetches.                    | 15        |
Installer — Python deps
| Variable                         | Meaning                                                 | Default     |
| MATRIX_SDK_PIP_INDEX_URL       | Primary pip index URL.                                  | pip default |
| MATRIX_SDK_PIP_EXTRA_INDEX_URL | Extra pip index URL.                                    | none        |
| MATRIX_SDK_PIP_EDITABLE        | Install local project as editable (pip install -e .). | 1         |
Git fetcher
| Variable                    | Meaning                             | Default


nstall -e .). | 1         |
Git fetcher
| Variable                    | Meaning                             | Default      |
| MATRIX_GIT_ALLOWED_HOSTS  | CSV allow-list for git clone hosts. | common hosts |
| MATRIX_GIT_ALLOW_INSECURE | Allow http:// git (discouraged).  | off          |
| MATRIX_SDK_DEBUG_GIT      | Extra git logs.                     | off          |
Search
**Location**: matrix_sdk.search
**Purpose**: Thin, defensive wrappers around GET /catalog/search with normalization, small retries, and optional mode fallbacks.
search(...)
**Behavior**
• Normalizes filters (lists/sets → CSV), clamps limit.
• Retries transient 5xx/network errors with exponential backoff.
• Optional **fallback chain** across modes (e.g., semantic → hybrid → keyword) when no results.
• Returns a dict by


off.
• Optional **fallback chain** across modes (e.g., semantic → hybrid → keyword) when no results.
• Returns a dict by default, or a typed SearchResponse if options.as_model=True.
**Example**
SearchOptions
Defaults:
• If fallback_order is not provided, the helper picks a sensible chain based on the initial mode.
• max_attempts applies to each HTTP attempt (initial + each fallback).
search_try_modes(...)
End-to-end example
Compatibility notes (0.1.9)
• The legacy matrix_sdk/installer.py module has been split into the **matrix_sdk/installer/** package.
• Import **compatibility is preserved**: from matrix_sdk.installer import LocalInstaller (and the dataclasses) still works.
• A few helpers are also re-exported for downstream compatibility:
_is_valid_runner_schema, _ensure_sse_url, _url_fro


s.
• A few helpers are also re-exported for downstream compatibility:
_is_valid_runner_schema, _ensure_sse_url, _url_from_manifest, _extract_mcp_sse_url, _coerce_runner_to_legacy_process.
Requirements
• Python **3.11 – 3.12** (per pyproject.toml).


Bulk Registration
The SDK includes a tiny, async **Bulk Registrar** that discovers MCP servers in a repo (or ZIP) and registers them into an **MCP Gateway Admin API** (e.g., http://localhost:4444). It’s optional and independent of the Hub.
!!! note
The registrar **does not** modify your local project. It discovers manifests and calls your **Gateway Admin API** to create/update server entries.
What it does (in one picture)
1. **Discovery (matrix-first)**
• If a matrix/ folder exists, read:
• matrix/index.json (list of servers **or** list of manifest paths), and/or
• matrix/*.manifest.json
• Else, fall back to pyproject.toml → [tool.mcp_server].
2. **Normalize → Manifest**
• Convert discovery into a canonical ServerManifest (Pydantic v1/v2 compatible).
3. **Register (upsert)**
• POST to your


• Convert discovery into a canonical ServerManifest (Pydantic v1/v2 compatible).
3. **Register (upsert)**
• POST to your Gateway’s Admin endpoint:
• Try **JSON** first.
• If the gateway rejects JSON with 422/400/415 (e.g. “Missing required field: 'name'”), auto-fallback to **form URL-encoded** with a sanitized name.
4. **Resilience**
• Idempotency key (SHA256 of manifest payload).
• Concurrency + exponential backoff.
• Optional **capability probe** (best-effort GET ${endpoint}/capabilities).
Quickstart (Python)
Typical successful result item from the gateway:
CLI usage (module)
You can invoke the registrar via the module entrypoint:
**Alternate sources**
**Optional flags**
• --concurrency 20 – number of parallel registrations
• --no-probe – disable capability probing
• --env-file .env.loca


*
• --concurrency 20 – number of parallel registrations
• --no-probe – disable capability probing
• --env-file .env.local – load env first (KEY=VALUE lines)
Source descriptor schema
A **source** describes where to discover manifests.
| Field   | Type | Required        | Example                                           |
| kind  | enum | yes             | "zip" \| "git" \| "dir"                     |
| path  | str  | for zip/dir | /abs/path/repo.zip or /abs/path/repo          |
| url   | str  | for git       | https://github.com/IBM/docling-mcp.git          |
| ref   | str  | optional (git)  | main / tag / SHA                                |
| probe | bool | optional        | true (default) – try ${endpoint}/capabilities |
You can pass **multiple** sources; the registrar handles each (wit


    | true (default) – try ${endpoint}/capabilities |
You can pass **multiple** sources; the registrar handles each (with overall concurrency control).
How discovery works
1. **Matrix folder (preferred)**
• matrix/index.json can be:
• An **array of strings** (each a path to *.manifest.json under matrix/), or
• An **array of objects** with "type": "mcp_server" and server fields.
• Any matrix/*.manifest.json is also read.
2. **Fallback: pyproject.toml**
• [tool.mcp_server] block is mapped to a ServerManifest.
• Endpoint is normalized to:
Registration behavior
• **JSON first**, then **auto-fallback to form** if the gateway says your JSON is invalid for the legacy “create” route:
• Common signal: HTTP 422 with "Missing required field: 'name'" or "Invalid name".
• **Name sanitization** (for the


ute:
• Common signal: HTTP 422 with "Missing required field: 'name'" or "Invalid name".
• **Name sanitization** (for the form fallback):
• Allowed: letters, digits, underscore (_), dot (.), hyphen (-), spaces.
• Disallowed chars are removed, whitespace is collapsed, and result is trimmed to 255 chars.
• If empty after cleaning, a stable fallback like server-<hash> is used.
**Idempotency**
• For each manifest, the registrar computes a SHA256 idempotency key from the JSON payload (sorted keys).
• You can safely re-run without creating duplicates (server-side must support idempotency).
**Concurrency & backoff**
• Overall concurrency is bound by a semaphore (configurable).
• Each upsert uses exponential backoff with jitter (defaults: max_retries=5, base_delay=1.0s).
**Capability probe (optiona


Each upsert uses exponential backoff with jitter (defaults: max_retries=5, base_delay=1.0s).
**Capability probe (optional)**
• If enabled, for HTTP/SSE endpoints the registrar **GETs** ${endpoint.url}/capabilities and merges the returned list into capabilities.
Environment variables
You can control the CLI/script by env vars (all optional):
| Variable                   | Meaning                                                           | Default                 |
| GATEWAY_URL              | Admin API base URL                                                | http://localhost:4444 |
| ADMIN_TOKEN              | Bearer token (alternates: GATEWAY_TOKEN, GATEWAY_ADMIN_TOKEN) | *(none)*                |
| ZIP_PATH                 | Path to local ZIP


| *(none)*                |
| ZIP_PATH                 | Path to local ZIP                                                 | *(none)*                |
| DIR_PATH                 | Path to local directory                                           | *(none)*                |
| GIT_URL                  | Git repo URL                                                      | *(none)*                |
| GIT_REF                  | Git ref                                                           | main                  |
| CONCURRENCY              | Parallel registrations                                            | 10 (CLI)              |
| PROBE                    | true/false capability probing                                 | true                  |
| ENV_FILE / DOTENV_FILE | Path to .env to l


apability probing                                 | true                  |
| ENV_FILE / DOTENV_FILE | Path to .env to load first                                      | .env.local            |
Return shape
BulkRegistrar.register_servers(sources) returns a **flat list** of results (one per discovered manifest). Each item is either:
or an error wrapper, e.g.:
Troubleshooting
**HTTP 401/403 (Unauthorized/Forbidden)**
→ Set ADMIN_TOKEN (or GATEWAY_TOKEN / GATEWAY_ADMIN_TOKEN).
**HTTP 422 “Missing required field: 'name'” or “Invalid name”**
→ Your gateway expects **form fields** and a sanitized name. The client will fallback to form automatically and clean the name. If you still see the error, your original name/id may be empty; discovery will synthesize a safe fallback.
**AnyUrl is not JSON se


ll see the error, your original name/id may be empty; discovery will synthesize a safe fallback.
**AnyUrl is not JSON serializable**
→ You’re likely bypassing the provided models. The registrar already serializes models into JSON-safe dicts; don’t send raw Pydantic types to httpx.
**RuntimeWarning: 'matrix_sdk.bulk.cli' in sys.modules…**
→ Ensure matrix_sdk/bulk/__init__.py doesn’t import cli. Keep it minimal:
**Git clone failures**
→ Prefer a local ZIP (ZIP_PATH) to avoid any network/git issues.
Security notes
• Only run against Gateways you control. The registrar sends **Bearer tokens** to the URL you provide.
• If you store tokens in .env files, protect those files (e.g., don’t commit them).
• The registrar does **not** execute arbitrary code in the repo; it reads manifests and TOML/JSO


g., don’t commit them).
• The registrar does **not** execute arbitrary code in the repo; it reads manifests and TOML/JSON files.
Minimal API reference
class BulkRegistrar
• register_servers(sources: Iterable[dict]) -> list[Any]
Discovers manifests for each source and upserts them concurrently, returning a flat list of results/errors.
Source kinds
• {"kind": "zip", "path": "/path/repo.zip", "probe": true}
• {"kind": "dir", "path": "/path/repo", "probe": true}
• {"kind": "git", "url": "https://...", "ref": "main", "probe": true}
Worked examples
**Register a local ZIP**
**Register docling-mcp (Git)**
**Script example**
Compatibility
• **Python**: 3.10+
• **Pydantic**: v1 *or* v2 (the models handle both)
• **httpx**: 0.27+
If your Gateway’s Admin API only supports form POSTs, the registrar wil


* v2 (the models handle both)
• **httpx**: 0.27+
If your Gateway’s Admin API only supports form POSTs, the registrar will **automatically switch** after a JSON attempt fails with a recognizable error.
See Usage for more scenarios.


Configuration
You configure the SDK by **passing options at construction time**. We recommend keeping secrets/URLs in environment variables and reading them in your app.
Environment variables (recommended)
Create a .env or export variables in your shell:
Then load them in Python (e.g., with os.getenv or python-dotenv) and pass to the client.
MatrixClient (Hub)
Used for search, detail, install, and (optionally) remote/ingest admin.
Common usage
Bulk Registrar (Gateway) — optional
If you also manage a Gateway, the SDK can **bulk-register** MCP servers you host. This is independent of the Hub client.
Required env
Optional env (source + behavior)
Minimal Python setup
**Notes**
• **Discovery (matrix-first):** the registrar looks for matrix/index.json or matrix/*.manifest.json. If none are prese


*
• **Discovery (matrix-first):** the registrar looks for matrix/index.json or matrix/*.manifest.json. If none are present, it falls back to [tool.mcp_server] in pyproject.toml.
• **Gateway compatibility:** the client POSTs **JSON** first; if your gateway expects form data on /admin/servers, it **auto-falls back** to URL-encoded form and **sanitizes name** to match gateway rules.
• **Idempotency & resilience:** a stable idempotency key (SHA-256 of the manifest) prevents duplicates; requests use concurrency and exponential backoff with jitter.
Advanced configuration
Timeouts and sessions
MatrixClient accepts a timeout (seconds) and an optional pre-configured httpx.Client session:
If you pass token=..., the client sets Authorization: Bearer <token> automatically.
Route overrides (if your Hub


ession:
If you pass token=..., the client sets Authorization: Bearer <token> automatically.
Route overrides (if your Hub paths differ)
Caching (ETag-aware)
The SDK ships a tiny in-memory cache that honors ETags for /catalog/search. It avoids re-downloading unchanged results and can speed up repeated queries.
**Notes**
• The cache stores response bodies and ETags keyed by request parameters.
• If the server returns **304 Not Modified**, the SDK serves the cached body.
• You can swap in your own cache implementation as long as it provides:
• make_key(path: str, params: dict) -> str
• get_etag(key: str) -> str | None
• get_body(key: str) -> dict | None
• save(key: str, *, etag: str | None, body: dict) -> None
Proxies and TLS
httpx honors HTTP_PROXY / HTTPS_PROXY / NO_PROXY. Example:
Or pass p


ag: str | None, body: dict) -> None
Proxies and TLS
httpx honors HTTP_PROXY / HTTPS_PROXY / NO_PROXY. Example:
Or pass proxies to your httpx.Client.
Error handling
The client raises MatrixError on non-2xx responses or network errors:
• HTTP errors → MatrixError(status=<http code>, detail=<server message>)
• Timeouts / connection issues → MatrixError(status=0, detail="timeout" or similar)
Server-side tip (Hub admins)
To produce absolute links (e.g., manifest_url, install_url) behind a proxy/CDN, set the Hub’s environment variable:
This is **not** an SDK setting, but it affects the links you receive from the Hub.


Contributing
Thank you for your interest!
• Please submit issues/feature requests on GitHub.
• See CONTRIBUTING.md for coding standards, branch policy, and more.
• All contributions require tests and clear documentation.
Code of Conduct
We follow the Contributor Covenant.
Dev setup
• Install pre-commit hooks:
• Run tests:


matrix-python-sdk
**matrix-python-sdk** is the official Python SDK for \[Matrix Hub] — the open catalog and installer for **agents**, **tools**, and **MCP servers**.
It gives teams a *single, dependable path* to **discover**, **evaluate**, **install**, and **run** AI components. Whether you’re curating a marketplace, wiring up internal tools, or shipping end-user assistants, this SDK helps you move from *“I think I need X”* to *“X is installed, configured, and running”* in minutes.
Why it matters
• **One interface for many ecosystems.** Agents, tools, and MCP servers are published in different places and formats. Matrix Hub normalizes them; this SDK makes them easy to query and install.
• **Reproducible installs.** The Hub returns concrete install plans and lockfiles; the SDK materializes


ry and install.
• **Reproducible installs.** The Hub returns concrete install plans and lockfiles; the SDK materializes them safely and consistently.
• **Built for production.** Secure archive extraction, Git host allow-lists, optional ETag caching, typed models, and careful retries mean fewer surprises in CI and prod.
• **Scales as your catalog grows.** Efficient search helpers (keyword/semantic/hybrid) with normalization and fallback keep UX fast even as catalogs reach millions of entries (the heavy lifting stays server-side).
What this package offers
• **Ergonomic client** (MatrixClient) to call Hub APIs: search, entity, install, and catalog remotes.
• **Search helpers** (matrix_sdk.search) with filter normalization, small retries, and *mode fallbacks* (semantic → hybrid → keyword).
• *


** (matrix_sdk.search) with filter normalization, small retries, and *mode fallbacks* (semantic → hybrid → keyword).
• **Local installer** (matrix_sdk.installer.LocalInstaller) to fetch artifacts, write adapters, and prepare runtime environments.
• **Lightweight runtime** (matrix_sdk.runtime) to start/stop/list/tail/doctor local MCP servers—no daemon required.
• **Bulk registrar** (matrix_sdk.bulk.*, optional) to discover manifests (ZIP/dir/Git) and register MCP servers into a Gateway with idempotency and capability probing.
Install
Quickstart — Search & install
Quickstart — Run locally
Advanced search (helper)
Prefer the high-level helper when you want resilience and typed results.
**Why use the helper?**
• Normalizes filters (lists/sets → CSV), clamps limits, and retries transient errors


ed results.
**Why use the helper?**
• Normalizes filters (lists/sets → CSV), clamps limits, and retries transient errors with jittered backoff.
• Smart mode fallbacks avoid user-visible “empty” results when semantic indices aren’t available in dev.
• Optional typed responses (SearchResponse) for ergonomic, safe code.
Bulk registration (optional)
Discover MCP server manifests and register them into a Gateway Admin API.
**Highlights**
• **Matrix-first discovery:** looks for matrix/index.json or matrix/*.manifest.json; falls back to pyproject.toml.
• **Gateway compatibility:** JSON first; auto-fallback to form with safe name sanitization.
• **Resilience:** concurrency control, idempotency keys, exponential backoff with jitter.
Design values
• **Reliability:** strict error types, small safe re


l, idempotency keys, exponential backoff with jitter.
Design values
• **Reliability:** strict error types, small safe retries, idempotent bulk writes, ETag-aware client cache.
• **Security by default:** safe archive extraction, Git host allow-lists, deny-by-default where sensible.
• **Performance:** lightweight client, normalized query params, and server-side scoring/indexing.
• **Compatibility:** Pydantic v1/v2 models, tolerant schema (unknown fields allowed), portable runtime.
Docs
• Installation
• Configuration
• Usage
• API Reference
• Bulk Registration
*matrix-python-sdk* helps you move faster from **idea → discovery → install → run** with production-grade guardrails and a clean, Pythonic API.


Installation
From PyPI
Requires Python 3.10+.
From source
Upgrade
Optional extras
• httpx (installed): HTTP client used by the SDK.
• pydantic (installed): typed models for responses.
• pyyaml (optional): enables fetch_manifest() to parse YAML manifests.


Reference (v0.1.9)
A quick map of the primary modules and entry points.
Core
• matrix_sdk.client.MatrixClient — Hub API client
Methods: search(), entity(), install(), list_remotes(), add_remote(), delete_remote(), trigger_ingest()
• matrix_sdk.installer.LocalInstaller — local install orchestration
Returns: BuildReport, EnvReport, BuildResult
• matrix_sdk.runtime — run local MCP servers
Functions: start(), stop(), status(), doctor(), tail_logs(), log_path()
Installer helpers
• Runner schema & discovery (re-exported):
• _is_valid_runner_schema(runner, logger)
• _coerce_runner_to_legacy_process(obj)
• _ensure_sse_url(url)
• _url_from_manifest(manifest)
• _extract_mcp_sse_url(node)
Artifact fetchers
• matrix_sdk.archivefetch — fetch_http_artifact, ArchiveFetchError
• matrix_sdk.gitfetch — fetc


(node)
Artifact fetchers
• matrix_sdk.archivefetch — fetch_http_artifact, ArchiveFetchError
• matrix_sdk.gitfetch — fetch_git_artifact, GitFetchError
Models
• matrix_sdk.schemas — Pydantic models for Hub responses
(SearchItem, SearchResponse, EntityDetail, InstallOutcome, etc.)
Exceptions
• matrix_sdk.client.MatrixError
• matrix_sdk.manifest.ManifestResolutionError
• matrix_sdk.archivefetch.ArchiveFetchError
• matrix_sdk.gitfetch.GitFetchError
Env toggles (common)
• MATRIX_SDK_DEBUG=1
• MATRIX_SDK_HTTP_TIMEOUT=15
• MATRIX_SDK_RUNNER_SEARCH_DEPTH=2
• MATRIX_SDK_ALLOW_MANIFEST_FETCH=1
• MATRIX_SDK_ENABLE_CONNECTOR=1


Usage (v0.1.9)
Quick, copy-pasteable examples for searching the Hub, installing locally, and (optionally) running a server.
Requires **Python 3.11+**.
1) Quick search
**Tip:** prefer the high-level helper below for tiny retries and optional fallbacks.
2) Install & build locally
The build step: 1) requests a plan, 2) writes files/artifacts, 3) ensures runner.json, 4) prepares envs.
3) Run locally (no daemon)
**Connector / attach mode:** if runner.json contains {"type":"connector","url":"http://127.0.0.1:6288/sse"}, runtime.start(...) **does not** spawn a process; it records the URL in the lock (pid=0). Use your MCP client to talk to that endpoint directly.
4) Advanced search helper
5) Manage catalog remotes
6) Bulk registration (Gateway, optional)
7) Error handling
Environment variables (qu


ch helper
5) Manage catalog remotes
6) Bulk registration (Gateway, optional)
7) Error handling
Environment variables (quick reference)
• MATRIX_SDK_DEBUG=1 — verbose logs (installer/runtime/search/etc.).
• MATRIX_HOME — base dir for state/logs (default: ~/.matrix).
• MATRIX_SDK_ENABLE_CONNECTOR=1 — allow connector runner synthesis (default **on**).
• MATRIX_SDK_HTTP_TIMEOUT — network timeout (seconds; default **15**).


⚡ MatrixLink Suite
*Production toolkit for **MatrixHub** — discover, select, and run MCP servers (orchestrators) and A2A agents across any cloud.*

<img alt="License: Apache-2.0" src="https://


s.io/badge/docs-mkdocs--material-blue?logo=mkdocs">

Requires **Python 3.11+**. Library published as **matrixlink** on PyPI. BFF/UI run on **:8080**.
MatrixLink Suite gives you a production-ready base to run **MCP Gateway + Orchestrators (MCP Servers) + A2A Agents** on any modern platform (IBM Code Engine, Google Cloud Run, AWS App Runner, Azure Container Apps, Knative/K8s, Local). It includes:
• libs/matrixlink/ — PyPI-ready client for MCP discovery, A2A messaging, and Orchestrator invocation
• apps/bff/ — Fron


• libs/matrixlink/ — PyPI-ready client for MCP discovery, A2A messaging, and Orchestrator invocation
• apps/bff/ — Front Door (FastAPI) exposing a clean /api/* surface with admin & MCP proxy
• ui/matrixhub-admin/ — Minimal React admin (read-only) for MCP servers & agents
• infra/ce/ — IBM Code Engine scripts for apps + secrets
• Makefile — Dev & ops targets (make help)
All services bind to port **8080**. East–west calls use **Bearer service tokens** (optionally mTLS).
**Default provider = local**. No external services are required to run BFF + UI locally.
What are MatrixHub and MatrixLink?
**MatrixHub** is an *internet-scale hub* and search engine (think **PyPI for AI agents & MCP servers**). You use it to discover high-quality agents/orchestrators by **capability** (skills, flows), **tru


ents & MCP servers**). You use it to discover high-quality agents/orchestrators by **capability** (skills, flows), **trust** (publisher, SBOM/attestation), and **fitness** (success rate, latency, region). Import chosen entries into your org’s private catalog with the right **visibility** and **policy**.
**MatrixLink** is the runtime connector: a tiny client library plus an optional BFF that makes **discovery and invocation portable** across clouds. It lets your code:
• Discover agents by **skill** via your **MCP Gateway**
• Call agents with a simple **A2A** contract (/message/send, optional SSE)
• Invoke orchestrators (/invoke/<flow>) with tenancy headers & bearer auth
• Auto-resolve service endpoints via environment variables (no code edits per provider)
**Together**: *discover globally,


Auto-resolve service endpoints via environment variables (no code edits per provider)
**Together**: *discover globally, run locally*. You pick building blocks from MatrixHub; MatrixLink wires them into your environment safely and repeatably.
Architecture
**Public API (edge):**
| Path                                        | Target       | Notes                              |
| GET /api/health                           | BFF          | liveness                           |
| GET /api/ready                            | BFF          | readiness (+ MCP health)           |
| POST /api/chat / /api/flows/*         | Orchestrator | main chat/flow entrypoint          |
| /api/mcp/*                                | BFF → MCP    | proxied, secured                   |
| /api/admin/*


                                | BFF → MCP    | proxied, secured                   |
| /api/admin/*                              | BFF          | agents CRUD/run, secured           |
Repository layout
Requirements
• Python 3.10+
• Node 18+ (UI)
• Docker (for container builds)
• Optional: IBM Cloud CLI for Code Engine deploys
Quickstart (local)
**1) Run the BFF (Front Door)**
**2) Run the Admin UI**
For local testing, set export ADMIN_BEARER=changeme-admin-bearer-token and use the same in the UI Settings.
When do I need an MCP Gateway?
MatrixLink’s BFF and UI run fine **without** MCP for basic health/admin. You’ll want an **MCP Gateway** when you need:
• **Runtime discovery** of agents/orchestrators by skill/tags
• **Central registry** + **RBAC** and **visibility** per team/tenant
• **Heal


ry** of agents/orchestrators by skill/tags
• **Central registry** + **RBAC** and **visibility** per team/tenant
• **Health & fitness** signals (route only to healthy entries)
• **Controlled rollouts** (blue/green, canary via tags)
**Local dev:** you can run a lightweight MCP Gateway on http://localhost:4444. In cloud, point MCP_BASE_URL to your managed MCP instance (or resolve via DOMAIN_SUFFIX+MCP_SERVICE_NAME).
Environment variables
**BFF**
• ADMIN_BEARER — token to guard /api/admin/* and /api/mcp/*
• MCP_BASE_URL — MCP Gateway URL (defaults to http://localhost:4444 for CLOUD_PROVIDER=local)
• MCP_BEARER_TOKEN — service token to call MCP Gateway
• AGENTS_BASE_URL — optional static base for agents (fan-in host)
• AGENTS_API_TOKEN — east–west bearer for agent invocations
**MatrixLink lib**


onal static base for agents (fan-in host)
• AGENTS_API_TOKEN — east–west bearer for agent invocations
**MatrixLink lib**
• CLOUD_PROVIDER — local (default), ce, gcrun, apprunner, aca, knative
• DOMAIN_SUFFIX — e.g. proj.region.codeengine.appdomain.cloud
• MCP_SERVICE_NAME — default mcp-gateway
• ORCH_SERVICE_NAME — default orchestrator
• AGENTS_DOMAIN_PREFIX — default agents
• MCP_BASE_URL, ORCH_BASE_URL, AGENTS_BASE_URL — explicit overrides
• MCP_BEARER_TOKEN, A2A_SERVICE_TOKEN, TENANT_HEADER, REQUEST_TIMEOUT
Using MatrixLink (Python)
Install locally from repo:
Or from PyPI (after you publish):
**Discover an agent by skill (via MCP Gateway)**
**Send data to an A2A agent**
**Invoke an orchestrator (MCP Server) flow**
Admin API (BFF)
| Method | Path | Secured? | Purpose |
| GET | /api/healt


*
**Invoke an orchestrator (MCP Server) flow**
Admin API (BFF)
| Method | Path | Secured? | Purpose |
| GET | /api/health | ❌ | Liveness |
| GET | /api/ready | ❌ | Readiness (+ MCP health) |
| GET | /api/mcp/servers | ✅ | MCP servers list (proxy) |
| GET | /api/mcp/tools | ✅ | MCP tools list (proxy) |
| POST | /api/mcp/servers | ✅ | Create MCP server (proxy) |
| GET | /api/admin/agents | ✅ | List agents |
| POST | /api/admin/agents | ✅ | Create agent |
| POST | /api/admin/agents/{id}/bind | ✅ | Bind agent → catalog URL |
| POST | /api/admin/agents/{id}/run | ✅ | Invoke an agent by name |
Secure = requires Authorization: Bearer ${ADMIN_BEARER}.
Docker
**BFF**
**Admin UI**
Deploy on IBM Code Engine (example)
1.  **Create secrets** (fill tokens first):
./infra/secrets/create-secrets.sh
2.  **


Deploy on IBM Code Engine (example)
1.  **Create secrets** (fill tokens first):
./infra/secrets/create-secrets.sh
2.  **Build & push images**:
make docker-build-bff docker-build-ui
make docker-push-bff docker-push-ui
3.  **Create apps**:
make ce-create-bff
make ce-create-ui
4.  **API Gateway routes**:
• /api/health|/api/ready|/api/admin/*|/api/mcp/* -> BFF
• /api/chat (or /api/flows/<flow>)               -> Orchestrator (separate repo/app)
Other providers (env presets)
**Google Cloud Run** (custom domains recommended)
**AWS App Runner**
**Azure Container Apps**
**Knative / K8s**
**Local / Docker Compose + Traefik**
Security & Ops
• **Secrets**: store tokens in provider secret stores; never in images
• **Auth**: OIDC/JWT at edge for /api/chat; ADMIN_BEARER for admin proxy; service Bearer fo


cret stores; never in images
• **Auth**: OIDC/JWT at edge for /api/chat; ADMIN_BEARER for admin proxy; service Bearer for east–west
• **Observability**: structured logs with X-Request-ID / X-Tenant-Id; add OTEL exporters if desired
• **SSE**: ensure the API Gateway does not buffer /stream responses
• **Scaling**: BFF & MCP Gateway minScale=1; Orchestrators/Agents can scale to 0
• **DB**: BFF uses SQLite for dev; switch to a managed DB in prod via DATABASE_URL
Make targets
Troubleshooting
• **ModuleNotFoundError: matrixlink** when running BFF locally → run make install.
• **MCP base URL not set** → set MCP_BASE_URL or use CLOUD_PROVIDER + DOMAIN_SUFFIX + MCP_SERVICE_NAME.
• **CORS** → set CORS_ALLOW_ORIGINS in BFF (e.g., http://localhost:3000).
• **401 on admin routes** → set ADMIN_BEARER a


• **CORS** → set CORS_ALLOW_ORIGINS in BFF (e.g., http://localhost:3000).
• **401 on admin routes** → set ADMIN_BEARER and pass Authorization: Bearer <token> from the UI.
License
Apache 2.0 — see LICENSE.


API
Public endpoints (via BFF & Orchestrator)
| Method | Path | Description |
| GET | /api/health | Liveness |
| GET | /api/ready | Readiness (+ MCP health) |
| POST | /api/chat | Main chat entrypoint (edge → orchestrator) |
Admin endpoints (BFF)
| Method | Path | Secured | Description |
| GET | /api/mcp/servers | ✅ | Proxy list MCP servers |
| GET | /api/mcp/tools | ✅ | Proxy list MCP tools |
| POST | /api/mcp/servers | ✅ | Create MCP server (proxy) |
| GET | /api/admin/agents | ✅ | List agents (local DB) |
| POST | /api/admin/agents | ✅ | Create agent |
| POST | /api/admin/agents/{id}/bind | ✅ | Bind agent → catalog URL |
| POST | /api/admin/agents/{id}/run | ✅ | Invoke agent by name |


Configuration
Environment variables (BFF)
• ADMIN_BEARER
• MCP_BASE_URL
• MCP_BEARER_TOKEN
• AGENTS_BASE_URL
• AGENTS_API_TOKEN
Environment variables (MatrixLink)
• CLOUD_PROVIDER — local|ce|gcrun|apprunner|aca|knative
• DOMAIN_SUFFIX — e.g. proj.region.codeengine.appdomain.cloud
• MCP_SERVICE_NAME — default mcp-gateway
• ORCH_SERVICE_NAME — default orchestrator
• AGENTS_DOMAIN_PREFIX — default agents
• MCP_BASE_URL, ORCH_BASE_URL, AGENTS_BASE_URL
• MCP_BEARER_TOKEN, A2A_SERVICE_TOKEN, TENANT_HEADER, REQUEST_TIMEOUT
Provider presets


Deploy
IBM Code Engine
1. Create secrets:
2. Build & push images:
3. Create apps:
4. Configure API Gateway routes:
• /api/health|/api/ready|/api/admin/*|/api/mcp/* -> BFF
• /api/chat (or /api/flows/<flow>)               -> Orchestrator
Other providers
Use env presets from **Configuration**. Endpoints resolve via either explicit *_BASE_URL or DOMAIN_SUFFIX + SERVICE_NAME.


FAQ
**Do I need MCP Gateway to start?**
No. You can run BFF + UI locally without MCP. Add MCP when you need discovery, health-based routing, and policy/RBAC.
**How does MatrixLink find URLs?**
Either via explicit *_BASE_URL envs, or by composing https://<SERVICE>.<DOMAIN_SUFFIX> using provider hints.
**Can I run agents/orchestrators at scale-to-zero?**
Yes. Keep MCP and BFF warm (minScale=1) and let agents/orchestrators scale to 0/∞.
**What about multi-cloud?**
MatrixLink is provider-agnostic. Move workloads between CE/Run/App Runner/ACA/Knative with env changes only.


MatrixLink Suite
MatrixLink Suite = **MatrixLink (PyPI client)** + **Front Door (BFF)** + **Admin UI** + **infra** to run **MCP Gateway + Orchestrators + A2A Agents**.
• All services bind **:8080** (host/path routing).
• East–west calls use **Bearer service tokens** (optional mTLS).
• Default provider = **local**.
See **Quickstart** to run locally in minutes.


MatrixHub × MatrixLink — internet hub + portable runtime
**MatrixHub** is an internet-scale hub and search engine (like PyPI, but for agents and MCP servers). It indexes capability cards, trust, health and fitness so your teams can **find and import** the best candidates.
**MatrixLink** is the portable, production-safe client + BFF that your apps use to **discover** and **invoke** those candidates at runtime with consistent contracts and env-based endpoints.
What MatrixHub provides
• Open catalog & search by skill (e.g. report.generate), tags (domain:finance), modes, license, region
• Trust/quality: uptime, success rate, latency, verified publisher, SBOM/attestation
• Governance: visibility (private|team|global), policy filtering, team tagging
• One-click import to your org’s tenant (mirro


ernance: visibility (private|team|global), policy filtering, team tagging
• One-click import to your org’s tenant (mirrored into your MCP Gateway)
How MatrixLink consumes it
• MCPClient.discover_agents(skill=..., input_mode=..., output_mode=...)
• MCPClient.discover_servers(role="orchestrator", tags=[...])
• A2AClient.send_message(endpoint, data) and optional SSE stream
• OrchestratorClient.invoke(flow, arguments)
• Provider-aware resolution via CLOUD_PROVIDER, DOMAIN_SUFFIX, or explicit *_BASE_URL
Typical workflow
1. Search on public MatrixHub → pick candidate(s)
2. Import into your org tenant → tagged, governed entries
3. Deploy/attach endpoints → heartbeat into MCP
4. Use MatrixLink from orchestrators/edge → discover and invoke at runtime
5. Operate rollouts (canary/blue-green) without


MCP Gateway — when and how to use it
**When you need it**
• Runtime discovery by skill/tags across many agents
• A registry with visibility, RBAC, health and fitness scoring
• Traffic shaping via tags (canary, tier) and safe rollouts
**Local dev**
• Run a lightweight MCP instance at http://localhost:4444
• Point BFF/clients with MCP_BASE_URL=http://localhost:4444
**Cloud**
• Deploy/consume a managed MCP; set MCP_BASE_URL (or use DOMAIN_SUFFIX + MCP_SERVICE_NAME)
• Use MCP_BEARER_TOKEN for service-to-service auth
**Discovery APIs (typical)**
• GET /discover/agents?skill=...&inputMode=...&outputMode=...&healthyOnly=true
• GET /discover/servers?role=orchestrator&tags=...
**Best practices**
• Keep MCP warm (minScale=1) for low latency discovery
• Enforce RBAC + tenancy headers end-to-end
• Emi


Security & Ops
Authentication & Authorization
• Edge: verify user OIDC/JWT for /api/chat and user-facing routes
• East–west: service bearer tokens; optionally mTLS between internal services
• Admin plane: ADMIN_BEARER for /api/admin/* and /api/mcp/*
Tenancy & Headers
• Propagate X-Tenant-Id from edge → orchestrator → agents
• Enforce visibility in MCP (private|team|global) and team tags
Observability
• Use X-Request-Id for correlation; structured logs
• Add OTEL exporters if desired; ensure SSE endpoints are not buffered by edge
SLO Guidance
• Discovery P99 < 50 ms
• Agent success rate > 99.5% (ex-upstream)
• First SSE event < 250 ms
Lifecycle & Rollouts
• Blue/green with tags (canary, stable)
• Traffic shaping in MCP; deprecate with policy windows
Data & Secrets
• Store secrets in provide


Troubleshooting
**ModuleNotFoundError: matrixlink when running BFF locally**
• Run make install to install the library into the BFF venv.
**MCP base URL not set**
• Set MCP_BASE_URL or configure CLOUD_PROVIDER + DOMAIN_SUFFIX + MCP_SERVICE_NAME.
**CORS errors in the Admin UI**
• Set CORS_ALLOW_ORIGINS in BFF (e.g., http://localhost:3000).
**401 on admin routes**
• Set ADMIN_BEARER and pass Authorization: Bearer <token> from the UI Settings.
**SSE not streaming**
• Ensure your API Gateway does **not buffer** text/event-stream responses.


Matrix Hub
[](https://hub.docker.com/r/ruslanmv/matrix-hub)
[](https://www.python.org/downloads/release/python-3100/)
[](https://github.com/agent-matrix/matrix-hub/actions/workflows/ci.yml)
[](https://agent-matrix.github.io/matrix-hub/)
[](LICENSE)
Welcome to Matrix Hub, your all‑in‑one AI agent marketplace and installer!
**Matrix Hub** is your production‑ready catalog & installer for AI agents, custom tools, and MCP servers.
It ingests well‑structured manifests from remote catalogs (e.g., GitHub), offers lightning‑fast **search** (lexic


rvers.
It ingests well‑structured manifests from remote catalogs (e.g., GitHub), offers lightning‑fast **search** (lexical + semantic hybrid), safely computes and executes **install plans** (pip/uv, Docker, Git, ZIP), and can automatically **register** everything with your MCP Gateway.
• **API**: FastAPI on port **443**
• **DB**: PostgreSQL (SQLite supported for lightweight/dev use)
• **Ingest**: Pull index.json from one or more remotes, validate & enrich manifests, optional chunk+embed
• **Install**: Idempotent steps, project adapters, lockfile generation
• **Auth**: Optional bearer‑token for admin endpoints
• **Logs**: Structured JSON with correlation IDs
What is Matrix Hub?
Imagine PyPI for intelligent agents and custom tools—Matrix Hub ingests curated manifests from GitHub (or any remo


 Hub?
Imagine PyPI for intelligent agents and custom tools—Matrix Hub ingests curated manifests from GitHub (or any remote catalog), lets you find the perfect agent in seconds using hybrid text + semantic search, and then safely installs everything into your project (pip, Docker, Git, or ZIP) with a single command. It even auto‑registers new services with your MCP Gateway, writes scaffolding adapters, and produces a lockfile so your builds are rock‑solid.
Ready to discover, install, and manage enterprise‑grade AI skills?
Explore the full documentation to get started!
Official Search API
Matrix Hub exposes a stable, public endpoint that returns the **Top‑5** best matches for a query across agents, tools, and MCP servers. This contract is additive and safe for production.
Endpoint
**Common p


or a query across agents, tools, and MCP servers. This contract is additive and safe for production.
Endpoint
**Common parameters**
• q *(string, required)* – user intent, e.g. summarize pdfs
• type *(enum: agent|tool|mcp\_server|any, default any)* – filter by type; any searches all.
• limit *(int, default 5)* – maximum items returned. Public API caps to **5**.
• with_snippets *(bool, default false)* – include a short snippet (first \~200 chars of summary/description) when available.
Advanced parameters (optional): mode (keyword|semantic|hybrid), with_rag (bool), rerank (none|llm), and CSV filters capabilities, frameworks, providers.
Response
Each item includes ranking scores and convenience links for import/install:
Quick start (curl)
To install a selected item:
See the full API doc: Top‑


ores and convenience links for import/install:
Quick start (curl)
To install a selected item:
See the full API doc: Top‑5 Search API.
**Deployment notes**
• Set PUBLIC_BASE_URL in your environment (e.g., https://api.example.com).
• The optional helper route GET /catalog/manifest/{id} can redirect to external manifests when source_url is set.
• No database migrations required.
Quick start (docker compose)
Requirements: Docker 24+, docker compose plugin.
You should see:
The app container waits for Postgres to be healthy, then starts the API. Ingest is scheduled (see INGEST\_INTERVAL\_MIN in .env).
Environment
All configuration is via environment variables; see .env.example for a documented template.
Key settings (common):
| Variable                 | Purpose


for a documented template.
Key settings (common):
| Variable                 | Purpose                                      | Example                                                                  |
| DATABASE_URL           | SQLAlchemy URL                               | postgresql+psycopg://matrix:matrix@db:5432/matrixhub                   |
| HOST / PORT          | Bind address/port                            | 0.0.0.0 / 443                                                       |
| MATRIX_REMOTES         | CSV/JSON list of index.json URLs to ingest | https://raw.githubusercontent.com/agent-matrix/catalog/main/index.json |
| INGEST_INTERVAL_MIN    | Background ingest interval (minutes)         | 15                                                                     |
| API_TOKEN


interval (minutes)         | 15                                                                     |
| API_TOKEN              | Bearer token for admin/protected endpoints   | set_me                                                                 |
| MCP_GATEWAY_URL        | MCP Gateway base URL                         | http://mcpgateway:7200                                                 |
| MCP_GATEWAY_TOKEN      | Bearer for Gateway admin API                 | supersecret                                                            |
| SEARCH_LEXICAL_BACKEND | pgtrgm or none                           | pgtrgm                                                                 |
| SEARCH_VECTOR_BACKEND  | pgvector / none                          | none


    |
| SEARCH_VECTOR_BACKEND  | pgvector / none                          | none                                                                   |
| SEARCH_HYBRID_WEIGHTS  | sem:0.6,lex:0.4,rec:0.1,q:0.1              |                                                                          |
| RAG_ENABLED            | false                                      |                                                                          |
| EMBED_MODEL            | Embedder identifier (informational)          | sentence-transformers/all-MiniLM-L6-v2                                 |
**Security:** If you set API_TOKEN, pass Authorization: Bearer <token> on protected endpoints (e.g., manual ingest). Without it, admin endpoints are disabled or open depending on route configuration.
API walk‑t


nts (e.g., manual ingest). Without it, admin endpoints are disabled or open depending on route configuration.
API walk‑through
Base URL defaults to http://localhost:443.
GET /health
Liveness/readiness. Optional DB probe:
Returns:
GET /catalog/search
Hybrid search over the catalog with optional filters.
**Query params**
• q (required) — free text query
• type — agent | tool | mcp_server
• capabilities — CSV, e.g. pdf,summarize
• frameworks — CSV, e.g. langgraph,watsonx_orchestrate
• providers — CSV, e.g. openai,watsonx
• mode — keyword | semantic | hybrid (default from settings)
• limit — default 20
• with_rag — true to enrich results with a short “fit\_reason”
• rerank — none | llm (default from settings)
**Example**
**Response (truncated)**
GET /catalog/entities/{id}
Fetch full entity det


none | llm (default from settings)
**Example**
**Response (truncated)**
GET /catalog/entities/{id}
Fetch full entity details for a specific catalog ID.
**Example**
POST /catalog/install
Compute and execute an install plan (pip/uv, docker, git, zip), write adapters into your project, and (optionally) register tools/servers with MCP‑Gateway. Emits matrix.lock.json.
**Body**
**Example**
**Response (truncated)**
Manifests & Schemas
Catalog authors publish manifests in a separate content repo (e.g., agent-matrix/catalog) and generate an index.json.
Matrix Hub ingests those manifests on a schedule or via an admin trigger.
Supported schemas (JSON Schema Draft 2020‑12):
• Agent: schemas/agent.manifest.schema.json
• Tool:  schemas/tool.manifest.schema.json
• MCP Server: schemas/mcp-server.manifest.


schemas/agent.manifest.schema.json
• Tool:  schemas/tool.manifest.schema.json
• MCP Server: schemas/mcp-server.manifest.schema.json
Minimal index.json formats accepted:
Example agent manifest (excerpt):
Local development
**Quality & tests**
**Where things live**
Migrations
This project includes Alembic scaffolding. To create and apply migrations:
In production, run migrations as part of your deploy step (e.g., init container or CI job) before starting new app instances.
Production notes
**Container image:** See Dockerfile (multi‑stage). Run with:
**Database:** Use PostgreSQL in production. SQLite is supported for quick local trials only.
**Scaling:** The API is stateless; scale horizontally behind a reverse proxy/load balancer. Ensure a single writer for migrations.
**Observability:** Logs


 scale horizontally behind a reverse proxy/load balancer. Ensure a single writer for migrations.
**Observability:** Logs are JSON‑formatted with correlation IDs (see src/utils/logging.py). Forward stdout/stderr to your log stack (e.g., Loki/ELK). Add metrics/tracing as needed.
**Security:**
• Set API_TOKEN to protect admin routes.
• Place the service behind TLS (e.g., NGINX/Envoy) and enforce client auth if needed.
• Configure outbound access policies for pip, docker, and git if your environment restricts egress.
**MCP Gateway:** Expose MCP_GATEWAY_URL and MCP_GATEWAY_TOKEN. The installer will register tools/servers after artifacts are installed.
Troubleshooting
**API returns 500 on /catalog/search**
• Check DB connectivity (/health?check_db=true).
• Ensure the entity table is populated (i


urns 500 on /catalog/search**
• Check DB connectivity (/health?check_db=true).
• Ensure the entity table is populated (ingest logs).
• If running SQLite, semantic/vector search is disabled by default.
**No results after ingest**
• Verify MATRIX_REMOTES points to a valid index.json.
• Review container logs for validation errors (schema mismatches).
**Install fails on pip/docker/git**
• See the results array in the install response for command stdout/stderr.
• Check that the container has network access and the artifact references are valid.
**Gateway registration errors**
• Confirm MCP_GATEWAY_URL is reachable and token is correct.
• Some gateways perform discovery automatically; our client handles transport normalization.
License
Apache‑2.0. See LICENSE.
See also
**Schemas:**
• Agent — sch


ically; our client handles transport normalization.
License
Apache‑2.0. See LICENSE.
See also
**Schemas:**
• Agent — schemas/agent.manifest.schema.json
• Tool — schemas/tool.manifest.schema.json
• MCP Server — schemas/mcp-server.manifest.schema.json
**Tests:** tests/ — smoke tests and examples; run with make test.


Installing Agents/Tools via Matrix Hub
You can install through the **CLI** or the **API**. Installation computes a plan, executes artifacts (pip/uv, docker, git, zip), writes adapters, registers to MCP Gateway, and emits matrix.lock.json.
CLI
API
Artifacts Supported
• pypi → uv pip install … (fallback to pip)
• oci → docker pull …
• git → git clone … (+ optional checkout)
• zip → curl + unzip
Adapters
Framework glue written to your project (e.g., LangGraph node).
Path defaults can be overridden per adapter spec.
Files added to matrix.lock.json under adapters_files.
Lockfile
matrix.lock.json records installed entities, artifacts, digests, and adapters for reproducibility.
Rollback (manual)
Remove adapters and revert project changes.
Re-run install with a known-good version (@version).


Registration with MCP Gateway
After installing, Matrix Hub can register entities with MCP Gateway via its admin API.
What Gets Registered
• **Tools**: POST /tools with name, integration_type (REST|MCP), request_type, url, input_schema, and optional headers/annotations.
• **MCP Servers**: POST /gateways with name, url, transport (HTTP/SSE/STDIO/STREAMABLEHTTP), and optional auth.
• Discovery of tools is **automatic** after registration; no separate endpoint needed.
• **Resources / Prompts** (optional): added via POST /resources / POST /prompts.
Idempotency & Conflicts
• Re-registration with identical payloads may yield 409 Conflict; Matrix Hub surfaces this in the install results.
• Admin token is required; set MCP_GATEWAY_TOKEN.
Security
• Use Gateway over TLS.
• Store auth tokens outside


Agents, Tools, and MCP Servers (Concepts)
Matrix Hub catalogs three entity types:
• **Agent**: an orchestrated capability (often a service with /invoke).
• **Tool**: a callable function/tool (often REST or MCP tool).
• **MCP Server**: a server that exposes MCP protocol endpoints and tools.
Manifests
Each entity has a manifest describing:
• Identity: type, id, name, version
• Metadata: summary, description, license, homepage
• Capabilities/tags; compatibility (frameworks/providers)
• Artifacts: pypi | oci | git | zip with specs
• Adapters: framework glue to drop into your project
• mcp_registration: how to register with MCP Gateway


API — Endpoints
Base URL defaults to http://localhost:443.
This Hub stores a catalog of manifests (agents/tools/mcp\_servers), installs artifacts, and syncs MCP servers into **MCP-Gateway**. Mutating endpoints require an admin token if API_TOKEN is set.
Auth
When API_TOKEN is configured in the Hub:
Admin-only endpoints below are marked accordingly.
GET /health
Check service health.
**Query params**
• check_db (bool, optional): also checks DB connectivity.
**200 Response**
GET /catalog/search
Hybrid search with filters (keyword/semantic/hybrid). Also handy for listing ingested items.
**Query params**
• q (string, optional): text query
• type (string, optional): agent | tool | mcp_server
• capabilities (CSV, optional)
• frameworks (CSV, optional)
• providers (CSV, optional)
• mode (string, o


l | mcp_server
• capabilities (CSV, optional)
• frameworks (CSV, optional)
• providers (CSV, optional)
• mode (string, optional): keyword | semantic | hybrid (default: hybrid)
• limit (int, default: 20)
• with_rag (bool, optional): include a brief fit explanation
**200 Response (truncated)**
**Useful scripts**
GET /catalog/entities/{id}
Return full metadata for an entity (resolved from the ingested manifests).
**Example**
**404** if not found.
POST /catalog/install  *(admin)*
Execute an install plan (artifacts/adapters) and best-effort **MCP-Gateway** registration when mcp_registration is present.
**Body**
**Notes**
• id must be <type>:<slug>@<version> and match the manifest.
• If manifest is provided inline:
• It is persisted as a catalog Entity.
• mcp_registration is saved on the entity


est.
• If manifest is provided inline:
• It is persisted as a catalog Entity.
• mcp_registration is saved on the entity (used later by sync).
• A matrix.lock.json is written under target.
• Best-effort gateway registration is attempted; errors are recorded in results and entity.gateway_error.
**200 Response (truncated)**
Remotes (catalog sources)
These endpoints manage remote catalog indexes (each is an index.json URL). When no remotes are stored, the Hub seeds them from MATRIX_REMOTES (CSV or JSON array) the first time you call GET /remotes or POST /remotes/sync.
GET /remotes
List configured remotes. If DB is empty, this call seeds from MATRIX_REMOTES.
**200 Response**
POST /remotes  *(admin)*
Add a remote index URL.
**Body**
**201 Response**
DELETE /remotes  *(admin)*
Remove a remote ind


OST /remotes  *(admin)*
Add a remote index URL.
**Body**
**201 Response**
DELETE /remotes  *(admin)*
Remove a remote index URL.
**Body**
**200 Response**
Ingest & Sync
POST /ingest  *(admin)*
Manually trigger ingestion for one or all remotes.
**Body**
**200 Response (truncated)**
POST /remotes/sync  *(admin)*
End-to-end sync:
1. Ensure remotes exist (seed from MATRIX_REMOTES if empty).
2. Ingest each remote.
3. Register **new** MCP servers into MCP-Gateway (Tool → Resources → Prompts → Gateway).
**200 Response**
Gateways (pending → registered)
These endpoints help you see what’s ingested but **not yet** registered in MCP-Gateway, and clean them up if needed.
GET /gateways/pending  *(admin)*
List ingested MCP servers with gateway_registered_at IS NULL.
**Query params**
• limit (int, default


/pending  *(admin)*
List ingested MCP servers with gateway_registered_at IS NULL.
**Query params**
• limit (int, default: 100, max 1000)
• offset (int, default: 0)
**200 Response**
**Tip:** This is exactly what scripts/list_pending_gateways.sh displays.
DELETE /gateways/pending/{uid}  *(admin)*
Delete a **pending** mcp_server by UID (does nothing if it’s already registered).
**200 Response**
**404** is not used; if the row can’t be deleted you get:
POST /gateways/pending/delete  *(admin)*
Bulk delete pending MCP servers.
**Body**
• Provide either uids **or** set all=true.
• With error_only=true, only entities with gateway_error are deleted.
**200 Response**
Scripts (helpers)
• scripts/list_ingested.sh
Lists all ingested mcp_server rows from the DB (falls back to /gateways/pending if DB is


rs)
• scripts/list_ingested.sh
Lists all ingested mcp_server rows from the DB (falls back to /gateways/pending if DB is unavailable). Shows status: PENDING vs REGISTERED.
• scripts/list_pending_gateways.sh
Calls GET /gateways/pending and prints a table. Exits with non-zero if any pending exist (useful in CI).
• scripts/verify_token.sh
Verifies your Hub → Gateway auth and lists /servers and /gateways on MCP-Gateway.
• examples/register_watsonx_local_via_hub.sh
Pushes an inline mcp_server manifest to the Hub and triggers /remotes/sync (which registers the server into MCP-Gateway).
Behavior notes
• **Inline install parity**: an inline manifest in POST /catalog/install is now persisted in the DB, including mcp_registration. The same fields drive /remotes/sync.
• **Gateway registration flow** (


 persisted in the DB, including mcp_registration. The same fields drive /remotes/sync.
• **Gateway registration flow** (on sync):
Tool → Resources → Prompts → Gateway. SSE endpoints are normalized to /messages/ when appropriate.
• **Error visibility**: sync errors are recorded in entity.gateway_error and surfaced by GET /gateways/pending.
Optional: Gateway pass-throughs
If you run the Gateway locally, use the Gateway’s own API (auth header from scripts/verify_token.sh):
• GET /health
• GET /servers
• GET /gateways
• POST /gateways (register a federated server)
These are **not** Hub endpoints; they belong to MCP-Gateway.
Status codes
• 200  OK
• 201  Created (POST /remotes)
• 400  Bad request (invalid body or params)
• 401  Unauthorized (missing/invalid token when required)
• 404  Not found


)
• 400  Bad request (invalid body or params)
• 401  Unauthorized (missing/invalid token when required)
• 404  Not found (for entity lookups; delete-pending returns a soft reason instead)
• 409  Conflict (idempotent creates may translate to OK upstream)
• 500  Internal server error
Environment
• API_TOKEN — enables admin protection on mutating routes.
• MATRIX_REMOTES — CSV or JSON array of index URLs; used to seed remotes when empty.
• DATA_DIR / DB_PATH — where catalog.sqlite is stored (deployment-specific).
• Gateway auth used by the Hub (for sync): MCP_GATEWAY_URL, JWT_SECRET_KEY, BASIC_AUTH_USERNAME, MCP_GATEWAY_TOKEN.


Matrix Hub — Top‑5 Search API
This document defines the **public, production‑safe** search endpoint that powers the Matrix Hub meta search for agents, tools, and MCP servers. The contract is **additive** and **backward‑compatible** with existing deployments.
Endpoint
GET /catalog/search
Purpose
Return the **Top‑5** best matches for a user query across agent, tool, and mcp_server entities.
Query Parameters
| Name            | Type                                  | Default        | Description                                                                                |
| q             | string                                | **required**   | The user intent. Example: summarize pdfs                                                       |
| type          | enum (agent\|tool\|mcp_se


mple: summarize pdfs                                                       |
| type          | enum (agent\|tool\|mcp_server\|any) | any          | Filter by entity type. any means no filter.                                                    |
| limit         | integer (1–100)                       | 5            | Maximum results returned. Public API caps to **5** even if larger is requested.                  |
| with_snippets | boolean                               | false        | Include a short snippet (first \~200 chars of summary/description) in each item, when available. |
| mode          | enum (keyword\|semantic\|hybrid)    | server default | Advanced: controls which backends participate.                                                   |
| with_rag      | boolean


 which backends participate.                                                   |
| with_rag      | boolean                               | false        | Advanced: include an optional fit_reason field per item.                                       |
| rerank        | enum (none\|llm)                    | server default | Advanced: apply an LLM reranker to the merged results.                                           |
**Note:** Additional filtering parameters exist (capabilities, frameworks, providers). They accept CSV values.
Response Shape
Field Notes
• manifest_url comes from the entity’s source_url when present; otherwise it may point to a local resolver GET /catalog/manifest/{id}.
• install_url is a convenience link; clients should still POST /catalog/install under the hood.
• snippe


/manifest/{id}.
• install_url is a convenience link; clients should still POST /catalog/install under the hood.
• snippet appears only if with_snippets=true and summary/description text exists.
• total is a conservative estimate of distinct entities matched by the underlying backends.
Example
Search for the top‑5 items related to extracting tables from PDFs, across all types:
Install
Once you select an item from the results, install it via the canonical install endpoint:
You may also install from an inline manifest by providing a manifest object in the body.
Compatibility & Stability
• This API is **additive**; existing clients continue to work.
• No database migrations are required for this feature.
• Works in SQLite (dev) and Postgres (prod). Semantic search via pgvector can be enabled l


Architecture
Components
• **API** (FastAPI): search, entities, install, remotes, ingest trigger.
• **DB** (PostgreSQL): normalized entity metadata, artifacts, tags, capabilities.
• **Ingestor**: pulls index.json, validates manifests, upserts entities.
• **Installer**: executes artifact steps, writes adapters, updates lockfile, registers with MCP Gateway.
• **Scheduler**: periodic ingestion via APScheduler.
Data model (high level)
• entity — (uid, type, name, version, summary, description, capabilities[], frameworks[], providers[], source_url, created_at, updated_at, provenance)
• artifact — (entity_id, kind, uri, hash, size, install_hint)
• tag & entity_tag, capability & entity_capability (many-to-many)
• optional embedding_chunk (when using vector search)
Diagram
Scaling
• Swap lexical ba


entity_capability (many-to-many)
• optional embedding_chunk (when using vector search)
Diagram
Scaling
• Swap lexical backend to OpenSearch and vector backend to Milvus without changing the public API.
• Keep Matrix Hub stateless; scale horizontally.


Blob Storage
Stores long text artifacts (READMEs, examples) for retrieval-augmented generation (RAG).
Backends
• **Local disk** (default): ${BLOB_DIR:-./data/blobs}
• **Object storage (later)**: S3/MinIO with lifecycle policies
Keys & Paths
• Key format: "{entity_uid}/{section}/{position}" → sanitized to a flat path
• Files are UTF-8 text
• Checksums stored in DB to detect changes
Retention
• Keep last N versions per entity (configurable).
• GC job prunes orphan blobs after successful re-indexing.
Security
• No secrets in blob content.
• If private catalogs are used, ensure the object store is private and accessed via presigned URLs.


Changelog
0.1.0 — Initial Release
• **API**: /health, /catalog/search, /catalog/entities/{id}, /catalog/install, /catalog/remotes, /catalog/ingest
• **Ingest**: index.json pull + schema validation
• **Search**: lexical (pg_trgm), semantic (pgvector optional), hybrid ranking
• **Install**: pip/uv, docker, git, zip; adapters; lockfile
• **MCP Gateway**: tool/server registration via admin API
• **Docs**: MkDocs site (Material)


Chunking
Prepares text for semantic search & RAG.
Inputs
• name, summary, description
• README/excerpts referenced by manifest or repository
• Examples / usage snippets (if provided)
Strategy
• **Hierarchical splitting**:
1. Headings (#, ##) → paragraphs → sentences
2. Target chunk size ~ **300–600 tokens** (configurable)
• **Metadata** per chunk:
• entity_uid, section, position, weight, source_uri, checksum
Weights
• Title/name: higher prior (e.g., 1.3×)
• Summary: 1.2×
• README/body: 1.0×
• Examples/code: 1.1×
Output
• A list of (chunk_id, entity_uid, text, weight, meta) ready for embedding.


Configuration
Matrix Hub is configured entirely via environment variables (see .env.example).
| Key | Description | Example |
| DATABASE_URL | SQLAlchemy URL | postgresql+psycopg://matrix:matrix@db:5432/matrixhub |
| HOST / PORT | Bind address & port | 0.0.0.0 / 443 |
| API_TOKEN | Bearer token for admin/protected routes | supersecret |
| MATRIX_REMOTES | CSV/JSON list of index.json URLs to ingest | https://raw.githubusercontent.com/.../index.json |
| INGEST_INTERVAL_MIN | Background ingestion interval (minutes) | 15 |
| SEARCH_LEXICAL_BACKEND | pgtrgm or none | pgtrgm |
| SEARCH_VECTOR_BACKEND | pgvector or none | none |
| EMBED_MODEL | Embedder model id (informational; pluggable) | all-MiniLM-L6-v2 |
| MCP_GATEWAY_URL | MCP Gateway base URL | http://mcpgateway:7200 |
| MCP_GATEWAY_TOKEN


pluggable) | all-MiniLM-L6-v2 |
| MCP_GATEWAY_URL | MCP Gateway base URL | http://mcpgateway:7200 |
| MCP_GATEWAY_TOKEN | Bearer for gateway admin API | supersecret |
Notes
• If API_TOKEN is set, pass Authorization: Bearer <token> on admin calls (/catalog/ingest, /catalog/remotes).
• For local SQLLite quick trials, you can use sqlite+pysqlite:///./data/catalog.sqlite. Some search features (pg_trgm/pgvector) won’t be available.


Container Notes (Matrix Hub + MCP‑Gateway)
This document explains how the Docker image is built and run for **Matrix Hub** together with the **MCP‑Gateway**, and how to operate it in production.
What’s inside the image
• **Two isolated Python virtualenvs**:
• **Matrix Hub venv:** /app/.venv
• **MCP‑Gateway venv:** /app/mcpgateway/.venv
• **Code & scripts:**
• App code under /app/src
• Gateway project under /app/mcpgateway
• Entrypoint supervisor: /app/scripts/run_prod.sh
• Helper scripts: /app/scripts/build_container.sh, /app/scripts/run_container.sh
• **Default exposed ports:**
• **Hub:** 443
• **Gateway:** 4444
• **Healthcheck:** probes Hub at http://127.0.0.1:${PORT:-443}/.
The entrypoint (scripts/run_prod.sh) launches **Gateway first**, waits for its port, then launches **Matrix Hub**.


43}/.
The entrypoint (scripts/run_prod.sh) launches **Gateway first**, waits for its port, then launches **Matrix Hub**. Both are supervised: if one exits, the other is stopped gracefully.
Environment files
• **Matrix Hub:** root .env (copied from .env.example if missing).
• **Gateway:** /app/mcpgateway/.env (copied from repo’s .env.gateway.local during build if present via setup script).
**Important variables:**
• **Hub bind:** HOST=0.0.0.0, PORT=443
• **Hub → Gateway integration:** MCP_GATEWAY_URL=http://127.0.0.1:4444 (or external URL if you skip the embedded gateway)
• **Gateway bind:** HOST=0.0.0.0, PORT=4444
Building the image
Use the helper script (recommended):
Direct docker build (if preferred):
Running the container
Use the run helper (recommended):
Key flags:
• --app-port PORT →


rect docker build (if preferred):
Running the container
Use the run helper (recommended):
Key flags:
• --app-port PORT → host port for Hub (container 443)
• --gw-port PORT → host port for Gateway (container 4444)
• --skip-gateway → do not start the embedded Gateway (sets GATEWAY_SKIP_START=1)
• --env-file PATH → pass a .env for Matrix Hub
• --data-volume NAME → persist /app/data (Hub data/SQLite)
• --gw-volume NAME → persist /app/mcpgateway/.state (Gateway state).
If used, set in Gateway env:
Direct docker run example:
Supervisor behavior (scripts/run_prod.sh)
• Starts **MCP‑Gateway** first, waits for :4444 (or gateway’s PORT) to listen.
• Starts **Matrix Hub** next on :443 (or Hub’s PORT).
• Uses **gunicorn + uvicorn workers** when available; falls back to uvicorn.
• **Workers** auto‑calc


43 (or Hub’s PORT).
• Uses **gunicorn + uvicorn workers** when available; falls back to uvicorn.
• **Workers** auto‑calculated as 2*CPU+1 (override via env).
• Graceful shutdown on exit; kills the sibling process if one dies.
• Runs Alembic migrations for Hub if alembic and alembic.ini are present.
**Tuning via env (override at runtime):**
• **Hub:** HUB_WORKERS, HUB_TIMEOUT, HUB_GRACEFUL_TIMEOUT, HUB_KEEPALIVE, HUB_MAX_REQUESTS, HUB_MAX_REQUESTS_JITTER, HUB_PRELOAD=true|false, HUB_LOG_LEVEL
• **Gateway:** GW_WORKERS, GW_TIMEOUT, GW_GRACEFUL_TIMEOUT, GW_KEEPALIVE, GW_MAX_REQUESTS, GW_MAX_REQUESTS_JITTER, GW_PRELOAD=true|false, GW_LOG_LEVEL
• **Skip gateway:** GATEWAY_SKIP_START=1
• **Override gateway app path (fallback mode):** GW_APP_MODULE=mcp_gateway.app:app
Using an **external** MCP‑Ga


_START=1
• **Override gateway app path (fallback mode):** GW_APP_MODULE=mcp_gateway.app:app
Using an **external** MCP‑Gateway
• Run with --skip-gateway (or GATEWAY_SKIP_START=1).
• Set **Matrix Hub** .env:
• Do **not** publish container’s 4444 in that scenario.
Volumes & persistence
• **Hub data:** /app/data → mount a named volume (e.g., matrixhub_data).
• **Gateway state (optional):** /app/mcpgateway/.state → mount a volume (e.g., mcpgw_data) and point gateway DATABASE_URL at a file in that mount.
Logs
• Both services log to **stdout/stderr** (12‑factor friendly).
• View logs:
Health / readiness
• Dockerfile healthcheck pings Hub at http://127.0.0.1:${PORT:-443}/.
• scripts/run_container.sh waits for Hub after start and prints the URLs.
Updating
1. Pull latest code (or update submodules).


pts/run_container.sh waits for Hub after start and prints the URLs.
Updating
1. Pull latest code (or update submodules).
2. Rebuild image:
3. Restart container:
Troubleshooting
• **Port already in use**
• Change host mapping: --app-port / --gw-port, or free the port.
• **Gateway module path error** (fallback mode):
Set GW_APP_MODULE to the correct ASGI path (e.g., my_gateway.main:app).
• **Hub import errors (e.g., SQLAlchemy)**
Rebuild image to ensure deps are installed: scripts/build_container.sh.
Locally (non‑container) run make setup.
• **Hub cannot reach gateway**
Check MCP_GATEWAY_URL and that gateway is reachable from the Hub container.
If using embedded gateway, ensure :4444 is listening inside the container (docker exec -it matrix-hub sh then lsof -nP -i :4444).
• **Migrations fail


sure :4444 is listening inside the container (docker exec -it matrix-hub sh then lsof -nP -i :4444).
• **Migrations failing**
Confirm alembic.ini exists and DB URL is reachable. Disable migrations by removing alembic or the alembic.ini file from the image if not used.
Security & ops recommendations
• Run behind a reverse proxy/ingress with **TLS termination**.
• Inject secrets (tokens, DB creds) through **orchestrator secrets** (K8s/Swarm), not baked into images.
• Restrict exposed ports; if using external gateway, **do not expose 4444** from the Hub container.
• Use non‑root (already configured). For stricter profiles, consider:
• Read‑only root filesystem, tmpfs for /tmp
• Drop capabilities (--cap-drop=ALL) if feasible
• Resource limits (CPU/memory) and ulimit adjustments as needed
Kuber


p
• Drop capabilities (--cap-drop=ALL) if feasible
• Resource limits (CPU/memory) and ulimit adjustments as needed
Kubernetes / Compose (high‑level)
• **Kubernetes:** Prefer **separate deployments** for Hub and Gateway. Point the Hub’s MCP_GATEWAY_URL at the Gateway service DNS.
• **Docker Compose:** Either use this single image (both processes) or two services (one for Hub, one for Gateway). The latter is recommended for clearer lifecycle and scaling.
Quick references
• **Build:** scripts/build_container.sh
• **Run:** scripts/run_container.sh
• **Entrypoint:** scripts/run_prod.sh
• **Default ports:** Hub 443, Gateway 4444
• **Hub env:** .env (copied from .env.example if missing)
• **Gateway env:** mcpgateway/.env (from gateway setup)
• **Volumes:** /app/data, optional /app/mcpgateway/.sta


missing)
• **Gateway env:** mcpgateway/.env (from gateway setup)
• **Volumes:** /app/data, optional /app/mcpgateway/.state
Production
This section shows a **simple, repeatable** way to build and run Matrix Hub + MCP‑Gateway for production using Postgres, split API/Worker roles, and the new prod build script.
1) Build a production image
Use the new helper (recommended):
Push to your registry (optional):
2) Prepare environment files
• **Hub (.env)** — copy from .env.example, then set at least:
• **Gateway (.env.gateway)** — copy from .env.gateway.example. You can keep the default SQLite or point to Postgres:
3) Bring up the production stack (Compose)
A production compose file typically defines four services: db (Postgres), api (Hub HTTP), worker (ingest scheduler), and gateway. Once your doc


le typically defines four services: db (Postgres), api (Hub HTTP), worker (ingest scheduler), and gateway. Once your docker-compose.prod.yml is in place:
• **API** listens on :443 with INGEST_SCHED_ENABLED=false.
• **Worker** runs the same app with INGEST_SCHED_ENABLED=true (no public port required).
• **Gateway** listens on :4444 (optional if you use an external gateway).
Check health:
4) Scale and upgrade safely
• Scale API (stateless) for more throughput/HA:
• Rolling upgrade:
5) Expectations in production
• **Database**: Postgres is required for real concurrency; dev SQLite is fine with WAL+timeouts.
• **Migrations**: Hub applies Alembic migrations on startup (already wired). Ensure DB connectivity.
• **Split roles**: Keep search responsive by running API and Worker as **separate proce


 wired). Ensure DB connectivity.
• **Split roles**: Keep search responsive by running API and Worker as **separate processes/containers**.
• **Secrets**: Provide DB/Gateway tokens via environment or orchestrator secrets (do not bake into images).
• **Proxy/TLS**: Place API and Gateway behind a reverse proxy/ingress that handles TLS, timeouts, and health checks.
*End of document.*


Deployment
Docker Compose (reference)
• See docker-compose.yaml included in the repo.
• Exposes the API on :443 and Postgres on :5432.
Container image
Kubernetes (guidance)
• Use a Deployment with 2+ replicas.
• Configure a Secret for API_TOKEN and gateway tokens.
• Add a Job or init container to run Alembic migrations before rollout.
• Use a Readiness probe on /health.
Persistence
• Postgres should be backed by persistent volumes.
• The API is stateless (no local writes beyond ephemeral temp files).


Development
Prerequisites
• Python 3.11 or 3.12
• Postgres (optional — SQLite works for unit tests)
• make for convenience
Setup
Make targets
• make dev — uvicorn with auto-reload
• make run — foreground server
• make lint / make fmt — Ruff static checks & formatting
• make test — pytest test suite
• make migrate m="msg" — Alembic revision
• make upgrade — apply migrations to head
Tests
• Unit tests use SQLite by default.
• CI runs on Python 3.11/3.12 (see .github/workflows/ci.yml).
Style
• Ruff enforces formatting and lint rules.
• Prefer type hints and small, composable functions.
• Keep external network operations behind service boundaries.