← TextQLCONTENT HISTORY
WHAT CHANGED
Update to TextQL
Snapshot Nov 7, 2026 · 00:09 UTC · version 1.4.0
Collection source: downloaded plugin package.
First saved snapshot
No earlier snapshot is available to establish a change.
Save TextQL and check future changes in Following.
Up to 3 plugins free · no card required.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "Reference for the TextQL tools: every tool, the rules for orgs, sandboxes and governed queries, and how to keep results small. Use whenever a task involves the organization's data, connectors, warehouse queries, or analysis Python that needs org data.",
"included_files": [],
"name": "refinery",
"skill_md_contents": "---\nname: refinery\ndescription: >-\n Reference for the TextQL tools: every tool, the rules for\n orgs, sandboxes and governed queries, and how to keep results small. Use\n whenever a task involves the organization's data, connectors, warehouse\n queries, or analysis Python that needs org data.\n---\n\n# TextQL Refinery MCP tools\n\nThe `refinery_*` tools run against the TextQL deployment this plugin points at,\nas the signed-in user. Every call goes through that user's roles, OAuth scopes\nand org scoping, so a refusal is a permission answer, not a bug to work around.\n\nEvery action is its own tool, taking only that action's fields. Results share\none envelope: `operation`, `label`, `data`, and optionally `source`, `sandbox_id`,\n`org`, `warning` and `error`. Read `error` and `warning` before trusting `data`.\n\n| Tool | What it does |\n| --- | --- |\n| `refinery_workspace_info`, `refinery_workspace_context` | Who you are, scopes and tool availability; the org's standing context (read-only) |\n| `refinery_workspace_orgs` | The organizations you belong to (read-only) |\n| `refinery_workspace_{chat,agent,dashboard,app,artifact}_{list,show}` | Saved work (read-only) |\n| `refinery_connector_db_{list,tables,preview}`, `refinery_connector_api_{list,get}` | Connectors, tables, previews and API connector metadata (read-only) |\n| `refinery_query` | SQL or governed TQL into a sandbox dataframe |\n| `refinery_sandbox_{new,list,status,rm,python,bash}` | Manage sandboxes and run code in them |\n| `refinery_{connector,query,sandbox}_{wait,cancel}` | Follow up on a long call that returned while still running |\n| `refinery_ontology_diff`, `refinery_ontology_propose` | Turn a sandbox's `library/` edits into an Ontology patch |\n| `refinery_org_set`, `refinery_org_clear` | The organization calls run in when they omit `org` |\n| `refinery_rpc_search` | Search the deployment's public RPC methods (read-only) |\n| `refinery_rpc_call` | Call one method that `refinery_rpc_search` returned |\n\n## Start here, every session\n\n1. `refinery_workspace_info`: who you are signed in as,\n the org, the scopes the grant carries, and which execution tools the org\n allows. Check it before assuming a capability exists.\n2. `refinery_workspace_context`: the organization's\n standing instructions about its own data (definitions, preferred tables,\n rules). The web app applies them on every turn, so read and follow them\n before writing any query. Skipping them is how an answer here ends up\n disagreeing with the app.\n\n## If the tools are missing or not signed in (Codex)\n\nWhen the `refinery_*` tools are unavailable or every call fails with an\nauthentication error, and you are running in Codex with a shell, sign in\nthrough Codex's own MCP config. Read the server `url` from `mcp.json` in this\nplugin's root (two directories above this skill), then run:\n\n```sh\ncodex mcp add refinery --url <url>\ncodex mcp login refinery\n```\n\n`login` opens a browser window where the user signs in and approves access.\nTell the user to finish it, then start a new thread so the tools load. Without\na shell, ask the user to run those two commands themselves.\n\n## Find the data before you query it\n\nUse the connector tools before `refinery_query`. They are read-only and cheap:\n\n- `refinery_connector_db_list` lists the org's database and BI connectors.\n- `refinery_connector_db_tables` with `connector` (id or exact name) and\n optionally `schema` or `like` (for example `\"orders%\"`) lists tables.\n- `refinery_connector_db_preview` with `connector` and `table` returns a few\n rows. Keep `max_rows` small (the default is 50, the cap is 1000).\n- `refinery_connector_api_list` and `refinery_connector_api_get` describe API\n connectors.\n\nName a table the way `refinery_connector_db_tables` listed it. A name that matches nothing, or\nseveral tables, is refused with the candidates.\n\nRetrieval has a fixed order: look for an existing definition in the org\ncontext, then run a governed `.tql`, then fall back to raw SQL. Python is for\npost-processing and charting only, never for fetching data.\n\n## Querying\n\n`refinery_query` with:\n\n- `connector`: id or exact name.\n- `sql` or `tql` (one of them). Raw SQL runs verbatim in the connector's own\n dialect, one statement per call. DDL/DML is refused unless the connector's\n `allow_sql_write_operations` is enabled — with it on, a write runs under\n the connector's credentials, so send only SELECT statements unless the user asked\n for a change and confirmed it. `tql` runs the approved Ontology copy of a\n governed model, with typed `params`.\n- `dataframe`: the name the result gets in the sandbox's Python session.\n- `max_rows` (TQL only): caps the rows loaded into the dataframe, not only the\n inline preview. Keep it low for samples; keep every row the analysis needs.\n- `sandbox_id`: see below.\n\n## Organizations: `org`\n\nOne sign-in connects every organization the user belongs to. A call that\nomits `org` (an organization id or exact name) runs in the default set with\n`refinery_org_set`, or else the organization the connection was approved in.\nWhen the user picks an organization, set it with `refinery_org_set`, remember\nits id, and pass it as `org` on every later call. `refinery_workspace_orgs`\nlists them, marks the current one and the default, and\nsays why any is unavailable. Results\nfrom another organization carry `org` in the envelope. Keep the same `org` for\nevery call of one task, sandbox calls included: a `sandbox_id` belongs to the\norganization that created it. If `refinery_workspace_orgs` says the app must be reconnected, the\nconnection only covers its original organization until the user reconnects it.\n\n## Sandboxes: reuse `sandbox_id`\n\nResults that touch a sandbox return a `sandbox_id`. Pass that same\n`sandbox_id` to every later `refinery_query` and `refinery_sandbox_*` call in\nthe task, so the dataframe you loaded is still there when you run Python on it.\n`refinery_sandbox_python` and `refinery_sandbox_bash` require it, and each\nresult's `hint` repeats the id to pass. Omitting it on `refinery_query` starts\na fresh, isolated sandbox with none of your earlier state.\n\n- `refinery_sandbox_python` with `code` runs in a persistent kernel.\n Variables and dataframes carry across calls in the same sandbox.\n- `refinery_sandbox_bash` with `code` runs a fresh process and may be\n disabled for the org (check `refinery_workspace_info`).\n- `refinery_sandbox_new`, `_list`, `_status` and `_rm` manage sandboxes.\n Keep the sandbox for follow-up questions in the same conversation; idle\n sandboxes are reaped after about an hour. `env` sets environment variables for a run.\n- A call that takes longer than about 25 seconds returns `operation_id` and a\n warning instead of its result; the work keeps running. Call the matching\n `_wait` tool (`refinery_query_wait`, `refinery_sandbox_wait` or\n `refinery_connector_wait`) with that `operation_id` and the same `org` until\n the result arrives. The matching `_cancel` tool stops it. Do not re-run the\n call: it would run twice.\n- Kernel state is lost when a sandbox restarts or is reaped after about an\n hour idle. Files under `/sandbox/files` survive. Re-run the data load if a\n warning says the sandbox restarted.\n- `pip install` is blocked. Common libraries (pandas, polars, numpy, scipy,\n sklearn, matplotlib, seaborn and more) are preinstalled.\n\n## Changing the Ontology: `refinery_ontology_*`\n\nEvery sandbox holds a working copy of the Ontology at `/sandbox/files/library`.\nTo change it:\n\n1. Edit files there with `refinery_sandbox_python` or `refinery_sandbox_bash`, in one sandbox.\n2. `refinery_ontology_diff` with that `sandbox_id` shows the pending changes.\n3. `refinery_ontology_propose` with the same `sandbox_id`, a `title` and a\n `description` authors a patch as the user. Pass `patch_number` to revise a\n patch the user already authored, or `draft` to file it for the user to\n review first.\n\nThe user's Ontology permissions decide the outcome. If the changes touch a\npath they cannot read, both tools refuse and name the paths. Where they own every touched path, the patch merges (`outcome`\n`merged`). Otherwise it waits for the owners (`awaiting_approval`, with the\n`reason`). Report the `outcome` and `url` as returned. Do not describe an\nopen patch as applied. Get the user's go-ahead before proposing.\n\n## Keep results small\n\nEverything a tool returns lands in your context. Aggregate in the sandbox and\nprint only the summary you need. For previews and samples keep `max_rows`\n(TQL) and `LIMIT` (raw SQL) low — both bound the loaded dataframe, so keep\nevery row the analysis needs — and set `max_output_chars` on\n`refinery_sandbox_python` and `refinery_sandbox_bash` runs when output could be large. A `truncated` flag means your code\nprinted too much: aggregate first rather than raising the cap.\n\n## Saved work\n\n`refinery_workspace_chat_list`, `_agent_list`, `_dashboard_list`, `_app_list`\nand `_artifact_list` find existing work, and the matching `_show` tool with `ref`\n(an id or unique name from a listing) opens one. Prefer an existing dashboard\nor agent answer over recomputing it.\n\n## Data apps: you CAN create and edit them\n\nThe `_app_` tools only read, but building and changing data apps is a core use\nof these tools: every write goes through `AppService` with\n`refinery_rpc_call`, by design. Never tell the user editing an app is\nunsupported, needs engineering, or has to be handed off to Ana. Search\n`query: \"app\"` for this deployment's methods; the usual ones are\n`GetAppFiles` (current code and files), `UpdateApp`, `CreateApp`,\n`RefreshApp`, `InvokeAppComputeFunction`, `QueryAppData`, and\n`ListAppVersions`/`RestoreAppVersion` to undo.\n\nRead before you write: `GetAppFiles`, change what the user asked for, then\n`UpdateApp`. `code` replaces the whole entry file. `files` and\n`computeFunctions` are ignored unless `replaceFiles` or\n`replaceComputeFunctions` is true, and then they replace the full list, so\nsend everything you want to keep. Edits go live immediately; confirm with the\nuser before changing an app other people use. After a write, call\n`VerifyAppRender` and fix the app until it returns `ready: true` before giving\nthe user its URL.\n\n## The escape hatch: `refinery_rpc_search` and `refinery_rpc_call`\n\nUse these only when no tool above does the job. The ChatGPT connector does not\nlist them; there, anything below that needs them, data app writes included, is\ndone in the TextQL app instead, so tell the user that rather than attempting it. Always search first with a\nfew keywords (`query: \"ontology upsert\"`), then call the exact `target` it\nreturned with a proto-JSON `body` (camelCase field names). Writes through\n`refinery_rpc_call` are real and immediate: confirm destructive or org-visible\nchanges with the user first, and never put login credentials in a body.\n\nConnector groups have no dedicated tool. Use `refinery_rpc_call` with these\n`ConnectorService` targets: `ListConnectorGroups` (body `{}`),\n`CreateConnectorGroup` (`name`, `description`, `connectorIds`),\n`UpdateConnectorGroup` (`groupId`, `name`, `description`),\n`DeleteConnectorGroup` (`groupId`), `UpdateConnectorGroupMembers` (`groupId`,\n`addConnectorIds`, `removeConnectorIds`), `ShareConnectorGroup` (`groupId`,\n`grants` of `{memberId|roleId|groupId, accessType: viewer|editor}`) and\n`UpdateConnectorGroupSettings` (`groupId`, `config` with `connectorType:\n\"AZURE_SYNAPSE\"` and an `azureSynapse` object). List first and act only on a\ngroup whose `canManage` is true. Only groups the caller manages or was given (directly, by role, or by user group)\nare listed; a connector reached only through its own sharing is standalone. A connector can belong to several groups; adding it preserves its other\nmemberships. Direct invitations and every group's viewer/editor grants combine at the highest level. Adding a\nconnector to a shared group gives everyone it is shared with access, so list the group's grants first, tell the\nuser who gains access, and confirm; do the same for who loses access before removing one.\nUse `ListConnectorGroupGrants` (`groupId`), `UpdateConnectorGroupGrant` (`groupId`, `grantId`, `accessType`),\nand `RevokeConnectorGroupGrant` (`groupId`, `grantId`) to manage group access. All three need the `connector:write` scope\nand group management rights; updating also needs edit access to every connector in the group. Revocation or group removal\nonly removes that group's contribution, along with any per-connector copies older versions wrote when sharing the group. Settings changes affect the connector in all its groups; show each\nsetting from its current value to the new one and confirm before changing live connections. For the full rules, fetch the deployment skill\nwith `refinery_rpc_call` on `OntologyManagementService/GetSkill`, body\n`{\"trigger\":\"connector-groups\"}`.\n\n## Never fabricate\n\nIf a query fails, a table is missing, or a permission is refused, say so and\nshow the error. Do not invent numbers, tables or columns.\n"
}SHA-256 of public snapshot: 48bae5a775bb13049b5ea3b9ffa9a92a95eda2cca595690b801c45e035bbafe5