@ethisyscore/app-bridge
v1.4.3
Published
EthisysCore Extension App Bridge — enables extension iframes to communicate with the EthisysCore host platform via MCP, navigation, toasts, theme sync, and user context.
Maintainers
Readme
EthisysCore App Bridge
@ethisyscore/app-bridge is the host bridge for plugin UI iframes. It gives plugin pages access to MCP tool/resource calls, host navigation, dialogs, panels, theme/context updates, session state, and reference data.
Installation
npm install @ethisyscore/app-bridgeQuick Start
import { createApp, BRIDGE_VERSION } from "@ethisyscore/app-bridge";
const app = await createApp({ version: BRIDGE_VERSION });
const context = await app.getContext();
const theme = await app.getTheme();
const result = await app.mcp.invokeTool("my-tool", { key: "value" });
const status = await app.mcp.getResource("my-plugin://status");
await app.referenceData.list("hr", "employees");
await app.referenceData.get("hr", "employees", "e-123");
app.navigatePage("stats");
app.onRouteChange((path) => router.navigate(path ? `/${path}` : "/"));
app.onThemeChange((nextTheme) => applyTheme(nextTheme));
app.onContextChange((nextContext) => updateUser(nextContext.userName));createApp(options)
| Option | Type | Description |
|--------|------|-------------|
| version | string | Required bridge contract version |
| timeoutMs | number | Optional handshake timeout |
| mockResponses | Record<string, string \| BridgeToolResult> | Standalone MCP/resource mocks |
| mockReferenceData | Record<string, ReferenceDataRecord[]> | Standalone reference-data mocks keyed by module.entityType |
API Surface
MCP
mcp.invokeTool(name, args)mcp.getResource(uri)
Navigation and UI
navigate(path)navigatePage(path, options?)getRoute()showToast(message, type)confirm(options)openDialog(options)closeDialog(result?)openPanel(options)closePanel()updatePanel(options)
Navigation State
setNavVisibility(itemId, visible)setNavVisibilities(overrides)setNavBadge(itemId, badge)
Context and Theme
getContext()getTheme()onContextChange(cb)onThemeChange(cb)
Session and Utilities
setSessionData(key, value)getSessionData(key)clearSessionData()copyToClipboard(text)downloadFile(options)
Lifecycle
onRouteChange(cb)onVisibilityChange(cb)onBeforeUnload(cb)onDestroy(cb)destroy()
Reference Data
The bridge exposes host-provided reference data for UI pages that need shared lookup data without going through bespoke MCP tools.
const employees = await app.referenceData.list<{ displayName: string }>("hr", "employees");
const employee = await app.referenceData.get<{ displayName: string }>("hr", "employees", "e-123");
const unsubscribe = app.referenceData.onChanged((signal) => {
console.log(signal.modulePrefix, signal.entityType, signal.entityId);
});The API surface is:
referenceData.list(modulePrefix, entityType)referenceData.get(modulePrefix, entityType, entityId)referenceData.onChanged(cb)
Standalone Development
Use mockResponses and mockReferenceData when running outside the host:
const app = await createApp({
version: BRIDGE_VERSION,
mockResponses: {
"my-plugin://status": JSON.stringify({ status: "Healthy", totalItems: 3 }),
},
mockReferenceData: {
"hr.employees": [
{
entityId: "e-123",
version: 1,
changedUtc: "2026-03-27T12:00:00Z",
fields: { displayName: "Ada Lovelace" },
},
],
},
});Navigation Contract
The host route is extension-relative. For a page hosted at /extensions/books/stats, getRoute() returns stats. In React/Vue SPAs, the recommended pattern is:
- call
getRoute()once on boot for the initial deep link - subscribe to
onRouteChange()for subsequent host-driven navigation - when the plugin initiates full-page navigation, call
navigatePage()so the host updates/extensions/...and then echoes the route back to the iframe
Important for sandboxed iframe apps:
- use
MemoryRouteror another in-memory router for full-page plugin UIs - use
navigatePage()for extension-local routed navigation initiated by the plugin UI - do not use
BrowserRouterinside a sandboxed plugin iframe - do not use the host-global
navigate()method for extension-internal page routing
BrowserRouter rewrites the iframe URL against the host origin, which causes broken navigations such as /books/... trying to load as a host page instead of staying inside the plugin shell.
Security Note
The live bridge currently allows allowedOrigins: ["*"] during the postMessage handshake. That is intentional for the host-controlled iframe environment because plugin pages may run under tenant-specific origins, local dev hosts, or opaque origins during embedded flows. Do not copy that assumption into unrelated browser integrations.
Build
npm ci
npm run build
npm test