feat(components): add folder parameter to transformations, apps and flows#438
feat(components): add folder parameter to transformations, apps and flows#438
Conversation
There was a problem hiding this comment.
Pull request overview
Adds support for organizing SQL transformations into Keboola UI folders by introducing an optional folder parameter on the SQL transformation create/update tools and persisting it as configuration metadata.
Changes:
- Add
folderparameter tocreate_sql_transformationandupdate_sql_transformation, persisting it via configuration metadata (KBC.configuration.folderName). - Add Storage API client support for searching configurations by metadata keys (
/search/component-configurations) plus helper utilities for folder discovery/setting. - Add/update unit tests and generated tool documentation (
TOOLS.md) for the new parameter.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
src/keboola_mcp_server/tools/components/tools.py |
Adds folder parameter to SQL transformation create/update and writes folder metadata after config creation/update. |
src/keboola_mcp_server/tools/components/utils.py |
Introduces get_transformation_folders and set_transformation_folder_metadata helpers. |
src/keboola_mcp_server/clients/storage.py |
Adds component_configurations_search() client method for searching configs by metadata keys. |
src/keboola_mcp_server/config.py |
Adds MetadataField.CONFIGURATION_FOLDER_NAME constant (KBC.configuration.folderName). |
tests/tools/components/test_utils.py |
Adds tests for folder discovery helper and folder metadata update helper behavior. |
tests/tools/components/test_tools.py |
Adds tests asserting folder metadata update is (or isn’t) called based on the folder parameter. |
TOOLS.md |
Updates tool schema/docs to include the new folder parameter and guidance. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
83b16a3 to
30dd263
Compare
This comment was marked as resolved.
This comment was marked as resolved.
- Add MetadataField.CONFIGURATION_FOLDER_NAME ('KBC.configuration.folderName')
- Add StorageClient.component_configurations_search() using the
/search/component-configurations endpoint to find configs by metadata key
- Add get_transformation_folders() helper returning total config count and
distinct folder names already in use
- Add set_transformation_folder_metadata() helper that sets
KBC.configuration.folderName metadata (errors are swallowed, non-fatal)
- Add optional folder parameter to create_sql_transformation and
update_sql_transformation; docstrings instruct the model to always assign
a folder when 20+ transformations exist in the project
- Extend tests for folder set / not-set cases and new util helpers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Strip whitespace from folder value in tools.py before branching (create + update) - set_transformation_folder_metadata now strips folder name internally and returns early when result is empty, preventing blank/whitespace-only folder names in UI - Wire get_transformation_folders into update_sql_transformation: surfaces change_summary folder hint when ≥20 transformations exist and no folder provided; merges with existing msg from internal update - Consolidate test_create/update_sql_transformation_folder tests: merge separate "folder metadata" and "folder hint" test functions into single parametrized tests covering folder provided, whitespace stripping, few transformations, many with/without existing folders - Consolidate test_set_transformation_folder_metadata: merge two separate test functions into one parametrized test covering normal, whitespace, empty, and whitespace-only inputs - Fix isort: move get_transformation_folders import to alphabetical position Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Python The /search/component-configurations endpoint does not accept componentId as a query parameter (returns 400 "This field was not expected."). Remove it from the URL params and filter results by componentId in Python instead. Update test_get_transformation_folders to include componentId in mock search result items so the new Python filter keeps them. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Use set for O(1) deduplication in get_transformation_folders - Wrap get_transformation_folders calls in try/except in both create_sql_transformation and update_sql_transformation so a transient API failure in the folder-hint path does not fail an already-successful transformation create/update Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract _build_folder_hint(total, existing_folders) helper to deduplicate the near-identical hint-building logic from both create and update tools - Fix folder parameter description in both tools: removed the incorrect instruction to call get_configs to find folder names (get_configs list responses do not include KBC.configuration.folderName metadata); replaced with accurate guidance that existing folders are surfaced in change_summary - Fix CONSIDERATIONS in create_sql_transformation docstring: same correction - Regenerate TOOLS.md from updated docstrings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Broaden exception catch in set_transformation_folder_metadata from HTTPStatusError to Exception so network errors (timeouts, DNS) are also handled gracefully; log as warning instead of exception to avoid noisy tracebacks for best-effort metadata - Short-circuit get_transformation_folders when total < 20: return early without calling the search API, since _build_folder_hint returns None anyway below the threshold - Add empty metadata_keys guard in component_configurations_search to prevent accidental unbounded project-wide fetches - Fix contradictory folder param description: replace "always assign … leave empty" with "you should assign … create a new one that clearly reflects the purpose" - Split test_get_transformation_folders into two tests to assert search API is NOT called when count < 20 (short-circuit) and IS called when count >= 20 - Regenerate TOOLS.md to reflect updated folder parameter description Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
preview.py passes all update_sql_transformation params through mutator_params directly to the internal function; the internal function was missing folder so any preview call with folder set raised TypeError. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The existing test omitted folder from toolParams so the TypeError introduced by the missing param in update_sql_transformation_internal was never caught. Adding folder to the fixture ensures the preview path is covered. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the UI calls /preview/configuration with a folder param, the preview
response now includes folder in originalConfig and updatedConfig so the
diff viewer shows the folder change.
- update_sql_transformation_internal fetches current KBC.configuration.folderName
metadata and returns {'original_folder': old, 'updated_folder': new} as a 4th
return value when the folder differs from the current value
- preview.py uses next(...isinstance(x, dict)...) instead of mutator_preview[0]
so the str msg (3rd value) is skipped and the folder dict (4th value) is picked up
- Outer update_sql_transformation call site updated to unpack 4-tuple with *_
- Preview test extended: mocks configuration_metadata_get and asserts folder diff
appears in originalConfig/updatedConfig
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…_internal A transient SAPI error in configuration_metadata_get would have aborted the real update_sql_transformation call, not just the preview path. The folder diff is best-effort: wrap in try/except Exception and log a warning, proceeding with folder_preview=None on failure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
/v2/storage/branch/default/components returns 400; the correct endpoint for listing all components with configs is /v2/storage/components.
- Rename get_transformation_folders → get_config_folders (already generic) - Move _build_folder_hint → build_folder_hint in utils.py (generalized) - Add folder param to create_flow, create_conditional_flow, update_flow, modify_flow - Add folder param to modify_data_app (create and update paths) - Add FlowToolOutput.change_summary and ModifiedDataAppOutput.change_summary fields - Add 22 parametrized tests covering folder metadata for flows and data apps - Regenerate TOOLS.md - Bump version to 1.50.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the try/except from set_transformation_folder_metadata so that failures when the user explicitly sets a folder are surfaced as errors. The non-critical hint-building path (get_config_folders) is already wrapped in try/except at each call site — that remains unchanged. Remove the corresponding test that validated the (now removed) swallow behavior. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…er description
- Add folder: str = '' to update_flow_internal and modify_data_app_internal so the
preview dispatcher (which spreads all tool_params verbatim) no longer crashes with
TypeError when folder is present in the request
- Fix modify_flow folder hint label: replace f'{flow_type} flows' with
'legacy flows'/'conditional flows' to match the labels used by the create paths
- Extract folder_field_description(singular, plural) helper to utils.py; replace 7
duplicated Field description strings across components/tools.py, flow/tools.py, and
data_apps.py with calls to the helper
- Fix curly/smart quotes in create_conditional_flow docstring (left by the binary
replace that fixed the SyntaxError) — replaced U+201C/U+201D with straight ASCII "
- Soften create_sql_transformation CONSIDERATIONS: 'always assign a folder' →
'consider organizing with a folder ... or create a new one'
- Regenerate TOOLS.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…data_app_internal - update_flow_internal: fetch current folderName metadata and build folder_preview dict, merged with schedule mutator_preview so the preview endpoint shows folder changes in the diff - modify_data_app_internal: same pattern; return type extended to tuple[DataApp, JsonDict, dict | None] so caller and preview endpoint receive the folder preview - update caller (deploy_data_app) to unpack 3-element tuple - update test mock return value from 2-tuple to 3-tuple to match new return type Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…remove redundant internal folder call - create_sql_transformation, create_flow, create_conditional_flow, _apply_folder (data app create): wrap set_transformation_folder_metadata in try/except so a transient metadata API failure doesn't orphan an already-created resource or hide its ID from the caller - build_folder_hint: use ', '.join(existing_folders) instead of raw list interpolation so the LLM hint reads as natural language, not Python syntax like ['Analytics', 'Sales'] - update_sql_transformation: remove folder=folder from the internal function call; the internal function fetched metadata only for preview purposes and the result was discarded — folder is already applied separately via set_transformation_folder_metadata afterwards - test_set_transformation_folder_metadata docstring: "swallows errors" → "propagates errors" after commit 5bfbf02 removed the try/except Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ck cleanup The c5d5800 commit correctly switched the endpoint from /v2/storage/branch/default/components to /v2/storage/components, but simultaneously changed the include query parameter from 'configuration' (singular, valid) to 'configurations' (plural, invalid). The Storage API only accepts 'configuration' as per ComponentResource = Literal['configuration', 'rows', 'state']. With the invalid value the API returns components without embedded configuration data, so component.get('configurations', []) always returns [] and no configs are ever deleted during integration test cleanup. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
007f3bb to
2bfdd61
Compare
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Description
Linear: AI-2344
Change Type
Summary
Adds an optional
folderparameter tocreate_sql_transformationandupdate_sql_transformationtools so transformations can be organized into Keboola UI folders.How it works:
KBC.configuration.folderNameconfiguration metadata (standard Keboola convention)component_configurations_search()storage client method uses the/search/component-configurationsendpoint to efficiently find existing folders by metadata keyget_transformation_folders()helper returns total transformation count and distinct folder names already in useset_transformation_folder_metadata()helper sets the folder metadata (errors are swallowed — non-fatal)get_configsTesting
Streamable-HTTPtransports)Optional testing
canary-orionMCP (SSEandStreamable-HTTP)canary-orioncanary-orionChecklist