Skip to main content
Glama
ruya-grp

fusion-query-mcp

by ruya-grp

fusion-query-mcp

An MCP server that lets an AI agent explore Oracle Fusion Cloud (SaaS) schema metadata, run pre-built BI Publisher reports with bind parameters, and — the part that matters — validate the results against ground truth you already trust, iterating until the answer is provably right.

Read §1 before anything else. This server does not run arbitrary SQL, and that is not a gap waiting to be filled. It is a boundary the Fusion pod enforces. Every design decision below follows from it.


1. The boundary that shapes everything

1.1 Fusion has no database

Oracle Fusion SaaS gives no direct database access. The only sanctioned way to get SQL results out is a BI Publisher Data Model of type SQL Query, executed through the BI Publisher Run Report API. The SQL inside a data model is fixed at design time; only parameter values travel over the API.

That much is documented Oracle behaviour. The interesting part is what happens next.

1.2 The obvious design, and why it is dead

The obvious way to build a query server on top of that is to create one data model whose SQL is a lexical parameter holding the whole statement:

SELECT * FROM (&p_query)      -- Plan A
SELECT &p_select FROM &p_from &p_where &p_group &p_order   -- Plan B

An &-prefixed BI Publisher parameter is substituted into the SQL text before parsing, so in principle it can carry anything. This is the widely repeated community pattern, and it is what this project was originally built around.

It does not work on a current Fusion pod. Controlled experiments against the customer's live pod, not guesses:

#

Experiment

Result

1

Run the report with p_query = SELECT 1 AS N FROM DUAL, parameter proven to arrive

ORA-00903: invalid table name

2

Inspect what the engine executed

SELECT * FROM () — the lexical was replaced by an empty string

3

Same with a bind parameter (:p_x) and a sentinel value

Sentinel came back verbatim

So: binds are substituted, lexicals are not. Not "malformed", not "mis-declared" — substituted with nothing, silently, every time. Plan B fails identically because it also uses &. This is near-certainly deliberate Oracle hardening: arbitrary SQL text arriving over a SaaS reporting API is exactly the thing a multi-tenant vendor must prevent, and a lexical parameter is the hole that would allow it.

Consequences for anyone tempted to "fix" this:

  • There is no clever escaping, encoding or wrapper that restores lexicals. The substitution happens (or does not) inside BI Publisher, before your text is ever SQL.

  • View Data in the data-model editor also renders the lexical empty, so you cannot distinguish "my parameter is wrong" from "this pod does not do lexicals" by looking at the editor. Only the round trip tells you, and it tells you ORA-00903 either way. That ambiguity is what costs the day.

  • The failure mode is identical to a beginner mistake, which is why the original design survived so long before being disproved.

1.3 What the server does instead

Pre-built reports, run with bind values.

An administrator creates each report's data model once, with real SQL and :bind variables. The server never sends SQL — it sends values for the binds of a report that already exists in the catalog. The registry of runnable reports lives in config.yaml under fusion.reports.

Schema exploration survives in full, because its SQL is fixed and only the LIKE patterns vary. That is what the three shipped reports are.

┌──────────────┐   MCP (stdio)   ┌──────────────────────────┐   HTTPS   ┌──────────────────────────────┐
│ Claude Code /│ ───────────────▶│  fusion-query-mcp        │ ─────────▶│ Oracle Fusion Cloud          │
│ Claude agent │                 │  ├ report registry       │           │  BI Publisher                │
└──────────────┘                 │  ├ execution backends    │           │  ├ XX_MCP_LIST_TABLES_RPT    │
                                 │  │  ├ SOAP (this pod)    │           │  ├ XX_MCP_DESCRIBE_TABLE_RPT │
                                 │  │  └ REST (404s here)   │           │  ├ XX_MCP_SEARCH_COLUMNS_RPT │
                                 │  ├ xml result parser     │           │  └ …your own reports         │
                                 │  ├ validation engine     │           │     SQL fixed, :binds vary   │
                                 │  ├ fixtures store        │           └──────────────────────────────┘
                                 │  ├ fusion hints KB       │
                                 │  └ audit log             │
                                 └──────────────────────────┘

This costs flexibility and buys three things worth having:

  1. It works. The path is verified end to end on a live pod.

  2. A real boundary. An agent can only run what an administrator built and registered. See §9 Security.

  3. Reviewable SQL. Every statement lives in a catalog object a functional owner can open, read and change, instead of being improvised per request.

Non-goals: no data model / catalog object creation via API; no DML or DDL of any kind; no pagination or streaming; no OTBI logical SQL; no per-end-user identity mapping (single service account).

1.4 The legacy lexical path is still shipped

fusion_run_query and fusion_validate_query still exist, and fusion.datasources / engine_mode still configure them. They are for a pod that does honour lexical substitution. On a pod that does not — and you should assume yours does not until you have proved otherwise — they will fail with ORA-00903 and the server's error hint will tell you exactly that and point you at the report tools. Do not spend a day there.


Related MCP server: mcp-oraclefusion

2. The tool surface

All tools are read-only against Fusion's data and return structured JSON with a short summary. Two write to the BI Publisher catalog rather than to business records: fusion_author_report (opt-in) and fusion_bootstrap.

Tool

Purpose

fusion_list_reports

What this server may run at all: registered reports, their parameters and defaults. Start here.

fusion_run_report

Run a registered report with bind values

fusion_validate_report

Run a registered report once, evaluate ground-truth expectations

fusion_list_tables

Tables by LIKE pattern, biggest first (runs list_tables)

fusion_describe_table

Ordered columns, types, comments (runs describe_table)

fusion_search_columns

Which table holds column X (runs search_columns)

fusion_docs_describe_table

Table docs from the local OEDM snapshot: column meanings, PK/FK join edges, view SQL — instant, no pod call (§2b)

fusion_docs_search_columns

Full-text search over documented column names and descriptions in the local snapshot (§2b)

fusion_describe_flexfields

DFF definitions: which ATTRIBUTEn column carries which custom segment (runs describe_flexfields)

fusion_adhoc_query

Opt-in (§2a): run one guarded SELECT via an ephemeral authored report

fusion_author_report

Opt-in (§2a): mint a persistent report from SQL and register it

fusion_save_fixture

Persist expectations as a regression test

fusion_list_fixtures / fusion_get_fixture

Browse saved fixtures

fusion_get_hints

Fusion schema traps and correct predicates

fusion_list_pods

Which pods this server can reach (from pods/*/config.yaml) and which is active

fusion_use_pod

Bind the session to one pod — config, credentials, reports, snapshot switch together

fusion_bootstrap

Create the four shipped exploration reports on a pod that lacks them, from SQL inside this package — the fix when the tools above fail on a healthy pod (§3.0)

fusion_health_check

Probe credentials, transport, parsing, registry

fusion_run_query / fusion_validate_query

Legacy lexical path — needs a pod that substitutes lexicals (§1.4)

Also exposed: the knowledge base as MCP resources (fusion://hints, fusion://hints/{topic}) and the working protocol as an MCP prompt (fusion-query-workflow).

The protocol an agent follows is: discover reports → explore schema (tables, columns, flexfields) → run a registered report, or compose SQL and run it ad-hoc → validate against ground truth.

2a. Agent-composed SQL — fusion_adhoc_query and fusion_author_report

Both are off by default and exist for one goal: hand the agent a report specification and let it produce the numbers — explore the schema, resolve DFF segments, build the query, run it — or mint the report in the system, with no human in the loop.

The mechanism is the §1 discovery turned around. The pod refuses to let SQL travel through a report (&p_query is never substituted), but nothing stops SQL from becoming one: fusion_adhoc_query authors an ephemeral data model + report via the catalog service (§3.2a), runs it once through the normal pipeline — binds, parameter-echo verification, redaction, audit — and deletes both objects in a finally. The real SQL enters the audit log before the round trip. fusion_author_report does the same authoring but keeps the pair, verifies it with a probe run, and registers it in reports.dynamic.yaml (never config.yaml — see below).

What survives from the report path: the read-only guard (now guarding genuinely caller-supplied SQL), the deny-list, max_sql_chars, the parameter-echo check on every bind, redaction and audit. What changes is the trust boundary, and this must be said plainly:

With allow_adhoc_queries on, the allow-list is no longer "the registered reports" but "any SELECT the guard admits, as the service account". The real boundary becomes the service account's Fusion roles and row-level Data Security. Scope that account before switching this on:

fusion:
  allow_adhoc_queries: true      # ephemeral ad-hoc SELECTs
  allow_report_authoring: true   # persistent agent-minted reports

Three rules keep the two registries honest: config.yaml is human-owned and is never written by the server; agent-minted reports live in reports.dynamic.yaml (gitignored), so wiping them is deleting one file; and on a name collision config.yaml always wins — an agent report can never shadow a human one.

2b. The local docs snapshot — fusion_docs_*

Oracle publishes full documentation for every Fusion table and view (the OEDM books on docs.oracle.com): column business descriptions, primary keys, foreign keys, indexes, flexfield mappings, and for views their defining SQL. None of that lives in the pod's dictionary, and each live schema tool costs a 12–14 s BI Publisher round-trip.

Build a local snapshot once:

pip install fusion-query-mcp[scrape]
fusion-query-oedm --release 26b --books procurement financials

Snapshots are one file per quarterly release, kept together under snapshots/ (snapshots/oedm_docs_<release>.sqlite3, the CLI's default name). On first docs use the server resolves the pod's release — the fusion.pod_release pin if set, else one ad-hoc probe of AD_PRODUCT_GROUPS when allow_adhoc_queries is on — and serves the matching file, falling back to oedm_db_path (and then to the newest release-named sibling) when no exact match exists. Every fallback is echoed in the tools' notes, and fusion_health_check reports pod_release next to the snapshot it chose.

When a snapshot is available, fusion_docs_describe_table and fusion_docs_search_columns serve exploration from it in milliseconds — including full-text search over column descriptions, so "supplier hold reason" finds the column without knowing any naming convention. Responses are slice-shaped (filter with columns_like) to keep token cost down.

The division of authority is deliberate: docs for meaning, pod for truth. The snapshot never decides what exists — a column present in the docs but missing on the pod fails loudly inside fusion_adhoc_query's echo-verified execution, so drift cannot produce a silent wrong answer. Custom objects and the pod's case-twin duplicates are only visible to the live tools; in a 10-table trial (release 26c vs a live pod) the docs matched the dictionary column-for-column on every verified table. Re-runs resume where they stopped; --refresh re-fetches after a release upgrade.

2c. The REST catalogue snapshot — fusion_api_*

Everything above reads. This snapshot exists to support doing: it is what turns "register a purchase requisition" into a list of questions a person can answer.

fusion-query-apicat purchaseRequisitions suppliers --probe-writes

Two passes, because the pod tells the truth in two different ways.

Pass 1 — the map. GET /fscmRestApi/resources/latest/<res>/describe gives every field with type, business description, length, list-of-values and child collections. Complete and safe. It is also large: one resource is 0.5–1.3 MB, so runtime discovery is not an option — this is exactly why a snapshot exists, the same argument as §2b.

Pass 2 — the truth (--probe-writes). POST an empty body and read which layer rejects it. HTTP 401/403 means the account cannot write the resource at all; HTTP 400 means the request cleared authorization and reached business validation, which then names the fields it wanted. Nothing is created either way.

Pass 2 exists because the declared metadata is wrong in both directions, measured on the pod 2026-08-15:

Resource / field

/describe says

The pod does

purchaseOrders

advertises POST

HTTP 403 — refused

ExternallyManagedFlag

mandatory: false

demanded at POST (POR-2010313)

RequisitionHeaderId

mandatory: true

generated; must not be sent

The best derivation from the declared flags still missed a genuinely required field — and that is the failure that hurts, because the agent builds a confident, incomplete payload. So the snapshot stores declared flags as declared_mandatory hints, and required_fields separately as what the pod itself demanded. required_source says which you are looking at; when it says "not probed", do not trust the hints.

fusion_api_list_actions lists what this account was observed able to create — never what /describe advertises. fusion_api_describe returns the interview script for one resource (25 settable fields on purchaseRequisitions, not the 68 declared; LOV child views separated from real child collections like lines). fusion_api_search maps a phrase the user said onto the field name the API expects.

Unlike the OEDM snapshot, this one is per pod, not per release: it records what a specific service account was observed able to do, and that does not carry across pods even on the same release.

The safety limit of pass 2, stated plainly: it is safe because a resource with required fields cannot be created by an empty body. A resource whose fields are all optional would be created. So it never runs unless asked, never sweeps discovered resources, and treats an HTTP 201 as an incident — deleting the record immediately and reporting it loudly.

2d. Doing things — fusion_resolve_value, fusion_prepare_action, fusion_commit_action

Everything before this reads. These three change data, and they are shaped around what the pod does not provide.

The loop. fusion_api_list_actions says what this account may create. fusion_api_describe gives the field list. fusion_resolve_value turns each name the user said into the id the API wants. fusion_prepare_action renders exactly what would travel and returns a token — nothing reaches Fusion yet. Only fusion_commit_action, with that token and confirmed=True, sends it.

Why the pause is here. Fusion has no "show a human first": an authorised, valid POST executes on arrival. Oracle already enforces privilege (HTTP 403 on a resource this account cannot write) and validation (PreparerId is required, POR-2010313) far better than this server could — see §9 — so what is left for this layer is confirmation, idempotency, and a record of intent.

Why refusal is a normal outcome. There is no dry-run for a Fusion create: a valid POST commits, so an invalid one is the only safe probe. Being refused is therefore how missing fields are discovered, and fusion_commit_action returns the pod's own sentence plus the field names parsed out of it rather than a summary — that sentence is the next question to ask the user.

Idempotency. Because retrying is the normal path, accidental double-submission is the normal risk, and Fusion offers no idempotency key on these resources. A fingerprint of verb + resource + record + payload is recorded when a commit succeeds; an identical action returns the first outcome instead of creating a second requisition. Only successes are remembered — re-running a refused attempt after adding the missing field is the intended workflow. The guard is per-process and not persisted, and it survives a pod rebind so rebinding cannot become a way to commit twice.

Resolving values runs on the read channel, not the action channel: the list-of-values views /describe advertises are not addressable over REST (all four candidate paths answered 404, probed 2026-08-15), so the mapping lives in knowledge/value_resolvers.yaml and the lookup is an ordinary guarded, audited SELECT. The user's text always travels as a bind. When several rows match, the candidates come back for the user to choose between — the tool never picks one.

Policy lives under actions: in config.yaml: a general posture plus exceptions named one at a time (actions.overrides), the same shape as the SQL guard's forbidden keywords plus explicit allowances.

2e. Scheduled processes — fusion_submit_job, fusion_job_status

The REST channel covers what has a resource. A great deal of Fusion does not: Import Payables Invoices, Create Accounting, bulk loads. Those run through ErpIntegrationService (61 operations, verified on the pod 2026-08-15).

fusion_submit_job(package, definition, parameters, confirmed=True)  -> request id
fusion_job_status(request_id)                                       -> state

Two things to know, both from the service's own contract rather than from documentation:

parameters is positional. The WSDL states it plainly: "The order of the parameters is maintained as per the list. The corresponding entry in the list should be blank when a given parameter is not passed." Pass "" for a skipped argument — omitting it shifts every later one by a slot, and the job runs against the wrong data without complaining.

A submitted job cannot be recalled. This is the sharp difference from the REST channel. A REST create is usually refused and changes nothing, so retrying is cheap; once an ESS request id exists, the job is queued. Submission returns when the job is queued, not when it has done anything — poll fusion_job_status before telling anyone it worked. finished is false for any state not known to be terminal, deliberately: one extra poll beats losing track of a running job.

Bulk loads (FBDI). fusion_import_bulk_data reads a file from disk, uploads it to UCM and queues the import jobs that consume it; fusion_find_uploaded_files answers "did it land, and in the right place?" without writing anything.

fusion_import_bulk_data(file_path, account, jobs, confirmed=True)
fusion_find_uploaded_files(prefix, account)   # read-only

Two things to get right, both taken from the service's own schemas (DocumentDetails.xsd, EssJob.xsd) rather than from documentation:

The UCM account is the quiet failure. A file filed under the wrong account uploads successfully and is then invisible to the import job — no error anywhere, on either side. That is why account has no default and why fusion_find_uploaded_files exists. Check it against the job's own documentation (fin$/payables$/import$ and the like) before confirming.

Job parameters are encoded two different ways in the same service. This is the trap worth memorising:

Operation

Tool

Encoding

submitESSJobRequest.paramList

fusion_submit_job

repeated elements, one per argument

EssJob.ParameterList

fusion_import_bulk_data

single comma-separated string

exportBulkData.parameterList

fusion_export_bulk_data

single comma-separated string

paramList and parameterList differ by two characters and take opposite shapes; all three were read off the schemas rather than inferred. A parameter containing a comma is refused rather than silently split — the schema defines no escaping, so splitting would shift every later argument by a slot and the job would run against the wrong data without complaining. Use fusion_submit_job for such parameters.

When a job fails, get the log. fusion_job_status reporting ERROR is the least useful true statement available; the reason is in the log.

fusion_job_log(request_id, file_type="log", save_to="./logs")

File contents are never returned inline — a job log can be megabytes — so pass save_to to write them and get the paths back. One undecodable file in a response does not lose the others.

The rest of the channel. fusion_export_bulk_data runs an extract job and leaves the output in UCM (marked destructive because a queued job cannot be recalled, not because it changes data). fusion_update_interface_data is the repair path: when an import loads ten thousand rows and rejects forty into the interface tables, it replaces those forty rather than reloading everything — load_request_id decides which load is being corrected, so read it back to the user before confirming.

2f. The worklist — fusion_list_tasks, fusion_task_detail, fusion_act_on_task

Approvals and notifications waiting for the signed-in user. A third channel: neither the REST resources nor ErpIntegrationService reach it.

fusion_list_tasks(status="ASSIGNED")
fusion_task_detail(number)
fusion_act_on_task(number, outcome, comment, confirmed=True)

Finding it took six failed guesses. fscmRestApi/.../notifications, worklistTasks under both the FSCM and HCM APIs, userNotifications and the older TaskQueryService SOAP endpoint all answer HTTP 404. What answers is /bpm/api/4.0/tasks.

Available outcomes are per task AND per user. Each task carries an actionList; entries with actionType: System are plumbing (REASSIGN, ESCALATE, ACQUIRE), and everything else is a business outcome. Observed on this pod with one account: two e-signature tasks offer APPROVE/REJECT, two change-order tasks offer only OK, and an absence approval offers nothing at all — it is held by a group the account has not acquired. So fusion_act_on_task reads the live task first and refuses an outcome that is not on it; an empty outcomes list is a real state, not an error.

No replay guard, deliberately. An approved task is no longer ASSIGNED, so a second attempt fails at the service. This is the one place in the server where the pod's own state is the duplicate protection.

Task payloads need Accept: application/xml. The business content behind a task (/tasks/{n}/payload) is XML only — application/json and even text/xml answer HTTP 406, while application/xml and */* work.

2g. Any Fusion API, not just procurement

Fusion has one REST API per product pillar. Resources may be written bare (purchaseRequisitions, resolving against FSCM) or qualified with a family:

hcm:workers          -> /hcmRestApi/resources/latest/workers
crm:accounts         -> /crmRestApi/resources/latest/accounts
purchaseRequisitions -> /fscmRestApi/resources/latest/purchaseRequisitions

Aliases (fscm, hcm, crm, scm, fin, prc, helpdesk) are a convenience, not a whitelist: any value ending in RestApi passes through, so a pillar the alias table has never heard of is reachable the day the pod exposes it. What each account may actually write there is still Oracle's decision, and still discovered by probing rather than declared.

2h. Any SOAP service — fusion_soap_*

ErpIntegrationService has a dedicated backend because it is used constantly and its quirks are worth encoding. That does not generalise: Fusion exposes dozens more services, and whole areas of functionality (purchase order change orders, opportunity management, BI Publisher catalog administration) exist only over SOAP.

fusion-query-apicat --soap /fscmService/PurchaseOrderService --soap /crmService/OpportunityService
fusion_soap_list_services()
fusion_soap_describe(service, operation=None)
fusion_soap_call(service, operation, parameters, confirmed=True)

Snapshotted, not discovered per call — and for a correctness reason, not a speed one. The envelope shape differs per service. Measured on the pod:

Service

Shape

/fscmService/ErpIntegrationService

wrapper in a /types/ namespace, payload in the service namespace

/xmlpserver/services/v2/CatalogService

one namespace for everything, no /types/

/crmService/OpportunityService

/types/ again, unrelated service namespace

An invoker that hardcoded any one of those would build a perfectly well-formed envelope that the other two reject. So each service's namespace pair is read from its own WSDL and stored. (The speed argument holds too — the ERP WSDL is 128 KB and its parameter names live in separately-fetched schemas.)

Both shapes are verified live: getESSJobStatus through the generic path returns the same answer as the dedicated fusion_job_status, and getFolderContents on the single-namespace BI Publisher service succeeds.

Parameters are sent in contract order, not dict order. A declared parameter you omit is sent as an empty element rather than dropped, because for positional operations omitting it shifts every later argument by a slot. A name that is not in the contract is reported back as ignored rather than silently discarded.

Everything here is gated as destructive, including read-only operations. Maintaining a list of which of several hundred operation names are safe would be a list that is wrong the moment a service is added; one door with one confirmation is the honest trade.

Descriptive flexfields — fusion_describe_flexfields

Customer-added fields land in the generic ATTRIBUTE1..n columns, and only the DFF definition says what each one means. The tool reads the definition tables this pod actually has (fnd_df_segments_b joined to _tl for the user-facing prompt, language-filtered), so a spec saying "supply duration" is resolvable to PO_HEADERS_ALL.ATTRIBUTE2 without a human. The DFF code is usually the base table name without _ALL. Two traps it already encodes: on this pod mainline definition rows carry SANDBOX_ID = '1' — the "obvious" SANDBOX_ID IS NULL filter returns zero rows forever — and segments outside Global Data Elements are only valid where ATTRIBUTE_CATEGORY equals their context code.

Row limits

A registered report has no row cap of its own — the data model returns what it returns, and the tool layer slices to max_rows and reports truncated honestly. Defaults come from limits.default_max_rows, ceiling from limits.hard_max_rows.

The guard

The SQL guard (SELECT/WITH only, DML and DBMS_* rejected, deny-list patterns, row-cap injection, missing-alias warning) protects every path that carries caller-supplied SQL: the legacy fusion_run_query, and now fusion_adhoc_query / fusion_author_report (§2a). It is deliberately absent from the registered-report path: there is no SQL in a report call to guard. The equivalent control there is that an administrator wrote the SQL and registered the report.


3. One-time Fusion setup — once per report

A Fusion administrator must do this. The server cannot create catalog objects for you. Do it once per report you want the agent to be able to run; the four exploration reports in §3.3 are the minimum for a useful server.

For those four, it is one command — they ship as SQL files inside the package and fusion-query-bootstrap creates them (§3.0). Reach for §3.2a only for a report of your own, and for §3.2b only when you would rather click.

3.0 The four core reports — fusion-query-bootstrap

fusion_list_tables, fusion_describe_table, fusion_search_columns and fusion_describe_flexfields are not implemented in Python: each one runs a BI Publisher report that has to exist in your catalog first. Until it does, the tools fail — and they fail after fusion_health_check passes, because the connection is fine and only the catalog objects are missing.

fusion-query-bootstrap --dry-run     # checks the manifest, contacts no pod
fusion-query-bootstrap               # creates and verifies all four

The SQL lives in src/fusion_query_mcp/bootstrap/*.sql and the rest of each contract — binds, defaults, and the column aliases the server parses — in manifest.yaml beside it. §3.3 below documents those files; it is no longer the place you copy them from.

What the command guarantees:

  • Offline-fatal first. Every check that does not need a pod — the read-only guard, declared columns against the SQL's own aliases, declared parameters against its :binds — runs before the first network call, so a mismatch cannot leave half the reports created.

  • Idempotent. An object already in the catalog is skipped, not overwritten. Re-running after a partial failure finishes the job. --force is the only way to replace one, and it is genuinely destructive: the pod has no upsert, so replacing means deleting the pair first (§8.7).

  • Verified, honestly. Each new report is run once through the real pipeline with echo checking on. A failed echo check is an error — the pod ignored the binds, so the report would answer the same thing whatever it is asked. Zero rows is only a warning: the probe binds (FND_%, CREATED_BY — chosen to be pillar-neutral) may simply match nothing on your pod.

  • Registry-aware. It ends by naming any report that exists on the pod but is missing from fusion.reports, with the block to paste. Start from config.example.yaml and there is nothing to paste: the four are already declared there, and a test holds the manifest and that file in step.

Useful flags: --pod NAME (multi-pod layouts), --only NAME (repeatable), --data-source — ApplicationDB_FSCM by default, which an HCM-only pod must override — and --folder for a catalog location other than /Custom/MCP.

fusion_bootstrap — the same thing, without the shell

The agent can do this itself. fusion_bootstrap is the MCP tool over the same manifest: it creates whichever of the four are missing, verifies each one, and writes the registry entries for any the config does not already declare — so an agent that meets a bare pod, finds fusion_list_tables failing, and calls this, is querying a minute later with no human in the loop.

It is deliberately much narrower than fusion_author_report:

fusion_bootstrap

fusion_author_report

SQL

four fixed files in this package

anything the agent composes

Existing object

skipped, always

replaced with force

Deletes anything

never

no, but --force in the CLI does

Gate

fusion.allow_bootstrap, on by default

fusion.allow_report_authoring, off

The default differs because the boundaries differ. Authoring widens what statements can reach the pod, which is a thing an administrator must weigh; bootstrapping widens nothing — the statements are fixed, reviewed and tested offline, and no caller-supplied SQL travels through it. What it can write is eight catalog objects with known names, and only onto paths that are empty. The real limit is the same one as everywhere else: a service account without BI authoring roles simply gets refused by Fusion. Set allow_bootstrap: false for a pod whose catalog must not be written to at all.

Replacing an existing report stays human-only, in a shell, with --force.

3.1 Service account

A Fusion user with BI authoring/consuming roles sufficient to create and run BI Publisher objects — typically BI Administrator for setup, and a narrower custom role for runtime.

Important: all row-level Data Security is evaluated against this account, not against whoever talks to the MCP server. Rows this account cannot see simply do not exist as far as any agent using this server is concerned.

3.2a Headless authoring — fusion-query-author

The pod exposes /xmlpserver/services/v2/CatalogService (verified on this pod 2026-08 alongside ExternalReportWSSService), and a data model / report pair is just two small zip archives — so the whole of §3.2b can be done by a CLI:

fusion-query-author --name open_pos \
    --sql-file open_pos.sql \
    --param "p_bu_name=%" --param "p_date_from=1900-01-01" \
    --verify "p_bu_name=%" --verify "p_date_from=2026-01-01"

That creates /Custom/MCP/XX_MCP_OPEN_POS_DM.xdm and ..._RPT.xdo, runs the report once with the --verify binds through the same pipeline the server uses (parameter-echo check included), and prints the config.yaml registry block to paste. Rules the CLI enforces so the pod cannot be handed a silent-failure shape:

  • The SQL goes through the same read-only guard as everything else — authoring is privileged, not exempt.

  • Result columns are derived from the top-level select-list aliases; a * projection or an unaliased expression is refused rather than guessed (--column passes them explicitly if you must).

  • A statement containing ]]> is rejected: it would terminate the CDATA section inside the data model and truncate the SQL without an error.

  • An existing catalog object is never overwritten without --force.

Credentials come from the same FUSION_USER / FUSION_PASS environment variables as the server. Two wire quirks the client pins (each cost a live round trip to learn): the three catalog operations address objects by three different element names (reportAbsolutePath / reportObjectAbsolutePathURL / objectAbsolutePath), and upload types are xdmz / xdoz.

Why this is a CLI and not an MCP tool. The report registry is the allow-list an agent operates inside. If the agent could author reports, the allow-list would guard nothing: any SQL becomes reachable by first minting a report for it. Creating a report is a human decision here, exactly as it was when a browser did it.

One happy consequence: the "a report needs sample data" rule turned out to be a UI-wizard gate only — an uploaded report runs fine without any, so the save-as-sample step (and the native dialog that can freeze the browser) disappears entirely on this path.

3.2b The browser recipe

For each report, in this order. The order is not cosmetic — steps 4 and 5 each depend on the one before, and the failure messages do not say so.

  1. New Data Model → Data Set → SQL Query.

    • Data Source: ApplicationDB_FSCM (Financials / SCM / Procurement). A data set is bound to exactly one data source, so HCM or CRM needs its own data model.

    • Type of SQL: Standard SQL.

  2. Write the SQL with :bind variables, not &lexicals. Every value the caller may vary is a bind:

    WHERE UPPER(t.table_name) LIKE UPPER(:p_pattern)

    Rules that are not optional:

    • Alias every selected expression with a plain uppercase identifier. Result column names become XML element names in the response; an unaliased expression can produce a document the parser cannot read.

    • Prefer UPPER(x) = UPPER(:p_x) over x = :p_x for anything compared against a dictionary name — see §8.2.

    • No trailing semicolon.

  3. Accept BIP's offer to create the parameters. On OK, the editor notices the :bind variables and offers to create matching parameters automatically. Say yes. It creates them with the right names and types, which is fiddlier to get right by hand. Then open Parameters and give each one a default value that returns rows (e.g. p_pattern → %HEADERS%, p_owner → FUSION). You need those defaults for step 4.

    This is the opposite of the lexical case, where the "enter values for lexical references" prompt does not create the parameter and you must add it by hand. Binds are the easy path in every respect.

  4. Click View Data and make it succeed. It runs the SQL with the parameter defaults from step 3. Fix the SQL until rows come back. This step is a hard prerequisite, not a sanity check — see step 5.

  5. Generate sample data (View Data → set a row count → Save as Sample Data). A report cannot be created from a data model that has no sample data, and sample data can only be produced by a View Data run that succeeded. So a data model whose defaults return nothing, or error, is a dead end that only announces itself two steps later when the report wizard refuses to proceed.

  6. Save the data model under /Shared Folders/Custom/MCP/.

  7. Create the report on that data model, any trivial layout. In report properties / output formats, enable Data (XML) output — attributeFormat: "xml" is what returns raw row data instead of a rendered document.

  8. Note both identifiers and put them in config.yaml (§3.4):

    • SOAP absolute path: /Custom/MCP/XX_MCP_LIST_TABLES_RPT.xdo

    • REST path (relative to Shared Folders), URL-encoded: Custom%2FMCP%2FXX_MCP_LIST_TABLES_RPT

3.3 The four shipped reports

These back fusion_list_tables, fusion_describe_table, fusion_search_columns and fusion_describe_flexfields. fusion-query-bootstrap (§3.0) creates all four for you — each statement below is the shipped file src/fusion_query_mcp/bootstrap/<name>.sql, reproduced here with the reasoning that shaped it. The column aliases are the contract the server parses, so keep the aliases exactly as written; edit one and the manifest beside the file must move with it, which tests/unit/test_bootstrap.py enforces offline. p_owner defaults to FUSION and p_table_pattern to % in the registry, so a caller may omit them.

list_tables — XX_MCP_LIST_TABLES_RPT

Binds: :p_pattern, :p_owner. Returns: OWNER, TABLE_NAME, APPROX_ROWS, TABLE_COMMENT.

SELECT t.owner        AS OWNER,
       t.table_name   AS TABLE_NAME,
       t.num_rows     AS APPROX_ROWS,
       tc.comments    AS TABLE_COMMENT
FROM   all_tables t
LEFT JOIN all_tab_comments tc
       ON tc.owner = t.owner
      AND tc.table_name = t.table_name
WHERE  UPPER(t.table_name) LIKE UPPER(:p_pattern)
AND    UPPER(t.owner)      LIKE UPPER(:p_owner)
ORDER BY t.num_rows DESC NULLS LAST, t.table_name
FETCH FIRST 5000 ROWS ONLY

num_rows is a stale optimiser statistic, not a live count — which is why it is surfaced to the agent as APPROX_ROWS. Ordering by it puts the business table above its interface, history and staging cousins, which is the single most useful ranking for schema discovery.

p_owner is compared with LIKE, not =, so a caller can pass % to search every schema the service account can see.

The FETCH FIRST must stay above limits.hard_max_rows (5000 vs 2000). It is a safety net against a pathological pattern, not the row cap — the tool layer does the capping, and it can only report truncated honestly if its limit is the one that binds. Set the data model's ceiling at or below hard_max_rows and a broad search silently returns a partial answer that looks complete.

describe_table — XX_MCP_DESCRIBE_TABLE_RPT

Binds: :p_table, :p_owner. Returns: OWNER, TABLE_NAME, COLUMN_ID, COLUMN_NAME, DATA_TYPE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE, NULLABLE, COLUMN_COMMENT, TABLE_COMMENT.

SELECT c.owner          AS OWNER,
       c.table_name     AS TABLE_NAME,
       c.column_id      AS COLUMN_ID,
       c.column_name    AS COLUMN_NAME,
       c.data_type      AS DATA_TYPE,
       c.data_length    AS DATA_LENGTH,
       c.data_precision AS DATA_PRECISION,
       c.data_scale     AS DATA_SCALE,
       c.nullable       AS NULLABLE,
       (SELECT MAX(cc.comments)
          FROM all_col_comments cc
         WHERE cc.owner       = c.owner
           AND cc.table_name  = c.table_name
           AND cc.column_name = c.column_name) AS COLUMN_COMMENT,
       (SELECT MAX(tc.comments)
          FROM all_tab_comments tc
         WHERE tc.owner      = c.owner
           AND tc.table_name = c.table_name)   AS TABLE_COMMENT
FROM   all_tab_columns c
WHERE  UPPER(c.table_name) = UPPER(:p_table)
AND    UPPER(c.owner)      LIKE UPPER(:p_owner)
ORDER BY c.column_id

The comments come from scalar subqueries, not joins, and that is deliberate. ALL_COL_COMMENTS can return more than one row for a column when the account reaches the object through several grants, and a LEFT JOIN would turn that into duplicated columns — the exact fan-out this server exists to detect, in the server's own SQL. A scalar subquery cannot multiply rows, so the shape is safe by construction rather than by inspection.

OWNER and TABLE_NAME are projected for a reason that is not obvious until it bites you: this pod carries two objects whose names differ only in case, so a case-insensitive lookup returns both, interleaved by COLUMN_ID. The server groups the rows by (OWNER, TABLE_NAME) and refuses to present them as one table — see §8.3. Grouping is by key, not by row order, so the simple ORDER BY c.column_id is sufficient.

search_columns — XX_MCP_SEARCH_COLUMNS_RPT

Binds: :p_pattern, :p_owner, :p_table_pattern. Returns: OWNER, TABLE_NAME, COLUMN_NAME, DATA_TYPE, APPROX_ROWS.

SELECT c.owner       AS OWNER,
       c.table_name  AS TABLE_NAME,
       c.column_name AS COLUMN_NAME,
       c.data_type   AS DATA_TYPE,
       t.num_rows    AS APPROX_ROWS
FROM   all_tab_columns c
LEFT JOIN all_tables t
       ON t.owner = c.owner
      AND t.table_name = c.table_name
WHERE  UPPER(c.column_name) LIKE UPPER(:p_pattern)
AND    UPPER(c.owner)       LIKE UPPER(:p_owner)
AND    UPPER(c.table_name)  LIKE UPPER(:p_table_pattern)
ORDER BY t.num_rows DESC NULLS LAST, c.table_name, c.column_name
FETCH FIRST 5000 ROWS ONLY

describe_flexfields — XX_MCP_DESCRIBE_FLEXFIELDS_RPT

Backs fusion_describe_flexfields. Binds: :p_flexfield, :p_context. Returns one row per DFF segment with the COLUMN_NAME mapping. Authored headless with fusion-query-author (§3.2a) — no browser was involved.

SELECT s.DESCRIPTIVE_FLEXFIELD_CODE  AS FLEXFIELD_CODE,
       f.NAME                        AS FLEXFIELD_NAME,
       s.CONTEXT_CODE                AS CONTEXT_CODE,
       s.SEGMENT_CODE                AS SEGMENT_CODE,
       st.NAME                       AS SEGMENT_PROMPT,
       s.COLUMN_NAME                 AS COLUMN_NAME,
       s.SEQUENCE_NUMBER             AS SEQUENCE_NUMBER,
       s.ENABLED_FLAG                AS ENABLED_FLAG,
       s.REQUIRED_FLAG               AS REQUIRED_FLAG,
       s.DISPLAY_TYPE                AS DISPLAY_TYPE,
       s.DEFAULT_VALUE               AS DEFAULT_VALUE,
       TO_CHAR(s.VALUE_SET_ID)       AS VALUE_SET_ID
FROM   fnd_df_segments_b s
LEFT   JOIN fnd_df_segments_tl st
       ON  st.APPLICATION_ID = s.APPLICATION_ID
       AND st.DESCRIPTIVE_FLEXFIELD_CODE = s.DESCRIPTIVE_FLEXFIELD_CODE
       AND st.CONTEXT_CODE = s.CONTEXT_CODE
       AND st.SEGMENT_CODE = s.SEGMENT_CODE
       AND st.LANGUAGE = USERENV('LANG')
       AND NVL(st.SANDBOX_ID, '~') = NVL(s.SANDBOX_ID, '~')
       AND NVL(st.ENTERPRISE_ID, -1) = NVL(s.ENTERPRISE_ID, -1)
LEFT   JOIN fnd_df_flexfields_tl f
       ON  f.APPLICATION_ID = s.APPLICATION_ID
       AND f.DESCRIPTIVE_FLEXFIELD_CODE = s.DESCRIPTIVE_FLEXFIELD_CODE
       AND f.LANGUAGE = USERENV('LANG')
       AND NVL(f.SANDBOX_ID, '~') = NVL(s.SANDBOX_ID, '~')
       AND NVL(f.ENTERPRISE_ID, -1) = NVL(s.ENTERPRISE_ID, -1)
WHERE  UPPER(s.DESCRIPTIVE_FLEXFIELD_CODE) LIKE UPPER(:p_flexfield)
AND    UPPER(s.CONTEXT_CODE) LIKE UPPER(:p_context)
ORDER  BY s.DESCRIPTIVE_FLEXFIELD_CODE, s.CONTEXT_CODE, s.SEQUENCE_NUMBER
FETCH  FIRST 5000 ROWS ONLY

Deliberately NO sandbox or seed-set filter. On this pod the mainline definition rows carry SANDBOX_ID = '1', so the "obvious" SANDBOX_ID IS NULL predicate returns zero rows — forever, without an error. Verified empirically before authoring (total = distinct = 10 for the PO_HEADERS DFF, so no striping duplication either). Re-check both facts when recreating this on another pod.

Views. The shipped set covers tables only. Fusion exposes a great deal through _V / _VL views that never appear in all_tables, so if you need them, build one more report over all_views the same way and register it. The exploration tools will not invent one for you.

3.4 Register the report

Add it to config.yaml under fusion.reports. Nothing else makes a report runnable — the registry is the allow-list:

fusion:
  reports:
    list_tables:
      soap_path: "/Custom/MCP/XX_MCP_LIST_TABLES_RPT.xdo"
      rest_path: "Custom%2FMCP%2FXX_MCP_LIST_TABLES_RPT"
      description: "Tables matching a LIKE pattern, most-populated first."
      parameters: [p_pattern, p_owner]      # accepted binds; anything else is rejected
      defaults: {p_owner: "FUSION"}         # used for whatever the caller omits

description and parameters are what fusion_list_reports shows an agent, so write the description for the agent, not for yourself: say what the report returns and what the parameters mean.

parameters is not optional in practice: an entry declaring none accepts none, because forwarding an undeclared name is how a typo reaches the pod unnoticed.

parameters is a hand-kept copy of the bind names inside the data model, and that is a real failure mode. Rename :p_pattern to :p_name in the data model and forget to change it here, and the server sends p_pattern, BI Publisher ignores an unknown parameter, the report runs on its stored defaults, and the pod answers HTTP 200 with rows that look entirely reasonable. Nothing errors.

The server defends against this because the pod hands over a receipt: every bind it actually received is echoed back at the root of the data XML. run_report compares that echo with what it sent and fails the call when they disagree, rather than returning rows that answer a different question. Set verify_echo: false on a report only if you have established that it does not echo — and understand you are switching the check off, not fixing it.

3.5 Prove the transport before wiring anything up

Which transport? On the pod this was built against, the REST endpoint /xmlpserver/services/rest/v1/reports/{path}/run answers a plain 404 — the v1 API is simply not exposed — while the SOAP ExternalReportWSSService works. Check yours:

# REST
curl -u "$FUSION_USER:$FUSION_PASS" -X POST \
  "https://<pod>.oraclecloud.com/xmlpserver/services/rest/v1/reports/Custom%2FMCP%2FXX_MCP_LIST_TABLES_RPT/run" \
  -H "Content-Type: multipart/form-data" \
  -F 'ReportRequest={"byPassCache":true,"attributeFormat":"xml","parameterNameValues":{"listOfParamNameValues":[{"name":"p_pattern","values":["%HEADERS%"]}]}};type=application/json' \
  -o out.bin -v

# SOAP
curl -X POST "https://<pod>.oraclecloud.com/xmlpserver/services/ExternalReportWSSService" \
  -H "Content-Type: application/soap+xml;charset=UTF-8" -H "SOAPAction: submitRequest" \
  -u "$FUSION_USER:$FUSION_PASS" --data @docs/soap_smoke.xml

docs/soap_smoke.xml ships with this repo. It is addressed at the legacy lexical report, so edit the .xdo path and the parameter block to point at one of your own reports before using it as a transport probe. SOAP returns the report bytes Base64-encoded inside <reportBytes>.

Set fusion.backend to rest or soap accordingly, then run fusion_health_check, which executes the list_tables report with a narrow pattern and reports backend, elapsed time, row count and the registered report names.


4. Install and run

Requires Python 3.12 and uv. Nothing else — no clone, no virtualenv, no paths to get right:

uvx --from git+https://github.com/ruya-grp/Fusion-MCP fusion-query-init

It asks four things — pod name, pod URL, service account, password — and then does the rest: writes the pod under ~/.fusion-query/pods/<name>/, creates the four exploration reports on the pod, registers them, runs the health check, and prints the line that registers the server with your MCP client. A live run ends like this:

Registered 4 report(s) in .../pods/my-pod/reports.dynamic.yaml
PASS  ok: Pod reachable via soap in 10186 ms; 241 row(s) from
      report:list_tables(p_owner=FUSION, p_pattern=%HEADERS%); 4 registered report(s).

Everything it does is available separately (--no-bootstrap, --no-verify, --no-input for scripting), and the pod it writes is a plain directory you can edit afterwards.

Where things live. An installed copy has no project directory and no dependable working directory, so configuration lives in $FUSION_HOME, or ~/.fusion-query/ when that is unset. An explicitly set FUSION_HOME wins over anything found by looking around, which is what makes one registration line work on every machine.

Working from a checkout

For developing the server itself:

uv venv --python 3.12
uv sync --all-groups

Then the same commands are on the path as uv run fusion-query-init, uv run fusion-query-bootstrap, and so on. A config.yaml or pods/ in the working directory takes precedence, so a checkout keeps behaving like a checkout.

Adding a pod by hand — four facts, nothing else

A pod is a directory under pods/. Its name is the pod name — what fusion_use_pod binds to — and the whole of its configuration is one line:

mkdir -p "pods/my pod" && cp docs/pod-template/config.yaml "pods/my pod/"
$EDITOR "pods/my pod/config.yaml"          # set base_url
cp docs/pod-template/.env.example "pods/my pod/.env"
$EDITOR "pods/my pod/.env"                 # set FUSION_USER / FUSION_PASS
fusion:
  base_url: https://your-pod.fa.ocs.oraclecloud.com

That is a complete pod. Everything else has a working default:

  • transport — backend: auto asks the pod which of SOAP/REST it publishes and remembers the answer (§8.5). There is one correct value per pod and the pod knows it, so it is not something to discover and record by hand.

  • reports — created and registered by fusion_bootstrap on first use (§3.0), or by fusion-query-bootstrap from a shell.

  • limits, security, redaction — defaults that work; config.example.yaml is the annotated reference for tuning them, not a file you must fill in.

One global variable, many pods. If FUSION_USER / FUSION_PASS are also set process-wide, they must name the same account as the pod's .env. When the two disagree the server refuses rather than picking: answering as the wrong service account returns HTTP 200 and plausible rows, with row-level Data Security evaluated against the wrong person.

Single-pod setup

Without a pods/ directory, one config.yaml at the project root does the same job. Start from the same template rather than from the full example:

cp docs/pod-template/config.yaml config.yaml

config.example.yaml is the annotated reference for everything you may set:

cp config.example.yaml config.yaml   # the long form, if you want the comments

Export credentials — never put them in the YAML:

export FUSION_USER='svc_mcp_query'
export FUSION_PASS='...'

Verify the transport end to end:

uv run python -c "from fusion_query_mcp.server import fusion_health_check as h; print(h())"

Then create the four reports the exploration tools run on (§3.0). This is the step a passing health check does not cover — the connection can be perfect while the catalog is empty:

uv run fusion-query-bootstrap

Registering with Claude Code

uv sync installs a console script into the venv, so point the client straight at it and pass the config path explicitly (an MCP server does not inherit the shell's working directory):

claude mcp add fusion-query --scope user --env FUSION_MCP_CONFIG=/path/to/OracleMCP/config.yaml -- /path/to/OracleMCP/.venv/bin/fusion-query-mcp

On Windows the executable is .venv\Scripts\fusion-query-mcp.exe. Verify with claude mcp list, which should report ✓ Connected.

Or configure any MCP client directly:

{
  "mcpServers": {
    "fusion-query": {
      "command": "/path/to/OracleMCP/.venv/bin/fusion-query-mcp",
      "env": { "FUSION_MCP_CONFIG": "/path/to/OracleMCP/config.yaml" }
    }
  }
}

Supplying credentials to a client-launched server

A server started by an MCP client does not see variables you exported in a terminal, so FUSION_USER / FUSION_PASS have to reach it another way. Two options, and the trade-off is real:

  • User-level environment variables (preferred). Stored by the OS and inherited by every child process, so the password never lands in a file inside this repository:

    setx FUSION_USER "svc_mcp_query"    # Windows; sign out and back in to apply

    On macOS/Linux, set them in your login shell profile or a keychain helper.

  • claude mcp add --env FUSION_PASS=... works, but writes the password in clear text into ~/.claude.json. Use it only for a throwaway sandbox pod.

Either way the password stays with you: nothing in this project reads, stores or logs it, and config.yaml holds only the names of the variables.


5. Configuration reference

See config.example.yaml for the annotated template.

Key

Default

Meaning

fusion.base_url

—

Pod URL, no trailing slash

fusion.username_env / password_env

FUSION_USER / FUSION_PASS

Names of the env vars holding credentials

fusion.backend

rest

rest | soap — §3.5. The verified pod needs soap

fusion.param_shape

auto

auto | flat | item — REST parameter JSON shape

fusion.reports.<key>

—

The report registry. soap_path, rest_path, description, parameters, defaults

fusion.datasources.<key>

—

Legacy lexical engine paths (§1.4)

fusion.engine_mode

whole_query

whole_query | clauses — legacy path only

fusion.double_encode_path

false

true if a load balancer eats %2F

limits.default_max_rows

100

Rows returned when the caller does not specify

limits.hard_max_rows

2000

Ceiling the caller cannot exceed

limits.timeout_seconds

120

Per-request timeout

limits.max_sql_chars

30000

Legacy path only — rejects over-long statements

security.denied_table_patterns

[]

SQL-LIKE patterns; legacy path, defence in depth only

security.redact_columns

[]

Values masked in output and in saved fixtures

audit.path

./audit/queries.jsonl

JSONL audit log

audit.log_row_data

false

Log the call and counts, not payloads

fixtures_dir

./fixtures

Where fixtures live

Environment overrides: FUSION_BASE_URL, FUSION_BACKEND, FUSION_PARAM_SHAPE, FUSION_ENGINE_MODE, FUSION_MAX_ROWS, FUSION_HARD_MAX_ROWS, FUSION_TIMEOUT_SECONDS, FUSION_MAX_SQL_CHARS, FUSION_FIXTURES_DIR, FUSION_AUDIT_PATH, FUSION_MCP_CONFIG (path to the config file itself).


6. Validating against ground truth

This is the feature that turns "the report ran" into "the answer is correct". You supply facts you already trust — a total read off the Fusion UI, an exported spreadsheet, a known document number — and the agent checks the result against them. It works identically on fusion_validate_report and (on a lexical-capable pod) fusion_validate_query; only the execution path differs.

Expectation types

type

Key fields

Catches

row_count

op: eq|min|max|between, value / min / max

wrong cardinality

column_set

required, forbidden

missing projections, alias mistakes

unique_key

columns

join fan-out — the single most common Fusion bug

contains_row

match, numeric_tolerance

a known record is present and correct

not_contains_row

match

a record that must be filtered out

aggregate

fn, column, op, value, tolerance

totals matching the UI

reference_dataset

key, mode, rows or rows_csv, allow_duplicate_keys

a full exported dataset

cross_check

oracle_sql, bind, tolerance

a complex result vs a simple trusted query

Not every expectation can see a fan-out, and it is worth knowing which. Join fan-out copies rows verbatim, so an expectation only detects it if duplication can falsify what it claims:

  • Catches it: unique_key (that is its whole job); reference_dataset, which fails when a key the reference supplied comes back more than once — an extra key still passes in contains_all mode, because a wider scope is what that mode is for; row_count with op: eq; aggregate over sum or count.

  • Blind to it, by arithmetic: aggregate over max, min, avg or count_distinct. Uniform duplication does not move any of them. No fix is possible inside the expectation — pair them with unique_key or an exact row_count. cross_check inherits the same blindness through bind.left.

  • Blind to it, by design: contains_row asserts that a row exists, and match is often a deliberately non-unique pattern ({"STATUS": "OPEN"}). Its detail reports how many rows matched, so duplication is visible on the passing result, but the verdict stays green.

This is the concrete form of the caveat below: pick two expectation types that fail for different reasons, not two spellings of the same claim.

cross_check needs a SQL-capable pod. It re-derives a number by running a second, simpler SQL statement — which this pod cannot do (§1.2). On a report-only pod the expectation does not crash the validation; it comes back as a failed expectation saying so. To get independent evidence without it, register a second, deliberately simpler report and compare the two results.

Validating a report

{
  "report": "list_tables",
  "params": {"p_pattern": "PO_HEADERS%"},
  "expectations": [
    {"type": "row_count", "op": "min", "value": 1},
    {"type": "column_set", "required": ["OWNER", "TABLE_NAME", "APPROX_ROWS"]},
    {"type": "unique_key", "columns": ["OWNER", "TABLE_NAME"]}
  ]
}

Expectations can also come from a saved fixture by name (fixture:), which is how a check becomes a regression test you re-run after a quarterly patch.

Fixture file

name: hhc_open_pos_aug2026
description: >
  Open standard POs for BU HHC-Entity01, August 2026.
  Ground truth from Procurement work area export on 2026-08-10.
datasource: fscm
expectations:
  - type: row_count
    op: between
    min: 1
    max: 500
  - type: column_set
    required: [PO_NUMBER, BU_NAME, SUPPLIER, TOTAL_AMOUNT]
  - type: unique_key
    columns: [PO_NUMBER]
  - type: contains_row
    match: {PO_NUMBER: "PO-2026-000123", TOTAL_AMOUNT: 150000}
    numeric_tolerance: 0.01
  - type: aggregate
    fn: sum
    column: TOTAL_AMOUNT
    op: eq
    value: 4250000.00
    tolerance: 0.50

A fuller, annotated version ships at docs/example_fixture.yaml — copy it into fixtures/ and edit. It is loaded by the test suite, so it cannot drift out of sync with the expectation schema. A fixture's sql: field is documentation of the statement the ground truth describes; on a report-only pod, the data model holds the real SQL and only the expectations are evaluated.

Two honest caveats

  1. A passing fixture proves consistency with the ground truth you supplied, not universal correctness. Use at least two independent expectation types per fixture — a contains_row and an aggregate, say. Two expectations restating the same fact prove almost nothing.

  2. A missing expected row is not always a query bug. Fusion row-level Data Security on the service account can legitimately hide rows. When contains_row fails with no near miss, widen the parameters until the row should be in scope; if it is still absent, the account cannot see it, and no amount of SQL will change that.

The working loop

The fusion-query-workflow prompt ships the full protocol. In short: fusion_list_reports to see what exists → fusion_search_columns / fusion_describe_table to check schema rather than guessing → run the report with max_rows <= 20 while iterating → validate → save the fixture. If the answer needs SQL no registered report provides, the correct outcome is to say so and ask an administrator for a new report, not to improvise.


7. Fusion schema traps

fusion_get_hints covers these in depth, and they matter most to whoever writes the SQL inside a data model. The three that corrupt results without raising an error:

  • _TL translation tables need AND t.LANGUAGE = USERENV('LANG'), or rows multiply by the number of installed languages.

  • _F / _M date-effective tables need TRUNC(SYSDATE) BETWEEN effective_start_date AND effective_end_date, or rows multiply by the number of history versions.

  • _ALL tables span business units and orgs. The UI's implicit BU context does not apply to raw SQL — filter org_id / bu_id deliberately.

Also: Fusion column names rarely equal UI labels (the UI's "Supplier" is VENDOR_NAME on POZ_SUPPLIERS_V), and all_tables.num_rows is a stale optimiser statistic, surfaced as APPROX_ROWS for that reason.


8. What surprised us on this pod

Verified by experiment, not inferred. Every item here cost time to discover.

8.1 Lexicals are not substituted; binds are

Covered in §1.2. It is the finding everything else follows from. If you take one thing from this README: &p_query becomes the empty string, :p_x arrives verbatim.

8.2 The dictionary is lower case

The pod serves its data dictionary in lower case — po_headers_all, gl_je_headers — because the objects were created with quoted lower-case names. Oracle's usual "unquoted identifiers are stored upper case" intuition is wrong here. Upper-casing a table name before comparing it returns zero rows, which reads as "that table does not exist" rather than as a bug. Every dictionary predicate therefore compares through UPPER() on both sides, so it works on either convention.

8.3 Two objects, one name, different case

The pod carries both FUSION.po_headers_all and FUSION.PO_HEADERS_ALL — two distinct objects with the same name in different case. The lower-case one is the real table (974 rows, populated comments); the upper-case one has no statistics and no comments.

A case-insensitive describe_table therefore returns 576 rows for a 288-column table, every column name appearing twice. Presented as one table that is simply wrong — it looks like a broken join, and the natural "fix" (dedupe by column name) hides a real fact about the pod. fusion_describe_table groups by (OWNER, TABLE_NAME) and makes the ambiguity visible instead of resolving it silently.

8.4 Comments are populated

Table and column comments are genuinely present on this pod, contrary to the common assumption that Fusion ships them NULL. They are useful enough to an agent that both list_tables and describe_table project them.

8.5 REST /run is not exposed; SOAP is

/xmlpserver/services/rest/v1/reports/{path}/run answers a plain 404 here, while ExternalReportWSSService works. Do not read the 404 as an authentication or path-encoding problem; the endpoint is not there.

This used to mean backend: soap in every config, discovered by an administrator and written down. It no longer does: backend: auto is the default and asks the pod. SOAP is probed first — not as a preference, but because ExternalReportWSSService sits beside the CatalogService this server already requires unconditionally for authoring, ad-hoc queries and bootstrapping. A pod where SOAP is missing cannot run this server at all, whatever backend says, so the transport that might be absent is the one probed second.

What does not trigger a fallback matters more than what does:

Signal

Falls back?

Why

404 / 405 / 501, "no such service"

yes

the endpoint is not published here

401 / 403

no

retrying doubles failed sign-ins, and Fusion locks accounts

Timeout

no

something answered slowly, so it exists

500 with a fault body

no

the service ran; the other one hears the same complaint

Connection refused / DNS

no

both transports are on the same host

The choice is remembered for the process, so the probe is paid once. Pinning backend: soap or rest still skips it entirely.

8.6 BI Publisher echoes your parameters as data

The response document carries every report parameter back as a leaf element at the root, alongside the repeating row groups. A report that matched nothing returns only the echoes:

<DATA_DS><P_OWNER>FUSION</P_OWNER><P_PATTERN>PO_HEADERS%</P_PATTERN></DATA_DS>

and one that matched rows returns them mixed together:

<DATA_DS><P_OWNER>FUSION</P_OWNER><P_PATTERN>%HEADERS%</P_PATTERN>
  <G_1><OWNER>FUSION</OWNER><TABLE_NAME>po_headers_all</TABLE_NAME></G_1>
  <G_1><OWNER>FUSION</OWNER><TABLE_NAME>gl_je_headers</TABLE_NAME></G_1>
</DATA_DS>

A naive parser reads the first document as one row of parameter values, which inverts every row-count check: row_count eq 0 fails and min 1 passes on an empty result. The rule this project uses: a row is a child of the root that has children of its own; root-level leaves are never rows, and dropping them is reported in ResultSet.warnings.

8.7a A missing config used to be a silently wrong one

config.example.yaml was in the list of filenames treated as "the configuration", so a checkout with no config.yaml quietly ran on the annotated template — whose base_url is the placeholder https://<pod>.oraclecloud.com. The server started, listed reports, and failed every call with a DNS error that pointed at the network rather than at "there is no configuration". It is no longer in that list: a missing config is now simply missing, and the health check says so.

8.7 uploadObject refuses an occupied path — so --force never overwrote

Found by bootstrapping into a scratch folder, twice. --force was implemented as "skip the client-side existence check", which is not the same thing as overwriting: the pod answers

PublicReportServiceImpl::executeUploadReport Failure:
Due to Report with Path [/Custom/.../XX_MCP_DESCRIBE_TABLE_DM.xdm] already exist!

There is no upsert. author_report therefore takes replace as well as force, and both CLIs map --force onto the pair: the report is deleted first, then the data model — that order, because deleting the model out from under the report would orphan it if the second call failed. fusion_author_report (the MCP tool) still passes force only, so nothing an agent can call deletes a catalog object.

Two smaller facts from the same runs: uploadObject creates the folder if it does not exist — /Custom/MCP_BOOTSTRAP_TEST was never made by hand — and a report run through run_report is bounded only by its own FETCH FIRST, since that path applies no row cap by design (§2 Row limits). The describe_flexfields probe returns exactly 5000 for that reason, so bootstrap names the ceiling rather than presenting the number as a total.


9. Security, privacy, audit

  • The report registry is a real boundary. An agent can only run reports an administrator built and registered in config.yaml, with only the parameters that report declares — an unknown parameter name is rejected before any round trip. Compared with the original design, where any SELECT the guard allowed could reach the pod, this is a genuine improvement: the set of possible statements is finite, reviewable, and owned by a human.

  • Credentials come only from environment variables or the OS keychain. Never in config.yaml, never in logs.

  • Single-account trust model. Every caller of this MCP server inherits the service account's full read scope. Row-level Data Security is evaluated against that account, not the human asking. If several people or clients will use the server, the report registry, the deny-list and the redaction list are the only differentiators. The SQL guard is defence in depth, not a security boundary — the real boundary is the service account's Fusion roles. Scope them narrowly.

  • Redaction. Columns matching security.redact_columns are masked in tool output and in fixtures the server writes (PDPL alignment). Validation warns when an expectation references a redacted column, because such a comparison can never pass.

  • Audit. One JSONL line per execution: timestamp, tool, datasource or report, backend, the call (report:list_tables(p_pattern=PO_HEADERS%) for a report, the SQL text on the legacy path), row count, elapsed ms, ORA code. Row payloads are not logged unless audit.log_row_data: true.

  • Fixtures hygiene. fixtures/ is gitignored by default. Use synthetic or masked ground truth when the repository is shared — real ground truth is client data.

  • Parameter values travel inside JSON/XML over HTTPS to Oracle. No third parties.


10. Development

uv run pytest -q                      # unit tests, no network
uv run pytest -q -m "not integration" # same, explicit
FUSION_LIVE=1 uv run pytest tests/integration -q   # live pod, env-gated

Unit tests never touch the network: the backends are exercised against canned multipart and SOAP payloads under tests/data/, and the pipeline and tool layers run against a fake pod. The live suite in tests/integration/ is the only thing that can confirm the [VERIFY-ON-POD] items in §11.

Agent evaluations are in tests/evals/ — see tests/evals/README.md for why there are two sets and why only one of them ships with answers filled in.

Layout:

src/fusion_query_mcp/
├── server.py           MCP tool surface
├── pipeline.py         the one execution path (execute + run_report)
├── metadata_sql.py     SQL builders — legacy lexical path only
├── config.py           YAML + env configuration, incl. the report registry
├── models.py           shared types
├── audit.py            JSONL audit log
├── redaction.py        column masking
├── fixtures_store.py   fixture persistence
├── backends/           soap_bip (this pod), rest_bip
├── engine/             sql_guard, limiter, xml_result, errors
├── validation/         models, evaluator, differ
└── knowledge/          fusion_hints.yaml + loader

SDK note: the design brief named FastMCP. In the official mcp Python SDK v2 that class was renamed MCPServer; the decorator surface is otherwise the same, and this project targets the current name rather than pinning to v1.


11. [VERIFY-ON-POD] checklist

Work through these with your Fusion administrator and record the answers in config.yaml. Items 1 and 2 decide whether you get the report architecture or the legacy one, so do them first.

  1. Are lexicals substituted? Build one throwaway data model SELECT * FROM (&p_query), create the report, run it over the API with p_query = SELECT 1 AS N FROM DUAL. ORA-00903 means no — use reports, and do not try to make it work. Anything else means your pod is more permissive than the one this was built on.

  2. Are binds honoured? A data model SELECT :p_x AS ECHO FROM DUAL must return your sentinel verbatim. If this fails too, the server cannot work at all on this pod.

  3. REST /run functional? If it 404s → backend: soap (§3.5).

  4. parameterNameValues shape (REST only) — flat vs item. Auto-detected; record the result as param_shape.

  5. Dictionary case. Run list_tables with p_pattern = %HEADERS% and look at the returned names. Lower case, upper case, or both (§8.2, §8.3)?

  6. Service-account roles confirmed by the Fusion admin; decide which pillars (FSCM only, or also HCM/CRM) get their own data models, since a data set is bound to exactly one data source.

Available Tools

40 tools
fusion_act_on_taskA
Destructive

Approve, reject or otherwise decide a task. THIS DECIDES SOMETHING.

outcome must be one of the values fusion_task_detail reported for this task -- they differ per task and per user, so never assume APPROVE exists. The value is checked against the live task before anything is sent.

There is no replay guard here and none is pretended: an approved task is no longer ASSIGNED, so a second attempt fails at the service rather than approving twice. That is the one place in this server where the pod's own state is the duplicate protection.

Args: number: Task number. outcome: The outcome id, e.g. APPROVE, REJECT, OK. comment: Optional comment recorded with the decision. confirmed: Set True only after explicit user confirmation.

ParametersJSON Schema
NameRequiredDescriptionDefault
numberYes
commentNo
outcomeYes
confirmedNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes well beyond the annotations by explaining there is no replay guard, that a second attempt fails naturally because the task is no longer ASSIGNED, and that pod state is the duplicate protection. This is rich, honest behavioral disclosure for a destructive, non-idempotent action.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but efficient. The warning is front-loaded, the replay-guard explanation is relevant, and the Args section is compact without filler. Every sentence adds necessary operational detail.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a mutation tool with an output schema, the description covers the decision action, dynamic outcome validation, failure behavior, duplicate protection, and all parameter semantics. Nothing essential for correctly invoking the tool is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries full responsibility for parameters. It explains each argument: number, outcome with the critical dynamic-value warning, optional comment, and the safety guard on confirmed. This fully compensates for the schema's lack of descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly identifies the action: approving, rejecting, or otherwise deciding a task. The emphatic 'THIS DECIDES SOMETHING' and the annotation title 'Submit a decision on a worklist task' make the mutation intent unmistakable and distinct from read-oriented siblings like fusion_task_detail or fusion_list_tasks.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives strong procedural guidance: outcome must come from fusion_task_detail, values differ per task/user, and APPROVE should never be assumed. It does not explicitly discuss alternatives or when-not-to-use, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_adhoc_queryA
Read-onlyIdempotent

Run one ad-hoc SELECT by authoring an ephemeral report for it.

This pod refuses lexical substitution, so caller SQL cannot travel through a fixed report -- instead this call turns the SQL into a report: a data-model/report pair is created in the catalog, run once with your bind values (parameter-echo verification included), and deleted again. Guarded (read-only, deny-list), audited with the real SQL, and gated behind fusion.allow_adhoc_queries in config.yaml.

Rules the statement must follow (they are the data-model rules):

  • Single SELECT/WITH, no trailing semicolon, read-only.

  • Alias every projection with a simple name -- aliases become the XML element names. SELECT * is rejected; name the columns.

  • Reference parameters as :bind names and pass a value for every one in binds. Literals work too, but binds keep the audit trail honest.

  • Explore first: fusion_list_tables / fusion_describe_table / fusion_search_columns / fusion_describe_flexfields, and read fusion_get_hints for the _TL/_F/_ALL join traps before joining anything.

Cost note: two catalog uploads + two deletes per call. If the same shape of question will be asked again, mint it once with fusion_author_report and run it as a registered report from then on.

Args: sql: The SELECT. Every projection aliased; binds as :p_name. binds: A value for every bind the SQL references, e.g. {"p_from": "2026-01-01"}. No defaults exist here. columns: Only needed when the select list defeats alias parsing; normally derived from the SQL. max_rows: Rows to return, clamped to the server's hard maximum. timeout_s: Per-call timeout in seconds.

ParametersJSON Schema
NameRequiredDescriptionDefault
sqlYes
bindsNo
columnsNo
max_rowsNo
timeout_sNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Reveals significant behavior beyond annotations: ephemeral catalog report creation/deletion, parameter-echo verification, audit with real SQL, config gate, and a cost note of two uploads plus two deletes per call. Annotations already mark it read-only/idempotent/non-destructive, and the description enriches that with implementation-level detail without contradicting it.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Long but every sentence earns its place; complex constraints are formatted as scannable bullets, and the critical 'one ad-hoc SELECT' is front-loaded. The cost/alternative guidance is concise but not extraneous.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, this is complete: usage constraints, parameter behavior, security/audit implications, performance cost, and routing to the persistent-report sibling are all present. An output schema exists, so not describing return values is acceptable.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the Args section fully compensates: each of the five parameters gets beyond-schema meaning (aliasing rule for sql, no defaults for binds, fallback role for columns, clamping for max_rows, units for timeout_s).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description opens with a specific verb-resource pair: 'Run one ad-hoc SELECT by authoring an ephemeral report for it.' It goes on to distinguish the tool from persistent reports by explaining the SQL is turned into a transient report rather than run through a fixed one, which separates it from fusion_run_query/fusion_run_report.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use it (one-off ad-hoc SELECT) and when not to: 'If the same shape of question will be asked again, mint it once with fusion_author_report and run it as a registered report from then on.' It also prescribes exploration workflow and hints at config gating.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_api_describeA
Read-onlyIdempotent

The field list for one REST resource -- the script for collecting input.

Returns each settable field with its type, business description, length limit and list-of-values, plus the child collections (lines, attachments) a complete document usually needs.

Read required_fields and required_source before trusting anything else. When required_source says the pod's own message, that list is authoritative. When it says "not probed", the per-field declared_mandatory flags are the only thing available and they are known wrong in BOTH directions on this pod: ExternallyManagedFlag is declared optional and is required; RequisitionHeaderId is declared mandatory and must not be sent at all.

Args: resource: Exact resource name, e.g. purchaseRequisitions. writable_only: Drop fields the caller can never set (generated ids, audit columns). On purchaseRequisitions this is 25 fields rather than 68 -- the other 43 are questions no user can answer. limit: Maximum number of fields returned.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
resourceYes
writable_onlyNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes well beyond the read-only/idempotent annotations by explaining exactly what is returned, how `writable_only` changes the field set, and documenting that `declared_mandatory` flags are unreliable in both directions on this pod. This is precisely the kind of non-obvious behavioral caveat an agent needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact but information-dense, with the core purpose front-loaded, followed by return-value details, a critical warning, and parameter semantics. Each sentence earns its place; nothing is filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the output schema exists and annotations cover safety/idempotency, the description covers the essential calling context: how to name the resource, what to expect in the response, and the known data-quality caveats. An agent has enough to invoke this tool correctly and interpret its output.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, but the description fully compensates: `resource` is explained with a concrete example, `writable_only` is defined with real field-count numbers, and `limit` is described as the maximum number of fields returned. Every parameter gets meaningful semantic context beyond its raw schema type.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states it returns the settable field list for one REST resource, with a clear verb ('returns') and resource scope. It also mentions child collections and required-field guidance, and its mention of 'REST resource' distinguishes it from sibling describe tools like fusion_soap_describe and fusion_describe_table.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives strong contextual guidance: 'Read `required_fields` and `required_source` before trusting anything else' and explains when the pod's own message is authoritative versus when flags are unreliable. It does not explicitly name alternatives or say when not to use this tool, but the provided usage context is clear enough for an agent to know it is the input-collection step.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_api_list_actionsA
Read-onlyIdempotent

List what this service account was OBSERVED able to create on this pod.

Start every "register a X" request here. Each entry carries the resource name and the fields the pod itself named as required -- harvested from its own rejection message, not derived from metadata.

This list is observed, never declared. On the pod this was built against, /describe advertises POST on purchaseOrders and the account gets HTTP 403; that resource is correctly absent here. If a resource you expect is missing, it was either never harvested or genuinely refused -- check with fusion_api_describe, which reports write_access either way.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds significant behavioral context beyond the annotations: the list is observed rather than declared, fields come from the pod's own rejection messages, and a concrete 403 example explains why some advertised resources are absent. It also clarifies the semantics of missing resources, which is critical for correct interpretation. No contradiction with the readOnlyHint/openWorldHint/idempotentHint annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is information-dense but every sentence earns its place: the core purpose, the entry-point instruction, the observed-vs-declared distinction, and the missing-resource fallback. The example with purchaseOrders is specific and memorable without being bloated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given there are no parameters and an output schema exists, the description supplies all necessary operational context: what the list contains, where the data comes from, how absence should be interpreted, and what to do next. It fully equips an agent to decide when and how to use this tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters and the input schema is empty, so there is no parameter meaning for the description to add. Per the baseline for zero-parameter tools, this is fully adequate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence states exactly what the tool does: list what the service account was observed able to create on the pod. It differentiates observed capabilities from declared ones and distinguishes this from sibling tools like fusion_api_describe by emphasizing this is harvested from rejection messages, not metadata.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says 'Start every "register a X" request here,' giving a clear when-to-use instruction. It also provides an alternative path: if a resource is missing, check with fusion_api_describe, which reports write_access either way. This gives the agent actionable routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_author_reportA

Mint a PERSISTENT report from SQL and register it for future runs.

Use this when a question will be asked again: an ad-hoc query costs catalog round trips every time, a registered report only once. The SQL becomes a BI Publisher data model + report under /Custom/MCP/, and the entry lands in the dynamic registry file (not config.yaml), immediately runnable via fusion_run_report / fusion_validate_report.

Same statement rules as fusion_adhoc_query: single guarded SELECT, every projection aliased, parameters as :bind names (discovered from the SQL automatically). Write description for the NEXT agent: say what the report returns and what each parameter means -- it is read through fusion_list_reports.

Strongly recommended: pass verify_binds with values that should return rows. The report is then run once through the normal pipeline (echo check included) before being registered, so a broken report is refused instead of registered.

Gated behind fusion.allow_report_authoring in config.yaml. A name that exists in config.yaml is never touched; force=True only overwrites reports this tool itself created.

Args: name: Registry key, lower_snake_case, e.g. open_pos_by_bu. sql: The SELECT with :bind parameters, every projection aliased. description: For the next agent -- returns what, parameters mean what. defaults: Values used for binds a caller omits, e.g. {"p_type": "%"}. verify_binds: Bind values for a one-off verification run after upload. force: Overwrite this tool's own earlier report of the same name.

ParametersJSON Schema
NameRequiredDescriptionDefault
sqlYes
nameYes
forceNo
defaultsNo
descriptionYes
verify_bindsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses important behaviors beyond annotations: the report is persisted under /Custom/MCP/, registered in a dynamic registry file rather than config.yaml, gated behind a config flag, and verified through a normal pipeline before registration. It also clarifies that force only overwrites reports created by this tool and never touches pre-existing config.yaml entries, which fully informs the agent of side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Although detailed, every sentence serves a clear purpose: purpose, when-to-use, SQL rules, description guidance, verification recommendation, gating, and overwrite semantics. The content is logically organized and front-loaded with the core purpose, making it easy for an agent to parse quickly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity and an existing output schema, the description covers all operational context: prerequisites, configuration gating, failure behavior, parameter meaning, verification, and overwrite rules. An agent has everything needed to call this tool correctly and avoid common mistakes.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description carries the full burden, and it succeeds by explaining every parameter: name's naming convention, sql's SELECT and bind rules, description's purpose for the next agent, defaults' role, verify_binds' verification run, and force's overwrite scope. This goes far beyond the bare type information in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a precise verb and resource: 'Mint a PERSISTENT report from SQL and register it for future runs.' It clearly distinguishes this tool from the sibling fusion_adhoc_query by contrasting one-time query cost vs. reusable registered reports, so an agent can select it without ambiguity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly states when to use this tool: 'Use this when a question will be asked again,' and contrasts it with ad-hoc queries that cost catalog round trips. It also names related run/validate tools and notes that the report is immediately runnable via fusion_run_report / fusion_validate_report, giving clear guidance on the workflow.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_bootstrapA

Create the exploration reports this pod is missing, from shipped SQL.

Call this when fusion_list_tables / fusion_describe_table / fusion_search_columns / fusion_describe_flexfields fail on a pod that is otherwise healthy: each of them runs a BI Publisher report that has to exist in the catalog first, and a fresh pod has none of them. A passing fusion_health_check does NOT cover this -- the connection can be perfect while the catalog is empty.

No SQL is accepted from you. The four statements are files inside this package, held to their declared columns and binds by offline tests; this tool only chooses whether to send them. It is therefore much narrower than fusion_author_report, and gated separately (fusion.allow_bootstrap).

Safe to call twice: an object already in the catalog is reported as skipped, never replaced and never deleted. Replacing one is a human decision, made with fusion-query-bootstrap --force in a shell.

Each new report is run once with pillar-neutral probe binds and its parameter echo checked. A report whose echo fails is left in the catalog but NOT registered, and reported as failed -- it would otherwise answer the same thing whatever it was asked.

Args: only: Bootstrap just these reports, e.g. ["list_tables"]. Default: all four. folder: Catalog folder to create them in. data_source: BI Publisher data source. Default ApplicationDB_FSCM; an HCM-only pod needs its own, e.g. ApplicationDB_HCM.

ParametersJSON Schema
NameRequiredDescriptionDefault
onlyNo
folderNo/Custom/MCP
data_sourceNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses behavior far beyond the annotations: it is safe to call twice, existing catalog objects are skipped and never replaced or deleted, replacement requires a human decision via a shell command, and each new report is echo-checked with a failure mode of being left in the catalog but not registered. This is rich, actionable context not available from readOnlyHint, destructiveHint, or idempotentHint.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but every sentence earns its place: purpose, trigger conditions, scope boundaries, safety/idempotency, failure semantics, and parameter explanations are each clearly separated. The first sentence front-loads the core purpose, and the Args section is tight and scannable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex bootstrap tool with 3 parameters, no required fields, and an output schema already present, the description covers all necessary context: when to call it, what it does not do, side effects, failure handling, and parameter defaults. Nothing an agent needs to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries the full burden of explaining all three parameters. It does so thoroughly: 'only' is shown with an example and default behavior ('all four'), 'folder' is given a purpose and default, and 'data_source' is explained with pod-specific guidance and an example ('ApplicationDB_HCM'). This compensates fully for the empty schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Create the exploration reports this pod is missing, from shipped SQL.' It explicitly names the affected sibling tools (fusion_list_tables, fusion_describe_table, fusion_search_columns, fusion_describe_flexfields) and differentiates itself from fusion_author_report, so an agent can identify its role without inspecting schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives an explicit trigger condition: 'Call this when fusion_list_tables / fusion_describe_table / fusion_search_columns / fusion_describe_flexfields fail on a pod that is otherwise healthy.' It also states a clear exclusion: a passing fusion_health_check does NOT cover this, and it contrasts itself with fusion_author_report, giving unambiguous routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_commit_actionA
Destructive

Execute a prepared action. THIS CHANGES DATA IN FUSION.

Requires the token from fusion_prepare_action and confirmed=True, which you may only set after the user has seen the preview and said yes. The token is single-use: a refused attempt needs a fresh prepare, which is also where you add the fields the pod asked for.

When the pod refuses, its own message and the fields it named come back rather than a summary -- that message is the next question to ask the user.

Args: token: From fusion_prepare_action. confirmed: Set True only after explicit user confirmation.

ParametersJSON Schema
NameRequiredDescriptionDefault
tokenYes
confirmedNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnlyHint=false, destructiveHint=true), it discloses that the token is single-use and that a refusal returns the pod's message and named fields rather than a normal summary. This materially changes how the agent should handle the response and subsequent user interaction.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded with a clear safety warning. The prose and Args section reinforce the critical confirmation requirement with minimal redundancy and no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

It covers the token lifecycle, the confirmation requirement, direct edit behavior, single-use semantics, and refusal behavior. Since an output schema exists, not listing return fields is acceptable; the description is complete enough for safe and correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries the full burden for both parameters. It clearly defines token as coming from fusion_prepare_action and confirmed as only set after explicit user confirmation, adding meaning the schema's titles and default value do not provide.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb and resource: 'Execute a prepared action,' and immediately signals that it changes data. It also distinguishes itself from fusion_prepare_action by referencing the token required from that prepare step, making the tool's role unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states exact preconditions: a token from fusion_prepare_action and confirmed=True only after the user has seen the preview and said yes. It also explains the failure path—a refused attempt needs a fresh prepare—so the agent knows when to go back to the sibling tool instead of retrying.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_describe_flexfieldsA
Read-onlyIdempotent

Which DFF segments exist, and which ATTRIBUTEn column holds each one.

Descriptive Flexfields are how Fusion customers add their own fields: the data lands in the generic ATTRIBUTE1..n / ATTRIBUTE_NUMBERn / ATTRIBUTE_DATEn columns of the base table, and only the DFF definition says what each column means. This tool reads that definition (from fnd_df_segments_b + _tl), so a spec that says "supply duration" can be resolved to PO_HEADERS_ALL.ATTRIBUTE2 without asking a human.

Protocol for an unknown table:

  1. fusion_describe_table the base table -- seeing ATTRIBUTE columns means a DFF may be in use.

  2. Call this with the DFF code, which is USUALLY the base table name without _ALL (PO_HEADERS_ALL -> PO_HEADERS); pass a LIKE pattern (PO_HEADERS%) when unsure -- related codes like PO_HEADERS_SH show up too, and % at both ends casts wider.

  3. Project the returned COLUMN_NAME in your query (fusion_adhoc_query), aliased to the SEGMENT_CODE, and filter ATTRIBUTE_CATEGORY by CONTEXT_CODE when the segment belongs to a context other than Global Data Elements (global segments apply to every row).

SEGMENT_PROMPT is the label users see in the UI -- match the user's wording against it. Context Data Element rows describe the context chooser itself (ATTRIBUTE_CATEGORY), not a data segment.

Args: flexfield: LIKE pattern over the DFF code, e.g. PO_HEADERS or %INVOICE%. Case-insensitive. context: Optional LIKE pattern over CONTEXT_CODE to narrow. limit: Maximum segment rows to return.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
contextNo
flexfieldYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses internal behavior beyond annotations: it reads fnd_df_segments_b/_tl, returns segment rows with COLUMN_NAME and CONTEXT_CODE, and clarifies that 'Context Data Element' rows describe the context chooser rather than data segments. It also gives practical semantics for SEGMENT_PROMPT and global vs. context-specific segments. Annotations already mark it read-only and idempotent, and nothing contradicts them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Though long, the description is densely informative with no filler. It front-loads the core question, provides a clear numbered protocol, includes concrete examples, and adds necessary caveats about context rows and prompt labels. Every sentence contributes operational value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a metadata-introspection tool, the description covers the full workflow: schema discovery, DFF code guessing, LIKE-pattern fallback, query construction, and context filtering. It explains return-relevant fields such as COLUMN_NAME, SEGMENT_CODE, CONTEXT_CODE, and SEGMENT_PROMPT, and the output schema removes the need to enumerate return structure. The limit parameter and its default are also addressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries full responsibility for parameter meaning. It explicitly documents all three parameters: flexfield as a case-insensitive LIKE pattern with examples, context as an optional LIKE pattern over CONTEXT_CODE, and limit as a maximum row count. This fully compensates for the schema's lack of descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening line states exactly what the tool does: maps DFF segments to the ATTRIBUTEn column that stores each one. It distinguishes itself from siblings like fusion_describe_table by explaining that this reads the DFF definition rather than the base table structure, and the protocol explicitly sequences the two.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'Protocol for an unknown table' section gives concrete when-to-use guidance: first describe the base table, then call this tool with the DFF code or a LIKE pattern, then project the returned column in an adhoc query. It also explains how to handle uncertain DFF codes and context-specific segments, which is exactly the kind of decision support an agent needs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_describe_tableA
Read-onlyIdempotent

Describe ONE Fusion object: ordered columns, data types, comments.

Call this before writing any query that names a column. Fusion column names rarely match the UI labels, and a wrong guess costs a full ORA-00904 round trip. Comments are populated on this pod and are genuinely informative.

The dual-object rule. This pod carries objects whose names differ only in letter case -- FUSION.po_headers_all and FUSION.PO_HEADERS_ALL are two distinct objects, and the dictionary lookup is case-insensitive, so a lookup for one returns both. Presenting their union would show a 288-column table as 576 columns with every name duplicated: a wrong answer that looks like a right one. So the rows are grouped by their exact (OWNER, TABLE_NAME) and exactly one group is ever described:

  1. If exactly one group spells its name the way you did (case-sensitive on the object, case-insensitive on the owner, since Oracle object names created with quotes really are case-sensitive), that group wins -- chosen_by: exact_name_match. Your spelling is the only unambiguous signal available.

  2. Otherwise the best-documented group wins -- chosen_by: most_documented: first a group that has a table comment, then the one with the most commented columns, then the most columns, then name order for determinism. On this pod the documented twin is the real transactional object; its namesake has no statistics and no comments. And it is decided from data the report already returned, so no extra round trip is needed.

Whenever more than one object matched, ambiguous is true, alternates lists every object that was NOT described, and the summary leads with it. The ambiguity is never resolved silently: if the choice is wrong, re-call with the exact spelling you meant (table="po_headers_all").

Args: table: Object name, optionally OWNER.OBJECT, e.g. AP_INVOICES_ALL. Case is preserved and used for the tie-break above. owner: Schema owner used when table is unqualified. datasource: Legacy composed-SQL fallback only; ignored on the report path.

ParametersJSON Schema
NameRequiredDescriptionDefault
ownerNoFUSION
tableYes
datasourceNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/openWorld/idempotent/non-destructive behavior. The description adds substantial behavioral detail beyond annotations: the case-sensitive dual-object rule, chosen_by tie-breaking logic, ambiguous flag, alternates list, no-silent-resolution guarantee, and the datasource fallback behavior. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long, but it is front-loaded with the core purpose and uses clear headers for complex rules. The dual-object rule justifies much of the length, though some narrative explanation could be trimmed without losing essential guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values need not be re-explained. The description covers invocation timing, parameter semantics, ambiguity behavior, and re-call guidance, making it functionally complete for an agent to select and invoke the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description fully carries parameter meaning. It explains table as 'OWNER.OBJECT' with case preservation, owner as the fallback schema when table is unqualified, and datasource as a legacy ignored path. This goes well beyond the bare schema properties.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Describe ONE Fusion object: ordered columns, data types, comments.' It clearly distinguishes this from list/search/sibling documentation tools by emphasizing 'ONE' object and precisely naming the output elements.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit usage guidance: 'Call this before writing any query that names a column' and explains the concrete cost of guessing wrong. However, it does not explicitly say when to prefer alternative tools like fusion_docs_describe_table or fusion_search_columns, so it stops short of full exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_docs_describe_tableA
Read-onlyIdempotent

Describe a table from the local Oracle-docs snapshot -- instant, no pod call.

Serves the OEDM documentation Oracle publishes for every Fusion table: column business descriptions, primary key, foreign keys (join edges!), indexes, flexfield mappings, and for views their defining SQL. Prefer this over fusion_describe_table for exploration -- it answers in milliseconds instead of 12-14 s and carries meaning the pod's dictionary lacks.

The snapshot is docs-derived, not pod ground truth: a column that exists here but not on the pod will fail loudly in fusion_adhoc_query, never silently. Custom objects and case-twin duplicates exist only on the pod, so fall back to the live tools when this returns not_found.

Args: table: Exact table or view name, e.g. PO_HEADERS_ALL. columns_like: Optional substring to filter columns by name or by words in their description -- use it on wide tables to keep responses small. limit: Maximum number of column entries returned.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
tableYes
columns_likeNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark this as readonly, open-world, idempotent, and non-destructive, and the description adds meaningful behavioral context beyond them: it uses a docs-derived snapshot rather than pod ground truth, returns not_found rather than silently succeeding, and reveals timing characteristics. This helps the agent reason about correctness and failure modes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured and front-loaded: purpose, differentiation, caveats, then parameter details. Despite being detailed, every sentence earns its place, and the formatting makes the key decision points and argument semantics easy to scan.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the output schema exists and the tool is documented as a read-only snapshot lookup, the description sufficiently covers return contents, limits, fallback behavior, and parameter semantics. Nothing an agent needs to call this tool successfully is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description fully carries parameter documentation. It explains that table is an exact table/view name with an example, that columns_like is a case-insensitive substring filter on column name or description, and that limit caps the number of returned column entries. This is exactly what an agent needs beyond the raw schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Describe a table from the local Oracle-docs snapshot'. It clearly enumerates what the tool returns (column business descriptions, primary key, foreign keys, indexes, flexfield mappings, view SQL) and explicitly contrasts itself with fusion_describe_table, so an agent can distinguish it from siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use guidance: 'Prefer this over fusion_describe_table for exploration' and explains why by comparing performance and semantic richness. It also provides fallback instructions: when the snapshot returns not_found, use the live tools because custom objects and case-twin duplicates exist only on the pod.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_docs_search_columnsA
Read-onlyIdempotent

Search columns across ALL documented tables by name or business meaning.

Full-text search over the local docs snapshot -- both column names and their descriptions -- so "supplier hold reason" finds POZ_SUPPLIER_SITES_ALL_M.PURCHASING_HOLD_REASON without knowing any naming convention. Instant and token-cheap; prefer it over the live fusion_search_columns for discovery, then verify existence on the pod only when you execute.

Args: term: Words to match (AND-combined), or a column-name fragment. limit: Maximum matches returned.

ParametersJSON Schema
NameRequiredDescriptionDefault
termYes
limitNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark this as read-only, idempotent, and non-destructive. The description adds valuable behavior beyond that: it searches a local snapshot, matches both column names and descriptions, combines words with AND, and warns that live-pod existence verification is needed before execution.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured and front-loaded with the core purpose. The example, caveat, and argument explanations all earn their place without excessive verbosity. It remains tightly scannable while conveying meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the output schema and annotations, the description covers everything necessary: source of data, matching semantics, parameter meanings, and a key caveat about verifying existence on the live pod. There are no material gaps for an agent to call this correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Although input schema description coverage is 0%, the description fully compensates by explaining both parameters: term as AND-combined words or a column-name fragment, and limit as the maximum number of matches returned. This is exactly the semantic detail an agent needs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action and resource: searching columns across ALL documented tables by name or business meaning. It is clearly differentiated from the live sibling tool fusion_search_columns by emphasizing the local docs snapshot and discovery-focused use case.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly directs when to use this tool: prefer it over fusion_search_columns for discovery and then verify existence on the pod when executing. It also gives a concrete example of search intent, making the usage context unmistakable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_export_bulk_dataA
Destructive

Run an extract job and leave its output in UCM. Returns a request id.

The mirror of fusion_import_bulk_data. Marked destructive because it queues a scheduled process that cannot be recalled -- not because it changes business data, which it does not.

parameters are joined with COMMAS here (the schema declares parameterList as a single string), unlike fusion_submit_job whose parameters are separate elements. A parameter containing a comma is refused rather than silently split.

Args: job_name: package,definition as one string. parameters: Job arguments, joined with commas. notification_code: The service's two-digit notification code. confirmed: Set True only after explicit user confirmation.

ParametersJSON Schema
NameRequiredDescriptionDefault
job_nameYes
confirmedNo
parametersNo
notification_codeNo10

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes well beyond the annotations: it explains why destructiveHint is true (queued process cannot be recalled, not business-data mutation), discloses the comma-joining rule, the refusal of comma-containing parameters, and the explicit user confirmation requirement. This is exactly the context an agent needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is organized into a purpose statement, a behavioral note, and an Args block, and every section has a purpose. It loses a point because it contains the inaccurate schema-reference sentence, which adds confusion rather than value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

It covers purpose, destructive nuance, confirmation, and parameter semantics (albeit with one error), and an output schema exists so return values needn't be detailed. It is incomplete in that the parameter-format contradiction undermines the agent's ability to invoke the tool confidently; it also does not point to the job-status sibling for tracking after the async queue.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds useful detail for job_name, notification_code, and confirmed, but its parameter guidance conflicts with the input schema. It claims the schema declares 'parameterList' as a single string and instructs that parameters are 'joined with commas,' while the actual schema declares parameters as an array of strings. This is misleading for invocation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Run an extract job and leave its output in UCM.' It returns a request id, and states it is the mirror of fusion_import_bulk_data, which clearly differentiates it from the import sibling and the generic fusion_submit_job.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It names the sibling mirror and explicitly contrasts the parameter format with fusion_submit_job, giving clear context for selection. It does not, however, spell out explicit 'use when/when-not' criteria, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_find_uploaded_filesA
Read-onlyIdempotent

List UCM document ids matching a file prefix in an account.

Read-only, and the cheapest way to answer "did my upload actually land, and in the right place?" -- which matters because a bulk upload's characteristic failure is a file sitting in an account no import job looks at.

Args: prefix: File-name prefix to search for. account: UCM account, e.g. fin$/payables$/import$.

ParametersJSON Schema
NameRequiredDescriptionDefault
prefixYes
accountYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already provide readOnlyHint, idempotentHint, and destructiveHint=false, so the bar is lower. The description adds useful behavioral context beyond annotations: it is the 'cheapest way' to perform this verification and it scopes results to a UCM account. It does not contradict any annotation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The definition is front-loaded with the core action, followed by a practical use-case sentence and a clean Args section. The failure-mode explanation earns its place because it clarifies why this tool matters. Minor redundancy exists with 'Read-only' duplicating the readOnlyHint annotation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-parameter read-only search tool, the description covers purpose, parameter semantics, and the key usage scenario. The output schema exists to document return values, and annotations cover safety and idempotency, so nothing critical is missing for an agent to select and invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It does: both parameters are explained with plain semantics, and 'account' includes a concrete format example, `fin$/payables$/import$`. It could add details like case-sensitivity or wildcard behavior, but it is sufficient for correct invocation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'List UCM document ids matching a file prefix in an account.' It clearly distinguishes the tool from job-status and import tools by framing it as the way to check whether an upload actually landed in the right place.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives a concrete when-to-use scenario: verifying that a bulk upload landed in the correct account, including the characteristic failure mode of files sitting in accounts no import job looks at. It does not explicitly name alternative sibling tools or state when not to use it, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_get_fixtureA
Read-onlyIdempotent

Read one saved fixture, including its expectations and stored SQL.

Args: name: The fixture name as reported by fusion_list_fixtures.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, openWorldHint, idempotentHint, and destructiveHint=false, so the safety profile is covered. The description adds the return content ('expectations and stored SQL') but does not disclose failure behavior, errors, or other behavioral details. This is adequate but minimal beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two short sentences and one argument note, with the core behavior front-loaded and no wasted words. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter read-only tool with rich annotations and an output schema present, the description is complete: it states what the tool reads, what the result includes, and where to obtain the required parameter. Nothing essential is missing for an agent to use it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must carry the semantic weight for the only parameter. 'The fixture name as reported by fusion_list_fixtures' fully defines what 'name' means and gives the agent an exact source for the value, which is significantly more meaningful than the schema's bare string type.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states a specific action ('Read') and a specific resource ('one saved fixture'), and further details what is included ('expectations and stored SQL'). It also distinguishes itself from sibling fusion_list_fixtures by focusing on a single fixture, so an agent can tell the tools apart.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context by saying the name must come from 'fusion_list_fixtures', which is a practical instruction for invoking this tool correctly. It does not explicitly state when not to use it or name alternatives, but the source-of-name guidance is sufficient for this simple case.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_get_hintsA
Read-onlyIdempotent

Curated Oracle Fusion schema knowledge: the traps that corrupt results silently.

Read the relevant topic before trusting a joined result. A missing _TL language filter or _F effective-date predicate does not raise an error -- it multiplies the rows, and the report looks like it worked. That guidance now applies to the SQL an administrator writes into a report's data model, which is where every join on this pod lives.

Args: topic: One of the knowledge-base topics (effective_dating, translations, multi_org, lookups, flexfields, aliasing, po, ap, inv, om, gl, suppliers, getting_started). Omit for the whole base.

ParametersJSON Schema
NameRequiredDescriptionDefault
topicNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, openWorld, idempotent, and non-destructive behavior, so the safety profile is fully covered. The description adds useful domain context about silent row multiplication and the SQL scope, but it does not disclose operational details such as return format, errors, or response size. The output schema appears to cover return structure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with a one-line purpose and then provides a brief, relevant example of the silent-corruption trap. The args section is compact and complete. The middle paragraph is slightly verbose but every sentence contributes meaningful guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-optional-parameter, read-only knowledge tool with an output schema, the description is nearly complete: it covers purpose, usage timing, parameter values, and omission behavior. It does not name sibling alternatives or exclusions, but those are not essential for this simple tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema only defines a nullable string `topic` with no enum or description, so 0% schema coverage means the description must compensate. It does so excelently by listing all accepted topic values and explicitly stating that omitting the argument returns the whole base. This fully resolves parameter ambiguity.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description identifies this as a knowledge-retrieval tool for curated Oracle Fusion schema traps, specifically the silent data-corruption risks in joined results. It is clearly distinct from the sibling query, validation, and report tools, but it lacks an explicit verb like 'retrieve' and does not directly contrast itself with a sibling.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives a concrete use condition: 'Read the relevant topic before trusting a joined result.' It also explains the failure mode that motivates the tool—missing `_TL` or `_F` predicates multiply rows silently. However, it does not explicitly state when not to use the tool or name alternative tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_health_checkA
Read-onlyIdempotent

Verify the whole path to the pod: credentials, report, bind values, parsing.

Probes something that actually works: it runs the registered list_tables report with a narrow pattern, so a green answer proves credentials, the catalog path, the bind-parameter shape and the XML parser all at once. On a pod with no reports registered it falls back to the legacy lexical probe (SELECT 1 AS N FROM DUAL), which is also what detects whether that pod wants the flat or the item-wrapped parameterNameValues JSON when param_shape is auto.

status is degraded -- not ok -- when the round trip succeeded but the report returned no rows: the transport works and the report is wrong, which is a different problem from an unreachable pod.

Args: datasource: Configured datasource key to probe (legacy path, and the datasource summary). An unknown name is reported as an error. report: Report to probe with instead of list_tables. pattern: Value for the probe report's p_pattern bind.

ParametersJSON Schema
NameRequiredDescriptionDefault
reportNo
patternNo
datasourceNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare read-only/idempotent/non-destructive, so the description does not need to restate safety. It adds valuable behavior: the `list_tables` probe strategy, the legacy `SELECT 1` fallback, the `param_shape` auto-detection nuance, and the `degraded` status meaning. This is rich behavioral disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is organized in short, purposeful segments: purpose, probe behavior, status interpretation, then arg documentation. Every sentence carries diagnostic value, and the main purpose is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers behavior, fallback, error semantics, degraded status, and all parameters, while an output schema handles return-value details. Nothing essential for an agent to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description fully compensates by explaining all three args: `datasource` as the configured key with error behavior, `report` as a replacement for `list_tables`, and `pattern` as the `p_pattern` bind value. This goes well beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a precise verb and target: 'Verify the whole path to the pod: credentials, report, bind values, parsing.' It clearly distinguishes this from sibling tools by framing it as an end-to-end probe that runs `list_tables`, unlike direct query/report tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The use case is strongly implied: use this to check connectivity, credentials, and report plumbing in one shot, with `status` semantics for interpreting results. It does not explicitly name alternatives or when-not-to-use, but the context is clear enough for an agent to select it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_import_bulk_dataA
Destructive

Load a data file into Fusion (FBDI) and queue the import jobs.

Reads file_path from disk, uploads it to UCM, and submits each job in jobs. THIS LOADS REAL DATA and cannot be recalled.

account is the UCM account, and it is the field that goes wrong quietly: a file filed under the wrong account uploads successfully and is then invisible to the import job, with no error anywhere. Confirm it against the job's documentation, e.g. fin$/payables$/import$.

Each entry in jobs is {"name": "package,definition", "parameters": [...]}. Note that these parameters are joined with COMMAS -- unlike fusion_submit_job, whose parameters are separate elements. A parameter containing a comma is refused rather than silently split; use fusion_submit_job for those.

Args: file_path: Local path of the file to upload. account: UCM account, e.g. fin$/payables$/import$. jobs: Import jobs to run, in order. notification_code: The service's two-digit notification code. confirmed: Set True only after explicit user confirmation.

ParametersJSON Schema
NameRequiredDescriptionDefault
jobsYes
accountYes
confirmedNo
file_pathYes
notification_codeNo10

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the destructiveHint annotation by stating that the operation loads real data, cannot be recalled, uploads to UCM, and queues jobs in order. It also discloses a subtle failure mode where a wrong account uploads successfully but becomes invisible to the import job with no error.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is detailed but every sentence adds operational value, and the most critical warning (loads real data, cannot be recalled) is front-loaded. The parameter explanations are structured and directly map to the schema properties without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive bulk-import tool with no schema-level parameter descriptions, this covers all necessary context: the data flow, the job format, ordering, confirmation requirement, and the key failure mode. Having an output schema makes the lack of return-value detail acceptable.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must carry the full burden, and it does: file_path is the local path, account is the UCM account with an example, jobs are described as ordered entries with a concrete structure, notification_code is the two-digit code, and confirmed is gated on user confirmation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific action: load a data file into Fusion (FBDI) and queue import jobs. It clearly distinguishes itself from related tools like fusion_submit_job by explaining the comma-joining behavior and bulk-import focus.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit guidance on when to use this tool versus fusion_submit_job, including the exact condition (parameters containing commas) that should route the agent to the alternative. Also instructs that `confirmed` must only be set after explicit user confirmation, and warns about the quiet account mismatch failure.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_job_logA
Read-onlyIdempotent

Fetch a finished job's log or output -- i.e. WHY it failed.

Call this whenever fusion_job_status reports ERROR. Without it the only honest thing you can tell the user is "the job failed", which is the least useful true statement available: the reason is in the log.

File contents are not returned inline -- a job log can be megabytes. Pass save_to to write them to a directory and get the paths back.

Args: request_id: The job's request id. file_type: log for the run log, out for the job's output. save_to: Optional directory to write the files into.

ParametersJSON Schema
NameRequiredDescriptionDefault
save_toNo
file_typeNolog
request_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish that the operation is read-only, idempotent, and non-destructive. The description adds non-obvious behavioral details that annotations cannot convey: file contents are not returned inline because logs can be megabytes, and passing save_to triggers writing files to a directory and returning paths.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is tightly organized: a one-line purpose, an explicit when-to-use instruction, a critical behavior warning, and a clean args list. Every sentence carries useful information and the structure is easy to scan.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's modest complexity and the presence of an output schema, the description covers everything needed: when to call, what each parameter means, what happens with save_to, and why logs are not inlined. Nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description is the only source of semantic meaning for all three parameters. It fully explains request_id, defines the two file_type values ('log' vs 'out'), and clarifies that save_to is an optional output directory with side effects.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description leads with a specific verb ('Fetch') and a concrete resource ('a finished job's log or output'), and ties it to a clear diagnostic purpose ('WHY it failed'). This distinguishes it sharply from sibling tools like fusion_job_status, which reports job state rather than retrieving logs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives an explicit trigger condition: call this whenever fusion_job_status reports ERROR. It also explains why this step is necessary, turning a simple feature description into a decision rule an agent can act on.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_job_statusA
Read-onlyIdempotent

Check a scheduled process submitted with fusion_submit_job.

finished tells you whether to stop polling. An unrecognised state counts as still running on purpose: abandoning a job because its state is not in our list would be worse than one more poll.

Args: request_id: The id returned by fusion_submit_job.

ParametersJSON Schema
NameRequiredDescriptionDefault
request_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds meaningful behavioral detail beyond the annotations: 'An unrecognised state counts as still running on purpose' explains a deliberate, safety-oriented behavior. This is exactly the kind of contextual disclosure that helps an agent avoid prematurely abandoning jobs. It aligns with the openWorldHint and readOnlyHint annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact, front-loaded with the core purpose, and every sentence adds value. The rationale for treating unknown states as running is justified in one clause. The Args section is direct and minimal.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter polling tool, the description covers what matters: what to check, when to stop polling, and how to handle unrecognized states. An output schema exists to describe return fields, so the description does not need to explain return values. The tool is fully usable from this definition alone.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema only names 'request_id' with title 'Request Id', providing zero description coverage. The description compensates fully by specifying that request_id is 'The id returned by fusion_submit_job,' which gives the agent the precise origin and meaning of the required parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb and resource: 'Check a scheduled process submitted with fusion_submit_job.' It clearly distinguishes this tool as the status/polling counterpart to a job submission tool. The reference to the sibling submit tool anchors its purpose without ambiguity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear usage context: it is for checking a scheduled process after submission, and 'finished tells you whether to stop polling.' It does not explicitly enumerate alternatives or exclusions, but the polling behavior is stated well enough for an agent to decide when to call it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_list_fixturesA
Read-onlyIdempotent

List saved ground-truth fixtures available for validation.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, openWorldHint=true, idempotentHint=true, and destructiveHint=false, which strongly cover the safety profile. The description adds minimal behavioral context beyond saying the fixtures are saved and are for validation, but it does not contradict the annotations or present any surprise behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence delivers the action, resource, and purpose without wasted words. The core function is front-loaded and easy to scan.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter read-only listing tool with strong annotations and an output schema, the description is complete. It tells the agent what will be listed and why, while the structured metadata covers safety and return shape.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters and the input schema is fully described by having an empty properties object. There is no parameter documentation burden for the description, so the baseline of 4 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses the specific verb 'List' with an explicit resource ('saved ground-truth fixtures') and states the purpose ('available for validation'). This clearly distinguishes it from sibling tools like fusion_save_fixture and fusion_get_fixture, which imply writing and retrieving individual fixtures rather than enumeration.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no explicit when-to-use or when-not-to-use guidance, and no alternative tools are named. However, the phrase 'available for validation' implies the tool is for enumerating saved fixtures before selecting one, so some usage context is present but not fully articulated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_list_podsA
Read-onlyIdempotent

Which Fusion pods this server can talk to, and which one is active.

Pods are directories under pods/, each holding a config.yaml (and a gitignored .env with that pod's credentials). Call this at the start of a session, then fusion_use_pod to bind the session to one pod. With no pods/ directory the server runs in single-config mode and this lists nothing.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark the operation as read-only, idempotent, and non-destructive. The description adds valuable behavioral context: it explains the pod directory structure, the presence of config.yaml and an .env credentials file, the active-pod concept, and the specific behavior when no pods/ directory exists. This goes well beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded: the first sentence states the core purpose, and the following sentences add only high-value context about pod structure, session flow, and the no-pods edge case. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given zero parameters and a rich output schema, the description fully covers what an agent needs to call this tool correctly: why to call it, when to call it, what it returns, and how to interpret an empty listing. No important operational context is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, and the schema coverage is effectively complete. The description adds no parameter-specific details, but none are needed. Baseline 4 is appropriate for a parameterless tool where there is nothing to clarify beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description uses a specific verb and resource ('list' and 'Fusion pods'), and clearly states the tool's output: which pods the server can talk to and which one is active. It is clearly distinguishable from siblings like fusion_use_pod because it explicitly frames listing as the precursor to a session-binding action.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use guidance: 'Call this at the start of a session, then fusion_use_pod to bind the session to one pod.' It also covers the single-config mode edge case, telling the agent when this tool will list nothing, which prevents misinterpretation of an empty result.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_list_reportsA
Read-onlyIdempotent

List the BI Publisher reports this server may run. Call this FIRST.

This is the whole of what is runnable. Arbitrary SQL does not work on a pod that refuses lexical substitution, so an administrator builds each report's data model once -- writing its SQL with :bind variables -- and registers it under fusion.reports in config.yaml. Everything an agent can ask this pod is therefore some registered report plus a choice of bind values, which is also the security boundary: no report, no query.

For each report you get its parameters (the bind names it accepts; anything else is rejected before a round trip) and its defaults (the values used for the ones you omit). If the answer you need has no report, say so and hand the user the SQL and parameters it would need -- that is a one-time administrator task, not something this server can work around.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes well beyond the annotations by explaining the security boundary, why arbitrary SQL cannot work, how reports are registered, and what each result contains: parameters and defaults. It also discloses that unknown bind parameters are rejected before a round trip. This aligns with the readOnly and idempotent hints and adds meaningful behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is longer than average but every sentence earns its place: it front-loads the purpose and 'Call this FIRST', then explains why reports are the only path, what the listing contains, and how to handle missing reports. There is no repetition or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, output-schema-backed listing tool, the description is fully complete. It covers invocation order, the meaning of results, the security model, and the fallback behavior when no report fits. The presence of an output schema means return-value details do not need to be repeated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters and the input schema fully covers this with 100% coverage, so the description has no parameter burden. It still adds useful context about the parameters and defaults exposed by each report, which helps the agent understand what the output means, but this is not required for invoking the tool.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'List the BI Publisher reports this server may run.' It also establishes the tool's role as the entry point to every possible query, which clearly distinguishes it from execution tools like fusion_run_report or administrative tools like fusion_author_report.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly says 'Call this FIRST' and explains that all runnable work is limited to registered reports, so an agent knows to consult this tool before attempting any query. It also gives guidance for the negative case: if no report exists, say so and hand the user the SQL and parameters as a one-time admin task. It does not explicitly name sibling tools to use instead, but the usage context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_list_tablesA
Read-onlyIdempotent

List Fusion tables matching a SQL LIKE pattern, most-populated first.

Runs the registered list_tables report with p_pattern / p_owner as bind values. The report applies no row cap of its own, so limit is applied here after the rows arrive and truncated says honestly whether more came back.

approx_rows comes from all_tables.num_rows, an optimiser statistic that can be stale or NULL -- treat it as a hint about which of several similarly named objects is the real transactional one, never as a count. It is also the quickest way to spot the same-name-different-case pairs this pod carries: the populated one is the real table.

The owner and pattern echoed back are the values that were actually bound, which is not always what you passed: omitting one applies the report's registered default, and the full set is in params_used.

Args: pattern: SQL LIKE pattern, e.g. %INVOICE%. % matches any run of characters. Matching is case-insensitive inside the data model. owner: Schema owner; FUSION for application data. The data model compares it with LIKE, not =, so % searches every schema the service account can see. Omit (null) to bind the report's registered default instead (FUSION in the shipped registry). limit: Maximum rows to return from those the report produced. include_views: Not backed by any report -- see the error it returns. datasource: Legacy composed-SQL fallback only; ignored on the report path.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
ownerNoFUSION
patternNo%
datasourceNo
include_viewsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, and non-destructive behavior; the description adds substantial non-obvious details: the report has no intrinsic row cap, `truncated` honestly reveals extra rows, `approx_rows` can be stale/NULL, and echoed `owner`/`pattern` may differ from passed values due to defaults. These disclosures go far beyond the annotations and contradict nothing.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description opens with a one-sentence summary, then uses tight paragraphs and a bulleted Args section. Every caveat—stale statistics, bound-value echoing, no row cap—is load-bearing for correct use, and there is no filler or tautology.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description fully equips an agent to select and invoke the tool correctly: result interpretation (`approx_rows`, `truncated`, `params_used`), default-binding behavior, and parameter traps are all addressed. Since an output schema already exists, not restating the full return shape is appropriate.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries full responsibility, and it delivers. It explains `pattern` with LIKE syntax and case-insensitivity, `owner` with LIKE-vs-= semantics and default binding, `limit` as a post-report cap, `include_views` as unsupported, and `datasource` as a legacy fallback that is ignored on the report path.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening line states a specific verb and resource: 'List Fusion tables matching a SQL LIKE pattern, most-populated first.' This clearly identifies what the tool does and its distinguishing qualifiers, making it easy to separate from sibling list/describe/query tools even without explicit sibling names.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides strong operational context: it runs the registered `list_tables` report, applies `limit` after rows arrive, and warns that `approx_rows` is only a stale hint, never a count. It also flags `include_views` as unsupported and `datasource` as legacy-only, but it never explicitly names alternative tools or gives a direct when-to-use vs. when-not-to-use comparison.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_list_tasksA
Read-onlyIdempotent

Approvals and notifications waiting for the signed-in user.

The worklist lives at /bpm/api/4.0/tasks -- a third channel, reached by neither the REST resources nor ErpIntegrationService. Six plausible routes (fscmRestApi/notifications, worklistTasks under both FSCM and HCM, userNotifications, the TaskQueryService SOAP endpoint) all answer 404.

Follow up with fusion_task_detail before acting: the list does not carry the available outcomes, and those differ per task AND per user.

Args: status: The service's own status values, e.g. ASSIGNED. Note ANY is not accepted by the service and is refused here with an explanation. limit: Maximum tasks returned.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
statusNoASSIGNED

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare read-only, idempotent, open-world, and non-destructive behavior. The description adds meaningful behavioral context beyond those: the specific endpoint, the refusal of ANY with an explanation, the absence of outcome data in the list, and the dead-end alternative routes. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with purpose, then provides integration context, usage caveats, and parameter details in a logical order. The list of six 404 routes is somewhat verbose but purposeful because it prevents wasted exploration. Slightly longer than strictly necessary, but every part earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With only two optional parameters and an output schema present, the description covers everything needed to call the tool correctly: user-scoped worklist, status semantics, limit behavior, invalid ANY handling, and the important caveat to follow up with fusion_task_detail. The output schema handles return-value documentation, so nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It does: status is explained as the service's own status values with ASSIGNED as an example and ANY explicitly excluded, while limit is defined as maximum tasks returned. This is sufficient for correct invocation, though enumerating valid status values would have been even stronger.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Approvals and notifications waiting for the signed-in user,' which clearly identifies what the tool returns and for whom. It also distinguishes this tool by naming its dedicated endpoint as a 'third channel' and explicitly points to fusion_task_detail as a separate follow-up tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explains when to use the tool: to see approvals/notifications for the signed-in user. It also gives explicit follow-up guidance—call fusion_task_detail before acting because outcomes are not included—and warns that status ANY is rejected. The note that six plausible routes answer 404 helps prevent the agent from trying wrong alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_prepare_actionA
Read-onlyIdempotent

Check an action and hold it for confirmation. Sends NOTHING to the pod.

Returns exactly what would travel, the guard's verdict, and a token. The action reaches Fusion only when fusion_commit_action presents that token.

Show the preview to the user in their own words and get an explicit yes before committing. That pause exists here because Fusion has none: an authorised, valid POST executes on arrival.

This cannot tell you the action will succeed. There is no dry-run for a Fusion create -- a valid POST commits -- so the pod has not seen this payload yet. What it can do is stop an action the policy forbids, and show you the fields the snapshot says are required before you spend a round trip.

Args: resource: e.g. purchaseRequisitions. Use fusion_api_list_actions to see what this account may actually create. payload: The record to send. Resolve every id with fusion_resolve_value first. verb: POST to create, PATCH to change an existing record. record_id: Required for PATCH -- the record to change.

ParametersJSON Schema
NameRequiredDescriptionDefault
verbNoPOST
payloadYes
resourceYes
record_idNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Even though annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, the description adds essential behavioral context: no payload is sent, a validation token is produced, and Fusion applies no confirmation pause. It also warns that prepare does not guarantee success and is not a dry-run. This goes far beyond what the annotations alone convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the most critical fact ('Sends NOTHING to the pod') and every sentence earns its place by adding either workflow, limitation, or parameter semantics. The structure moves from behavior to workflow to caveats to arguments, making it easy for an agent to parse. It is longer than minimal, but the length is justified by the safety-critical nature of the tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description is complete for a safe-preview tool of this complexity: it covers behavior, outputs, workflow, limitations, and parameter semantics, and it references the output schema rather than duplicating return details. It also explains why this tool exists given Fusion's lack of a confirmation pause. Nothing an agent needs to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With schema description coverage at 0%, the description carries the full burden and does so well. It explains resource with an example and how to discover valid values, payload with an id-resolution prerequisite, verb with POST/PATCH semantics, and record_id as required for PATCH. Every parameter gains meaning beyond its schema title or default.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: it checks an action and holds it for confirmation, explicitly saying it sends nothing to the pod. It also distinguishes itself from fusion_commit_action by explaining that the action only reaches Fusion when that sibling presents the returned token. This is exactly what an agent needs to tell this tool apart from its siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear when-to-use guidance: show the preview to the user, get explicit yes, then commit via fusion_commit_action. It also names supporting siblings (fusion_api_list_actions for valid resources, fusion_resolve_value for ids) and explains what the tool cannot do, preventing misuse as a dry-run. This is explicit workflow-level guidance, not just a vague hint.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_resolve_valueA
Read-onlyIdempotent

Turn a name the user said into the id the API wants.

RequisitioningBUId wants 300000003588643; the user says "the Egypt BU". Call this for every id-shaped field before putting it in a payload.

Runs as a normal guarded SELECT on the read channel (the pod's list-of-values views are not addressable over REST -- all candidate paths answer HTTP 404), so it is audited like any other query.

Four outcomes, and ambiguous is the important one: when several rows match, the candidates come back for the user to choose between. Never pick one yourself -- two business units both matching "IHC" is exactly the situation where guessing produces a confidently wrong document.

Args: field: The REST attribute name, e.g. RequisitioningBUId. text: What the user said. Matched case-insensitively as a substring.

ParametersJSON Schema
NameRequiredDescriptionDefault
textYes
fieldYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations, the description discloses that the tool runs as a normal guarded SELECT on the read channel and is audited like any other query. It also explains the four-outcome model and emphasizes the ambiguous case where candidates are returned for the user to choose. This is rich, non-obvious behavioral context that aligns with readOnlyHint and idempotentHint.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with a crisp one-line purpose, followed by a useful example, an explicit invocation rule, a rationale, and an important ambiguity warning. Every sentence earns its place, and the argument list is concise and directly tied to the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter resolution tool, the description covers when to use it, how to pass parameters, matching behavior, ambiguous-result handling, and safety/auditing context. The output schema can carry the exact return-value details, so nothing critical is missing for an agent to select and invoke the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Input schema coverage is 0%, but the description fully defines both parameters: field is the REST attribute name with a concrete example, and text is what the user said with matching semantics ('case-insensitively as a substring'). This completely compensates for the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific transformation: 'Turn a name the user said into the id the API wants,' and gives a concrete example mapping RequisitioningBUId to 'the Egypt BU'. It also explicitly instructs when to invoke it ('Call this for every id-shaped field before putting it in a payload'), making it clearly distinguishable from sibling tools like fusion_run_query.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives an explicit when-to-use rule: call this for every id-shaped field before building a payload. It also explains why direct alternatives fail ('all candidate paths answer HTTP 404') and warns the agent never to pick an ambiguous match itself, which is essential decision-making guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_run_queryA
Read-onlyIdempotent

Run a read-only SQL SELECT -- ONLY on a pod that substitutes lexical parameters.

Check whether this tool can work here before you use it. It ships the statement as a lexical BI Publisher parameter, and a Fusion pod that has lexical substitution disabled (the common SaaS hardening, and the case this server was built for) silently drops it: the data model then executes SELECT * FROM () and Oracle answers ORA-00903. No rewrite of your SQL fixes that, so do not enter a repair loop -- the failure hint says so explicitly when it sees that signature.

The working path on such a pod is the report registry: call fusion_list_reports to see what an administrator has already built, then fusion_run_report to run one with bind values and fusion_validate_report to check it. Schema exploration works either way.

If your pod does honour lexicals, this is the full free-SQL surface. The statement must be a single SELECT or WITH; anything else is rejected before it leaves this machine, and a row cap is appended automatically unless the SQL already limits rows. Give every selected expression a simple alias (SUM(x) AS TOTAL_AMOUNT) -- result columns become XML element names, and unaliased expressions can produce an unparsable document.

Args: sql: The SELECT/WITH statement to execute. datasource: Configured datasource key (default: the server's default). max_rows: Row cap for this call, clamped to the server's hard maximum. timeout_s: Per-call timeout in seconds. select_list: engine_mode: clauses only -- the projection without SELECT. from_clause: engine_mode: clauses only -- the FROM body without FROM. where_clause: engine_mode: clauses only -- including the WHERE keyword. group_clause: engine_mode: clauses only -- including GROUP BY/HAVING. order_clause: engine_mode: clauses only -- including ORDER BY/FETCH.

ParametersJSON Schema
NameRequiredDescriptionDefault
sqlNo
max_rowsNo
timeout_sNo
datasourceNo
from_clauseNo
select_listNo
group_clauseNo
order_clauseNo
where_clauseNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnly and idempotent annotations, the description discloses critical failure behavior: lexical substitution may be silently disabled, the statement could become `SELECT * FROM ()`, and no SQL rewrite fixes it. It also reveals that the tool appends a row cap, rejects anything not a single SELECT/WITH, and can produce unparsable XML output if expressions lack aliases.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but every section earns its place: a front-loaded warning, a clear alternative path, a precise scope statement, and a compact parameter list. It is structured with bolded warnings and separators that make the critical information easy to scan.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, zero schema coverage, and a non-obvious failure mode, the description covers everything needed to invoke it correctly: prerequisites, failure signature, workaround path, SQL constraints, row-cap behavior, alias rules, and per-parameter semantics. An output schema exists, so not detailing return values is acceptable.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries full responsibility, and it delivers. It explains all 9 parameters in the Args list, including the engine_mode clause-building parameters, the meaning of max_rows clamping, timeout_s, and datasource defaulting, plus the alias requirement for selected expressions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states a specific verb and resource: 'Run a read-only SQL SELECT -- ONLY on a pod that substitutes lexical parameters.' It distinguishes this tool from the report-registry path and explicitly frames it as the 'full free-SQL surface,' making its identity unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use and when-not-to-use guidance: check whether the pod honors lexical parameters, avoid a repair loop if the ORA-00903 signature appears, and instead use fusion_list_reports followed by fusion_run_report and fusion_validate_report. This directly names alternatives and the condition that selects them.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_run_reportA
Read-onlyIdempotent

Run one registered BI Publisher report with bind values.

The statement lives in the report's data model, written once by an administrator; you choose only the values for the binds it declares. That is the same trust boundary the Fusion UI enforces, and on a pod that refuses lexical substitution it is the only path that reaches the database at all.

Discover names and parameters with fusion_list_reports. An unknown report name, or a parameter the report does not declare, is rejected here -- before any round trip -- with a message listing what IS accepted; correct the call from that list rather than guessing, because a dropped parameter would run the report on its stored defaults and return a plausible wrong answer.

The report applies no row cap of its own, so max_rows is applied after the rows arrive: row_count is what you are given, rows_returned_by_report is what came back, and truncated is true when those differ.

Args: report: Registered report name, as listed by fusion_list_reports. params: Bind values, e.g. {"p_pattern": "%INVOICE%"}. Omitted parameters take the report's registered default. max_rows: Rows to return, clamped to the server's hard maximum. timeout_s: Per-call timeout in seconds.

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsNo
reportYes
max_rowsNo
timeout_sNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds substantial behavior beyond the annotations: unknown reports/params are rejected before any round trip, omitted parameters silently fall back to stored defaults yielding plausible wrong answers, row caps are applied after rows arrive, and truncated semantics are clarified. This is exactly the kind of non-obvious behavior an agent needs to know.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is longer than average but each paragraph earns its place by covering validation behavior, fallback hazards, and row-count semantics. It is front-loaded with a clear one-line purpose and uses structure well. It could be tightened slightly, but the length is justified by the subtle failure modes it prevents.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the output schema already describes the return shape, the description covers everything else an agent needs: how to discover valid inputs, exact validation semantics, dangers of dropped parameters, row-cap behavior, and per-parameter details. No important gap remains.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries the full burden for parameter semantics. It explains each parameter meaningfully: report is a registered name, params are bind values with defaults, max_rows is clamped to the server maximum, and timeout_s is per-call. The included example {'p_pattern': '%INVOICE%'} makes the expected shape concrete.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence states the exact action: 'Run one registered BI Publisher report with bind values.' This clearly distinguishes it from siblings like fusion_run_query and fusion_adhoc_query, which operate on ad-hoc queries rather than registered reports.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly directs the agent to discover valid report names and parameters via fusion_list_reports, and explains when this is the only path to the database. It does not explicitly name alternatives to avoid, but the context strongly implies when to use this tool versus a query tool, so it is clear but not exhaustive.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_save_fixtureA

Persist a validated set of expectations as a reusable regression test.

Worth doing every time a validation passes: Fusion quarterly patches change views and add columns, and a saved fixture turns "this was right in August" into something you can re-run in November.

For a report validation, record the report and its bind values in description (and the report's call descriptor in sql) so the fixture says what it was proved against.

Values in configured redacted columns are masked before the file is written.

Args: name: Fixture name; letters, digits, _ and - only. expectations: The expectation objects to persist. description: Where the ground truth came from -- record the source and date. datasource: Datasource (or report) the fixture applies to. sql: The validated query or report call, stored alongside for reference. overwrite: Replace an existing fixture of the same name.

ParametersJSON Schema
NameRequiredDescriptionDefault
sqlNo
nameYes
overwriteNo
datasourceNo
descriptionNo
expectationsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds useful behavioral context beyond annotations: redacted-column values are masked before writing, and sql is stored alongside for reference. Annotations already indicate the operation is not read-only and not destructive; no contradiction is present.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose, gives a short rationale, and then uses a compact, labeled Args section. Every sentence contributes either to selection, usage timing, or parameter semantics.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 6-parameter write operation with an output schema, the description is nearly complete: it covers all params, the masking behavior, and the persistence intent. It does not spell out what happens when overwrite is false and a fixture already exists, so there is a small but non-fatal gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the Args block fully compensates by explaining all six parameters, including the name character restriction, the purpose of expectations, and the overwrite replacement behavior. This is exactly what an agent needs to fill the arguments correctly.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Persist a validated set of expectations as a reusable regression test.' This clearly distinguishes fusion_save_fixture from retrieval siblings like fusion_get_fixture or fusion_list_fixtures.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives an explicit trigger ('Worth doing every time a validation passes') and explains why with the quarterly patch example. It does not name a specific alternative or state when not to use this tool, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_search_columnsA
Read-onlyIdempotent

Find which tables carry a column -- the fastest answer to "where does X live?".

Runs the registered search_columns report with p_pattern / p_owner / p_table_pattern as bind values. Results are ordered by the owning table's row-count statistic, so the real transactional table tends to surface above its interface, history and staging namesakes -- and above its same-name-different-case twin, which has no statistics at all.

The report applies no row cap, so limit is applied here and truncated reports honestly when more rows came back than were returned.

Args: pattern: SQL LIKE pattern over the column name, e.g. %SEGMENT1%. owner: Schema owner. Omit (null) to let the report's own default apply. table_pattern: Optional LIKE pattern to restrict the table name too. limit: Maximum rows to return from those the report produced. datasource: Legacy composed-SQL fallback only; ignored on the report path.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
ownerNoFUSION
patternYes
datasourceNo
table_patternNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Even though annotations already declare readOnlyHint, openWorldHint, idempotentHint, and destructiveHint=false, the description adds substantial behavioral context: it runs a registered report with specific bind values, orders results by table row-count statistics, applies no row cap internally so limit is enforced here, and reports 'truncated' honestly. It also explains that datasource is ignored on the report path, which is valuable non-obvious behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with a crisp one-line purpose and then progresses through report behavior, ordering, limit handling, and per-argument details. It is somewhat verbose in the ordering explanation with examples like 'interface, history and staging namesakes', but that detail is informative rather than filler. Overall it earns its length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, the presence of an output schema, and rich annotations, the description is complete for invocation. It explains every parameter, the report execution path, the ordering heuristic, and the limit/truncation semantics. No critical operational detail needed to call the tool correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries the full burden of explaining parameters. It does this thoroughly: pattern is defined as a SQL LIKE pattern with an example, owner is explained as optional with a report default, table_pattern is described as an optional LIKE restriction, limit is defined as the maximum rows returned, and datasource is disclosed as a legacy fallback that is ignored on the report path. This goes well beyond the raw schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a clear, specific purpose: 'Find which tables carry a column' and positions it as 'the fastest answer to where does X live?'. This is a concrete verb+resource statement. However, it does not explicitly distinguish itself from the similar sibling fusion_docs_search_columns, so it stops short of full sibling differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly establishes when to use the tool: when you need to locate which tables contain a column, framed as the fastest answer. It also gives practical usage context such as LIKE patterns and the limit behavior. It does not explicitly state when not to use it or name alternatives, but the primary use case is unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_soap_callA
Destructive

Invoke any snapshotted SOAP operation. THIS MAY CHANGE DATA.

The envelope is built from the service's own contract, not a guess: the namespaces differ per service (some split the operation wrapper into a /types/ namespace, some use one namespace for everything) and a hardcoded shape produces a well-formed envelope the other services reject.

parameters is a name/value map, but it is sent in contract order, not dict order. A parameter the contract declares and you omit is sent as an empty element rather than dropped -- for positional operations, omitting it shifts every later argument by a slot.

Marked destructive because the operation set includes creates, cancels and deletes. Read-only operations go through the same gate; that is the cost of one door rather than a maintained list of which names are safe.

Args: service: Service path, e.g. /fscmService/PurchaseOrderService. operation: Operation name from fusion_soap_describe. parameters: Name/value map of arguments. confirmed: Set True only after explicit user confirmation.

ParametersJSON Schema
NameRequiredDescriptionDefault
serviceYes
confirmedNo
operationYes
parametersNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the destructive/read-only annotations, the description discloses the envelope is built from the service contract with service-specific namespaces, parameters are sent in contract order, omitted parameters become empty elements shifting positional arguments, and read-only operations still pass through the destructive gate. This is substantial behavioral context that annotations alone do not provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The warning about data change is front-loaded, each paragraph adds unique behavioral or safety information, and the Args list is compact. The namespace discussion is a little detailed but earns its place because it explains a real failure mode.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex mutating tool, the description covers prerequisites, parameter behavior, safety gate, and service-specific envelope construction. An output schema exists, so return-value documentation is not required from the description, and nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the Args section explains every parameter: service gets a concrete path example, operation is sourced from fusion_soap_describe, parameters is a name/value map with ordering and omission semantics, and confirmed has a strict safety condition. This fully compensates for the schema's lack of descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Invoke any snapshotted SOAP operation.' It also references fusion_soap_describe for operation names and explicitly warns it may change data, making it easy to distinguish from sibling inspection tools such as fusion_soap_describe and fusion_soap_list_services.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides a clear prerequisite—operation names must come from fusion_soap_describe—and states that confirmed must only be set after explicit user confirmation. It does not explicitly enumerate alternative tools or when-not-to-use conditions, but the scope ('any snapshotted SOAP operation') and the destructive gate make the usage context clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_soap_describeA
Read-onlyIdempotent

Operations of a snapshotted SOAP service, or one operation in detail.

Parameters come back in the order the contract declares them, which matters: several Fusion operations are positional, and a shifted argument produces a call that runs against the wrong data without complaining.

Args: service: Service path, e.g. /fscmService/ErpIntegrationService. operation: Optional single operation to detail.

ParametersJSON Schema
NameRequiredDescriptionDefault
serviceYes
operationNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, openWorld, and non-destructive behavior. The description adds meaningful context beyond that: the snapshotted nature of the service and the warning that parameter order matters and can cause silent, incorrect calls. This is valuable behavioral disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact, front-loaded with the core purpose, and uses a clean Args section. The positional-order warning is not padding; it is high-value guidance directly relevant to calling Fusion operations safely.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present and annotations covering safety, the description does not need to explain return values or side effects. Both input parameters are documented and the crucial positional-order caveat is included. The only real gap is explicit routing guidance relative to sibling tools.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must carry parameter meaning. It documents both parameters: service with a concrete path example and operation as optional. This adds real value beyond the schema, though it could be slightly richer about operation name format.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description identifies the tool as one that returns operations of a snapshotted SOAP service, optionally detailing a single operation. This makes the resource and action clear, and distinguishes it from call/list tools in the sibling set. It stops short of explicitly naming a sibling it is not, so it does not fully earn a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The usage context is implied through the positional-parameter warning: an agent should inspect operation parameter order before making calls. However, the description never explicitly says when to use this tool versus fusion_soap_call or fusion_soap_list_services, and it offers no exclusion criteria.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_soap_list_servicesA
Read-onlyIdempotent

SOAP services snapshotted for this pod, with their operation counts.

Fusion exposes dozens of SOAP services beyond the REST resources -- whole areas of functionality (purchase order change orders, opportunity management, BI Publisher catalog administration) exist only here.

Snapshot them with: fusion-query-apicat --soap /fscmService/XxxService

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already declare readOnlyHint, openWorldHint, idempotentHint, and non-destructive behavior. The description adds valuable context by clarifying that the service list is a snapshot, that operation counts are included, and that the SOAP surface extends beyond REST resources. No contradiction with annotations exists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the essential definition in the first sentence, then adds useful context about why SOAP services matter and how snapshots are produced. The third sentence references an external CLI command, which is slightly tangential but still helps explain the snapshot origin without bloating the description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, read-only tool with an output schema and strong annotations, the description is largely complete. It explains what is returned (services with operation counts), the pod scope, and why this tool is useful. It could be slightly stronger by explicitly routing to fusion_soap_describe for detailed service inspection, but that gap is minor given the available structured metadata.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters and the input schema is empty, so there are no parameter semantics for the description to clarify. The baseline of 4 applies because there is no parameter information missing that the description would need to compensate for.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('list') and resource ('SOAP services snapshotted for this pod'), and adds that operation counts are included. It clearly distinguishes itself from the sibling tools fusion_soap_describe and fusion_soap_call by focusing on the inventory of services rather than inspecting or invoking a single service.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage: use this tool to discover available SOAP services that are not exposed through REST, especially when exploring functionality like purchase order change orders or BI Publisher catalog administration. However, it does not explicitly say when to choose this tool instead of fusion_soap_describe or fusion_soap_call, nor does it state exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_submit_jobA
Destructive

Run a Fusion scheduled process (ESS job). THIS STARTS REAL WORK.

For everything that has no REST resource: Import Payables Invoices, Create Accounting, and the rest of Scheduled Processes. Returns a request id; poll it with fusion_job_status.

A submitted job CANNOT be recalled. Unlike a REST create -- which is often refused, changing nothing -- there is no safe failed attempt here: once the request id exists the job is queued. Confirm with the user before setting confirmed.

parameters is POSITIONAL, and the pod's own words on it are: "the order of the parameters is maintained as per the list. The corresponding entry in the list should be blank when a given parameter is not passed." Pass an empty string to skip an argument -- never omit it, or every later argument shifts by one and the job runs against the wrong data without complaining.

Args: package: ESS job package, e.g. /oracle/apps/ess/financials/payables/invoices/transactions. definition: Job definition name, e.g. APXIIMPT. parameters: Positional arguments; "" for any that is skipped. confirmed: Set True only after explicit user confirmation.

ParametersJSON Schema
NameRequiredDescriptionDefault
packageYes
confirmedNo
definitionYes
parametersNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes well beyond the annotations (which already mark destructiveHint=true). It discloses irreversibility: 'A submitted job CANNOT be recalled.' It also reveals that even a failed attempt has side effects: 'once the request id exists the job is queued.' This adds crucial context about real-world consequences not captured by the structured annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but not bloated. It front-loads the most critical warning ('THIS STARTS REAL WORK'), then provides scope, return behavior, irreversibility, and parameter guidance. The quoted pod wording adds length but is unique and valuable. A slightly tighter phrasing around the REST create comparison could make it more concise, but every sentence contributes meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

This is a high-complexity tool with an output schema available, so return-value explanation is not needed. The description covers the operational essentials: when it applies, how to confirm, what to expect (request id), how parameters must be ordered, and irreversibility. There are no significant gaps for an agent to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description carries the full burden. It explains each of the four parameters in the Args section, including examples for `package` and `definition`, and provides detailed positional semantics for `parameters` with the explicit instruction to use empty strings for skipped arguments. It also explains the `confirmed` flag. No parameter is left unexplained.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description opens with a specific verb and resource: 'Run a Fusion scheduled process (ESS job).' It explicitly distinguishes itself from REST-based operations by saying 'For everything that has no REST resource: Import Payables Invoices, Create Accounting, and the rest of Scheduled Processes.' This clearly separates it from sibling tools like fusion_run_query or fusion_run_report.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use this tool: 'For everything that has no REST resource' and lists concrete example jobs. It also tells the agent to poll with `fusion_job_status` for results. It additionally gives a strong when-not-to-use signal: unlike a REST create, a submitted job cannot be safely failed, so user confirmation is required before setting `confirmed`.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_task_detailA
Read-onlyIdempotent

One task in full, including the outcomes this user may actually submit.

Read outcomes before offering the user a choice. It is derived from the task's own actionList, keeping only entries that are not actionType: System -- the system entries (REASSIGN, ESCALATE, ACQUIRE...) are plumbing, not decisions.

An EMPTY outcomes list is a real and common state, not an error: it means the signed-in account cannot action this task, typically because it is assigned to a group the account has not acquired. Observed on this pod: an absence-approval task offers no business outcome at all while two e-signature tasks offer APPROVE/REJECT to the same account.

Args: number: The task number from fusion_list_tasks.

ParametersJSON Schema
NameRequiredDescriptionDefault
numberYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes well beyond the annotations (readOnlyHint/openWorldHint/idempotentHint/destructiveHint) by explaining how `outcomes` is derived from the task's `actionList` with System entries filtered out, and by explicitly warning that an EMPTY outcomes list is a real common state, not an error, with a concrete observed pod example. This is exactly the interpretive context that structured annotations cannot convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the purpose in the first line and each paragraph earns its place: operational guidance, edge-case semantics, and argument sourcing. The observed-pod anecdote is slightly longer than strictly necessary, but it reinforces a non-obvious behavior, so the length is justified.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present and annotations covering the read-only/idempotent safety profile, the description fully covers everything else an agent needs: what the tool returns, the non-obvious meaning of empty outcomes, why it can be empty (assigned group not acquired), and where to obtain the single argument. No critical gap remains for a single-parameter read tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% — the schema only names the parameter "Number" with type string — so the description carries the full burden. It compensates by specifying the provenance: "The task number from `fusion_list_tasks`," which is the key semantic an agent needs to populate the argument correctly. It stops short of 5 because it doesn't hint at string format or any transformation needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

"One task in full, including the outcomes this user may actually submit" clearly identifies the resource (a single task) and the distinctive content (actionable outcomes), which separates it from fusion_list_tasks (list view) and fusion_act_on_task (taking action). However, no explicit verb like "read" or "get" is present — the intent is stated as a noun phrase, so it stops just short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear operational context: read `outcomes` before offering the user a choice, and the `number` argument comes from `fusion_list_tasks`. It implicitly establishes when to call this tool (when full task detail and the user's actual decision options are needed), but it does not explicitly name exclusions or alternative siblings to consider instead.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_update_interface_dataA
Destructive

Replace the rejected rows of a partly-failed import with corrected ones.

The repair path: an import loads ten thousand rows, forty are rejected into the interface tables, and this replaces those forty rather than reloading the whole file.

load_request_id identifies WHICH load's error rows are being corrected. Pass the wrong one and the correction lands on a different batch, so read it back to the user before confirming.

Args: process_name: The import job's process name. load_request_id: Request id of the load being corrected. file_path: Local file holding the corrected rows. account: UCM account for the upload. confirmed: Set True only after explicit user confirmation.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountYes
confirmedNo
file_pathYes
process_nameYes
load_request_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark destructiveHint=true and readOnlyHint=false, but the description adds crucial behavioral context: passing the wrong load_request_id lands the correction on a different batch, and the tool must not be confirmed until the user reads back the request ID. This warns about the destructive, mis-targeting risk and the confirmation requirement beyond what annotations state.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with a one-sentence summary, followed by a concrete illustrative scenario, a crucial warning, and a terse Args list. Every sentence carries operational value; the example scales the problem ('ten thousand rows, forty rejected') without bloating. No filler or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With five parameters, zero schema descriptions, a destructive annotation, and an output schema present, the description covers everything needed for correct invocation: all parameter meanings, the confirmation protocol, and the risk of using the wrong load_request_id. It leaves no essential gap for the agent to call or validate this tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so every parameter must be explained in the description. It does so clearly: process_name, load_request_id, file_path, account, and confirmed all receive meaningful definitions. The warning about load_request_id adds critical semantic nuance beyond the mere schema name.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Replace the rejected rows of a partly-failed import with corrected ones,' a specific verb-resource pair that clearly states what the tool does. It distinguishes itself from broader import/submission siblings by focusing on the repair of rejected rows, making the tool's role unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly frames the repair path: an import with forty rejected rows is corrected by replacing those rows 'rather than reloading the whole file.' This contrasts with the alternative and tells the agent when to invoke this tool. It also gives a safety guideline: verify the load_request_id and require explicit user confirmation before confirming.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_use_podA
Read-onlyIdempotent

Bind this session to one pod: every later call runs against it.

Switches config, credentials, report registry, fixtures, audit log and docs snapshot to the named pod in one move, and stays in effect until the session ends or this is called again. Schema knowledge does NOT carry across pods -- releases and customizations (DFFs, custom objects) differ, so re-verify anything pod-specific after a switch. Every data-bearing response echoes the active pod in its pod field.

Args: pod: Directory name under pods/, e.g. test or prod.

ParametersJSON Schema
NameRequiredDescriptionDefault
podYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds significant behavioral context beyond the annotations: it switches config, credentials, report registry, fixtures, audit log, and docs snapshot; persists until session end or re-call; and alerts that schema knowledge does not carry across pods. This enriches the readOnly/idempotent hints without contradicting them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is tightly structured, front-loading the central behavior in the first sentence, then explaining side effects, caveats, and parameters. Every sentence adds useful information with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a session-binding tool, the description is complete: it defines the effect, the lifecycle, the caveat about cross-pod schema knowledge, and the response convention. An output schema exists, so the description need not spell out return values.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema provides only the parameter name 'pod' with no description, so the description carries the full burden. It explains that the value is a directory name under pods/ and gives concrete examples ('test' or 'prod'). This is sufficient for a single simple parameter, though it could have pointed to fusion_list_pods for available values.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence, 'Bind this session to one pod: every later call runs against it,' clearly states the verb, resource, and immediate consequence. It also distinguishes itself from sibling tools by describing a session-wide context switch rather than a data operation, which is unique among the sibling list.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly implies when to use the tool: before subsequent calls when a specific pod context is needed. It explains how long the effect lasts and even warns about schema differences across pods. However, it does not explicitly name sibling alternatives like fusion_list_pods for discovering pod names, so the guidance stops just short of fully routing the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_validate_queryA
Read-onlyIdempotent

Validate free SQL -- ONLY on a pod that substitutes lexical parameters.

Same engine as fusion_validate_report, pointed at a statement instead of a registered report. It therefore inherits fusion_run_query's limitation: on a pod with lexical substitution disabled the statement never reaches the database (SELECT * FROM (), ORA-00903), and the response hint says so. Use fusion_validate_report instead there -- validating a registered report's output is the path that works, and validation is the point of this server.

Where free SQL does run, this is what turns "the query ran" into "the query is correct". Supply the facts the user already trusts -- a total read off the Fusion UI, a known document number, an exported spreadsheet -- and every expectation is evaluated (never short-circuited) so one round trip tells you everything that is wrong.

Read the diff on failure; it is a repair signal, not just a verdict. A unique_key failure means join fan-out. An expected row that is absent means either the WHERE is too tight or Fusion row-level Data Security hides it from the service account -- add a broadened cross_check COUNT(*) to tell those apart before rewriting the query.

Two honest limits: a passing fixture proves consistency with the ground truth supplied, not universal correctness (use at least two independent expectation types); and results reflect what the single service account is allowed to see.

Args: sql: The SELECT statement to validate. expectations: Inline expectation objects. Mutually exclusive with fixture. fixture: Name of a saved fixture to validate against. datasource: Configured datasource key. max_rows: Row cap for the validation run; keep it above the expected count. timeout_s: Per-call timeout in seconds.

ParametersJSON Schema
NameRequiredDescriptionDefault
sqlYes
fixtureNo
max_rowsNo
timeout_sNo
datasourceNo
expectationsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark it read-only and non-destructive, and the description adds substantial behavior: the lexical-substitution failure mode with ORA-00903, all expectations always evaluated, diff interpretation, join fan-out, and row-level security visibility. It also discloses the limitation that passing only proves consistency with supplied ground truth.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but front-loaded, with the core purpose and key constraint in the first line. Each paragraph earns its place, and the final Args bullet list gives a scannable parameter reference without padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, the description covers when to use it, failure modes, diagnostic interpretation, and limitations. An output schema exists for response details, so the description's focus on selection and interpretation is appropriately complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema provides no property descriptions (0% coverage), but the Args section explains every parameter, including mutual exclusivity of expectations and fixture, the meaning of datasource as a configured key, max_rows as a cap above expected count, and timeout_s as per-call timeout. This fully compensates for the schema gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource, 'Validate free SQL', and immediately differentiates itself from fusion_validate_report ('same engine... pointed at a statement instead of a registered report'). It clearly establishes this tool validates ad hoc SQL rather than registered reports.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives an explicit condition for use ('ONLY on a pod that substitutes lexical parameters') and names the alternative ('Use fusion_validate_report instead there'). It also explains when the tool applies ('Where free SQL does run') and how to distinguish failure modes.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fusion_validate_reportA
Read-onlyIdempotent

Run a registered report once and check its results against known ground truth.

This is the tool that makes an answer trustworthy rather than merely plausible, and it is the validation path that works on a pod without lexical substitution. Supply the facts the user already trusts -- a total read off the Fusion UI, a known document number, an exported spreadsheet -- and every expectation is evaluated (never short-circuited), so one round trip tells you everything that is wrong rather than the first thing.

Read the diff on failure; it is a repair signal, not just a verdict. Because the SQL lives in the report's data model, some repairs are not yours to make:

  • unique_key failed -- join fan-out inside the report: a _TL join without LANGUAGE = USERENV('LANG'), or an _F/_M join without an effective-date predicate. Report it to whoever owns the data model.

  • an expected row is absent -- either the report's filter is too tight, or Fusion row-level Data Security hides that row from the service account. Vary the bind values to test the first before assuming the second.

  • an aggregate is off -- cancelled/draft rows still included, mixed currencies summed together, or fan-out multiplying the amount.

cross_check needs to run an independent SQL statement, which this pod refuses. It comes back as one failed expectation explaining exactly that, while every other expectation is still evaluated normally -- so do not read its failure as a data problem.

Two honest limits: a passing fixture proves consistency with the ground truth supplied, not universal correctness (use at least two independent expectation types); and results reflect what the single service account is allowed to see.

Args: report: Registered report name, as listed by fusion_list_reports. params: Bind values for the report. Omitted parameters take its defaults. expectations: Inline expectation objects. Mutually exclusive with fixture. fixture: Name of a saved fixture to validate against. max_rows: Rows to validate over; keep it above the expected row count, as the report itself applies no cap and this one is applied here. timeout_s: Per-call timeout in seconds.

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsNo
reportYes
fixtureNo
max_rowsNo
timeout_sNo
expectationsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, open-world, and non-destructive. The description adds substantial behavior beyond that: every expectation is evaluated without short-circuiting, the diff is a repair signal, common failure modes map to concrete root causes, and cross_check returns as one failed expectation by design. It also discloses the honest limits about ground-truth consistency and service-account visibility.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long, but the length is justified by the tool's complexity. It front-loads the core purpose, uses bullet lists for failure-mode triage, and ends with a compact Args section. A little trimming of the philosophical framing ('trustworthy rather than merely plausible') would make it tighter, but no sentence is pure filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a validation tool with 6 parameters, no schema-level descriptions, and high behavioral nuance, the description covers everything an agent needs: full parameter semantics, failure interpretation, repair ownership, environment limitations, and the meaning of a passing fixture. The existence of an output schema relieves it of documenting return shapes, and it appropriately leaves expectation object schemas open while giving concrete examples of valid ground-truth sources.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries the full burden—and it pays off. The Args section explains every parameter in plain terms, adds the relationship that expectations and fixture are mutually exclusive, clarifies max_rows is applied locally because the report applies no cap, and notes that omitted params take defaults. This far exceeds what the bare schema offers.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence, 'Run a registered report once and check its results against known ground truth,' names a specific verb, resource, and outcome. It further distinguishes itself as 'the validation path that works on a pod without lexical substitution,' separating it from sibling report/validation tools without needing to read their schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives strong context on when to use the tool: supply trusted ground truth and get all failures in one pass. It also explains what cross_check will do in this environment and instructs to vary bind values to test filter tightness before assuming data security issues. It never explicitly names a competing sibling such as fusion_validate_query or fusion_run_report, so it stops short of a full when-to-use-vs-alternatives statement.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 40 tool updatesv0.1.0
    • First observedfusion_act_on_task
    • First observedfusion_adhoc_query
    • First observedfusion_api_describe
    • First observedfusion_api_list_actions
    • First observedfusion_api_search
    • First observedfusion_author_report
    • First observedfusion_bootstrap
    • First observedfusion_commit_action
    • First observedfusion_describe_flexfields
    • First observedfusion_describe_table
    • First observedfusion_docs_describe_table
    • First observedfusion_docs_search_columns
    • First observedfusion_export_bulk_data
    • First observedfusion_find_uploaded_files
    • First observedfusion_get_fixture
    • First observedfusion_get_hints
    • First observedfusion_health_check
    • First observedfusion_import_bulk_data
    • First observedfusion_job_log
    • First observedfusion_job_status
    • First observedfusion_list_fixtures
    • First observedfusion_list_pods
    • First observedfusion_list_reports
    • First observedfusion_list_tables
    • First observedfusion_list_tasks
    • First observedfusion_prepare_action
    • First observedfusion_resolve_value
    • First observedfusion_run_query
    • First observedfusion_run_report
    • First observedfusion_save_fixture
    • First observedfusion_search_columns
    • First observedfusion_soap_call
    • First observedfusion_soap_describe
    • First observedfusion_soap_list_services
    • First observedfusion_submit_job
    • First observedfusion_task_detail
    • First observedfusion_update_interface_data
    • First observedfusion_use_pod
    • First observedfusion_validate_query
    • First observedfusion_validate_report

TDQS

A4.2/5.0

Scored across 40 tools

Disambiguation4/5

Most tools have crisp distinct purposes, and the domain prefixes (docs_, api_, soap_) in names do real disambiguation work. However, there are several deliberate parallel pairs — live vs docs-snapshot describe/search, run_query vs adhoc_query vs run_report, validate_report vs validate_query — whose boundaries are only clear after reading the lengthy descriptions, so a skimming agent could easily pick the wrong query or validation path.

Naming Consistency4/5

The set overwhelmingly follows fusion_<verb>_<noun> (or fusion_<module>_<verb>_<noun> for docs_/api_/soap_), which is a strong, predictable convention across 40 tools. A few outliers break the verb-first shape — fusion_health_check, fusion_adhoc_query, fusion_bootstrap — but they remain readable and do not undermine the overall pattern.

Tool Count3/5

40 tools is heavy and exceeds the comfortable band, though the count reflects a genuinely broad scope: SQL, BI reports, REST, SOAP, ESS/FBDI, approvals, pods, and docs each form their own cluster. The live-vs-docs-snapshot duplications and the three overlapping query execution paths inflate the number without adding new capability, making the surface larger than it needs to be.

Completeness4/5

The surface covers the full lifecycle across channels: schema exploration, query execution (report, ad-hoc, free-SQL), validation with fixtures, guarded write actions (REST, SOAP, ESS, FBDI), monitoring (job status/log, health check), and repair (bootstrap, interface-data correction). Minor gaps exist — no fixture deletion, no report deletion, no direct REST record GET by ID, no job cancellation — but all are workable around or explained as inherent constraints.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Enables AI tools to interact with Oracle databases through query execution, schema browsing, stored procedure calls, and transaction management. Supports multiple database connections with safety features like read-only mode and dangerous query detection.
    16
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    Read-only access to Oracle Fusion Cloud ERP data via natural language queries, with support for accounts payable, procurement, general ledger, and more.
    30
    3
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Provides AI assistants with secure, structured access to Oracle Database through MCP, enabling SQL execution, metadata exploration, and stored procedure execution.
    -
  • F
    license
    Not graded
    quality
    B
    maintenance
    Grounds a Fusion-SQL agent against Oracle Fusion schema catalog, enabling table/column search, validation, and relationship discovery.
    -