Fusion MCP
# Fusion MCP — Oracle Fusion ERP Cloud MCP Server
A **local stdio MCP server** (Python, FastMCP) that gives an LLM client (Claude
Desktop / Claude Code) **read-only access** to an Oracle Fusion / ERP Cloud database —
discover objects, describe tables, read source, and run SELECT queries — all via the
**BI Publisher SOAP API** (no direct DB connection required).
## Architecture
```
Claude Desktop / Claude Code (MCP stdio)
│
▼
Fusion MCP Server (Python, FastMCP)
Tools ──► SQL guard ──► BIP client (requests + SOAP)
│
Catalog SQL builders (ALL_OBJECTS, ALL_TAB_COLUMNS, …)
│
▼
Oracle Fusion — BI Publisher web services
ExternalReportWSSService (runReport → base64 CSV)
│
▼
FUSION schema + ALL_* data dictionary
```
## Prerequisites
### 1 — Fusion BIP service account
Create (or identify) a Fusion user that:
- Has the **BI Publisher role** (`BIPAdministrator` or a custom role with BIP access).
- Has **read-only access** on the FUSION schema and the Oracle data dictionary
(`SELECT_CATALOG_ROLE` equivalent, or appropriate Fusion data roles).
- Will be used as `FUSION_USER` / `FUSION_PASSWORD` in `.env`.
This is the primary security boundary — the app-layer SQL guard enforces
`SELECT`/`WITH` only, but the BIP service account must also be read-only at
the database level.
### 2 — Python 3.11+
Install [uv](https://docs.astral.sh/uv/) or use pip.
## Setup (local development)
```powershell
# Clone the repo
git clone https://github.com/ramesharavapally/FUSION-MCP.git
cd FUSION-MCP
# Install (editable + dev deps)
uv pip install -e ".[dev]"
# Copy and fill in credentials
cp .env.example .env
# Edit .env: set FUSION_BASE_URL, FUSION_USER, FUSION_PASSWORD
```
## Running the server
```powershell
# From a local clone
uv run fusion-mcp
# Or directly from GitHub without cloning (production use)
uvx --from git+https://github.com/ramesharavapally/FUSION-MCP.git fusion-mcp
```
### Deploying the SQL-runner report
The server does **not** deploy any BIP artifacts — you set up the report
manually in the BI Publisher catalog before running the server.
The query is sent as a **base64-encoded bind parameter** named `query1` (not a
lexical `&query1`). This is deliberate: passing raw SQL as a lexical value trips
BIP's SQL-injection guard (`SQLInjection Error: Invalid parameter value …`), and
on Fusion SaaS that guard can't be disabled. Base64-encoding keeps the parameter
value free of SQL keywords, and a PL/SQL data model decodes it and opens a cursor.
Create a CSV-output report over a data model whose dataset is this PL/SQL block:
```sql
DECLARE
TYPE refcursor IS REF CURSOR;
xdo_cursor REFCURSOR;
l_query VARCHAR2(32000);
FUNCTION get_query(p_query IN VARCHAR2) RETURN VARCHAR2 IS
BEGIN
RETURN utl_raw.cast_to_varchar2(
utl_encode.base64_decode(utl_raw.cast_to_raw(p_query)));
END;
BEGIN
l_query := get_query(:query1);
OPEN :xdo_cursor FOR l_query;
END;
```
The server base64-encodes each query and strips any trailing `;` before sending
(the decoded statement must not carry a semicolon, since it is opened as a
cursor). Then point the server at the report:
```ini
# .env
FUSION_REPORT_PATH=/your/existing/report/path.xdo
```
## Tests
```powershell
# Unit tests — no Fusion connection required
uv run pytest
# Single test file
uv run pytest tests/test_sql_guard.py
```
## MCP Inspector (interactive tool testing)
```powershell
npx @modelcontextprotocol/inspector uv run fusion-mcp
```
## Register with Claude Desktop
Add to `claude_desktop_config.json` (typically at
`%APPDATA%\Claude\claude_desktop_config.json` on Windows or
`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
```json
{
"mcpServers": {
"fusion-mcp": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/ramesharavapally/FUSION-MCP.git",
"fusion-mcp"
],
"env": {
"FUSION_BASE_URL": "https://<pod>.fa.ocs.oraclecloud.com",
"FUSION_USER": "your_bip_service_user",
"FUSION_PASSWORD": "your_password",
"FUSION_MAX_ROWS": "100",
"LOG_LEVEL": "INFO"
}
}
}
}
```
`uvx` fetches the package directly from GitHub and runs it in an isolated
environment — no local clone or `pip install` needed. Claude Desktop
re-uses the cached environment on subsequent starts; to pick up a new
commit, restart Claude Desktop (uvx re-checks the git ref on each cold
start).
> **Tip:** pin to a specific commit or tag for stability:
> ```
> "git+https://github.com/ramesharavapally/FUSION-MCP.git@v1.0.0"
> ```
## Available MCP tools
| Tool | Description |
|---|---|
| `search_objects` | Search `ALL_OBJECTS` by name pattern (tables, views, packages, …) |
| `search_tables` | Find tables/views by name or comment |
| `search_columns` | Find tables by column name or column comment |
| `search_source` | Full-text search across `ALL_SOURCE` |
| `describe_table` | Full column/PK/index/stats description of a table or view |
| `read_object_source` | Read full source of a procedure/function/package from `ALL_SOURCE` |
| `get_procedure_signature` | Discover argument names, types, and directions from `ALL_ARGUMENTS` |
| `list_package_contents` | List all subprograms inside a package |
| `execute_query` | Run a read-only `SELECT`/`WITH` query; returns CSV-parsed rows |
> **Note:** `call_procedure` is not available in Fusion MCP. Oracle BIP executes
> SELECT data models only — anonymous PL/SQL blocks are not supported.
> Use `get_procedure_signature` and `read_object_source` for procedure discovery.
## Security model
1. **SQL guard** (`safety/sql_guard.py`) — rejects anything that is not a single
`SELECT`/`WITH`; blocks `;` chaining; validates identifiers used in catalog SQL.
This is the app-layer boundary.
2. **BIP data model** — the deployed report decodes the base64 `query1` bind
parameter and opens it as a cursor (`OPEN … FOR`), which only executes a single
query; multi-statement/DML text cannot be smuggled through this path.
3. **Service account** — the BIP user should have read-only database grants (see
prerequisites above). This is the ultimate security boundary.
## Configuration reference
| Variable | Default | Description |
|---|---|---|
| `FUSION_BASE_URL` | — | Fusion pod URL, e.g. `https://<pod>.fa.ocs.oraclecloud.com` |
| `FUSION_USER` | — | BIP service account username |
| `FUSION_PASSWORD` | — | BIP service account password |
| `FUSION_REPORT_PATH` | `/Custom/py_sql/SampleReport.xdo` | BIP catalog path for the manually-deployed report |
| `FUSION_MAX_ROWS` | `100` | Maximum rows returned per query |
| `FUSION_REQUEST_TIMEOUT_S` | `120` | HTTP timeout for BIP calls (seconds) |
| `LOG_LEVEL` | `INFO` | Logging level (DEBUG, INFO, WARNING, ERROR) |
TDQS
Scored across 9 tools
Each tool has a clearly distinct purpose: describe_table for table metadata, execute_query for read-only SQL, get_procedure_signature for argument details, list_package_contents for package subprograms, read_object_source for source code, and multiple search tools for different search scopes (columns, objects, source, tables). No two tools overlap in functionality.
All tool names follow a consistent verb_noun pattern in snake_case (e.g., describe_table, execute_query, search_columns). The verbs (describe, execute, get, list, read, search) are intuitive and match the action. No mixing of styles or ambiguous names.
With 9 tools, the server is well-scoped for its purpose of exploring and querying an Oracle Fusion database. Each tool earns its place without being overwhelming or too sparse.
The tool set covers most essential operations: discovery (search tools), description (describe_table), query execution (execute_query), and code examination (read_object_source, list_package_contents, get_procedure_signature). However, there is no simple 'list all tables' tool without a keyword, so users must search even for a broad listing. This minor gap prevents a perfect score.