Ask your Gaia-X data space in plain language. MCP-native Python SDK for Eclipse EDC.
Project description
Pythia: Ask Your Dataspace
Team Lead: Jonathan Kaleve — Pareo
Pythia is a natural-language and MCP client for Gaia-X / Eclipse EDC data spaces. Instead of working with provider URLs, asset IDs, ODRL policies, and the EDC negotiation state machine directly, you ask a question in plain language and Pythia handles catalog discovery, contract negotiation, transfer, and retrieval.
from pythia import DataSpace
ds = DataSpace(
management_url="http://consumer:29193/management",
providers=[{"dsp": "http://provider:19194/protocol/2025-1", "id": "provider"}],
)
# Natural language query — Pythia negotiates, retrieves, and returns a readable
# Answer (a synthesized table + provenance). Pass raw=True for the raw asset bytes.
answer = await ds.ask("CO2 emissions data for German automotive suppliers 2023")
print(answer.to_markdown())
It also ships as an MCP server, exposing data-space queries as tools for Claude, GPT, or any MCP-compatible agent.
Components
| Layer | Component |
|---|---|
| 1 — Core client | Async httpx EDC client, full DSP state machine, typed errors, no RabbitMQ |
| 2 — NL interface | ds.ask() — parallel catalog fan-out + offline granite-embedding-97m-multilingual-r2 ranking + auto-negotiate; returns a readable Answer by default (synthesized table on success, or an explained refusal in Answer.note), raw=True for bytes |
| 3 — MCP server | pythia-mcp — wraps ds.ask() as MCP tools for Claude / GPT / any agent |
Install
pip install 'pythia-edc[all]'
Running the demo
The repo ships a self-contained demo stack driven by a single ./demo entrypoint.
./demo up # start the stack
./demo ask "CO2 emissions for German automotive suppliers 2023"
./demo repl # interactive query loop
./demo down # stop the stack
By default ./demo up runs everything locally: provider + consumer EDC
connectors, a mock data server, and provider seeding.
Config profiles
Any setting can live in a profile file instead of inline env vars. ./demo
loads demo.env by default and exports every assignment to both the stack
scripts and the Python client. Copy the example to get started:
cp demo.env.example demo.env # edit, then ./demo up
Keep several named profiles and select one per run with ENV_FILE:
ENV_FILE=demo.consumer.env ./demo up
Local consumer, remote providers
To run only the consumer locally and talk to providers that run on separate
servers, set CONSUMER_ONLY=1 (skips the local provider, mock server, and
seeding) and point PYTHIA_PROVIDERS at the remote DSP endpoints. See
demo.env.example for the full template, including the TLS variables
(PYTHIA_CA_BUNDLE, PYTHIA_CLIENT_CERT/KEY) for talking to remote providers.
CONSUMER_ONLY=1 \
PYTHIA_PROVIDERS='[{"dsp":"https://provider1.example/protocol/2025-1","id":"rheinmobil"}]' \
./demo up
Usage
# Low-level
catalog = await ds.catalog.query(provider_dsp="...", provider_id="...")
agreement_id = await ds.negotiate(provider_dsp="...", provider_id="...",
offer_id=offer.id, asset_id=asset.id)
data = await ds.fetch(provider_dsp="...", provider_id="...",
agreement_id=agreement_id, asset_id=asset.id)
# Natural language — returns a readable Answer (table + provenance)
answer = await ds.ask("quarterly SVHC substance reports from EU chemical suppliers")
print(answer.to_markdown())
# ...or the raw asset bytes, for the developer/agent path
raw = await ds.ask("quarterly SVHC substance reports", raw=True)
MCP server
export PYTHIA_MANAGEMENT_URL="http://localhost:29193/management"
export PYTHIA_PROVIDERS='[{"dsp": "http://localhost:19194/protocol/2025-1", "id": "provider"}]'
pythia-mcp
Gaia-X standards
| Standard | Usage |
|---|---|
DSP dataspace-protocol-http:2025-1 |
Full state machine: catalog → negotiate → transfer → EDR |
| DCAT | Catalog parsing, asset/offer extraction |
| ODRL | Policy offer construction with correct context |
| MCP (Model Context Protocol) | AI agent integration (pythia-mcp server) |
| Verifiable Credentials + SHACL | Trust slice: verify a provider VC (structure + signature) and validate the offer + ODRL policy against a SHACL shape before negotiating |
Provider VC verification
When verify_trust=True and a credential_source is supplied, Pythia verifies each
provider's Verifiable Credential fully offline before validating its offer:
- structural validation (required
@context/type/id/issuerfields, SHACL shape) - validity-window check (
validFrom/ expiry) - Ed25519
JsonWebSignature2020detached-JWS signature verification viadid:key - issuer ↔ signing-key binding (the verifying key must belong to the credential issuer)
- an optional issuer trust-list of allowed issuer DIDs
from pythia import DataSpace, StaticCredentialSource
ds.ask("CO2 emissions from German automotive suppliers 2023",
verify_trust=True,
credential_source=StaticCredentialSource({"provider": provider_vc}),
trust_list={"did:key:z6Mk..."})
Rejections surface as structured CredentialError.failures, rendered into prose by the
Explainer (e.g. "the credential has expired", "the credential issuer is not trusted").
In the demo, trust verification is on by default: ./demo ask "..." verifies each
provider's VC and SHACL-validates its offer before negotiating. The demo mints VCs from a
fixed demo CA for the trusted providers, while DonauTech is intentionally untrusted —
its VC is signed by an issuer not on the consumer's trust-list, so it is rejected
(UntrustedIssuer) and the query falls through to a trusted provider. Disable the gate
with ./demo ask --no-verify-trust "...". (The library default of verify_trust is
unchanged — off — so non-demo callers opt in explicitly as shown above.)
Roadmap: full GXDCH / Notary online credential verification, and resolving did:web
issuer DID-documents (the current crypto path effectively trusts did:key issuers, since
the verifying key must equal the issuer); provider auto-discovery (the Meta Registry is a
trust-profile source, not a connector directory, so endpoint discovery needs an EDC
FederatedCatalog or a self-description convention).
Prerequisites
- Python 3.11+, running EDC consumer + provider connectors
sentence-transformersfords.ask()(offline,ibm-granite/granite-embedding-97m-multilingual-r2, ~130MB, multilingual)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pythia_edc-0.2.0.tar.gz.
File metadata
- Download URL: pythia_edc-0.2.0.tar.gz
- Upload date:
- Size: 58.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f773630b9d7ebc10e51a34325cfdbb06ea1dae83017abba93aa3a1e049c97020
|
|
| MD5 |
74bbd67a060578e722d397c90da893cf
|
|
| BLAKE2b-256 |
f1c2e2cfd50d3ea84ba71f3a632617a02c25109751ef353595b245bfcad0f4d7
|
File details
Details for the file pythia_edc-0.2.0-py3-none-any.whl.
File metadata
- Download URL: pythia_edc-0.2.0-py3-none-any.whl
- Upload date:
- Size: 38.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d3efcfbb62fb01d7cfab9dfa9f9a8bd7a7a11896912aeeb4ad3340b2247caddc
|
|
| MD5 |
0bfa7edcf2966d7f40188d46c552a64d
|
|
| BLAKE2b-256 |
b53a3fcd2efba20066038e57757592dee3ebdea68e2031125c2fbd0bfb98fa15
|