@casys/mcp-bridge
v0.2.1
Published
Bridge MCP Apps interactive UIs to messaging platforms (Telegram Mini Apps, LINE LIFF)
Downloads
180
Maintainers
Readme
@casys/mcp-bridge
Bridge MCP Apps interactive UIs to messaging platforms. Turn any MCP tool with a ui:// resource into a Telegram Mini App or LINE LIFF app.
MCP Server (tools with ui:// resources)
|
v
+------------------+
| @casys/mcp-bridge |
| Resource Server | Serves HTML + injects bridge.js
| Bridge Client | Intercepts postMessage, routes via WebSocket
| Platform Adapters | Telegram theme/viewport/auth mapping
+------------------+
|
v
Telegram Mini App / LINE LIFF WebViewInstall
# npm
npm install @casys/mcp-bridge
# Deno
deno add jsr:@casys/mcp-bridgeQuick Start
1. Create a resource server with a tool handler
import { startResourceServer } from "@casys/mcp-bridge";
const server = await startResourceServer({
port: 4000,
platform: "telegram",
appBaseDir: "./my-app", // Directory containing your MCP App HTML
resourceBaseUrl: "https://my-domain.com",
onMessage: async (session, message) => {
// Handle tools/call requests from the UI
if (message.method === "tools/call") {
const toolName = message.params?.name;
if (toolName === "get_data") {
return {
jsonrpc: "2.0",
id: message.id,
result: { content: [{ type: "text", text: JSON.stringify({ value: 42 }) }] },
};
}
}
return null;
},
});
console.log(`Bridge running at http://localhost:${server.port}`);2. Create your MCP App HTML
<!DOCTYPE html>
<html>
<head><title>My MCP App</title></head>
<body>
<button id="btn">Get Data</button>
<div id="result"></div>
<script>
// bridge.js is auto-injected by the resource server
// It intercepts postMessage and routes via WebSocket to your handler
window.addEventListener("mcp-bridge-ready", () => {
document.getElementById("btn").onclick = async () => {
const id = Date.now();
window.parent.postMessage({
jsonrpc: "2.0", id,
method: "tools/call",
params: { name: "get_data", arguments: {} },
}, "*");
};
});
window.addEventListener("message", (e) => {
if (e.data?.result) {
document.getElementById("result").textContent = JSON.stringify(e.data.result);
}
});
</script>
</body>
</html>3. Expose via HTTPS and configure Telegram
# Option A: Reverse proxy (recommended for production)
# Add to your Caddy/nginx config:
# /app/* -> localhost:4000
# /bridge -> localhost:4000
# Option B: ngrok (for development)
ngrok http 4000Then configure your Telegram bot via @BotFather:
/setmenubutton-> select your bot- Enter your HTTPS URL:
https://your-domain.com/app/my-app/index.html - Open the bot on Telegram mobile -> tap Menu Button
How It Works
- User opens Mini App in Telegram (or LINE)
- Resource server serves the MCP App HTML with
bridge.jsauto-injected - bridge.js intercepts
postMessagecalls from the MCP App - Messages are routed via WebSocket to the resource server
- Resource server forwards
tools/callto your handler - Response flows back: handler -> WebSocket -> bridge.js -> MCP App
The MCP App doesn't know it's running in Telegram. It uses the standard MCP Apps SDK (postMessage), and the bridge handles the translation.
API
Resource Server
import { startResourceServer } from "@casys/mcp-bridge";
import type { ResourceServerConfig } from "@casys/mcp-bridge";
const config: ResourceServerConfig = {
port: 4000,
platform: "telegram",
appBaseDir: "./my-app",
resourceBaseUrl: "https://my-domain.com",
csp: {
scriptSources: ["https://telegram.org"],
connectSources: ["wss://my-domain.com"],
frameAncestors: ["https://web.telegram.org"],
},
onMessage: async (session, message) => { /* ... */ },
};Protocol Helpers
import {
buildToolCallRequest,
buildSuccessResponse,
buildErrorResponse,
isRequest,
isResponse,
MessageRouter,
} from "@casys/mcp-bridge";
const router = new MessageRouter();
router.onRequest("tools/call", async (params) => {
return { content: [{ type: "text", text: "result" }] };
});Platform Adapters
// Telegram — used internally by bridge.js, or standalone
import { TelegramPlatformAdapter } from "@casys/mcp-bridge";
const adapter = new TelegramPlatformAdapter();
const hostContext = await adapter.initialize();
// { colorScheme: "dark", viewportHeight: 640, ... }
// LINE LIFF
import { LineAdapter } from "@casys/mcp-bridge";Resource URI Parsing
import { parseResourceUri, resolveToHttp } from "@casys/mcp-bridge";
const uri = parseResourceUri("ui://my-server/dashboard.html?tab=metrics");
const httpUrl = resolveToHttp(uri, "https://my-domain.com");
// => "https://my-domain.com/my-server/dashboard.html?tab=metrics"Architecture
| Layer | Component | Role |
|-------|-----------|------|
| Client | bridge.js | IIFE injected into MCP App HTML. Intercepts postMessage, routes via WebSocket |
| Server | ResourceServer | HTTP server (serves HTML + bridge.js), WebSocket endpoint, session management |
| Protocol | MessageRouter | JSON-RPC 2.0 routing, pending request tracking, timeout |
| Adapters | TelegramPlatformAdapter | Maps Telegram WebApp SDK to MCP Apps HostContext |
| Security | CSP + SessionStore | Content-Security-Policy headers, session auth, path traversal protection |
Development
# Run tests (87 tests)
deno task test
# Type-check
deno task check
# Lint
deno task lint
# Run the demo
deno task demoCompanion Package
Built to work with @casys/mcp-server — the production MCP server framework. Use @casys/mcp-server to build MCP tools with ui:// resources, and @casys/mcp-bridge to deliver them to messaging platforms.
License
MIT
