Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Huawei Cloud Open MCP

English | 中文

PyPI

Huawei Cloud Open MCP

Open Connect. Explore What's Next.

One open, local Model Context Protocol server connects code agents to Huawei Cloud in natural language. It works with opencode, Codex, Cursor, or any other MCP-capable client. There are no per-service wrappers. The agent explores the full catalog (300+ products, 17,000+ APIs) step by step, narrows down to one concrete API call, then executes it with a locally signed request. This is a personal, local deployment. The gateway runs entirely on your machine, and your AK/SK never leave it.

Three composable modes, selected with --mode (comma-separated, e.g. openapi,data):

  • openapi (default) talks to Huawei Cloud OpenAPI.
  • discover connects to cloud-hosted Huawei Cloud MCP servers (experimental, not documented yet).
  • data runs read-only SQL analytics and transformations over inline/local data with DataFusion, pure local compute that needs no credentials and is not governed by the safety policy.

A typical openapi,data workflow pulls a large dataset with execute_api, saves it to a file, then aggregates it with query_data or reshapes it into a new dataset with transform_data. Only the aggregated results or artifact metadata enter the model context.

The gateway serves stdio by default, the form your code agent launches. --transport http adds Streamable HTTP for multi-client deployments; see HTTP transport.

How it works

  • Progressive workflow. The agent explores step by step: search_apis (entity-graph cross-product search when the intent doesn't name a product) → list_products → get_product → list_apis → get_api → (get_api_examples) → execute_api, narrowing 17,000+ APIs to one concrete call. Each step keeps the LLM context bounded; the full guide ships in the server instructions.
  • Metadata-driven, zero SDK. API metadata is fetched live from Huawei Cloud API Explorer and cached in memory. Requests are signed locally (SDK-HMAC-SHA256, self-implemented) and sent straight to Huawei Cloud.
  • Secure by default. Every execute_api must pass a safety policy (allowlist/denylist); with no policy configured, everything is denied. Rules hot-reload, and the agent can request minimal grants via manage_policy.

Quick start

Connect once, and your agent explores the rest.

Prerequisites

  • Python 3.10+ and uv on your PATH (or pip); see Compatibility
  • A code agent: opencode or Codex; any MCP-capable client works
  • A Huawei Cloud Access Key (AK/SK) from a minimal-privilege IAM sub-user (recommended: read-only permissions for what you plan to query)
  • Network access to apiexplorer.cn-north-4.myhuaweicloud.com

Compatibility

Supported, per package metadata:

  • OS: Windows, macOS, Linux. The base package is pure Python plus tantivy (search_apis BM25 engine), which ships native wheels for the major platforms; the optional [datafusion] extra (data mode) ships native wheels for Windows x86_64, macOS x86_64/arm64, and Linux x86_64/aarch64 (manylinux)
  • Python: 3.10+ (requires-python = ">=3.10"); the [datafusion] extra covers the same range

Tested, full unit + integration suite (uv run pytest, e2e excluded; data-mode tests included via the dev dependency group), Linux x86_64:

Python 3.10 3.11 3.12 3.13
Full suite (incl. data mode) pass pass pass pass

Windows and macOS are expected to work. Dependency resolution for those platforms is verified, and the code has no OS-specific branches, but they are not machine-tested; there is no CI matrix yet.

Install

The quick start runs the gateway via uvx, so there is no install step; the first invocation fetches the package automatically. For a persistent install:

pip install huaweicloud-open-mcp                  # or: uv tool install / pipx install — then replace `uvx huaweicloud-open-mcp` with `huaweicloud-open-mcp` in Step 3
pip install "huaweicloud-open-mcp[datafusion]"    # optional extra: data-mode SQL engine

Step 1: Provide credentials

The gateway reads your AK/SK from ~/.huaweicloud/credentials (INI format, [basic] section); on Windows that is %USERPROFILE%\.huaweicloud\credentials. See Credentials for the inline environment-variable alternative.

Create a .huaweicloud directory in your home directory, then a credentials file inside it with the following content:

[basic]
ak = your-access-key-id
sk = your-secret-access-key

# optional — uncomment as needed:
# security_token = <temporary-security-token>
# project_id = <project-id>
# domain_id = <domain-id>

Optional keys (uncomment as needed): security_token (temporary credentials, IAM temporary AK/SK + securitytoken, valid for 15 minutes to 24 hours; attached automatically and included in the request signature), project_id (optional static value; substituted into {project_id} path parameters and the X-Project-Id header), domain_id (global-level services; full support in progress). Keep the file private; it holds your secret: run chmod 600 ~/.huaweicloud/credentials on macOS/Linux; on Windows a file in your user profile is only readable by your account by default. The server reads this file at startup; the log line server start: ... credentials=configured (see --log-file) confirms it was picked up.

Step 2: Create a read-only safety policy

The gateway refuses every execute_api call unless a policy file explicitly allows it. With no policy configured, everything is denied.

Create a policy file, for example hwc-policy.json in your home directory, with the following content:

[
  "ECS:*List*=allow",
  "*=deny"
]

Each rule has the form product:apiPattern=allow|deny, with fnmatch-style wildcards and case-insensitive matching; lines starting with # are comments. The gateway evaluates rules top-down and the first match wins, so this file allows every ECS API whose name contains List and denies everything else. Clients need the absolute path to this file, for example /home/you/hwc-policy.json on macOS/Linux or C:\Users\you\hwc-policy.json on Windows, because they spawn the server with their own working directory.

Step 3: Register the gateway with your code agent

opencode: add to opencode.json (project-level, works on every OS) or the global config (~/.config/opencode/opencode.json on macOS/Linux; on Windows, prefer the project-level file or the OPENCODE_CONFIG environment variable pointing to an absolute path):

{
  "mcp": {
    "huaweicloud": {
      "type": "local",
      "command": [
        "uvx", "huaweicloud-open-mcp",
        "--policy", "/home/you/hwc-policy.json"
      ],
      "enabled": true
    }
  }
}

Codex: add to ~/.codex/config.toml (on Windows: %USERPROFILE%\.codex\config.toml) or run (single line, works in any shell):

codex mcp add huaweicloud -- uvx huaweicloud-open-mcp --policy /home/you/hwc-policy.json
[mcp_servers.huaweicloud]
command = "uvx"
args = ["huaweicloud-open-mcp", "--policy", "/home/you/hwc-policy.json"]

Replace the example path with your own absolute path from Step 2 (on Windows, e.g. C:\Users\you\hwc-policy.json; inside JSON/TOML strings write it with escaped backslashes: "C:\\Users\\you\\hwc-policy.json").

Output: start your agent. The eight gateway tools appear, prefixed with your server name (huaweicloud_search_apis, huaweicloud_list_products, huaweicloud_get_product, huaweicloud_list_apis, huaweicloud_get_api, huaweicloud_get_api_examples, huaweicloud_execute_api, huaweicloud_manage_policy). In Codex, codex mcp list shows the server and /mcp in the TUI confirms it is connected.

Credentials come from Step 1, so no secrets live in the client config. If you prefer inline environment variables instead, see Credentials.

Step 4: Browse the catalog (first real call)

Input (say to your agent):

List the available Huawei Cloud products.

The agent calls list_products, which fetches live metadata from Huawei Cloud API Explorer.

Output (abridged):

{
  "ok": true,
  "total": 310,
  "products": [
    { "product": "ECS", "name": "弹性云服务器", "category": "计算",
      "link": "https://www.huaweicloud.com/product/ecs.html" },
    { "product": "EVS", "name": "云硬盘", "category": "存储",
      "link": "https://www.huaweicloud.com/product/evs.html" }
  ]
}

Step 5: Execute a real read-only API

Input (say to your agent):

List my ECS servers in cn-north-4.

The agent runs the progressive workflow: list_apis(ECS) to find the API, get_api to read its parameters, then execute_api, which signs the request with your AK/SK and sends it to real Huawei Cloud.

Output (abridged):

{
  "ok": true,
  "product": "ECS",
  "api": "ListServersDetails",
  "status": 200,
  "body": {
    "servers": [
      { "name": "ecs-01", "status": "ACTIVE", "id": "1d4e…" }
    ],
    "count": 2
  }
}

"count": 0 with an empty servers list is also a success. Your account simply has no instances in that region; ask again with another region (for example cn-east-3).

On security: requests are signed locally and your SK never leaves your machine; the policy file confines the agent to read-only ECS List* APIs. Widen it deliberately, one pattern at a time; see Safety policy.

No account yet? Mock mode

Run the same flow without credentials: skip Step 1, and add --mock to the server command in Step 3 (uvx huaweicloud-open-mcp --mock --policy <policy-path>, e.g. /home/you/hwc-policy.json or C:\Users\you\hwc-policy.json).

Output: Step 4 works identically; the product catalog is real metadata. Step 5 returns simulated server data shaped exactly like the real response (mock endpoint, no Huawei Cloud account involved).

OBS uploads & downloads

OBS object APIs (PutObject / GetObject / AppendObject / UploadPart) never stream data through the gateway. In real mode, execute_api always answers with a presigned-URL envelope, no flag needed, and the client moves the bytes directly to or from OBS, with no size limit:

{
  "ok": true,
  "presign": {
    "url": "https://<bucket>.obs.<region>.myhuaweicloud.com/<key>?AccessKeyId=...&Expires=...&Signature=...",
    "method": "PUT",
    "expires_in": 900,
    "signed_content_type": "application/octet-stream",
    "headers": { "Content-Type": "application/octet-stream" }
  }
}

Use the URL with any HTTP client; the gateway never sees the data:

curl -X PUT --upload-file big.dat '<url>' -H 'Content-Type: application/octet-stream'

Rules that matter:

  • Content-Type is part of the signature. For uploads, pass _presign_content_type to lock it and send exactly the headers listed in headers; if you don't lock it, the signature assumes no Content-Type, and the direct request must not send one (curl -H 'Content-Type:'). The envelope's note field warns about this case.
  • _presign_expires tunes validity in seconds (default 900).
  • GetObject presigns run one HEAD metadata pre-check first (object bytes still never pass the gateway). The envelope then carries expected_size / expected_etag for post-download verification; a 404 (bucket or object gone, for example a deleted FunctionGraph source bucket) denies the presign outright instead of returning a URL that downloads an XML error page; other pre-check failures degrade gracefully (no expected fields, note explains).
  • All other OBS APIs (bucket management, tagging, ACL, …) execute through the gateway as usual; pass _presign=true explicitly if you want a URL for one of them. Non-OBS products reject _presign. Mock mode keeps hitting the mock endpoint.

HTTP transport (multi-session)

By default the gateway speaks stdio, the local form your code agent launches. It can also serve Streamable HTTP from a single process for multiple concurrent MCP client sessions:

uvx huaweicloud-open-mcp --transport http                  # listens on http://127.0.0.1:8000/mcp
uvx huaweicloud-open-mcp --transport http --http-host 0.0.0.0 --http-port 9000

Point any MCP-capable client at the endpoint:

{ "mcpServers": { "huaweicloud": { "type": "http", "url": "http://127.0.0.1:8000/mcp" } } }

What changes over HTTP:

  • Every client connection gets its own policy session. session-scope rules granted via manage_policy (directly or through elicitation) are visible only to that connection and invisible to every other; reconnecting starts a fresh session with no inherited grants; once rules burn within the granting session. permanent rules land in the policy file and stay shared across sessions and restarts, as always.
  • Requests without MCP session identity (the modern single-exchange protocol) cannot write session-scoped grants; they get a structured rejection instead of silently sharing state.
  • GET /healthz serves probes. Audit events (with --audit-file) carry a session field attributing each call to its MCP session; over stdio the audit output is byte-identical to before.
  • Idle sessions are recycled: a session bucket untouched for 30 minutes is reclaimed, and any grant dies after 24 hours. A session that keeps working keeps its grants alive.

Security posture:

  • By default the gateway binds 127.0.0.1 (loopback also auto-enables the SDK's DNS-rebinding protection). For containers, set HUAWEICLOUD_MCP_HTTP_HOST=0.0.0.0 in the image or run command; exposing the port is already an explicit decision. Binding a non-loopback address prints a warning, because anyone who can reach the port can act with this deployment's AK/SK. Beyond a trusted network, put a reverse proxy with TLS and auth in front. v1 ships no built-in HTTP authentication.
  • Multiple worker processes are not supported (session state lives in the process).

Embedding (ASGI): build_app(...).streamable_http_app() returns a Starlette app whose lifespan runs the MCP session manager. Drive it with any ASGI runner (uvicorn/gunicorn). If you compose the HTTP serving yourself, declare it in the args passed to build_app (--transport http / HUAWEICLOUD_MCP_TRANSPORT=http) so per-session isolation is wired in. Composing HTTP on a stdio-declared deployment would let session-scoped grants from session-less requests share one namespace.

Tools (openapi mode)

All tools return one flat envelope, ok plus tool-specific fields, failures as {"ok": false, "reason": ...}, on both the text content and the structured content channels (absent optional fields may appear as null in structured content).

Tool Purpose
search_apis Entity-graph cross-product search (workflow step 0): when the user intent doesn't name a product, returns candidate products + representative APIs + matched_via evidence (aliases / colloquial keywords / tags); ranking = tantivy BM25 over API text (in-RAM index built at startup) + hand-tuned identity signals; deprecated-API governance mirrors list_apis (annotate / hide); build-time snapshot, mounted via --entity-index
list_products Full Huawei Cloud product catalog — identifier, display name, category, product link; keyword/category filter
get_product One product's details (classification, API count, global vs regional)
list_apis A product's API directory with tag_groups overview; tag/search/limit/offset to narrow
get_api One API's full documentation (parameters, required fields, enums, constraints) — read before executing. Oversized docs (>200k chars) spill the full envelope to disk and stub the heaviest fields in the response
get_api_examples Official request examples for one API
execute_api Execute one API: path/query params flattened, request body under body; errors come back structured, 429 retried with backoff. Oversized responses (>200k chars) are spilled to disk automatically: the result carries a spill envelope (path/format/bytes/note) and body keeps a truncated preview; _spill=false opts out per call. _jsonpath projects the response body via JSONPath (e.g. $.servers[*].id or {"id": "$.servers[0].id"}): on full hit body is replaced with the projected value, on miss the original body is kept plus extract.misses hints. On GET requests, a required query param the caller omitted is auto-filled from the metadata default (e.g. DNS ListPrivateZones fills type=private); the result reports what was filled via applied_defaults, explicit values are never overridden
manage_policy Read/add/remove safety-policy rules at runtime (hot effect, no restart); line accepts a single rule or an array for batch add/remove

Tools (data mode)

Local analytics and transformation on DataFusion (optional extra: pip install "huaweicloud-open-mcp[datafusion]"; the tools return a friendly install hint when missing).

Tool Purpose
query_data Read-only SQL over named tables: {"name": {"data": [objects]}} (inline) or {"name": {"path": "file"}} (local csv/parquet/jsonl/json-array, format auto-detected by extension); returns column schema + JSON-safe rows with row-count/char-budget truncation
transform_data Persist a read-only SQL transformation to a new data file: out = {"path", "format"?} (csv/parquet/jsonl), atomic write, refuse-overwrite by default (overwrite=true to allow); returns artifact metadata (path/format/rows/bytes) + a small preview

Strictly read-only SQL: only SELECT/WITH/EXPLAIN/SHOW/DESCRIBE statements pass the guard; multi-statement and write statements (INSERT/CREATE/COPY TO/…) are rejected. In transform_data the engine applies the write after the guard, via the structured out parameter (audit NDJSON records the write path), never via SQL. Both tools touch no cloud APIs, need no credentials and are not subject to the safety policy. Deploy them only where the agent session is trusted to read (and, for transform_data, write) local files.

Safety policy

A policy file is a JSON array (or plain text) of rules. The gateway evaluates rules top-down; the first match wins:

[
  "ECS:*List*=allow",
  "VPC:*Show*=allow",
  "*=deny"
]
  • Rules have the form product:apiPattern=allow|deny. Matching uses fnmatch-style wildcards, is case-insensitive for product and API, and lines starting with # are comments.
  • With no --policy configured, every execution is denied.
  • Grant scopes (via manage_policy add):
    • once: single execution, burned after use
    • session (default): this agent session only
    • temporary: TTL-based expiry
    • permanent: written to the policy file
  • Batch grants: line also accepts an array of rules. The whole batch shares one scope; batch add is all-or-nothing (any invalid line rejects the entire batch, nothing applied); batch remove is per-rule best-effort (misses are reported per item). The batch envelope carries per-rule results; top-level ok is true only when every rule succeeded.
  • Everything hot-reloads: external edits to the file apply immediately, and so do add/remove via manage_policy. Grant minimal rules first (once/session), product-wide only when justified.
  • Denials return an actionable reason. With --elicitation auto|required, the server proposes a grant over MCP elicitation. Five choices:
    • api: minimal rule, one-shot
    • api_session: minimal rule, session-scoped
    • product: product-wide, session-scoped
    • readonly: product read-only rule set (*List*/*Show*/*Get*/*Query*, session-scoped; preferred for browsing tasks)
    • none: no grant
  • The default is off for predictable cross-client behavior.

A richer example ships with the package: configs/safety-policy.example.json.

Custom hints (optional)

A hints file lets a deployment inject its own guidance into the discovery chain: a global instructions block appended to the server instructions, plus per-product notes and per-API texts attached to discovery results (list_products / get_product / list_apis / get_api). A product-level SOP appears on the get_product / list_apis top-level envelopes as sops_index, a lightweight index (task name and description only), while the full rendered sops text (with steps) is attached only when the caller passes include_sops=true.

{
  "instructions": "This deployment targets ops inspection: prefer List*/Show* APIs for batch lookups.",
  "products": {
    "ECS": {
      "notes": "Prefer ListServersDetails for listing servers.",
      "sops": {
        "Create a server": ["ListFlavors to check availability", "CreateServers to provision", "ShowServer to poll status=ACTIVE"],
        "Resize a server": {
          "description": "Online resize procedure (discovery exposes name + description only; steps via include_sops)",
          "steps": ["ShowServer to confirm", "ResizeServer to submit", "ConfirmResize to finalize"]
        }
      },
      "apis": {
        "ResizeServer": "Check flavor availability with ListFlavors first."
      }
    },
    "OBS": "Object upload/download always returns a presign envelope; the gateway never moves bytes."
  }
}
  • Official metadata is never replaced; hints are returned in an extra hints field (product and API notes merged, product first).
  • Product-level SOP (sops, optional): mapping-only (tasks must be named); a task value is a string (verbatim), an array of strings (auto-numbered 1. x, blank steps dropped and numbering compacted), or a dict {"description"?: str, "steps"?: string | array[string]} (key whitelist {description, steps}, unknown keys fail fast; a task without steps is description-only). Discovery envelopes (get_product / list_apis top-level) always carry sops_index, a lightweight [{name, description?}] list (no steps); the full rendered text (Task:\ncontent, description first, blank line between tasks) comes as the extra sops string field, attached only when the tool is called with include_sops=true (default false; no-op when the product has no SOP configured). List items and get_api / get_api_examples / execute_api are never annotated.
  • Injected only on successful discovery results, never on denials (policy rejections stay untouched); get_api_examples and execute_api are never annotated.
  • Product keys and apis keys are case-insensitive; a product value may be a plain string (product note only) or an object with notes / apis / sops.
  • Optional top-level boolean api_notes_in_list_apis (default true): when false, list_apis items carry no API-level notes (top-level product notes and get_api merged notes are unaffected). The generated help-center completion file sets this to false, so get_api remains the only response that carries these notes.
  • Loaded at startup and hot-reloaded from the config file at runtime (no restart needed); invalid configs fail fast at startup.
  • Default file: with no --hints and no HUAWEICLOUD_MCP_OPENAPI_HINTS, configs/help-docs-hints.json (repo-root copy first, then the one bundled in the installed package) is loaded automatically; if absent it is skipped silently. Pass --hints off (or an empty env value) to disable explicitly. Explicit paths/bare names keep fail-fast semantics on a missing file.
  • The file path supports a bare filename: an existing explicit path (absolute or cwd-relative) is used as-is; otherwise it resolves as configs/<name> (repo-root configs/ first, then the copy bundled in the installed package, handy for uvx/pip installs).
  • Curated seed: product entries in configs/help-docs-hints-curation.json may carry sops (passed through as a raw mapping); curated entries survive api-refresh helphints regeneration.

Example: configs/openapi-hints.example.json (loadable as --hints openapi-hints.example.json).

Help-center description completion (optional, build-time)

api-refresh ships two extra stages (not part of the default refresh range) that collect the richer "功能介绍" sections from the official help center and turn them into a hints file:

uv run api-refresh helpdocs    # crawl + parse help-center pages (sitemap seeds + same-docset link BFS, resumable)
uv run api-refresh helphints   # match to apiexplorer APIs, diff, emit data/help_completions/ + data/hints/help-docs-hints.json
uv run huaweicloud-open-mcp --hints data/hints/help-docs-hints.json

Entity graph and search_apis (build-time)

api-refresh graph deterministically builds the entity association graph from apis_docs.json and huawei_products.json (product/API/tag nodes, same-name twins, attributions, tag coverage), merged with the configs/entity-knowledge.json knowledge base. With --llm, the build also runs LLM extraction of colloquial aliases, product semantic relations and per-API query keywords (ENTITY_LLM_BASE_URL/API_KEY/MODEL; fingerprint-incremental reruns touch only changed products; curated entries are never overwritten):

uv run api-refresh graph            # deterministic build (data/graph/entity-index.json + configs copy shipped in wheel)
uv run api-refresh graph --llm      # build-time LLM extraction (~150 calls, resumable via fingerprints)
uv run huaweicloud-open-mcp         # runtime defaults to configs/entity-index.json (silently disabled if missing)

At runtime search_apis feeds the snapshot through a tantivy BM25 engine. The engine tokenizes the ~18k API texts (ASCII words and camelCase splits; CJK 2-grams) into an in-RAM index built once at startup (~0.6s), answers queries in a few ms, and merges a hand-tuned identity layer (aliases, product names, discriminative tags, product-breadth prior) with the BM25 scores. A human-labeled golden set (tests/fixtures/entity_eval.json, 45 cases) guards ranking quality and runs as part of the test suite.

  • Diff-only: an API enters the file only when the help-center intro is materially richer than the API Explorer description (--min-gain, default 20 chars).
  • Each note carries the full intro (capped via --cap, default 2000 chars, … marker) plus the official doc URL.
  • Rate-limited crawl (0.4s/page) with bot-verification backoff and resumable checkpoints; artifacts are rebuildable and not committed. See AGENTS.md for the full pipeline rules.
  • --hints / --deprecated-index accept bare filenames resolved against configs/ (repo root first, then the bundled package copy).

The same pipeline emits a deprecated-API index (data/help_completions/deprecated.json) sourced from the help center's own (废弃) titles. API Explorer's op.deprecated metadata is unreliable here: in the ECS pilot, the help center surfaces 45 deprecated ECS APIs while API Explorer flags only 1. Mount the index to govern the list_apis discovery surface:

uv run huaweicloud-open-mcp --deprecated-index data/help_completions/deprecated.json                 # annotate (default): list_apis items carry deprecated: true + replacement
uv run huaweicloud-open-mcp --deprecated-index ... --deprecated-mode hide                            # hide: deprecated APIs are filtered from list_apis (counts stay coherent)
  • annotate/hide only affect the list_apis discovery surface; get_api/execute_api always work. A narrowed discovery list does not refuse detail.
  • off: no governance even with the index mounted. Passing an explicit mode without --deprecated-index fails fast at startup.
  • Without --deprecated-index, behavior is byte-for-byte unchanged (no mode means no governance).

Configuration

CLI flags

Flag Default Description
--mode <modes> openapi Run mode(s), comma-separated (openapi/discover/data, e.g. openapi,data; env HUAWEICLOUD_MCP_MODE)
--transport stdio|http stdio Transport: stdio (default, client-launched local form) or http (Streamable HTTP, single process serving multiple MCP sessions; streamable-http normalizes to http; invalid values fail fast; env HUAWEICLOUD_MCP_TRANSPORT)
--http-host <addr> 127.0.0.1 HTTP bind address (loopback auto-enables DNS-rebinding protection; non-loopback warns — see HTTP transport; env HUAWEICLOUD_MCP_HTTP_HOST)
--http-port <port> 8000 HTTP port (env HUAWEICLOUD_MCP_HTTP_PORT)
--http-path <path> /mcp HTTP MCP endpoint path, must start with / (env HUAWEICLOUD_MCP_HTTP_PATH)
--mock off Point execute_api at the API Explorer mock endpoint (no credentials needed)
--mock-base <url> — Mock endpoint base URL override (env HUAWEICLOUD_MCP_MOCK_BASE)
--mock-passthrough off Mock mode: forward execute business params to the mock endpoint (env HUAWEICLOUD_MCP_MOCK_PASSTHROUGH)
--policy <file> — Safety policy file; missing → all executions denied
--region <id> cn-north-4 Default region
--hints <file|off> configs/help-docs-hints.json (silent skip if absent) Optional custom-hints file (deploy-side guidance injected into instructions and discovery results); off disables; bare filename resolves against configs/
--deprecated-index <file> — Optional deprecated-API index; enables list_apis annotate/hide governance; bare filename resolves against configs/
--deprecated-mode <annotate|hide|off> annotate (when index configured) annotate = list_apis items carry deprecated: true + replacement (counts unchanged); hide = deprecated APIs filtered from list_apis before pagination (counts stay coherent); off = no governance; affects list_apis only — get_api/execute_api always work; explicit mode requires --deprecated-index (env HUAWEICLOUD_MCP_DEPRECATED_MODE)
--elicitation auto|required|off off MCP-elicitation confirmation for policy changes
--spill-dir <dir> system temp dir (hwc-mcp-spill) Where oversized responses/envelopes are spilled (empty or off disables spilling; pure truncation returns)
--auth-demote on|off on Auth-header required metadata normalization: at conversion time, required is demoted to false for x-auth-token/x-security-token/authorization (case-insensitive) — auth is always supplied by this gateway's AK/SK signing layer, agents never fabricate tokens; off keeps metadata as-is (env HUAWEICLOUD_MCP_AUTH_DEMOTE)
--auth-demote-pass <list> — Auth-demote exemption list: comma-separated PRODUCT:API (exact) or PRODUCT/PRODUCT:* (product-wide); exempted APIs keep their original metadata; affects metadata normalization only, not the validation layer (env HUAWEICLOUD_MCP_AUTH_DEMOTE_PASS)
--metadata-corrections <path> bundled configs/metadata-corrections.json Metadata corrections: doc-verified factual fixes for stale upstream metadata (first cases: RDS:StartupInstance x-constraint, FunctionGraph:CreateEvent malformed name.pattern), exact PRODUCT:API keys with drop (line-level) / replace (whole-field) patches on envelope/op fields, plus doc-level JSON Pointer patches (v1 whitelist /definitions/) applied copy-on-write before get_api formatting and execute_api validation — cached docs are never mutated; bare filenames resolve like --hints (env HUAWEICLOUD_MCP_METADATA_CORRECTIONS)
--audit-file <file> disabled Audit trail (NDJSON): one {ts, tool, input, ok} line per tool call
--log-level / --log-file INFO / logs/huaweicloud-open-mcp.log Logging (rotating file; stderr mirrors WARNING+)

Environment variables

Variable Purpose
HUAWEICLOUD_SDK_AK / HUAWEICLOUD_SDK_SK Access key / secret key (real mode); see Credentials for the profile-file alternative
HUAWEICLOUD_SDK_SECURITY_TOKEN Optional temporary-security-credential token; attached as X-Security-Token (and x-obs-security-token for OBS) before signing, and included in the request signature
HUAWEICLOUD_SDK_PROJECT_ID Optional static project ID (substituted into {project_id} path parameters and the X-Project-Id header)
HUAWEICLOUD_SDK_DOMAIN_ID Optional; loaded for global-level services (full support in progress)
HUAWEICLOUD_MCP_MODE Same as --mode
HUAWEICLOUD_MCP_TRANSPORT Same as --transport
HUAWEICLOUD_MCP_HTTP_HOST / HUAWEICLOUD_MCP_HTTP_PORT / HUAWEICLOUD_MCP_HTTP_PATH Same as --http-host / --http-port / --http-path
HUAWEICLOUD_MCP_REGION Same as --region
HUAWEICLOUD_MCP_MOCK Same as --mock (1/true/yes)
HUAWEICLOUD_MCP_MOCK_BASE Mock endpoint base URL override
HUAWEICLOUD_MCP_MOCK_PASSTHROUGH Same as --mock-passthrough
HUAWEICLOUD_MCP_POLICY_FILE Same as --policy
HUAWEICLOUD_MCP_OPENAPI_HINTS Same as --hints
HUAWEICLOUD_MCP_DEPRECATED_INDEX Same as --deprecated-index
HUAWEICLOUD_MCP_DEPRECATED_MODE Same as --deprecated-mode
HUAWEICLOUD_MCP_AUDIT_FILE Same as --audit-file
HUAWEICLOUD_MCP_SPILL_DIR Same as --spill-dir
HUAWEICLOUD_MCP_AUTH_DEMOTE Same as --auth-demote (on/off)
HUAWEICLOUD_MCP_AUTH_DEMOTE_PASS Same as --auth-demote-pass
HUAWEICLOUD_MCP_METADATA_CORRECTIONS Same as --metadata-corrections
HUAWEICLOUD_MCP_ELICIT Same as --elicitation
HUAWEICLOUD_MCP_LOG_LEVEL / HUAWEICLOUD_MCP_LOG_FILE Same as --log-level / --log-file

Credentials

The gateway loads AK/SK from two sources, checked in order: environment variables first, then ~/.huaweicloud/credentials (on Windows: %USERPROFILE%\.huaweicloud\credentials). When both are set, environment variables win.

Option A: Profile file (quick-start main path)

~/.huaweicloud/credentials, exactly what Step 1 creates:

[basic]
ak = your-access-key-id
sk = your-secret-access-key

# optional — uncomment as needed:
# security_token = <temporary-security-token>
# project_id = <project-id>
# domain_id = <domain-id>
  • [basic] section with ak and sk is required; the three optional keys mirror the environment variables below.
  • A missing file is silently skipped; the server simply runs without credentials (metadata tools keep working; see below).
  • Keep it private; the file holds your secret: run chmod 600 ~/.huaweicloud/credentials on macOS/Linux; on Windows a file in your user profile is only readable by your account by default.

Option B: Environment variables (inline in client registration)

Set them in the client registration instead of the profile file:

opencode: add an environment block next to command:

"environment": {
  "HUAWEICLOUD_SDK_AK": "your-access-key-id",
  "HUAWEICLOUD_SDK_SK": "your-secret-access-key"
}

Codex: add --env flags before -- (single line, works in any shell):

codex mcp add huaweicloud --env HUAWEICLOUD_SDK_AK=your-access-key-id --env HUAWEICLOUD_SDK_SK=your-secret-access-key -- uvx huaweicloud-open-mcp --policy /home/you/hwc-policy.json
Variable Purpose
HUAWEICLOUD_SDK_AK / HUAWEICLOUD_SDK_SK Required pair
HUAWEICLOUD_SDK_SECURITY_TOKEN Optional temporary-security-credential token; attached as X-Security-Token (and x-obs-security-token for OBS) before signing, and included in the request signature
HUAWEICLOUD_SDK_PROJECT_ID Optional static project ID (substituted into {project_id} path parameters and the X-Project-Id header)
HUAWEICLOUD_SDK_DOMAIN_ID Optional; loaded for global-level services (full support in progress)

Behavior notes

  • Without credentials, metadata tools (list_products, list_apis, get_api, …) keep working; their data comes from the public API Explorer. Only execute_api needs credentials.
  • Use a dedicated minimal-privilege IAM sub-user's AK/SK. The gateway can then only do what that user could do anyway.
  • Signing happens locally; the SK never leaves your machine, and credentials never appear in logs.

Troubleshooting

Symptom Likely cause Fix
Client shows "failed to connect" or the server exits immediately --policy path is relative or the file is missing — the server fails fast on a bad policy file Use an absolute path to a file that exists
The gateway tools never appear in the agent uvx is not on the client's PATH Find it with which uvx (macOS/Linux) or where uvx (Windows) and use the absolute path in place of uvx in the command
Metadata tools work but execute_api fails Credentials not loaded (empty [basic] section, or env vars shadow an empty file) Fix ~/.huaweicloud/credentials (see Credentials) or set the HUAWEICLOUD_SDK_* environment variables
execute_api returns {"ok": false, "reason": ...} mentioning policy The API is not allowed by the policy file (the quick-start file allows only ECS:*List*) Edit the policy file — changes hot-reload without restarting the server — or, after confirming with the user, have the agent add a rule via manage_policy
401 / SignatureDoesNotMatch Wrong AK or SK Recheck the credentials source in use (env wins over profile file)
403 from Huawei Cloud with a permission error The IAM user lacks the permission Grant the minimal IAM policy needed for that API
"count": 0 but you have servers Resources live in another region Ask again with an explicit region, e.g. cn-east-3
Mock calls hang or time out No network route to the API Explorer endpoint Check proxy/firewall access to apiexplorer.cn-north-4.myhuaweicloud.com

For deeper diagnosis, add --log-level DEBUG --log-file <log> to the registration command, for example /tmp/hwc-mcp.log on macOS/Linux or %TEMP%\hwc-mcp.log on Windows, and inspect the log file.

Documentation

Explore the design behind the gateway:

Document Type Language
docs/architecture.md Design overview (layers, modules, logging, tests) 中文
docs/mcp-openapi.md openapi-mode design (workflow, signing, OBS lane) 中文
AGENTS.md Contributor conventions (TDD seams) 中文
benchmarks/README.md Workflow-benchmark design 中文

Development

uv sync                                  # deps (incl. dev)
uv run huaweicloud-open-mcp              # run from source
uv run pytest                            # unit + integration (e2e skipped by default)
uv run pytest -m e2e                     # real-credential E2E
uv run ruff check src tests              # lint
uv run mypy src                          # type check

Companion CLIs: api-refresh (offline APIE pipeline that turns API Explorer data into OpenAPI 2.0 docs; graph builds the entity graph with --llm semantic extraction; help-center completion stages helpdocs/helphints) and api-docs (metadata queries from the terminal). Details in AGENTS.md.

License

Apache-2.0

Metadata

Release files for huaweicloud-open-mcp 0.2.0a4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for huaweicloud-open-mcp 0.2.0a4
File Size Uploaded
huaweicloud_open_mcp-0.2.0a4.tar.gz 2.9 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for huaweicloud-open-mcp 0.2.0a4
File Interpreter ABI Platform
huaweicloud_open_mcp-0.2.0a4-py3-none-any.whl Python 3 none any Details

Total release size: 4.4 MB

Release files / huaweicloud_open_mcp-0.2.0a4.tar.gz

Download URL huaweicloud_open_mcp-0.2.0a4.tar.gz
Size 2.9 MB
Tags Source
SHA-256 checksum
How to use checksums
317790b46abdf0d4a4655c9c7abae64d9b902d50d4f0a17c0011b9ac8fbb4249
BLAKE2b-256 checksum
How to use checksums
164ccfdfdfead83b90922538dd1030b82824ab16da2b77ac41c0f96276323d7a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / huaweicloud_open_mcp-0.2.0a4-py3-none-any.whl

Download URL huaweicloud_open_mcp-0.2.0a4-py3-none-any.whl
Size 1.4 MB
Tags Python 3
SHA-256 checksum
How to use checksums
77f67c78a1085ccac302de88dad4ed96fad2e0c54ce1fa5e95ee47c8345d964d
BLAKE2b-256 checksum
How to use checksums
f703bd6db02a9774a298e8a08b6a6a01b696e35f1149a10757e96ea830477f72
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page