oleander

The all-in-one data stack for agents.

От сообщества: Добавлен пользователем или импортирован; проверьте владельца перед подключениемРаботаетБез входаГлобальныйБесплатноТолько чтение

Что умеет

  • Identity Get: Returns the authenticated identity and organization context resolved by the MCP server.
  • System Health: Returns MCP server health information.
  • Lake Query: Run a SQL query in Oleander Lake, a DuckDB-based query environment that can read from Oleander-managed tables, built-in telemetry tables, the Oleander Iceberg catalog, and user-registered

Какие данные видит

Нужен ли аккаунт

Не нужен: сервер работает без входа

The all-in-one data stack for agents. Your data jobs deserve to run on the right tool. Whether you’re bringing existing infra, or exploring our open source deployments, oleander empowers your entire data stack to work together as a team.

Список инструментов сервера (40)

Технические названия из tools/list. Нужны только разработчикам.

identity_getReturns the authenticated identity and organization context resolved by the MCP server.
system_healthReturns MCP server health information.
lake_queryRun a SQL query in Oleander Lake, a DuckDB-based query environment that can read from Oleander-managed tables, built-in telemetry tables, the Oleander Iceberg catalog, and user-registered catalogs. Use this tool for ad hoc analysis, investigation, and data exploration across telemetry and non-telemetry tables available in Oleander Lake. Write statements (INSERT/UPDATE/DELETE/…) are allowed and run directly. CTAS and RTAS are not supported. For best performance on the built-in telemetry tables (`oleander.telemetry.run_events`, `oleander.telemetry.traces`, `oleander.telemetry.logs`), use a literal timestamp range filter on the partitioned time column: - `event_time >= TIMESTAMP '2026-03-25 00:00:00' AND event_time < TIMESTAMP '2026-03-26 00:00:00'` - `start_time >= TIMESTAMP '2026-03-25 00:00:00' AND start_time < TIMESTAMP '2026-03-26 00:00:00'` - `time >= TIMESTAMP '2026-03-25 00:00:00' AND time < TIMESTAMP '2026-03-26 00:00:00'` Do not use `CURRENT_DATE`, `CAST(... AS DATE)`, `DATE(...)`, or other derived date expressions for partition filters on these telemetry tables, because DuckDB may not perform partition pruning in those cases. DuckDB may also fail to push predicates into Iceberg scans through CTEs, so when using CTEs, put the partition filter inside the CTE that reads the telemetry table. Do not guess column names. Before querying a table for the first time in a session, verify its columns with `DESCRIBE <catalog>.<namespace>.<table>` (or a `LIMIT 0` select), then write the real query. If a query fails with a Binder Error, the "Candidate bindings" list names similar columns that do exist — re-check the schema rather than retrying another guess. DuckDB does not accept the multi-pattern form `col ILIKE ANY ('%a%', '%b%')`. Use OR-ed `ILIKE` conditions or `regexp_matches(col, '(?i)a|b')` instead.
spark_sql_submitSubmit a Spark SQL query for asynchronous execution on oleander serverless Spark. Use this tool instead of `lake_query` for larger queries that need configurable compute or whose results should be written directly to a table. This tool does not return query rows. It submits a job and returns a generated `run_id` with submission metadata. The query result is stored in `output_table`. Write modes: - `OVERWRITE` replaces the output table contents (default) - `APPEND` appends the query result to the output table Choose the submission details based on the user's intent: - Ad hoc exploration of large data: when the user primarily wants to inspect query results and Spark is chosen because the data is too large for `lake_query`, generate a descriptive job name and a clearly temporary, unique output table in an available writable namespace. Use `OVERWRITE` for that temporary table. Do not ask the user to choose a job name or write mode unless the generated destination could conflict with existing data. After the Spark run completes, use `lake_query` to read a useful sample from the temporary output table so the user can see the result. - Named query job with persistent output: when the user asks to run a Spark query job and store its result in a designated table, use the supplied output table. If the user has not specified whether existing data should be replaced or extended, ask them to choose `OVERWRITE` or `APPEND`; never silently overwrite a persistent table. Use a supplied job name when present. If no name is supplied, generate a concise name when it is only an operational identifier, or ask for one when the name is expected to be durable or meaningful for lineage, monitoring, or repeated runs. These are situational rules, not mandatory prompts. Infer safe operational details when they do not affect persistent user data, and ask only when the choice changes persistent data semantics or a durable job identity. Compute is configurable with `driver_machine_type`, `executor_machine_type`, and `executor_numbers`. Machine types use the `spark.<size>.<class>` values exposed by the input schema. Defaults are `spark.1.b` for the driver and executors, with 2 executors. Requires `confirm=true` because this operation starts compute and writes to the configured output table. For an agent-created temporary output table, the user's request to run the ad hoc query is sufficient authorization; set `confirm=true` after choosing safe temporary details. For a persistent output table, set it only after the destination and write behavior are explicit from the request or have been clarified with the user. On success, report the full `run_id`, state, output table, write mode, and compute configuration from the response. Do not imply that result rows are included in the response. Use `jobs_runs_get` with the returned run ID to monitor execution.
spark_artifacts_listList Spark artifacts uploaded to Oleander for Oleander-managed Spark jobs. Returns JSON: `{ ok: true, data: { scripts: [...], hasMore: boolean } }`. Each element of `scripts` is an artifact record with (when present): `name`, `version`, `language`, `status`, `createdAt`, `updatedAt`, `entrypoint`, `mainClass`, `pyFiles`, `virtualenv`, `id`. When presenting results, always display as a table (omit columns the API did not return; never show null placeholders): ┌──────────────────────────────┬─────────┬──────────┬─────────────────────┐ │ Artifact │ Version │ Status │ Uploaded (UTC) │ ├──────────────────────────────┼─────────┼──────────┼─────────────────────┤ │ <name> │ v<n> │ <status> │ <createdAt> │ └──────────────────────────────┴─────────┴──────────┴─────────────────────┘ Prefer `name` for the Artifact column; avoid exposing raw storage URIs from `entrypoint` when `name` is available. The listing is latest READY version per artifact name; call out the highest `version` per row as the current runnable revision. After listing, automatically suggest the next step without waiting for the user: - to inspect source: spark_artifacts_get - to run: spark_jobs_submit Always add a one-line plain-English summary after the table. Use this tool when the user wants to see available Spark scripts or JARs. - To view artifact content: use spark_artifacts_get - To upload a new artifact: use spark_artifacts_upload - To submit a job: use spark_jobs_submit // GENERAL RULES // Naming - Always write "oleander" with a lowercase "o" — never "Oleander" - Always prefer symlink names (e.g. default.iris_dataset) over raw S3/warehouse paths - Shorten run IDs to first 8 characters when displaying, except in the initial submit block where the full run ID is shown // Formatting - Always display timestamps in ISO 8601 UTC (e.g. 2026-05-01T15:19:58Z) - Always display costs to 4 decimal places (e.g. $0.0046) For costs under $0.0001, use scientific notation - Format large numbers with commas (e.g. 1,234,567 records, 2.3 GB) - Bold field labels using **label** syntax, not values In terminal contexts where markdown does not render, fall back to plain key: value format // Data handling - Omit sections where data is unavailable rather than showing nulls - When run_id, pipeline_id, or namespace/job are already known from a prior tool call in this session, reuse them without asking the user to repeat them // Error handling - When a tool returns an error, always display: Error: <message> [tool: <tool_name>] Then suggest the most relevant next step // Agentic behavior - Never ask the user if they want cost, lineage, or investigations — always call them automatically at the right step - Never summarize or stop after a terminal state — always gather cost and lineage first before presenting final output - Never describe what a job does at submit time — that appears in the lineage summary after the run completes
spark_artifacts_uploadUpload a PySpark script or JAR as a versioned Spark artifact to Oleander. Returns JSON: `{ ok: true, data: { artifact: { ... }, uploadedComponents: string[] } }` where `artifact` matches the list API shape (`name`, `version`, `language`, `status`, `createdAt`, `updatedAt`, `entrypoint`, etc.). This tool performs the full artifact upload flow used by the CLI: - loads the latest ready version to determine the optimistic-lock base version - creates a pending artifact version through the Spark artifacts API - uploads the provided file contents to the returned presigned PUT URL or URLs - commits the uploaded artifact version so it becomes ready for execution Input fields: - `entrypoint`: artifact entrypoint basename such as `job.py` or `pipeline.jar` - `language`: optional explicit language override. If omitted, `.py` defaults to `python` and `.jar` defaults to `java` - `entrypoint_content_base64`: base64-encoded entrypoint file contents - `py_files`: optional Python dependency archive name ending in `.zip` or `.egg` - `py_files_content_base64`: base64-encoded contents for `py_files` - `virtualenv`: optional Python virtualenv archive name ending in `.tar.gz` - `virtualenv_content_base64`: base64-encoded contents for `virtualenv` - `main_class`: optional main class for jar artifacts - `confirm`: must be `true` to execute this write operation Each upload creates a new version; prior ready versions stay runnable. Confirm the artifact `entrypoint` basename with the user before uploading. This tool preserves optimistic locking; concurrent uploads may return a version conflict error. When presenting results, always display: Artifact <artifact.name> Version v<artifact.version> Status <artifact.status> Uploaded <artifact.createdAt UTC> Omit lines for fields the response does not include. After a successful upload, automatically suggest next steps (do not wait for the user): - to submit immediately: spark_jobs_submit - to verify content first: spark_artifacts_get Always add a one-line plain-English summary after the block. Use this tool when the user wants to upload or update a Spark script or JAR. - To list existing artifacts: use spark_artifacts_list - To submit the uploaded job: use spark_jobs_submit // GENERAL RULES // Naming - Always write "oleander" with a lowercase "o" — never "Oleander" - Always prefer symlink names (e.g. default.iris_dataset) over raw S3/warehouse paths - Shorten run IDs to first 8 characters when displaying, except in the initial submit block where the full run ID is shown // Formatting - Always display timestamps in ISO 8601 UTC (e.g. 2026-05-01T15:19:58Z) - Always display costs to 4 decimal places (e.g. $0.0046) For costs under $0.0001, use scientific notation - Format large numbers with commas (e.g. 1,234,567 records, 2.3 GB) - Bold field labels using **label** syntax, not values In terminal contexts where markdown does not render, fall back to plain key: value format // Data handling - Omit sections where data is unavailable rather than showing nulls - When run_id, pipeline_id, or namespace/job are already known from a prior tool call in this session, reuse them without asking the user to repeat them // Error handling - When a tool returns an error, always display: Error: <message> [tool: <tool_name>] Then suggest the most relevant next step // Agentic behavior - Never ask the user if they want cost, lineage, or investigations — always call them automatically at the right step - Never summarize or stop after a terminal state — always gather cost and lineage first before presenting final output - Never describe what a job does at submit time — that appears in the lineage summary after the run completes
spark_artifacts_getFetch the UTF-8 text of a Spark artifact entrypoint by `entrypoint` basename and optional `version`. Returns JSON: `{ ok: true, data: "<entrypoint source text>" }` (the MCP layer JSON-encodes the string). If `version` is omitted, the API returns the latest ready version; with `version`, that specific revision. **Python (`.py`) only:** this endpoint returns the full PySpark source. Java/Scala JAR entrypoints do **not** expose text or manifest content here; inspect metadata via spark_artifacts_list instead. When presenting results, always display: Artifact: <entrypoint basename> v<version or "latest"> (omit Uploaded / Size lines — not returned by this tool) <source text> After retrieving content, automatically suggest next steps without waiting for the user: - to run the job: spark_jobs_submit - to see recent runs: jobs_runs_list (needs `namespace` + `job`) Always add a one-line plain-English summary after the body. Use this tool when the user wants to inspect or review a Spark script. - To list all artifacts: use spark_artifacts_list - To upload a new version: use spark_artifacts_upload - To submit the job: use spark_jobs_submit // GENERAL RULES // Naming - Always write "oleander" with a lowercase "o" — never "Oleander" - Always prefer symlink names (e.g. default.iris_dataset) over raw S3/warehouse paths - Shorten run IDs to first 8 characters when displaying, except in the initial submit block where the full run ID is shown // Formatting - Always display timestamps in ISO 8601 UTC (e.g. 2026-05-01T15:19:58Z) - Always display costs to 4 decimal places (e.g. $0.0046) For costs under $0.0001, use scientific notation - Format large numbers with commas (e.g. 1,234,567 records, 2.3 GB) - Bold field labels using **label** syntax, not values In terminal contexts where markdown does not render, fall back to plain key: value format // Data handling - Omit sections where data is unavailable rather than showing nulls - When run_id, pipeline_id, or namespace/job are already known from a prior tool call in this session, reuse them without asking the user to repeat them // Error handling - When a tool returns an error, always display: Error: <message> [tool: <tool_name>] Then suggest the most relevant next step // Agentic behavior - Never ask the user if they want cost, lineage, or investigations — always call them automatically at the right step - Never summarize or stop after a terminal state — always gather cost and lineage first before presenting final output - Never describe what a job does at submit time — that appears in the lineage summary after the run completes
jobs_runs_listList recent completed runs for a specific job (namespace + job name) from Oleander telemetry. Returns runs from the last 7 days with start/end times, duration, and end state, plus optional pipeline id and run-duration trend context. Requires both `namespace` and `job`. Use `jobs_runs_get` when you have a run ID and need full execution details.
jobs_runs_getGet the full execution context for a specific job run by its unique run ID. Returns current state (SUBMITTED, QUEUED, SCHEDULED, START, RUNNING, COMPLETE, FAIL, ABORT), timestamps, duration, engine and infrastructure details, read/write datasets with Iceberg snapshot IDs and record counts, warnings, and related job and pipeline metadata — everything needed to understand what happened in a single job run without manually correlating logs or dashboards. For serverless Spark runs, also includes captured source code for the run. When presenting results, always display in this exact format: Job complete: **<namespace>/<job_name>** **Run** <run_id_full> **State** <state> **Duration** <duration>s (Total time: <wall_time>s) **Source** <entrypoint>@v<version> // RUN_TRACE_FORMAT is a section block — always embed inside a // larger job run or pipeline run display, not as a standalone header. **Run trace:** <namespace>/<job_name> · <run_id_short> · <duration>s **Submitted:** <submitted_at ISO UTC> **Queued:** <queued_at ISO UTC> (+<delta>s) **Scheduled:** <scheduled_at ISO UTC> (+<delta>s) **Started:** <started_at ISO UTC> (+<delta>s) **Ended:** <ended_at ISO UTC> (+<delta>s) View trace: https://oleander.dev/app/pipelines?pipelineId=<pipeline_id>&runId=<run_id_full> Run trace rules: - Omit any state where the timestamp is null or unavailable — never show null or placeholder values - Each delta (+Xs) is elapsed time since the previous state, not since submission Example: Queued delta = queued_at minus submitted_at Started delta = started_at minus scheduled_at - Duration is execution time: started_at to ended_at, for any pipeline or job type — not limited to Spark - For RUNNING jobs, replace Ended with: **Running:** <current_time minus started_at>s (in progress) - If any phase delta exceeds its historical p50 by more than 50%, append (slow) to that line Example: **Queued:** 2026-05-01T15:20:03Z (+94s) (slow) - Omit the View trace line when pipeline_id is null or unavailable Datasets: ┌───────────┬──────────────────────────┬──────────────┬─────────┬────────┐ │ Direction │ Table │ Snapshot │ Records │ Size │ ├───────────┼──────────────────────────┼──────────────┼─────────┼────────┤ │ Read │ <symlink_name> │ <snapshot> │ <count> │ <size> │ │ Write │ <symlink_name> │ <snapshot> │ <count> │ <size> │ └───────────┴──────────────────────────┴──────────────┴─────────┴────────┘ I/O Summary: Bytes read: <X> MB Bytes written: <X> MB Records read: <X> Records written: <X> Files read: <X> Files written: <X> Warnings: - <warning> [fatal|non-fatal] Exit: <"No errors. Job exited cleanly." or error detail> View trace: https://oleander.dev/app/pipelines?pipelineId=<pipeline_id>&runId=<run_id_full> Next steps: - COMPLETE: run jobs_lineage_get for downstream impact - FAIL / ABORT: run investigations_list for root cause - RUNNING: re-run jobs_runs_get to poll for status Use this when you already have a job run ID and need the full picture. - For cost: use jobs_cost_get - For logs: use jobs_logs_get - For OTel traces: use jobs_traces_get - For lineage: use jobs_lineage_get - For failed run investigations: use investigations_list - For pipeline-level context: use pipelines_runs_get - For listing job runs: use jobs_runs_list // GENERAL RULES // Naming - Always write "oleander" with a lowercase "o" — never "Oleander" - Always prefer symlink names (e.g. default.iris_dataset) over raw S3/warehouse paths - Shorten run IDs to first 8 characters when displaying, except in the initial submit block where the full run ID is shown // Formatting - Always display timestamps in ISO 8601 UTC (e.g. 2026-05-01T15:19:58Z) - Always display costs to 4 decimal places (e.g. $0.0046) For costs under $0.0001, use scientific notation - Format large numbers with commas (e.g. 1,234,567 records, 2.3 GB) - Bold field labels using **label** syntax, not values In terminal contexts where markdown does not render, fall back to plain key: value format // Data handling - Omit sections where data is unavailable rather than showing nulls - When run_id, pipeline_id, or namespace/job are already known from a prior tool call in this session, reuse them without asking the user to repeat them // Error handling - When a tool returns an error, always display: Error: <message> [tool: <tool_name>] Then suggest the most relevant next step // Agentic behavior - Never ask the user if they want cost, lineage, or investigations — always call them automatically at the right step - Never summarize or stop after a terminal state — always gather cost and lineage first before presenting final output - Never describe what a job does at submit time — that appears in the lineage summary after the run completes
jobs_logs_getGet paginated logs for a specific Oleander run by its unique run ID. Use this tool when the user wants the log lines collected for one run, optionally filtered by a text search, severity levels, or a pagination cursor from a previous response. The response includes: - `logs`: log entries ordered by newest first, each with `time`, `body`, `severity`, `traceId`, and `spanId` - `nextCursor`: cursor to request the next page of older logs, or `null` when there are no more results - `totalCount`: total number of matching log lines on the first page when available Input fields: - `run_id`: the run whose logs to retrieve - `search`: optional case-insensitive substring filter applied to the log body - `limit`: maximum number of log entries to return, up to 1000 - `cursor`: optional cursor returned by a prior call to continue pagination - `log_levels`: optional list of severities to include, chosen from `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`, and `UNSPECIFIED` Prefer this tool for run-specific log inspection. Do not use it for full run metadata, lineage graphs, or general telemetry queries across many runs.
jobs_traces_getGet paginated trace spans for a specific Oleander run by its unique run ID. Use this tool when the user wants the trace spans collected for one run. The response is ordered by newest span start time first and includes a pagination cursor for older spans. The response includes: - `traces`: span entries with identifiers, timing, status, attributes, events, and links - `nextCursor`: cursor to request the next page of older spans, or `null` when there are no more results - `totalCount`: total number of matching spans on the first page when available Input fields: - `run_id`: the run whose trace spans to retrieve - `limit`: maximum number of spans to return, up to 1000 - `cursor`: optional cursor returned by a prior call to continue pagination Prefer this tool for run-specific trace inspection. Do not use it for logs, lineage graphs, or general telemetry queries across many runs.
jobs_cost_getGet the full cost breakdown for a specific job run by its unique run ID. Returns cost for a single atomic job execution. Always call this tool automatically after jobs_runs_get for COMPLETE runs — do not wait for the user to ask. When presenting results, always display: // COST BLOCK FORMAT Cost: **<job_name>** (<run_id_short>) **Total:** $X.XXXX **Duration:** <duration>s **vs. baseline:** +X% above avg (last 30 runs) Breakdown: ┌───────────────────┬─────────┐ │ Usage │ Cost │ ├───────────────────┼─────────┤ │ X.XXXX vCPU-hours │ $X.XXXX │ │ X.XXXX GB-hours │ $X.XXXX │ ├───────────────────┼─────────┤ │ Total │ $X.XXXX │ └───────────────────┴─────────┘ **Cost per record written:** $X.XXXXXX Always add a one-line plain-English cost summary after the table. Example: "A very cheap run — $0.0046 for 71s of Spark compute." Omit sections where data is unavailable rather than showing nulls. Use this tool for single job run cost. - For pipeline-level cost aggregation: use pipelines_cost_get - For full job run context: use jobs_runs_get - For logs: use jobs_logs_get - For OTel traces: use jobs_traces_get - For lineage: use jobs_lineage_get // GENERAL RULES // Naming - Always write "oleander" with a lowercase "o" — never "Oleander" - Always prefer symlink names (e.g. default.iris_dataset) over raw S3/warehouse paths - Shorten run IDs to first 8 characters when displaying, except in the initial submit block where the full run ID is shown // Formatting - Always display timestamps in ISO 8601 UTC (e.g. 2026-05-01T15:19:58Z) - Always display costs to 4 decimal places (e.g. $0.0046) For costs under $0.0001, use scientific notation - Format large numbers with commas (e.g. 1,234,567 records, 2.3 GB) - Bold field labels using **label** syntax, not values In terminal contexts where markdown does not render, fall back to plain key: value format // Data handling - Omit sections where data is unavailable rather than showing nulls - When run_id, pipeline_id, or namespace/job are already known from a prior tool call in this session, reuse them without asking the user to repeat them // Error handling - When a tool returns an error, always display: Error: <message> [tool: <tool_name>] Then suggest the most relevant next step // Agentic behavior - Never ask the user if they want cost, lineage, or investigations — always call them automatically at the right step - Never summarize or stop after a terminal state — always gather cost and lineage first before presenting final output - Never describe what a job does at submit time — that appears in the lineage summary after the run completes
bigquery_cost_getGet the full cost breakdown for a specific BigQuery table - what it costs to produce, maintain, and query this table over a time window. Traces cost back to the pipelines, runs, and queries that wrote to it. Use this tool when the user asks: - What does it cost to produce this dataset in BigQuery? - Which pipelines are driving the cost for this table? - Are there optimization opportunities for this table? - How has the cost for this table changed over time? - What percentage of queries hit the cache vs scan data? Input fields: - `table`: fully qualified BigQuery table name (e.g. sentry.events.raw or project.dataset.table) - `namespace`: dataset namespace in Oleander. Defaults to `default` when omitted. - `start`: start of time window (ISO UTC or Unix epoch seconds) - `end`: end of time window (ISO UTC or Unix epoch seconds) (when omitted with `start`, backend defaults to now) When `start` and `end` are both omitted, defaults to last 30 days. // DISPLAY When presenting results, always display in this exact format: BigQuery Table Cost - **<namespace>.<table>** - <window> **Total:** $X.XXXX **Cost per run:** $X.XXXX avg (p75: $X.XX - p95: $X.XX) **Runs:** <count> total - <cache_hits> cache hits (free) **Bytes billed:** X.X TB total - X.X GB avg per run **Slots used:** X,XXX ms avg Produced by: ┌──────────────────────────────────┬──────────┬────────┬───────────┐ │ Pipeline │ Cost │ Runs │ Avg cost │ ├──────────────────────────────────┼──────────┼────────┼───────────┤ │ <pipeline_name> │ $X.XXXX │ <n> │ $X.XXXX │ │ <pipeline_name> │ $X.XXXX │ <n> │ $X.XXXX │ └──────────────────────────────────┴──────────┴────────┴───────────┘ Query breakdown by statement type: ┌──────────────────────┬────────┬──────────┬──────────────┐ │ Type │ Runs │ Cost │ Avg bytes │ ├──────────────────────┼────────┼──────────┼──────────────┤ │ SELECT │ <n> │ $X.XXXX │ X.X GB │ │ INSERT │ <n> │ $X.XXXX │ X.X GB │ │ CREATE TABLE AS │ <n> │ $X.XXXX │ X.X GB │ └──────────────────────┴────────┴──────────┴──────────────┘ // RAW QUERIES If `runQueries` is present in the response: - Each entry has a `sql` field that is a JSON-encoded string - Always parse the JSON string first - Extract and display only the `query` property - never the raw JSON, never `_producer`, never `_schemaURL` - Deduplicate: group runs with identical query text together - Truncate individual queries at 500 chars with "... (truncated, X chars total)" if longer Display format: Queries: Runs <runId_short>[, <runId_short>...] - <statement_type>: <extracted query text> Example with deduplication: Runs 019df4c1, 019df4f6, 019dff64 - CREATE_TABLE_AS_SELECT: CREATE OR REPLACE TABLE `project.main.titanic_sample` AS SELECT * FROM `project.main.titanic` LIMIT 100 Run 019df4f6 - CALL: CALL `project.main.create_titanic_sample`() Omit this section entirely if `runQueries` is absent. // QUERY PLAN If any entry in `runQueries` has a `plan` field: - The `plan` field is a JSON-encoded string - Always parse it first to extract the `plan` array - Only render the plan for runs where `plan` is present - Render as a readable stage tree using this format: Query plan (run <runId_short>): <stage.name> (<recordsRead> -> <recordsWritten> records · <slotMs>ms) <step.kind>: <step.substeps - join with ", ", truncate long lists> └─ feeds -> <next stage name> Surface these signals inline next to the relevant step: - Full table scan: READ with no partition filter -> append ⚠ full scan - Large shuffle: shuffleOutputBytes > 1MB -> append ⚠ shuffle X MB - Spill to disk: shuffleOutputBytesSpilled > 0 -> append ⚠ spilled Example rendering: Query plan (run 019dff64): S00: Input (891 -> 100 records · 89ms) READ: PassengerId, Survived ... FROM main.titanic ⚠ full scan LIMIT: 100 WRITE: -> __stage00_output S01: Coalesce (100 -> 100 records · 725ms) READ: FROM __stage00_output └─ feeds -> S02: Output S02: Output (100 -> 100 records · 219ms) READ: FROM __stage01_output EXPORT: -> main.titanic_sample Omit this section for runs where `plan` is absent. // PLAN-BASED OPTIMIZATION SUGGESTIONS After rendering the query plan, always analyze the plan data and surface specific optimization suggestions: // 1. Full table scan If a READ step has no partition filter substep: "⚠ Full table scan on <table> - <recordsRead> rows scanned to produce <recordsWritten> rows (<selectivity>% selectivity). Adding a WHERE clause on a partitioned column would reduce bytes scanned and cost." // 2. Over-parallelization If parallelInputs >> recordsRead (e.g. 50 workers for 100 rows): "⚠ Over-parallelized coalesce - <parallelInputs> workers processing <recordsRead> rows. This inflates slot usage (<slotMs>ms in Coalesce vs <inputSlotMs>ms in Input). Consider reducing parallelism for small result sets." // 3. Expensive coalesce vs input ratio If coalesce slotMs > input slotMs significantly: "⚠ Coalesce stage (<slotMs>ms) is Xx more expensive than Input stage (<inputSlotMs>ms) - disproportionate for <recordsCount> rows. Likely caused by over-parallelization." // 4. High wait ratio If waitRatioAvg >= 0.5 on output stage: "⚠ Output stage spent <waitPct>% of time waiting (I/O bound). Consider writing to a partitioned table to reduce write overhead." // 5. LIMIT without partition filter If LIMIT step exists but READ has no partition filter: "⚠ LIMIT <n> applied after full table scan - BigQuery scanned all <recordsRead> rows before applying LIMIT. Push filters upstream or use TABLESAMPLE for sampling workloads." // 6. Shuffle spill If shuffleOutputBytesSpilled > 0: "⚠ Shuffle spilled <bytes> to disk in <stage> - indicates memory pressure. Consider reducing result set size or increasing slot reservation." // 7. Minimum billing applied If bytesProcessed << bytesBilled (10MB minimum threshold): "⚠ BigQuery billed 10MB minimum - actual data processed was only <bytesProcessed>. For this query pattern, cost is fixed regardless of optimization. Consider batching multiple small queries into one to amortize the minimum billing threshold." Format suggestions as a numbered list after the plan: Optimization suggestions: 1. <specific suggestion with estimated impact> 2. <specific suggestion with estimated impact> 3. <specific suggestion with estimated impact> Always quantify impact where possible: "Reducing parallelism could save ~X slot-ms per run" "Adding partition filter on <column> could reduce bytes scanned from 10MB to ~<estimate>KB" Downstream consumers (<count>): - <table_name> (read <n> times) - <table_name> (read <n> times) Schema: last changed <date> - <summary of changes if available> Optimization signals: - <n> of <total> runs processed full table scans - partition pruning not applied. Estimated savings: ~$X.XX/month - <n> of <total> runs were cache hits - $0 cost - Bytes processed vs billed gap: X% - consider clustering on <column> to reduce scan size - <any query plan signals: full scans, broadcast joins, excessive shuffle, missing partition filters> Always add a plain-English summary after the display: Example: "sentry.events.raw cost $1,243 last 30 days, driven primarily by events_ingestion_dag. 39% of runs performed full table scans - applying partition pruning could reduce cost by an estimated $480/month." // DISPLAY RULES - Cost is computed from total_bytes_billed: cost = (total_bytes_billed / 1,099,511,627,776) × $5.00 Note: display assumes on-demand pricing - actual cost may differ for flat-rate or committed use discount customers - Cache hits (cache_hit = true) are always $0 - surface them as a positive signal, not a cost driver - Full table scan detection uses two signals: 1. total_partitions_processed = 0 on a partitioned table 2. query_plan contains READ steps with no partition filter Prefer query_plan signal when available - it is more reliable - Format bytes as: X.X GB under 1TB, X.X TB at 1TB or above - Format costs to 4 decimal places (e.g. $0.0046) - Format large numbers with commas (e.g. 1,234,567 records) - Omit any section where data is unavailable rather than showing nulls or empty tables // NEXT STEPS After displaying, proactively suggest based on what was observed: - If full table scans detected: "Want me to show which specific runs triggered full scans and what partition filter would fix them?" - If one pipeline dominates spend (>60%): "Want me to drill into <pipeline_name> run-level cost to find the most expensive individual runs?" - If cost trend is increasing: "Cost is up X% vs the previous period - want me to identify which runs or queries drove the increase?" - If cache hit rate is low (<20%): "Only X% of queries hit the cache - want me to identify which queries could be made cacheable?" - If downstream consumers exist: "This table has <n> downstream consumers - want me to show the full dependency graph and downstream cost impact?" - Always available: "Want me to compare this table's cost against other tables in the same pipeline?" Use this tool for single table cost intelligence. - For a specific run: use jobs_cost_get - For full pipeline lineage: use jobs_lineage_get - For run-level context: use jobs_runs_get - For raw lineage events: use lineage_events_list // GENERAL RULES // Naming - Always write "oleander" with a lowercase "o" — never "Oleander" - Always prefer symlink names (e.g. default.iris_dataset) over raw S3/warehouse paths - Shorten run IDs to first 8 characters when displaying, except in the initial submit block where the full run ID is shown // Formatting - Always display timestamps in ISO 8601 UTC (e.g. 2026-05-01T15:19:58Z) - Always display costs to 4 decimal places (e.g. $0.0046) For costs under $0.0001, use scientific notation - Format large numbers with commas (e.g. 1,234,567 records, 2.3 GB) - Bold field labels using **label** syntax, not values In terminal contexts where markdown does not render, fall back to plain key: value format // Data handling - Omit sections where data is unavailable rather than showing nulls - When run_id, pipeline_id, or namespace/job are already known from a prior tool call in this session, reuse them without asking the user to repeat them // Error handling - When a tool returns an error, always display: Error: <message> [tool: <tool_name>] Then suggest the most relevant next step // Agentic behavior - Never ask the user if they want cost, lineage, or investigations — always call them automatically at the right step - Never summarize or stop after a terminal state — always gather cost and lineage first before presenting final output - Never describe what a job does at submit time — that appears in the lineage summary after the run completes
lineage_events_listList collected OpenLineage run events in Oleander within a required time range. Use this tool when the user wants raw or structured OpenLineage run events for a specific period, optionally filtered by run ID, job namespace, job name, run state, or OpenLineage integration. The required `start` and `end` inputs are Unix epoch seconds, and this tool returns events where `start <= event_time < end`. Results are paginated with `limit` and `offset`, and can be sorted by event time or event size. Returned results are OpenLineage `RunEvent` records. Beyond basic run identity, job info, run state, parent context, datasets, and producer metadata, events may carry standard or custom facets on the run, job, inputs, and outputs. In practice this can include run facets such as parent or root run context, errors, nominal time, execution parameters, processing engine details, and tags; job facets such as SQL, source code, source location, ownership, documentation, or job type; and dataset facets such as schema, versioning, lifecycle or storage metadata, column lineage, and input or output statistics or quality metrics like row count, byte size, and file count. Integration-specific facets may also include engine details such as Spark logical plans or other debugging metadata. Comparing these facets across baseline, anomalous, and failing runs is often useful during incident investigation, even when a run reports `COMPLETE` without an explicit error. Prefer this tool when the user wants OpenLineage event history, lineage event inspection, integration-level event details, or raw run-event data over a known time window. Do not use it for high-level run summaries when a specific Oleander run record is needed, or for querying traces, logs, or non-lineage telemetry data.
jobs_lineage_getGet the immutable, version-specific lineage graph for a concrete execution context in Oleander. Maps the exact dataset versions (snapshots) read and written during a specific run, including upstream dependencies, child runs, and downstream consumers. This graph represents historical fact — it is point-in-time and will not change. It is distinct from a mutable job-level lineage view that reflects the current state of a job across all runs. `parent_run` is the effective input for this tool: - provide the run ID in `parent_run` - if that run has child runs (e.g. a Spark application spawning internal jobs), the graph includes those child runs as part of the run context - if the user wants the full lineage graph for a parent execution that spawned internal runs, use the parent execution's run ID as `parent_run` - `depth` limits graph traversal depth (default: 2) This tool is not limited to Spark. It works for any execution context represented through OpenLineage run events, including Spark, Airflow, dbt, and DuckDB. Engine awareness matters — cross-engine flows (e.g. Spark produced it, DuckDB queried it) are a key signal for impact analysis. For Spark specifically: - if `parent_run` is a Spark application run ID, the graph includes the internal Spark job runs under that application - if `parent_run` is an internal Spark job run ID, the graph is limited to that job run context Prefer this tool when the user asks: - What did this run read and write? - What is upstream or downstream of this execution? - Show me the flow for this run - Which downstream jobs or queries consumed the data this run produced? - Debug this Spark or Airflow execution - Show me child runs under this parent execution Do not use this tool for raw OpenLineage event listing, high-level run metadata lookup, or telemetry table queries. // LINEAGE GRAPH FORMAT Lineage: **<job_name>** (<run_id_short>) [READ] <symlink_name> (snapshot <external_version_id>) │ Schema: <field1>, <field2>... │ ├─► [RUN] <child_job_name> (<duration>s, <state>) │ │ │ ▼ │ [WRITE] <symlink_name> (snapshot <external_version_id>) │ Schema: <field1>, <field2>, <field3>... │ │ │ ▼ │ [RUN] <downstream_job> (<engine>, <state>) │ │ │ ▼ │ [OUT] <field1>, <field2> │ └─► [RUN] <child_job_name> (<duration>s, <state>) Lineage rendering rules: - Always use symlink names not raw S3/warehouse paths - Show snapshot IDs (external_version_id) next to dataset nodes - Show duration and state next to run nodes - Label engine type for non-Spark runs (DuckDB, Airflow, dbt) - Indent child runs under their parent to show logical grouping - Show schema fields for WRITE nodes — truncate to 5 fields with "... and N more" if schema is large - Show schema fields for READ nodes where available - Always show downstream consumers if present — this is the key signal for impact analysis // LINEAGE SUMMARY After the graph, always display: Summary: - Inputs (<count>): - <symlink_name> — <N> fields (<field list>) - Outputs (<count>): - <symlink_name> — <N> fields written via <description> - Downstream (<count> consumers): - <symlink_name> consumed by <job_name> (<engine>) - Signals: - <schema changes, unexpected consumers, cross-engine flows, etc.> Omit any section where count is zero rather than showing empty lists. If there is only one input or output, omit the count and write it as a single line. // PROACTIVE SUGGESTIONS After the summary, proactively suggest next steps: - If downstream consumers exist: note impact risk and suggest lineage_columns_get on written dataset versions, or lineage_events_list for dataset and facet context - If schema fields changed between runs: suggest lineage_columns_get or lineage_events_list - For cost context: suggest jobs_cost_get // COMPARING TWO RUNS When the user asks to compare two runs, call this tool twice — once per run ID as `parent_run` — then present the graphs side by side highlighting: - Datasets added or removed - Schema fields added, removed, or changed - New or missing downstream consumers - Duration and cost differences (use jobs_runs_get and jobs_cost_get for timing and cost figures) Note: although the input schema includes `run`, this tool currently uses `parent_run` as the effective run identifier. // GENERAL RULES // Naming - Always write "oleander" with a lowercase "o" — never "Oleander" - Always prefer symlink names (e.g. default.iris_dataset) over raw S3/warehouse paths - Shorten run IDs to first 8 characters when displaying, except in the initial submit block where the full run ID is shown // Formatting - Always display timestamps in ISO 8601 UTC (e.g. 2026-05-01T15:19:58Z) - Always display costs to 4 decimal places (e.g. $0.0046) For costs under $0.0001, use scientific notation - Format large numbers with commas (e.g. 1,234,567 records, 2.3 GB) - Bold field labels using **label** syntax, not values In terminal contexts where markdown does not render, fall back to plain key: value format // Data handling - Omit sections where data is unavailable rather than showing nulls - When run_id, pipeline_id, or namespace/job are already known from a prior tool call in this session, reuse them without asking the user to repeat them // Error handling - When a tool returns an error, always display: Error: <message> [tool: <tool_name>] Then suggest the most relevant next step // Agentic behavior - Never ask the user if they want cost, lineage, or investigations — always call them automatically at the right step - Never summarize or stop after a terminal state — always gather cost and lineage first before presenting final output - Never describe what a job does at submit time — that appears in the lineage summary after the run completes
pipelines_runs_listList runs for a pipeline by pipeline ID, with optional time range, state filters, and pagination. Uses the v2 pipeline runs API (metrics, baselines, and alert summaries when available). Requires `pipeline_id` (UUID). Optional `start` and `end` are Unix-epoch **seconds** as strings (same as the `/app/pipelines` API). `states` is a comma-separated list of run states; `run` filters to a specific run UUID; `root_only` controls whether only root runs are included (default true).
pipelines_runs_getGet the full execution context for a pipeline run by its unique run ID. A pipeline run is the parent execution that contains one or more job runs. Returns current state (SUBMITTED, QUEUED, SCHEDULED, START, RUNNING, COMPLETE, FAIL, ABORT), timestamps, duration, aggregated I/O across all job runs, cost breakdown by job run, warnings, and full job run inventory — everything needed to understand what happened across an entire pipeline execution without manually correlating individual job runs. When presenting results, always display in this exact format: Pipeline Run: <pipeline_name> (<run_id_short>) State: <state> Duration: <duration>s Job Runs: <count> total · <count> complete · <count> failed // RUN_TRACE_FORMAT is a section block — always embed inside a // larger job run or pipeline run display, not as a standalone header. **Run trace:** <namespace>/<job_name> · <run_id_short> · <duration>s **Submitted:** <submitted_at ISO UTC> **Queued:** <queued_at ISO UTC> (+<delta>s) **Scheduled:** <scheduled_at ISO UTC> (+<delta>s) **Started:** <started_at ISO UTC> (+<delta>s) **Ended:** <ended_at ISO UTC> (+<delta>s) View trace: https://oleander.dev/app/pipelines?pipelineId=<pipeline_id>&runId=<run_id_full> Run trace rules: - Omit any state where the timestamp is null or unavailable — never show null or placeholder values - Each delta (+Xs) is elapsed time since the previous state, not since submission Example: Queued delta = queued_at minus submitted_at Started delta = started_at minus scheduled_at - Duration is execution time: started_at to ended_at, for any pipeline or job type — not limited to Spark - For RUNNING jobs, replace Ended with: **Running:** <current_time minus started_at>s (in progress) - If any phase delta exceeds its historical p50 by more than 50%, append (slow) to that line Example: **Queued:** 2026-05-01T15:20:03Z (+94s) (slow) - Omit the View trace line when pipeline_id is null or unavailable Job Runs: ┌──────────────────────────┬──────────────┬───────────┬────────┐ │ Job │ State │ Duration │ Cost │ ├──────────────────────────┼──────────────┼───────────┼────────┤ │ <job_name> │ <state> │ <X>s │ $X.XX │ │ <job_name> │ <state> │ <X>s │ $X.XX │ └──────────────────────────┴──────────────┴───────────┴────────┘ Datasets: ┌───────────┬──────────────────────────┬──────────────┬─────────┬────────┐ │ Direction │ Table │ Snapshot │ Records │ Size │ ├───────────┼──────────────────────────┼──────────────┼─────────┼────────┤ │ Read │ <symlink_name> │ <snapshot> │ <count> │ <size> │ │ Write │ <symlink_name> │ <snapshot> │ <count> │ <size> │ └───────────┴──────────────────────────┴──────────────┴─────────┴────────┘ I/O Summary: Bytes read: <X> MB Bytes written: <X> MB Records read: <X> Records written: <X> Files read: <X> Files written: <X> Cost: Total: $X.XXXX Compute: $X.XXXX Storage I/O: $X.XXXX Cost per record: $X.XXXXXX Most expensive job: <job_name> ($X.XXXX) vs. baseline: +X% above avg (last 30 pipeline runs) Warnings: - <warning> [job_name · fatal|non-fatal] Exit: <"All job runs completed successfully" or error detail with job name> Next steps: - COMPLETE: run pipelines_cost_get for full cost breakdown - COMPLETE: run jobs_lineage_get on any job for downstream impact - FAIL / ABORT: run investigations_list for root cause - RUNNING: re-run pipelines_runs_get to poll for status Warnings should reference the job run they originated from. Use this when you have a pipeline run ID and need the full picture. - For full cost breakdown: use pipelines_cost_get - For a specific job run: use jobs_runs_get - For lineage: use jobs_lineage_get - For failed run investigations: use investigations_list - For listing pipeline runs: use pipelines_runs_list // GENERAL RULES // Naming - Always write "oleander" with a lowercase "o" — never "Oleander" - Always prefer symlink names (e.g. default.iris_dataset) over raw S3/warehouse paths - Shorten run IDs to first 8 characters when displaying, except in the initial submit block where the full run ID is shown // Formatting - Always display timestamps in ISO 8601 UTC (e.g. 2026-05-01T15:19:58Z) - Always display costs to 4 decimal places (e.g. $0.0046) For costs under $0.0001, use scientific notation - Format large numbers with commas (e.g. 1,234,567 records, 2.3 GB) - Bold field labels using **label** syntax, not values In terminal contexts where markdown does not render, fall back to plain key: value format // Data handling - Omit sections where data is unavailable rather than showing nulls - When run_id, pipeline_id, or namespace/job are already known from a prior tool call in this session, reuse them without asking the user to repeat them // Error handling - When a tool returns an error, always display: Error: <message> [tool: <tool_name>] Then suggest the most relevant next step // Agentic behavior - Never ask the user if they want cost, lineage, or investigations — always call them automatically at the right step - Never summarize or stop after a terminal state — always gather cost and lineage first before presenting final output - Never describe what a job does at submit time — that appears in the lineage summary after the run completes
pipelines_cost_getGet the full cost breakdown for a pipeline run by its unique run ID. Aggregates cost across all job runs in the pipeline. Always call this tool automatically after pipelines_runs_get for COMPLETE runs — do not wait for the user to ask. When presenting results, always display: Cost: <pipeline_name> (<run_id_short>) Total: $X.XXXX Duration: <duration>s vs. baseline: +X% above avg (last 30 pipeline runs) Breakdown: ┌───────────────────┬─────────┐ │ Usage │ Cost │ ├───────────────────┼─────────┤ │ X.XXXX vCPU-hours │ $X.XXXX │ │ X.XXXX GB-hours │ $X.XXXX │ │ X.XXXX GB │ $X.XXXX │ ├───────────────────┼─────────┤ │ Total │ $X.XXXX │ └───────────────────┴─────────┘ Cost by job run: ┌──────────────────────────┬─────────┬───────────┐ │ Job │ Cost │ Duration │ ├──────────────────────────┼─────────┼───────────┤ │ <job_name> │ $X.XXXX │ <X>s │ │ <job_name> │ $X.XXXX │ <X>s │ └──────────────────────────┴─────────┴───────────┘ Cost per record written: $X.XXXXXX Most expensive job: <job_name> ($X.XXXX) Always add a one-line plain-English cost summary after the tables. Example: "A very cheap run — $0.0117 for 57s of Spark compute across 2 job runs." Omit sections where data is unavailable rather than showing nulls. Use this tool for pipeline-level cost aggregation. - For individual job run cost: use jobs_cost_get - For full pipeline context: use pipelines_runs_get - For lineage: use jobs_lineage_get // GENERAL RULES // Naming - Always write "oleander" with a lowercase "o" — never "Oleander" - Always prefer symlink names (e.g. default.iris_dataset) over raw S3/warehouse paths - Shorten run IDs to first 8 characters when displaying, except in the initial submit block where the full run ID is shown // Formatting - Always display timestamps in ISO 8601 UTC (e.g. 2026-05-01T15:19:58Z) - Always display costs to 4 decimal places (e.g. $0.0046) For costs under $0.0001, use scientific notation - Format large numbers with commas (e.g. 1,234,567 records, 2.3 GB) - Bold field labels using **label** syntax, not values In terminal contexts where markdown does not render, fall back to plain key: value format // Data handling - Omit sections where data is unavailable rather than showing nulls - When run_id, pipeline_id, or namespace/job are already known from a prior tool call in this session, reuse them without asking the user to repeat them // Error handling - When a tool returns an error, always display: Error: <message> [tool: <tool_name>] Then suggest the most relevant next step // Agentic behavior - Never ask the user if they want cost, lineage, or investigations — always call them automatically at the right step - Never summarize or stop after a terminal state — always gather cost and lineage first before presenting final output - Never describe what a job does at submit time — that appears in the lineage summary after the run completes
lineage_columns_getGet the column-level lineage graph for a specific dataset version in Oleander. Use this tool when the user wants to understand how columns are related across dataset versions, including which input columns contributed to which output columns. This is a detailed dataset-version-centered lineage view built from the transformations observed for a concrete execution context. Unlike run-level lineage, which connects runs to dataset versions, this tool connects columns in dataset versions directly. It shows how columns in upstream dataset versions map to columns in downstream dataset versions. The graph does not include run instance nodes. Use `dataset_version` as the required center of the graph. Optional inputs refine the view: - `column`: limit the graph to a specific column within the given dataset version - `direction`: return only `UPSTREAM`, only `DOWNSTREAM`, or both when omitted - `depth`: limit graph traversal depth Prefer this tool when the user asks questions like: - where did this column come from? - which source columns feed this output column? - what downstream columns depend on this column? - show column lineage for this dataset version - trace upstream or downstream column dependencies Do not use this tool for run metadata, raw OpenLineage events, run-level lineage graphs, or telemetry table queries. Use it for detailed column-to-column lineage across dataset versions.
catalogs_listList the Iceberg catalogs available in Oleander, including the built-in Oleander catalog and user-registered catalogs, along with the tables currently visible in those catalogs. Use this tool when the user wants to discover what catalogs are available, inspect registered catalog details, or see which catalog, namespace, and table combinations currently exist in Oleander. The result includes: - `registeredCatalogs`: detailed catalog metadata for user-registered catalogs, such as catalog name, type, and catalog properties - `catalogs`: the visible catalog contents as catalog, namespace, and table triples for all currently available tables For supported registered catalogs such as Amazon S3 Tables, catalog details may include properties like the AWS IAM role ARN provided for access, the table bucket name, and the AWS region. Prefer this tool when the user asks questions like: - what catalogs are available in Oleander? - what tables exist across Oleander and registered catalogs? - what is the name or type of a registered catalog? - which namespace contains this table? - what S3 Tables catalogs are registered? To inspect the Iceberg REST catalog metadata for one table, use catalogs_tables_metadata_get. To compute table or partition size, use catalogs_tables_size_get. For table discovery in a specific catalog or namespace, use catalogs_tables_list. For namespace discovery, use catalogs_namespaces_list. Do not use this tool for querying table contents, run metadata, lineage graphs, or telemetry events. Use it for catalog discovery and catalog metadata inspection.
catalogs_namespaces_listList Iceberg namespaces in a catalog. Use this tool when the user wants to discover which namespaces exist in a catalog before listing or creating tables. If the user does not specify `catalog`, use the default `oleander` catalog. The response includes `catalog` and `namespaces`. Prefer this tool over catalogs_list when the user asks: - what namespaces exist in catalog X? - what namespaces are in oleander? To list tables in a namespace, use catalogs_tables_list. To create a catalog namespace, use catalogs_namespaces_create.
catalogs_tables_listList Iceberg tables in a catalog, optionally scoped to a single namespace. Use this tool when the user wants to discover which tables exist in a catalog or namespace. Input fields: - `catalog`: registered catalog name (for example `oleander`) - `namespace`: optional namespace. When omitted, tables are listed across all namespaces in the catalog. The response includes `tables` as catalog, namespace, and table entries. Prefer this tool over catalogs_list when the user asks: - what tables exist in catalog X? - what tables are in oleander.default? - which tables are in this namespace? To inspect schema or Iceberg metadata for one table, use catalogs_tables_metadata_get. To query rows, use lake_query.
catalogs_namespaces_createCreate an Iceberg namespace in a catalog. Use this tool when the user wants to create a new Iceberg catalog namespace before creating or loading tables. This is not an OpenLineage job namespace. If the user does not specify `catalog`, use the default `oleander` catalog. If the user does not specify `namespace`, ask the user for the namespace name before calling this tool. Input fields: - `catalog`: optional; defaults to `oleander` - `namespace`: Iceberg catalog namespace to create - `confirm`: must be `true` because this creates catalog metadata Namespace creation is idempotent when the namespace already exists. After creating a catalog namespace, use catalogs_tables_create or catalogs_tables_load to add tables. For a local data file, call catalogs_files_stage first.
catalogs_tables_metadata_getRead the Iceberg REST catalog table metadata for a single table by catalog, namespace, and table name. This tool calls `GET /api/v1/catalogs/{catalog}/namespaces/{namespace}/tables/{table}` and returns the raw table metadata envelope from the API. If the user does not specify `catalog`, use the default `oleander` catalog. If the user does not specify `namespace`, use the default `default` namespace. If the user does not specify `table`, ask the user for the table name before calling this tool. Important response fields: - `location`: table storage location - `schemas`: Iceberg schema history, including fields and types - `partition-specs`: Iceberg partition specs - `current-snapshot-id`, `snapshots`, `refs`, and `properties` when present Use this tool when the user asks for table metadata, schema, fields, Iceberg metadata JSON, partition spec, current snapshot, table location, or table properties. Do not use this tool to query table rows or run SQL. Use lake_query for data queries.
catalogs_tables_size_getRead the computed Iceberg table size for a single table, optionally restricted to a snapshot and/or exact partition filters. This tool calls `GET /api/v1/catalogs/{catalog}/namespaces/{namespace}/tables/{table}/size`. It computes size from Iceberg metadata and manifest files, summing live data files only. If the user does not specify `catalog`, use the default `oleander` catalog. If the user does not specify `namespace`, use the default `default` namespace. If the user does not specify `table`, ask the user for the table name before calling this tool. Optional inputs: - `snapshot_id`: Iceberg snapshot ID. When omitted, the API uses the current snapshot. - `partition_filters`: exact partition filters as `[{ key, value }]`. These map to repeated `partitionKey` and `partitionValue` query parameters. Use this when the user asks for a table partition size. Response fields: - `sizeBytes`: total size in bytes as a string - `recordCount`: total record count as a string - `dataFileCount`: number of matching data files - `snapshotId`: snapshot used, or null for an empty table - `partitionFilters`: filters applied by the API Use this tool when the user asks for table size, partition size, data file count, record count from Iceberg metadata, or size at a particular snapshot. Do not use this tool to query table rows or inspect schema. Use lake_query for row queries and catalogs_tables_metadata_get for schema and partition specs.
catalogs_tables_createCreate an empty Iceberg table in an Oleander catalog from an explicit Iceberg schema object. Use this tool when the user wants to create a table with a known schema, not when they want to load data from a file or URL. For creating a table from data, use catalogs_tables_load. For a local data file, call catalogs_files_stage first. If the user does not specify `catalog`, use the default `oleander` catalog. If the user does not specify `namespace`, use the default `default` namespace. If the user does not specify `table`, ask the user for the table name before calling this tool. If the user does not provide a complete Iceberg schema with field IDs, ask for it before calling this tool. Input fields: - `schema`: Iceberg struct schema object, including field IDs - `partition_spec`: optional Iceberg partition spec object - `table`: target table name - `catalog`: optional; defaults to `oleander` - `namespace`: optional; defaults to `default` - `confirm`: must be `true` because this creates a table Return the created table target to the user. If the API reports that the table already exists, surface that error directly.
catalogs_tables_dropDrop an Iceberg table from an Oleander catalog. This is a destructive table-level operation. The table will no longer be available through the catalog, and downstream jobs, dashboards, notebooks, or applications that read it may fail. Before calling this tool, explicitly confirm with the user: - the full table name: `catalog.namespace.table` - that they want to drop the entire table, not only remove columns - that they understand downstream consumers may fail until updated Prefer checking table metadata with catalogs_tables_metadata_get and checking lineage or downstream consumers when relevant before dropping production tables. If the user does not specify `catalog`, use the default `oleander` catalog. If the user does not specify `namespace`, use the default `default` namespace. If the user does not specify `table`, ask the user for the table name before calling this tool. Input fields: - `table`: table to drop - `catalog`: optional; defaults to `oleander` - `namespace`: optional; defaults to `default` - `confirm`: must be `true` because this drops the table Do not call this tool speculatively. Return the dropped table identity to the user.
catalogs_columns_addAdd one or more columns to an existing Iceberg table through schema evolution. Use this tool when the user asks to add top-level or nested struct columns to an Iceberg table. The server assigns Iceberg field IDs for new columns and preserves existing field IDs. If the user does not specify `catalog`, use the default `oleander` catalog. If the user does not specify `namespace`, use the default `default` namespace. If the user does not specify `table`, ask the user for the table name before calling this tool. Input fields: - `columns`: array of columns to add. Each column has `path`, `type`, optional `required`, `doc`, `initial_default`, and `write_default` - `include_deprecated_last_column_id`: optional compatibility flag for catalogs that require deprecated `last-column-id` on add-schema updates - `confirm`: must be `true` because this changes table metadata This tool rejects columns that already exist. Return the schema evolution result, including assigned field IDs and the new schema ID.
catalogs_columns_renameRename one existing Iceberg table column while preserving its Iceberg field ID. This can be a backward-incompatible change for downstream jobs, dashboards, notebooks, or applications that still reference the old column name. Before calling this tool, explicitly confirm with the user: - the full table name: `catalog.namespace.table` - the current column path being renamed - the exact new column name - that they understand downstream consumers may fail until updated Prefer checking table metadata with catalogs_tables_metadata_get, and lineage or downstream consumers when relevant, before using this tool on production tables. If the user does not specify `catalog`, use the default `oleander` catalog. If the user does not specify `namespace`, use the default `default` namespace. Input fields: - `path`: current column path, for example `["address", "state"]` - `new_name`: new leaf column name - `include_deprecated_last_column_id`: optional compatibility flag for catalogs that require deprecated `last-column-id` on add-schema updates - `confirm`: must be `true` because this may break consumers Do not call this tool speculatively. Return the schema evolution result, including the preserved field ID and new schema ID.
catalogs_columns_dropDrop one or more columns from an existing Iceberg table through schema evolution. This is a backward-incompatible metadata change for downstream jobs, dashboards, notebooks, or applications that still read the dropped columns. It does not rewrite existing data files; old files may still physically contain the dropped columns, but readers using the new schema will not expose them. Before calling this tool, explicitly confirm with the user: - the full table name: `catalog.namespace.table` - every column path to drop - that they understand downstream consumers may fail until updated Prefer checking table metadata with catalogs_tables_metadata_get, and lineage or downstream consumers when relevant, before dropping production columns. The API rejects drops that are known to be unsafe because the field is referenced by partition specs, sort orders, or identifier fields. If the user does not specify `catalog`, use the default `oleander` catalog. If the user does not specify `namespace`, use the default `default` namespace. Input fields: - `paths`: column paths to drop, for example `[["address", "province"], ["age"]]` - `include_deprecated_last_column_id`: optional compatibility flag for catalogs that require deprecated `last-column-id` on add-schema updates - `confirm`: must be `true` because this may break consumers Do not call this tool speculatively. Return the schema evolution result, including dropped field IDs and the new schema ID.
catalogs_tables_loadCreate an Iceberg table from an existing dataset URI that Oleander can access, such as a public HTTP URL or an S3 URI. Use this tool when the user asks to create or load a table from a dataset URL or existing object path. If the user does not specify `catalog`, use the default `oleander` catalog. If the user does not specify `namespace`, use the default `default` namespace. If the user does not specify `table`, ask the user for the table name before calling this tool. Do not invent a table name from the URL, filename, or dataset title. Input fields: - `from`: source dataset URI or URL - `file_type`: one of `parquet`, `csv`, or `json` - `table`: target table name; required and must come from the user - `catalog`: optional; defaults to `oleander` - `namespace`: optional; defaults to `default` - `confirm`: must be `true` because this creates a table The target table must not already exist. For a local file, first use `catalogs_files_stage`, upload it with curl, and then call this tool with the returned destination. Return the created table target and source URI to the user. If the API returns that the source is inaccessible or the table already exists, surface that error directly.
catalogs_files_stageRequest a presigned PUT URL and destination S3 URI for a local dataset file. This tool does not read or upload the local file and does not create the Iceberg table. Use this tool when the user asks to create a table from a local file. If the user only provides a public URL or existing S3 URI, use `catalogs_tables_load` instead. If the user does not specify `catalog`, use the default `oleander` catalog. If the user does not specify `namespace`, use the default `default` namespace. If the user does not specify `table`, ask the user for the table name before calling this tool. Do not invent a table name from the filename, URL, or dataset title. Input fields: - `filename`: basename of the local file being staged - `table`: target table name; required and must come from the user - `content_type`: optional upload content type; defaults in the staging API when omitted - `catalog`: optional; defaults to `oleander` - `namespace`: optional; defaults to `default` After this tool succeeds, complete the workflow yourself: 1. Upload the local file directly with curl using the returned `upload.putUrl`. Use `--request PUT --upload-file <local-file-path>`, add every header from `upload.requiredHeaders` with `--header`, and do not add Oleander authorization headers to the presigned request. 2. Check that curl succeeds. Never read or base64-encode the file for the upload. 3. Call `catalogs_tables_load` with `from` set exactly to the returned `destination`, the user's target table, and the matching `file_type`. Presigned URLs expire after `upload.expiresIn` seconds. If the upload URL expires, call this tool again. Quote the URL and header values in the curl command so shell metacharacters are not interpreted.
investigations_listList investigations, optionally filtered by job. Returns investigations with their alert context, status, and diagnosis progress.
investigations_getGet a single investigation by ID, including its diagnosis steps, findings, and related alert/job/pipeline context.
spark_jobs_submitSubmit a Spark job for execution on Oleander serverless Spark. Returns the SUBMIT envelope JSON below as `{ ok: true, data: <envelope> }`. Cluster is fixed to `oleander`; external EMR Serverless / Glue clusters are not supported by this tool. Requires `confirm: true`. Submit response envelope (`data`): { runId: "<uuid>", state: "SUBMITTED", namespace: "<namespace>", jobName: "<job_name>", pipelineId: "<uuid>" | null, submittedAt: "<ISO UTC>", artifact: { name, version, entrypointUri }, compute: { driverMachineType, executorMachineType, executorNumbers }, costEstimate?: <cost block — present only when computable> } // ARTIFACT CHECK — REQUIRED BEFORE EVERY SUBMIT Before submitting, you MUST call spark_artifacts_list. If the artifact exists with status READY, stop — do not upload. Go directly to spark_jobs_submit. Do NOT narrate "let me check" or "uploading now" — just call the tools silently and display only the SUBMIT block output. If upload was performed, do NOT display upload results. The only output the user sees is the SUBMIT block below. Upload is an implementation detail, not a user-facing event. Only upload if: - The artifact does not exist in the list - The user explicitly says "upload a new version" user: submit iris_to_species_summary.py → spark_artifacts_list # check silently → artifact exists and is READY # skip upload silently → spark_jobs_submit # submit directly Pass the artifact basename `name` from spark_artifacts_list to `properties.entrypoint` (not the full storage `entrypoint` URI). Input fields (see inputSchema): `namespace`, `name`, `jobTags`, `runTags`, `properties` (`entrypoint`, `entrypointArguments`, `driverMachineType`, `executorMachineType`, `executorNumbers`, `sparkConf`, `packages`), `confirm`. Machine types (`vCPU` / `RAM`): - `spark.1.c`: 1 vCPU / 2 GB - `spark.2.c`: 2 vCPU / 4 GB - `spark.4.c`: 4 vCPU / 8 GB - `spark.8.c`: 8 vCPU / 16 GB - `spark.16.c`: 16 vCPU / 32 GB - `spark.1.b`: 1 vCPU / 4 GB - `spark.2.b`: 2 vCPU / 8 GB - `spark.4.b`: 4 vCPU / 16 GB - `spark.8.b`: 8 vCPU / 32 GB - `spark.16.b`: 16 vCPU / 64 GB - `spark.1.m`: 1 vCPU / 8 GB - `spark.2.m`: 2 vCPU / 16 GB - `spark.4.m`: 4 vCPU / 30 GB - `spark.8.m`: 8 vCPU / 60 GB - `spark.16.m`: 16 vCPU / 120 GB Resource sizing follows `driverMachineType`, `executorMachineType`, and `executorNumbers`. Spark conf entries for driver/executor cores, memory, and executor instance counts are ignored in favor of those machine types. // SUBMIT DISPLAY — EXACT FORMAT REQUIRED The first and only thing displayed to the user after submit MUST be this exact format. Read every value directly from the envelope; do not invent or fetch them. Do NOT display upload results, upload confirmation, or any description of what the job does: Job successfully submitted **<namespace>/<jobName>** **Run:** <runId> **Artifact:** <artifact.name> v<artifact.version> **State:** <state> **Namespace:** <namespace> **Compute:** <compute.executorNumbers>x <compute.executorMachineType> **Submitted:** <submittedAt ISO UTC> Never show: - Driver machine type in the submit block - Upload confirmation messages - "Job submitted successfully" without the namespace/jobName header - Plain key value format — always use **key:** value - Any description of what the job does The Compute line format is ALWAYS: <compute.executorNumbers>x <compute.executorMachineType> Example: 2x spark.1.b Never show driver machine type in the submit block. // COST ESTIMATE If the response includes `costEstimate`, display this block immediately after the SUBMIT block. Omit the entire block when `costEstimate` is absent. When `costEstimate.estimatedCost` is present (history-backed): Cost estimate (~<avgDurationSeconds>s avg, <runsInHistory> runs): **Est. total:** ~$<estimatedCost> vCPU: $<breakdown.vcpu> Memory: $<breakdown.memory> Otherwise (`costEstimate.fallback` is present, no run history), iterate `fallback` and render one row per entry in order: Cost estimate (no run history): <durationSeconds/60> min: ~$<estimatedCost> View trace (only if `pipelineId` is non-null): https://oleander.dev/app/pipelines?pipelineId=<pipelineId>&runId=<runId> // POLL Immediately after submitting, automatically call jobs_runs_get with the returned runId on this schedule: - Every 10 seconds when state is SUBMITTED or QUEUED - Every 30 seconds when state is SCHEDULED, START, or RUNNING Do not wait for the user. Do not ask for confirmation. Display state transitions inline as they occur: SUBMITTED → QUEUED → SCHEDULED → START → RUNNING → COMPLETE // ON COMPLETE — GATHER FIRST, DISPLAY SECOND The job run reaching COMPLETE is not the end of the task. STOP. Do not display anything yet. You MUST call ALL of the following tools right now, in this exact order, before displaying any output whatsoever: 1. jobs_cost_get — call this now, always required 2. jobs_lineage_get — call this now, always required 3. pipelines_cost_get — call this now if pipeline_id available You are not done until all required tool calls above are complete and their results are in hand. Only then may you display output. Checklist before displaying — all must be true: [ ] jobs_cost_get has been called and returned [ ] jobs_lineage_get has been called and returned [ ] display has NOT started yet Do NOT display job run context. Do NOT summarize. Do NOT stop. Do NOT ask the user. Do NOT explain what you are about to do. Just call the tools. // ON COMPLETE — DISPLAY Only after all tool calls above are complete, display in this exact order: // 1. JOB RUN CONTEXT Job complete: **<namespace>/<jobName>** **Run** <runId> **State** COMPLETE **Duration** <duration>s **Source** <artifact.name>@v<artifact.version> // RUN_TRACE_FORMAT is a section block — always embed inside a // larger job run or pipeline run display, not as a standalone header. **Run trace:** <namespace>/<job_name> · <run_id_short> · <duration>s **Submitted:** <submitted_at ISO UTC> **Queued:** <queued_at ISO UTC> (+<delta>s) **Scheduled:** <scheduled_at ISO UTC> (+<delta>s) **Started:** <started_at ISO UTC> (+<delta>s) **Ended:** <ended_at ISO UTC> (+<delta>s) View trace: https://oleander.dev/app/pipelines?pipelineId=<pipeline_id>&runId=<run_id_full> Run trace rules: - Omit any state where the timestamp is null or unavailable — never show null or placeholder values - Each delta (+Xs) is elapsed time since the previous state, not since submission Example: Queued delta = queued_at minus submitted_at Started delta = started_at minus scheduled_at - Duration is execution time: started_at to ended_at, for any pipeline or job type — not limited to Spark - For RUNNING jobs, replace Ended with: **Running:** <current_time minus started_at>s (in progress) - If any phase delta exceeds its historical p50 by more than 50%, append (slow) to that line Example: **Queued:** 2026-05-01T15:20:03Z (+94s) (slow) - Omit the View trace line when pipeline_id is null or unavailable Datasets: ┌───────────┬──────────────────────────┬──────────────┬─────────┬────────┐ │ Direction │ Table │ Snapshot │ Records │ Size │ ├───────────┼──────────────────────────┼──────────────┼─────────┼────────┤ │ Read │ <symlink_name> │ <snapshot> │ <count> │ <size> │ │ Write │ <symlink_name> │ <snapshot> │ <count> │ <size> │ └───────────┴──────────────────────────┴──────────────┴─────────┴────────┘ I/O Summary: Bytes read: <X> MB Bytes written: <X> MB Records read: <X> Records written: <X> Warnings: - <warning> [fatal|non-fatal] Exit: No errors. Job exited cleanly. View trace: https://oleander.dev/app/pipelines?pipelineId=<pipelineId>&runId=<runId> // 2. COST // COST BLOCK FORMAT Cost: **<job_name>** (<run_id_short>) **Total:** $X.XXXX **Duration:** <duration>s **vs. baseline:** +X% above avg (last 30 runs) Breakdown: ┌───────────────────┬─────────┐ │ Usage │ Cost │ ├───────────────────┼─────────┤ │ X.XXXX vCPU-hours │ $X.XXXX │ │ X.XXXX GB-hours │ $X.XXXX │ ├───────────────────┼─────────┤ │ Total │ $X.XXXX │ └───────────────────┴─────────┘ **Cost per record written:** $X.XXXXXX Always add a one-line plain-English cost summary after the table. Example: "A very cheap run — $0.0046 for 71s of Spark compute." // 3. LINEAGE // LINEAGE GRAPH FORMAT Lineage: **<job_name>** (<run_id_short>) [READ] <symlink_name> (snapshot <external_version_id>) │ Schema: <field1>, <field2>... │ ├─► [RUN] <child_job_name> (<duration>s, <state>) │ │ │ ▼ │ [WRITE] <symlink_name> (snapshot <external_version_id>) │ Schema: <field1>, <field2>, <field3>... │ │ │ ▼ │ [RUN] <downstream_job> (<engine>, <state>) │ │ │ ▼ │ [OUT] <field1>, <field2> │ └─► [RUN] <child_job_name> (<duration>s, <state>) Lineage rendering rules: - Always use symlink names not raw S3/warehouse paths - Show snapshot IDs (external_version_id) next to dataset nodes - Show duration and state next to run nodes - Label engine type for non-Spark runs (DuckDB, Airflow, dbt) - Indent child runs under their parent to show logical grouping - Show schema fields for WRITE nodes — truncate to 5 fields with "... and N more" if schema is large - Show schema fields for READ nodes where available - Always show downstream consumers if present — this is the key signal for impact analysis // LINEAGE SUMMARY After the graph, always display: Summary: - Inputs (<count>): - <symlink_name> — <N> fields (<field list>) - Outputs (<count>): - <symlink_name> — <N> fields written via <description> - Downstream (<count> consumers): - <symlink_name> consumed by <job_name> (<engine>) - Signals: - <schema changes, unexpected consumers, cross-engine flows, etc.> Omit any section where count is zero rather than showing empty lists. If there is only one input or output, omit the count and write it as a single line. // 4. PLAIN-ENGLISH SUMMARY Always end with a single summary line combining run, cost, and lineage context. Example: "iris_species_summary written successfully — 71s, $0.0046, reading from default.iris_dataset, already consumed downstream by a downstream consumer." // 5. PROACTIVE QUESTIONS // PROACTIVE QUESTIONS After the summary line, always offer the most relevant next actions based on what was observed in the run. Pick the most relevant 2-3 from this list — do not list all of them: - If downstream consumers exist in the lineage graph: "A downstream consumer is already reading <output_table> — want me to trace its full dependency graph?" - If this run has been submitted before: "Want me to compare this run's lineage graph and cost against the previous run to spot any differences?" - If cost is above baseline: "This run was X% above baseline — want me to break down which child job drove the cost increase?" - If records written differ significantly from records read: "Only <X> of <Y> input records were written — want me to check the lineage for dropped or filtered records?" - If schema of output dataset changed: "The output schema changed since the last run — want me to diff the schemas and check downstream impact?" - If multiple child runs exist: "This pipeline spawned <N> child runs — want me to break down cost and duration per child job?" - If no downstream consumers exist yet: "No downstream consumers yet — want me to query the lake to verify the output table looks correct?" - Always available: "Want me to submit another run with different compute or configuration?" // ON FAIL OR ABORT The job run reaching FAIL or ABORT is not the end of the task. STOP. Do not display anything yet. Immediately and automatically call investigations_list — do not wait for the user. Do not summarize. Do not stop. Job failed: **<namespace>/<jobName>** **Run** <runId> **State** FAIL **Duration** <duration>s **Source** <artifact.name>@v<artifact.version> // RUN_TRACE_FORMAT is a section block — always embed inside a // larger job run or pipeline run display, not as a standalone header. **Run trace:** <namespace>/<job_name> · <run_id_short> · <duration>s **Submitted:** <submitted_at ISO UTC> **Queued:** <queued_at ISO UTC> (+<delta>s) **Scheduled:** <scheduled_at ISO UTC> (+<delta>s) **Started:** <started_at ISO UTC> (+<delta>s) **Ended:** <ended_at ISO UTC> (+<delta>s) View trace: https://oleander.dev/app/pipelines?pipelineId=<pipeline_id>&runId=<run_id_full> Run trace rules: - Omit any state where the timestamp is null or unavailable — never show null or placeholder values - Each delta (+Xs) is elapsed time since the previous state, not since submission Example: Queued delta = queued_at minus submitted_at Started delta = started_at minus scheduled_at - Duration is execution time: started_at to ended_at, for any pipeline or job type — not limited to Spark - For RUNNING jobs, replace Ended with: **Running:** <current_time minus started_at>s (in progress) - If any phase delta exceeds its historical p50 by more than 50%, append (slow) to that line Example: **Queued:** 2026-05-01T15:20:03Z (+94s) (slow) - Omit the View trace line when pipeline_id is null or unavailable **Exit:** <error detail> View trace: https://oleander.dev/app/pipelines?pipelineId=<pipelineId>&runId=<runId> Then display investigation summary if found, then offer: - "Want me to dig into the logs for the root cause?" - "Want me to compare this failed run against the last successful run?" - "Want me to resubmit with a different compute configuration?" Use this tool when the user wants to run a Spark job. - To upload an artifact first: use spark_artifacts_upload - To list available artifacts: use spark_artifacts_list - To abort a running job: use spark_jobs_abort - To check run status: use jobs_runs_get // GENERAL RULES // Naming - Always write "oleander" with a lowercase "o" — never "Oleander" - Always prefer symlink names (e.g. default.iris_dataset) over raw S3/warehouse paths - Shorten run IDs to first 8 characters when displaying, except in the initial submit block where the full run ID is shown // Formatting - Always display timestamps in ISO 8601 UTC (e.g. 2026-05-01T15:19:58Z) - Always display costs to 4 decimal places (e.g. $0.0046) For costs under $0.0001, use scientific notation - Format large numbers with commas (e.g. 1,234,567 records, 2.3 GB) - Bold field labels using **label** syntax, not values In terminal contexts where markdown does not render, fall back to plain key: value format // Data handling - Omit sections where data is unavailable rather than showing nulls - When run_id, pipeline_id, or namespace/job are already known from a prior tool call in this session, reuse them without asking the user to repeat them // Error handling - When a tool returns an error, always display: Error: <message> [tool: <tool_name>] Then suggest the most relevant next step // Agentic behavior - Never ask the user if they want cost, lineage, or investigations — always call them automatically at the right step - Never summarize or stop after a terminal state — always gather cost and lineage first before presenting final output - Never describe what a job does at submit time — that appears in the lineage summary after the run completes
spark_jobs_abortAbort a Spark **job run** in Oleander by its Oleander **run** UUID. Returns JSON: `{ ok: true, data: <abort API body> }` (shape from `POST /api/v2/runs/{run_id}/abort`). Typical success means cancellation was accepted or the run was already terminal; conflicts return an error payload. Only meaningful while the run is still active (for example SUBMITTED, QUEUED, SCHEDULED, START, RUNNING). If the run is already COMPLETE, FAIL, or ABORT, expect a conflict-style error rather than a silent no-op. Requires `confirm: true`. Confirm with the user before aborting — this terminates execution and cannot be resumed from the same run ID. Input fields: - `run_id`: Oleander run UUID (shorten to first 8 characters when displaying) - `confirm`: must be `true` When presenting results, always display: Job Aborted: <job_name from jobs_runs_get if available> (<run_id_short>) State: ABORT (or actual terminal state if already ended) Aborted at: <ended_at from jobs_runs_get when available UTC> Duration: <duration>s (omit if unavailable) Omit lines rather than showing nulls. After aborting, **automatically** call investigations_list — do not wait for the user. Always add a one-line plain-English summary after the block. Use this tool when the user wants to stop a running Spark job. - To check current run state first: use jobs_runs_get - To view investigations after abort: use investigations_list - To resubmit work: use spark_jobs_submit // GENERAL RULES // Naming - Always write "oleander" with a lowercase "o" — never "Oleander" - Always prefer symlink names (e.g. default.iris_dataset) over raw S3/warehouse paths - Shorten run IDs to first 8 characters when displaying, except in the initial submit block where the full run ID is shown // Formatting - Always display timestamps in ISO 8601 UTC (e.g. 2026-05-01T15:19:58Z) - Always display costs to 4 decimal places (e.g. $0.0046) For costs under $0.0001, use scientific notation - Format large numbers with commas (e.g. 1,234,567 records, 2.3 GB) - Bold field labels using **label** syntax, not values In terminal contexts where markdown does not render, fall back to plain key: value format // Data handling - Omit sections where data is unavailable rather than showing nulls - When run_id, pipeline_id, or namespace/job are already known from a prior tool call in this session, reuse them without asking the user to repeat them // Error handling - When a tool returns an error, always display: Error: <message> [tool: <tool_name>] Then suggest the most relevant next step // Agentic behavior - Never ask the user if they want cost, lineage, or investigations — always call them automatically at the right step - Never summarize or stop after a terminal state — always gather cost and lineage first before presenting final output - Never describe what a job does at submit time — that appears in the lineage summary after the run completes
docs_searchSearch the official Oleander documentation site (docs.oleander.dev). Use this for product how-tos, setup guides (dbt, BigQuery, MCP/agent configuration, Spark jobs), API reference, CLI, and the TypeScript or Python SDKs — not for the organization's own telemetry (use the lineage, run, and lake tools for that). Results include titles, https links, page paths, and snippets. When a full page is needed, call docs_pages_get with the page path from a result. Prefer searching the docs over guessing product behavior, and include relevant doc links in answers.
docs_pages_getLoad one full Oleander documentation page by path (for example `lake`, `api-reference/query-the-lake`, `sdk/typescript/query`). Use page paths returned by docs_search.
charts_renderRender an inline chart from a small, already-aggregated dataset. In the oleander web chat the chart draws directly in the conversation; in other MCP clients this returns the validated chart spec as structured data. Use this when the user asks for a chart, graph, plot, or visualization, or when a query result is a trend over time that is clearly better seen than read. Typical flow: run lake_query with an aggregating query (GROUP BY), then pass the result here. Data rules: - Aggregate in SQL first. Never chart raw rows: at most 200 data points, and bar charts read best with 20 or fewer categories. - At most 4 series. If there are more, keep the top 3 and fold the rest into an "Other" series in SQL. - Series values must be numeric. The x-axis value (`x_key`) may be a category name, a number, or an ISO 8601 date/timestamp string; sort rows by x in the SQL so the chart reads left to right. - Every record in `data` must contain `x_key` and each series `key`. Chart type guide: `line` or `area` for trends over time, `bar` for comparing categories, `scatter` for correlation between two measures (numeric x required). The rendered chart includes a built-in "view data" table, so do not repeat the charted numbers as a table afterwards — follow the chart with a one-line plain-English takeaway instead.
get_more_toolsCheck for additional tools whenever your task might benefit from specialized capabilities - even if existing tools could work as a fallback.
oleander: подключить к Claude, ChatGPT, Cursor · Connectors.fun