Best Practices for Extending Chat-MCP with New MCP Capabilities
To extend Chat-MCP with new MCP capabilities, define the data contract in src/main/types.ts, register the capability schema in src/main/main.ts, and update the configuration to expose the feature through the automatic preload bridge.
Chat-MCP is an Electron-based client for the Model Context Protocol (MCP) that coordinates between multiple MCP servers through a strict three-layer architecture. Understanding how to properly extend this application with new MCP capabilities requires following a schema-first design that maintains type safety across the main and renderer processes. This guide walks through the exact implementation patterns found in the ai-ql/chat-mcp repository.
Understanding the Three-Layer Architecture
Chat-MCP’s extension model relies on three coordinated layers that handle configuration, inter-process communication (IPC), and renderer exposure.
Configuration and Client Initialization
The bootstrap process begins in src/main/main.ts, where the application reads config.json to identify available MCP servers. For each entry, the code instantiates a Client and extracts advertised capabilities via client.getServerCapabilities() (lines 44-78). This discovery phase determines which features the application will expose to the user interface.
IPC Registration Layer
For every capability reported by the server—such as tools, prompts, or resources—the main process constructs IPC channels following the pattern <server>-<type>/<action>. These channels wire to manageRequests using validation schemas that strictly define request and response shapes (lines 57-71 and 73-80). This layer ensures that all communication adheres to type-safe contracts before reaching the MCP server.
Renderer Bridge
The preload script at src/preload/preload.ts discovers initialized servers and builds a JavaScript object where keys represent server names and leaf objects contain async methods forwarding calls to the IPC channels (lines 30-60). This object exposes to the renderer as window.mcpServers through Electron's contextBridge (lines 62-65), creating a clean API surface that remains sandboxed from the main process.
Step-by-Step Guide to Extending MCP Capabilities
Adding a new capability requires modifications across the type system, IPC registration, and configuration files.
Step 1: Define the Data Contract in types.ts
Add a JSON schema or Zod schema to src/main/types.ts describing the request and response payloads for your new capability. The manageRequests function uses this for runtime validation, and the client SDK uses it to generate correct TypeScript types.
// src/main/types.ts
import { z } from "zod";
export const ReportAnalyticsResultSchema = z.object({
success: z.boolean(),
message: z.string().optional(),
});
export const StatsAnalyticsResultSchema = z.object({
totalEvents: z.number(),
activeUsers: z.number(),
});
Step 2: Extend the IPC Map in main.ts
In src/main/main.ts, locate the capabilitySchemas object. Add a new top-level key for your capability (e.g., analytics) and map its actions to the schemas created in step one (lines 57-71). The registerIpcHandlers loop automatically creates IPC handlers for each action without additional wiring.
// src/main/main.ts
const capabilitySchemas = {
// existing capabilities...
analytics: {
report: ReportAnalyticsResultSchema,
stats: StatsAnalyticsResultSchema,
},
};
Step 3: Update the Server Configuration
Insert a new entry under mcpServers in src/main/config.json (or the user-provided config file) that includes the new capability name. The bootstrap code reads this file and includes the new capability when building the features list.
{
"mcpServers": {
"myMcp": {
"command": "node ./my-mcp-server.js",
"analytics": {}
}
}
}
Step 4: Access from the Renderer Process
No code changes are required in the preload script. The preload.ts file automatically discovers the new capability name and creates method stubs via api[name][capability] = createAPIMethods(...). The renderer can immediately call window.mcpServers.<server>.<capability>.<action>() without additional boilerplate.
// Renderer process
await window.mcpServers.myMcp.analytics.report({ event: "login", userId: "123" });
const stats = await window.mcpServers.myMcp.analytics.stats();
console.log(stats);
Step 5: Add Custom Client Methods (Optional)
If the underlying MCP server requires a custom client method, extend src/main/client.ts to expose a typed wrapper that calls the appropriate schema-validated method.
// src/main/client.ts
export async function sendAnalyticsReport(client: Client, payload: any) {
return await client.invoke("analytics/report", payload);
}
Key Architectural Considerations
When extending Chat-MCP with new capabilities, adhere to these architectural constraints to maintain system integrity:
-
Naming Convention: All IPC channels follow
<server>-<type>/<action>. Maintain this pattern so that theregisterIpcHandlersloop can process them automatically without manual registration code. -
Schema-First Design: All validation lives in the schema objects defined in
src/main/types.ts. Adding a capability without a corresponding schema causes runtime errors whenmanageRequestsattempts to validate incoming requests. -
Lazy Exposure: The preload script builds the API only after
listClients()resolves. This ensures that any new server reporting additional capabilities reflects instantly in the renderer without requiring an application reload. -
Separation of Concerns: The renderer never communicates directly with MCP processes. All traffic routes through IPC handlers, keeping the UI sandboxed and preventing unauthorized access to system resources.
Summary
- Define data contracts in
src/main/types.tsusing Zod or JSON schemas to ensure type safety. - Register capabilities in
src/main/main.tsby extending thecapabilitySchemasobject to enable automatic IPC handler creation. - Update
src/main/config.jsonto advertise new server capabilities under themcpServerskey. - The preload bridge exposes new capabilities automatically via
window.mcpServerswithout modifyingsrc/preload/preload.ts. - Follow the
<server>-<type>/<action>naming convention for IPC channels to maintain compatibility with the registration loop.
Frequently Asked Questions
Where should I define validation schemas for new MCP capabilities?
Define all validation schemas in src/main/types.ts. The manageRequests function relies on these schemas for runtime validation, and they provide compile-time type safety for both the server implementation and UI components. Using Zod or JSON schemas here ensures that invalid requests never reach your MCP server.
Do I need to modify the preload script when adding new capabilities?
No. The src/preload/preload.ts script dynamically discovers capabilities from initialized clients through listClients() and automatically creates method stubs using createAPIMethods. As long as you register the capability in src/main/main.ts and src/main/config.json, the bridge exposes it to the renderer as window.mcpServers.<server>.<capability>.
What naming convention should I use for IPC channels?
Follow the pattern <server>-<type>/<action> (e.g., myServer-analytics/report or myServer-tools/list). This convention is hardcoded into the registerIpcHandlers loop in src/main/main.ts. Deviating from this pattern will result in channels that the automatic registration logic cannot detect or wire to validation schemas.
How does Chat-MCP ensure type safety when extending capabilities?
Chat-MCP uses a schema-first architecture where src/main/types.ts defines Zod or JSON schemas for all capabilities. The manageRequests function validates all IPC traffic against these schemas before forwarding to the MCP client. This prevents type mismatches between the MCP server and the Electron client while providing autocomplete and type checking in the renderer process.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →