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/_document_adapters.py
"""Bounded lexical, OOXML-part and image-header adapters; no conversion."""
from __future__ import annotations
import codecs
from html.parser import HTMLParser
import io
import json
from pathlib import PurePosixPath
import re
import struct
import sys
import zipfile
import zlib
XML_VERSION = "0.7.1"
MAX_TEXT = 2 * 1024 * 1024
MAX_ENTRIES = 256
MAX_INFLATED = 16 * 1024 * 1024
MAX_RATIO = 100
MAX_NODES = 100_000
MAX_DEPTH = 64
MAX_IMAGE_PIXELS = 100_000_000
CT = "http://schemas.openxmlformats.org/package/2006/content-types"
W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
P = "http://schemas.openxmlformats.org/presentationml/2006/main"
A = "http://schemas.openxmlformats.org/drawingml/2006/main"
S = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
OFFICE = {
"docx": ("/word/document.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"),
"pptx": ("/ppt/presentation.xml", "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"),
"xlsx": ("/xl/workbook.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"),
}
class AdapterBlocked(ValueError):
"""Fixed content-free reason; callers never forward parser exceptions."""
def facts(**values):
return {
"page_count": "not-applicable",
"text_characters": "not-assessed", "text_available": "not-assessed",
"total_images": "not-assessed", "total_drawings": "not-assessed",
"table_structure": "not-assessed", "layout_relationships": "not-assessed",
"input_profile": "unknown", "answerability": "not-assessed",
"ocr_need": "not-assessed", **values,
}
def text_facts(count, basis, **values):
return facts(text_characters=count, text_available=count > 0, text_basis=basis, **values)
class HTMLCounts(HTMLParser):
def __init__(self):
super().__init__(convert_charrefs=True)
self.characters = 0
self.tags = {"img": 0, "table": 0, "tr": 0, "td": 0, "th": 0}
self.suppressed = []
self.nodes = 0
def handle_starttag(self, tag, attrs):
self.nodes += 1
if self.nodes > MAX_NODES:
raise AdapterBlocked("document-node-limit")
if tag in {"script", "style"}:
self.suppressed.append(tag)
if not self.suppressed and tag in self.tags:
self.tags[tag] += 1
def handle_endtag(self, tag):
if self.suppressed and self.suppressed[-1] == tag:
self.suppressed.pop()
def handle_data(self, data):
if not self.suppressed:
self.characters += len(data)
def assess_text(data, hint):
if len(data) > MAX_TEXT:
raise AdapterBlocked("text-input-limit")
try:
text = data.decode("utf-8-sig", errors="strict")
except UnicodeError:
raise AdapterBlocked("unsupported-text-encoding-or-format") from None
if any(ord(char) < 32 and char not in "\t\r\n" for char in text):
raise AdapterBlocked("unsupported-binary-format")
kind = "text" if hint == "auto" else hint
if kind not in {"text", "markdown", "html", "json"}:
raise AdapterBlocked("format-hint-mismatch")
if kind == "json":
def reject_constant(value):
raise AdapterBlocked("nonstandard-json")
def unique_object(pairs):
result = {}
for key, value in pairs:
if key in result:
raise AdapterBlocked("duplicate-json-key")
result[key] = value
return result
try:
value = json.loads(text, parse_constant=reject_constant, object_pairs_hook=unique_object)
except AdapterBlocked:
raise
except (ValueError, UnicodeError):
raise AdapterBlocked("invalid-json") from None
count = nodes = objects = arrays = keys = 0
pending = [(value, 1)]
while pending:
value, depth = pending.pop()
nodes += 1
if nodes > MAX_NODES or depth > MAX_DEPTH:
raise AdapterBlocked("document-node-or-depth-limit")
if isinstance(value, str):
count += len(value)
elif isinstance(value, dict):
objects += 1
keys += len(value)
pending.extend((item, depth + 1) for item in value.values())
elif isinstance(value, list):
arrays += 1
pending.extend((item, depth + 1) for item in value)
return kind, "stdlib-json", text_facts(count, "string-values-only",
objects=objects, arrays=arrays, keys=keys)
if kind == "html":
parser = HTMLCounts()
parser.feed(text)
parser.close()
return kind, "stdlib-HTMLParser", text_facts(
parser.characters, "data-events-excluding-script-style",
html_elements=parser.tags, validation="tolerant-syntax-not-rendering")
return kind, "stdlib-utf8", text_facts(
len(text), "decoded-characters-including-whitespace-and-markup",
markdown_structure="not-assessed" if kind == "markdown" else "not-applicable")
def xml_parser():
try:
import defusedxml
from defusedxml.ElementTree import fromstring
from defusedxml.common import DefusedXmlException
from xml.etree.ElementTree import ParseError
except ImportError:
raise AdapterBlocked("office-parser-dependency-missing") from None
if defusedxml.__version__ != XML_VERSION:
raise AdapterBlocked("office-parser-version-unsupported")
return fromstring, (DefusedXmlException, ParseError)
def bounded_xml(data, parse, errors):
if len(data) > MAX_TEXT:
raise AdapterBlocked("archive-entry-limit")
try:
root = parse(data, forbid_dtd=True, forbid_entities=True, forbid_external=True)
except errors:
raise AdapterBlocked("unsafe-or-invalid-office-xml") from None
pending, nodes = [(root, 1)], 0
while pending:
element, depth = pending.pop()
nodes += 1
if nodes > MAX_NODES or depth > MAX_DEPTH:
raise AdapterBlocked("document-node-or-depth-limit")
pending.extend((child, depth + 1) for child in element)
return root, nodes
def assess_office(data, hint, parse, errors):
try:
with zipfile.ZipFile(io.BytesIO(data)) as archive:
entries = archive.infolist()
if len(entries) > MAX_ENTRIES:
raise AdapterBlocked("archive-entry-count-limit")
names, total = set(), 0
for entry in entries:
path = PurePosixPath(entry.filename)
if (entry.filename in names or path.is_absolute() or ".." in path.parts
or "\\" in entry.filename or ":" in entry.filename):
raise AdapterBlocked("unsafe-or-duplicate-archive-entry")
names.add(entry.filename)
total += entry.file_size
if entry.flag_bits & 1:
raise AdapterBlocked("encrypted-office-archive")
if entry.compress_type not in {zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED}:
raise AdapterBlocked("unsupported-archive-compression")
if (entry.file_size > MAX_TEXT or total > MAX_INFLATED
or entry.file_size > MAX_RATIO * max(entry.compress_size, 1)):
raise AdapterBlocked("archive-inflation-limit")
if entry.filename.lower().endswith("vbaproject.bin"):
raise AdapterBlocked("macro-package-not-assessed")
def read_xml(part):
if part not in names:
raise AdapterBlocked("incomplete-office-package")
with archive.open(part) as source:
content = source.read(MAX_TEXT + 1)
return bounded_xml(content, parse, errors)
if "[Content_Types].xml" not in names:
raise AdapterBlocked("unsupported-zip-format")
types, nodes = read_xml("[Content_Types].xml")
if types.tag != f"{{{CT}}}Types":
raise AdapterBlocked("unsupported-office-namespace")
declared = [(child.get("PartName"), child.get("ContentType")) for child in types]
if any("macroenabled" in (kind or "").lower() for _, kind in declared):
raise AdapterBlocked("macro-package-not-assessed")
kinds = [kind for kind, definition in OFFICE.items() if definition in declared]
if len(kinds) != 1:
raise AdapterBlocked("unsupported-office-profile")
kind = kinds[0]
if hint not in {"auto", kind}:
raise AdapterBlocked("format-hint-mismatch")
main = OFFICE[kind][0][1:]
root, used = read_xml(main)
nodes += used
expected = {"docx": f"{{{W}}}document", "pptx": f"{{{P}}}presentation",
"xlsx": f"{{{S}}}workbook"}[kind]
if root.tag != expected:
raise AdapterBlocked("unsupported-office-namespace")
patterns = {
"docx": r"word/document\.xml",
"pptx": r"ppt/slides/slide[0-9]+\.xml",
"xlsx": r"xl/(worksheets/sheet[0-9]+|sharedStrings|tables/table[0-9]+)\.xml",
}
parts = sorted(part for part in names if re.fullmatch(patterns[kind], part))
if not parts:
raise AdapterBlocked("office-content-parts-unavailable")
characters = tables = image_refs = paragraphs = formulas = cells = 0
for part in parts:
tree, used = (root, 0) if part == main else read_xml(part)
nodes += used
if nodes > MAX_NODES:
raise AdapterBlocked("document-node-limit")
allowed_roots = {
"docx": {f"{{{W}}}document"}, "pptx": {f"{{{P}}}sld"},
"xlsx": {f"{{{S}}}worksheet", f"{{{S}}}sst", f"{{{S}}}table"},
}[kind]
if tree.tag not in allowed_roots:
raise AdapterBlocked("unsupported-office-namespace")
for element in tree.iter():
if element.tag == {"docx": f"{{{W}}}t", "pptx": f"{{{A}}}t",
"xlsx": f"{{{S}}}t"}[kind]:
characters += len(element.text or "")
tables += element.tag in {f"{{{W}}}tbl", f"{{{A}}}tbl", f"{{{S}}}table"}
image_refs += element.tag == f"{{{A}}}blip"
paragraphs += element.tag in {f"{{{W}}}p", f"{{{A}}}p"}
formulas += element.tag == f"{{{S}}}f"
cells += element.tag == f"{{{S}}}c"
result = text_facts(
characters, "stored-text-elements-not-rendered-or-cell-occurrences",
page_count="not-assessed", inspected_xml_parts=len(parts),
table_elements=tables, image_reference_elements=image_refs,
paragraphs=paragraphs if kind != "xlsx" else "not-applicable",
formula_elements=formulas if kind == "xlsx" else "not-applicable",
cell_elements=cells if kind == "xlsx" else "not-applicable",
slide_parts=len(parts) if kind == "pptx" else "not-applicable",
declared_sheets=sum(e.tag == f"{{{S}}}sheet" for e in root.iter())
if kind == "xlsx" else "not-applicable",
coverage={"docx": "main-document-only", "pptx": "all-standard-slide-parts",
"xlsx": "standard-worksheet-shared-string-table-parts"}[kind],
relationships="not-resolved", rendering="not-assessed",
)
return kind, f"stdlib-zipfile+defusedxml=={XML_VERSION}", result
except (zipfile.BadZipFile, zipfile.LargeZipFile, EOFError, zlib.error):
raise AdapterBlocked("invalid-office-archive") from None
def image_facts(kind, width, height, **values):
if not width or not height or width * height > MAX_IMAGE_PIXELS:
raise AdapterBlocked("image-dimension-limit")
return kind, "stdlib-struct-header", facts(
width=width, height=height, text_characters="not-assessed",
text_available="not-assessed", validation="header-only-pixels-not-assessed",
frame_count="not-assessed", **values)
def assess_png(data):
if len(data) < 33 or data[8:16] != b"\0\0\0\rIHDR":
raise AdapterBlocked("invalid-png-header")
width, height, depth, color, compression, filtering, interlace = struct.unpack(">IIBBBBB", data[16:29])
if (zlib.crc32(data[12:29]) != struct.unpack(">I", data[29:33])[0]
or compression or filtering or interlace not in (0, 1)
or depth not in {0: {1, 2, 4, 8, 16}, 2: {8, 16}, 3: {1, 2, 4, 8},
4: {8, 16}, 6: {8, 16}}.get(color, set())):
raise AdapterBlocked("invalid-png-header")
return image_facts("png", width, height, bit_depth=depth, color_type=color)
def assess_jpeg(data):
position = 2
for _ in range(4096):
if position >= len(data) or data[position] != 255:
raise AdapterBlocked("invalid-jpeg-header")
while position < len(data) and data[position] == 255:
position += 1
if position >= len(data):
raise AdapterBlocked("invalid-jpeg-header")
marker = data[position]
position += 1
if marker in (0xDA, 0xD9) or position + 2 > len(data):
raise AdapterBlocked("jpeg-frame-header-unavailable")
length = int.from_bytes(data[position:position + 2], "big")
if length < 2 or position + length > len(data):
raise AdapterBlocked("invalid-jpeg-header")
if marker in (0xC0, 0xC1, 0xC2):
if length < 8:
raise AdapterBlocked("invalid-jpeg-header")
depth, height, width, components = struct.unpack(">BHHB", data[position + 2:position + 8])
if length != 8 + 3 * components or not 1 <= components <= 4 or depth not in (8, 12):
raise AdapterBlocked("invalid-jpeg-header")
return image_facts("jpeg", width, height, precision=depth, components=components)
position += length
raise AdapterBlocked("image-marker-limit")
def assess_document(data, hint, deny_reads):
for encoding in ("utf-8-sig", "cp437", "ascii", "utf-8"):
codecs.lookup(encoding)
office = data.startswith((b"PK\x03\x04", b"PK\x05\x06", b"PK\x07\x08"))
parse, errors = xml_parser() if office else (None, ())
sys.addaudithook(deny_reads)
if office:
return assess_office(data, hint, parse, errors)
if data.startswith(b"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1"):
raise AdapterBlocked("legacy-or-encrypted-office-not-assessed")
if data.startswith(b"\x89PNG\r\n\x1a\n"):
if hint not in {"auto", "png"}:
raise AdapterBlocked("format-hint-mismatch")
return assess_png(data)
if data.startswith(b"\xff\xd8"):
if hint not in {"auto", "jpeg"}:
raise AdapterBlocked("format-hint-mismatch")
return assess_jpeg(data)
return assess_text(data, hint)
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