BigQuery Data Platform vs Keboola MCP Server | AllMCPs
Side-by-Side Model Context Protocol Comparison
BigQuery Data Platform vs Keboola MCP Server
In-depth architectural comparison of the BigQuery Data Platform and Keboola MCP Server MCP servers. Compare execution transports, security boundaries, tool capabilities, quality scores, and ready-to-paste client installation snippets for Claude, Cursor, Windsurf, and VS Code.
At a Glance & Executive Verdict
BigQuery Data Platform
Data Platforms · Local stdio
Quality: 52/100 (Good) | Auth: No auth required
Keboola MCP Server
Data Platforms · Local stdio
Quality: 64/100 (Good) | Auth: OAuth 2.0
Verdict Summary: Choose BigQuery Data Platform if you need specialized Data Platforms tools running via a local process. Choose Keboola MCP Server if your workspace requires Data Platforms integration with local subprocess execution. Both servers can be configured concurrently in your client's mcpServers manifest.
Which MCP Server Should You Choose?
Choose BigQuery Data Platform when:
You need dedicated capabilities in the Data Platforms domain.
You prefer local stdio subprocess transport architecture.
Your security boundary fits: No auth required (Free / Open Source).
You have access to required keys: BQ_CODE_ASSET_LOCATION.
BigQuery Data Platform is categorized under Data Platforms and uses a local stdio subprocess. In contrast, Keboola MCP Server belongs to Data Platforms using local stdio subprocess. Select BigQuery Data Platform when you need capabilities focused on data platforms and Keboola MCP Server when you require tools for data platforms.
Notebooks, saved queries and data canvases in BigQuery Studio
get_code_asset
One notebook or saved query's body, notebook outputs stripped
find_code_assets_using_table
Which notebooks/saved queries **read** a table
list_notebook_schedules
Scheduled Colab notebooks, with how many recent runs failed
list_notebook_runs
Individual notebook runs across every schedule — failures by default
+2 more tools listed on main page
Keboola MCP Server Tools (44)
get_components
Retrieves detailed information about one or more components by their IDs.
RETURNS FOR EACH COMPONENT:
- Component metadata (name, type, description)
- Documentation and usage instructions
- Configuration JSON schema (required for creating/updating configurations)
- Links to component dashboard in Keboola UI
WHEN TO USE:
- Before creating a new configuration: fetch the component to get its configuration schema
- Before updating a configuration: fetch the component to understand valid configuration options
- When user asks about component capabilities or documentation
PREREQUISITES:
- You must know the component_id(s). If unknown, first use `find_component_id` or `docs` tool to discover them.
EXAMPLES:
- User: "Create a generic extractor configuration"
→ First call `find_component_id` to get the component_id, then call this tool to get the schema
- User: "What options does the Snowflake writer support?"
→ Call this tool with the Snowflake writer component_id to retrieve its documentation and schema
get_configs
Retrieves component configurations in the project with optional filtering.
Can list summaries of multiple configurations (grouped by component) or retrieve full details
for specific configurations.
Returns a list of components, each containing:
- Component metadata (ID, name, type, description)
- Configurations for that component (summaries by default, full details if requested)
- Links to the Keboola UI
PARAMETER BEHAVIOR:
- If configs is provided (non-empty): Returns FULL details ONLY for those configs.
- Else if component_ids is provided (non-empty): Lists config summaries for those components.
- Else: Lists configs based on component_types (all types if empty).
WHEN TO USE:
- For listing: Use component_types/component_ids.
- For details: Use configs (can handle multiple).
WHEN NOT TO USE:
- Do NOT list all configs just to find a configuration by name. Use `search` with
item_types=["configuration", "transformation"] instead.
- Only use broad listing (empty component_types and component_ids) when you need
a complete inventory of all configurations in the project.
EXAMPLES:
- List all configs (summaries): component_types=[], component_ids=[]
- List extractors (summaries): component_types=["extractor"]
- Get details for specific configs:
configs=[{"component_id": "keboola.ex-db-mysql", "configuration_id": "12345"}]
get_config_examples
Retrieves sample configuration examples for a specific component.
USAGE:
- Use before calling `create_config` or `add_config_row` to understand the expected parameters structure.
- Use when you want to see example configurations for a specific component.
EXAMPLES:
- user_input: `Show me example configurations for component X`
- set the component_id parameter accordingly
- returns a markdown formatted string with configuration examples
create_config
Creates a root component configuration using the specified name, component ID, configuration JSON, and description.
Not for SQL transformations (`keboola.snowflake-transformation` / `keboola.google-bigquery-transformation`),
data apps (`keboola.data-apps`) or flows — use the dedicated tools (see WHEN NOT TO USE). This IS the tool for
Python (`keboola.python-transformation-v2`), R (`keboola.r-transformation-v2`) and DuckDB
(`keboola.duckdb-transformation`) transformations.
BEFORE CALLING - REQUIRED STEPS:
1. Call `get_components([component_id])` to retrieve the component's `configuration_schema`.
2. Read `configuration_schema.required` to find ALL mandatory top-level fields.
3. Call `get_config_examples(component_id)` to see real-world parameter examples.
4. Populate `parameters` with every required field before calling this tool.
Skipping these steps will cause a schema validation error.
USAGE:
- Use when you want to create a new root configuration for a specific component.
WHEN NOT TO USE:
- `keboola.orchestrator` / `keboola.flow` → use `create_flow` / `create_conditional_flow`
- `keboola.data-apps` → use `modify_python_js_data_app` / `modify_streamlit_data_app` / `deploy_data_app`
- `keboola.snowflake-transformation` / `keboola.google-bigquery-transformation` → use `create_sql_transformation`
EXAMPLES:
- user_input: `Create a new configuration for component X with these settings`
- set the component_id and configuration parameters accordingly
- returns the created component configuration if successful.
update_config
Updates an existing root component configuration by modifying its parameters, storage mappings, name or description.
Not for SQL transformations (`keboola.snowflake-transformation` / `keboola.google-bigquery-transformation`),
data apps (`keboola.data-apps`) or flows — use the dedicated tools (see WHEN NOT TO USE). This IS the tool for
updating Python (`keboola.python-transformation-v2`), R (`keboola.r-transformation-v2`) and DuckDB
(`keboola.duckdb-transformation`) transformations.
This tool allows PARTIAL parameter updates - you only need to provide the fields you want to change.
All other fields will remain unchanged.
Use this tool when modifying existing configurations; for configuration rows, use update_config_row instead.
WHEN TO USE:
- Modifying configuration parameters (credentials, settings, API keys, etc.)
- Updating storage mappings (input/output tables or files)
- Changing configuration name or description
- Any combination of the above
WHEN NOT TO USE:
- `keboola.orchestrator` / `keboola.flow` → use `update_flow`
- `keboola.data-apps` → use `modify_python_js_data_app` / `modify_streamlit_data_app` / `deploy_data_app`
- `keboola.snowflake-transformation` / `keboola.google-bigquery-transformation` → use `update_sql_transformation`
PREREQUISITES:
- Configuration must already exist (use create_config for new configurations)
- You must know both component_id and configuration_id
- For parameter updates: Review the component's root_configuration_schema using get_components.
- For storage updates: Ensure mappings are valid for the component type
IMPORTANT CONSIDERATIONS:
- Parameter updates are PARTIAL - only specify fields you want to change
- parameter_updates supports granular operations: set keys, replace strings, remove keys, or append to lists
- Parameters must conform to the component's root_configuration_schema
- Validate schemas before calling: use get_components to retrieve root_configuration_schema
- For row-based components, this updates the ROOT only (use update_config_row for individual rows)
WORKFLOW:
1. Retrieve current configuration using get_configs (to understand current state)
2. Identify specific parameters/storage mappings to modify
3. Prepare parameter_updates list with targeted operations
4. Call update_config with only the fields to change
add_config_row
Creates a component configuration row in the specified configuration_id, using the specified name,
component ID, configuration JSON, and description.
BEFORE CALLING - REQUIRED STEPS:
1. Call `get_components([component_id])` to retrieve the component's `configuration_row_schema`.
2. Read `configuration_row_schema.required` to find ALL mandatory top-level fields.
3. Call `get_config_examples(component_id)` to see real-world row parameter examples.
4. Populate `parameters` with every required field before calling this tool.
Skipping these steps will cause a schema validation error.
USAGE:
- Use when you want to create a new row configuration for a specific component configuration.
WHEN NOT TO USE:
- `keboola.orchestrator` / `keboola.flow` → use `create_flow` / `create_conditional_flow`
- `keboola.data-apps` → use `modify_python_js_data_app` / `modify_streamlit_data_app` / `deploy_data_app`
- `keboola.snowflake-transformation` / `keboola.google-bigquery-transformation` → use `create_sql_transformation`
EXAMPLES:
- user_input: `Create a new configuration row for component X with these settings`
- set the component_id, configuration_id and configuration parameters accordingly
- returns the created component configuration if successful.
update_config_row
Updates an existing component configuration row by modifying its parameters, storage mappings, name, or description.
This tool allows PARTIAL parameter updates - you only need to provide the fields you want to change.
All other fields will remain unchanged.
Configuration rows are individual items within a configuration, often representing separate data sources,
tables, or endpoints that share the same component type and parent configuration settings.
WHEN TO USE:
- Modifying row-specific parameters (table sources, filters, credentials, etc.)
- Updating storage mappings for a specific row (input/output tables or files)
- Changing row name or description
- Any combination of the above
WHEN NOT TO USE:
- `keboola.orchestrator` / `keboola.flow` → use `update_flow`
- `keboola.data-apps` → use `modify_python_js_data_app` / `modify_streamlit_data_app` / `deploy_data_app`
- `keboola.snowflake-transformation` / `keboola.google-bigquery-transformation` → use `update_sql_transformation`
PREREQUISITES:
- The configuration row must already exist (use add_config_row for new rows)
- You must know component_id, configuration_id, and configuration_row_id
- For parameter updates: Review the component's row_configuration_schema using get_components
- For storage updates: Ensure mappings are valid for row-level storage
IMPORTANT CONSIDERATIONS:
- Parameter updates are PARTIAL - only specify fields you want to change
- parameter_updates supports granular operations: set individual keys, replace strings, or remove keys
- Parameters must conform to the component's row_configuration_schema (not root schema)
- Validate schemas before calling: use get_components to retrieve row_configuration_schema
- Each row operates independently - changes to one row don't affect others
- Row-level storage is separate from root-level storage configuration
WORKFLOW:
1. Retrieve current configuration using get_configs to see existing rows
2. Identify the specific row to modify by its configuration_row_id
3. Prepare parameter_updates list with targeted operations for this row
4. Call update_config_row with only the fields to change
run_sync_action
Executes a synchronous action for a component configuration or a component row configuration.
Effects depend on the component and action; execution can modify external resources.
WHEN TO USE:
- For finding available values of a configuration field
- For validating already configured values (e.g. testing a database connection)
- For listing remote resources such as endpoints, schemas or tables
create_sql_transformation
Creates an SQL transformation using the specified name, SQL query following the current SQL dialect, a detailed
description, and a list of created table names.
CONSIDERATIONS:
- By default, SQL transformation must create at least one table to produce a result; omit only if the user
explicitly indicates that no table creation is needed.
- Each SQL code block must include descriptive name that reflects its purpose and group one or more executable
semantically related SQL statements.
- Each SQL query statement within a code block must be executable and follow the current SQL dialect.
- Use delimited identifiers for the current SQL dialect for all identifiers and FQN references.
- When referring to the input tables within the SQL query, use fully qualified table names, which can be
retrieved using appropriate tools.
- When creating a new table within the SQL query (e.g. CREATE TABLE ...): use only the table name with
delimited identifiers, without the fully qualified path; add the plain table name without delimiters
to the `created_table_names` list.
- Unless otherwise specified by user, transformation name and description are generated based on the SQL query
and user intent.
- If there are 20 or more SQL transformations in the project, consider organizing them with a folder: existing
folder names are surfaced in the response's change_summary — use one of them or create a new one.
USAGE:
- Use when you want to create a new SQL transformation.
- This is THE tool for creating `keboola.snowflake-transformation` and `keboola.google-bigquery-transformation`
components (do NOT use `create_config` for these); the transformation ID is derived automatically from the
workspace SQL dialect.
- Snowflake/BigQuery only. For Python, R, or DuckDB transformations, use `create_config` with the appropriate
`component_id` instead — this tool cannot create them.
EXAMPLES:
- user_input: `Can you create a new transformation out of this sql query?`
- set the sql_code_blocks to the query, and set other parameters accordingly.
- returns the created SQL transformation configuration if successful.
- user_input: `Generate me an SQL transformation which [USER INTENT]`
- set the sql_code_blocks to the query based on the [USER INTENT], and set other parameters accordingly.
- returns the created SQL transformation configuration if successful.
update_sql_transformation
Updates an existing SQL transformation configuration by modifying its SQL code, storage mappings,
name or description.
This tool allows PARTIAL parameter updates for transformation SQL blocks and code - you only need to provide
the operations you want to perform. All other fields will remain unchanged.
Use this for modifying SQL transformations created with create_sql_transformation.
WHEN TO USE:
- SQL transformations only (Snowflake/BigQuery); use update_config for Python/R/DuckDB transformations
- Modifying SQL queries in transformation (add/edit/remove SQL statements)
- Updating transformation block or code block names
- Changing input/output table mappings for the transformation
- Updating the transformation name or description
- Any combination of the above
PREREQUISITES:
- Transformation must already exist (use create_sql_transformation for new transformations)
- You must know the configuration_id of the transformation
- SQL dialect is determined automatically from the workspace
- CRITICAL: Use get_configs first to see the current transformation structure and get block_id/code_id values
TRANSFORMATION STRUCTURE:
A transformation has this hierarchy:
transformation
└─ blocks[] - List of transformation blocks (each has a unique block_id)
└─ block.name - Descriptive name for the block
└─ block.codes[] - List of code blocks within the block (each has a unique code_id)
└─ code.name - Descriptive name for the code block
└─ code.script - SQL script (string with SQL statements)
Example structure from get_configs:
{
"blocks": [
{
"id": "b0", ← block_id needed for operations (format: b{index})
"name": "Data Preparation",
"codes": [
{
"id": "b0.c0", ← code_id needed for operations (format: b{block_index}.c{code_index})
"name": "Load customers",
"script": "SELECT * FROM customers WHERE status = 'active';"
}
]
}
]
}
PARAMETER UPDATE OPERATIONS:
All operations use block_id and code_id to identify elements (get these from get_configs first).
ID Format:
- block_id: "b0", "b1", "b2", etc. (format: b{index})
- code_id: "b0.c0", "b0.c1", "b1.c0", etc. (format: b{block_index}.c{code_index})
1. BLOCK OPERATIONS:
- add_block: Create a new block in the transformation
{"op": "add_block", "block": {"name": "New Block", "codes": []}, "position": "end"}
- remove_block: Delete an entire block
{"op": "remove_block", "block_id": "b0"}
- rename_block: Change a block's name
{"op": "rename_block", "block_id": "b2", "block_name": "Updated Name"}
2. CODE BLOCK OPERATIONS:
- add_code: Create a new code block within an existing block
{"op": "add_code", "block_id": "b1", "code": {"name": "New Code", "script": "SELECT 1;"}, "position": "end"}
- remove_code: Delete a code block
{"op": "remove_code", "block_id": "b0", "code_id": "b0.c0"}
- rename_code: Change a code block's name
{"op": "rename_code", "block_id": "b1", "code_id": "b1.c2", "code_name": "Updated Name"}
3. SQL SCRIPT OPERATIONS:
- set_code: Replace the entire SQL script (overwrites existing)
{"op": "set_code", "block_id": "b0", "code_id": "b0.c0", "script": "SELECT * FROM new_table;"}
- add_script: Append or prepend SQL to existing script (preserves existing)
{"op": "add_script", "block_id": "b2", "code_id": "b2.c1", "script": "WHERE date > '2024-01-01'",
"position": "end"}
- str_replace: Find and replace text in SQL scripts
{"op": "str_replace", "search_for": "old_table", "replace_with": "new_table", "block_id": "b0",'
"code_id": "b0.c0"}
- Omit code_id to replace in all codes of a block
- Omit both block_id and code_id to replace everywhere
IMPORTANT CONSIDERATIONS:
- Parameter updates are PARTIAL - only the operations you specify are applied
- All other parts of the transformation remain unchanged
- Each SQL script must be executable and follow the current SQL dialect:
- Use delimited identifiers for the current SQL dialect.
- Never mix delimiter styles within a single query.
- Storage configuration is COMPLETE REPLACEMENT - include ALL mappings you want to keep
- Leave updated_description empty to preserve the original description
- SCHEMA CHANGES: Destructive schema changes (removing columns, changing types, renaming columns) require
manually deleting the output table before running the updated transformation to avoid schema mismatch errors.
Non-destructive changes (adding columns) typically do not require table deletion.
WORKFLOW:
1. Call get_configs to retrieve current transformation structure and identify block_id/code_id values
2. Identify what needs to change (SQL code, storage, description)
3. For SQL changes: Prepare parameter_updates list with targeted operations
4. For storage changes: Build complete storage configuration (include all mappings)
5. Call update_sql_transformation with change_description and only the fields to change
EXAMPLE WORKFLOWS:
Example 1 - Update SQL script in existing code block:
Step 1: Get current config
result = get_configs(component_id="keboola.snowflake-transformation", configuration_id="12345")
# Note the block_id (e.g., "b0") and code_id (e.g., "b0.c1") from result
Step 2: Update the SQL
update_sql_transformation(
configuration_id="12345",
change_description="Updated WHERE clause to filter active customers only",
parameter_updates=[
{
"op": "set_code",
"block_id": "b0", # from step 1
"code_id": "b0.c0", # from step 1
"script": "SELECT * FROM customers WHERE status = 'active' AND region = 'US';"
}
]
)
Example 2 - Append a new code block to the second block of an existing transformation:
update_sql_transformation(
configuration_id="12345",
change_description="Added aggregation step",
parameter_updates=[
{
"op": "add_code",
"block_id": "b1", # second block
"code": {
"name": "Aggregate Sales",
"script": "SELECT customer_id, SUM(amount) as total FROM orders GROUP BY customer_id;"
},
"position": "end"
}
]
)
Example 3 - Replace table name across all SQL scripts:
update_sql_transformation(
configuration_id="12345",
change_description="Renamed source table from old_customers to customers",
parameter_updates=[
{
"op": "str_replace",
"search_for": "old_customers",
"replace_with": "customers"
# No block_id or code_id = applies to all scripts
}
]
)
Example 4 - Update storage mappings:
update_sql_transformation(
configuration_id="12345",
change_description="Added new input table",
storage={
"input": {
"tables": [
{
"source": "in.c-main.customers",
"destination": "customers"
},
{
"source": "in.c-main.orders",
"destination": "orders"
}
]
},
"output": {
"tables": [
{
"source": "result",
"destination": "out.c-main.customer_summary"
}
]
}
}
)
modify_streamlit_data_app
Creates or updates a Streamlit data app.
Considerations:
- The `source_code` parameter must be a complete and runnable Streamlit app. It must include a placeholder
`{QUERY_DATA_FUNCTION}` where a `query_data` function will be injected. This function queries the workspace to get
data, it accepts a string of SQL query following current sql dialect and returns a pandas DataFrame with the results
from the workspace.
- Write SQL queries so they are compatible with the current workspace backend, you can ensure this by using the
`query_data` tool to inspect the data in the workspace before using it in the data app.
- If you're updating an existing data app, provide the `configuration_id` parameter and the `change_description`
parameter. To keep existing data app values during an update, leave them as empty strings, lists, or None
appropriately based on the parameter type.
- After creating or updating a data app with this tool, ALWAYS call
`deploy_data_app(action="deploy", configuration_id=...)` to start a new app or restart an existing app so
changes take effect. Without this step, a newly created app will not start, and an existing app will keep
running the previous deployment without the latest changes.
- New apps use the HTTP basic authentication by default for security unless explicitly specified otherwise; when
updating, set `authentication_type` to `default` to keep the existing authentication type configuration
(including OIDC setups) unless explicitly specified otherwise.
SQL & DATA TYPE RULES:
- Use delimited identifiers for the current SQL dialect for all column names and aliases in SQL.
Match the exact identifier case used in SQL when referencing columns in Python code.
- `query_data` RETURNS ALL COLUMNS AS STRINGS regardless of SQL CAST. Always convert types in Python after loading:
`df["col"] = pd.to_numeric(df["col"], errors="coerce").fillna(0)` and
`df["date"] = pd.to_datetime(df["date"], errors="coerce")`.
modify_python_js_data_app
Creates or updates a python-js data app.
Two-app project model. Every python-js project has a persistent **prod app** that owns the
only managed git repository for the project, and zero or more **drafts** parented to that
prod app. A draft is a Storage configuration with `parameters.dataApp.isDraft=true` and
`parameters.dataApp.parentConfigurationId=<prod cfg id>`; it's an *external-git* app that
clones the parent prod's repo at a pinned branch on every deploy. Drafts are surfaced in the
Keboola UI under their parent prod app. Use `deploy_data_app(mode='dev')` to deploy a draft
as a dev version of the data app (hot reload + auto-auth for iframe preview); use
`delete_python_js_data_app_draft` to tear a draft down after its branch has been promoted.
**MCP never runs git on your behalf.** All git work — clone, branch, commit, push, merge,
branch-delete — is yours. MCP gives you authenticated clone URLs and manages configs/deploys;
it never invokes git.
**The draft flow below is mandatory — never edit prod source directly.** Every source-code
change goes through a draft branch that the user previews and explicitly approves first. NEVER
push directly to `main`: `main` only ever advances by merging an approved draft branch, and
only after the user has approved that draft's preview.
Three scenarios the agent has to distinguish:
## Scenario A — Create a brand-new data app
1. `modify_python_js_data_app(slug='demo')` → `(configuration_id=PROD, repo_url=R)`.
PROD owns the only managed repo for this app.
2. `modify_python_js_data_app(slug='demo-draft', parent_configuration_id=PROD)`
→ `(configuration_id=DRAFT, repo_url=R, git_clone_url=U, branch='draft-<hex>')`.
The default branch is generated fresh per draft (`draft-<hex>`) so it can never collide
with a branch left behind by an earlier draft. Override with `branch=<name>` for a
descriptive name — if you do, YOU own uniqueness (see step 3).
3. YOU: `git clone U`; `git checkout -b <branch>` (the repo of a brand-new prod app is empty,
so there is no `main` to branch from yet); write source; `git push origin <branch>`.
4. `deploy_data_app(action='deploy', configuration_id=DRAFT, mode='dev')`
→ preview URL serving the draft's pinned branch as a dev version. Iterate with the user.
5. Once approved — YOU: `git checkout main && git merge <branch>`. On a brand-new app (step 3
above) `main` does not exist yet, so create it from the approved draft instead:
`git checkout -b main <branch>`. Then `git push origin main`;
`git push origin --delete <branch>` (branch deletes ARE permitted on managed repos).
6. `deploy_data_app(action='deploy', configuration_id=PROD)`
→ prod URL now serves the merged `main`.
7. `delete_python_js_data_app_draft(configuration_id=DRAFT)`
→ tears down the draft's config + data-app instance. Always run this once promoted.
## Scenario B — Edit an existing data app
You already have PROD's `configuration_id` (from `get_data_apps` or earlier conversation).
1. `create_python_js_data_app_git_credential(configuration_id=PROD)`
→ fresh `git_clone_url U` with an embedded one-time token.
2. `modify_python_js_data_app(
slug='demo-draft-<short suffix>',
parent_configuration_id=PROD,
branch='<describes-the-change>', # e.g. 'add-revenue-filter'
)` → `(DRAFT, R, U2, branch)`. Use U2 (it has its own fresh token).
3. YOU: `git clone U2`; `git fetch origin && git checkout -B <branch> --no-track origin/main` —
state the base explicitly, and `--no-track` so `origin/main` does not become the branch's
upstream (otherwise a bare `git push` on the draft branch would target `main`). NEVER a bare
`git checkout <branch>`: when that branch already exists on the remote (a descriptive name
reused from an earlier session), git silently checks out its stale tip instead of branching
from `main`, and the preview then serves outdated code with no error. Edit source;
`git push origin <branch>`. If that push is rejected as non-fast-forward, the remote branch
still holds unmerged commits from an abandoned earlier draft — either
`git push --force-with-lease origin <branch>` or `git push origin --delete <branch>` first.
Never resolve a rejected push with a bare `git checkout`.
4–7. Same as Scenario A steps 4–7.
## Scenario C — Continue an unfinished draft
The previous sandbox is gone. You have PROD's `configuration_id` but no working clone and no
draft handle.
1. `get_data_apps(configuration_ids=[PROD])` → returns PROD's detail including `drafts: [...]`.
Pick the draft the user means (ask if multiple and unclear). Each entry exposes its
`configuration_id`, slug, and pinned branch.
2. `create_python_js_data_app_git_credential(configuration_id=PROD)`
→ fresh `git_clone_url U` (the previous one was minted in a wiped sandbox and is lost).
Drafts have no managed repo of their own — always mint against PROD.
3. YOU: `git clone U`; `git checkout <draft's pinned branch>`. Here you DO want the existing
branch — but confirm it is current before you build on it:
`git rev-list --count <branch>..origin/main` must print 0, otherwise `git merge origin/main`
first. Resume work; `git push`.
4. `deploy_data_app(action='deploy', configuration_id=<DRAFT>, mode='dev')` → preview URL.
The draft's branch is already pinned in its config.
5–7. Same promote/cleanup sequence as Scenario A steps 5–7.
## Argument rules
- `parent_configuration_id` is **create-only**. Rejected on update.
- `branch` on **create** is only valid when `parent_configuration_id` is set (pins the new
draft's branch). Defaults to a generated, collision-free `'draft-<hex>'`. Must not be
`'main'`. Rejected on prod create. A branch you supply yourself may already exist on the
repo — branch it off `origin/main` explicitly (see Scenario B step 3).
On **update** `branch` repoints an existing **external-git** app's pinned branch (see below).
- `slug` is optional on create (auto-derived from `name` when omitted; drafts get a unique
suffix). See "Slug on update" below.
- The **update path** (passing `configuration_id`) is for changing `name`, `description`,
`authentication_type`, `auto_suspend_after_seconds`, `storage` on either a prod app or
a draft, for changing a prod app's `slug` (see "Slug on update"), and for repointing an
**external-git** app's `branch` (a draft, or an app bound to an external repository — an
app on a Keboola-managed git repo is rejected, its branch is owned by the platform). Source code changes go through the git flow above, not this
tool. After a `branch` repoint, call `deploy_data_app` to serve the new branch.
## Authentication
New apps default to HTTP basic authentication for safety. Pass `authentication_type='no-auth'`
to expose publicly -- only when the user explicitly asks for a public app. `'no-auth'` is
rejected on drafts: a draft inherits the prod app's data access, so disabling its
authentication would expose Storage-reading and Storage-writing endpoints publicly. Never
disable authentication to make the preview work -- the in-platform preview
(`deploy_data_app(mode='dev')`) authenticates on top of the configured auth. On update,
`authentication_type='default'` preserves the existing `authorization` block (including OIDC
setups configured outside the MCP); `'basic-auth'` / `'no-auth'` overwrite it.
## Slug constraint
Must be DNS-label-safe (lowercase letters, digits, hyphens). Optional on create: when omitted it
is auto-derived from `name` and capped at 50 characters (the data-app URL-prefix limit enforced
by the UI; drafts additionally get a short unique `-draft-<suffix>`, still within 50, to keep
slugs unique across the prod app and its drafts). Pass an explicit slug to override; an explicit
slug must be at most 63 characters (the DNS-label max), and note the UI's own URL-prefix limit is
50, so an explicit slug of 51-63 characters may still be rejected at deploy time.
## Slug on update
The slug is the app URL (`https://<slug>-<app id>.hub.<stack>`), so changing it moves the app.
1. Until the prod app is first deployed, a rename moves a slug that follows the name (e.g.
`new-app` for "New App") to the new name automatically — nobody has the URL yet.
2. Once the app has been deployed, a rename keeps the slug. Change it only with an explicit
`slug`, and only after the user approved the new URL.
3. Drafts never change their slug.
After an explicit `slug` the new URL applies on the next `deploy_data_app` and the old URL stops
working; tell the user both.