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://mp-staging.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/file_ingest.py
from __future__ import annotations
import argparse
import hashlib
import sys
from pathlib import Path
from typing import Any
try:
from ._progress import Progress, add_progress_argument, reporting
from ._common import (
SEARCH_AUDIENCE, HelperFailure, ReadRecovery, TokenProvider, Transport, azure_cli_token,
blocked_result, digest, emit_result, http_request, load_approved_input,
)
from . import _file_inventory
except ImportError:
from _progress import Progress, add_progress_argument, reporting
from _common import (
SEARCH_AUDIENCE, HelperFailure, ReadRecovery, TokenProvider, Transport, azure_cli_token,
blocked_result, digest, emit_result, http_request, load_approved_input,
)
import _file_inventory
def _confirm_ambiguous_upload(
list_url: str,
token: str,
transport: Transport,
record: dict[str, Any],
plan: dict[str, Any],
request_ids: list[str],
failure: HelperFailure,
warnings: list[str],
) -> dict[str, Any] | None:
"""Resolve an ambiguous upload with bounded readback, never upload replay.
Only ambiguous outcomes (transport failure, timeout, 409/429/5xx) reach
this helper. A definitive 200/201 response never calls it. Returns the
matching server record when the readback proves the approved file
exists, otherwise ``None`` so the caller reports ``partial``.
"""
recovery = ReadRecovery()
try:
recovery.delay(failure)
observed, _ = _file_inventory._list_files(
list_url, token, transport=transport, recovery=recovery,
)
except HelperFailure as read_failure:
warnings.append(f"Upload readback failed ({read_failure.code}); original upload failure retained.")
return None
finally:
request_ids.extend(recovery.request_ids)
warnings.extend(recovery.diagnostics())
matches = [item for item in observed if item.get("fileName") == record["path"]]
if len(matches) != 1 or not _file_inventory._matches(matches[0], plan, record):
return None
return matches[0]
@reporting("file-upload")
def execute(
document: dict[str, Any],
*,
token_provider: TokenProvider = azure_cli_token,
transport: Transport = http_request,
allow_new_uploads: bool = False,
progress: Progress | None = None,
upload_session=None,
source_check=None,
allow_upload_retry=True,
) -> dict[str, Any]:
if allow_new_uploads or upload_session is not None or len(document.get("plan", {}).get("files", [])) > 1:
try:
from .file_upload import run_batch
except ImportError:
from file_upload import run_batch
return run_batch(document, token_provider=token_provider, transport=transport, progress=progress,
allow_new_uploads=allow_new_uploads, session=upload_session,
source_check=source_check, allow_upload_retry=allow_upload_retry)
progress.update("file-inventory")
plan = document["plan"]
fingerprint = document["_computed_fingerprint"]
root, records = _file_inventory._validate_plan(plan)
list_url = _file_inventory._list_url(plan)
token = token_provider(SEARCH_AUDIENCE)
readonly = ReadRecovery() if not allow_new_uploads else None
before, request_ids = _file_inventory._list_files(list_url, token, transport=transport, recovery=readonly)
warnings: list[str] = list(readonly.warnings) if readonly is not None else []
if _file_inventory.inventory_digest(before) != plan.get("expected_server_inventory_digest"):
raise HelperFailure(
"server-inventory-drift",
"Server file inventory changed after approval.",
blocked_at="reconciliation",
warnings=readonly.diagnostics() if readonly is not None else [],
)
matched = _file_inventory.reconcile_inventory(plan, before, allow_new_uploads=allow_new_uploads)
created: list[dict[str, Any]] = []
reused: list[dict[str, Any]] = []
acknowledged_ids: list[str] = []
progress.update("file-upload", uploads_acknowledged=0, files_reused=0)
for record in records:
progress.update("file-upload", uploads_acknowledged=len(created), files_reused=len(reused))
if record["path"] in matched:
reused.append(
{
"fileId": matched[record["path"]].get("fileId"),
"fileName": record["path"],
"sha256": record["sha256"],
}
)
continue
path = _file_inventory._resolve_file(root, record)
try:
content = path.read_bytes()
except OSError as exc:
raise HelperFailure(
"inventory-unreadable",
f"Approved file became unreadable: {record['path']}.",
blocked_at="execution",
writes=created,
resources_remaining=_file_inventory._remaining_files(created),
partial=bool(created),
) from exc
try:
post_read_stat = path.stat()
except OSError as exc:
raise HelperFailure(
"inventory-unreadable",
f"Approved file became unreadable: {record['path']}.",
blocked_at="execution",
writes=created,
resources_remaining=_file_inventory._remaining_files(created),
partial=bool(created),
) from exc
content_digest = "sha256:" + hashlib.sha256(content).hexdigest()
if (
content_digest != record["sha256"]
or len(content) != record["size"]
or post_read_stat.st_mtime_ns != record["mtime_ns"]
):
raise HelperFailure(
"inventory-drift",
f"Approved file changed before upload: {record['path']}.",
blocked_at="confirmation",
writes=created,
resources_remaining=_file_inventory._remaining_files(created),
partial=bool(created),
)
body, boundary = _file_inventory._multipart(plan, record, content, fingerprint)
try:
result = transport(
"POST",
list_url,
token,
body=body,
headers={"Content-Type": f"multipart/form-data; boundary={boundary}"},
)
except HelperFailure as failure:
if failure.http_status is not None and 400 <= failure.http_status < 500 and failure.http_status not in {408, 409, 429}:
raise HelperFailure(
failure.code, failure.message, blocked_at=failure.blocked_at,
writes=created, resources_remaining=_file_inventory._remaining_files(created),
request_id=failure.request_id, status=failure.http_status, partial=bool(created),
) from failure
# The transport outcome is ambiguous (we do not know whether the
# server received the write): attempt one readback before
# concluding partial, per the ambiguous-write contract.
confirmed = _confirm_ambiguous_upload(
list_url, token, transport, record, plan, request_ids, failure, warnings
)
if confirmed is not None:
created.append({"fileName": record["path"], "sha256": record["sha256"]})
if failure.request_id:
request_ids.append(failure.request_id)
continue
uncertain = {
"action": "upload-unverified",
"type": "knowledge-source-file",
"fileName": record["path"],
"sha256": record["sha256"],
}
raise HelperFailure(
failure.code,
failure.message,
blocked_at=failure.blocked_at,
writes=created + [uncertain],
resources_remaining=_file_inventory._remaining_files(created) + [uncertain],
request_id=failure.request_id,
status=failure.http_status,
partial=True,
warnings=warnings,
) from failure
if result.status not in {200, 201}:
ambiguous = result.status in {408, 409, 429} or result.status >= 500
if ambiguous:
confirmed = _confirm_ambiguous_upload(
list_url, token, transport, record, plan, request_ids,
HelperFailure("upload-failed", "Upload response is ambiguous.",
blocked_at="execution", status=result.status,
request_id=result.request_id, retry_after=result.retry_after,
recovery_deadline=result.recovery_deadline),
warnings,
)
if confirmed is not None:
created.append(
{"fileName": record["path"], "sha256": record["sha256"]}
)
if result.request_id:
request_ids.append(result.request_id)
continue
uncertain = {
"action": "upload-unverified",
"type": "knowledge-source-file",
"fileName": record["path"],
"sha256": record["sha256"],
}
raise HelperFailure(
"upload-failed",
f"Upload returned unexpected HTTP {result.status}.",
blocked_at="execution",
writes=created + ([uncertain] if ambiguous else []),
resources_remaining=(
_file_inventory._remaining_files(created) + ([uncertain] if ambiguous else [])
),
request_id=result.request_id,
status=result.status,
partial=bool(created) or ambiguous,
warnings=warnings,
)
# A definitive 200/201 response is not ambiguous: trust it rather than
# re-listing the whole source after every single file. The complete
# inventory is verified once, in bulk, after the loop.
created.append({"fileName": record["path"], "sha256": record["sha256"]})
if result.request_id:
request_ids.append(result.request_id)
acknowledged_ids.append(ReadRecovery.safe_id(result.request_id))
progress.update("file-readback", uploads_acknowledged=len(created), files_reused=len(reused))
ack_warnings = (["Acknowledged upload request IDs: " + ", ".join(acknowledged_ids)]
if acknowledged_ids else [])
recovery = ReadRecovery()
try:
after, after_request_ids = _file_inventory._list_files(
list_url, token, transport=transport, recovery=recovery,
)
except HelperFailure as failure:
raise HelperFailure(
failure.code,
failure.message,
blocked_at=failure.blocked_at,
writes=created + failure.writes,
resources_remaining=(
_file_inventory._remaining_files(created) + failure.resources_remaining
),
resources_reused=failure.resources_reused,
resources_unverified=failure.resources_unverified,
warnings=[*warnings, *ack_warnings, *failure.warnings, *recovery.diagnostics()],
request_id=failure.request_id,
status=failure.http_status,
partial=bool(created or failure.partial),
) from failure
request_ids.extend(after_request_ids)
warnings.extend(recovery.warnings)
if len(after) != len(records):
raise HelperFailure(
"readback-mismatch",
"Final server inventory count differs from the approved inventory.",
blocked_at="verification",
writes=created,
resources_remaining=_file_inventory._remaining_files(created),
request_id=request_ids[-1] if request_ids else None,
partial=bool(created),
warnings=[*warnings, *ack_warnings, *recovery.diagnostics()],
)
verified: list[dict[str, Any]] = []
for record in records:
matches = [item for item in after if item.get("fileName") == record["path"]]
if len(matches) != 1 or not _file_inventory._matches(matches[0], plan, record):
raise HelperFailure(
"readback-mismatch",
f"File readback failed for {record['path']}.",
blocked_at="verification",
writes=created,
resources_remaining=_file_inventory._remaining_files(created),
request_id=request_ids[-1] if request_ids else None,
partial=bool(created),
warnings=[*warnings, *ack_warnings, *recovery.diagnostics()],
)
verified.append(
{
"fileId": matches[0].get("fileId"),
"fileName": record["path"],
"sha256": record["sha256"],
"size": record["size"],
}
)
progress.update("file-readback", files_verified=len(verified))
return {
"status": "completed",
"outcome": str(plan.get("outcome") or "file-knowledge-source-ingestion"),
"approved_plan": {"fingerprint": fingerprint, "confirmed": True},
"resources": {
"created": created,
"reused": reused,
"updated": [],
"skipped": [],
},
"api_contracts": [
{"operation": "upload-file", "version": _file_inventory.API_VERSION, "preview": True}
],
"data_movement": {
"boundary": {"local_root_digest": digest(str(root))},
"result": "exact approved files uploaded directly to Search",
},
"auth": {"mode": "entra-user", "principals": []},
"rbac": plan.get("rbac", {"assignments": []}),
"network": plan.get("network", {"posture": "preserved", "evidence": None}),
"verification": {
"readback": verified,
"server_inventory_digest": digest(_file_inventory._normalize_inventory(after)),
"request_ids": request_ids,
"idempotency": "matching marker metadata is zero-write",
},
"warnings": warnings,
"ownership": {
"run_owned": created,
"reused_not_owned": reused,
"owner": plan.get("owner"),
},
"cleanup": {
"status": "not-requested",
"separate_confirmation_required": True,
},
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--input", type=Path, required=True)
add_progress_argument(parser)
args = parser.parse_args(argv)
fingerprint: str | None = None
owner: Any = None
outcome = "file-knowledge-source-ingestion"
try:
document, plan, fingerprint = load_approved_input(args.input)
document["_computed_fingerprint"] = fingerprint
owner = plan.get("owner")
outcome = str(plan.get("outcome") or outcome)
result = execute(document, progress=Progress("file-upload", enabled=args.progress))
except HelperFailure as failure:
result = blocked_result(
failure,
outcome=outcome,
fingerprint=fingerprint,
owner=owner,
)
emit_result(result)
return 3 if result["status"] == "partial" else 2
emit_result(result)
return 0
if __name__ == "__main__":
sys.exit(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.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download foundry-iq.zip