agentleFS
bcastelinobcastelino/powerbi-dashboard-generator/skills/data-source-connector/SKILL.md

data-source-connector

Skill1 starsChanged 6 months agoMITView on GitHub

Source-agnostic adapter that introspects any data storage (SQL databases, cloud warehouses, Excel/CSV files, OData/REST APIs, SharePoint lists) and emits a normalized data-model.json describing tables, columns, types, and relationships. Use this skill at the start of the dashboard pipeline whenever the user has not provided a structured data model. It also emits a clarification question list when schema cannot be auto-discovered, and produces the source-specific M-Code / import block consumed by semantic-mapper.

The file

We split this skill into the rules it gives an agent

7 rules. About 0 are common, 2 are distinctive, and 5 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 5

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    If schema cannot be fully discovered (missing credentials, ambiguous grain, no FK constraints), emit openQuestions[] for the orchestrator …

    line 119
  2. Very specific:

    Run scripts/introspect_source.py (or the source-specific adapter) to:

    line 137
  3. Distinctive:

    If the source supports FK constraints (SQL databases) → use them directly

    line 143
  4. Very specific:

    If unsure, add to openQuestions[] and ask the user at Gate A.

    line 158
  5. Distinctive:

    Every table has at least one column

    line 255
  6. Very specific:

    Every relationship references existing {table, column} on both sides

    line 256
  7. Very specific:

    Every column has both dataType and sourceProviderType

    line 257

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Data Source Connector

Universal data-source adapter for the Power BI dashboard pipeline. This skill abstracts away the specifics of every supported source and produces a single, normalized artifact — data-model.json — that downstream skills (semantic-mapper, visual-selector, visual-generator) can consume without knowing where the data actually came from.

When to Use This Skill

  • The orchestrator (or the user) has identified a source but no structured data model exists
  • The agent needs to enumerate tables, columns, types, and relationships from any backend
  • A Genie YAML metric view is not available (use this skill instead of semantic-mapper's YAML path)
  • The agent needs the source-specific M-Code / connection block to embed in TMDL partitions

Supported Sources

Source Typetype valueRequired Inputs
Databricksdatabrickshostname, warehouse_id, catalog, schema
Snowflakesnowflakeaccount, warehouse, database, schema, role
BigQuerybigqueryproject, dataset
Azure Synapsesynapseserver, database
SQL Serversqlserverserver, database
PostgreSQLpostgreshost, port, database, schema
MySQLmysqlhost, port, database
Oracleoraclehost, port, service_name
Excelexcelpath (.xlsx file); each sheet becomes a table
CSVcsvpath or directory of CSVs; each file becomes a table
Parquetparquetpath or directory
ODataodataservice_url
REST APIrestbase_url, endpoints[], auth
SharePoint Listssharepointsite_url, list_names[]

Inputs

  • Source descriptor — { type, ...connection params } from the orchestrator or the user
  • Scope hint (optional) — which tables / sheets / endpoints to include (defaults to all)
  • Sampling preference (optional) — number of sample rows to capture per table (default 5)

Outputs

Primary: data-model.json

Source-agnostic, normalized representation. Schema:

{
  "source": {
    "type": "excel",
    "connection": { "path": "C:/data/sales.xlsx" },
    "discoveredAt": "2026-05-13T11:00:00Z"
  },
  "tables": [
    {
      "name": "fact_sales",
      "physicalName": "Orders",
      "role": "fact",
      "grain": "one row per order line",
      "rowCountEstimate": 50000,
      "columns": [
        {
          "name": "order_id",
          "physicalName": "OrderID",
          "dataType": "int64",
          "sourceProviderType": "bigint",
          "isPrimaryKey": true,
          "nullable": false
        },
        {
          "name": "customer_key",
          "physicalName": "CustomerID",
          "dataType": "int64",
          "sourceProviderType": "bigint",
          "isForeignKey": true,
          "foreignKey": { "table": "dim_customer", "column": "customer_key" }
        },
        {
          "name": "total_value",
          "physicalName": "TotalValue",
          "dataType": "double",
          "sourceProviderType": "double",
          "formatHint": "currency"
        }
      ],
      "sampleRows": [
        { "order_id": 1, "customer_key": 42, "total_value": 199.99 }
      ]
    }
  ],
  "relationships": [
    {
      "from": { "table": "fact_sales", "column": "customer_key" },
      "to":   { "table": "dim_customer", "column": "customer_key" },
      "cardinality": "many-to-one",
      "isActive": true,
      "inferredFrom": "naming convention"
    }
  ],
  "mCodeAdapter": {
    "mode": "import",
    "templates": {
      "fact_sales": "let Source = Excel.Workbook(File.Contents(\"C:/data/sales.xlsx\"), null, true), Orders_Sheet = Source{[Item=\"Orders\",Kind=\"Sheet\"]}[Data], #\"Promoted Headers\" = Table.PromoteHeaders(Orders_Sheet, [PromoteAllScalars=true]) in #\"Promoted Headers\""
    }
  },
  "openQuestions": [
    {
      "id": "q1",
      "scope": "relationship",
      "question": "Is the relationship between fact_sales.customer_key and dim_customer.customer_key correct? It was inferred from column-name similarity, not from a foreign key constraint."
    }
  ]
}

Secondary: Clarification report

If schema cannot be fully discovered (missing credentials, ambiguous grain, no FK constraints), emit openQuestions[] for the orchestrator to surface to the user.

Workflow

Step 1: Validate Connection Inputs

For each source type, check that required inputs are present. If any are missing, emit a clarification question and stop.

Examples:

  • databricks missing warehouse_id → ask: "What is the Databricks SQL warehouse ID?"
  • excel missing path → ask: "What is the full path to the Excel file?"
  • sqlserver missing credentials → ask: "Is this a trusted-connection database, or do I need a username and password?"

See references/clarification-questions.md for the full question bank.

Step 2: Probe the Source

Run scripts/introspect_source.py (or the source-specific adapter) to:

  1. List tables / sheets / endpoints
  2. For each table: list columns with native types, nullability, primary key flags
  3. Sample rows: pull 5 sample rows per table (configurable)
  4. Foreign key discovery:
    • If the source supports FK constraints (SQL databases) → use them directly
    • Otherwise → infer from column-name patterns (<table>_key, <table>_id, identical names across tables) and flag as inferredFrom: naming convention for user confirmation at Gate A

Step 3: Classify Tables (Fact vs. Dimension)

Heuristics:

SignalLikely Role
Has multiple FK columns + a numeric measure columnfact
Name contains fact_, sales, orders, transactions, eventsfact
Name contains dim_, customers, products, dates, geographydimension
Only one PK column + descriptive columnsdimension
Has a date column with daily continuitydate dimension

If unsure, add to openQuestions[] and ask the user at Gate A.

Step 4: Normalize Types

Map source-native types to TMDL types (consumed by semantic-mapper):

Source TypedataTypesourceProviderType
STRING, VARCHAR, NVARCHAR, TEXTstringnvarchar(65535)
INT, INTEGER, INT32int64int
BIGINT, LONGint64bigint
DOUBLE, FLOAT, REAL, DECIMAL, NUMERICdoubledouble
DATEdateTimedate
DATETIME, TIMESTAMP, DATETIME2dateTimedatetime2
BOOLEAN, BITbooleanbit

For Excel/CSV with no declared types, sniff from sample rows.

Step 5: Generate the M-Code Adapter Block

Each source has a different M-Code template. Per-source templates live in references/connection-patterns.md. Examples:

Databricks (DirectQuery):

let
    Source = DatabricksMultiCloud.Catalogs("<hostname>", "/sql/1.0/warehouses/<warehouse_id>", [Catalog = "", Database = ""]),
    <catalog>_Database = Source{[Name="<catalog>",Kind="Database"]}[Data],
    <schema>_Schema = <catalog>_Database{[Name="<schema>",Kind="Schema"]}[Data],
    <table>_Table = <schema>_Schema{[Name="<table>",Kind="Table"]}[Data]
in
    <table>_Table

Excel (Import):

let
    Source = Excel.Workbook(File.Contents("<path>"), null, true),
    <sheet>_Sheet = Source{[Item="<sheet>",Kind="Sheet"]}[Data],
    #"Promoted Headers" = Table.PromoteHeaders(<sheet>_Sheet, [PromoteAllScalars=true])
in
    #"Promoted Headers"

CSV (Import):

let
    Source = Csv.Document(File.Contents("<path>"), [Delimiter=",", Columns=<n>, Encoding=65001, QuoteStyle=QuoteStyle.Csv]),
    #"Promoted Headers" = Table.PromoteHeaders(Source, [PromoteAllScalars=true])
in
    #"Promoted Headers"

SQL Server (DirectQuery):

let
    Source = Sql.Database("<server>", "<database>"),
    <schema>_<table> = Source{[Schema="<schema>",Item="<table>"]}[Data]
in
    <schema>_<table>

The connector emits one M-Code block per table into data-model.json → mCodeAdapter.templates.

Step 6: Mode Selection

Set mCodeAdapter.mode based on source:

SourceMode
Databricks, Snowflake, BigQuery, Synapse, SQL Server (large), OracledirectQuery
Excel, CSV, Parquet, SharePoint List, REST APIimport
Postgres, MySQLdirectQuery if user opts in, otherwise import

Step 7: Emit openQuestions

Append a question for every uncertainty:

  • Ambiguous fact/dimension classification
  • Inferred (not declared) foreign keys
  • Multiple date columns (which is the primary date?)
  • Tables with no measurable columns (skip or include?)
  • Files with multiple sheets where some look like junk (e.g., metadata, instructions)

Outputs Handed to Downstream

FileConsumerPurpose
data-model.jsonsemantic-mapper, visual-selector, nlq-dashboard-orchestratorNormalized model
data-model.json.openQuestions[]nlq-dashboard-orchestrator (Gate A)Drive clarification dialog
data-model.json.mCodeAdaptersemantic-mapperTMDL partition source blocks

Validation Checklist

  1. Every table has at least one column
  2. Every relationship references existing {table, column} on both sides
  3. Every column has both dataType and sourceProviderType
  4. Exactly zero or one date-dimension table is flagged as role: "date dimension" per date role
  5. At least one table is classified as fact
  6. mCodeAdapter.templates has one entry per table in tables[]
  7. openQuestions is empty OR every entry has a unique id and a non-empty question

Error Handling

ErrorResolution
Cannot reach sourceSurface error verbatim; ask user for corrected connection params
Authentication failedAsk user for credentials; never store them in data-model.json
Empty schema (no tables found)Stop and ask the user to verify scope
Table with zero columnsSkip and log a warning
Source type unsupportedAsk user to convert to a supported source (e.g., export DB query to CSV)

Resources

  • scripts/introspect_source.py — Main connector entry point; routes to source-specific adapters
  • references/connection-patterns.md — Per-source connection recipes and M-Code templates
  • references/clarification-questions.md — Standard question bank for missing/ambiguous inputs
  • references/data-model-schema.md — Full JSON schema for data-model.json

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.