PyStreamMCP 2.1.0__tar.gz → 3.2.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.
- pystreammcp-3.2.0/LICENSE +22 -0
- pystreammcp-3.2.0/PKG-INFO +231 -0
- pystreammcp-3.2.0/README.md +167 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/pyproject.toml +11 -3
- pystreammcp-3.2.0/python/PyStreamMCP.egg-info/PKG-INFO +231 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/PyStreamMCP.egg-info/SOURCES.txt +3 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/PyStreamMCP.egg-info/requires.txt +5 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/__init__.py +10 -2
- pystreammcp-3.2.0/python/pystreammcp/_mcp_connector.py +796 -0
- pystreammcp-3.2.0/python/pystreammcp/_mcp_tools.py +766 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/adapters.py +18 -5
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/agent.py +63 -17
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/api.py +36 -13
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/cli.py +46 -14
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/cli_daemon.py +17 -11
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/cli_dashboard.py +28 -9
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/context.py +5 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/decomposition.py +13 -3
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/discovery.py +6 -3
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/integrations/crewai.py +18 -6
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/integrations/haystack.py +25 -8
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/integrations/langchain.py +60 -23
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/integrations/langsmith.py +17 -7
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/integrations/llamaindex.py +117 -23
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/integrations/pydantic_ai.py +18 -6
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/integrations/semantic_kernel.py +25 -8
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/mcp_server.py +8 -1
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/ml/relevance.py +29 -12
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/ml/tagging.py +155 -53
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/ml.py +11 -3
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/multi_agent.py +1 -2
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/observability/metrics.py +41 -21
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/okf_core.py +32 -17
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/okf_discovery.py +70 -40
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/okf_query_planner.py +29 -13
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/optimization/advanced.py +34 -23
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/optimization/decomposition.py +6 -1
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/optimization.py +8 -1
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/orchestration/airflow.py +7 -5
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/orchestration/nocode_rpa.py +14 -7
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/qa/validator.py +56 -33
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/quality.py +72 -43
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/query.py +44 -11
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/server.py +129 -29
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/webhook_handlers.py +44 -24
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/webhook_router.py +43 -19
- pystreammcp-3.2.0/setup.py +18 -0
- pystreammcp-3.2.0/tests/test_mcp_connector_discovery.py +283 -0
- pystreammcp-3.2.0/tests/test_mcp_tools_handler.py +184 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/tests/test_sprint1_foundation.py +18 -0
- pystreammcp-3.2.0/tests/test_webhook_hmac_auth.py +168 -0
- pystreammcp-2.1.0/LICENSE +0 -21
- pystreammcp-2.1.0/PKG-INFO +0 -130
- pystreammcp-2.1.0/README.md +0 -64
- pystreammcp-2.1.0/python/PyStreamMCP.egg-info/PKG-INFO +0 -130
- pystreammcp-2.1.0/python/pystreammcp/_mcp_connector.py +0 -142
- pystreammcp-2.1.0/python/pystreammcp/_mcp_tools.py +0 -727
- pystreammcp-2.1.0/setup.py +0 -28
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/MANIFEST.in +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/PyStreamMCP.egg-info/dependency_links.txt +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/PyStreamMCP.egg-info/top_level.txt +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/integrations/__init__.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/ml/__init__.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/observability/__init__.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/optimization/__init__.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/orchestration/__init__.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/orchestration/temporal.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/qa/__init__.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/scripts/__init__.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/scripts/post_install.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/python/pystreammcp/streaming.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/setup.cfg +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/tests/test_integration_phase2.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/tests/test_langchain_integration.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/tests/test_llamaindex_integration.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/tests/test_okf_core.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/tests/test_okf_discovery.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/tests/test_okf_query_planner.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/tests/test_phase3_advanced.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/tests/test_phase3_items12.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/tests/test_phase3_items345.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/tests/test_sprint2_langchain.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/tests/test_sprint3_rag_frameworks.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/tests/test_sprint4_agent_frameworks.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/tests/test_sprint5_orchestration.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.0}/tests/test_statguardian_integration.py +0 -0
- {pystreammcp-2.1.0 → pystreammcp-3.2.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,231 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PyStreamMCP
|
|
3
|
+
Version: 3.2.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
|
+
[](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+.
|
|
95
|
+
|
|
96
|
+
## Quick start
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from pystreammcp import Agent
|
|
100
|
+
|
|
101
|
+
agent = Agent(
|
|
102
|
+
agent_id="recommendation_engine",
|
|
103
|
+
name="Product Recommendation Engine",
|
|
104
|
+
optimization_strategy="token_efficient", # or "balanced" / "quality_first"
|
|
105
|
+
max_tokens=1000,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
result = agent.query("What are the top 10 customers by lifetime value?")
|
|
109
|
+
|
|
110
|
+
print(f"Baseline tokens: {result.baseline_tokens}")
|
|
111
|
+
print(f"Optimized tokens: {result.optimized_tokens}")
|
|
112
|
+
print(f"Reduction: {result.cost_reduction_percent:.1f}%")
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Token counts are estimated from the actual query text (a standard
|
|
116
|
+
~4-characters-per-token heuristic) and the reduction percentage is
|
|
117
|
+
computed from the selected `optimization_strategy` — not a fixed number
|
|
118
|
+
returned regardless of input. See [`examples/basic_usage.py`](examples/basic_usage.py)
|
|
119
|
+
for a full runnable walkthrough, including framework adapters for
|
|
120
|
+
LangChain, LlamaIndex, CrewAI, Semantic Kernel, PydanticAI, and Haystack
|
|
121
|
+
under [`pystreammcp.integrations`](python/pystreammcp/integrations/).
|
|
122
|
+
|
|
123
|
+
## MCP orchestration & federation
|
|
124
|
+
|
|
125
|
+
`Orchestrator` (`pystreammcp.Orchestrator`) discovers and routes across
|
|
126
|
+
other MCP-enabled projects you configure in `pystreammcp.toml`:
|
|
127
|
+
|
|
128
|
+
```toml
|
|
129
|
+
[federation]
|
|
130
|
+
endpoints = [
|
|
131
|
+
"http://localhost:8765/mcp",
|
|
132
|
+
"http://localhost:8766/mcp",
|
|
133
|
+
]
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from pystreammcp import Orchestrator
|
|
138
|
+
|
|
139
|
+
orch = Orchestrator() # reads ./pystreammcp.toml
|
|
140
|
+
result = orch.discover_mcp_projects()
|
|
141
|
+
# {"projects": [{"project_name": ..., "endpoint": ..., "status": "healthy"|"unavailable", ...}], "total": N}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
This actually probes each configured endpoint's real `tools/list` MCP
|
|
145
|
+
method — with nothing configured, or nothing reachable, it honestly
|
|
146
|
+
reports zero/unavailable projects rather than a fixed fixture list.
|
|
147
|
+
`Orchestrator` also does lexical tool-relevance ranking
|
|
148
|
+
(`rank_tools_by_relevance`) and capability matching
|
|
149
|
+
(`detect_compatible_projects`) against whatever it actually discovers.
|
|
150
|
+
|
|
151
|
+
To expose these (plus tool-routing and webhook-event handling) as MCP
|
|
152
|
+
tools, `PyStreamMCPHandler` (`pystreammcp._mcp_tools`) wires the MCP
|
|
153
|
+
tool-call surface directly to a live `Orchestrator`/`EventRouter`
|
|
154
|
+
instance — every handler call is backed by real, inspectable state, not
|
|
155
|
+
a hardcoded response.
|
|
156
|
+
|
|
157
|
+
**Honest limitations**: a few orchestration capabilities are accepted at
|
|
158
|
+
the API level but not fully implemented yet — cross-project federated
|
|
159
|
+
query *execution* and cross-database joins currently report
|
|
160
|
+
`"status": "not_implemented"` with an explanation, rather than fabricated
|
|
161
|
+
result rows, because there's no real query engine behind them yet.
|
|
162
|
+
|
|
163
|
+
## Webhook event server
|
|
164
|
+
|
|
165
|
+
`pystreammcp.server.create_flask_app()` exposes a small REST/webhook
|
|
166
|
+
surface for orchestration events (agent MCPs reporting `mcp.available`,
|
|
167
|
+
`tool.invoked`, health updates, etc. to `POST /orchestration/webhooks/events`).
|
|
168
|
+
|
|
169
|
+
### Webhook security (HMAC-SHA256)
|
|
170
|
+
|
|
171
|
+
Inbound events to `/orchestration/webhooks/events` must be signed:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
export PYSTREAMMCP_WEBHOOK_SECRET="a long random shared secret"
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Senders compute `HMAC-SHA256(secret, raw_request_body)` and send it as:
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
X-PyStreamMCP-Signature: sha256=<hex digest>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from pystreammcp.server import compute_webhook_signature
|
|
185
|
+
import json, requests
|
|
186
|
+
|
|
187
|
+
body = json.dumps({"event_type": "mcp.available", "data": {...}}).encode()
|
|
188
|
+
signature = compute_webhook_signature(secret, body)
|
|
189
|
+
|
|
190
|
+
requests.post(
|
|
191
|
+
"http://localhost:8000/orchestration/webhooks/events",
|
|
192
|
+
data=body,
|
|
193
|
+
headers={
|
|
194
|
+
"Content-Type": "application/json",
|
|
195
|
+
"X-PyStreamMCP-Signature": signature,
|
|
196
|
+
},
|
|
197
|
+
)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Requests with a missing or invalid signature are rejected with `401`. If
|
|
201
|
+
`PYSTREAMMCP_WEBHOOK_SECRET` isn't set at all, the endpoint fails closed
|
|
202
|
+
and rejects every request with `503` — there is no "accept unsigned
|
|
203
|
+
events" fallback.
|
|
204
|
+
|
|
205
|
+
### Network defaults
|
|
206
|
+
|
|
207
|
+
The HTTP/CLI servers (`pystreammcp server`, `PyStreamMCPServer`,
|
|
208
|
+
`PyStreamMCPAPI.run`) bind to `127.0.0.1` (localhost-only) by default.
|
|
209
|
+
Pass `--host 0.0.0.0` (CLI) or `host="0.0.0.0"` explicitly if you need the
|
|
210
|
+
server reachable from other hosts — e.g. inside a container that already
|
|
211
|
+
has its own network boundary.
|
|
212
|
+
|
|
213
|
+
## Testing
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
pip install -e ".[dev,api,mcp,langchain,llamaindex,semantic-kernel]"
|
|
217
|
+
pytest tests/ -v
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Rust workspace (not shipped)
|
|
221
|
+
|
|
222
|
+
`core/` and `python/src/lib.rs` are an in-progress Rust workspace intended
|
|
223
|
+
as a future performance backend. **It is not part of the published PyPI
|
|
224
|
+
package** — the wheel built from `pyproject.toml` is pure Python — and it
|
|
225
|
+
does not currently compile. Don't rely on it; it's tracked separately
|
|
226
|
+
from the Python package described in this README.
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
Proprietary License — Free to use with explicit attribution. See
|
|
231
|
+
[LICENSE](LICENSE).
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# PyStreamMCP
|
|
2
|
+
|
|
3
|
+
[](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+.
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from pystreammcp import Agent
|
|
36
|
+
|
|
37
|
+
agent = Agent(
|
|
38
|
+
agent_id="recommendation_engine",
|
|
39
|
+
name="Product Recommendation Engine",
|
|
40
|
+
optimization_strategy="token_efficient", # or "balanced" / "quality_first"
|
|
41
|
+
max_tokens=1000,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
result = agent.query("What are the top 10 customers by lifetime value?")
|
|
45
|
+
|
|
46
|
+
print(f"Baseline tokens: {result.baseline_tokens}")
|
|
47
|
+
print(f"Optimized tokens: {result.optimized_tokens}")
|
|
48
|
+
print(f"Reduction: {result.cost_reduction_percent:.1f}%")
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Token counts are estimated from the actual query text (a standard
|
|
52
|
+
~4-characters-per-token heuristic) and the reduction percentage is
|
|
53
|
+
computed from the selected `optimization_strategy` — not a fixed number
|
|
54
|
+
returned regardless of input. See [`examples/basic_usage.py`](examples/basic_usage.py)
|
|
55
|
+
for a full runnable walkthrough, including framework adapters for
|
|
56
|
+
LangChain, LlamaIndex, CrewAI, Semantic Kernel, PydanticAI, and Haystack
|
|
57
|
+
under [`pystreammcp.integrations`](python/pystreammcp/integrations/).
|
|
58
|
+
|
|
59
|
+
## MCP orchestration & federation
|
|
60
|
+
|
|
61
|
+
`Orchestrator` (`pystreammcp.Orchestrator`) discovers and routes across
|
|
62
|
+
other MCP-enabled projects you configure in `pystreammcp.toml`:
|
|
63
|
+
|
|
64
|
+
```toml
|
|
65
|
+
[federation]
|
|
66
|
+
endpoints = [
|
|
67
|
+
"http://localhost:8765/mcp",
|
|
68
|
+
"http://localhost:8766/mcp",
|
|
69
|
+
]
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from pystreammcp import Orchestrator
|
|
74
|
+
|
|
75
|
+
orch = Orchestrator() # reads ./pystreammcp.toml
|
|
76
|
+
result = orch.discover_mcp_projects()
|
|
77
|
+
# {"projects": [{"project_name": ..., "endpoint": ..., "status": "healthy"|"unavailable", ...}], "total": N}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This actually probes each configured endpoint's real `tools/list` MCP
|
|
81
|
+
method — with nothing configured, or nothing reachable, it honestly
|
|
82
|
+
reports zero/unavailable projects rather than a fixed fixture list.
|
|
83
|
+
`Orchestrator` also does lexical tool-relevance ranking
|
|
84
|
+
(`rank_tools_by_relevance`) and capability matching
|
|
85
|
+
(`detect_compatible_projects`) against whatever it actually discovers.
|
|
86
|
+
|
|
87
|
+
To expose these (plus tool-routing and webhook-event handling) as MCP
|
|
88
|
+
tools, `PyStreamMCPHandler` (`pystreammcp._mcp_tools`) wires the MCP
|
|
89
|
+
tool-call surface directly to a live `Orchestrator`/`EventRouter`
|
|
90
|
+
instance — every handler call is backed by real, inspectable state, not
|
|
91
|
+
a hardcoded response.
|
|
92
|
+
|
|
93
|
+
**Honest limitations**: a few orchestration capabilities are accepted at
|
|
94
|
+
the API level but not fully implemented yet — cross-project federated
|
|
95
|
+
query *execution* and cross-database joins currently report
|
|
96
|
+
`"status": "not_implemented"` with an explanation, rather than fabricated
|
|
97
|
+
result rows, because there's no real query engine behind them yet.
|
|
98
|
+
|
|
99
|
+
## Webhook event server
|
|
100
|
+
|
|
101
|
+
`pystreammcp.server.create_flask_app()` exposes a small REST/webhook
|
|
102
|
+
surface for orchestration events (agent MCPs reporting `mcp.available`,
|
|
103
|
+
`tool.invoked`, health updates, etc. to `POST /orchestration/webhooks/events`).
|
|
104
|
+
|
|
105
|
+
### Webhook security (HMAC-SHA256)
|
|
106
|
+
|
|
107
|
+
Inbound events to `/orchestration/webhooks/events` must be signed:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
export PYSTREAMMCP_WEBHOOK_SECRET="a long random shared secret"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Senders compute `HMAC-SHA256(secret, raw_request_body)` and send it as:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
X-PyStreamMCP-Signature: sha256=<hex digest>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from pystreammcp.server import compute_webhook_signature
|
|
121
|
+
import json, requests
|
|
122
|
+
|
|
123
|
+
body = json.dumps({"event_type": "mcp.available", "data": {...}}).encode()
|
|
124
|
+
signature = compute_webhook_signature(secret, body)
|
|
125
|
+
|
|
126
|
+
requests.post(
|
|
127
|
+
"http://localhost:8000/orchestration/webhooks/events",
|
|
128
|
+
data=body,
|
|
129
|
+
headers={
|
|
130
|
+
"Content-Type": "application/json",
|
|
131
|
+
"X-PyStreamMCP-Signature": signature,
|
|
132
|
+
},
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Requests with a missing or invalid signature are rejected with `401`. If
|
|
137
|
+
`PYSTREAMMCP_WEBHOOK_SECRET` isn't set at all, the endpoint fails closed
|
|
138
|
+
and rejects every request with `503` — there is no "accept unsigned
|
|
139
|
+
events" fallback.
|
|
140
|
+
|
|
141
|
+
### Network defaults
|
|
142
|
+
|
|
143
|
+
The HTTP/CLI servers (`pystreammcp server`, `PyStreamMCPServer`,
|
|
144
|
+
`PyStreamMCPAPI.run`) bind to `127.0.0.1` (localhost-only) by default.
|
|
145
|
+
Pass `--host 0.0.0.0` (CLI) or `host="0.0.0.0"` explicitly if you need the
|
|
146
|
+
server reachable from other hosts — e.g. inside a container that already
|
|
147
|
+
has its own network boundary.
|
|
148
|
+
|
|
149
|
+
## Testing
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
pip install -e ".[dev,api,mcp,langchain,llamaindex,semantic-kernel]"
|
|
153
|
+
pytest tests/ -v
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Rust workspace (not shipped)
|
|
157
|
+
|
|
158
|
+
`core/` and `python/src/lib.rs` are an in-progress Rust workspace intended
|
|
159
|
+
as a future performance backend. **It is not part of the published PyPI
|
|
160
|
+
package** — the wheel built from `pyproject.toml` is pure Python — and it
|
|
161
|
+
does not currently compile. Don't rely on it; it's tracked separately
|
|
162
|
+
from the Python package described in this README.
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
Proprietary License — Free to use with explicit attribution. See
|
|
167
|
+
[LICENSE](LICENSE).
|
|
@@ -4,13 +4,20 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "PyStreamMCP"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "3.2.0"
|
|
8
8
|
description = "Intelligent MCP orchestration hub - Intent understanding, capability matching, tool ranking with cohesive foundation"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
license = {
|
|
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 = [
|
|
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)
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PyStreamMCP
|
|
3
|
+
Version: 3.2.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
|
+
[](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+.
|
|
95
|
+
|
|
96
|
+
## Quick start
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from pystreammcp import Agent
|
|
100
|
+
|
|
101
|
+
agent = Agent(
|
|
102
|
+
agent_id="recommendation_engine",
|
|
103
|
+
name="Product Recommendation Engine",
|
|
104
|
+
optimization_strategy="token_efficient", # or "balanced" / "quality_first"
|
|
105
|
+
max_tokens=1000,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
result = agent.query("What are the top 10 customers by lifetime value?")
|
|
109
|
+
|
|
110
|
+
print(f"Baseline tokens: {result.baseline_tokens}")
|
|
111
|
+
print(f"Optimized tokens: {result.optimized_tokens}")
|
|
112
|
+
print(f"Reduction: {result.cost_reduction_percent:.1f}%")
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Token counts are estimated from the actual query text (a standard
|
|
116
|
+
~4-characters-per-token heuristic) and the reduction percentage is
|
|
117
|
+
computed from the selected `optimization_strategy` — not a fixed number
|
|
118
|
+
returned regardless of input. See [`examples/basic_usage.py`](examples/basic_usage.py)
|
|
119
|
+
for a full runnable walkthrough, including framework adapters for
|
|
120
|
+
LangChain, LlamaIndex, CrewAI, Semantic Kernel, PydanticAI, and Haystack
|
|
121
|
+
under [`pystreammcp.integrations`](python/pystreammcp/integrations/).
|
|
122
|
+
|
|
123
|
+
## MCP orchestration & federation
|
|
124
|
+
|
|
125
|
+
`Orchestrator` (`pystreammcp.Orchestrator`) discovers and routes across
|
|
126
|
+
other MCP-enabled projects you configure in `pystreammcp.toml`:
|
|
127
|
+
|
|
128
|
+
```toml
|
|
129
|
+
[federation]
|
|
130
|
+
endpoints = [
|
|
131
|
+
"http://localhost:8765/mcp",
|
|
132
|
+
"http://localhost:8766/mcp",
|
|
133
|
+
]
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from pystreammcp import Orchestrator
|
|
138
|
+
|
|
139
|
+
orch = Orchestrator() # reads ./pystreammcp.toml
|
|
140
|
+
result = orch.discover_mcp_projects()
|
|
141
|
+
# {"projects": [{"project_name": ..., "endpoint": ..., "status": "healthy"|"unavailable", ...}], "total": N}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
This actually probes each configured endpoint's real `tools/list` MCP
|
|
145
|
+
method — with nothing configured, or nothing reachable, it honestly
|
|
146
|
+
reports zero/unavailable projects rather than a fixed fixture list.
|
|
147
|
+
`Orchestrator` also does lexical tool-relevance ranking
|
|
148
|
+
(`rank_tools_by_relevance`) and capability matching
|
|
149
|
+
(`detect_compatible_projects`) against whatever it actually discovers.
|
|
150
|
+
|
|
151
|
+
To expose these (plus tool-routing and webhook-event handling) as MCP
|
|
152
|
+
tools, `PyStreamMCPHandler` (`pystreammcp._mcp_tools`) wires the MCP
|
|
153
|
+
tool-call surface directly to a live `Orchestrator`/`EventRouter`
|
|
154
|
+
instance — every handler call is backed by real, inspectable state, not
|
|
155
|
+
a hardcoded response.
|
|
156
|
+
|
|
157
|
+
**Honest limitations**: a few orchestration capabilities are accepted at
|
|
158
|
+
the API level but not fully implemented yet — cross-project federated
|
|
159
|
+
query *execution* and cross-database joins currently report
|
|
160
|
+
`"status": "not_implemented"` with an explanation, rather than fabricated
|
|
161
|
+
result rows, because there's no real query engine behind them yet.
|
|
162
|
+
|
|
163
|
+
## Webhook event server
|
|
164
|
+
|
|
165
|
+
`pystreammcp.server.create_flask_app()` exposes a small REST/webhook
|
|
166
|
+
surface for orchestration events (agent MCPs reporting `mcp.available`,
|
|
167
|
+
`tool.invoked`, health updates, etc. to `POST /orchestration/webhooks/events`).
|
|
168
|
+
|
|
169
|
+
### Webhook security (HMAC-SHA256)
|
|
170
|
+
|
|
171
|
+
Inbound events to `/orchestration/webhooks/events` must be signed:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
export PYSTREAMMCP_WEBHOOK_SECRET="a long random shared secret"
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Senders compute `HMAC-SHA256(secret, raw_request_body)` and send it as:
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
X-PyStreamMCP-Signature: sha256=<hex digest>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from pystreammcp.server import compute_webhook_signature
|
|
185
|
+
import json, requests
|
|
186
|
+
|
|
187
|
+
body = json.dumps({"event_type": "mcp.available", "data": {...}}).encode()
|
|
188
|
+
signature = compute_webhook_signature(secret, body)
|
|
189
|
+
|
|
190
|
+
requests.post(
|
|
191
|
+
"http://localhost:8000/orchestration/webhooks/events",
|
|
192
|
+
data=body,
|
|
193
|
+
headers={
|
|
194
|
+
"Content-Type": "application/json",
|
|
195
|
+
"X-PyStreamMCP-Signature": signature,
|
|
196
|
+
},
|
|
197
|
+
)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Requests with a missing or invalid signature are rejected with `401`. If
|
|
201
|
+
`PYSTREAMMCP_WEBHOOK_SECRET` isn't set at all, the endpoint fails closed
|
|
202
|
+
and rejects every request with `503` — there is no "accept unsigned
|
|
203
|
+
events" fallback.
|
|
204
|
+
|
|
205
|
+
### Network defaults
|
|
206
|
+
|
|
207
|
+
The HTTP/CLI servers (`pystreammcp server`, `PyStreamMCPServer`,
|
|
208
|
+
`PyStreamMCPAPI.run`) bind to `127.0.0.1` (localhost-only) by default.
|
|
209
|
+
Pass `--host 0.0.0.0` (CLI) or `host="0.0.0.0"` explicitly if you need the
|
|
210
|
+
server reachable from other hosts — e.g. inside a container that already
|
|
211
|
+
has its own network boundary.
|
|
212
|
+
|
|
213
|
+
## Testing
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
pip install -e ".[dev,api,mcp,langchain,llamaindex,semantic-kernel]"
|
|
217
|
+
pytest tests/ -v
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Rust workspace (not shipped)
|
|
221
|
+
|
|
222
|
+
`core/` and `python/src/lib.rs` are an in-progress Rust workspace intended
|
|
223
|
+
as a future performance backend. **It is not part of the published PyPI
|
|
224
|
+
package** — the wheel built from `pyproject.toml` is pure Python — and it
|
|
225
|
+
does not currently compile. Don't rely on it; it's tracked separately
|
|
226
|
+
from the Python package described in this README.
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
Proprietary License — Free to use with explicit attribution. See
|
|
231
|
+
[LICENSE](LICENSE).
|