MCP Server¶
Connect your AI of choice to governed AcuGIS Report Server datasets.
AcuGIS Report Server Professional includes an MCP (Model Context Protocol) server that allows compatible AI clients to discover, understand, and query Report Server datasets.
The MCP server uses Dataset Intelligence as the governed data catalog. AI clients do not receive database credentials and cannot submit arbitrary SQL. They can only discover and execute existing report datasets that the authenticated Report Server user is permitted to view.
You ask a normal question. The AI client uses the datasets, parameters, and field descriptions already defined in Report Server. You do not write SQL, and you do not give the AI a database login.
How a question is answered¶
You normally do not call MCP tools yourself. You ask a question in the AI client. The client decides which tools to use.
User asks a question
↓
AI client
↓
AcuGIS Report Server MCP
↓
list_datasets
↓
describe_dataset
↓
query_dataset
↓
AI analyzes the returned data
For example, you might ask:
Which Smith Residential properties had the highest maintenance costs in 2026?
The client can look up the datasets you are allowed to use, inspect the parameters and fields, run the existing dataset, and answer from the rows that come back.
Requirements¶
| Requirement | What it means |
|---|---|
| AcuGIS Report Server Professional license | The route is compiled only into the Professional package. Dataset Intelligence, which MCP uses, stays off until a license is valid. An unlicensed Professional process refuses the call. The Open Source package does not register /api/v1/mcp. |
| Dataset Intelligence | MCP reads the Dataset Intelligence catalog of report datasets. |
| MCP enabled by an administrator | JRS_MCP_ENABLED is off until an administrator turns it on. |
| HTTPS for remote access | Production clients should use https://. |
| Personal Access Token | The same Report Server token used for other API access. There is no separate MCP token type. |
| MCP-compatible client | A client that can call a remote MCP server over streamable HTTP. |
Existing report permissions apply. A user can discover, describe, or query a dataset only when that user is allowed to view the dataset's parent report. MCP uses the authenticated user and the same view check as the rest of Report Server. It does not add a separate MCP permission scope.
Create the token under Profile → API Tokens. See API Tokens.
Server configuration¶
MCP is disabled until an administrator enables it. Accepted values for JRS_MCP_ENABLED are 1, true, yes, and on. Any other value, including an empty value, leaves the endpoint unavailable.
On a reverse-proxied public installation, also list the public hostname the proxy preserves:
JRS_MCP_ALLOWED_HOSTS is the list of public hostnames allowed for MCP requests when Report Server is behind a reverse proxy. Multiple hostnames are comma-separated. Matching ignores letter case. The default HTTPS port (443) and the default HTTP port (80) do not need to be written in the list. Any other port is part of the hostname, for example reports.example.com:8443. An empty list does not allow public hostnames.
Report Server listens on loopback by default. A connection on that listener accepts a loopback host (localhost, 127.0.0.1, or ::1) and each hostname in JRS_MCP_ALLOWED_HOSTS. Keep the reverse proxy configured to preserve the public host and to forward X-Forwarded-Proto, as described in Reverse proxy. Leave host checking in place.
The MCP endpoint is:
Use HTTPS for production remote access. If Report Server is published under a base path, include that path in the URL. See Configuration and Reverse proxy.
Restart Report Server after changing these variables. While MCP is disabled, /api/v1/mcp is not served. The Open Source package does not register the route. On a Professional package without a valid license, enabling the flag still refuses the request because Dataset Intelligence is not entitled. That denial says Community Edition.
Authentication¶
The MCP endpoint uses an existing Report Server Personal Access Token. The token acts as the user who created it, including that user's report permissions.
Store the token in the MCP client's secret or credential setting when the client has one.
Treat the token like a password
- Never put the token in a URL or query string.
- Never commit the token to source control.
- Store it in the MCP client's secret or credential mechanism when one is available.
- Revoke the token from Profile → API Tokens if it is exposed.
The examples on this page use placeholders. They are not real tokens.
Tools¶
The AI client calls these tools. People using the client do not need to invoke them by hand.
list_datasets¶
Lists active main report datasets the authenticated user is permitted to view.
Each entry can include:
- dataset id
- dataset and report name
- datasource name and type
- query language
- profile status
list_datasets synchronizes the Dataset Intelligence report-dataset catalog, then returns the datasets that the user can view. A synchronization failure is reported as an error. An empty list means the sync completed and no visible main datasets were found.
list_datasets does not:
- execute the dataset
- return SQL
- return credentials
- return rows
The client receives up to 100 datasets per call. That page size can be raised to 500. Further datasets are available by asking for the next page. The AI client handles paging.
describe_dataset¶
Returns metadata for one dataset. It does not execute the dataset.
The description can include:
- parameters, including which values are required and any non-secret default
- fields and types
- business metadata such as display names, descriptions, units, and tags
- governance, such as certification, owner, steward, and classification
- stored profiling information, including null and distinct counts when a profile already exists
- lineage, as the source tables the dataset depends on
- where the dataset is used
describe_dataset reads metadata that is already stored. It does not run the dataset query, and it does not create a new profile.
Field descriptions, units, and governance notes give the AI more context about what a field means. Empty descriptions leave the client with the field name and type only. Secret parameter defaults are omitted.
query_dataset¶
Executes an existing report main dataset using its declared parameters, and returns rows.
The AI supplies a dataset id from list_datasets and, when needed, parameter values learned from describe_dataset. Omitting parameters uses the dataset's existing non-secret defaults.
These limits are fixed:
- The AI cannot supply SQL.
- The AI cannot select an arbitrary database connection.
- Only the report's existing main dataset is executed.
- Existing Report Server report permissions apply.
- Only declared parameters can be supplied.
- Jasper
$P!{}query-text substitution is rejected. Those datasets are not executed through MCP. - Standard
$P{}bound parameters are supported. - A response contains at most 5,000 rows.
truncatedis true when more rows exist.row_countis the number of rows in that response. - Execution waits about 30 seconds. JDBC datasources are canceled when that limit is reached. DuckDB, Cassandra, and BigQuery queries are not interrupted by this MCP deadline; they keep their own provider limits, so a timed-out MCP call does not always stop those queries immediately.
All three tools are presented to the MCP client as read-only. Administrators remain responsible for making sure the underlying report datasets are appropriate for analytical access. A dataset that changes data when it runs is still that dataset when MCP executes it.
Example: Property Performance¶
The dataset in this section is an example. A Report Server installation does not automatically contain a dataset named Property Performance. Names, ids, and values below are illustrative.
Example dataset: Property Performance
Example fields:
| Field | Meaning in this example |
|---|---|
property_id |
Property identifier |
property_name |
Property name |
city |
City |
state |
State |
units |
Unit count |
occupancy |
Occupancy |
rent_collected |
Rent collected |
maintenance_cost |
Maintenance cost |
Example parameters:
| Parameter | Role in this example |
|---|---|
account_id |
Account to query |
year |
Year to query |
property_id |
Optional. When omitted, the dataset returns properties for the account and year. |
A user asks:
Which Smith Residential properties had the highest maintenance costs in 2026?
The client can call list_datasets and find Property Performance. It then calls describe_dataset and learns that the dataset accepts account_id, year, and property_id. Because property_id is optional in this example, the client can ask for the whole account and year.
The client then calls query_dataset. The dataset id below is only an example. Dataset ids are installation-specific. The client should take the id from list_datasets on that server.
An illustrative result:
{
"dataset_id": 3,
"columns": [
{"name": "property_name", "type": "java.lang.String"},
{"name": "maintenance_cost", "type": "java.lang.Double"}
],
"rows": [
{
"property_name": "Maple Court",
"maintenance_cost": 18450.00
},
{
"property_name": "Oak Terrace",
"maintenance_cost": 12780.00
}
],
"row_count": 2,
"truncated": false
}
These values are illustrative. They are not actual Property Pro results, and the number 3 is not a universal dataset id.
From that result, an AI client could answer:
Maple Court had the highest maintenance cost in 2026 at 18,450, followed by Oak Terrace at 12,780.
The answer should use the units and currency documented on the dataset. This example does not define a currency.
Example questions¶
Questions like these are a good fit when the relevant dataset, parameters, and metadata are available:
- What datasets are available to me?
- What fields and parameters are available in the Property Performance dataset?
- What does the occupancy field represent?
- Show me the properties with the highest maintenance costs in 2026.
- Which properties collected the most rent?
- Compare occupancy across properties for 2025 and 2026.
- Are there fields in this dataset that have a high percentage of null values?
- What source tables does this dataset depend on?
Whether a question can be answered depends on:
- which datasets are available
- the dataset parameters
- the fields those datasets return
- Dataset Intelligence metadata, including descriptions, units, profiles, and lineage
- the authenticated user's permissions
The AI should not be expected to answer a question that needs data the available datasets do not expose. A question about null values needs a stored profile that includes null counts. A lineage question needs stored lineage. A definition question needs a field description or other business metadata.
Dataset design for AI use¶
Datasets intended for analytical questions work best when:
- parameters allow a useful slice of the data, and an individual record is optional when a broader question is valid
- field names are meaningful
- field descriptions are populated in Dataset Intelligence
- units are documented
- business terminology is documented
- governance metadata is populated
- lineage is available
- parameter defaults are sensible where a default is safe
Property Performance illustrates the parameter point. A report designed only to display one property might require property_id. For analytical questions, making property_id optional lets someone ask about all properties for an account and year, while still allowing one property to be selected.
Keep the report's existing permissions. Design the dataset for the audience that should see it. Do not widen report access so that an AI client can see more than the user is allowed to see.
Maintain descriptions, units, tags, governance, and lineage on the Dataset Intelligence page (/dataset-intelligence). MCP returns that stored metadata from describe_dataset.
Security and governance¶
MCP does not give the AI unrestricted database access.
Current safeguards:
- Existing Report Server authentication. The client presents a Personal Access Token for a Report Server user.
- Existing report permissions. The user must be allowed to view the parent report.
- Only cataloged, active, main report datasets are available.
- The caller cannot supply SQL.
- Database credentials are not returned.
- Only declared parameters are accepted.
- Datasets that use Jasper
$P!{}query-text substitution are rejected for MCP execution. - Results are limited to 5,000 rows, with an execution wait of about 30 seconds.
- Tool calls are written to the audit trail when auditing is enabled. Query events record the dataset, report, row count, and whether the result was truncated. They do not record parameter values or result rows.
- A missing dataset, an inactive dataset, a dataset that is not the report's main dataset, and a dataset the user is not allowed to view all produce the same client-facing result: dataset not found.
query_dataset is marked read-only for the MCP client. That marking tells the client the tool is not a general write action. It does not change the dataset. Administrators remain responsible for ensuring each report dataset is appropriate to run for analysis.
MCP does not add a compliance certification. Permissions, dataset design, and token handling remain the administrator's responsibility.
Troubleshooting¶
No datasets are returned¶
list_datasets synchronizes the Dataset Intelligence report catalog before it lists datasets. Confirm that the authenticated user can view the relevant reports and that those reports have a valid main dataset. Reports the user cannot view are omitted. A failed catalog sync is an error, not an empty list.
dataset not found¶
The dataset may not exist, may not be an active main dataset, or the authenticated user may not have permission to view its parent report. The server intentionally does not tell the client which of those cases applies. Call list_datasets with the same token and use an id from that result.
missing required parameter¶
Call describe_dataset and supply each parameter it marks as required. The error names the missing parameter when the name can be shown safely.
unsupported query-text substitution¶
The report main query uses Jasper $P!{} substitution, which inserts parameter text into the query. query_dataset does not execute those datasets. Standard $P{} bound parameters remain supported. Revise the report query to use bound parameters if it should be available through MCP.
invalid Host header¶
The request reached Report Server on the loopback listener with a public hostname that is not allowed. Set JRS_MCP_ALLOWED_HOSTS to the public hostname the reverse proxy preserves, for example reports.example.com. See Reverse proxy. Leave host checking enabled.
Query result is truncated¶
MCP returns at most 5,000 rows. When truncated is true, more rows matched than the response includes. Narrow the request with the dataset's declared parameters.
Optional example: OpenAI Responses API¶
OpenAI is not required. Any MCP client that can call the streamable HTTP endpoint can use the same Report Server URL and Personal Access Token. This section shows one optional integration that uses the OpenAI Responses API remote MCP tool.
The Report Server URL in this example must be reachable from OpenAI over HTTPS. Replace the model with one your OpenAI account can call that supports remote MCP. The model name below is the one used in OpenAI's current guide.
export OPENAI_API_KEY='...'
export JRS_PAT='...'
curl https://api.openai.com/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d "$(cat <<EOF
{
"model": "gpt-6-astra",
"input": "Using AcuGIS Report Server, examine the Property Performance dataset and tell me which Smith Residential properties had the highest maintenance costs in 2026. Use the available dataset tools and do not guess about information that is not provided by the dataset.",
"tools": [
{
"type": "mcp",
"server_label": "acugis_report_server",
"server_description": "Governed datasets from AcuGIS Report Server.",
"server_url": "https://reports.example.com/api/v1/mcp",
"authorization": "${JRS_PAT}",
"require_approval": "never"
}
]
}
EOF
)"
Put the Personal Access Token in authorization. Pass the token value only, without a Bearer prefix. The current Responses API guide uses this field for the credential it sends to the MCP server, as Authorization: Bearer plus that token. Keep JRS_PAT out of source control. The '...' values above are placeholders.
OpenAI does not store the authorization value. Send it on every request. require_approval set to never lets the example run without an approval step. OpenAI can instead require approval before data is sent to Report Server. Leave approval enabled when you want to review each tool call.
Property Performance in this prompt is the same illustrative dataset used earlier on this page.