This release is a pre-release and may not be stable for production use.
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.discoverconnects to cloud-hosted Huawei Cloud MCP servers (experimental, not documented yet).dataruns 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_apimust pass a safety policy (allowlist/denylist); with no policy configured, everything is denied. Rules hot-reload, and the agent can request minimal grants viamanage_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_typeto lock it and send exactly the headers listed inheaders; 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'snotefield warns about this case. _presign_expirestunes validity in seconds (default 900).GetObjectpresigns run oneHEADmetadata pre-check first (object bytes still never pass the gateway). The envelope then carriesexpected_size/expected_etagfor 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=trueexplicitly 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 viamanage_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;oncerules burn within the granting session.permanentrules 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 /healthzserves probes. Audit events (with--audit-file) carry asessionfield 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, setHUAWEICLOUD_MCP_HTTP_HOST=0.0.0.0in 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
--policyconfigured, every execution is denied. - Grant scopes (via
manage_policyadd):once: single execution, burned after usesession(default): this agent session onlytemporary: TTL-based expirypermanent: written to the policy file
- Batch grants:
linealso accepts an array of rules. The whole batch shares one scope; batchaddis all-or-nothing (any invalid line rejects the entire batch, nothing applied); batchremoveis per-rule best-effort (misses are reported per item). The batch envelope carries per-ruleresults; top-levelokis 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-shotapi_session: minimal rule, session-scopedproduct: product-wide, session-scopedreadonly: product read-only rule set (*List*/*Show*/*Get*/*Query*, session-scoped; preferred for browsing tasks)none: no grant
- The default is
offfor 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
hintsfield (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-numbered1. 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 withoutstepsis description-only). Discovery envelopes (get_product/list_apistop-level) always carrysops_index, a lightweight[{name, description?}]list (no steps); the full rendered text (Task:\ncontent, description first, blank line between tasks) comes as the extrasopsstring field, attached only when the tool is called withinclude_sops=true(defaultfalse; no-op when the product has no SOP configured). List items andget_api/get_api_examples/execute_apiare never annotated. - Injected only on successful discovery results, never on denials (policy rejections stay untouched);
get_api_examplesandexecute_apiare never annotated. - Product keys and
apiskeys are case-insensitive; a product value may be a plain string (product note only) or an object withnotes/apis/sops. - Optional top-level boolean
api_notes_in_list_apis(defaulttrue): whenfalse,list_apisitems carry no API-level notes (top-level product notes andget_apimerged notes are unaffected). The generated help-center completion file sets this tofalse, soget_apiremains 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
--hintsand noHUAWEICLOUD_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-rootconfigs/first, then the copy bundled in the installed package, handy foruvx/pipinstalls). - Curated seed: product entries in
configs/help-docs-hints-curation.jsonmay carrysops(passed through as a raw mapping); curated entries surviveapi-refresh helphintsregeneration.
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-indexaccept bare filenames resolved againstconfigs/(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/hideonly affect thelist_apisdiscovery surface;get_api/execute_apialways work. A narrowed discovery list does not refuse detail.off: no governance even with the index mounted. Passing an explicit mode without--deprecated-indexfails 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 withakandskis 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/credentialson 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. Onlyexecute_apineeds 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
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)
| File | Size | Uploaded | |
|---|---|---|---|
| huaweicloud_open_mcp-0.2.0a4.tar.gz | 2.9 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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}
|