Skip to main content

Chapter 10: Dedicated io.Connect Desktop MCP HTTP Server

In Chapter 9, ACME Banking hosted MCP Web in a hidden io.Connect Desktop service app. That works well when the MCP server can run in a browser window and use browser-compatible code.

In this chapter, you will use another Desktop-specific architecture: a dedicated MCP HTTP server started by io.Connect Desktop as a Node service app.

This pattern is useful when your organization prefers a more traditional server process, or when your MCP capabilities need access to resources that don't belong in a web app. For example, a dedicated server can use server-side libraries, file system access, database drivers, private network calls, or other Node.js capabilities that are not suitable for MCP Web.

The assistant experience will stay the same. io.Assist will still discover clients, use Working Context, find apps and workspaces, and show MCP App previews. The difference is the MCP transport and hosting model:

io.Connect Desktop
starts dedicated Node service app

MCP HTTP server
connects to io.Connect Desktop
registers getClients
exposes MCP Core capabilities over HTTP

io.Assist
connects to http://localhost:8989/mcp

This chapter moves the MCP transport from MCP Web to MCP HTTP:

What Changes In This Chapter?

The Chapter 9 setup used a hidden web service app:

io-cd-mcp-host
starts MCP Web
registers getClients

In this chapter, that app remains in the project so you can see how the guide evolved, but it no longer starts MCP Web. The MCP setup moves into a new Node.js project:

mcp-http-server
builds to dist/index.cjs
starts MCP HTTP
registers getClients

AI Web in the Angular and React assistants will choose the right MCP connection based on the environment:

  • in io.Connect Browser, io.Assist starts and uses MCP Web
  • in io.Connect Desktop, io.Assist connects to the dedicated MCP HTTP server

This keeps the guide flexible. Browser deployments can keep the assistant-hosted MCP Web pattern from Chapter 8, while Desktop deployments can use a standalone MCP HTTP server.

Review The MCP HTTP Shell

The start files include a shell project:

mcp-http-server

The shell has the build setup needed for io.Connect Desktop:

mcp-http-server/package.json
{
"name": "io-assist-anywhere-mcp-http-server",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "npm run create-local-env && rollup --config rollup.config-prebuild.js && rollup --config rollup.config-cjs.js",
"create-local-env": "node ./scripts/create-local-env.js"
}
}

The build script does two things:

  • reads VITE_IO_INTELLIGENCE_LICENSE_KEY from the project root .env file
  • bundles src/index.ts into dist/index.cjs

The .cjs output is important because io.Connect Desktop can start it as a Node service app.

Add The MCP HTTP Dependency

Install @interopio/mcp-http in the frontend project root:

cd io-assist-anywhere-start
npm install @interopio/mcp-http

This installs the latest version available on npm. The project already uses root-level dependencies for shared io.Connect packages because several apps are browser or desktop clients. The MCP HTTP server will use the same pattern.

Disable The Desktop MCP Web Host

Open:

apps/io-cd-mcp-host/src/main.ts

Comment out the MCP Web imports and startup code, but keep the comments in place so the progression remains visible:

apps/io-cd-mcp-host/src/main.ts
import IODesktop from "@interopio/desktop";
import IOWorkspaces from "@interopio/workspaces-api";
// Chapter 10 moves this logic to the dedicated MCP HTTP server.
// Keep the imports and startup calls here as comments so you can see how the guide progressed.
//
// 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 Web host skipped. Chapter 10 uses the dedicated MCP HTTP server.");
};

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

You are not deleting the old MCP Web configuration files. They are still useful reference material for understanding how the guide moved from MCP Web to MCP HTTP.

Implement The MCP HTTP Server

Open:

mcp-http-server/src/index.ts

Replace the shell code with a Desktop MCP HTTP server.

Start with the imports and shared constants:

mcp-http-server/src/index.ts
import IODesktop from "@interopio/desktop";
import IoIntelMCPHttpFactory from "@interopio/mcp-http";
import IOWorkspaces from "@interopio/workspaces-api";

const HTTP_PORT = 8989;
const GET_CLIENTS_METHOD = "getClients";

IODesktop connects the server process to io.Connect Desktop. IoIntelMCPHttpFactory starts the MCP HTTP server. IOWorkspaces gives MCP Core access to the Workspaces API, which is required for workspace discovery, workspace start, and workspace preview capabilities.

Move the ACME Banking client store into this server:

mcp-http-server/src/index.ts
interface Client {
id: string;
portfolioId: string;
firstName: string;
lastName: string;
segment: string;
advisor: string;
riskProfile: string;
}

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 data shape.

Now move the static MCP tool definition into the server:

mcp-http-server/src/index.ts
const getClientsTool = {
availability: "constant" as const,
name: "get_clients",
config: {
title: "Get Clients",
description: "Returns the list of ACME Banking clients available in io.Connect Desktop.",
inputSchema: {
type: "object",
properties: {},
additionalProperties: false
},
outputSchema: {
type: "object",
properties: {
clients: {
type: "array",
items: {
type: "object",
properties: {
id: { type: "string" },
portfolioId: { type: "string" },
firstName: { type: "string" },
lastName: { type: "string" },
segment: { type: "string" },
advisor: { type: "string" },
riskProfile: { type: "string" }
},
required: ["id", "portfolioId", "firstName", "lastName", "segment", "advisor", "riskProfile"]
}
}
},
required: ["clients"]
}
},
interop: {
methodName: GET_CLIENTS_METHOD
}
};

This is the same guide capability you exposed through MCP Web earlier. The get_clients MCP tool delegates to the getClients interop method, and that method returns:

{
clients
}

Finally, connect to io.Connect Desktop, register the interop method, and start MCP HTTP:

mcp-http-server/src/index.ts
const start = async (): Promise<void> => {
const licenseKey = process.env.VITE_IO_INTELLIGENCE_LICENSE_KEY;

if (!licenseKey) {
throw new Error("Missing VITE_IO_INTELLIGENCE_LICENSE_KEY. Add it to the root .env file before building.");
}

const io = await IODesktop({
logger: "info",
libraries: [IOWorkspaces]
});

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

await IoIntelMCPHttpFactory(io, {
licenseKey,
server: {
port: HTTP_PORT
},
mcpCoreServer: {
tools: {
static: {
methods: [getClientsTool]
}
}
}
});

console.log(`ACME Banking MCP HTTP server started on http://localhost:${HTTP_PORT}/mcp`);
};

start().catch((error) => {
console.error("Failed to start ACME Banking MCP HTTP server", error);
});

You don't need to explicitly enable the built-in system tools here. MCP Core enables them by default, and the Workspaces API loaded through IOWorkspaces gives them access to the Desktop workspace resources they need.

Point io.Assist To MCP HTTP In Desktop

In Chapter 8, the assistants started MCP Web directly. Keep that behavior for io.Connect Browser, but use the MCP HTTP server in io.Connect Desktop.

In the Angular assistant, add the Desktop check and MCP config:

apps/io-assist-angular/src/app/app.config.ts
const MCP_HTTP_SERVER_URL = "http://localhost:8989/mcp";
const IS_IO_CONNECT_DESKTOP = Boolean((window as any).glue42gd || (window as any).iodesktop);

const IO_INTEL_MCP_CONFIG = IS_IO_CONNECT_DESKTOP
? {
remote: {
streamableHttp: {
url: MCP_HTTP_SERVER_URL,
name: "ACME Banking MCP HTTP",
},
},
web: {
enabled: false,
},
}
: {
web: {
enabled: true,
},
};

Then pass it to AI Web:

apps/io-assist-angular/src/app/app.config.ts
aiWebConfig: {
agentServer: {
baseUrl: AGENT_SERVER_URL,
},
mcp: {
ioIntel: IO_INTEL_MCP_CONFIG,
},
},

Also make sure the assistant starts MCP Web only outside Desktop:

apps/io-assist-angular/src/app/app.config.ts
const createIOConnect: typeof IOBrowser = async (config) => {
const io = await IOBrowser(config);

if (!IS_IO_CONNECT_DESKTOP) {
await startMCPWebServer(io);
}

return io;
};

Apply the same idea in React:

apps/io-assist-react/src/App.tsx
const MCP_HTTP_SERVER_URL = 'http://localhost:8989/mcp'
const isIOConnectDesktop = Boolean((window as any).glue42gd || (window as any).iodesktop)

const ioIntelMCPConfig = isIOConnectDesktop
? {
remote: {
streamableHttp: {
url: MCP_HTTP_SERVER_URL,
name: 'ACME Banking MCP HTTP',
},
},
web: {
enabled: false,
},
}
: {
web: {
enabled: true,
},
}

Then pass it to AI Web from the React staticConfig:

apps/io-assist-react/src/App.tsx
const staticConfig: IoAssistStaticConfig = {
aiWebConfig: {
agentServer: {
baseUrl: AGENT_SERVER_URL,
},
mcp: {
clientsConfig: {
enforceStrictCapabilities: false,
capabilities: {
extensions: {
'io.modelcontextprotocol/ui': {
mimeTypes: ['text/html;profile=mcp-app'],
},
},
},
},
mcpApps: {
sandboxProxyUrl: MCP_SANDBOX_PROXY_URL,
displayMode: 'workspace',
},
ioIntel: ioIntelMCPConfig,
},
},
}

And skip embedded MCP Web startup when the React assistant runs in Desktop:

apps/io-assist-react/src/App.tsx
const createIOConnect: typeof IOBrowser = async (config) => {
const io = await IOBrowser(config)

if (!isIOConnectDesktop) {
await startMCPWebServer(io)
}

return io
}

With this setup, the assistant can communicate with the dedicated server through the MCP HTTP endpoint, while still keeping the Browser path from the earlier chapters.

Add The Desktop Node Service Definition

Update the io.Connect Desktop app definitions with the dedicated MCP server app. If you need a refresher on Desktop application definitions, see the io.Connect Desktop App Management documentation. For the Node.js-specific app definition fields, see the io.Connect Desktop Node.js app definition documentation.

Use the following service definition:

io-connect-desktop/app-definitions.json
{
"name": "io-intelligence-mcp-server",
"type": "node",
"service": true,
"details": {
"showConsole": true,
"passGlueToken": true,
"logging": true,
"path": "%IO_CD_USER_DATA_DIR%/remoteApps/io-intelligence-mcp-server/index.cjs"
},
"allowLogging": true
}

This definition tells io.Connect Desktop to start the MCP HTTP server as a Node service app.

The important details are:

  • type: "node" starts a Node.js process instead of a visible window
  • service: true marks it as a background service app
  • passGlueToken: true lets the process connect back to the current io.Connect Desktop environment
  • path must point to the built .cjs bundle that io.Connect Desktop can start

The exact folder under your Desktop user data directory can vary between environments. Use the location that matches your io.Connect Desktop setup, and update the path in the definition if you choose a different folder.

Build And Copy The Server

Build the MCP HTTP server:

cd io-assist-anywhere-start/mcp-http-server
npm run build

The build creates:

mcp-http-server/dist/index.cjs

Copy that .cjs file to the location expected by your io.Connect Desktop app definition. With the definition above, the final file must be available at:

%IO_CD_USER_DATA_DIR%/remoteApps/io-intelligence-mcp-server/index.cjs

If you use a different location, update the details.path field in the app definition so io.Connect Desktop can identify and start the server.

Test The Desktop Flow

Start the agent backend:

cd agentic-backend
npm start

Build and place the MCP HTTP server bundle as described above, then start io.Connect Desktop with the ACME Banking app definitions and workspace layout available.

Open either io.Assist implementation in io.Connect Desktop. The assistant can run as a standalone app or inside a workspace.

Repeat the preview test from Chapter 7:

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

The result should be the same from the user's point of view:

  • the assistant discovers the ACME Banking clients through get_clients
  • the assistant discovers the best workspace for the request
  • the assistant shows a Workspace Widget preview
  • you can restore the workspace from the preview

This time, the MCP capabilities come from the dedicated MCP HTTP server instead of MCP Web.

What You Have Built

You now have three valid MCP hosting patterns in the ACME Banking guide:

  • io.Connect Browser can use assistant-hosted MCP Web
  • io.Connect Desktop can use a hidden web service app that hosts MCP Web
  • io.Connect Desktop can use a dedicated Node service app that hosts MCP HTTP

The dedicated MCP HTTP server gives Desktop deployments a more traditional architecture. It keeps MCP capabilities outside the visible assistant app, lets io.Connect Desktop manage the server lifecycle, and opens the door for server-side capabilities that don't belong in the browser.

At the same time, the assistant workflow remains stable. io.Assist still talks to the agent backend, AI Web still connects to MCP, and the ACME Banking user still gets the same integrated assistant experience.