AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
Foundry IQ provides a structured framework for creating, diagnosing, and connecting knowledge bases to AI agents. It supports making local or Blob-based documents searchable, managing multi-source knowledge configurations, and retrieving information with citations. The skill emphasizes strict adherence to defined procedures for service discovery, resource creation, and troubleshooting to ensure reliable agent-knowledge integration.
What it does
- Creates searchable knowledge bases from local or Azure Blob storage documents.
- Connects existing knowledge bases to AI agents.
- Diagnoses issues with knowledge base configurations or unsupported connectors.
- Retrieves information from knowledge bases with source citations.
- Manages the lifecycle of search services and knowledge sources.
- Routes requests based on specific operational needs rather than source counts.
When to use it
- When you need to make local or cloud-stored documents searchable by an AI agent.
- When connecting an existing knowledge base to a new or current agent.
- When troubleshooting or diagnosing a knowledge base configuration.
- When you need to create a new search service or knowledge source.
- When performing retrieval tasks that require citations from a knowledge base.
Advantages
- Provides a clear, procedural approach to knowledge base management.
- Ensures consistent handling of multi-source knowledge configurations.
- Includes built-in diagnostic paths for troubleshooting.
- Supports secure, citation-backed retrieval for agent responses.
- Prevents unauthorized or unsupported configuration changes.
Usage examples
- “Create a new knowledge base from my Azure Blob container.”
- The agent will initiate the knowledge base creation procedure, validating the source and setting up the necessary search indexing.
- “Connect my existing knowledge base to the current agent.”
- The agent will follow the connection workflow to link the specified knowledge base to the agent's retrieval pipeline.
- “Why is my multi-source knowledge base failing to index?”
- The agent will trigger the diagnostic procedure to identify configuration drifts or unsupported connector issues.
- “Retrieve information about project timelines from the knowledge base.”
- The agent will query the knowledge base and provide an answer with citations from the source documents.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/microsoft/foundry-iq/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip foundry-iq.zip -d .agents/skills
Documentation by Microsoft
Foundry IQ
Read one procedure before questions/actions; invocation is not a read.
Read the owning procedure before blocked/unsupported responses too.
Before mutation plans/approval, successfully read its required pre-action references, selected
branches only. Do not reload successful reads. On failure, try only permitted
bounded exact-path reads with any supported reader; never bypass restrictions
or search broadly. If still unavailable: blocked: reference-unavailable, name
missing references and inability to plan. Never invent requirements/plans.
Failures first.
Cleanup and receipt-backed execution go directly to their lifecycle/producer owner; do not reopen Search intake, provisioning or hardening.
Before expensive service discovery, reuse supplied resource/intent; otherwise ask one early USE EXISTING / FIND CANDIDATES / CREATE NEW choice. Only FIND enumerates; supplied identity uses exact/minimum scoped resolution. CREATE checks its proposed name, not existing-service inventories. Preserve these answers across source/KB/CU/model handoffs; selection is not write approval.
Searchable docs: KB + validated retrieval. Confirm intent once; KS-only must be explicit. Child success is not KB completion. Unclear/compound/mode/completion: read.
Route by requested operation, not existing KB source count. Connecting an existing KB with two or more sources, without changing the KB, uses Connect. Read-only operations use their owning procedure and actual helper constraints; this does not add retrieval modes or supported source kinds. Explicit unsupported provisioning or multi-source KB creation/reconfiguration uses Diagnose and stops before discovery, even when connecting an agent is also requested. This routing takes precedence over classic-search terms (e.g. hybrid search) in compound requests. Never end in a plain refusal: Diagnose gives the boundary, docs pointer and agent handoff; writes still need an approved concrete plan. Do not silently execute only the supported part of a compound request. If existing-KB connection versus KB creation/reconfiguration is unclear, read intent-routing and clarify that scope before Azure discovery. Never infer KB mutation.
| Outcome | Read |
|---|---|
| Cleanup | Plan cleanup |
| Failure/drift | Diagnose |
| Unsupported connector provisioning / multi-source KB creation or reconfiguration | Diagnose |
| Connect | Connect |
| Read KB | Query |
| Search only | Search |
| File KS only | File |
| Blob/ADLS KS only | Blob |
| Searchable/KB | KB |
Generic agents: microsoft-foundry.
Reads: no approval; approve unchanged plans before writes.
Hide hashes. No Search/Storage keys, scope widening, guessed identity/boundaries,
drift repair or joint cleanup/creation approval. Acceptance != success.
Files
- ATTRIBUTION.mdAdded by AutoGineer977 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SKILL.md3.7 KiB
- agents/connect-hosted-toolbox.md7.8 KiB
- agents/connect-hosted.md7.8 KiB
- agents/connect-prompt-sdk-fallback.md7.8 KiB
- agents/connect.md7.8 KiB
- agents/create-missing-agent.md7.8 KiB
- helpers/_blob_observation.pyScript4.4 KiB
- helpers/_blob_source_read.pyScript39.5 KiB
- helpers/_bootstrap_io.pyScript30.1 KiB
- helpers/_cleanup_dependencies.pyScript27.5 KiB
- helpers/_cleanup_receipts.pyScript13.5 KiB
- helpers/_common.pyScript36.2 KiB
- helpers/_document_adapters.pyScript15.0 KiB
- helpers/_document_limits.pyScript3.9 KiB
- helpers/_embedding.pyScript7.0 KiB
- helpers/_file_inventory.pyScript24.4 KiB
- helpers/_file_source_config.pyScript15.6 KiB
- helpers/_indexer_observation.pyScript7.1 KiB
- helpers/_initial_prompt.pyScript12.5 KiB
- helpers/_private_json.pyScript2.4 KiB
- helpers/_progress.pyScript12.1 KiB
- helpers/_prompt_read.pyScript17.7 KiB
- helpers/_search_read.pyScript6.0 KiB
- helpers/_source_readback.pyScript1.6 KiB
- helpers/blob-contracts.md7.8 KiB
- helpers/blob-cu-contracts.md7.8 KiB
- helpers/blob_inventory.pyScript17.1 KiB
- helpers/blob_recheck.pyScript64.6 KiB
- helpers/blob_source.pyScript35.8 KiB
- helpers/bootstrap-contracts.md7.8 KiB
- helpers/bootstrap_azure.pyScript41.5 KiB
- helpers/cleanup_plan.pyScript41.4 KiB
- helpers/contracts.md7.8 KiB
- helpers/cu_ingestion_auth.pyScript12.5 KiB
- helpers/document-assessment.md7.8 KiB
- helpers/document_assess.pyScript14.0 KiB
- helpers/file-upload-recovery.md7.8 KiB
- helpers/file_cu_canary.pyScript32.7 KiB
- helpers/file_cu_mi.pyScript9.6 KiB
- helpers/file_ingest.pyScript15.4 KiB
- helpers/file_source.pyScript31.7 KiB
- helpers/file_upload.pyScript53.7 KiB
- helpers/hosted_connect.pyScript30.8 KiB
- helpers/kb-contracts.md7.8 KiB
- helpers/knowledge_base_retrieve.pyScript7.7 KiB
- helpers/model-discovery-contracts.md7.8 KiB
- helpers/model-discovery-scopes.md7.8 KiB
- helpers/model_discovery.pyScript23.7 KiB
- helpers/private-artifacts.md7.2 KiB
- helpers/private_artifacts.pyScript4.8 KiB
- helpers/prompt_cleanup.pyScript22.0 KiB
- helpers/prompt_connect.pyScript62.3 KiB
- helpers/requirements-assessment.txt103 B
- helpers/retrieval-verification-contracts.md7.8 KiB
- helpers/retrieval_verify.pyScript19.6 KiB
- helpers/search-intake-contracts.md7.6 KiB
- helpers/search_intake.pyScript18.4 KiB
- helpers/search_reconcile.pyScript59.7 KiB
- helpers/source_vector.pyScript33.2 KiB
- helpers/throttle-recovery.md5.0 KiB
- helpers/vector-contracts.md7.8 KiB
- knowledge-bases/create.md8.1 KiB
- knowledge-bases/retrieve.md7.8 KiB
- knowledge-sources/create-azure-blob.md7.8 KiB
- knowledge-sources/create-file.md7.8 KiB
- lifecycle/cleanup.md7.8 KiB
- references/abstention.md1.0 KiB
- references/blob-binding-evidence.md7.8 KiB
- references/blob-indexer-observation.md7.7 KiB
- references/blob-ingestion-progress.md6.5 KiB
- references/blob-readiness-recheck.md7.8 KiB
- references/blob-vector-readiness.md2.6 KiB
- references/bootstrap-azure.md7.8 KiB
- references/content-fit.md7.8 KiB
- references/cu-ingestion.md7.8 KiB
- references/file-cu-canary.md7.8 KiB
- references/intent-routing.md7.7 KiB
- references/owner.md2.1 KiB
- references/platform-contracts.md7.7 KiB
- references/platform-interfaces.md7.8 KiB
- references/resource-intake.md7.8 KiB
- references/search-substrate.md7.8 KiB
- references/standard-cu.md7.8 KiB
- search-services/create.md7.8 KiB
- troubleshooting/diagnose.md8.7 KiB
helpers/model_discovery.py
"""Bounded metadata-only model/CU intake; never a provisioning or readiness probe."""
from __future__ import annotations
import argparse
import json
import re
import time
from urllib.parse import parse_qs, quote, unquote, urlsplit
try:
from ._bootstrap_io import MAX_BYTES, failure, read_json, run_cli
from ._common import HelperFailure, canonical_bytes
from .bootstrap_azure import _error
except ImportError:
from _bootstrap_io import MAX_BYTES, failure, read_json, run_cli
from _common import HelperFailure, canonical_bytes
from bootstrap_azure import _error
API = "2024-10-01"
HOST = "https://management.azure.com"
GUID = r"[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}"
NAME = r"[A-Za-z0-9][A-Za-z0-9_.()-]{0,89}"
ACCOUNT = re.compile(
rf"/subscriptions/({GUID})/resourceGroups/([^/]{{1,90}})"
rf"/providers/Microsoft.CognitiveServices/accounts/({NAME})", re.I | re.ASCII
)
DEPLOYMENT = re.compile(ACCOUNT.pattern + rf"/deployments/({NAME})", ACCOUNT.flags)
ENDPOINT_SUFFIXES = {
"embedding": ("openai.azure.com", "services.ai.azure.com", "cognitiveservices.azure.com"),
"chat": ("openai.azure.com", "services.ai.azure.com", "cognitiveservices.azure.com"),
"cu": ("services.ai.azure.com",),
}
LIMITS = {"pages": 10, "rows": 200, "endpoints": 10, "command_seconds": 20, "total_seconds": 120}
BOOLEAN_CAPABILITIES = ("embeddings", "chatCompletion")
# Public examples: https://github.com/Azure/azure-cli/blob/dev/src/azure-cli/azure/cli/command_modules/cognitiveservices/tests/latest/recordings/test_cognitiveservices_deployment.yaml
TOKEN_CAPABILITIES = ("maxContextToken", "maxOutputToken")
MAX_TOKEN_LIMIT = 2147483647
ACCOUNT_INVENTORY = "{id:id,name:name,kind:kind,location:location}"
ACCOUNT_METADATA = (
"{id:id,name:name,kind:kind,location:location,properties:"
"(type(properties) == 'object' && "
"{endpoint:properties.endpoint,endpoints:((contains(keys(properties), 'endpoints') && "
"[properties.endpoints] || [`{}`]) | [0]),"
"provisioningState:properties.provisioningState} || `false`)}"
)
def capability_query():
fields = []
for key in BOOLEAN_CAPABILITIES:
value = "properties.capabilities." + key
# JMESPath 0.9.5 equates 1.0/0.0 with true/false; check types before equality.
fields.append(f"{key}:((type({value}) == 'string' && {value} == 'true' || "
f"type({value}) == 'boolean' && {value} == `true`) && 'true' || "
f"(type({value}) == 'string' && {value} == 'false' || "
f"type({value}) == 'boolean' && {value} == `false`) && 'false' || `null`)")
for key in TOKEN_CAPABILITIES:
value = "properties.capabilities." + key
number = f"to_number({value})"
# Canonical decimal integers only; never echo a string based on its field name.
fields.append(f"{key}:({number} > `0` && {number} <= `{MAX_TOKEN_LIMIT}` && "
f"to_string({number}) == to_string({value}) && "
f"!contains(to_string({number}), '.') && to_string({number}) || `null`)")
return ("(type(properties.capabilities) == 'object' && {"
+ ",".join(fields) + "} || `{}`)")
DEPLOYMENT_METADATA = (
"{id:id,name:name,properties:(type(properties) == 'object' && "
"{model:((type(properties.model) == 'object' || !contains(keys(properties), 'model')) && "
"{name:properties.model.name,version:properties.model.version,format:properties.model.format} || `false`),"
"capabilities:" + capability_query() + ",provisioningState:properties.provisioningState} || `false`)}"
)
WARNINGS = [
"Metadata candidates only: not CU defaults/configuration, capacity, effective RBAC, network or model-call proof.",
"Only the selected/default subscription and optional group were searched; no cross-subscription discovery.",
"Public Azure ARM only; bounded discovery is not proof of absence outside its scope.",
"Missing/invalid purpose capabilities are unknown; selection is not model suitability.",
]
def invalid(message="Use exactly the documented discovery fields and selectors."):
return failure("discovery-input-invalid", message)
def scalar(value):
if value is None:
return None
if not isinstance(value, str) or not re.fullmatch(r"[A-Za-z0-9_. ()/-]{1,256}", value):
raise failure("discovery-readback-invalid", "Metadata text is missing or unsafe; raw values withheld.")
return value
def public_capabilities(value):
if not isinstance(value, dict):
return {}
projected = {}
for key in BOOLEAN_CAPABILITIES:
child = value.get(key)
if type(child) is bool:
projected[key] = "true" if child else "false"
elif isinstance(child, str) and child in ("true", "false"):
projected[key] = child
for key in TOKEN_CAPABILITIES:
child = value.get(key)
if type(child) is int:
child = str(child)
if (isinstance(child, str) and re.fullmatch(r"[1-9][0-9]{0,9}", child)
and int(child) <= MAX_TOKEN_LIMIT):
projected[key] = child
return projected
def safe_result(result):
"""Defend the public boundary as well as the CLI/input normalization boundary."""
for row in result["deployments"]:
row["capabilities"] = public_capabilities(row["capabilities"])
if result["selected"] and result["selected"]["deployment"]:
row = result["selected"]["deployment"]
row["capabilities"] = public_capabilities(row["capabilities"])
if len(canonical_bytes(result)) + 1 > MAX_BYTES:
result.update(status="blocked", accounts=[], deployments=[], selected=None)
result["first_failure"] = {
"code": "discovery-limit", "status": None,
"message": "Required output exceeds one MiB; supply resource_group or an exact ID. No absence conclusion.",
"request_id": None, "message_digest": None,
}
return result
def resource_group(value):
# Microsoft.Resources: Unicode letters/decimal digits and _-().; no final period.
return (isinstance(value, str) and 1 <= len(value) <= 90 and not value.endswith(".")
and all(c in "_-()." or c.isalpha() or c.isdecimal() for c in value))
def resource_match(value, *, deployment=False):
pattern = DEPLOYMENT if deployment else ACCOUNT
match = pattern.fullmatch(value) if isinstance(value, str) else None
return match if match and resource_group(match.group(2)) else None
def identity_key(value):
# Do not merge distinct Unicode names through multi-character folds (sharp-s -> ss).
return "".join(folded if len(folded := c.casefold()) == 1 else c.lower() for c in value)
def origin(value, purpose):
if not isinstance(value, str):
return None
suffixes = "|".join(re.escape(suffix) for suffix in ENDPOINT_SUFFIXES.get(purpose, ()))
if suffixes and re.fullmatch(r"https://[a-z0-9][a-z0-9-]{0,62}\.(?:" + suffixes + r")/?", value):
return value.rstrip("/")
return None
def validate(request):
keys = {"schema_version", "purpose", "subscription_id", "resource_group",
"account_id", "account_name", "deployment"}
if not isinstance(request, dict) or not keys <= set(request) or set(request) - keys - {"endpoint"}:
raise invalid()
if request["schema_version"] != "1.0" or request["purpose"] not in ("none", "embedding", "chat", "cu"):
raise invalid()
for field, pattern in (("subscription_id", GUID), ("account_name", NAME)):
value = request[field]
if value is not None and (not isinstance(value, str) or not re.fullmatch(pattern, value)):
raise invalid()
if request["resource_group"] is not None and not resource_group(request["resource_group"]):
raise invalid()
aid, deployment = request["account_id"], request["deployment"]
if aid is not None and not resource_match(aid):
raise invalid()
if deployment is not None and (
not isinstance(deployment, str) or not (re.fullmatch(NAME, deployment) or resource_match(deployment, deployment=True))
):
raise invalid()
if request["purpose"] in ("none", "cu") and deployment is not None:
raise invalid()
dep = resource_match(deployment, deployment=True)
if dep:
parent = deployment.rsplit("/", 2)[0]
if aid and identity_key(aid) != identity_key(parent):
raise invalid("Deployment and account selectors disagree.")
aid = parent
if aid:
sub, group, name = resource_match(aid).groups()
for field, observed in (("subscription_id", sub), ("resource_group", group), ("account_name", name)):
if request[field] and identity_key(request[field]) != identity_key(observed):
raise invalid("Explicit scope and exact resource selector disagree.")
if request.get("endpoint") is not None and (
not origin(request["endpoint"], request["purpose"])
or not (aid or (request["account_name"] and request["resource_group"]))
):
raise invalid("An endpoint selector requires a supported origin and an exact account selector.")
return aid, dep.group(4) if dep else deployment
def endpoint(properties, purpose, selected=None):
"""Endpoint map keys are not a documented CU discriminator."""
values = [("properties.endpoint", properties.get("endpoint"))]
mapping = properties.get("endpoints", {})
if not isinstance(mapping, dict):
raise failure("discovery-readback-invalid", "ARM endpoints metadata is malformed.")
values += [("properties.endpoints", value) for value in mapping.values()]
candidates = {}
for source, value in values:
if value is None:
continue
if not isinstance(value, str):
raise failure("discovery-readback-invalid", "ARM endpoint metadata is malformed.")
# Match an observed origin only; never rewrite hosts, append paths or fetch it.
normalized = origin(value, purpose)
if normalized:
candidates.setdefault(normalized, set()).add(source)
if len(candidates) > LIMITS["endpoints"]:
raise failure("discovery-limit", "More than ten supported endpoint origins; no partial choices.")
choices = sorted(candidates)
if selected is not None:
value = origin(selected, purpose)
if value not in candidates:
return None, [], "endpoint-selection-not-observed", choices
elif len(candidates) != 1:
return None, [], "endpoint-missing-or-ambiguous", choices
else:
value = choices[0]
return value, sorted(candidates[value]), "candidate-only", choices
class Reader:
def __init__(self, cli, clock, *, aggregate=False):
self.cli, self.clock, self.start = cli, clock, clock()
self.page_count, self.row_count, self.byte_count = 0, 0, 0
self.aggregate = aggregate
def call(self, arguments):
remaining = LIMITS["total_seconds"] - (self.clock() - self.start)
if remaining <= 0:
raise failure("discovery-limit", "Discovery deadline reached; narrow scope or supply an exact ID.")
code, out, err = self.cli(arguments, min(LIMITS["command_seconds"], remaining))
if len(out) > MAX_BYTES or len(err) > MAX_BYTES:
raise failure("discovery-limit", "CLI output exceeded one MiB; narrow scope. No absence conclusion.")
if code:
raw = err or out
error = _error(raw)
match = re.search(r"request[\s-]?id[\"'\s:]+(" + GUID + ")",
raw.decode("utf-8", errors="replace"), re.I)
if match:
error.request_id = match.group(1)
raise error
self.byte_count += len(out) + len(err)
if self.aggregate and self.byte_count > MAX_BYTES:
raise failure("discovery-limit", "Aggregate CLI output exceeds one MiB; supply resource_group or an exact ID.")
try:
return json.loads(out)
except (ValueError, UnicodeError, RecursionError):
raise failure("discovery-readback-invalid", "CLI did not return valid JSON; no absence conclusion.")
def get(self, path, sub, projection=ACCOUNT_METADATA):
return self.call(["rest", "--method", "get", "--url", HOST + quote(path, safe="/") + "?api-version=" + API,
"--query", projection, "--subscription", sub])
def pages(self, path, sub, projection=ACCOUNT_INVENTORY):
encoded = quote(path, safe="/")
url, rows, seen = HOST + encoded + "?api-version=" + API, [], set()
# map preserves null/malformed entries and raw counts, unlike a filtering projection.
projection_query = "{value:map(&" + projection + ",value),nextLink:nextLink}"
while self.page_count < LIMITS["pages"]:
if url in seen:
raise failure("discovery-limit", "Repeated ARM page; discovery incomplete.")
seen.add(url)
self.page_count += 1
result = self.call(["rest", "--method", "get", "--url", url,
"--query", projection_query, "--subscription", sub])
if not isinstance(result, dict) or not isinstance(result.get("value"), list):
raise failure("discovery-readback-invalid", "ARM listing is incomplete or malformed.")
rows.extend(result["value"])
self.row_count += len(result["value"])
if self.row_count > LIMITS["rows"]:
raise failure("discovery-limit", "More than 200 aggregate rows; supply resource_group or an exact ID.")
url = result.get("nextLink")
if not url:
return rows
if not isinstance(url, str):
raise failure("discovery-readback-invalid", "ARM nextLink is malformed.")
try:
parts = urlsplit(url)
decoded_path = unquote(parts.path, errors="strict")
except (ValueError, UnicodeError):
raise failure("discovery-readback-invalid", "ARM nextLink is malformed.")
query = parse_qs(parts.query, keep_blank_values=True)
if (any(ord(c) < 33 or ord(c) == 127 or c.isspace() for c in url)
or parts.scheme != "https" or parts.netloc != "management.azure.com"
or identity_key(decoded_path) != identity_key(path) or parts.fragment
or query.get("api-version") != [API]
or set(query) - {"api-version", "$skiptoken", "skiptoken"}):
raise failure("discovery-scope-mismatch", "ARM nextLink escaped the selected collection; not followed.")
url = HOST + encoded + "?" + parts.query
raise failure("discovery-limit", "Ten aggregate pages reached; supply resource_group or an exact ID. No partial choices.")
def account_row(raw, sub, group, purpose, selected_endpoint=None, *, inventory=False):
if not isinstance(raw, dict):
raise failure("discovery-readback-invalid", "Account readback is malformed.")
match = resource_match(raw.get("id"))
if not match or match.group(1).casefold() != sub.casefold() or (
group and identity_key(match.group(2)) != identity_key(group)
):
raise failure("discovery-scope-mismatch", "Account readback does not match the selected scope.")
if not isinstance(raw.get("name"), str) or raw["name"].casefold() != match.group(3).casefold():
raise failure("discovery-readback-invalid", "Account name does not match its ARM ID.")
kind = scalar(raw.get("kind"))
if kind not in (("AIServices",) if purpose == "cu" else ("AIServices", "OpenAI")):
return None
props = {} if inventory else raw.get("properties", {})
if not isinstance(props, dict):
raise failure("discovery-readback-invalid", "Account properties are malformed.")
uri, sources, state, candidates = endpoint(props, purpose, selected_endpoint)
if inventory:
state = "not-assessed"
return {"account_id": raw["id"], "name": match.group(3), "resource_group": match.group(2),
"location": scalar(raw.get("location")), "kind": kind,
"provisioning_state": scalar(props.get("provisioningState")),
"endpoint": uri, "endpoint_sources": sources, "endpoint_state": state,
"endpoint_candidates": candidates, "selection_input": None}
def discover(request, *, cli=run_cli, clock=time.monotonic):
result = {"schema_version": "1.0", "status": "blocked", "purpose": None, "scope": None,
"accounts": [], "deployments": [], "selected": None,
"limits": dict(LIMITS), "warnings": list(WARNINGS), "first_failure": None,
"writes_performed": []}
endpoint_unresolved = False
try:
aid, deployment = validate(request)
result["purpose"] = purpose = request["purpose"]
if purpose == "none":
result.update(status="skipped", warnings=["Model-free route: zero CLI or Azure calls."])
return result
reader = Reader(cli, clock, aggregate=True)
sub, group = request["subscription_id"], request["resource_group"]
if aid:
sub, group, _ = resource_match(aid).groups()
if sub is None:
context = reader.call(["account", "show", "--query", "{id:id}"])
if not isinstance(context, dict) or not re.fullmatch(GUID, str(context.get("id", ""))):
raise failure("discovery-context-unavailable", "No valid signed-in default subscription; no account scan.")
sub = context["id"]
result["scope"] = {"subscription_id": sub, "resource_group": group}
name = request["account_name"]
base = "/subscriptions/" + sub
if group:
base += "/resourceGroups/" + group
if aid is None and name and group:
aid = base + "/providers/Microsoft.CognitiveServices/accounts/" + name
if aid is None:
rows = reader.pages(base + "/providers/Microsoft.CognitiveServices/accounts", sub)
accounts = [account_row(row, sub, group, purpose, inventory=True) for row in rows]
accounts = [row for row in accounts if row and (not name or row["name"].casefold() == name.casefold())]
for candidate in accounts:
candidate["selection_input"] = dict(
request, subscription_id=sub, resource_group=candidate["resource_group"],
account_id=candidate["account_id"], account_name=candidate["name"],
endpoint=candidate["endpoint"])
if len({identity_key(row["account_id"]) for row in accounts}) != len(accounts):
raise failure("discovery-readback-invalid", "Duplicate account IDs; selection is ambiguous.")
result["accounts"] = sorted(accounts, key=lambda row: identity_key(row["account_id"]))
if not name or len(accounts) != 1:
result["status"] = "account-choice-required" if accounts else "no-candidates"
return safe_result(result)
aid = accounts[0]["account_id"]
group = accounts[0]["resource_group"]
raw = reader.get(aid, sub)
row = account_row(raw, sub, group, purpose, request.get("endpoint"))
if row is None or identity_key(row["account_id"]) != identity_key(aid):
raise failure("discovery-scope-mismatch", "Exact account is not a matching model/CU candidate.")
result["accounts"] = [row]
row["selection_input"] = dict(request, subscription_id=sub, resource_group=row["resource_group"],
account_id=aid, account_name=row["name"],
endpoint=row["endpoint"] or request.get("endpoint"))
if row["endpoint"] is None:
endpoint_unresolved = True
raise failure("discovery-endpoint-unresolved",
"Resolve the endpoint using observed endpoint_candidates and selection_input; no deployment read, never guess.")
if purpose == "cu":
result.update(status="selected", selected={"account": row, "deployment": None})
return safe_result(result)
path = aid + "/deployments"
raws = ([reader.get(path + "/" + deployment, sub, DEPLOYMENT_METADATA)] if deployment
else reader.pages(path, sub, DEPLOYMENT_METADATA))
for raw in raws:
if not isinstance(raw, dict) or not isinstance(raw.get("id"), str):
raise failure("discovery-readback-invalid", "Deployment metadata is malformed.")
match = resource_match(raw["id"], deployment=True)
if not match or identity_key(raw["id"].rsplit("/", 2)[0]) != identity_key(aid) or (
deployment and match.group(4).casefold() != deployment.casefold()
):
raise failure("discovery-scope-mismatch", "Deployment readback escaped the selected account/name.")
if not isinstance(raw.get("name"), str) or raw["name"].casefold() != match.group(4).casefold():
raise failure("discovery-readback-invalid", "Deployment name does not match its ARM ID.")
props = raw.get("properties")
if not isinstance(props, dict):
raise failure("discovery-readback-invalid", "Deployment properties metadata is malformed.")
model = props.get("model", {})
if not isinstance(model, dict):
raise failure("discovery-readback-invalid", "Deployment model metadata is malformed.")
capabilities = public_capabilities(props.get("capabilities"))
item = {"deployment_id": raw["id"], "name": match.group(4),
"model_name": scalar(model.get("name")), "model_version": scalar(model.get("version")),
"model_format": scalar(model.get("format")), "capabilities": capabilities,
"provisioning_state": scalar(props.get("provisioningState")),
"selection_input": dict(request, subscription_id=sub, resource_group=group,
account_id=aid, account_name=row["name"], deployment=match.group(4),
endpoint=row["endpoint"])}
result["deployments"].append(item)
result["deployments"].sort(key=lambda row: identity_key(row["deployment_id"]))
if len({identity_key(row["deployment_id"]) for row in result["deployments"]}) != len(result["deployments"]):
raise failure("discovery-readback-invalid", "Duplicate deployment IDs; selection is ambiguous.")
if deployment:
result.update(status="selected", selected={"account": row, "deployment": result["deployments"][0]})
else:
result["status"] = "deployment-choice-required" if raws else "no-candidates"
except HelperFailure as exc:
result.update(status="blocked", selected=None, deployments=[])
if exc.code == "bootstrap-cli-output-limit":
result["warnings"].append(
"Required metadata exceeded discovery bounds; supply resource_group or an exact ID. No absence conclusion.")
if not endpoint_unresolved:
result["accounts"] = []
result["first_failure"] = {"code": exc.code, "status": exc.http_status, "message": exc.message,
"request_id": exc.request_id, "message_digest": getattr(exc, "message_digest", None)}
result["warnings"].extend(exc.warnings)
return safe_result(result)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--input", required=True)
args = parser.parse_args()
try:
request = read_json(args.input)
except HelperFailure as exc:
result = discover({})
result["first_failure"].update(code=exc.code, message=exc.message)
else:
result = discover(request)
print(json.dumps(safe_result(result), sort_keys=True, separators=(",", ":")))
return 2 if result["status"] == "blocked" else 0
if __name__ == "__main__":
raise SystemExit(main())
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Python
The author says: Azure
Security
What our scanner noticed· Medium risk
- References to credential files
- Instructions that use the network
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Microsoft
- Publisher
- Microsoft (Official)
- Source
- microsoft/skills · .github/plugins/foundry-iq-skills/skills/foundry-iq · commit 0696600
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from .github/plugins/foundry-iq-skills/LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Microsoft or Microsoft.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download foundry-iq.zip