PyStreamMCP
An AI-agent intelligence layer: query planning, context discovery, and token-cost optimization, exposed both as a Python SDK and as an MCP (Model Context Protocol) tool server, plus a small event-driven orchestration/federation layer for coordinating multiple MCP endpoints.
Pure Python — pip install just works, no Rust toolchain or compiler
required.
Use cases
- Giving an AI agent a query-planning layer over multiple data sources, exposed as real MCP tools rather than hand-rolled per-agent glue code.
- Coordinating multiple MCP endpoints through the event-driven orchestration/federation layer instead of wiring each one separately.
- Tracking token-cost optimization across agent queries.
- Not yet a good fit for: anything depending on the Rust workspace — see Rust workspace (not shipped) below; the published package is pure Python only.
Install
pip install PyStreamMCP
Optional extras, install what you actually need:
pip install "PyStreamMCP[api]" # FastAPI + Flask HTTP servers
pip install "PyStreamMCP[mcp]" # MCP protocol client library
pip install "PyStreamMCP[langchain]" # LangChain adapter
pip install "PyStreamMCP[llamaindex]" # LlamaIndex adapter
pip install "PyStreamMCP[semantic-kernel]" # Semantic Kernel adapter
pip install "PyStreamMCP[all-integrations]" # every framework adapter
Requires Python 3.9+ for the core SDK (Agent, SourceRegistry,
Orchestrator). The mcp and semantic-kernel extras require Python
3.10+ — their upstream packages don't publish Python 3.9-compatible
releases, so pip install for those extras will fail on 3.9. CI tests
3.10, 3.11, and 3.12 only; 3.9 is not covered by CI.
Quick start
from pystreammcp import Agent
agent = Agent(
agent_id="recommendation_engine",
name="Product Recommendation Engine",
optimization_strategy="token_efficient", # or "balanced" / "quality_first"
max_tokens=1000,
)
result = agent.query("What are the top 10 customers by lifetime value?")
print(f"Baseline tokens: {result.baseline_tokens}")
print(f"Optimized tokens: {result.optimized_tokens}")
print(f"Reduction: {result.cost_reduction_percent:.1f}%")
Token counts are estimated from the actual query text (a standard
~4-characters-per-token heuristic) and the reduction percentage is
computed from the selected optimization_strategy — not a fixed number
returned regardless of input. See examples/basic_usage.py
for a full runnable walkthrough, including framework adapters for
LangChain, LlamaIndex, CrewAI, Semantic Kernel, PydanticAI, and Haystack
under pystreammcp.integrations.
Data source discovery
SourceRegistry (pystreammcp.SourceRegistry) ranks registered data
sources by real word-overlap relevance to a query — not a fabricated or
hardcoded result:
from pystreammcp import SourceRegistry
registry = SourceRegistry()
registry.register(
"orders_db",
description="customer order history and revenue by quarter",
type="database",
tags=["orders", "revenue"],
)
results = registry.discover("customer revenue by quarter")
# [{"name": "orders_db", "type": "database", "relevance": 0.4444, "matched_terms": [...]}]
The same registry backs POST /discover (REST), the pystreammcp_discover
MCP tool, and LangchainAdapter.discover() — register sources once via
POST /sources / pystreammcp_register_source / registry.register(),
then discover against them from any of the three surfaces. This is a
simple Jaccard token-overlap heuristic, not semantic/embedding search.
MCP orchestration & federation
Orchestrator (pystreammcp.Orchestrator) discovers and routes across
other MCP-enabled projects you configure in pystreammcp.toml:
[federation]
endpoints = [
"http://localhost:8765/mcp",
"http://localhost:8766/mcp",
]
from pystreammcp import Orchestrator
orch = Orchestrator() # reads ./pystreammcp.toml
result = orch.discover_mcp_projects()
# {"projects": [{"project_name": ..., "endpoint": ..., "status": "healthy"|"unavailable", ...}], "total": N}
This actually probes each configured endpoint's real tools/list MCP
method — with nothing configured, or nothing reachable, it honestly
reports zero/unavailable projects rather than a fixed fixture list.
Orchestrator also does lexical tool-relevance ranking
(rank_tools_by_relevance) and capability matching
(detect_compatible_projects) against whatever it actually discovers.
To expose these (plus tool-routing and webhook-event handling) as MCP
tools, PyStreamMCPHandler (pystreammcp._mcp_tools) wires the MCP
tool-call surface directly to a live Orchestrator/EventRouter
instance — every handler call is backed by real, inspectable state, not
a hardcoded response.
Honest limitations: a few orchestration capabilities are accepted at
the API level but not fully implemented yet — cross-project federated
query execution and cross-database joins currently report
"status": "not_implemented" with an explanation, rather than fabricated
result rows, because there's no real query engine behind them yet.
Webhook event server
pystreammcp.server.create_flask_app() exposes a small REST/webhook
surface for orchestration events (agent MCPs reporting mcp.available,
tool.invoked, health updates, etc. to POST /orchestration/webhooks/events).
Webhook security (HMAC-SHA256)
Inbound events to /orchestration/webhooks/events must be signed:
export PYSTREAMMCP_WEBHOOK_SECRET="a long random shared secret"
Senders compute HMAC-SHA256(secret, raw_request_body) and send it as:
X-PyStreamMCP-Signature: sha256=<hex digest>
from pystreammcp.server import compute_webhook_signature
import json, requests
body = json.dumps({"event_type": "mcp.available", "data": {...}}).encode()
signature = compute_webhook_signature(secret, body)
requests.post(
"http://localhost:8000/orchestration/webhooks/events",
data=body,
headers={
"Content-Type": "application/json",
"X-PyStreamMCP-Signature": signature,
},
)
Requests with a missing or invalid signature are rejected with 401. If
PYSTREAMMCP_WEBHOOK_SECRET isn't set at all, the endpoint fails closed
and rejects every request with 503 — there is no "accept unsigned
events" fallback.
Network defaults
The HTTP servers (PyStreamMCPServer / create_flask_app(),
PyStreamMCPAPI.run) bind to 127.0.0.1 (localhost-only) by default.
Pass host="0.0.0.0" explicitly if you need the server reachable from
other hosts — e.g. inside a container that already has its own network
boundary.
The pystreammcp CLI: pip install PyStreamMCP now installs a real
pystreammcp command ([project.scripts] in pyproject.toml) backed by
the Click group in python/pystreammcp/cli.py:
query/server/version/dashboard. python -m pystreammcp.cli reaches
the same Click group (the file's old, separate, smaller hand-rolled
command set has been removed). server lazy-imports pystreammcp.api, so
pystreammcp version/query/dashboard work without installing the
api extra; running pystreammcp server without it prints a clear
"install pystreammcp[api]" error instead of crashing with
ModuleNotFoundError. See ROADMAP_HONEST.md §2.2
(now marked fixed) for how this was previously broken.
Testing
pip install -e ".[dev,api,mcp,langchain,llamaindex,semantic-kernel]"
pytest tests/ -v
Note: this exact command needs Python 3.10+ (see Install); on
3.9 the mcp and semantic-kernel extras fail to resolve, so drop them
from the extras list if you're on 3.9.
Known Issues
2026-10-11 pass — workspace-wide error sweep; the Rust backend now
compiles and its tests pass, Python ruff is clean, and a stale
integration test was rewritten against the real schema:
-
The
core/Rust workspace compiles again. It had accumulated 43cargo checkerrors:orchestration/(reranking/orchestrator referenced a stalestage2_decisionfield,RankingStrategy/FusionMethodwere missingSerialize/Deserialize,ContentSegmentwas missingPartialEq,TokenBudgetwas re-exported from the wrong module);metadata/(cache eviction borrow-checker error, missingCacheStats.evictions,clear_cache/cache_statswere notasync);selective_retrieval/(QueryIntentmissingPartialEq, struct-variantBudgetTiermatch patterns, a duplicatedRerankerConfig);quality_gates/(duplicatedConfidenceConfig/SLACheckResult); andobservability/(per-subsystem*Configconstructors resolved to a non-existent path). All fixed;cargo testis green — 138 library + 29 integration tests, 0 failures.- Build note:
rust-toolchain.tomlstill pinschannel = "1.81", which cannot even parse the committed lockfile — transitive deps such asidna_adapter 1.2.2requireedition2024, i.e. Cargo ≥ 1.85. The pin was intentionally left unchanged; build withRUSTUP_TOOLCHAIN=1.97 cargo ...until the pin is revisited.
- Build note:
-
5 Python
F821undefined-name errors fixed inpython/pystreammcp/integrations/{langchain,llamaindex,semantic_kernel}.py— the retriever/tool/plugin classes were referenced in annotations but never imported. AddedTYPE_CHECKING-guarded imports so the optional dependency types are visible to type-checkers without forcing a hard runtime import.ruff check python/ --select E9,F63,F7,F82is clean. -
core/tests/phase_v05_metadata_tests.rswas dead code — it had never compiled (it referenced an aspirationalMetadataEntry/profile schema that exists nowhere in the tree) and so was never run. Rewritten against the real metadata API (DatabaseMetadata/MCPToolMetadata,Metadata::Web/Database/Mcp, profile keyspublic_postgres,bigquery_analytics,ml_inference, …); all 29 tests pass. -
3 pre-existing heuristic test failures fixed.
orchestration/complexity.rs: a "very complex" query scored lower than a merely "complex" one because reasoning terms (analysis,correlation,anomalies,pattern, …) weren't recognised — added ananalytical_terms()signal and widened theComplexband.selective_retrieval/intent.rs: added an enumeration (comma-list) signal and raised the multi-entity weight so list-heavy and design-style queries classify correctly. No production behaviour change (these tiers only feedbudgets::assign_tier, and the detectors are otherwise unused). -
No end-to-end tests for LLM failure statesFixed. PyStreamMCP's own code never calls a model provider directly (no code path here to addexcept RateLimitError/APIErrorhandling to — seeAgentFrameworkAdapterbelow), so what's now tested is the part this repo actually owns: that the MCP tool-call path doesn't crash when whatever it's orchestrating fails.pystreammcp.testingexportsRateLimitError,ContextWindowExceededError,MalformedModelResponseError, andFailingAgent/FailingAdapterstand-ins that raise them;mcp_server.py'scall_tool/process_messagenow catch exceptions raised during tool execution and return a structured{"status": "error", "error_type": ..., "message": ...}response instead of propagating uncaught. Seetests/test_mcp_server_resilience.py. -
No Pydantic validation for the MCP tool protocol itselfFixed. Each of the four MCP tools now has a Pydantic arg model (QueryToolArgs/DiscoverToolArgs/RegisterSourceToolArgs/OptimizeToolArgsinmcp_server.py) validated incall_toolbefore dispatch (wrong types, invalid enum values, and missing required fields all now return a structuredvalidation_errorsresponse instead of reaching the handler unchecked), andMCPTool.input_schemais generated from those same models viamodel_json_schema()— the advertised schema and the schema actually enforced can no longer drift apart the way a hand-duplicated dict could. -
No shipped mock MCP server / mock LLM fixtureFixed. Addedtests/conftest.py(centralizing thesys.pathsetup 5 test files were duplicating, plusmock_adapter/failing_agent/failing_adapter/llm_failurepytest fixtures) andpystreammcp/testing.py, exportingMockAdapter(promoted from the old test-local copy) and the failure-injection stand-ins above for downstream projects' own test suites to import directly. -
Per-query context discovery now has a real implementation (
pystreammcp.discovery.SourceRegistry): register data sources (name, description, type, tags), and/discover(REST),pystreammcp_discover(MCP), andLangchainAdapter.discover()all rank registered sources by real Jaccard token-overlap against the query — an empty registry or a non-matching context returns an empty list, not fabricated data. This is a simple word-overlap heuristic, not semantic/embedding search — if you need that, wrapSourceRegistrywith your own similarity scoring. -
TheFixed.pystreammcpCLI is dead codepyproject.tomlnow has a[project.scripts]entry, and the module's__main__guard invokes the real Click group. See Network defaults above andROADMAP_HONEST.md§2.2. -
Three orchestration-tool discovery adapters return fabricated resultsFixed.orchestration/temporal.py'sTemporalDiscoveryActivity/TemporalWorkflow,airflow.py'sPyStreamMCPDiscoveryOperator, andnocode_rpa.py'sN8nWebhookTriggernow take an optional sharedSourceRegistryand call its realdiscover()instead of synthesizingsource_0.. source_4. With nothing registered they honestly return zero sources (seeROADMAP_HONEST.md§2.1). -
pip-audit(run 2026-09-20 against thedev,api,mcp,langchain, llamaindex,semantic-kernelextras) found 7 known CVEs in transitive dependencies (werkzeugviaflask,nltkviallama-index) — seeROADMAP_HONEST.md§4. Not fixed this pass. -
No open GitHub issues as of this pass.
Documentation
ROADMAP_HONEST.md— full, unhedged status: what's tested, what's fabricated/broken, technical debt, what's explicitly not built.docs/ARCHITECTURE.md— how the shipped package is structured, with a diagram.OKF_INTEGRATION.md— the OKF (Open Knowledge Format) catalog/discovery/query-planner feature (real, tested, not mentioned elsewhere in this README).CONTRIBUTING.md,SECURITY.md,CODE_OF_CONDUCT.md,CHANGELOG.md.
Rust workspace (not shipped)
core/ and python/src/lib.rs are an in-progress Rust workspace intended
as a future performance backend. It is not part of the published PyPI
package — the wheel built from pyproject.toml is pure Python. It now
compiles and its unit/integration tests pass (see the 2026-10-11 entry in
Known Issues), but note the 1.81 toolchain pin: build it
with RUSTUP_TOOLCHAIN=1.97 cargo .... Don't rely on it as a shipped
artifact; it's tracked separately from the Python package described in this
README.
License
This project is licensed under the Apache License 2.0.
Metadata
Release files for PyStreamMCP 3.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pystreammcp-3.3.1.tar.gz | 143.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pystreammcp-3.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 271.6 kB
Release files / pystreammcp-3.3.1.tar.gz
| Download URL | pystreammcp-3.3.1.tar.gz |
|---|---|
| Size | 143.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d536d8d62d4238406d7f8b5214232a564f42180237f1d33bb8b7133219811248
|
|
BLAKE2b-256 checksum How to use checksums |
e73cdd2fcfff71ba26e79f1417c6aaba67b4888cbe506afea30ca8fe97cfdb3a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / pystreammcp-3.3.1-py3-none-any.whl
| Download URL | pystreammcp-3.3.1-py3-none-any.whl |
|---|---|
| Size | 128.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
75b34faf165c20aec897c7d52f1f1b753490f5e9cbade6d2757570b593ee8e33
|
|
BLAKE2b-256 checksum How to use checksums |
1f5ea5ea1d41790b261bd98106d1e7970344d29ba8889d5a8f1c1e55e9f0374b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|