Skip to main content

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

Project description

sap-mcm-client

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

Go

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

Quickstart

Python

from sap_mcm_client import MCMClient, Division, OverallStatus

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 = 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 = 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 = client.classes.list(division=Division.ELECTRICITY)
    models = client.models.list(include=["market_locations"])

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 = 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.1.tar.gz (107.1 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.1-py3-none-any.whl (52.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: sap_mcm_client-0.0.1.tar.gz
  • Upload date:
  • Size: 107.1 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.1.tar.gz
Algorithm Hash digest
SHA256 c5fbd07a28819a09a07d8dd97da4d53d59a248e6561e223179687df0004ce9f3
MD5 8b558bd7838a0af011c68a1d83ba2ff9
BLAKE2b-256 7112170b9ae42842de78c56d70ec4b8491a420b4ecd14d665bc3f20adc17985d

See more details on using hashes here.

Provenance

The following attestation bundles were made for sap_mcm_client-0.0.1.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.1-py3-none-any.whl.

File metadata

  • Download URL: sap_mcm_client-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 52.2 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 88c381f5e12ac6420f29d4f6ef74300d4dd7ee71c01b816a8cdb565f32d087bb
MD5 1028e9b75aa9aa7abf0f76f1fd2cee62
BLAKE2b-256 0650da8a006c45f6aefb3228a92255a9e291670978a2ffa904f232703671bc69

See more details on using hashes here.

Provenance

The following attestation bundles were made for sap_mcm_client-0.0.1-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