PyStreamMCP 2.1.0__tar.gz → 3.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. pystreammcp-3.3.0/LICENSE +22 -0
  2. pystreammcp-3.3.0/PKG-INFO +305 -0
  3. pystreammcp-3.3.0/README.md +241 -0
  4. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/pyproject.toml +11 -3
  5. pystreammcp-3.3.0/python/PyStreamMCP.egg-info/PKG-INFO +305 -0
  6. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/PyStreamMCP.egg-info/SOURCES.txt +6 -0
  7. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/PyStreamMCP.egg-info/requires.txt +5 -0
  8. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/__init__.py +13 -3
  9. pystreammcp-3.3.0/python/pystreammcp/_mcp_connector.py +796 -0
  10. pystreammcp-3.3.0/python/pystreammcp/_mcp_tools.py +766 -0
  11. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/adapters.py +18 -5
  12. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/agent.py +63 -17
  13. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/api.py +83 -17
  14. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/cli.py +46 -14
  15. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/cli_daemon.py +17 -11
  16. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/cli_dashboard.py +28 -9
  17. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/context.py +5 -0
  18. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/decomposition.py +13 -3
  19. pystreammcp-3.3.0/python/pystreammcp/discovery.py +216 -0
  20. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/integrations/crewai.py +18 -6
  21. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/integrations/haystack.py +25 -8
  22. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/integrations/langchain.py +75 -34
  23. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/integrations/langsmith.py +17 -7
  24. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/integrations/llamaindex.py +117 -23
  25. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/integrations/pydantic_ai.py +18 -6
  26. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/integrations/semantic_kernel.py +25 -8
  27. pystreammcp-3.3.0/python/pystreammcp/mcp_server.py +330 -0
  28. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/ml/relevance.py +29 -12
  29. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/ml/tagging.py +155 -53
  30. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/ml.py +11 -3
  31. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/multi_agent.py +1 -2
  32. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/observability/metrics.py +41 -21
  33. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/okf_core.py +32 -17
  34. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/okf_discovery.py +70 -40
  35. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/okf_query_planner.py +29 -13
  36. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/optimization/advanced.py +34 -23
  37. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/optimization/decomposition.py +6 -1
  38. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/optimization.py +8 -1
  39. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/orchestration/airflow.py +7 -5
  40. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/orchestration/nocode_rpa.py +14 -7
  41. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/qa/validator.py +56 -33
  42. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/quality.py +72 -43
  43. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/query.py +44 -11
  44. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/server.py +129 -29
  45. pystreammcp-3.3.0/python/pystreammcp/testing.py +123 -0
  46. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/webhook_handlers.py +44 -24
  47. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/webhook_router.py +43 -19
  48. pystreammcp-3.3.0/setup.py +18 -0
  49. pystreammcp-3.3.0/tests/test_discovery.py +118 -0
  50. pystreammcp-3.3.0/tests/test_mcp_connector_discovery.py +283 -0
  51. pystreammcp-3.3.0/tests/test_mcp_server_resilience.py +172 -0
  52. pystreammcp-3.3.0/tests/test_mcp_tools_handler.py +184 -0
  53. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_sprint1_foundation.py +21 -2
  54. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_sprint2_langchain.py +12 -1
  55. pystreammcp-3.3.0/tests/test_webhook_hmac_auth.py +168 -0
  56. pystreammcp-2.1.0/LICENSE +0 -21
  57. pystreammcp-2.1.0/PKG-INFO +0 -130
  58. pystreammcp-2.1.0/README.md +0 -64
  59. pystreammcp-2.1.0/python/PyStreamMCP.egg-info/PKG-INFO +0 -130
  60. pystreammcp-2.1.0/python/pystreammcp/_mcp_connector.py +0 -142
  61. pystreammcp-2.1.0/python/pystreammcp/_mcp_tools.py +0 -727
  62. pystreammcp-2.1.0/python/pystreammcp/discovery.py +0 -79
  63. pystreammcp-2.1.0/python/pystreammcp/mcp_server.py +0 -276
  64. pystreammcp-2.1.0/setup.py +0 -28
  65. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/MANIFEST.in +0 -0
  66. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/PyStreamMCP.egg-info/dependency_links.txt +0 -0
  67. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/PyStreamMCP.egg-info/top_level.txt +0 -0
  68. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/integrations/__init__.py +0 -0
  69. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/ml/__init__.py +0 -0
  70. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/observability/__init__.py +0 -0
  71. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/optimization/__init__.py +0 -0
  72. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/orchestration/__init__.py +0 -0
  73. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/orchestration/temporal.py +0 -0
  74. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/qa/__init__.py +0 -0
  75. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/scripts/__init__.py +0 -0
  76. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/scripts/post_install.py +0 -0
  77. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/python/pystreammcp/streaming.py +0 -0
  78. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/setup.cfg +0 -0
  79. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_integration_phase2.py +0 -0
  80. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_langchain_integration.py +0 -0
  81. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_llamaindex_integration.py +0 -0
  82. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_okf_core.py +0 -0
  83. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_okf_discovery.py +0 -0
  84. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_okf_query_planner.py +0 -0
  85. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_phase3_advanced.py +0 -0
  86. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_phase3_items12.py +0 -0
  87. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_phase3_items345.py +0 -0
  88. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_sprint3_rag_frameworks.py +0 -0
  89. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_sprint4_agent_frameworks.py +0 -0
  90. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_sprint5_orchestration.py +0 -0
  91. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_statguardian_integration.py +0 -0
  92. {pystreammcp-2.1.0 → pystreammcp-3.3.0}/tests/test_webhook_router.py +0 -0
@@ -0,0 +1,22 @@
1
+ Proprietary License — Free to use with explicit attribution
2
+
3
+ Copyright © 2026 Georgi Mammen Mullassery
4
+
5
+ Permission is granted to any person obtaining a copy of this software to use,
6
+ copy, modify, and distribute this software and its documentation, provided that:
7
+
8
+ 1. The above copyright notice and this permission notice appear in all copies
9
+ or substantial portions of the software.
10
+
11
+ 2. Explicit attribution to the original author (Georgi Mammen Mullassery) is
12
+ provided in any project documentation and significant code comments where
13
+ this software is used.
14
+
15
+ 3. The software is provided "AS IS" without warranty of any kind, express or
16
+ implied, including but not limited to the warranties of merchantability,
17
+ fitness for a particular purpose, or non-infringement.
18
+
19
+ 4. In no event shall the author be liable for any claim, damages, or other
20
+ liability arising from the use of this software.
21
+
22
+ For more information, visit: https://github.com/Mullassery
@@ -0,0 +1,305 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyStreamMCP
3
+ Version: 3.3.0
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: Proprietary License — Free to use with explicit attribution
7
+ Project-URL: Repository, https://github.com/Mullassery/PyStreamMCP
8
+ Project-URL: Issues, https://github.com/Mullassery/PyStreamMCP/issues
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: Other/Proprietary License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: pydantic>=2.0
22
+ Requires-Dist: python-frontmatter>=1.0.0
23
+ Requires-Dist: click>=8.0
24
+ Requires-Dist: rich>=13.0
25
+ Requires-Dist: httpx>=0.24.0
26
+ Requires-Dist: toml>=0.10; python_version < "3.11"
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=7.0; extra == "dev"
29
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
30
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
31
+ Provides-Extra: api
32
+ Requires-Dist: fastapi>=0.104.0; extra == "api"
33
+ Requires-Dist: uvicorn[standard]>=0.24.0; extra == "api"
34
+ Requires-Dist: pydantic-settings>=2.0; extra == "api"
35
+ Requires-Dist: flask>=2.3.0; extra == "api"
36
+ Provides-Extra: mcp
37
+ Requires-Dist: mcp>=0.1.0; extra == "mcp"
38
+ Provides-Extra: langchain
39
+ Requires-Dist: langchain>=0.1.0; extra == "langchain"
40
+ Provides-Extra: llamaindex
41
+ Requires-Dist: llama-index>=0.9.0; extra == "llamaindex"
42
+ Provides-Extra: semantic-kernel
43
+ Requires-Dist: semantic-kernel>=0.4.0; extra == "semantic-kernel"
44
+ Provides-Extra: crewai
45
+ Requires-Dist: crewai>=0.1.0; extra == "crewai"
46
+ Provides-Extra: pydantic-ai
47
+ Requires-Dist: pydantic-ai>=0.1.0; extra == "pydantic-ai"
48
+ Provides-Extra: haystack
49
+ Requires-Dist: haystack-ai>=2.0.0; extra == "haystack"
50
+ Provides-Extra: temporal
51
+ Requires-Dist: temporalio>=1.0.0; extra == "temporal"
52
+ Provides-Extra: airflow
53
+ Requires-Dist: apache-airflow>=2.7.0; extra == "airflow"
54
+ Provides-Extra: all-integrations
55
+ Requires-Dist: langchain>=0.1.0; extra == "all-integrations"
56
+ Requires-Dist: llama-index>=0.9.0; extra == "all-integrations"
57
+ Requires-Dist: semantic-kernel>=0.4.0; extra == "all-integrations"
58
+ Requires-Dist: crewai>=0.1.0; extra == "all-integrations"
59
+ Requires-Dist: pydantic-ai>=0.1.0; extra == "all-integrations"
60
+ Requires-Dist: haystack-ai>=2.0.0; extra == "all-integrations"
61
+ Requires-Dist: temporalio>=1.0.0; extra == "all-integrations"
62
+ Requires-Dist: apache-airflow>=2.7.0; extra == "all-integrations"
63
+ Dynamic: license-file
64
+
65
+ # PyStreamMCP
66
+
67
+ [![CI](https://github.com/Mullassery/PyStreamMCP/actions/workflows/ci.yml/badge.svg)](https://github.com/Mullassery/PyStreamMCP/actions/workflows/ci.yml)
68
+
69
+ An AI-agent intelligence layer: query planning, context discovery, and
70
+ token-cost optimization, exposed both as a Python SDK and as an MCP
71
+ (Model Context Protocol) tool server, plus a small event-driven
72
+ orchestration/federation layer for coordinating multiple MCP endpoints.
73
+
74
+ Pure Python — `pip install` just works, no Rust toolchain or compiler
75
+ required.
76
+
77
+ ## Install
78
+
79
+ ```bash
80
+ pip install PyStreamMCP
81
+ ```
82
+
83
+ Optional extras, install what you actually need:
84
+
85
+ ```bash
86
+ pip install "PyStreamMCP[api]" # FastAPI + Flask HTTP servers
87
+ pip install "PyStreamMCP[mcp]" # MCP protocol client library
88
+ pip install "PyStreamMCP[langchain]" # LangChain adapter
89
+ pip install "PyStreamMCP[llamaindex]" # LlamaIndex adapter
90
+ pip install "PyStreamMCP[semantic-kernel]" # Semantic Kernel adapter
91
+ pip install "PyStreamMCP[all-integrations]" # every framework adapter
92
+ ```
93
+
94
+ Requires Python 3.9+ for the core SDK (`Agent`, `SourceRegistry`,
95
+ `Orchestrator`). The `mcp` and `semantic-kernel` extras require Python
96
+ 3.10+ — their upstream packages don't publish Python 3.9-compatible
97
+ releases, so `pip install` for those extras will fail on 3.9. CI tests
98
+ 3.10, 3.11, and 3.12 only; 3.9 is not covered by CI.
99
+
100
+ ## Quick start
101
+
102
+ ```python
103
+ from pystreammcp import Agent
104
+
105
+ agent = Agent(
106
+ agent_id="recommendation_engine",
107
+ name="Product Recommendation Engine",
108
+ optimization_strategy="token_efficient", # or "balanced" / "quality_first"
109
+ max_tokens=1000,
110
+ )
111
+
112
+ result = agent.query("What are the top 10 customers by lifetime value?")
113
+
114
+ print(f"Baseline tokens: {result.baseline_tokens}")
115
+ print(f"Optimized tokens: {result.optimized_tokens}")
116
+ print(f"Reduction: {result.cost_reduction_percent:.1f}%")
117
+ ```
118
+
119
+ Token counts are estimated from the actual query text (a standard
120
+ ~4-characters-per-token heuristic) and the reduction percentage is
121
+ computed from the selected `optimization_strategy` — not a fixed number
122
+ returned regardless of input. See [`examples/basic_usage.py`](examples/basic_usage.py)
123
+ for a full runnable walkthrough, including framework adapters for
124
+ LangChain, LlamaIndex, CrewAI, Semantic Kernel, PydanticAI, and Haystack
125
+ under [`pystreammcp.integrations`](python/pystreammcp/integrations/).
126
+
127
+ ## Data source discovery
128
+
129
+ `SourceRegistry` (`pystreammcp.SourceRegistry`) ranks *registered* data
130
+ sources by real word-overlap relevance to a query — not a fabricated or
131
+ hardcoded result:
132
+
133
+ ```python
134
+ from pystreammcp import SourceRegistry
135
+
136
+ registry = SourceRegistry()
137
+ registry.register(
138
+ "orders_db",
139
+ description="customer order history and revenue by quarter",
140
+ type="database",
141
+ tags=["orders", "revenue"],
142
+ )
143
+
144
+ results = registry.discover("customer revenue by quarter")
145
+ # [{"name": "orders_db", "type": "database", "relevance": 0.4444, "matched_terms": [...]}]
146
+ ```
147
+
148
+ The same registry backs `POST /discover` (REST), the `pystreammcp_discover`
149
+ MCP tool, and `LangchainAdapter.discover()` — register sources once via
150
+ `POST /sources` / `pystreammcp_register_source` / `registry.register()`,
151
+ then discover against them from any of the three surfaces. This is a
152
+ simple Jaccard token-overlap heuristic, not semantic/embedding search.
153
+
154
+ ## MCP orchestration & federation
155
+
156
+ `Orchestrator` (`pystreammcp.Orchestrator`) discovers and routes across
157
+ other MCP-enabled projects you configure in `pystreammcp.toml`:
158
+
159
+ ```toml
160
+ [federation]
161
+ endpoints = [
162
+ "http://localhost:8765/mcp",
163
+ "http://localhost:8766/mcp",
164
+ ]
165
+ ```
166
+
167
+ ```python
168
+ from pystreammcp import Orchestrator
169
+
170
+ orch = Orchestrator() # reads ./pystreammcp.toml
171
+ result = orch.discover_mcp_projects()
172
+ # {"projects": [{"project_name": ..., "endpoint": ..., "status": "healthy"|"unavailable", ...}], "total": N}
173
+ ```
174
+
175
+ This actually probes each configured endpoint's real `tools/list` MCP
176
+ method — with nothing configured, or nothing reachable, it honestly
177
+ reports zero/unavailable projects rather than a fixed fixture list.
178
+ `Orchestrator` also does lexical tool-relevance ranking
179
+ (`rank_tools_by_relevance`) and capability matching
180
+ (`detect_compatible_projects`) against whatever it actually discovers.
181
+
182
+ To expose these (plus tool-routing and webhook-event handling) as MCP
183
+ tools, `PyStreamMCPHandler` (`pystreammcp._mcp_tools`) wires the MCP
184
+ tool-call surface directly to a live `Orchestrator`/`EventRouter`
185
+ instance — every handler call is backed by real, inspectable state, not
186
+ a hardcoded response.
187
+
188
+ **Honest limitations**: a few orchestration capabilities are accepted at
189
+ the API level but not fully implemented yet — cross-project federated
190
+ query *execution* and cross-database joins currently report
191
+ `"status": "not_implemented"` with an explanation, rather than fabricated
192
+ result rows, because there's no real query engine behind them yet.
193
+
194
+ ## Webhook event server
195
+
196
+ `pystreammcp.server.create_flask_app()` exposes a small REST/webhook
197
+ surface for orchestration events (agent MCPs reporting `mcp.available`,
198
+ `tool.invoked`, health updates, etc. to `POST /orchestration/webhooks/events`).
199
+
200
+ ### Webhook security (HMAC-SHA256)
201
+
202
+ Inbound events to `/orchestration/webhooks/events` must be signed:
203
+
204
+ ```bash
205
+ export PYSTREAMMCP_WEBHOOK_SECRET="a long random shared secret"
206
+ ```
207
+
208
+ Senders compute `HMAC-SHA256(secret, raw_request_body)` and send it as:
209
+
210
+ ```
211
+ X-PyStreamMCP-Signature: sha256=<hex digest>
212
+ ```
213
+
214
+ ```python
215
+ from pystreammcp.server import compute_webhook_signature
216
+ import json, requests
217
+
218
+ body = json.dumps({"event_type": "mcp.available", "data": {...}}).encode()
219
+ signature = compute_webhook_signature(secret, body)
220
+
221
+ requests.post(
222
+ "http://localhost:8000/orchestration/webhooks/events",
223
+ data=body,
224
+ headers={
225
+ "Content-Type": "application/json",
226
+ "X-PyStreamMCP-Signature": signature,
227
+ },
228
+ )
229
+ ```
230
+
231
+ Requests with a missing or invalid signature are rejected with `401`. If
232
+ `PYSTREAMMCP_WEBHOOK_SECRET` isn't set at all, the endpoint fails closed
233
+ and rejects every request with `503` — there is no "accept unsigned
234
+ events" fallback.
235
+
236
+ ### Network defaults
237
+
238
+ The HTTP/CLI servers (`pystreammcp server`, `PyStreamMCPServer`,
239
+ `PyStreamMCPAPI.run`) bind to `127.0.0.1` (localhost-only) by default.
240
+ Pass `--host 0.0.0.0` (CLI) or `host="0.0.0.0"` explicitly if you need the
241
+ server reachable from other hosts — e.g. inside a container that already
242
+ has its own network boundary.
243
+
244
+ ## Testing
245
+
246
+ ```bash
247
+ pip install -e ".[dev,api,mcp,langchain,llamaindex,semantic-kernel]"
248
+ pytest tests/ -v
249
+ ```
250
+
251
+ Note: this exact command needs Python 3.10+ (see [Install](#install)); on
252
+ 3.9 the `mcp` and `semantic-kernel` extras fail to resolve, so drop them
253
+ from the extras list if you're on 3.9.
254
+
255
+ ## Known Issues
256
+
257
+ - ~~No end-to-end tests for LLM failure states~~ **Fixed.** PyStreamMCP's
258
+ own code never calls a model provider directly (no code path here to add
259
+ `except RateLimitError`/`APIError` handling *to* — see `AgentFrameworkAdapter`
260
+ below), so what's now tested is the part this repo actually owns: that the
261
+ MCP tool-call path doesn't crash when whatever it's orchestrating fails.
262
+ `pystreammcp.testing` exports `RateLimitError`, `ContextWindowExceededError`,
263
+ `MalformedModelResponseError`, and `FailingAgent`/`FailingAdapter` stand-ins
264
+ that raise them; `mcp_server.py`'s `call_tool`/`process_message` now catch
265
+ exceptions raised during tool execution and return a structured
266
+ `{"status": "error", "error_type": ..., "message": ...}` response instead
267
+ of propagating uncaught. See `tests/test_mcp_server_resilience.py`.
268
+ - ~~No Pydantic validation for the MCP tool protocol itself~~ **Fixed.**
269
+ Each of the four MCP tools now has a Pydantic arg model
270
+ (`QueryToolArgs`/`DiscoverToolArgs`/`RegisterSourceToolArgs`/`OptimizeToolArgs`
271
+ in `mcp_server.py`) validated in `call_tool` before dispatch (wrong types,
272
+ invalid enum values, and missing required fields all now return a
273
+ structured `validation_errors` response instead of reaching the handler
274
+ unchecked), and `MCPTool.input_schema` is generated from those same models
275
+ via `model_json_schema()` — the advertised schema and the schema actually
276
+ enforced can no longer drift apart the way a hand-duplicated dict could.
277
+ - ~~No shipped mock MCP server / mock LLM fixture~~ **Fixed.** Added
278
+ `tests/conftest.py` (centralizing the `sys.path` setup 5 test files were
279
+ duplicating, plus `mock_adapter`/`failing_agent`/`failing_adapter`/
280
+ `llm_failure` pytest fixtures) and `pystreammcp/testing.py`, exporting
281
+ `MockAdapter` (promoted from the old test-local copy) and the
282
+ failure-injection stand-ins above for downstream projects' own test
283
+ suites to import directly.
284
+ - **Per-query context discovery** now has a real implementation
285
+ (`pystreammcp.discovery.SourceRegistry`): register data sources (name,
286
+ description, type, tags), and `/discover` (REST), `pystreammcp_discover`
287
+ (MCP), and `LangchainAdapter.discover()` all rank *registered* sources
288
+ by real Jaccard token-overlap against the query — an empty registry or
289
+ a non-matching context returns an empty list, not fabricated data. This
290
+ is a simple word-overlap heuristic, not semantic/embedding search — if
291
+ you need that, wrap `SourceRegistry` with your own similarity scoring.
292
+ - No open GitHub issues as of this pass.
293
+
294
+ ## Rust workspace (not shipped)
295
+
296
+ `core/` and `python/src/lib.rs` are an in-progress Rust workspace intended
297
+ as a future performance backend. **It is not part of the published PyPI
298
+ package** — the wheel built from `pyproject.toml` is pure Python — and it
299
+ does not currently compile. Don't rely on it; it's tracked separately
300
+ from the Python package described in this README.
301
+
302
+ ## License
303
+
304
+ Proprietary License — Free to use with explicit attribution. See
305
+ [LICENSE](LICENSE).
@@ -0,0 +1,241 @@
1
+ # PyStreamMCP
2
+
3
+ [![CI](https://github.com/Mullassery/PyStreamMCP/actions/workflows/ci.yml/badge.svg)](https://github.com/Mullassery/PyStreamMCP/actions/workflows/ci.yml)
4
+
5
+ An AI-agent intelligence layer: query planning, context discovery, and
6
+ token-cost optimization, exposed both as a Python SDK and as an MCP
7
+ (Model Context Protocol) tool server, plus a small event-driven
8
+ orchestration/federation layer for coordinating multiple MCP endpoints.
9
+
10
+ Pure Python — `pip install` just works, no Rust toolchain or compiler
11
+ required.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ pip install PyStreamMCP
17
+ ```
18
+
19
+ Optional extras, install what you actually need:
20
+
21
+ ```bash
22
+ pip install "PyStreamMCP[api]" # FastAPI + Flask HTTP servers
23
+ pip install "PyStreamMCP[mcp]" # MCP protocol client library
24
+ pip install "PyStreamMCP[langchain]" # LangChain adapter
25
+ pip install "PyStreamMCP[llamaindex]" # LlamaIndex adapter
26
+ pip install "PyStreamMCP[semantic-kernel]" # Semantic Kernel adapter
27
+ pip install "PyStreamMCP[all-integrations]" # every framework adapter
28
+ ```
29
+
30
+ Requires Python 3.9+ for the core SDK (`Agent`, `SourceRegistry`,
31
+ `Orchestrator`). The `mcp` and `semantic-kernel` extras require Python
32
+ 3.10+ — their upstream packages don't publish Python 3.9-compatible
33
+ releases, so `pip install` for those extras will fail on 3.9. CI tests
34
+ 3.10, 3.11, and 3.12 only; 3.9 is not covered by CI.
35
+
36
+ ## Quick start
37
+
38
+ ```python
39
+ from pystreammcp import Agent
40
+
41
+ agent = Agent(
42
+ agent_id="recommendation_engine",
43
+ name="Product Recommendation Engine",
44
+ optimization_strategy="token_efficient", # or "balanced" / "quality_first"
45
+ max_tokens=1000,
46
+ )
47
+
48
+ result = agent.query("What are the top 10 customers by lifetime value?")
49
+
50
+ print(f"Baseline tokens: {result.baseline_tokens}")
51
+ print(f"Optimized tokens: {result.optimized_tokens}")
52
+ print(f"Reduction: {result.cost_reduction_percent:.1f}%")
53
+ ```
54
+
55
+ Token counts are estimated from the actual query text (a standard
56
+ ~4-characters-per-token heuristic) and the reduction percentage is
57
+ computed from the selected `optimization_strategy` — not a fixed number
58
+ returned regardless of input. See [`examples/basic_usage.py`](examples/basic_usage.py)
59
+ for a full runnable walkthrough, including framework adapters for
60
+ LangChain, LlamaIndex, CrewAI, Semantic Kernel, PydanticAI, and Haystack
61
+ under [`pystreammcp.integrations`](python/pystreammcp/integrations/).
62
+
63
+ ## Data source discovery
64
+
65
+ `SourceRegistry` (`pystreammcp.SourceRegistry`) ranks *registered* data
66
+ sources by real word-overlap relevance to a query — not a fabricated or
67
+ hardcoded result:
68
+
69
+ ```python
70
+ from pystreammcp import SourceRegistry
71
+
72
+ registry = SourceRegistry()
73
+ registry.register(
74
+ "orders_db",
75
+ description="customer order history and revenue by quarter",
76
+ type="database",
77
+ tags=["orders", "revenue"],
78
+ )
79
+
80
+ results = registry.discover("customer revenue by quarter")
81
+ # [{"name": "orders_db", "type": "database", "relevance": 0.4444, "matched_terms": [...]}]
82
+ ```
83
+
84
+ The same registry backs `POST /discover` (REST), the `pystreammcp_discover`
85
+ MCP tool, and `LangchainAdapter.discover()` — register sources once via
86
+ `POST /sources` / `pystreammcp_register_source` / `registry.register()`,
87
+ then discover against them from any of the three surfaces. This is a
88
+ simple Jaccard token-overlap heuristic, not semantic/embedding search.
89
+
90
+ ## MCP orchestration & federation
91
+
92
+ `Orchestrator` (`pystreammcp.Orchestrator`) discovers and routes across
93
+ other MCP-enabled projects you configure in `pystreammcp.toml`:
94
+
95
+ ```toml
96
+ [federation]
97
+ endpoints = [
98
+ "http://localhost:8765/mcp",
99
+ "http://localhost:8766/mcp",
100
+ ]
101
+ ```
102
+
103
+ ```python
104
+ from pystreammcp import Orchestrator
105
+
106
+ orch = Orchestrator() # reads ./pystreammcp.toml
107
+ result = orch.discover_mcp_projects()
108
+ # {"projects": [{"project_name": ..., "endpoint": ..., "status": "healthy"|"unavailable", ...}], "total": N}
109
+ ```
110
+
111
+ This actually probes each configured endpoint's real `tools/list` MCP
112
+ method — with nothing configured, or nothing reachable, it honestly
113
+ reports zero/unavailable projects rather than a fixed fixture list.
114
+ `Orchestrator` also does lexical tool-relevance ranking
115
+ (`rank_tools_by_relevance`) and capability matching
116
+ (`detect_compatible_projects`) against whatever it actually discovers.
117
+
118
+ To expose these (plus tool-routing and webhook-event handling) as MCP
119
+ tools, `PyStreamMCPHandler` (`pystreammcp._mcp_tools`) wires the MCP
120
+ tool-call surface directly to a live `Orchestrator`/`EventRouter`
121
+ instance — every handler call is backed by real, inspectable state, not
122
+ a hardcoded response.
123
+
124
+ **Honest limitations**: a few orchestration capabilities are accepted at
125
+ the API level but not fully implemented yet — cross-project federated
126
+ query *execution* and cross-database joins currently report
127
+ `"status": "not_implemented"` with an explanation, rather than fabricated
128
+ result rows, because there's no real query engine behind them yet.
129
+
130
+ ## Webhook event server
131
+
132
+ `pystreammcp.server.create_flask_app()` exposes a small REST/webhook
133
+ surface for orchestration events (agent MCPs reporting `mcp.available`,
134
+ `tool.invoked`, health updates, etc. to `POST /orchestration/webhooks/events`).
135
+
136
+ ### Webhook security (HMAC-SHA256)
137
+
138
+ Inbound events to `/orchestration/webhooks/events` must be signed:
139
+
140
+ ```bash
141
+ export PYSTREAMMCP_WEBHOOK_SECRET="a long random shared secret"
142
+ ```
143
+
144
+ Senders compute `HMAC-SHA256(secret, raw_request_body)` and send it as:
145
+
146
+ ```
147
+ X-PyStreamMCP-Signature: sha256=<hex digest>
148
+ ```
149
+
150
+ ```python
151
+ from pystreammcp.server import compute_webhook_signature
152
+ import json, requests
153
+
154
+ body = json.dumps({"event_type": "mcp.available", "data": {...}}).encode()
155
+ signature = compute_webhook_signature(secret, body)
156
+
157
+ requests.post(
158
+ "http://localhost:8000/orchestration/webhooks/events",
159
+ data=body,
160
+ headers={
161
+ "Content-Type": "application/json",
162
+ "X-PyStreamMCP-Signature": signature,
163
+ },
164
+ )
165
+ ```
166
+
167
+ Requests with a missing or invalid signature are rejected with `401`. If
168
+ `PYSTREAMMCP_WEBHOOK_SECRET` isn't set at all, the endpoint fails closed
169
+ and rejects every request with `503` — there is no "accept unsigned
170
+ events" fallback.
171
+
172
+ ### Network defaults
173
+
174
+ The HTTP/CLI servers (`pystreammcp server`, `PyStreamMCPServer`,
175
+ `PyStreamMCPAPI.run`) bind to `127.0.0.1` (localhost-only) by default.
176
+ Pass `--host 0.0.0.0` (CLI) or `host="0.0.0.0"` explicitly if you need the
177
+ server reachable from other hosts — e.g. inside a container that already
178
+ has its own network boundary.
179
+
180
+ ## Testing
181
+
182
+ ```bash
183
+ pip install -e ".[dev,api,mcp,langchain,llamaindex,semantic-kernel]"
184
+ pytest tests/ -v
185
+ ```
186
+
187
+ Note: this exact command needs Python 3.10+ (see [Install](#install)); on
188
+ 3.9 the `mcp` and `semantic-kernel` extras fail to resolve, so drop them
189
+ from the extras list if you're on 3.9.
190
+
191
+ ## Known Issues
192
+
193
+ - ~~No end-to-end tests for LLM failure states~~ **Fixed.** PyStreamMCP's
194
+ own code never calls a model provider directly (no code path here to add
195
+ `except RateLimitError`/`APIError` handling *to* — see `AgentFrameworkAdapter`
196
+ below), so what's now tested is the part this repo actually owns: that the
197
+ MCP tool-call path doesn't crash when whatever it's orchestrating fails.
198
+ `pystreammcp.testing` exports `RateLimitError`, `ContextWindowExceededError`,
199
+ `MalformedModelResponseError`, and `FailingAgent`/`FailingAdapter` stand-ins
200
+ that raise them; `mcp_server.py`'s `call_tool`/`process_message` now catch
201
+ exceptions raised during tool execution and return a structured
202
+ `{"status": "error", "error_type": ..., "message": ...}` response instead
203
+ of propagating uncaught. See `tests/test_mcp_server_resilience.py`.
204
+ - ~~No Pydantic validation for the MCP tool protocol itself~~ **Fixed.**
205
+ Each of the four MCP tools now has a Pydantic arg model
206
+ (`QueryToolArgs`/`DiscoverToolArgs`/`RegisterSourceToolArgs`/`OptimizeToolArgs`
207
+ in `mcp_server.py`) validated in `call_tool` before dispatch (wrong types,
208
+ invalid enum values, and missing required fields all now return a
209
+ structured `validation_errors` response instead of reaching the handler
210
+ unchecked), and `MCPTool.input_schema` is generated from those same models
211
+ via `model_json_schema()` — the advertised schema and the schema actually
212
+ enforced can no longer drift apart the way a hand-duplicated dict could.
213
+ - ~~No shipped mock MCP server / mock LLM fixture~~ **Fixed.** Added
214
+ `tests/conftest.py` (centralizing the `sys.path` setup 5 test files were
215
+ duplicating, plus `mock_adapter`/`failing_agent`/`failing_adapter`/
216
+ `llm_failure` pytest fixtures) and `pystreammcp/testing.py`, exporting
217
+ `MockAdapter` (promoted from the old test-local copy) and the
218
+ failure-injection stand-ins above for downstream projects' own test
219
+ suites to import directly.
220
+ - **Per-query context discovery** now has a real implementation
221
+ (`pystreammcp.discovery.SourceRegistry`): register data sources (name,
222
+ description, type, tags), and `/discover` (REST), `pystreammcp_discover`
223
+ (MCP), and `LangchainAdapter.discover()` all rank *registered* sources
224
+ by real Jaccard token-overlap against the query — an empty registry or
225
+ a non-matching context returns an empty list, not fabricated data. This
226
+ is a simple word-overlap heuristic, not semantic/embedding search — if
227
+ you need that, wrap `SourceRegistry` with your own similarity scoring.
228
+ - No open GitHub issues as of this pass.
229
+
230
+ ## Rust workspace (not shipped)
231
+
232
+ `core/` and `python/src/lib.rs` are an in-progress Rust workspace intended
233
+ as a future performance backend. **It is not part of the published PyPI
234
+ package** — the wheel built from `pyproject.toml` is pure Python — and it
235
+ does not currently compile. Don't rely on it; it's tracked separately
236
+ from the Python package described in this README.
237
+
238
+ ## License
239
+
240
+ Proprietary License — Free to use with explicit attribution. See
241
+ [LICENSE](LICENSE).
@@ -4,13 +4,20 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "PyStreamMCP"
7
- version = "2.1.0"
7
+ version = "3.3.0"
8
8
  description = "Intelligent MCP orchestration hub - Intent understanding, capability matching, tool ranking with cohesive foundation"
9
9
  readme = "README.md"
10
- license = { text = "Proprietary" }
10
+ license = {text = "Proprietary License — Free to use with explicit attribution"}
11
11
  authors = [{ name = "Georgi Mammen Mullassery", email = "mullassery@gmail.com" }]
12
12
  requires-python = ">=3.9"
13
- dependencies = ["pydantic>=2.0", "python-frontmatter>=1.0.0", "click>=8.0", "rich>=13.0"]
13
+ dependencies = [
14
+ "pydantic>=2.0",
15
+ "python-frontmatter>=1.0.0",
16
+ "click>=8.0",
17
+ "rich>=13.0",
18
+ "httpx>=0.24.0",
19
+ "toml>=0.10; python_version < \"3.11\"",
20
+ ]
14
21
  classifiers = [
15
22
  "Development Status :: 5 - Production/Stable",
16
23
  "Intended Audience :: Developers",
@@ -39,6 +46,7 @@ api = [
39
46
  "fastapi>=0.104.0",
40
47
  "uvicorn[standard]>=0.24.0",
41
48
  "pydantic-settings>=2.0",
49
+ "flask>=2.3.0",
42
50
  ]
43
51
 
44
52
  # MCP (Model Context Protocol)