PyStreamMCP 3.3.1__py3-none-any.whl → 3.3.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pystreammcp/__init__.py CHANGED
@@ -7,7 +7,7 @@ Reduces token usage by 60-75% while maintaining quality.
7
7
  Includes StatGuardian integration for data quality validation.
8
8
  """
9
9
 
10
- __version__ = "3.3.1"
10
+ __version__ = "3.3.2"
11
11
 
12
12
  from .agent import Agent, QueryResult
13
13
  from .query import Query, QueryIntent, QueryConstraints
pystreammcp/cli_daemon.py CHANGED
@@ -1,16 +1,28 @@
1
1
  """Dashboard daemon - runs in background, restarts on close"""
2
2
 
3
+ import re
4
+ import shlex
3
5
  import subprocess
4
6
  import time
5
7
  import signal
6
8
  import sys
7
9
  from pathlib import Path
8
10
 
11
+ _PACKAGE_NAME_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.-]*$")
12
+
9
13
 
10
14
  class DashboardDaemon:
11
15
  def __init__(self, package_name: str, dashboard_cmd: str):
16
+ if not isinstance(package_name, str) or not _PACKAGE_NAME_RE.match(
17
+ package_name
18
+ ):
19
+ raise ValueError(f"Invalid package name: {package_name!r}")
12
20
  self.package_name = package_name
13
- self.dashboard_cmd = dashboard_cmd
21
+ # Split into argv and run without a shell so that a caller-supplied
22
+ # command cannot inject shell metacharacters.
23
+ self.dashboard_cmd = shlex.split(dashboard_cmd)
24
+ if not self.dashboard_cmd:
25
+ raise ValueError("dashboard_cmd must not be empty")
14
26
  self.process = None
15
27
  self.running = True
16
28
 
@@ -27,7 +39,7 @@ class DashboardDaemon:
27
39
 
28
40
  self.process = subprocess.Popen(
29
41
  self.dashboard_cmd,
30
- shell=True,
42
+ shell=False,
31
43
  stdout=subprocess.PIPE,
32
44
  stderr=subprocess.PIPE,
33
45
  )
@@ -0,0 +1,266 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyStreamMCP
3
+ Version: 3.3.2
4
+ Summary: Intelligent MCP orchestration hub - Intent understanding, capability matching, tool ranking with cohesive foundation
5
+ Author-email: Georgi Mammen Mullassery <mullassery@gmail.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Repository, https://github.com/Mullassery/PyStreamMCP
8
+ Project-URL: Issues, https://github.com/Mullassery/PyStreamMCP/issues
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: pydantic>=2.0
21
+ Requires-Dist: python-frontmatter>=1.0.0
22
+ Requires-Dist: click>=8.0
23
+ Requires-Dist: rich>=13.0
24
+ Requires-Dist: httpx>=0.24.0
25
+ Requires-Dist: toml>=0.10; python_version < "3.11"
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=7.0; extra == "dev"
28
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
29
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
30
+ Provides-Extra: api
31
+ Requires-Dist: fastapi>=0.104.0; extra == "api"
32
+ Requires-Dist: uvicorn[standard]>=0.24.0; extra == "api"
33
+ Requires-Dist: pydantic-settings>=2.0; extra == "api"
34
+ Requires-Dist: flask>=2.3.0; extra == "api"
35
+ Provides-Extra: mcp
36
+ Requires-Dist: mcp>=0.1.0; extra == "mcp"
37
+ Provides-Extra: langchain
38
+ Requires-Dist: langchain>=0.1.0; extra == "langchain"
39
+ Provides-Extra: llamaindex
40
+ Requires-Dist: llama-index>=0.9.0; extra == "llamaindex"
41
+ Provides-Extra: semantic-kernel
42
+ Requires-Dist: semantic-kernel>=0.4.0; extra == "semantic-kernel"
43
+ Provides-Extra: crewai
44
+ Requires-Dist: crewai>=0.1.0; extra == "crewai"
45
+ Provides-Extra: pydantic-ai
46
+ Requires-Dist: pydantic-ai>=0.1.0; extra == "pydantic-ai"
47
+ Provides-Extra: haystack
48
+ Requires-Dist: haystack-ai>=2.0.0; extra == "haystack"
49
+ Provides-Extra: temporal
50
+ Requires-Dist: temporalio>=1.0.0; extra == "temporal"
51
+ Provides-Extra: airflow
52
+ Requires-Dist: apache-airflow>=2.7.0; extra == "airflow"
53
+ Provides-Extra: all-integrations
54
+ Requires-Dist: langchain>=0.1.0; extra == "all-integrations"
55
+ Requires-Dist: llama-index>=0.9.0; extra == "all-integrations"
56
+ Requires-Dist: semantic-kernel>=0.4.0; extra == "all-integrations"
57
+ Requires-Dist: crewai>=0.1.0; extra == "all-integrations"
58
+ Requires-Dist: pydantic-ai>=0.1.0; extra == "all-integrations"
59
+ Requires-Dist: haystack-ai>=2.0.0; extra == "all-integrations"
60
+ Requires-Dist: temporalio>=1.0.0; extra == "all-integrations"
61
+ Requires-Dist: apache-airflow>=2.7.0; extra == "all-integrations"
62
+ Dynamic: license-file
63
+
64
+ # PyStreamMCP
65
+
66
+ [![PyPI](https://img.shields.io/pypi/v/pystreammcp)](https://pypi.org/project/pystreammcp/)
67
+ [![CI](https://github.com/Mullassery/PyStreamMCP/actions/workflows/ci.yml/badge.svg)](https://github.com/Mullassery/PyStreamMCP/actions/workflows/ci.yml)
68
+ [![Python 3.9+](https://img.shields.io/badge/Python-3.9%2B-blue)](https://www.python.org)
69
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](./LICENSE)
70
+
71
+ Context and token optimization for AI agents — query planning, source discovery, and MCP orchestration.
72
+
73
+ ## What it is
74
+
75
+ PyStreamMCP is an AI-agent intelligence layer: query planning, context discovery, and
76
+ token-cost optimization, exposed both as a Python SDK and as an MCP (Model Context
77
+ Protocol) tool server. It also includes a small event-driven orchestration/federation
78
+ layer for coordinating multiple MCP endpoints. The published package is **pure Python** —
79
+ `pip install` works with no Rust toolchain or compiler.
80
+
81
+ ## Features / Highlights
82
+
83
+ - **Agent query planning** — `pystreammcp.Agent` with selectable optimization strategies
84
+ (`token_efficient`, `balanced`, `quality_first`) and token accounting.
85
+ - **Source discovery** — `SourceRegistry` ranks registered data sources by real
86
+ word-overlap relevance (Jaccard) to a query; backs REST `/discover`, the
87
+ `pystreammcp_discover` MCP tool, and `LangchainAdapter.discover()`.
88
+ - **MCP tool server** — `PyStreamMCPHandler` wires the MCP tool-call surface directly to a
89
+ live `Orchestrator`/`EventRouter`; MCP tool args are validated with Pydantic and the
90
+ advertised schema is generated from the same models.
91
+ - **MCP orchestration & federation** — `Orchestrator` probes each configured endpoint's
92
+ real `tools/list` MCP method, with lexical tool-relevance ranking and capability
93
+ matching.
94
+ - **Webhook event server** — `pystreammcp.server.create_flask_app()` exposes an
95
+ HMAC-SHA256-signed event endpoint; unsigned or invalid events are rejected, and the
96
+ endpoint fails closed (`503`) if no secret is configured.
97
+ - **Framework adapters** — LangChain, LlamaIndex, CrewAI, Semantic Kernel, PydanticAI,
98
+ and Haystack under `pystreammcp.integrations`.
99
+ - **CLI** — `pystreammcp` with `query` / `server` / `version` / `dashboard`.
100
+ - **OKF integration** — an Open Knowledge Format catalog/discovery/query-planner feature
101
+ (see [`OKF_INTEGRATION.md`](OKF_INTEGRATION.md)).
102
+
103
+ ## Who it's for / Use cases
104
+
105
+ - **Giving an AI agent a query-planning layer over multiple data sources**, exposed as
106
+ real MCP tools rather than hand-rolled per-agent glue code.
107
+ - **Coordinating multiple MCP endpoints** through the event-driven
108
+ orchestration/federation layer instead of wiring each one separately.
109
+ - **Tracking token-cost optimization** across agent queries.
110
+ - **Adding MCP support to an existing agent framework** through the shipped adapters.
111
+
112
+ ## What it is not
113
+
114
+ - **The Rust backend is not shipped.** `core/` and `python/src/lib.rs` are an in-progress
115
+ Rust workspace intended as a future performance backend; the wheel built from
116
+ `pyproject.toml` is pure Python. The workspace compiles and its tests pass, but it is
117
+ tracked separately and not part of the published package.
118
+ - **Some orchestration capabilities are not fully implemented.** Cross-project federated
119
+ query *execution* and cross-database joins currently report
120
+ `"status": "not_implemented"` with an explanation, rather than fabricated result rows.
121
+ - **Source discovery is a word-overlap heuristic, not semantic search.**
122
+ `SourceRegistry` uses Jaccard token overlap; wrap it with your own similarity scoring if
123
+ you need embeddings.
124
+ - **Optional concurrency/framework packages have Python constraints.** The core SDK
125
+ (`Agent`, `SourceRegistry`, `Orchestrator`) supports Python 3.9+, but the `mcp` and
126
+ `semantic-kernel` extras require Python 3.10+ (their upstream packages don't publish
127
+ 3.9-compatible releases). CI tests 3.10, 3.11, and 3.12 only.
128
+ - **Known dependency CVEs.** `pip-audit` (run 2026-09-20) found 7 known CVEs in
129
+ transitive dependencies (`werkzeug` via `flask`, `nltk` via `llama-index`). See
130
+ [`ROADMAP_HONEST.md`](ROADMAP_HONEST.md) §4.
131
+ - **The HTTP servers have no auth layer of their own.** They bind to `127.0.0.1`
132
+ (localhost-only) by default; pass `host="0.0.0.0"` explicitly only inside a network
133
+ boundary you control. The event endpoint requires HMAC-SHA256 signatures. See
134
+ [SECURITY.md](SECURITY.md).
135
+
136
+ ## Project status
137
+
138
+ **Beta.** Current version **3.3.1** (`Development Status :: 4 - Beta`). The pure-Python
139
+ package is the shipped artifact and is under active hardening; the Rust workspace compiles
140
+ and passes its tests but is not shipped. The most recent pass fixed the Rust build,
141
+ Python `ruff` findings, and a stale integration test — see
142
+ [`ROADMAP_HONEST.md`](ROADMAP_HONEST.md) for the unhedged status.
143
+
144
+ ## Installation
145
+
146
+ ```bash
147
+ pip install PyStreamMCP
148
+ ```
149
+
150
+ Optional extras — install only what you need:
151
+
152
+ ```bash
153
+ pip install "PyStreamMCP[api]" # FastAPI + Flask HTTP servers
154
+ pip install "PyStreamMCP[mcp]" # MCP protocol client library
155
+ pip install "PyStreamMCP[langchain]" # LangChain adapter
156
+ pip install "PyStreamMCP[llamaindex]" # LlamaIndex adapter
157
+ pip install "PyStreamMCP[semantic-kernel]" # Semantic Kernel adapter
158
+ pip install "PyStreamMCP[all-integrations]" # every framework adapter
159
+ ```
160
+
161
+ Requires Python 3.9+ for the core SDK; the `mcp` and `semantic-kernel` extras require
162
+ Python 3.10+.
163
+
164
+ ## Quick start
165
+
166
+ ```python
167
+ from pystreammcp import Agent
168
+
169
+ agent = Agent(
170
+ agent_id="recommendation_engine",
171
+ name="Product Recommendation Engine",
172
+ optimization_strategy="token_efficient", # or "balanced" / "quality_first"
173
+ max_tokens=1000,
174
+ )
175
+
176
+ result = agent.query("What are the top 10 customers by lifetime value?")
177
+
178
+ print(f"Baseline tokens: {result.baseline_tokens}")
179
+ print(f"Optimized tokens: {result.optimized_tokens}")
180
+ print(f"Reduction: {result.cost_reduction_percent:.1f}%")
181
+ ```
182
+
183
+ Token counts are estimated from the actual query text (a ~4-characters-per-token
184
+ heuristic) and the reduction is computed from the selected strategy — not a fixed number.
185
+ See [`examples/basic_usage.py`](examples/basic_usage.py) for a full walkthrough.
186
+
187
+ ## Usage
188
+
189
+ **Data source discovery:**
190
+
191
+ ```python
192
+ from pystreammcp import SourceRegistry
193
+
194
+ registry = SourceRegistry()
195
+ registry.register(
196
+ "orders_db",
197
+ description="customer order history and revenue by quarter",
198
+ type="database",
199
+ tags=["orders", "revenue"],
200
+ )
201
+
202
+ results = registry.discover("customer revenue by quarter")
203
+ # [{"name": "orders_db", "type": "database", "relevance": 0.4444, "matched_terms": [...]}]
204
+ ```
205
+
206
+ **MCP orchestration & federation** (`pystreammcp.toml`):
207
+
208
+ ```toml
209
+ [federation]
210
+ endpoints = [
211
+ "http://localhost:8765/mcp",
212
+ "http://localhost:8766/mcp",
213
+ ]
214
+ ```
215
+
216
+ ```python
217
+ from pystreammcp import Orchestrator
218
+
219
+ orch = Orchestrator() # reads ./pystreammcp.toml
220
+ result = orch.discover_mcp_projects()
221
+ ```
222
+
223
+ **Webhook event server (HMAC-SHA256):**
224
+
225
+ ```bash
226
+ export PYSTREAMMCP_WEBHOOK_SECRET="a long random shared secret"
227
+ ```
228
+
229
+ Senders compute `HMAC-SHA256(secret, raw_request_body)` and send it as
230
+ `X-PyStreamMCP-Signature: sha256=<hex digest>`. Requests with a missing or invalid
231
+ signature are rejected with `401`; with no secret configured, the endpoint rejects every
232
+ request with `503`.
233
+
234
+ **Testing:**
235
+
236
+ ```bash
237
+ pip install -e ".[dev,api,mcp,langchain,llamaindex,semantic-kernel]"
238
+ pytest tests/ -v
239
+ ```
240
+
241
+ ## Documentation
242
+
243
+ - [`ROADMAP_HONEST.md`](ROADMAP_HONEST.md) — full, unhedged status: what's tested, what's fabricated/broken, technical debt, what's explicitly not built
244
+ - [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — how the shipped package is structured, with a diagram
245
+ - [`OKF_INTEGRATION.md`](OKF_INTEGRATION.md) — the OKF catalog/discovery/query-planner feature
246
+ - [`CHANGELOG.md`](CHANGELOG.md) — release history
247
+ - [`examples/`](examples/) — runnable examples, including framework integrations and the MCP orchestrator
248
+
249
+ ## Contributing
250
+
251
+ Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) and
252
+ [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). Report security issues per
253
+ [SECURITY.md](SECURITY.md).
254
+
255
+ ## Related projects
256
+
257
+ - [PyStreamXL](https://github.com/Mullassery/PyStreamXL) — stream large Excel workbooks with constant memory
258
+ - [PyStreamPDF](https://github.com/Mullassery/PyStreamPDF) — token-efficient PDF extraction for RAG
259
+ - [PyStreamMCP](https://github.com/Mullassery/PyStreamMCP) — context and token optimization for AI agents
260
+ - [PyStreamAI](https://github.com/Mullassery/PyStreamAI) — ML deployment toolkit (canary/A-B, cost tracking, ONNX)
261
+ - [PyTokenCalc](https://github.com/Mullassery/PyTokenCalc) — LLM token counting and cost estimation
262
+ - [Browse all projects](https://github.com/Mullassery?tab=repositories)
263
+
264
+ ## License
265
+
266
+ This project is licensed under the [Apache License 2.0](LICENSE).
@@ -1,11 +1,11 @@
1
- pystreammcp/__init__.py,sha256=xVcMzBcJbK004OFoVRYaPJEk_QrjlXOMjB52ZHkfces,2881
1
+ pystreammcp/__init__.py,sha256=QWzbxF2R1ASWxvVz8z4rW_8VJNRUrXH5b3hR5D_ft6I,2881
2
2
  pystreammcp/_mcp_connector.py,sha256=CTPdQHT64aVxmolwG0l0irCj2uQshwJvhbdbGgyBEu0,31208
3
3
  pystreammcp/_mcp_tools.py,sha256=7s3DtzIbJxHVFMmISdd9EcFbmtKrSMIDXH27j0onhMY,32540
4
4
  pystreammcp/adapters.py,sha256=uB0zGKJU6jcTyRsUDIiiOgGg6K-8dbJw1YLt0ZiAiao,6801
5
5
  pystreammcp/agent.py,sha256=LJUPxnvcrT01rG8l3pqVXQ58OZyeqBn41GYM-hyHIWo,6385
6
6
  pystreammcp/api.py,sha256=4_9i86lQFKxzHdPB1TXzYVBJS6jna9Q2_s7Ea3-v6vg,12860
7
7
  pystreammcp/cli.py,sha256=nkVGnaqjdxeALoBwv0klaSHfjdrRg_fwYY9h_1afiiU,4145
8
- pystreammcp/cli_daemon.py,sha256=_ZSNtifMGOIkcR2mZaycdDDYYes4jtv0cdeMdMKU0Jw,1999
8
+ pystreammcp/cli_daemon.py,sha256=Ck9ICofTWwV-jE3jxBfud9BSAElWMBiJA4asoUCGAh0,2513
9
9
  pystreammcp/cli_dashboard.py,sha256=J5pnUUpkvQGjltUOFN6SPbNF1yyXsjHQR0VCI14ty6M,10562
10
10
  pystreammcp/context.py,sha256=WIgwbb0MAZTnPfQiZW0PLunEtxJatsHmwTnCRsvSqLU,2213
11
11
  pystreammcp/decomposition.py,sha256=peRR1MZbH29DCWMSvtL4Btzp1QhpXsFuVo5LOXPnoIs,6532
@@ -48,9 +48,9 @@ pystreammcp/qa/__init__.py,sha256=NvUUjyWNIyMfckW4J34Cn1xKYX5CnDlM22mXZF3pfnY,17
48
48
  pystreammcp/qa/validator.py,sha256=qTpdkFE7Z7l16fE1emwqfbr8W2_zh1m4Zvzz3hjnGt8,10694
49
49
  pystreammcp/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
50
  pystreammcp/scripts/post_install.py,sha256=lnxiXH8Owq_F4M4KFHNL-GJ5JMYVevUcVo3M5JXOpRo,2719
51
- pystreammcp-3.3.1.dist-info/licenses/LICENSE,sha256=9Z5Dr-U7th_6-121oVLdN8Bdtlotgo_7E71IzjHQQL4,11348
52
- pystreammcp-3.3.1.dist-info/METADATA,sha256=qDfFNbjmT1qHW41p0XUBwux5JJKEn4BbQxIVnmPu5Ms,18567
53
- pystreammcp-3.3.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
54
- pystreammcp-3.3.1.dist-info/entry_points.txt,sha256=EEyR2OxJzCyf353Yipp7-g844uj4y81nTrN06k5786k,52
55
- pystreammcp-3.3.1.dist-info/top_level.txt,sha256=SHNryMw9AAzKhuqpS2tCkxIfTpx4z623tlW8BazwTNM,12
56
- pystreammcp-3.3.1.dist-info/RECORD,,
51
+ pystreammcp-3.3.2.dist-info/licenses/LICENSE,sha256=9Z5Dr-U7th_6-121oVLdN8Bdtlotgo_7E71IzjHQQL4,11348
52
+ pystreammcp-3.3.2.dist-info/METADATA,sha256=vPNE3pbLPO5VmqO3MGSAlQzsKcr6UVESn5tysLaauPU,11665
53
+ pystreammcp-3.3.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
54
+ pystreammcp-3.3.2.dist-info/entry_points.txt,sha256=EEyR2OxJzCyf353Yipp7-g844uj4y81nTrN06k5786k,52
55
+ pystreammcp-3.3.2.dist-info/top_level.txt,sha256=SHNryMw9AAzKhuqpS2tCkxIfTpx4z623tlW8BazwTNM,12
56
+ pystreammcp-3.3.2.dist-info/RECORD,,
@@ -1,404 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: PyStreamMCP
3
- Version: 3.3.1
4
- Summary: Intelligent MCP orchestration hub - Intent understanding, capability matching, tool ranking with cohesive foundation
5
- Author-email: Georgi Mammen Mullassery <mullassery@gmail.com>
6
- License-Expression: Apache-2.0
7
- Project-URL: Repository, https://github.com/Mullassery/PyStreamMCP
8
- Project-URL: Issues, https://github.com/Mullassery/PyStreamMCP/issues
9
- Classifier: Development Status :: 4 - Beta
10
- Classifier: Intended Audience :: Developers
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.9
13
- Classifier: Programming Language :: Python :: 3.10
14
- Classifier: Programming Language :: Python :: 3.11
15
- Classifier: Programming Language :: Python :: 3.12
16
- Classifier: Programming Language :: Python :: 3.13
17
- Requires-Python: >=3.9
18
- Description-Content-Type: text/markdown
19
- License-File: LICENSE
20
- Requires-Dist: pydantic>=2.0
21
- Requires-Dist: python-frontmatter>=1.0.0
22
- Requires-Dist: click>=8.0
23
- Requires-Dist: rich>=13.0
24
- Requires-Dist: httpx>=0.24.0
25
- Requires-Dist: toml>=0.10; python_version < "3.11"
26
- Provides-Extra: dev
27
- Requires-Dist: pytest>=7.0; extra == "dev"
28
- Requires-Dist: pytest-cov>=4.0; extra == "dev"
29
- Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
30
- Provides-Extra: api
31
- Requires-Dist: fastapi>=0.104.0; extra == "api"
32
- Requires-Dist: uvicorn[standard]>=0.24.0; extra == "api"
33
- Requires-Dist: pydantic-settings>=2.0; extra == "api"
34
- Requires-Dist: flask>=2.3.0; extra == "api"
35
- Provides-Extra: mcp
36
- Requires-Dist: mcp>=0.1.0; extra == "mcp"
37
- Provides-Extra: langchain
38
- Requires-Dist: langchain>=0.1.0; extra == "langchain"
39
- Provides-Extra: llamaindex
40
- Requires-Dist: llama-index>=0.9.0; extra == "llamaindex"
41
- Provides-Extra: semantic-kernel
42
- Requires-Dist: semantic-kernel>=0.4.0; extra == "semantic-kernel"
43
- Provides-Extra: crewai
44
- Requires-Dist: crewai>=0.1.0; extra == "crewai"
45
- Provides-Extra: pydantic-ai
46
- Requires-Dist: pydantic-ai>=0.1.0; extra == "pydantic-ai"
47
- Provides-Extra: haystack
48
- Requires-Dist: haystack-ai>=2.0.0; extra == "haystack"
49
- Provides-Extra: temporal
50
- Requires-Dist: temporalio>=1.0.0; extra == "temporal"
51
- Provides-Extra: airflow
52
- Requires-Dist: apache-airflow>=2.7.0; extra == "airflow"
53
- Provides-Extra: all-integrations
54
- Requires-Dist: langchain>=0.1.0; extra == "all-integrations"
55
- Requires-Dist: llama-index>=0.9.0; extra == "all-integrations"
56
- Requires-Dist: semantic-kernel>=0.4.0; extra == "all-integrations"
57
- Requires-Dist: crewai>=0.1.0; extra == "all-integrations"
58
- Requires-Dist: pydantic-ai>=0.1.0; extra == "all-integrations"
59
- Requires-Dist: haystack-ai>=2.0.0; extra == "all-integrations"
60
- Requires-Dist: temporalio>=1.0.0; extra == "all-integrations"
61
- Requires-Dist: apache-airflow>=2.7.0; extra == "all-integrations"
62
- Dynamic: license-file
63
-
64
- # PyStreamMCP
65
-
66
- [![CI](https://github.com/Mullassery/PyStreamMCP/actions/workflows/ci.yml/badge.svg)](https://github.com/Mullassery/PyStreamMCP/actions/workflows/ci.yml)
67
-
68
- An AI-agent intelligence layer: query planning, context discovery, and
69
- token-cost optimization, exposed both as a Python SDK and as an MCP
70
- (Model Context Protocol) tool server, plus a small event-driven
71
- orchestration/federation layer for coordinating multiple MCP endpoints.
72
-
73
- Pure Python — `pip install` just works, no Rust toolchain or compiler
74
- required.
75
-
76
- ## Use cases
77
-
78
- - **Giving an AI agent a query-planning layer over multiple data
79
- sources**, exposed as real MCP tools rather than hand-rolled per-agent
80
- glue code.
81
- - **Coordinating multiple MCP endpoints** through the event-driven
82
- orchestration/federation layer instead of wiring each one separately.
83
- - **Tracking token-cost optimization** across agent queries.
84
- - **Not yet a good fit for:** anything depending on the Rust workspace —
85
- see [Rust workspace (not shipped)](#rust-workspace-not-shipped) below;
86
- the published package is pure Python only.
87
-
88
- ## Install
89
-
90
- ```bash
91
- pip install PyStreamMCP
92
- ```
93
-
94
- Optional extras, install what you actually need:
95
-
96
- ```bash
97
- pip install "PyStreamMCP[api]" # FastAPI + Flask HTTP servers
98
- pip install "PyStreamMCP[mcp]" # MCP protocol client library
99
- pip install "PyStreamMCP[langchain]" # LangChain adapter
100
- pip install "PyStreamMCP[llamaindex]" # LlamaIndex adapter
101
- pip install "PyStreamMCP[semantic-kernel]" # Semantic Kernel adapter
102
- pip install "PyStreamMCP[all-integrations]" # every framework adapter
103
- ```
104
-
105
- Requires Python 3.9+ for the core SDK (`Agent`, `SourceRegistry`,
106
- `Orchestrator`). The `mcp` and `semantic-kernel` extras require Python
107
- 3.10+ — their upstream packages don't publish Python 3.9-compatible
108
- releases, so `pip install` for those extras will fail on 3.9. CI tests
109
- 3.10, 3.11, and 3.12 only; 3.9 is not covered by CI.
110
-
111
- ## Quick start
112
-
113
- ```python
114
- from pystreammcp import Agent
115
-
116
- agent = Agent(
117
- agent_id="recommendation_engine",
118
- name="Product Recommendation Engine",
119
- optimization_strategy="token_efficient", # or "balanced" / "quality_first"
120
- max_tokens=1000,
121
- )
122
-
123
- result = agent.query("What are the top 10 customers by lifetime value?")
124
-
125
- print(f"Baseline tokens: {result.baseline_tokens}")
126
- print(f"Optimized tokens: {result.optimized_tokens}")
127
- print(f"Reduction: {result.cost_reduction_percent:.1f}%")
128
- ```
129
-
130
- Token counts are estimated from the actual query text (a standard
131
- ~4-characters-per-token heuristic) and the reduction percentage is
132
- computed from the selected `optimization_strategy` — not a fixed number
133
- returned regardless of input. See [`examples/basic_usage.py`](examples/basic_usage.py)
134
- for a full runnable walkthrough, including framework adapters for
135
- LangChain, LlamaIndex, CrewAI, Semantic Kernel, PydanticAI, and Haystack
136
- under [`pystreammcp.integrations`](python/pystreammcp/integrations/).
137
-
138
- ## Data source discovery
139
-
140
- `SourceRegistry` (`pystreammcp.SourceRegistry`) ranks *registered* data
141
- sources by real word-overlap relevance to a query — not a fabricated or
142
- hardcoded result:
143
-
144
- ```python
145
- from pystreammcp import SourceRegistry
146
-
147
- registry = SourceRegistry()
148
- registry.register(
149
- "orders_db",
150
- description="customer order history and revenue by quarter",
151
- type="database",
152
- tags=["orders", "revenue"],
153
- )
154
-
155
- results = registry.discover("customer revenue by quarter")
156
- # [{"name": "orders_db", "type": "database", "relevance": 0.4444, "matched_terms": [...]}]
157
- ```
158
-
159
- The same registry backs `POST /discover` (REST), the `pystreammcp_discover`
160
- MCP tool, and `LangchainAdapter.discover()` — register sources once via
161
- `POST /sources` / `pystreammcp_register_source` / `registry.register()`,
162
- then discover against them from any of the three surfaces. This is a
163
- simple Jaccard token-overlap heuristic, not semantic/embedding search.
164
-
165
- ## MCP orchestration & federation
166
-
167
- `Orchestrator` (`pystreammcp.Orchestrator`) discovers and routes across
168
- other MCP-enabled projects you configure in `pystreammcp.toml`:
169
-
170
- ```toml
171
- [federation]
172
- endpoints = [
173
- "http://localhost:8765/mcp",
174
- "http://localhost:8766/mcp",
175
- ]
176
- ```
177
-
178
- ```python
179
- from pystreammcp import Orchestrator
180
-
181
- orch = Orchestrator() # reads ./pystreammcp.toml
182
- result = orch.discover_mcp_projects()
183
- # {"projects": [{"project_name": ..., "endpoint": ..., "status": "healthy"|"unavailable", ...}], "total": N}
184
- ```
185
-
186
- This actually probes each configured endpoint's real `tools/list` MCP
187
- method — with nothing configured, or nothing reachable, it honestly
188
- reports zero/unavailable projects rather than a fixed fixture list.
189
- `Orchestrator` also does lexical tool-relevance ranking
190
- (`rank_tools_by_relevance`) and capability matching
191
- (`detect_compatible_projects`) against whatever it actually discovers.
192
-
193
- To expose these (plus tool-routing and webhook-event handling) as MCP
194
- tools, `PyStreamMCPHandler` (`pystreammcp._mcp_tools`) wires the MCP
195
- tool-call surface directly to a live `Orchestrator`/`EventRouter`
196
- instance — every handler call is backed by real, inspectable state, not
197
- a hardcoded response.
198
-
199
- **Honest limitations**: a few orchestration capabilities are accepted at
200
- the API level but not fully implemented yet — cross-project federated
201
- query *execution* and cross-database joins currently report
202
- `"status": "not_implemented"` with an explanation, rather than fabricated
203
- result rows, because there's no real query engine behind them yet.
204
-
205
- ## Webhook event server
206
-
207
- `pystreammcp.server.create_flask_app()` exposes a small REST/webhook
208
- surface for orchestration events (agent MCPs reporting `mcp.available`,
209
- `tool.invoked`, health updates, etc. to `POST /orchestration/webhooks/events`).
210
-
211
- ### Webhook security (HMAC-SHA256)
212
-
213
- Inbound events to `/orchestration/webhooks/events` must be signed:
214
-
215
- ```bash
216
- export PYSTREAMMCP_WEBHOOK_SECRET="a long random shared secret"
217
- ```
218
-
219
- Senders compute `HMAC-SHA256(secret, raw_request_body)` and send it as:
220
-
221
- ```
222
- X-PyStreamMCP-Signature: sha256=<hex digest>
223
- ```
224
-
225
- ```python
226
- from pystreammcp.server import compute_webhook_signature
227
- import json, requests
228
-
229
- body = json.dumps({"event_type": "mcp.available", "data": {...}}).encode()
230
- signature = compute_webhook_signature(secret, body)
231
-
232
- requests.post(
233
- "http://localhost:8000/orchestration/webhooks/events",
234
- data=body,
235
- headers={
236
- "Content-Type": "application/json",
237
- "X-PyStreamMCP-Signature": signature,
238
- },
239
- )
240
- ```
241
-
242
- Requests with a missing or invalid signature are rejected with `401`. If
243
- `PYSTREAMMCP_WEBHOOK_SECRET` isn't set at all, the endpoint fails closed
244
- and rejects every request with `503` — there is no "accept unsigned
245
- events" fallback.
246
-
247
- ### Network defaults
248
-
249
- The HTTP servers (`PyStreamMCPServer` / `create_flask_app()`,
250
- `PyStreamMCPAPI.run`) bind to `127.0.0.1` (localhost-only) by default.
251
- Pass `host="0.0.0.0"` explicitly if you need the server reachable from
252
- other hosts — e.g. inside a container that already has its own network
253
- boundary.
254
-
255
- **The `pystreammcp` CLI**: `pip install PyStreamMCP` now installs a real
256
- `pystreammcp` command (`[project.scripts]` in `pyproject.toml`) backed by
257
- the Click group in `python/pystreammcp/cli.py`:
258
- `query`/`server`/`version`/`dashboard`. `python -m pystreammcp.cli` reaches
259
- the same Click group (the file's old, separate, smaller hand-rolled
260
- command set has been removed). `server` lazy-imports `pystreammcp.api`, so
261
- `pystreammcp version`/`query`/`dashboard` work without installing the
262
- `api` extra; running `pystreammcp server` without it prints a clear
263
- "install `pystreammcp[api]`" error instead of crashing with
264
- `ModuleNotFoundError`. See [`ROADMAP_HONEST.md`](ROADMAP_HONEST.md) §2.2
265
- (now marked fixed) for how this was previously broken.
266
-
267
- ## Testing
268
-
269
- ```bash
270
- pip install -e ".[dev,api,mcp,langchain,llamaindex,semantic-kernel]"
271
- pytest tests/ -v
272
- ```
273
-
274
- Note: this exact command needs Python 3.10+ (see [Install](#install)); on
275
- 3.9 the `mcp` and `semantic-kernel` extras fail to resolve, so drop them
276
- from the extras list if you're on 3.9.
277
-
278
- ## Known Issues
279
-
280
- **2026-10-11 pass — workspace-wide error sweep; the Rust backend now
281
- compiles and its tests pass, Python `ruff` is clean, and a stale
282
- integration test was rewritten against the real schema:**
283
-
284
- - **The `core/` Rust workspace compiles again.** It had accumulated 43
285
- `cargo check` errors: `orchestration/` (reranking/orchestrator referenced
286
- a stale `stage2_decision` field, `RankingStrategy`/`FusionMethod` were
287
- missing `Serialize`/`Deserialize`, `ContentSegment` was missing
288
- `PartialEq`, `TokenBudget` was re-exported from the wrong module);
289
- `metadata/` (cache eviction borrow-checker error, missing
290
- `CacheStats.evictions`, `clear_cache`/`cache_stats` were not `async`);
291
- `selective_retrieval/` (`QueryIntent` missing `PartialEq`,
292
- struct-variant `BudgetTier` match patterns, a duplicated
293
- `RerankerConfig`); `quality_gates/` (duplicated `ConfidenceConfig`/
294
- `SLACheckResult`); and `observability/` (per-subsystem `*Config`
295
- constructors resolved to a non-existent path). All fixed; `cargo test`
296
- is green — **138 library + 29 integration tests, 0 failures**.
297
- - Build note: `rust-toolchain.toml` still pins `channel = "1.81"`, which
298
- cannot even parse the committed lockfile — transitive deps such as
299
- `idna_adapter 1.2.2` require `edition2024`, i.e. Cargo ≥ 1.85. The pin
300
- was intentionally **left unchanged**; build with
301
- `RUSTUP_TOOLCHAIN=1.97 cargo ...` until the pin is revisited.
302
- - **5 Python `F821` undefined-name errors fixed** in
303
- `python/pystreammcp/integrations/{langchain,llamaindex,semantic_kernel}.py`
304
- — the retriever/tool/plugin classes were referenced in annotations but
305
- never imported. Added `TYPE_CHECKING`-guarded imports so the optional
306
- dependency types are visible to type-checkers without forcing a hard
307
- runtime import. `ruff check python/ --select E9,F63,F7,F82` is clean.
308
- - **`core/tests/phase_v05_metadata_tests.rs` was dead code** — it had never
309
- compiled (it referenced an aspirational `MetadataEntry`/profile schema
310
- that exists nowhere in the tree) and so was never run. Rewritten against
311
- the real metadata API (`DatabaseMetadata`/`MCPToolMetadata`,
312
- `Metadata::Web/Database/Mcp`, profile keys `public_postgres`,
313
- `bigquery_analytics`, `ml_inference`, …); all 29 tests pass.
314
- - **3 pre-existing heuristic test failures fixed.**
315
- `orchestration/complexity.rs`: a "very complex" query scored *lower* than
316
- a merely "complex" one because reasoning terms (`analysis`, `correlation`,
317
- `anomalies`, `pattern`, …) weren't recognised — added an
318
- `analytical_terms()` signal and widened the `Complex` band.
319
- `selective_retrieval/intent.rs`: added an enumeration (comma-list) signal
320
- and raised the multi-entity weight so list-heavy and design-style queries
321
- classify correctly. No production behaviour change (these tiers only feed
322
- `budgets::assign_tier`, and the detectors are otherwise unused).
323
-
324
- - ~~No end-to-end tests for LLM failure states~~ **Fixed.** PyStreamMCP's
325
- own code never calls a model provider directly (no code path here to add
326
- `except RateLimitError`/`APIError` handling *to* — see `AgentFrameworkAdapter`
327
- below), so what's now tested is the part this repo actually owns: that the
328
- MCP tool-call path doesn't crash when whatever it's orchestrating fails.
329
- `pystreammcp.testing` exports `RateLimitError`, `ContextWindowExceededError`,
330
- `MalformedModelResponseError`, and `FailingAgent`/`FailingAdapter` stand-ins
331
- that raise them; `mcp_server.py`'s `call_tool`/`process_message` now catch
332
- exceptions raised during tool execution and return a structured
333
- `{"status": "error", "error_type": ..., "message": ...}` response instead
334
- of propagating uncaught. See `tests/test_mcp_server_resilience.py`.
335
- - ~~No Pydantic validation for the MCP tool protocol itself~~ **Fixed.**
336
- Each of the four MCP tools now has a Pydantic arg model
337
- (`QueryToolArgs`/`DiscoverToolArgs`/`RegisterSourceToolArgs`/`OptimizeToolArgs`
338
- in `mcp_server.py`) validated in `call_tool` before dispatch (wrong types,
339
- invalid enum values, and missing required fields all now return a
340
- structured `validation_errors` response instead of reaching the handler
341
- unchecked), and `MCPTool.input_schema` is generated from those same models
342
- via `model_json_schema()` — the advertised schema and the schema actually
343
- enforced can no longer drift apart the way a hand-duplicated dict could.
344
- - ~~No shipped mock MCP server / mock LLM fixture~~ **Fixed.** Added
345
- `tests/conftest.py` (centralizing the `sys.path` setup 5 test files were
346
- duplicating, plus `mock_adapter`/`failing_agent`/`failing_adapter`/
347
- `llm_failure` pytest fixtures) and `pystreammcp/testing.py`, exporting
348
- `MockAdapter` (promoted from the old test-local copy) and the
349
- failure-injection stand-ins above for downstream projects' own test
350
- suites to import directly.
351
- - **Per-query context discovery** now has a real implementation
352
- (`pystreammcp.discovery.SourceRegistry`): register data sources (name,
353
- description, type, tags), and `/discover` (REST), `pystreammcp_discover`
354
- (MCP), and `LangchainAdapter.discover()` all rank *registered* sources
355
- by real Jaccard token-overlap against the query — an empty registry or
356
- a non-matching context returns an empty list, not fabricated data. This
357
- is a simple word-overlap heuristic, not semantic/embedding search — if
358
- you need that, wrap `SourceRegistry` with your own similarity scoring.
359
- - ~~The `pystreammcp` CLI is dead code~~ **Fixed.** `pyproject.toml` now
360
- has a `[project.scripts]` entry, and the module's `__main__` guard
361
- invokes the real Click group. See [Network defaults](#network-defaults)
362
- above and [`ROADMAP_HONEST.md`](ROADMAP_HONEST.md) §2.2.
363
- - ~~Three orchestration-tool discovery adapters return fabricated
364
- results~~ **Fixed.** `orchestration/temporal.py`'s
365
- `TemporalDiscoveryActivity`/`TemporalWorkflow`, `airflow.py`'s
366
- `PyStreamMCPDiscoveryOperator`, and `nocode_rpa.py`'s
367
- `N8nWebhookTrigger` now take an optional shared `SourceRegistry` and
368
- call its real `discover()` instead of synthesizing `source_0..
369
- source_4`. With nothing registered they honestly return zero sources
370
- (see [`ROADMAP_HONEST.md`](ROADMAP_HONEST.md) §2.1).
371
- - `pip-audit` (run 2026-09-20 against the `dev,api,mcp,langchain,
372
- llamaindex,semantic-kernel` extras) found 7 known CVEs in transitive
373
- dependencies (`werkzeug` via `flask`, `nltk` via `llama-index`) — see
374
- [`ROADMAP_HONEST.md`](ROADMAP_HONEST.md) §4. Not fixed this pass.
375
- - No open GitHub issues as of this pass.
376
-
377
- ## Documentation
378
-
379
- - [`ROADMAP_HONEST.md`](ROADMAP_HONEST.md) — full, unhedged status:
380
- what's tested, what's fabricated/broken, technical debt, what's
381
- explicitly not built.
382
- - [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — how the shipped
383
- package is structured, with a diagram.
384
- - [`OKF_INTEGRATION.md`](OKF_INTEGRATION.md) — the OKF (Open Knowledge
385
- Format) catalog/discovery/query-planner feature (real, tested, not
386
- mentioned elsewhere in this README).
387
- - [`CONTRIBUTING.md`](CONTRIBUTING.md), [`SECURITY.md`](SECURITY.md),
388
- [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md),
389
- [`CHANGELOG.md`](CHANGELOG.md).
390
-
391
- ## Rust workspace (not shipped)
392
-
393
- `core/` and `python/src/lib.rs` are an in-progress Rust workspace intended
394
- as a future performance backend. **It is not part of the published PyPI
395
- package** — the wheel built from `pyproject.toml` is pure Python. It now
396
- compiles and its unit/integration tests pass (see the 2026-10-11 entry in
397
- [Known Issues](#known-issues)), but note the `1.81` toolchain pin: build it
398
- with `RUSTUP_TOOLCHAIN=1.97 cargo ...`. Don't rely on it as a shipped
399
- artifact; it's tracked separately from the Python package described in this
400
- README.
401
-
402
- ## License
403
-
404
- This project is licensed under the [Apache License 2.0](LICENSE).