fusion-query-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| FUSION_HOME | No | Override directory for configuration and pod data. Defaults to ~/.fusion-query/ when unset. | |
| FUSION_PASS | No | Fusion service account password. May be omitted if credentials are supplied via the pod's .env file. | |
| FUSION_USER | No | Fusion service account username. May be omitted if credentials are supplied via the pod's .env file. | |
| FUSION_BACKEND | No | Override for fusion.backend: 'rest' or 'soap'. | |
| FUSION_BASE_URL | No | Override for fusion.base_url: the Oracle Fusion pod URL without a trailing slash. | |
| FUSION_MAX_ROWS | No | Override for limits.default_max_rows. | |
| FUSION_AUDIT_PATH | No | Override for audit.path. | |
| FUSION_MCP_CONFIG | Yes | Path to the Fusion MCP configuration file (config.yaml). This must be passed explicitly because an MCP server does not inherit the shell's working directory. | |
| FUSION_ENGINE_MODE | No | Override for fusion.engine_mode: 'whole_query' or 'clauses' (legacy lexical path). | |
| FUSION_PARAM_SHAPE | No | Override for fusion.param_shape: 'auto', 'flat', or 'item'. | |
| FUSION_FIXTURES_DIR | No | Override for fixtures_dir. | |
| FUSION_HARD_MAX_ROWS | No | Override for limits.hard_max_rows. | |
| FUSION_MAX_SQL_CHARS | No | Override for limits.max_sql_chars (legacy lexical path only). | |
| FUSION_TIMEOUT_SECONDS | No | Override for limits.timeout_seconds. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| fusion_run_queryA | 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
The working path on such a pod is the report registry: call
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
( 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: |
| fusion_list_tablesA | List Fusion tables matching a SQL LIKE pattern, most-populated first. Runs the registered
The Args:
pattern: SQL LIKE pattern, e.g. |
| fusion_describe_tableA | 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 --
Whenever more than one object matched, Args:
table: Object name, optionally |
| fusion_search_columnsA | Find which tables carry a column -- the fastest answer to "where does X live?". Runs the registered The report applies no row cap, so Args:
pattern: SQL LIKE pattern over the column name, e.g. |
| fusion_list_podsA | Which Fusion pods this server can talk to, and which one is active. Pods are directories under |
| fusion_use_podA | 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 Args:
pod: Directory name under pods/, e.g. |
| fusion_docs_describe_tableA | 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 The snapshot is docs-derived, not pod ground truth: a column that exists
here but not on the pod will fail loudly in Args:
table: Exact table or view name, e.g. |
| fusion_docs_search_columnsA | 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
Args: term: Words to match (AND-combined), or a column-name fragment. limit: Maximum matches returned. |
| fusion_describe_flexfieldsA | 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 Protocol for an unknown table:
Args:
flexfield: LIKE pattern over the DFF code, e.g. |
| fusion_api_list_actionsA | 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,
|
| fusion_api_describeA | 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 ( Read Args:
resource: Exact resource name, e.g. |
| fusion_api_searchA | Find REST resources and fields by name or business meaning. Instant, local, no pod round trip. Use it to turn a phrase the user said
("the business unit that raises the request") into the field name the API
expects ( Args: term: Substring to match against resource names, field names and business descriptions. limit: Maximum matches per kind (resources and fields counted apart). |
| fusion_resolve_valueA | Turn a name the user said into the id the API wants.
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 Args:
field: The REST attribute name, e.g. |
| fusion_prepare_actionA | 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 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. |
| fusion_commit_actionA | Execute a prepared action. THIS CHANGES DATA IN FUSION. Requires the token from 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_submit_jobA | 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 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
Args:
package: ESS job package, e.g. |
| fusion_import_bulk_dataA | Load a data file into Fusion (FBDI) and queue the import jobs. Reads
Each entry in Args:
file_path: Local path of the file to upload.
account: UCM account, e.g. |
| fusion_soap_list_servicesA | 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 |
| fusion_soap_describeA | 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. |
| fusion_soap_callA | 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
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. |
| fusion_list_tasksA | Approvals and notifications waiting for the signed-in user. The worklist lives at Follow up with Args:
status: The service's own status values, e.g. |
| fusion_task_detailA | One task in full, including the outcomes this user may actually submit. Read An EMPTY Args:
number: The task number from |
| fusion_act_on_taskA | Approve, reject or otherwise decide a task. THIS DECIDES SOMETHING.
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. |
| fusion_job_logA | Fetch a finished job's log or output -- i.e. WHY it failed. Call this whenever File contents are not returned inline -- a job log can be megabytes. Pass
Args:
request_id: The job's request id.
file_type: |
| fusion_export_bulk_dataA | Run an extract job and leave its output in UCM. Returns a request id. The mirror of
Args:
job_name: |
| fusion_update_interface_dataA | 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.
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. |
| fusion_find_uploaded_filesA | 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. |
| fusion_job_statusA | Check a scheduled process submitted with
Args:
request_id: The id returned by |
| fusion_list_reportsA | 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 For each report you get its |
| fusion_run_reportA | 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 The report applies no row cap of its own, so Args:
report: Registered report name, as listed by |
| fusion_adhoc_queryA | 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
Rules the statement must follow (they are the data-model rules):
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 |
| 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 Strongly recommended: pass Gated behind Args:
name: Registry key, lower_snake_case, e.g. |
| 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 ( 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 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. |
| fusion_validate_queryA | Validate free SQL -- ONLY on a pod that substitutes lexical parameters. Same engine as 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 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 |
| fusion_validate_reportA | 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
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_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
Values in configured redacted columns are masked before the file is written. Args:
name: Fixture name; letters, digits, |
| fusion_list_fixturesA | List saved ground-truth fixtures available for validation. |
| fusion_get_fixtureA | Read one saved fixture, including its expectations and stored SQL. Args:
name: The fixture name as reported by |
| fusion_get_hintsA | Curated Oracle Fusion schema knowledge: the traps that corrupt results silently. Read the relevant topic before trusting a joined result. A missing Args:
topic: One of the knowledge-base topics ( |
| fusion_health_checkA | Verify the whole path to the pod: credentials, report, bind values, parsing. Probes something that actually works: it runs the registered
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 |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| fusion-query-workflow | The protocol for discovering, running and validating Fusion reports. |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| Oracle Fusion query hints | The full Fusion schema knowledge base as Markdown. |
TDQS
Scored across 40 tools
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.
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.
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.
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.