Skip to main content
Glama
izzy-Ti

Fayda MCP

by izzy-Ti

Fayda MCP Python Library (fayda-mcp)

PyPI version License

An importable, headless Python library providing Model Context Protocol (MCP) tools and verification services for Ethiopian National ID (Fayda eSignet).

Key Capabilities

  • Explicit FastMCP Tools:

    • eSignet OIDC: start_verification, get_verification_status, get_verification_result, cancel_verification.

    • Citizen Details Matching: start_details_verification, get_details_match_result.

    • Offline QR Code: submit_qr_verification, get_qr_verification_result.

    • Policy & Health: list_allowed_checks, list_verification_purposes, check_service_health.

  • Zero Raw Demographic / Biometric Leaks: Outputs minimal boolean predicates (identity_verified, age_over_18, credential_signature_valid), strictly forbidding citizen biometrics, OTPs, or demographic dumps from entering LLM contexts.

  • Offline & Edge Verification: High-density QR verification runs completely offline—requiring no OIDC callback, no webhook, and no client private signing key (only authority public key / trust bundle).

  • Physical Credential Security Boundaries: Physical QR scanning validates issuer provenance and credential integrity, but strictly sets holder_authenticated=false and never asserts identity_verified without live authentication. Copied/screenshotted QR threat model is enforced fail-closed.

  • Scanner Text First Delivery: Core library accepts raw ASCII scanner text directly from handheld barcode readers and camera SDKs (image scanning is a separate optional adapter).

  • Cryptographic Security: OIDC PKCE S256, RFC 7523 private_key_jwt client assertions, and RFC 7515 Appendix F RS256 detached JWS signature verification.

  • Pluggable Architecture: Zero mandatory database or web framework runtime dependencies in core; optional extras for FastAPI, Redis, and Neon/PostgreSQL. Redis remains optional for temporary references and rate limits.

  • Multi-Tenant Caller Isolation: Built-in tenant and principal boundaries ensuring Caller A cannot access Caller B verification records.


Related MCP server: signet

Installation

# Core library (headless FastMCP + Fayda OIDC + Offline QR verification engine)
pip install fayda-mcp

# With optional FastAPI integration
pip install "fayda-mcp[fastapi]"

# Full bundle with Redis and Neon/Postgres adapters
pip install "fayda-mcp[fastapi,redis,postgres]"

Quick Start (Offline QR Verification)

from fayda_mcp import FaydaConfig, FaydaVerificationService
from fayda_mcp.storage.memory import MemoryResultRepository, MemorySessionStore

# 1. Enable QR verification with Fayda authority public key
config = FaydaConfig.sandbox(
    client_id="kiosk_qr_verifier",
    redirect_uri="https://localhost/unused",
    qr_verification_enabled=True,
    qr_key_bundle_path="path/to/fayda_qr_keys.pem",  # or qr_public_key_pem
)

# 2. Initialize service (memory or database storage)
service = FaydaVerificationService(
    config=config,
    sessions=MemorySessionStore(),
    results=MemoryResultRepository(),
)

# 3. Submit scanned QR text (no network or callbacks needed)
result = await service.submit_qr_verification(
    qr_text="<raw_scanner_text_from_physical_card>",
    purpose="kyc",
    checks=["credential_signature_valid", "age_over_18"],
)
# Returns minimal filtered agent predicates; demographics and photo are excluded!
print(result.checks)  # {'credential_signature_valid': True, 'age_over_18': True}
print(result.holder_authenticated)  # False (offline scan alone does not prove live presence)

Quick Start (OIDC Sandbox)

import base64
from fayda_mcp import FaydaConfig, FaydaVerificationService
from fayda_mcp.storage.memory import MemorySessionStore, MemoryResultRepository
from fayda_mcp.mcp.factory import create_mcp_server

# Configure your Fayda / eSignet credentials
config = FaydaConfig(
    client_id="YOUR_CLIENT_ID",
    redirect_uri="http://localhost:3000/callback",
    authorization_endpoint="[https://esignet.ida.fayda.et/authorize](https://esignet.ida.fayda.et/authorize)",
    token_endpoint="[https://esignet.ida.fayda.et/v1/esignet/oauth/v2/token](https://esignet.ida.fayda.et/v1/esignet/oauth/v2/token)",
    userinfo_endpoint="[https://esignet.ida.fayda.et/v1/esignet/oidc/userinfo](https://esignet.ida.fayda.et/v1/esignet/oidc/userinfo)",
    issuer="[https://esignet.ida.fayda.et](https://esignet.ida.fayda.et)",
    jwks_uri="[https://esignet.ida.fayda.et/v1/esignet/oauth/v2/jwks.json](https://esignet.ida.fayda.et/v1/esignet/oauth/v2/jwks.json)",
    private_key=base64.b64decode("YOUR_PRIVATE_KEY_B64").decode("utf-8"),
)

service = FaydaVerificationService(
    config=config,
    sessions=MemorySessionStore(),
    results=MemoryResultRepository(),
)

server = create_mcp_server(service=service)

if __name__ == "__main__":
    server.run(transport="stdio")

Quick Start (Citizen Details Matching)

from fayda_mcp import FaydaConfig, FaydaVerificationService
from fayda_mcp.matching.schemas import ExpectedDetails
from fayda_mcp.storage.memory import MemoryResultRepository, MemorySessionStore

# 1. Initialize service with pluggable session and result repositories
service = FaydaVerificationService(
    config=FaydaConfig.sandbox(client_id="rp_client", redirect_uri="http://localhost:8000/callback"),
    sessions=MemorySessionStore(),
    results=MemoryResultRepository(),
)

# 2. Authenticated backend submits citizen expected details (AES-256-GCM encrypted at rest)
sub = await service.submit_expected_details(
    application_user_ref="cust_1001",
    purpose="onboarding",
    expected_details=ExpectedDetails(
        full_name="Abebe Bikila",
        date_of_birth="1995/04/12",
        dob_calendar="gregorian",
    ),
    ttl_seconds=900,
)

# 3. Agent launches verification using the opaque reference token
start = await service.start_details_verification(
    expected_details_ref=sub.expected_details_ref,
    purpose="onboarding",
    application_user_ref="cust_1001",
)

# 4. After citizen authenticates via eSignet callback, query minimal match outcome
result = await service.get_details_match_result(start.request_id)
# Returns tri-state flags; zero raw demographics, DOBs, or tokens leaked!
print(result.matching_status)  # "matched" | "mismatched" | "unavailable"
print(result.matches)          # {"full_name": True, "date_of_birth": True}

Running the Complete Sandbox Flow

Run the included standalone sandbox walkthrough:

python examples/sandbox_flow.py

This runs the entire end-to-end lifecycle (starts verification -> builds authorization link -> simulates citizen callback -> queries status -> retrieves privacy-preserving boolean result -> verifies caller isolation) without editing library internals.


Examples


Documentation


License

MIT License. See LICENSE for details.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    MCP server for AI agent identity — verify agents with Ed25519 signatures, check trust scores, sign and verify content, exchange encrypted messages. Built on the Agent Identity Protocol (AIP).
    8
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Open-source MCP server that exposes Signet cryptographic tools over stdio. It provides tools to generate Ed25519 keypairs, sign MCP actions, verify Signet receipts, and compute canonical content hashes for AI agent audit and accountability workflows.
    4
    40
    Apache 2.0
  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides AI agents with a DID-based identity, secure wallet, and cloud KMS-backed signing keys, enabling trusted interactions with persons, companies, and other agents via standards like OIDC4VCI, OIDC4VP, and SD-JWT.
    Apache 2.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to prove knowledge of a private key through a three-pass commitment, challenge, and response exchange, paired with SHA-256 Merkle inclusion proofs, JWT HMAC-SHA256 claim validation, constant-time secret comparison, and sliding-window nonce replay protection. Runs as a zero-dependency JSON-RPC 2.0 stdio server compatible with Claude Desktop, Cursor, and Windsurf.
    7
    MIT