Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CODING_STANDARDS_AND_STRUCTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ export DKU_API_KEY="your-api-key"
- Give a fixed-value parameter a `Literal` so its allowed values ride in the schema, and drop the `require_allowed_value` check it replaces. Keep the check and describe the values in prose instead when it normalizes input (for example `.upper()`), since a `Literal` would reject values that work today, or when the value set is large enough that listing it in every catalog send costs more than a description.

## Cobuild Write-Routing Convention
- This server intentionally does not expose direct create/update/delete tools for in-project flow and analytic assets (recipes, ML analyses, dashboards, insights, agents, agent tools, scenarios, webapps, wiki articles, data quality rules, knowledge banks, semantic models, evaluation stores). Project-level building goes through `dataiku_mcp/tools/cobuild.py`'s Cobuild conversation tools instead. `set_container_exec_config` is the narrow, placement-only exception; all other changes stay with Cobuild.
- This server intentionally does not expose direct create/update/delete tools for in-project flow and analytic assets (recipes, ML analyses, dashboards, insights, agents, agent tools, scenarios, webapps, wiki articles, data quality rules, knowledge banks, semantic models, evaluation stores). Project-level building goes through `dataiku_mcp/tools/cobuild.py`'s Cobuild conversation tools instead. `set_container_exec_config` and `set_recipe_code_env` are the narrow, selection-only exceptions; all other changes stay with Cobuild.
- Do not add a new direct write tool for an in-project asset type beyond the fixed exceptions below. If a gap in Cobuild's coverage is found, note it in the relevant SKILL.md rather than adding an MCP write tool around it.
- A new direct write tool is only justified when the operation is cross-project, instance-level, or must happen before a project/Cobuild conversation exists. The fixed bootstrap exceptions are `create_project`, `create_upload_dataset`, `create_managed_folder`, `upload_file_to_managed_folder`, `write_project_library_file`, and `set_project_variables`; all but `create_project` write into a project, and each creates a container or carries local content rather than building logic. The other fixed exceptions are deterministic execution of existing assets (`build_datasets`, `run_recipe`, `run_scenario`, `abort_job`, which stops an existing job, and `get_dataset_metrics`, which recomputes a dataset's existing metric probes so a read does not return stale stored values) and deterministic container execution placement for an existing object (`set_container_exec_config`).
- A new direct write tool is only justified when the operation is cross-project, instance-level, or must happen before a project/Cobuild conversation exists. The fixed bootstrap exceptions are `create_project`, `create_upload_dataset`, `create_managed_folder`, `upload_file_to_managed_folder`, `write_project_library_file`, and `set_project_variables`; all but `create_project` write into a project, and each creates a container or carries local content rather than building logic. The other fixed exceptions are deterministic execution of existing assets (`build_datasets`, `run_recipe`, `run_scenario`, `abort_job`, which stops an existing job, and `get_dataset_metrics`, which recomputes a dataset's existing metric probes so a read does not return stale stored values), deterministic container execution placement for an existing object (`set_container_exec_config`), and deterministic code-environment selection for an existing Python recipe (`set_recipe_code_env`), which Cobuild cannot change.

## Fixed Tool Surface

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ npx skills add dataiku/dataiku-headless

Dataiku Headless is an MCP server and agent skill library for operating Dataiku from an AI agent. Connect it to a Dataiku instance to inspect projects, gather context, and use Cobuild—Dataiku's agent for building data pipelines, analytics, machine learning models, multi-agent workflows, applications, and automation pipelines.

Cobuild runs as a retained conversation through MCP tools. This repository intentionally keeps its own tool surface small: inspection tools, three deterministic executions of existing assets (`build_datasets`, `run_recipe`, and `run_scenario`), narrow configuration actions such as selecting which container an existing object runs in, and a few bootstrap actions that Cobuild cannot perform, such as creating a project or uploading a local file.
Cobuild runs as a retained conversation through MCP tools. This repository intentionally keeps its own tool surface small: inspection tools, three deterministic executions of existing assets (`build_datasets`, `run_recipe`, and `run_scenario`), narrow configuration actions such as selecting which container an existing object runs in or which code environment an existing Python recipe uses, and a few bootstrap actions that Cobuild cannot perform, such as creating a project or uploading a local file.

## Capability reference

Expand Down Expand Up @@ -304,7 +304,7 @@ The Dataiku Headless marketplace plugin uses stdio transport. For customer-manag
│ │ ├── webapps.py # WebApp/backend-state inspection tools
│ │ ├── wikis.py # Wiki article inspection tools
│ │ ├── project_libraries.py # Project library inspection/search + local-file write
│ │ ├── recipes.py # Recipe inspection tools
│ │ ├── recipes.py # Recipe inspection and Python code-env selection
│ │ ├── machine_learning/ # ML analysis/saved-model inspection tools
│ │ └── utils/ # Tool validation and response-shaping utilities
│ ├── config/ # Models, stdio/HTTP configuration, and request routing
Expand Down
103 changes: 100 additions & 3 deletions dataiku_mcp/tools/recipes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,32 @@
# See the License for the specific language governing permissions and
# limitations under the License.

"""Recipe inspection tools."""
"""Recipe inspection tools and Python recipe code-environment selection."""

from __future__ import annotations

from typing import Any
from typing import Annotated, Any, Literal

from fastmcp import Context
from pydantic import Field

from ..instance_policy import PROJECT_READ_POLICY
from ..instance_policy import PROJECT_EDIT_POLICY, PROJECT_READ_POLICY
from ..server import mcp
from ..auth import get_dss_client
from ..executors import run_blocking
from .utils.serialization import columnar, compact_json
from .utils.validation import require_non_empty_string

PythonEnvMode = Literal["INHERIT", "EXPLICIT_ENV", "USE_BUILTIN_MODE"]
PythonEnvName = Annotated[
str | None,
Field(
description=(
"Python environment name from list_code_envs, copied exactly; "
"only with env_mode='EXPLICIT_ENV'."
)
),
]

CODE_RECIPE_TYPES = {
"python",
Expand Down Expand Up @@ -112,6 +125,40 @@ def _settings_view(settings, include_engine_params: bool = False) -> dict:
return result


def _python_env_selection(settings, recipe_name: str) -> dict:
"""Return the live code-environment selection of one Python recipe.

The selection is a reference into the settings, so replacing it in the
params and calling ``settings.save()`` writes back only the environment
choice; the code, inputs, outputs, and other params are saved unchanged.
"""
recipe_type = _get_recipe_type(settings)
if recipe_type != "python":
raise ValueError(
f"Recipe '{recipe_name}' is a '{recipe_type}' recipe; only Python "
"recipes are supported"
)
params = settings.get_recipe_params()
selection = params.get("envSelection") if isinstance(params, dict) else None
if not isinstance(selection, dict):
raise ValueError(
f"Recipe '{recipe_name}' does not expose a code environment selection"
)
return selection


def _require_python_env(client, env_name: str) -> None:
"""Refuse an unknown name, which Dataiku can store and only reject at run time."""
if not any(
env.get("envName") == env_name and env.get("envLang") == "PYTHON"
for env in client.list_code_envs()
):
raise ValueError(
f"No Python code environment named '{env_name}'. Use "
"list_code_envs(language='PYTHON') and copy a returned name exactly"
)


@mcp.tool(
meta=PROJECT_READ_POLICY,
title="List Recipes",
Expand Down Expand Up @@ -162,3 +209,53 @@ def _run():
)

return compact_json(await run_blocking(_run))


@mcp.tool(
meta=PROJECT_EDIT_POLICY,
title="Set Python Recipe Code Environment",
annotations={
"readOnlyHint": False,
"destructiveHint": True,
"idempotentHint": True,
"openWorldHint": False,
},
)
async def set_recipe_code_env(
project_key: str,
recipe_name: str,
env_mode: PythonEnvMode,
ctx: Context,
env_name: PythonEnvName = None,
) -> str:
"""Select which code environment one existing Python recipe runs with."""
project_key = require_non_empty_string(project_key, "project_key")
recipe_name = require_non_empty_string(recipe_name, "recipe_name")
if env_mode == "EXPLICIT_ENV":
env_name = require_non_empty_string(env_name, "env_name")
selection = {"envMode": env_mode, "envName": env_name}
elif env_name is not None:
raise ValueError("'env_name' is only valid with env_mode='EXPLICIT_ENV'")
else:
selection = {"envMode": env_mode}
await ctx.info(
f"Setting the code environment of recipe '{recipe_name}' in {project_key}..."
)

def _run():
client = get_dss_client()
recipe = client.get_project(project_key).get_recipe(recipe_name)
settings = recipe.get_settings()
previous = dict(_python_env_selection(settings, recipe_name))
if env_mode == "EXPLICIT_ENV":
_require_python_env(client, env_name)
settings.get_recipe_params()["envSelection"] = selection
settings.save()
return {
"project_key": project_key,
"recipe_name": recipe_name,
"previous_env_selection": previous,
"env_selection": _python_env_selection(recipe.get_settings(), recipe_name),
}

return compact_json(await run_blocking(_run))
22 changes: 13 additions & 9 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ its object-specific references as directed.
or change recipe logic, code, inputs, or outputs, or to create or modify the definition
of an ML analysis, dashboard, insight, agent, agent tool, scenario, webapp, wiki article,
data quality rule, knowledge bank, semantic model, or evaluation store. The container
execution override below changes only which container an existing object runs in.
execution override below changes only which container an existing object runs in, and
the Python recipe code-environment selection changes only which environment an existing
Python recipe runs with.

Headless writes Dataiku objects directly in four cases: **bootstrap** (get a project
or local content onto the instance so Cobuild has something to work with), **project
Expand All @@ -37,8 +39,8 @@ Dataiku permissions and API availability still apply to permitted calls.

Automation excludes all six Cobuild tools, even conversation listing and polling,
and `create_project`, `update_project_settings`, `set_project_variables`,
`set_container_exec_config`, `create_upload_dataset`, `create_managed_folder`,
`upload_file_to_managed_folder`, `write_project_library_file`.
`set_container_exec_config`, `set_recipe_code_env`, `create_upload_dataset`,
`create_managed_folder`, `upload_file_to_managed_folder`, `write_project_library_file`.
It permits `build_datasets`, `run_recipe`, `run_scenario`, `abort_job`, metric
recomputation through `get_dataset_metrics`, and every
project-folder operation, including creation, deletion, and moving projects.
Expand All @@ -56,13 +58,13 @@ test. Design and Automation require a successful DSS connection test in local se

## Surface

**131 tools** · 93 read · 24 direct Dataiku write · 6 Cobuild · 4 execute · 3 local
**132 tools** · 93 read · 25 direct Dataiku write · 6 Cobuild · 4 execute · 3 local
profile · 1 connection test

| Bucket | # | Scope |
|---|---|---|
| Read / inspect | 93 | Never mutates |
| Direct Dataiku write | 24 | Bootstrap, project configuration, cross-project, admin |
| Direct Dataiku write | 25 | Bootstrap, project configuration, cross-project, admin |
| Cobuild conversation | 6 | All flow and analytic building |
| Execute | 5 | `build_datasets`, `run_recipe`, `run_scenario`, `abort_job`, `get_dataset_metrics` (recomputes metrics before reading them) |
| Local profile action | 3 | `configure_instance`, `switch_instance`, `delete_instance` |
Expand All @@ -74,14 +76,15 @@ bucket here.

## Built by Cobuild — limited direct exceptions

Three partial exceptions: Headless can create a dataset from a local file, create an
empty managed folder, and select container execution for an existing object (see
*Handled directly by Headless*). It cannot build or change analytic logic here.
Four partial exceptions: Headless can create a dataset from a local file, create an
empty managed folder, select container execution for an existing object, and select the
code environment of an existing Python recipe (see *Handled directly by Headless*). It
cannot build or change analytic logic here.

| Area | Inspect | Direct action |
|---|---|---|
| Datasets | `list_datasets`, `get_dataset_info`, `get_dataset_sample`, `get_dataset_profile`, `get_dataset_metrics`, `get_dataset_column_descriptions`, `export_dataset` | `build_datasets` |
| Recipes | `list_recipes`, `get_recipe_settings` | `set_container_exec_config`, `run_recipe` |
| Recipes | `list_recipes`, `get_recipe_settings` | `set_container_exec_config`, `set_recipe_code_env` (Python recipes), `run_recipe` |
| Flow & zones | `get_flow_graph`, `list_flow_zones`, `get_flow_object_metadata` | — |
| ML analyses | `list_ml_analyses`, `get_ml_analysis_summary`, `get_ml_analysis_settings`, `list_ml_analysis_models`, `get_ml_model_details` | `set_container_exec_config` (single-ML-task analyses) |
| Saved models | `list_saved_models`, `list_saved_model_versions`, `get_saved_model_version_details` | — |
Expand Down Expand Up @@ -115,6 +118,7 @@ No Cobuild involved. Scope says what kind of access, and where a write lands.
|---|---|---|---|
| Projects | `count_projects`, `list_projects`, `get_project_metadata`, `get_project_variables`, `get_project_settings` | `create_project`, `set_project_variables`, `update_project_settings` | Bootstrap and project configuration — direct writes are **in-project** |
| Container execution placement | `get_recipe_settings`, `get_ml_analysis_settings`, `get_webapp_settings`, `get_knowledge_bank_settings`, `get_agent_tool_settings`, `list_container_exec_configs` | `set_container_exec_config` | Project configuration, **in-project** — sets which container a recipe, ML task, WebApp backend, Knowledge Bank, or agent tool runs in. Saved-model retrains use their training recipe's `params.containerSelection`. |
| Python recipe code environment | `get_recipe_settings`, `list_code_envs` | `set_recipe_code_env` | Project configuration, **in-project** — selects the inherited, an explicit, or the built-in environment for one existing Python recipe. Its code, inputs, outputs, and other settings are unchanged. |
| Uploaded Files datasets | — | `create_upload_dataset` | Bootstrap, **in-project** — supplied rows in HTTP; local files in stdio only |
| Managed folders | `list_managed_folders`, `get_managed_folder_info`, `get_managed_folder_contents` | `create_managed_folder`, `upload_file_to_managed_folder` | Bootstrap, **in-project** — local-file upload is stdio only |
| Project libraries | `list_project_library`, `read_project_library_file`, `search_project_library`, `validate_project_library_file` | `write_project_library_file` | Bootstrap, **in-project** — local-file write is stdio only |
Expand Down
7 changes: 4 additions & 3 deletions skills/dataiku-headless/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ Use this for any Dataiku task. Choose the right reference guide first, inspect t

Automation excludes all Cobuild tools, including conversation listing and polling,
and project creation, project settings/variables updates, container placement,
dataset/folder creation, file upload, and library writes. Builds, recipe/scenario
runs, and job aborts remain available and can change outputs. For project
construction or edits, ask which Design instance to use and
recipe code-environment selection, dataset/folder creation, file upload, and library
writes. Builds, recipe/scenario runs, and job aborts remain available and can change
outputs. For project construction or edits, ask which Design instance to use and
switch to it; do not bypass the policy through raw APIs.

Agent Management, Govern and Deployer report `connection_status=unsupported` without a DSS probe.
Expand Down Expand Up @@ -64,6 +64,7 @@ unsupported calls fail before execution.
| Inspect datasets, schema, samples, or metrics; export a local CSV; or create an Uploaded Files dataset | `./references/datasets.md` |
| Inspect recipes, choose a recipe family, or ground a flow transformation | `./references/recipes.md` |
| Select which container one existing recipe, ML task, WebApp backend, Knowledge Bank, or agent tool runs in | `./references/container-execution.md` |
| Select which code environment one existing Python recipe runs with | `./references/recipes.md` |
| Run an existing dataset or recipe, or track jobs, waits, and logs | `./references/jobs.md` |
| Inspect managed folders, create one on a chosen connection, or upload a user-supplied local file into one | `./references/managed_folders.md` |
| Inspect the project library or write one user-supplied local source file | `./references/project-libraries.md` |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Matching a workload's language does not establish package or runtime compatibili
6. Create only managed Design-node `PYTHON` or `R` environments. Package-spec changes are also limited to managed Design-node environments; permissions, build targets, and rebuilds can be updated on other environment types.
7. Requested-package changes automatically update the local environment and rebuild images. Container or Spark Kubernetes target changes rebuild images. Use `force_rebuild=true` only when a clean local environment rebuild is intended; it does not rebuild images by itself.
8. Use `delete_code_env` when deletion is requested. It checks Dataiku usages first and returns any blocking PROJECT, NOTEBOOK, SCENARIO_STEP, or other usage records with remediation guidance; do not infer that deletion succeeded until `deleted` is true.
9. Route recipe, ML analysis, and code-agent environment selection changes through `../cobuild.md`.
9. To select the environment of an existing Python recipe, use `set_recipe_code_env` as described in `../recipes.md`. Route other recipe, ML analysis, and code-agent environment selection changes through `../cobuild.md`.

## Permissions

Expand Down
Loading
Loading