Skip to main content

Typed Python client for the SAP Cloud for Utilities Foundation Measurement Concept Management (MCM) APIs

Project description

sap-mcm-client

License: MIT PyPI version Supported Python versions Go Reference Go Report Card

Python Tests Python Coverage Python Lint Python Formatting Go Tests Go Coverage Go Lint

Typed Python and Go client for the SAP Cloud for Utilities Foundation Measurement Concept Management (MCM) OData V4 APIs.

What this does

Provides typed models and an HTTP client that hides the OData V4 protocol behind a clean, domain-specific interface. Instead of constructing raw OData queries with $expand, $filter, and $select, you work with typed Python (Pydantic v2) or Go structs.

Status

Alpha. The type definitions are derived from the SAP MCM OpenAPI specs (v1.1.0) and have not yet been validated against a live SAP system.

Supported APIs

All five APIs of the SAP Cloud for Utilities Foundation package:

API Python Go Operations SAP docs
Measurement Concept Instance CRUD + 4 lifecycle actions + 5 sub-entity updates + 3 notifications API guide · reference
Measurement Concept Class Read-only (list + get) reference
Measurement Concept Model Read-only (list + get) reference
Instance Migration Batch import: migrate + get + list staged + purge + check progress API guide
Time Series 12 read variants + 2 upload + 3 delete reference

Deeper background on the MCM domain itself (Messkonzeptklasse, Messkonzeptmodell, Messkonzeptinstanz):

For a condensed tour of the entity hierarchy and OData conventions, see docs/SPECS_ANALYSIS.md. The SAP OpenAPI specs themselves are not redistributed in this repository (they're SAP IP); see CONTRIBUTING.md for how to download them locally.

Installation

Python

pip install sap-mcm-client

PyPI project page: pypi.org/project/sap-mcm-client

Go

go get github.com/Hochfrequenz/sap-mcm-client/mcm

Module / API docs: pkg.go.dev/github.com/Hochfrequenz/sap-mcm-client/mcm

Quickstart

Python

The Python client is async (built on aiohttp); call it from within an event loop and await each operation.

import asyncio

from sap_mcm_client import MCMClient, Division, OverallStatus


async def main() -> None:
    async with MCMClient(
        base_url="https://c4u-foundation-mcm-service.cfapps.eu10.hana.ondemand.com",
        token_url="https://mysubaccount.authentication.eu10.hana.ondemand.com/oauth/token",
        client_id="...",
        client_secret="...",
    ) as client:
        # List instances with typed filters — no OData query strings needed
        instances = await client.instances.list(
            division=Division.ELECTRICITY,
            overall_status=OverallStatus.ACTIVE,
            top=50,
        )
        for instance in instances.items:
            print(f"{instance.id_text}: {instance.description}")

        # Fetch one instance with full expansion
        instance = await client.instances.get(
            "01234567-89ab-cdef-0123-456789abcdef",
            include=["all"],
        )
        for metering_location in instance.metering_locations:
            for task in metering_location.metering_tasks:
                print(task.register_code)

        # List classes and models
        classes = await client.classes.list(division=Division.ELECTRICITY)
        models = await client.models.list(include=["market_locations"])


asyncio.run(main())

Go

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/Hochfrequenz/sap-mcm-client/mcm"
)

func main() {
    client := mcm.NewClient(mcm.Config{
        BaseURL: "https://c4u-foundation-mcm-service.cfapps.eu10.hana.ondemand.com",
        Auth: mcm.AuthConfig{
            TokenURL:     "https://mysubaccount.authentication.eu10.hana.ondemand.com/oauth/token",
            ClientID:     "...",
            ClientSecret: "...",
        },
    })

    ctx := context.Background()

    // List instances
    top := 50
    instances, err := client.Instances.List(ctx, &mcm.ListOptions{
        Top:    &top,
        Filter: map[string]string{"division_code": "EL", "overallStatus_code": "ACTIVE"},
    })
    if err != nil {
        log.Fatal(err)
    }
    for _, inst := range instances.Items {
        description := ""
        if inst.Description != nil {
            description = *inst.Description
        }
        fmt.Printf("%s: %s\n", inst.IDText, description)
    }

    // Fetch one instance with full expansion (expansion is automatic on Get)
    inst, err := client.Instances.Get(ctx, "01234567-89ab-cdef-0123-456789abcdef")
    if err != nil {
        if mcm.IsNotFound(err) {
            log.Fatal("instance not found")
        }
        log.Fatal(err)
    }
    fmt.Println(len(inst.MeteringLocations), "metering locations")
}

OAuth2 Configuration

The client authenticates against SAP BTP using the OAuth2 Client Credentials flow. You need four values from your SAP subaccount's service binding:

Value Example Where to find it
base_url https://c4u-foundation-mcm-service.cfapps.eu10.hana.ondemand.com Service binding url (in some regions replace eu10 with ap10)
token_url https://<subaccount>.authentication.eu10.hana.ondemand.com/oauth/token Service binding uaa.url + /oauth/token
client_id sb-xsuaa-xxxxx!b12345|mcm-service!b67890 Service binding uaa.clientid
client_secret <generated secret> Service binding uaa.clientsecret

Recommended: store credentials in environment variables and load them via python-dotenv (Python) or os.Getenv (Go). Never commit credentials to the repo.

For the underlying administration details (service instance provisioning, role collections, JWT scopes), see SAP's Administration Guide for the MCM Component.

Limitations

Be honest about what this client can and can't do today:

  • Not yet validated against a live SAP system. All models are derived from the OpenAPI specs downloaded from api.sap.com on 2026-04-13. The real API may have undocumented fields, different error formats, or additional enum values.
  • Test fixtures are spec-derived, not recorded from real responses. A recording script will close this gap in a future version.
  • Enum values may be incomplete. The specs list known codes, but the real system may accept additional values. All enums are typed strings so unknown values still deserialize correctly.
  • No batch support yet. OData $batch requests for atomic multi-entity updates are not implemented.

Error handling

Python

from sap_mcm_client import MCMClient, MCMNotFoundError, MCMForbiddenError

try:
    instance = await client.instances.get("some-uuid")
except MCMNotFoundError:
    print("Instance does not exist")
except MCMForbiddenError as e:
    print(f"Access denied: {e.detail}")

Full exception hierarchy: MCMAPIErrorMCMValidationError (400), MCMAuthenticationError (401), MCMForbiddenError (403), MCMNotFoundError (404). MCMAuthError is raised separately when OAuth2 token acquisition fails.

Go

inst, err := client.Instances.Get(ctx, "some-uuid")
if err != nil {
    switch {
    case mcm.IsNotFound(err):
        fmt.Println("instance does not exist")
    case mcm.IsForbidden(err):
        fmt.Println("access denied")
    default:
        log.Fatal(err)
    }
}

Development

Python

pip install -e ".[tests,linting,type_check,formatting]"
tox -e tests        # pytest
tox -e linting      # pylint (10/10 required)
tox -e type_check   # mypy --strict
tox -e coverage     # coverage >= 80%
tox -e spell_check  # codespell
black . && isort .  # auto-format

Go

go test ./...
golangci-lint run --enable dupl,goconst,gocyclo

Contributing

See CONTRIBUTING.md for the workflow when updating types from new spec versions, and CLAUDE.md for conventions used throughout the codebase.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sap_mcm_client-0.0.2.tar.gz (110.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sap_mcm_client-0.0.2-py3-none-any.whl (54.4 kB view details)

Uploaded Python 3

File details

Details for the file sap_mcm_client-0.0.2.tar.gz.

File metadata

  • Download URL: sap_mcm_client-0.0.2.tar.gz
  • Upload date:
  • Size: 110.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for sap_mcm_client-0.0.2.tar.gz
Algorithm Hash digest
SHA256 003c15980dbe67cba7b1ab963b1bacdeb0c5be05f5bba3525e98b2b185e3a871
MD5 a298ac63bd413dfa2a863ffaaaec2f6e
BLAKE2b-256 ee6a83ce17c744ce86470e3d6e8fff97026d61c8fa5ff6f58c4a97236e4e9b2a

See more details on using hashes here.

Provenance

The following attestation bundles were made for sap_mcm_client-0.0.2.tar.gz:

Publisher: python-publish.yml on Hochfrequenz/sap-mcm-client

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sap_mcm_client-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: sap_mcm_client-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 54.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for sap_mcm_client-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 f0930d2b236fe26af748297afba102ae4d321b2b7b05996083fc6a74f8c469d3
MD5 6a078902b0ba60cbff4bef05932abc38
BLAKE2b-256 a5c8e9991e933c9f68fadcde0ac5937d9569c10becc4c04e34018882fbd5709e

See more details on using hashes here.

Provenance

The following attestation bundles were made for sap_mcm_client-0.0.2-py3-none-any.whl:

Publisher: python-publish.yml on Hochfrequenz/sap-mcm-client

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page