Building MCP Apps (Interactive UI) with Custom Tools
MCP Apps are interactive applications built on the Model Context Protocol (MCP) that enable an AI client to render a sandboxed user interface (for example, an HTML app) within the chat experience. The UI can exchange messages with the host and request tool execution through the host, enabling structured and interactive workflows beyond plain text responses. For general information about MCP Apps, see the Model Context Protocol documentation for MCP Apps.
In NetSuite, an MCP App is implemented as a tool that includes UI metadata in its tool schema. Standard tools return results to the chat as text or structured output, whereas an MCP App tool additionally declares a UI resource so the AI client can render an interactive UI associated with the tool.
Read the following topics for more information:
For general information about custom tools, see SuiteScript 2.1 Custom Tool Script Type.
How MCP Apps Work in NetSuite
MCP Apps build on the existing custom tools framework in NetSuite. They follow the same deployment model and core custom tool requirements (see Custom Tool Script Requirements), and add the following MCP App support:
-
UI metadata in the tool schema - A tool can declare a UI metadata using the
_meta.ui.resourceUriattribute. This tells an MCP-enabled AI client that the tool has an associated UI resource and where to load it. For information about the tool schema, see Custom Tool JSON Schema. -
Bundled HTML UI Resource - The AI client can load and render the bundled HTML UI. The UI runs in the chat experience and communicates with the host to receive tool results and request additional tool execution.
For the required files and configuration, see MCP App Requirements.
For information about building the UI for the MCP App, see the Model Context Protocol documentation for Building an MCP App.
MCP App Requirements
To create an MCP App in NetSuite, your SuiteCloud project (SuiteApp or ACP) must include the components listed below. These are the same components required for custom tools, with MCP App support added through UI metadata in the tool schema (_.meta.ui.resourceUri) and a bundled HTML UI resource.
See also Custom Tool Script Requirements.
-
Custom Tool Script File - A SuiteScript custom tool script that implements the server-side logic for your tool. The script must follow the custom tool script requirements (including
@NScriptType CustomToolJSDoc tag and asynchronous entry point functions). -
Custom Tool JSON Schema - A JSON schema that defines the tool input and output. For information about the tool schema, see Custom Tool JSON Schema.
To enable an MCP App UI for a tool, add UI metadata to the tool definition using the
_meta.ui.resourceUriattribute. For example:"_meta": { "ui": { "resourceUri": "ui://SuiteApps/<SuiteAppId>/<folder>/mcp-app.html" } } -
Toolset XML Object - A toolset SDF XML definition file that specifies the custom tool script and schema files and defines the permissions used to control tool visibility in the AI client. For more information, see Custom Tool Scripts as XML Definitions.
-
Bundled HTML File for the UI - The HTML file referenced by
_meta.ui.resourceUri. The HTML must be bundled and self-contained and must be included in your SuiteCloud project at the referenced path. The UI must not dynamically load dependencies or fetch external resources at runtime. If your app has CSS and JavaScript assets, you should bundle your UI and assets into a single HTML file.Note:The source UI project does not need to be included in the SuiteCloud project. Only the bundled HTML output must be included in the SuiteCloud project at the File Cabinet path referenced by the custom tool schema.
Important:When you deploy a SuiteApp or account customization project (ACP) that includes a custom tool, you can't edit the custom tool script file, JSON schema file, or HTML resource file in the File Cabinet. You can edit these files only through SuiteCloud Development Framework (SDF).
For information about how to build the UI for the MCP App, see the Model Context Protocol documentation for Building an MCP App.
Take note of the following:
-
Resources in SuiteScript folder can be exposed - Files included in your project (including files in the SuiteScripts folder) can be referenced as UI resources and provided to an AI client when referenced by a tool's UI metadata.
-
Hidden files are not a security boundary - MCP App HTML files are not hidden from AI clients solely because they are marked as hidden when requested through the NetSuite AI Connector. Do not rely on hidden as an access-control mechanism; instead, rely on toolset permissions and careful schema/tool exposure.
Creating MCP Apps
This procedure focuses on creating an MCP App, which is a custom tool that provides an interactive UI. Your UI can call other tools for business logic (if needed), or the MCP App tool can implement the logic itself.
For the end-to-end procedure to create and deploy a custom tool, see Creating Custom Tools for the NetSuite AI Connector Service.
To create an MCP App custom tool:
-
In a new or existing SuiteCloud project (SuiteApp or ACP), choose whether to create a new tool that provides a UI or add a UI to an existing tool. Ensure the tool meets the custom tool requirements. For more information, see Custom Tool Script Requirements.
-
In the custom tool JSON schema, add
_meta.ui.resourceUrito the tool definition for the MCP App tool. -
Build and bundle the UI into a self-contained HTML file. Your UI should include all required CSS and JavaScript assets directly in the HTML file and should not fetch external resources at runtime.
To build the UI, you can use the @modelcontextprotocol/ext-apps SDK, which provides tools and an App class to facilitate communication between your UI and the AI client. For more guidance and usage examples, see the Model Context Protocol documentation for Building an MCP App.
-
Add the bundled HTML file to your SuiteCloud project at the location referenced by the
resourceUrivalue.-
For ACPs, place the file in the SuiteScripts folder at the referenced path.
-
For SuiteApps, place the file in the SuiteApps folder at the referenced path.
-
-
Deploy the SuiteCloud project to your NetSuite account.
-
From your AI Client, connect to the NetSuite AI Connector. For more information, see Connect to the NetSuite AI Connector Service.
-
Test the MCP App custom tool in the AI client. If your UI calls other tools for logic, verify the logic tools are deployed and that you can access them from the AI client.
MCP App Best Practices
-
Separate logic from UI - Implement core actions as standard tools that can run without a UI. Then implement a separate MCP App tool that provides the UI experience. This allows AI clients to:
-
Run the action directly when no user interaction is required, and
-
Load the UI only when user input/confirmation is needed.
-
-
Use clear tool and method naming - Follow the existing custom tool best practices for unique, descriptive names and safe, auditable operations. For more information, see Custom Tool Script Best Practices.
-
Treat UI resources as exposed content - Because MCP App UI files can be delivered to the MCP client, do not embed secrets or sensitive internal data in the HTML.
MCP App Code Samples
The following example shows how to implement a simple logic tool and an MCP App tool scenario in NetSuite, where the logic tool performs a customer search and the MCP App tool presents a UI that lets the user select a customer status before running the search. This example demonstrates how separate tool methods and schema entries are used: one for the UI (MCP App tool) and one for the business logic (logic tool). The UI communicates with both tools using the APIs provided by the @modelcontextprotocol/ext-apps package.
Scenario:
-
The MCP App tool provides a list of available customer statuses to the interactive UI.
-
When the user selects a status in the UI, the UI calls the logic tool to fetch customer names that match the selected status using a saved search.
-
Results are displayed in the UI.
This example assumes all workflow and user interactions-such as selecting a status, triggering a customer search, and displaying results-are implemented in the HTML UI file referenced by meta.ui.resourceUri using the APIs and components in the @modelcontextprotocol/ext-apps module. For information about building and bundling the UI HTML file, see the Model Context Protocol documentation for Building an MCP App.
This code sample includes only the NetSuite custom tool script, custom tool JSON schema, and toolset SDF XML object. It does not include the bundled HTML UI file referenced by _meta.ui.resourceUri. The sample may require changes to the script, schema, and toolset XML before it can be used in your own project. For a complete working example that includes the UI, see the MCP App Sample in the SuiteCloud Project Repository on Oracle Samples GitHub.
MCP App Custom Tool Script File
This sample script defines two tool methods:
-
listCustomersByStatus - The logic tool. Uses the NetSuite search module to return customer names for a selected status.
-
customerStatusPickerApp - The MCP App tool. Returns available statuses for the UI to display.
/**
* customersearchtools.js
* @NApiVersion 2.1
* @NModuleScope Public
* @NScriptType CustomTool
*/
define(['N/search'], function (search) {
return {
// Logic tool for listing customers by status
listCustomersByStatus: async function (args) {
try {
var status = args.status || "CUSTOMER_ACTIVE";
var results = [];
var customerSearch = search.create({
type: search.Type.CUSTOMER,
filters: [['entitystatus', 'is', status]],
columns: ['entityid']
});
customerSearch.run().each(function (result) {
results.push(result.getValue('entityid'));
return true;
});
return {
customers: results, error: ""
};
} catch (e) {
return { customers: [], error: e.message || String(e) };
}
},
// MCP app tool that provides status options for the UI
customerStatusPickerApp: async function () {
return {
availableStatuses: JSON.stringify([
{ id: "CUSTOMER_ACTIVE", label: "Active" },
{ id: "CUSTOMER_INACTIVE", label: "Inactive" }
]),
error: ""
};
}
};
});
MCP App Custom Tool JSON Schema
This schema defines both tools, indicating expected input and output for each. The MCP app tool includes the _meta.ui.resourceUri property, identifying which tool presents the interactive UI.
{
"tools": [
{
"name": "listCustomersByStatus",
"description": "List customers by status using a saved search.",
"inputSchema": {
"type": "object",
"properties": {
"status": {"type": "string", "description": "Customer status internal ID"}
},
"required": ["status"]
},
"outputSchema": {
"type": "object",
"properties": {
"customers": {"type": "array", "items": { "type": "string" }, "description": "Customer names"},
"error": {"type": "string", "description": "Error message if execution fails"}
},
"required": ["customers"]
},
"annotations": {
"title": "List Customers by Status",
"readOnlyHint": true
}
},
{
"name": "customerStatusPickerApp",
"description": "MCP app tool that provides a status picker UI for customer search.",
"inputSchema": { "type": "object", "properties": {}, "required": [] },
"outputSchema": {
"type": "object",
"properties": {
"availableStatuses": {"type": "string", "description": "Available status options (as JSON array)"},
"error": {"type": "string", "description": "Error message if execution fails"}
},
"required": ["availableStatuses"]
},
"annotations": {
"title": "Customer Status Picker (UI)"
},
"_meta": {
"ui": {
"resourceUri": "ui://SuiteApps/com.example.customersearchtools/status-picker/mcp-app.html"
}
}
}
]
}
MCP App Toolset SDF Object XML
This XML file defines the script, schema, and permissions.
<toolset scriptid="custtoolset_customersearch">
<name>Customer Search Tools</name>
<scriptfile>[/SuiteApps/com.example.customersearchtools/customersearchtools.js]</scriptfile>
<rpcschema>[/SuiteApps/com.example.customersearchtools/customersearchtools_schema.json]</rpcschema>
<exposetoaiconnector>T</exposetoaiconnector>
<permissions>
<permission>
<permkey>LIST_CUSTJOB</permkey>
<permlevel>VIEW</permlevel>
</permission>
</permissions>
</toolset>