The viewer toolbar can be extended with your own button: the JSP renders the report and places the viewer tag, and a small client-side script adds the button once the viewer is ready.

Render the report and place the viewer. In index.jsp, wrapped in a container with a known id:
String reportPath = request.getSession().getServletContext().getRealPath("/reports/TwoSimpleLists.mrt");
StiReport report = StiSerializeManager.deserializeReport(new File(reportPath));
report.render();
StiWebViewerOptions options = new StiWebViewerOptions();
pageContext.setAttribute("report", report);
pageContext.setAttribute("options", options);
// <div id="viewerDiv"><stiwebviewer:webviewer report="${report}" options="${options}" /></div>
Add the button to the toolbar. On page load, get the viewer's JavaScript object and insert the button in its onready handler:
function loaded() {
    var viewer = window["js" + document.getElementById("viewerDiv").childNodes[1]["id"]];
    viewer.onready = function () {
        var customButton = viewer.SmallButton("customButton", "Custom Button", "emptyImage");
        customButton.image.src = "icon.png";
        customButton.action = function () {
            alert("Custom Button Event");
        };

        var toolbarTable = viewer.controls.toolbar.firstChild.firstChild;
        var buttonsTable = toolbarTable.rows[0].firstChild.firstChild;
        var customButtonCell = buttonsTable.rows[0].insertCell(0);
        customButtonCell.appendChild(customButton);
    };
}
// <body onload="loaded()">

How it works. The report is rendered on the server and served through the standard StiWebResourceServlet and StiWebViewerActionServlet registered in web.xml. The custom button lives entirely on the client: it is built with the viewer's own button factory, so it matches the toolbar style, and its action can run any JavaScript — call your own endpoint, open a dialog, or trigger application logic.