Skip to main content

Chapter 9: Extend For io.Connect Desktop

ACME Banking now has an io.Assist experience that can discover clients, understand Workspace Context, find apps and workspaces, and show MCP App previews in io.Connect Browser.

In this chapter, you will extend the same flow to io.Connect Desktop.

The important part is how little has to change. The application definitions, workspace metadata, MCP Web configuration, getClients interop method, and io.Assist configuration all keep the same shape. For the Desktop-specific option in this chapter, you will add one small service app to host MCP Web and register the guide interop method.

That portability is one of the strengths of the io.Connect platform: you can build the workflow once, then adapt the hosting model to the environment.

You don't have to move MCP Web into a separate app to support io.Connect Desktop. The Chapter 8 pattern, where MCP Web starts directly inside the io.Assist app, can also work in Desktop. In this chapter, you will use a hidden service app because it is a useful Desktop-specific option and shows how Desktop can host shared background capabilities independently from any visible UI.

The Desktop service app uses the same MCP Web and MCP Core configuration shape as the Browser chapters:

What Changes In Desktop?

In io.Connect Browser, the browser platform and the assistant app handled the environment:

io.Connect Browser platform
starts guide apps
registers getClients

io.Assist
starts MCP Web
connects AI Web to MCP Web

In io.Connect Desktop, there is no browser platform app doing that work for the guide. One option is to keep MCP Web inside io.Assist, just like in Chapter 8. For this chapter, you will use another option: add a hidden service app.

io.Connect Desktop
starts hidden io-cd-mcp-host service app

io-cd-mcp-host
connects to io.Connect Desktop
registers getClients
starts MCP Web

io.Assist
connects to the MCP Web server hosted by the service app

The assistant can then run as a standalone app or inside a workspace and still use the same capabilities.

Choose A Desktop Hosting Model

There are two valid ways to host MCP Web in io.Connect Desktop.

The first option is to keep MCP Web integrated into the io.Assist app. This is the smallest change from Chapter 8. When the assistant starts, it starts MCP Web and AI Web connects to it from the same app. This works well when the assistant is the only owner of the MCP Web configuration and you don't need the MCP server before the assistant app opens.

The second option is to move MCP Web into a hidden Desktop service app. This is the option used in this chapter. It is helpful when you want Desktop to start the MCP server as part of the environment, keep shared interop methods outside the visible assistant UI, or make MCP Web available consistently whether io.Assist is opened as a standalone app or inside a workspace.

Both models use the same io.Connect APIs and the same MCP Web configuration style. The hidden service app is not a workaround; it is a Desktop deployment pattern.

Extend The Desktop MCP Host Shell

The start project already includes a shell app:

apps/io-cd-mcp-host

This app doesn't have a UI. It already has the Vite, TypeScript, environment-file, and port setup needed for Chapter 9. It also connects to io.Connect Desktop and loads the Workspaces API.

The shell starts on port 4203 and contains this initial startup code:

apps/io-cd-mcp-host/src/main.ts
import IODesktop from "@interopio/desktop";
import IOWorkspaces from "@interopio/workspaces-api";

const start = async () => {
const io = await IODesktop({
libraries: [IOWorkspaces]
});

window.io = io;

console.log("io.Connect Desktop MCP host shell started");
};

start().catch((error) => {
console.error("Failed to start io.Connect Desktop MCP host shell", error);
});

The libraries: [IOWorkspaces] part is important. Without it, the MCP Web server can still expose simple interop-backed tools, but it won't have the Workspaces API needed for the built-in workspace searching and starting tools.

In this chapter, you will add the guide-specific io.Intelligence code: the client data, the getClients interop registration, and MCP Web startup.

Create the client data and method name:

apps/io-cd-mcp-host/src/clients.ts
export const GET_CLIENTS_METHOD = "getClients";

export interface Client {
id: string;
portfolioId: string;
firstName: string;
lastName: string;
segment: string;
advisor: string;
riskProfile: string;
}

export const clients: Client[] = [
{
id: "CL-10024",
portfolioId: "PF-8801",
firstName: "Amelia",
lastName: "Reed",
segment: "Private Banking",
advisor: "M. Carter",
riskProfile: "Balanced"
}
];

Keep the full client list from the solution code. The shortened snippet above only shows the shape.

Now add the MCP Web configuration:

apps/io-cd-mcp-host/src/mcp.ts
import type { IOConnectDesktop } from "@interopio/desktop";
import { ServerFactory, type IoIntelMCPWeb } from "@interopio/mcp-web";
import { GET_CLIENTS_METHOD } from "./clients";

const GET_CLIENTS_TOOL = "get_clients";

const getMCPWebServerConfig = (): IoIntelMCPWeb.Server.Config => ({
licenseKey: import.meta.env.VITE_IO_INTELLIGENCE_LICENSE_KEY,
mcpCoreServer: {
tools: {
static: {
methods: [
{
availability: "constant",
name: GET_CLIENTS_TOOL,
config: {
title: "Get Clients",
description: "Returns the list of ACME Banking clients available in io.Connect Desktop.",
inputSchema: {
type: "object",
properties: {},
additionalProperties: false
}
},
interop: {
methodName: GET_CLIENTS_METHOD
}
}
]
}
}
}
});

export const startMCPWebServer = async (io: IOConnectDesktop.API): Promise<void> => {
await ServerFactory(io, getMCPWebServerConfig());

console.log("MCP Web server started in io.Connect Desktop host");
};

This is the same static MCP tool pattern you used earlier. The tool is still named get_clients, and it still delegates to the getClients interop method. The only difference is that the server now receives an io.Connect Desktop API object.

Finally, update the hidden host startup:

apps/io-cd-mcp-host/src/main.ts
import IODesktop from "@interopio/desktop";
import IOWorkspaces from "@interopio/workspaces-api";
import { clients, GET_CLIENTS_METHOD } from "./clients";
import { startMCPWebServer } from "./mcp";

const start = async () => {
const io = await IODesktop({
libraries: [IOWorkspaces]
});

await io.interop.register(GET_CLIENTS_METHOD, () => ({
clients
}));

await startMCPWebServer(io);

window.io = io;

console.log("io.Connect Desktop MCP host started");
};

start().catch((error) => {
console.error("Failed to start io.Connect Desktop MCP host", error);
});

When you run the frontend package, this host app starts with the rest of the guide apps:

npm start

The frontend package now serves:

AppURL
io.Connect Browser platformhttp://localhost:4200
Client Listhttp://localhost:4201
Client Portfoliohttp://localhost:4202
io.Connect Desktop MCP hosthttp://localhost:4203
Angular io.Assisthttp://localhost:4003
React io.Assisthttp://localhost:4004

Update The Assistant MCP Startup

In Chapter 8, the assistant started MCP Web itself. That is still correct for io.Connect Browser, but in io.Connect Desktop the hidden service app owns MCP Web.

Update both assistant implementations so they skip local MCP Web startup when they are running inside io.Connect Desktop:

apps/io-assist-angular/src/app/mcp.ts
const isIOConnectDesktop = (): boolean => Boolean((window as any).glue42gd || (window as any).iodesktop);

export const startMCPWebServer = async (io: IOConnectAPI): Promise<void> => {
if (isIOConnectDesktop()) {
console.info("MCP Web server is hosted by the io.Connect Desktop service app.");
return;
}

const hasMCPWebServer = io.interop.methods().some((method) => method.name === MCP_SERVER_METHOD_NAME);

if (hasMCPWebServer) {
console.info("MCP Web server is already available.");
return;
}

const { ServerFactory } = await import("@interopio/mcp-web");

await ServerFactory(io, getMCPWebServerConfig());
};

Use the same check in the React assistant:

apps/io-assist-react/src/mcp.ts
const isIOConnectDesktop = (): boolean => Boolean((window as any).glue42gd || (window as any).iodesktop)

export const startMCPWebServer = async (io: IOConnectAPI): Promise<void> => {
if (isIOConnectDesktop()) {
console.info('MCP Web server is hosted by the io.Connect Desktop service app.')
return
}

const hasMCPWebServer = io.interop.methods().some((method) => method.name === MCP_SERVER_METHOD_NAME)

if (hasMCPWebServer) {
console.info('MCP Web server is already available.')
return
}

const { ServerFactory } = await import('@interopio/mcp-web')

await ServerFactory(io, getMCPWebServerConfig())
}

This keeps one assistant codebase working in both environments:

  • in io.Connect Browser, io.Assist starts MCP Web
  • in io.Connect Desktop, the hidden service app starts MCP Web

Prepare Desktop Definitions

The app definitions you created in Chapter 6 are compatible with io.Connect Desktop, so you can extract them into a separate file and use them for Desktop as well. For more background on how io.Connect Desktop loads and manages applications, see the io.Connect Desktop App Management documentation.

Create a Desktop app definitions file:

io-connect-desktop/app-definitions.json

Include the same app definitions for:

  • client-list
  • client-portfolio
  • io-assist-angular
  • io-assist-react

Then add the hidden MCP host service app:

io-connect-desktop/app-definitions.json
{
"name": "io-cd-mcp-host",
"type": "window",
"service": true,
"hidden": true,
"details": {
"url": "http://localhost:4203"
},
"caption": "Hidden ACME Banking service app that hosts MCP Web and registers the getClients interop method for io.Connect Desktop.",
"allowMultiple": false,
"ignoreFromLayouts": true,
"customProperties": {
"includeInWorkspaces": false
}
}

Copy these application definitions to the correct io.Connect Desktop application definitions location for your environment.

You also need the client-management workspace layout in io.Connect Desktop. For more details on Desktop layouts and where workspace layout definitions fit, see the io.Connect Desktop Layouts documentation.

Create:

io-connect-desktop/workspace-layouts.json

This file should contain the workspace layout object directly:

io-connect-desktop/workspace-layouts.json
{
"name": "client-management",
"type": "Workspace",
"metadata": {
"description": "A workspace layout for ACME Banking client management. It includes a client list for selecting the active client and a portfolio view that displays portfolio details from the selected workspace client context."
},
"components": []
}

Keep the full layout structure from the solution code. The important detail is that the Desktop layout file contains the workspace object itself, not an array of workspace objects.

Copy the workspace layout to the correct io.Connect Desktop workspace layout location for your environment.

Enable The In-Memory Store

For this guide flow, io.Connect Desktop must have the in-memory store enabled, and the io.Assist app must be allowed to use it. See the io.Connect Desktop in-memory app store documentation for the relevant configuration.

This matters because the assistant flow uses system tools that discover and start applications and workspaces from the available io.Connect definitions. MCP Apps also rely on the platform being able to register and use temporary app definitions when rendering interactive previews.

The exact io.Connect Desktop configuration location depends on your environment, so use the setup that matches your Desktop distribution. The required behavior is:

  • io.Connect Desktop can load the ACME Banking app definitions
  • io.Connect Desktop can load the client-management workspace layout
  • the hidden io-cd-mcp-host app starts as a service app
  • the io.Assist app is allowed to use the in-memory store
  • the MCP host app has the Workspaces API loaded

Test The Desktop Flow

Start the agent backend:

cd agentic-backend
npm start

In another terminal, start the guide frontend apps:

cd io-assist-anywhere-start
npm start

Then start io.Connect Desktop with the ACME Banking app definitions and workspace layout available.

Open either io.Assist implementation in io.Connect Desktop. You can start it as a standalone app or place it inside a workspace. Both modes should work, but workspace-context questions require io.Assist to be inside the workspace whose context you want it to read.

Repeat the tests from the previous chapters:

Hi! What can you do for me

The assistant should understand that it is running inside io.Connect.

Open the client-management workspace, select a client, and ask:

who is my client

The assistant should use Working Context to answer based on the selected client.

If io.Assist is running as a standalone window, this specific question won't work because the Working Context configuration reads from the workspace that contains io.Assist. A standalone window isn't inside a workspace, so there is no current workspace context to read. The assistant should still be able to discover clients, find workspace definitions, show previews, and start the right workspace.

Ask:

show me my clients

The assistant should use the get_clients MCP tool and return the ACME Banking client list.

Finally, ask:

show me the portfolio details for Amelia Reed. But before opening any workspace, I would like to get a preview

The assistant should discover Amelia Reed, find the client-management workspace, and show a Workspace Widget preview. You should be able to restore the workspace from that preview.

What You Have Built

You have now made the ACME Banking assistant work in both io.Connect Browser and io.Connect Desktop.

The core assistant flow stayed the same:

  • get_clients still exposes ACME Banking clients
  • Working Context still provides the selected client
  • system tools still discover apps and workspaces
  • MCP Apps still show interactive workspace previews
  • io.Assist still talks to the same agent backend

Only the hosting model changed. Browser can host MCP Web from the assistant app, while Desktop uses a hidden service app to host MCP Web and register guide-specific interop methods.

That gives ACME Banking one assistant experience that adapts to both web and desktop deployments with minimal changes.