--- name: push-inventory-collector description: >- Generate one customer-hosted collector script that lists agents, MCP servers, or REST APIs, uploads each asset ZIP with POST /push, then finishes with POST /commit. TRIGGER when: the user asks to push inventory, generate a push script, How to Push, self-managed scanner, or download the collector skill. Trigger phrases include "push inventory", "generate a push script", "How to Push", "self-managed scanner", "collector skill", "POST /push", "POST /commit". DO NOT TRIGGER when: creating a pull scanner (skill setup-service-scanner) or running a hosted scan (skill run-service-scan-and-view-results). license: Apache-2.0 compatibility: Requires a coding agent that can write files, plus the language the customer names at runtime. allowed-tools: Bash Read Write Edit AskUserQuestion metadata: author: ms-phoenix version: "1.0.0" --- > **Agent context:** For execution instructions, authentication, input types, and x-origin resolution, read [https://dev-portal.mulesoft.com/AGENTS.md](https://dev-portal.mulesoft.com/AGENTS.md). # Push inventory collector Generate one collector script for one self-managed scanner configuration. Do not assume a cloud vendor, a programming language, or a credential shape until the customer provides them. The script is the pull-scanner connection form on their machine, in the language they name. It reads their APIs, builds one payload per asset, uploads each payload, and then commits the scan. The scanner reconciles and publishes. The script does not publish by itself. There is no universal provider credential set. There is no schedule on this scanner. Do not add a schedule, a last-run time, or a "run discovery" step. ## When to Use This Skill **Use this skill when the user asks to:** - Generate a collector script for a self-managed scanner - How to Push, download the collector skill, or push inventory - Write a script that calls POST /push then POST /commit **Trigger keywords:** push inventory, push script, How to Push, self-managed scanner, collector skill, POST /push, POST /commit. **Do NOT use this skill when:** creating a pull scanner that discovers from a connected cloud platform — use skill setup-service-scanner. Running a hosted scan and reviewing staging assets — use skill run-service-scan-and-view-results. ## Prerequisites - Organization id and scanner configuration id copied from the self-managed scanner page - A Connected App with client-credentials grant and `manage:exchange` on that organization - The provider API specification they can paste or attach (OpenAPI, Postman, or a redacted request and response) - Authentication documentation for those APIs, without secret values - A language they can run on their machine ## Reference files - **`references/scanner-contract.md`** — ZIP layout, POST /push multipart, POST /commit body. Read it before writing the upload and commit functions. - **`references/generated-script-standard.md`** — runtime inputs, stderr stages, JSON stdout, mapping fingerprint. Read it when writing the script. - **`references/provider-auth-resolution.md`** — one question per turn, managed-only auth, new-provider contract. Read it when choosing operations and credential fields. ## Workflow ### Rules that always apply 1. **Customer-facing words** — use the table below in every question, in `--help`, in the FAILED block, and in **Ways to run the script**. A wrong verb here is a generation failure. 2. **One question per response** — never combine asset type, inventory, auth, or language. 3. **Never ask for a provider name** — infer the provider from a usable spec. On an unreadable or incomplete spec, ask only the missing list, fetch, handshake, export, or auth piece. 4. **Never reorder** — skip a step only when this chat already answered it with usable data. Ask a step again when the previous answer was unreadable, the wrong kind of file, or missing a required piece. 5. **Do not create a scanner during generation** — do not log in, do not call Anypoint CLI, do not mint a token to discover an organization. Organization id and configuration id are values copied from the scanner page into the inputs file or the parameters. Use these words. Do not hard-code a vendor, a non-production host, or a loopback address into a question or an example host. The example host is `https://anypoint.mulesoft.com`. | Say | Do not say | |---|---| | script | adapter, collector, product command | | input file, parameters, or prompts | three CLIs, CLI mode | | self-managed scanner | self-serve, self-service, build your own | | commit the scan | seal, seal the zip, complete and publish, publish to Exchange | The upload call is `POST .../push`. The finish call is `POST .../commit`. The question is exactly: "Commit this scan? [y/N]". A generated script that calls `POST .../seal`, `POST .../complete`, or `POST .../push-assets` is a generation failure. The JSON body still has a boolean field named `complete`. That field is not the customer sentence. Examples of an inventory (OpenAPI, a Postman collection, a redacted request and response) show the shape. They are not a menu and not a default. Language examples are not a fixed list. Do not default to one language. A script that creates a configuration, calls `anypoint-cli`, or calls `GET /accounts/api/me` in order to invent an organization id is a generation failure. That auto-fill is later work. It is not this script. The only customer questions in this chat are the quoted sentences in Steps 1–5, the missing-piece follow-ups, "Does this look fine?", and the generated script's "Commit this scan? [y/N]". Do not ask which provider. ### Step 1: Ask asset type When the asset type is not already explicit, the whole message is: "What asset type does this scanner configuration accept? Reply with one of: agent, mcp, or rest-api. agent is one agent card. mcp is one MCP server card. rest-api is one API description. One script commits one of these, to one configuration." Then stop. Explicit means they wrote `agent`, `mcp`, or `rest-api`, or a harmless alias (`agents` → `agent`, `api` / `REST APIs` / `APIs` → `rest-api`, `MCP` / `MCP servers` → `mcp`). A provider name, a file name, or an operation name is not an asset type. Do not infer the asset type from a single-type contract. Inferring the type so generation can jump ahead is a generation failure. ### Step 2: Collect inventory Once the type is known, the whole next message is: "Please provide the API specification or documentation for the provider APIs that list and fetch these assets. OpenAPI or Swagger, a Postman or Insomnia collection, RAML, an API Blueprint, a GraphQL schema, an SDK reference, or a redacted request and response are examples, not required formats." Then stop. Do not ask which provider. Infer the provider from a usable spec. Do not invent calls. Do not fetch a spec they did not provide. The catalog later in this file is a shape reminder for a matching paste, not a substitute. Read what they attached. Then stop if it is not yet usable. Ask only the missing piece. One question per response. - Unreadable, empty, unrelated, or not a spec or redacted request and response. Whole message: "This attachment is not a usable specification for listing and fetching these assets. Please attach or paste a valid API spec or a redacted request and response that includes those APIs." - No list API for the type they named. Whole message: "This specification does not include an API that lists {agent|mcp|rest-api} assets. Please attach or paste that list API." - Agent list with no fetch or card API. Whole message: "This specification lists assets but does not include the fetch or card API needed to build the agent zip. Please attach or paste that call." - MCP list with no `initialize` / `tools/list` (or the equivalent handshake). Whole message: "This specification does not include initialize and tools/list (or the equivalent handshake) needed to build an MCP card. Please attach or paste those calls." - REST list with no export or OpenAPI download. Whole message: "This specification does not include the export or OpenAPI download needed to build a REST zip. Please attach or paste that call." A usable inventory has the list call plus the fetch, card, handshake, or export that type needs. Only then ask authentication. Vertex MCP, Gemini MCP, Gemini agents, Azure AI Foundry MCP, and Microsoft 365 Copilot with no customer list docs are enum-only: ask for a customer list call, or stop. If they ask which file to use, they only have this skill and the files they attach; do not name a path from this package. ### Step 3: Collect authentication Once the inventory is usable, the whole next message is: "Please provide the authentication documentation needed to call those APIs. Include the token or signing request, the response, which field is a secret, and how a call is proven to be the right account. Do not paste secret values." Then stop. A long-lived secret, a session token, and a private key are never typed into this chat. They belong in the inputs file or in the environment when the script runs. If that document is missing a piece, ask only that piece. Whole message, fill one gap: "The authentication documentation is missing {the token or signing request|the response|which field is a secret|how a call is proven to be the right account}. Please add that piece. Do not paste secret values." Do not proceed to language until those four are present. Use the authentication document they pasted. Do not open a catalog file from this skill. When they describe Salesforce connected-org (C2C) login or a MuleSoft-managed authorization-code flow, that is not customer-executable: ask for a request and response they can run from their machine. Until they provide one, do not write files. A pasted SigV4 access-key flow still uses the request and response they pasted. Do not copy pull-scanner-only fields such as Hub Role ARN, Spoke Role Name, External ID, Account IDs, or Target Regions into the script. ### Step 4: Ask script language Once auth is known, the whole next message is: "Which script language should I use? Project conventions are optional. Any language you can run is acceptable. Named languages are examples, not a fixed list." Then stop. Do not default to a language. Writing a file before they answer is a failure. If they name a language and omit conventions, write a plain script in that language. Default script stderr the customer sees: the numbered step banner only when that step runs; after a successful list `listed N, matching M, skipped K`; then one line per **matching** asset (`type`, external id, display name); `No matching {ASSET_TYPE} assets found.` when M is 0; FAILED for real failures. Do not print skip ids, `skipping MCP`, or `not MCP` unless `--verbose`. ### Step 5: Show the mapping **[GATE] WAIT for an explicit yes to "Does this look fine?" before writing files.** When type, inventory, auth, and language are known, do not write files in that turn. Show the mapping in this order, then stop. Do not treat dumping the script as having shown the mapping. 1. What was collected, in plain English: the list and detail calls, the auth field names (not values), the language, and the one asset type. 2. One row per inventory call. Columns: Call, Request, Response field, Zip path. 3. One row per field the zip will contain. Columns: Source, Sent as, Required. Source is the operation and response path, or `constant`. If a required Source cannot be pointed at, leave the row blank and ask only for that API or field. Do not guess. If any required Source is blank, stop here. Do not show items 4–8 and do not ask "Does this look fine?" until every required Source is filled. 4. One Representative generated JSON object for a single asset, using values from their inventory when they gave an example, otherwise empty strings. For `agent`, every required A2A v0.3 field has a row (`name`, `description`, `protocolVersion`, `url`, `version`, `capabilities`, `defaultInputModes`, `defaultOutputModes`, `skills`). A v1 card uses `supportedInterfaces` or `supported_interfaces` and has no top-level `url`. The artifact is `a2a-card.json` or `a2a-v1-card.json`. For `mcp`, `specification/mcp-metadata.json` with `transport` required; if capabilities are included, include all four booleans `tools`, `resources`, `prompts`, and `logging`; do not add `serverInfo`. For `rest-api`, `specification/oas.json` with `info` and `paths`; `externalId` is the provider id, and one stage is one asset. 5. How the zip is uploaded after they run the script: `POST {ANYPOINT_BASE}/agent-scanner/api/v1/organizations/{org}/scanner-configurations/{config}/push` is the zip as multipart part `file`, filename `{externalId}.zip`. Not JSON. Finish is a separate `POST .../commit`. The customer question is "Commit this scan? [y/N]". Do not name `/push-assets`, `/complete`, or `/seal`. 6. Script architecture: the function names to write (provider login, list, shape zip, token, upload, optional commit, print status) and the numbered stderr steps below. 7. Flags, environment variables, prompts, and which of those are secret. Same names as **The script**. `--help` three columns: Prompt label, Parameter flag, Input file key. 8. Ask exactly: "Does this look fine?" Do not show Prerequisites or Ways to run the script in this turn. Those come after the files exist. Showing them before they say yes is a generation failure. If they say no, ask which row is wrong, fix the table, and ask again. Still do not write files. If they say yes, write the two files. After the files exist, show Prerequisites, then Ways to run the script in this order: interactive, parameters, input file, and the full `inputs.env` as a fenced block named `inputs.env` so they can download it. Do not ask "Does this look fine?" after writing. That yes is the only approval. The script is not written until they say yes, so a run does not ask them to approve the mapping again and does not refuse live because a fingerprint is pending. Record the approved rows as `MAPPING_FINGERPRINT`: canonical JSON, UTF-8, keys sorted, `{"assetType":"...","rows":[{"source":"...","sentAs":"...","required":true}]}`, rows in the order shown, SHA-256 hex. That value is a record of the chat yes. It is not a second question. Classify with the check described in their inventory. Do not classify by display name. A list that returns more than one kind of asset still yields only the type they named. The other kinds are skipped. A shared AgentCore `ListAgentRuntimes` is not permission to zip both agents and MCP. Agent script: skip `protocolConfiguration.serverProtocol=MCP`, then `GetAgentCard` or the documented synthesized card. MCP script: SigV4 JSON-RPC `initialize`, then `tools/list`; null `initialize` = skip. Name is never classification. REST script lists API Gateway (or their REST inventory), not AgentCore runtimes. A skip is a count, not a default log line: increment `skipped` and continue. Do not print `skipped {id}`, `skipping MCP`, or `not MCP` on stderr unless `--verbose` is set. `--verbose` may print `skipped {id} `. FAILED still prints (list/page errors, HTTP 401/403, missing required zip fields, upload refusals). AgentCore MCP handshake URL is not the Exchange card field `transport.path` (`/mcp`). The invoke URL is `https://bedrock-agentcore.{region}.amazonaws.com/runtimes/{urlencoded agentRuntimeArn}/invocations?qualifier={endpointName}`. `agentRuntimeArn` comes from `get-agent-runtime` / the list item. Encode it with `urllib.parse.quote(arn, safe="")` (every `:`, `/`, and extra character). `endpointName` is `list-agent-runtime-endpoints` `runtimeEndpoints[0].name`, usually `DEFAULT`. No endpoint name means skip that runtime. `Accept` is `application/json, text/event-stream`. Parse JSON or SSE `data:` lines. `initialize` params are `protocolVersion` `2025-03-26`, `capabilities` `{}`, `clientInfo`. HTTP 401/403 on initialize is FAILED. HTTP 400/404/405/415/422 or a missing `result` object is skip. A generated script that POSTs `/runtimes/{agentRuntimeId}/invocations/mcp`, uses the unencoded runtime id in the path, or omits `?qualifier=` is a generation failure. A default run that prints `skipped {id}` for every non-matching runtime is a generation failure. ### Step 6: Write the two files Two files, in the language they named. No markdown guide. The chat already showed the mapping. Prerequisites and Ways to run come after these files exist. 1. The script. One run lists the assets, checks each matching object, uploads each matching one, and then either commits the scan or stops. Zero matching assets stop after list/shape: no MuleSoft login. The asset type is a source constant (`ASSET_TYPE`). The script does not accept `--asset-type`, `SCANNER_ASSET_TYPE`, or an environment variable that switches type. 2. An inputs file, `inputs.env`, written to disk beside the script and shown again as a fenced block named `inputs.env` so the customer can download it. The block contains the whole file. A chat that only prints the command `--input-file inputs.env` is a generation failure. Every secret value is empty. Non-secret placeholders are obvious fakes, never a real id from this chat. Mark each line `(required)` or `(optional)` in a comment. Do not write a pipeline, a container, a schedule, or a second library. `inputs.env` always contains these MuleSoft lines, then the provider keys from the auth they described: ``` # (required) Anypoint host. Example: https://anypoint.mulesoft.com ANYPOINT_BASE= # (required) Organization id, copied from the scanner page ANYPOINT_ORG_ID= # (required) Scanner configuration id, copied from the scanner page SCANNER_CONFIGURATION_ID= # (required) Connected App client id ANYPOINT_CLIENT_ID= # (required) Connected App client secret. Leave empty in chat. Fill this on the machine that runs the script. ANYPOINT_CLIENT_SECRET= # (optional) Commit the scan without a prompt. yes or no. Default no. PUSH_COMMIT= ``` Append one line per provider credential the script will actually ask for, with `(required)` or `(optional)`. Do not copy a credential set from a different provider. A session token, a private key, or any other long secret is `(required)` only when their auth document says the call fails without it. When that document offers more than one credential source, ask which one before the mapping. The script uses only that source. It does not fall back to another. An attached role or a named profile is allowed when they select it. Do not demand an access key for that choice. An unattended run names the source in the inputs file. It does not guess from leftover environment variables. For AWS that line is `AWS_AUTH_MODE` with one of `profile`, `default-chain`, or `access-keys`. The rest of this file is the script contract. Read **The script**, **One input mode**, **ZIP and push**, and `references/scanner-contract.md` before writing. Read `references/generated-script-standard.md` for stderr, JSON stdout, and the mapping fingerprint. Read `references/provider-auth-resolution.md` for managed-only auth. ### Step 7: Verify generation and show how to run After the two files exist, show Prerequisites, then **Ways to run the script** in this order: interactive, parameters, input file, and the full `inputs.env` as a fenced block named `inputs.env` so they can download it. Do not ask "Does this look fine?" after writing. Run the language checker in **Same job, any language** on the generated file. Search for forbidden patterns (`json=` or `"Content-Type": "application/json"` on `/push`, `POST .../seal`, `POST .../complete`, `--client-secret`, `args.[a-z]+-`). If the checker cannot run here, search the file for those patterns. Do not declare completion until the file parses in the named language and those searches are clean. ## Connected App The script logs in to Anypoint with one Connected App: client id and client secret, grant `client_credentials`. It never asks for an Anypoint username or password. An Organization Administrator creates the app once, in Access Management, before they run the script. The script cannot create it. 1. Access Management → Connected Apps → Create App. 2. Type: App acts on its own behalf (client credentials). 3. Add scope `manage:exchange` and apply it to this organization. A token without that scope is rejected. 4. Save. Copy the client id. Store the client secret as `ANYPOINT_CLIENT_SECRET`. Do not paste it into this chat. The self-managed types are offered only when `push-scanner-enabled` is on and the entitlement matches: `agent` for agents, `apiScanners` for MCP and REST. If upload returns that the type is absent, stop and say so. Do not invent an id. Token request, in every language: `POST {ANYPOINT_BASE}/accounts/api/v2/oauth2/token` with `Content-Type: application/x-www-form-urlencoded` and body `grant_type=client_credentials&client_id=...&client_secret=...`. Never JSON. ## What the script does Inside that one run the script prints a numbered step banner on stderr only when that step runs, then stops. Do not split them across programs. Do not print a later banner after the run has stopped. ``` ── Step 1: provider login ── Step 2: list assets ── Step 3: shape zips ── Step 4: MuleSoft login ── Step 5: publish ── Step 6: commit ``` `--dry-run` stops after step 3. There is no MuleSoft login and no secret prompt. That skip is success. After step 3, if zero assets matched `ASSET_TYPE`: - Print `No matching {ASSET_TYPE} assets found.` on stderr (`agent`, `mcp`, or `rest-api`). - Print the JSON object on stdout (`ok` true, that type's count `0`, `skipped` for classified-away and handshake skips, empty publish ids). - Exit 0. - Do not print Step 4, 5, or 6. Do not prompt for the Connected App secret. Do not call `/push` or `/commit`. - `--dry-run` uses this same stop. - Live exception only: `PUSH_ALLOW_EMPTY=yes` continues to `GET .../workspace` and the empty-commit path in step 5. 1. Read inputs. See **One input mode**. 2. Call their list and detail APIs with their auth. Follow pages until the list ends or a page cap is hit. If the list call itself fails, exit 2 and do not commit. After a successful list, print one summary line: `listed N, matching M, skipped K`. Do not print one line per skipped id unless `--verbose`. 3. For each matching asset, build the object from the mapping they accepted. If a required field is missing, print FAILED and do not upload that object. An invalid object is the script's fault. Print each matching asset on its own line: type, external id, display name. Print the zip path `{id}/specification/config.json` plus the artifact. A zip that uses `{id}/config.json` (no `specification/`) is a generation failure. 4. Upload each valid ZIP with `POST {ANYPOINT_BASE}/agent-scanner/api/v1/organizations/{org}/scanner-configurations/{config}/push`. The body is the zip bytes, not JSON. `Content-Type` is `multipart/form-data` with exactly one part named `file`, filename `{externalId}.zip`, part `Content-Type: application/zip`. HTTP 202 JSON is `{assetId, action, workspaceRunId}`. Keep that `workspaceRunId`. Every later upload in this run must return the same id. The server checks the zip again. A refusal is FAILED, with the response body. The same provider id replaces the staged object. It does not create a second row, and Exchange is not called. An id uploaded earlier and absent from this list stays staged until `DELETE .../push/{externalId}`. Omission is not deletion. HTTP 415 means this POST was not a zip part named `file` (JSON body, `application/x-www-form-urlencoded`, `Content-Type: multipart/form-data` with no boundary, or a boundary that starts with `=`). A generated script that posts JSON to `/push`, uses `json=`, sets `Content-Type: application/json` on `/push`, or names the part anything other than `file` is a generation failure. 5. Close the workspace and declare completeness separately. `workspaceRunId` comes only from a 202 `/push` (or `GET .../workspace` when `PUSH_ALLOW_EMPTY=yes` and nothing was uploaded). Inventing `run-…` or posting `/commit` without that id is a generation failure. Zero accepted uploads: do not call `/commit`. - The list stopped at a page cap, or any upload failed, and at least one upload was accepted: `POST .../commit` with `{"workspaceRunId":"","complete":false}`. Do not ask. `complete:false` is upsert only. It must not be `true`. - The list finished and every upload succeeded: ask on a TTY, "Commit this scan? [y/N]". The default is no. `--commit` or `PUSH_COMMIT=yes` is the yes when there is no TTY. Yes sends `POST .../commit` with `"complete":true`. No uploads and does not commit. - An empty successful list does not commit unless they also set `PUSH_ALLOW_EMPTY=yes`. 6. Print one JSON object on stdout: counts, `truncated`, `workspaceRunId`, `scanRunId`, and whether the scan was committed. Exit 0 when nothing failed. Exit 2 when a list, an upload, or a requested commit failed. `complete: true` means this list is the full inventory, so the scanner may retire assets that were not in the list. `complete: false` means upsert only. The upload is one ZIP per asset because that is the API. The customer confirms the mapping and the sample JSON. Do not spend the chat on archive layout. The script still produces a valid ZIP: one directory whose name is the provider's stable id, `specification/config.json` with `name` and `type`, and the artifact next to it (`a2a-card.json` or `a2a-v1-card.json`, `mcp-metadata.json`, or `oas.json`). Live stderr after MuleSoft values resolve (non-secrets in full; secret presence only): ``` Anypoint host: https://anypoint.mulesoft.com Anypoint Organization ID: … Scanner Configuration ID: … Connected App Client ID: … Connected App Client Secret: set ``` Ask only the missing ones. `--dry-run` must not prompt for those Anypoint rows. ## One input mode One run uses one value source. Control flags do not pick a source and do not block prompts. Control flags are `--help`, `--dry-run`, `--verbose`, and `--commit`. Provider fields come from the authentication document they pasted, minus pull-scanner-only hub/spoke fields. AWS, Azure, GCP, Databricks, Snowflake, Kong, and a provider that is not in the catalog use that same rule. Do not copy an AWS field onto another provider. Inside the chosen source, every field uses this fill order. A variable already in the shell fills the value. It does not switch the source, and a blank prompt must not erase it. 1. Command flag already set → use it. Do not prompt. 2. Else the environment variable. 3. Else the `--input-file` / `PUSH_INPUT_FILE` key, when that source is the input file. 4. Else a TTY prompt in this same process. Hide a secret. An empty answer keeps step 2. 5. Else, not a TTY → `FAILED` naming the missing input. Three ways to supply values. They are OR. 1. **Input file.** `--input-file` or `PUSH_INPUT_FILE` is set. Every required value, including secrets, is in that file. A missing required key is FAILED. `--dry-run` and `--commit` may be added. They do not switch the run to parameters mode. 2. **Parameters.** A value flag is on the command line, and no input file is set. Value flags are `--anypoint-base`, `--org`, `--config`, `--client-id`, then only that provider's flags. AWS adds `--profile` and `--region` / `--aws-region`. Azure and GCP do not get `--profile`. Secrets are environment variables, never flags. A missing required value is FAILED. No prompt. 3. **Prompts.** No input file and no value flag, and stdin is a TTY. `--dry-run` alone is this mode. Ask only for a label that is still empty after the environment. A secret already in the environment is not asked again. `--dry-run` does not call the scanner. It does not require the Anypoint host, organization id, configuration id, client id, or client secret. It still needs the provider credentials that list the assets. No file, no value flag, and no TTY → FAILED naming the missing input. ## Pull-form provider login Step 1 looks like the Exchange connection form for **that** provider, not a cloud-CLI tutorial. Derive prompts from the authentication document they pasted. If a current pull form matches that provider, use that credential class's `@Field(label=...)` for the fields the script actually asks. Do not copy an AWS, Azure, or Kong field set onto a different provider. Do not send any of these values to MuleSoft. The push configuration is `authScheme=customer-upload` with no `authParameters`. | Pull scanner form (UI label) | Script prompt | Secret? | |---|---|---| | AWS Access Key ID | `AWS Access Key ID` | no | | AWS Secret Access Key | `AWS Secret Access Key` | yes | | AWS Session Token | hidden, only when the access key is `ASIA` after stripping quotes and the token is not already in the environment or the input file | yes | | AWS Region | `AWS Region` | no | | Tenant ID / Client ID / Client Secret (Azure) | same labels | secret last | | Subscription ID (Azure APIM) | same label | no | | Personal Access Token (Kong) | same label | yes | | GCP Project ID / Service Account Email / Private Key | same labels | private key yes | | Workspace URL + client id/secret (Databricks) | same labels | secret yes | Do not prompt Hub Role ARN, Spoke Role Name, External ID, Account IDs, or Target Regions. Those names exist on the pull scanner form. This script does not assume a role. A `--help` row labeled Hub Role ARN, Spoke Role Name, External ID, Account IDs, or Target Regions is a generation failure. ## Already logged in The laptop CLI/SDK session is the default. Access keys, client secrets, and private keys are the other branch. - **AWS.** `~/.aws/config` / `~/.aws/credentials` already work. `--help` must have the row `AWS Profile | --profile | AWS_PROFILE`. `--profile NAME` uses that profile and does not ask for Access Key, Secret, or Session Token. `--profile` / `AWS_PROFILE` wins over leftover `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` in the environment. Do not tell them to `export AWS_PROFILE`. On a TTY, **AWS Access Key ID** first. Blank, and `--profile` / `AWS_PROFILE` already set: do not ask Secret. Blank, and no profile yet: prompt **AWS Profile** (empty = default chain already on this machine), then **AWS Region** only. Non-TTY: `--profile`, `AWS_AUTH_MODE=default-chain`, or access keys. Blank Access Key with none of those is FAILED; do not silently call STS on leftover environment credentials or a default chain they did not choose. An AWS `--help` that lists Access Key and Region but never `--profile` / `AWS_PROFILE` is a generation failure. - **Azure.** `az` is already logged in. Use that session (DefaultAzureCredential / `az account`). Do not add `--profile`. Do not prompt Tenant ID, Client ID, or Client Secret unless they pasted those as the auth document they will actually use. - **GCP.** `gcloud` is already logged in (ADC). Use that session. Do not add `--profile`. Do not prompt Service Account Email or Private Key unless they pasted the service-account JSON as the auth they will actually use. - **Kong, Databricks, Snowflake, Claude, LangSmith.** No analog of `aws/config`. Use that provider's token fields. Do not invent `--profile`. `--profile` is AWS only. A `--help` row `--profile` on Azure, GCP, Kong, or Databricks is a generation failure. ## AWS login AWS asks for one login, then the region. Nothing else. Order on a TTY: **AWS Access Key ID**, then either the already-logged-in profile path above, or **AWS Secret Access Key** (hidden) when the key is non-empty, then **AWS Session Token** (hidden) only when the unquoted key starts with `ASIA` and the token is not already set, then **AWS Region**. Skipping Access Key ID and jumping to Secret is a generation failure. - Access keys: `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` from the flag/env/file/prompt. Strip wrapping quotes, including `“”‘’`, **before** an `ASIA` check. A check on the raw prompt string is a generation failure. Pass the unquoted key, secret, and token into boto3 together. Do not leave a leftover `AWS_SESSION_TOKEN` in the process environment. If the key is `ASIA` and the token is still empty, `FAILED` names `AWS_SESSION_TOKEN` before any AWS call. - Region: `--region` / `--aws-region` / `AWS_REGION`. One region. Do not ask for a second region list. `--help` provider rows for AWS are only `--profile`, the access-key pair (secret has no flag), and `--region` / `--aws-region`. Required unquote (or the same behavior in the language they named): ```python def _unquote_aws(value): text = (value or "").strip() marks = "\"'" + "\u201c\u201d\u2018\u2019" while len(text) >= 2 and text[0] in marks and text[-1] in marks: text = text[1:-1].strip() return text ``` ASIA check is `_unquote_aws(ack).upper().startswith("ASIA")`. After writing, search the script for `startswith("ASIA")` applied to the raw prompt string. Any such match is a generation failure. AWS inventory paths such as `/ListAgentRuntimes` are boto3 operation names. They are not URLs. Call `boto3.client("bedrock-agentcore-control").list_agent_runtimes` and `boto3.client("bedrock-agentcore").get_agent_card`. A handwritten SigV4 request, a POST to `https://bedrock-agentcore-control.../ListAgentRuntimes`, or `Content-Type: application/x-amz-json-1.1` without the AWS SDK is a generation failure. The 403 body `Unable to determine service/operation name to be authorized` means that mistake. `--region us-east-1 --profile scanner` must reach STS as region `us-east-1` and profile `scanner`. Do not prompt AWS Secret Access Key when Access Key ID is blank and `--profile` is set. Nested Bash `${CLI_REGION:-${AWS_REGION:-x}}}}` is a generation failure (`us-east-1}`). Sequential assignments only. After writing a `.sh`, search for `}}}}` and `--region ${`. ## The script Flags, same spelling in every language: `--dry-run`, `--input-file`, `--anypoint-base`, `--org`, `--config`, `--client-id`, `--commit`, `--verbose`, `--help`. There is no `--asset-type` and no `--client-secret`. `--profile` is AWS only. `--region` / `--aws-region` is only when that provider uses a region. Other provider flags come from their auth document. `--help` exits 0 and shows three columns for every input, in this order of ways to supply it: Prompt label, Parameter flag, Input file key. Connected App fields first, then the already-logged-in row for that cloud if it has one (`AWS Profile` / `az` / `gcloud`), then the fields from their auth document. The client secret has no flag. Help explains `--dry-run` and `--commit`. A `--help` that lists `--client-id` but never mentions **Connected App Client Secret** or `ANYPOINT_CLIENT_SECRET` is a generation failure. Copy this into argparse `epilog` / usage: ``` Inputs and where to obtain them: --anypoint-base / ANYPOINT_BASE: Anypoint host. Scanner path is that host plus /agent-scanner. Example US production: https://anypoint.mulesoft.com --org / ANYPOINT_ORG_ID: organization id from the Exchange scanner page --config / SCANNER_CONFIGURATION_ID: configuration id from that same page --client-id / ANYPOINT_CLIENT_ID: Connected App Client ID Connected App Client Secret: ANYPOINT_CLIENT_SECRET, matching --input-file key, or hidden prompt labeled "Connected App Client Secret". Never a process argument. No --client-secret. AWS scripts also: --profile / AWS_PROFILE: AWS Profile. Already logged in via ~/.aws/config. Wins over leftover AWS_ACCESS_KEY_ID. Not an Azure or GCP flag. AWS Access Key ID: AWS_ACCESS_KEY_ID or prompt. Blank uses --profile / the default chain. AWS Secret Access Key: AWS_SECRET_ACCESS_KEY or hidden prompt. No flag. Skip when Access Key is blank and --profile or the default chain is in use. --region / --aws-region / AWS_REGION: AWS Region Azure scripts: az already logged in (DefaultAzureCredential). Do not add --profile. GCP scripts: gcloud already logged in (ADC). Do not add --profile. ``` Python argparse dests use underscores (`args.anypoint_base`). `args.approve-mapping` is a generation failure. After writing, search the script for `args.[a-z]+-`. Any match is a failure. `--dry-run` builds the objects on disk and does not call the scanner. It does not commit. It does not prove the server will accept the object. The server check happens on upload. Say that in the help text and in **Ways to run the script**. A generated script that posts to `localhost`, `127.0.0.1`, a laptop port, or any URL other than `{ANYPOINT_BASE}/agent-scanner` is a generation failure. There is no `--scanner-base` and no `SCANNER_BASE`. ## Ways to run the script After the two files exist, the chat shows this section in this order, then it stops: interactive, then parameters, then the input file. The command is whatever runs the file they asked for. Do not show a Python, Bash, Java, or Go command unless that is the language they named. The blocks below use `