{"name":"io.github.S-CurveLabs/letin","slug":"s-curvelabs-letin","title":"letin: Power Query (M)","description":"Inspect, refactor, lint, run and version Power Query (M) in Excel workbooks and Power BI projects.","url":"https://mcp.market/server/s-curvelabs-letin","rating":null,"grade":"C","score":65,"certified":false,"status":"active","category":"other","tags":[],"presence":{"score":16,"stars":0,"forks":0,"downloads_week":14,"last_push_at":"2026-09-22T03:12:53.000Z","license":"MIT"},"uptime":null,"claimed":false,"transport":"pypi","callable_via_gateway":false,"default_price_micros":0,"repository":"https://github.com/S-CurveLabs/letin","website":null,"version":"0.2.0","remotes":[],"packages":[{"registryType":"pypi","registryBaseUrl":"https://pypi.org","identifier":"letin","version":"0.2.0","runtimeHint":"uvx","transport":{"type":"stdio"},"environmentVariables":[{"description":"Folder for query snapshots. Defaults to %LOCALAPPDATA%\\letin.","format":"filepath","name":"LETIN_HOME"}]}],"tools":[{"name":"apply_transform","description":"Add a standard Power Query step without hand-writing M. `action` and `params` come from list_transforms, e.g. action='unpivot_other_columns', params={'keep_columns': ['Region']}; or action='group_by', params={'group_columns': ['Region'], 'aggregations': [{'name': 'Total', 'operation': 'sum', 'column': 'Amount'}]}. The step is appended to the end unless after_step names an earlier step (the next st","write_action":false,"price_micros":0,"input_schema":null},{"name":"copy_queries","description":"Copy queries between workbooks / projects / folders, bringing the queries they depend on along.","write_action":false,"price_micros":0,"input_schema":null},{"name":"delete_query","description":"Delete a query. Refuses when other queries reference it unless force=true.","write_action":true,"price_micros":0,"input_schema":null},{"name":"dependency_graph","description":"Query-to-query lineage. With `query`, only its upstream and downstream. format: 'json' or 'mermaid'. Also reports roots (read external data), leaves (final outputs), orphans and cyclic references.","write_action":false,"price_micros":0,"input_schema":null},{"name":"diff_queries","description":"Compare the queries of two sources (workbook vs workbook, workbook vs exported folder, PBIP vs PBIP ...). Line endings are ignored. To compare against a snapshot pass its `path` from list_snapshots.","write_action":false,"price_micros":0,"input_schema":null},{"name":"evaluate_m","description":"Run any M expression and get the result as data. Tables come back with column names, types and the first `rows` rows (cell errors shown as #ERROR); lists, records, scalars and functions (with signature) are described too; an M error comes back as reason + message. With `source`, the expression can reference that source's queries by name (e.g. Table.RowCount(Sales)), including unsaved edits of an o","write_action":true,"price_micros":0,"input_schema":null},{"name":"evaluate_query","description":"Freshly evaluate a query, or the query only up to `step`, and return columns, types and sample rows. Use it to get real column names before apply_transform, to see what a step does to the data, or to find the step where an error first appears (evaluate step by step).","write_action":false,"price_micros":0,"input_schema":null},{"name":"evaluate_steps","description":"Evaluate EVERY step of a query in one engine call and describe each one (columns, types, first `rows` rows, or the error). The fastest way to see how a query transforms its data and to find the step where an error first appears: one call instead of evaluate_query once per step.","write_action":false,"price_micros":0,"input_schema":null},{"name":"export_queries","description":"Write every query to `folder` as one .pq file each plus pq-manifest.json: ready to commit to git and diff in a PR. Existing .pq files in the folder are replaced.","write_action":true,"price_micros":0,"input_schema":null},{"name":"extract_parameter","description":"Turn a hard-coded text value (path, server, URL) into a Power Query parameter and reference it everywhere. With match_prefix, \"C:\\Data\" also rewrites \"C:\\Data\\sales.csv\" to SourceFolder & \"\\sales.csv\".","write_action":false,"price_micros":0,"input_schema":null},{"name":"find_in_queries","description":"Search the M of every query. scope: all | code | strings | comments. Returns query, step and line for each hit.","write_action":false,"price_micros":0,"input_schema":null},{"name":"format_m","description":"Pretty-print M (an expression or a section document): one step per line, long calls broken at their arguments, comments kept. The output is verified token-for-token against the input, so meaning can never change.","write_action":false,"price_micros":0,"input_schema":null},{"name":"format_queries","description":"Format one query, or every query, in place (same guarantees as format_m).","write_action":false,"price_micros":0,"input_schema":null},{"name":"get_query","description":"Full M code of one query plus its parsed steps, data sources, dependencies and dependents.","write_action":false,"price_micros":0,"input_schema":null},{"name":"import_queries","description":"Load .pq files from `folder` back into `source`. mode='update' changes/adds queries; mode='sync' also deletes queries that are not in the folder.","write_action":false,"price_micros":0,"input_schema":null},{"name":"lint_queries","description":"Audit queries for folding breakers, hard-coded paths/servers, dead steps, brittle column lists, untyped columns, duplicated logic, orphans and cycles. Each finding carries a concrete suggestion. min_severity: info | warning | error.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_data_sources","description":"Inventory of every external source (files, folders, servers, URLs) each query reads, and whether it is hard-coded or parameterised.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_queries","description":"Every query with its kind (query/function/parameter), step count, load destination, and what it depends on / is used by.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_snapshots","description":"Snapshots taken automatically before each write to this source, newest first.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_sources","description":"List workbooks open in Excel and models open in Power BI Desktop (both usable as `source`), and explain the other source forms.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_transforms","description":"Catalog of guided actions for apply_transform (unpivot, pivot, group_by, merge, filter, types, ...) with their params.","write_action":false,"price_micros":0,"input_schema":null},{"name":"m_function_help","description":"Look up the M standard library (generated from the real engine: 850+ members). Give an exact name ('Table.Group') for its signature and description, a namespace ('Table.' or 'List') to list its members, or words to search names and descriptions ('unpivot', 'split text delimiter'). Use it instead of guessing signatures.","write_action":false,"price_micros":0,"input_schema":null},{"name":"preview_query","description":"Column names and first rows of a query that is loaded to a worksheet table (open Excel workbook only). Use it to get real column names before apply_transform.","write_action":false,"price_micros":0,"input_schema":null},{"name":"refresh_query","description":"Refresh one query in an open Excel workbook and wait for it; returns the mashup engine's error text if it fails.","write_action":false,"price_micros":0,"input_schema":null},{"name":"remove_step","description":"Delete a step and rewire the steps that used it to the step it read from.","write_action":true,"price_micros":0,"input_schema":null},{"name":"rename_query","description":"Rename a query and rewrite every reference to it in all other queries (token-aware: strings, comments and same-named columns are left alone).","write_action":false,"price_micros":0,"input_schema":null},{"name":"rename_step","description":"Rename a step inside a query and every reference to it.","write_action":false,"price_micros":0,"input_schema":null},{"name":"replace_in_queries","description":"Find and replace across all (or the listed) queries in one go, e.g. repoint a server or folder. scope: 'strings' (only inside text literals; safest, the default), 'code' (outside strings and comments), 'all'.","write_action":false,"price_micros":0,"input_schema":null},{"name":"restore_snapshot","description":"Roll back to a snapshot. With `queries`, only those are restored; otherwise the whole query set is put back (queries created since are deleted). 'latest' undoes the most recent write.","write_action":false,"price_micros":0,"input_schema":null},{"name":"set_query","description":"Replace a query's M (or create it with create_if_missing). Rejects unbalanced brackets/strings before writing. For standard actions prefer apply_transform; for renames prefer rename_query / rename_step.","write_action":false,"price_micros":0,"input_schema":null},{"name":"validate_m","description":"Check M before using it: syntax errors with line/column and a plain-English cause (missing comma between steps, unquoted step name, if without else ...), unknown steps/queries/functions with did-you-mean, and wrong argument counts for library functions. Pass `formula` (an expression or a whole section document); add `source` so its query names are known. Or pass `source` (+ optional `query`) alone","write_action":false,"price_micros":0,"input_schema":null}],"scan":{"score":65,"grade":"C","scanned_at":"2026-11-01T18:07:50.458Z","report":{"scannerVersion":"0.1.10","scannedAt":"2026-11-01T18:07:50.395Z","components":{"code":{"score":25,"max":25,"notes":["27 source files scanned"]},"reliability":{"score":-1,"max":20,"notes":["no gateway calls yet and no remote to probe"]},"poisoning":{"score":-1,"max":15,"notes":["tools not inspected (local package is not executed); not counted"]},"auth":{"score":12,"max":15,"notes":["local package, no credentials required"]},"maintenance":{"score":3,"max":15,"notes":["repository not readable: repo not found"]},"identity":{"score":2,"max":10,"notes":["no repository or website to verify"]}},"findings":[],"inputs":{"packages":[{"registryType":"pypi","identifier":"letin","version":"0.2.0","found":true,"weeklyDownloads":14,"license":"MIT","dependencyCount":4,"publishedAt":"2026-09-22T03:14:13.977251Z","repositoryUrl":"https://github.com/S-CurveLabs/letin"}],"repo":{"found":false,"owner":"S-CurveLabs","repo":"letin","error":"repo not found"},"icon":{"url":null,"source":"none"},"presence":{"stars":0,"forks":0,"downloadsWeek":14,"license":"MIT","lastPushAt":"2026-09-22T03:12:53.000Z","score":16}}}},"grade_history":[{"kind":"downgrade","fromGrade":"A","toGrade":"C","reason":"score 65","createdAt":"2026-11-01T18:07:51.048Z"}],"reviews":[]}