@olonjs/mcp 1.0.104

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/bridge.ts
4
+ import { chromium } from "playwright";
5
+ var PlaywrightBridge = class {
6
+ constructor(targetUrl, privateKey) {
7
+ this.targetUrl = targetUrl;
8
+ this.privateKey = privateKey;
9
+ }
10
+ browser = null;
11
+ page = null;
12
+ async connect() {
13
+ console.error(`[PlaywrightBridge] Launching browser...`);
14
+ this.browser = await chromium.launch({ headless: true });
15
+ const context = await this.browser.newContext();
16
+ this.page = await context.newPage();
17
+ console.error(`[PlaywrightBridge] Navigating to ${this.targetUrl}...`);
18
+ await this.page.goto(this.targetUrl, { waitUntil: "domcontentloaded" });
19
+ if (this.privateKey) {
20
+ try {
21
+ const authInputSelector = 'input[type="password"]';
22
+ const authButtonSelector = 'button:has-text("Authenticate")';
23
+ const isAuthWall = await this.page.locator(authInputSelector).isVisible({ timeout: 2e3 }).catch(() => false);
24
+ if (isAuthWall) {
25
+ console.error(`[PlaywrightBridge] Auth wall detected. Injecting private key...`);
26
+ await this.page.fill(authInputSelector, this.privateKey);
27
+ await this.page.click(authButtonSelector);
28
+ }
29
+ } catch (err) {
30
+ console.error(`[PlaywrightBridge] Error during auth bypass:`, err);
31
+ }
32
+ }
33
+ console.error(`[PlaywrightBridge] Waiting for WebMCP to initialize...`);
34
+ await this.page.waitForFunction(() => {
35
+ return window.navigator.modelContextProtocol !== void 0;
36
+ }, void 0, { timeout: 15e3 }).catch(() => {
37
+ throw new Error("Timeout waiting for navigator.modelContextProtocol. Ensure you are navigating to an OlonJS Studio route (e.g. /admin) and WebMCP is enabled.");
38
+ });
39
+ console.error(`[PlaywrightBridge] Connected to WebMCP successfully.`);
40
+ }
41
+ async listTools() {
42
+ if (!this.page) throw new Error("Not connected");
43
+ return this.page.evaluate(() => {
44
+ return window.navigator.modelContextProtocol.listTools();
45
+ });
46
+ }
47
+ async readResource(uri) {
48
+ if (!this.page) throw new Error("Not connected");
49
+ return this.page.evaluate(async (targetUri) => {
50
+ return window.navigator.modelContextProtocol.readResource(targetUri);
51
+ }, uri);
52
+ }
53
+ async executeTool(toolName, inputArgsJson) {
54
+ if (!this.page) throw new Error("Not connected");
55
+ return this.page.evaluate(async ({ name, args }) => {
56
+ return window.navigator.modelContextProtocol.executeTool(name, args);
57
+ }, { name: toolName, args: inputArgsJson });
58
+ }
59
+ async disconnect() {
60
+ if (this.browser) {
61
+ await this.browser.close();
62
+ this.browser = null;
63
+ this.page = null;
64
+ console.error(`[PlaywrightBridge] Disconnected.`);
65
+ }
66
+ }
67
+ };
68
+
69
+ // src/server.ts
70
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
71
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
72
+ import {
73
+ CallToolRequestSchema,
74
+ ListToolsRequestSchema,
75
+ ListResourcesRequestSchema,
76
+ ReadResourceRequestSchema
77
+ } from "@modelcontextprotocol/sdk/types.js";
78
+ var OlonJsMcpServer = class {
79
+ constructor(bridge) {
80
+ this.bridge = bridge;
81
+ this.server = new Server(
82
+ {
83
+ name: "@olonjs/mcp",
84
+ version: "1.0.0"
85
+ },
86
+ {
87
+ capabilities: {
88
+ tools: {},
89
+ resources: {}
90
+ // Enabling resources so Agent can read olon:// URIs
91
+ }
92
+ }
93
+ );
94
+ this.setupHandlers();
95
+ this.server.onerror = (error) => console.error("[MCP Error]", error);
96
+ process.on("SIGINT", async () => {
97
+ await this.bridge.disconnect();
98
+ await this.server.close();
99
+ process.exit(0);
100
+ });
101
+ }
102
+ server;
103
+ setupHandlers() {
104
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
105
+ try {
106
+ const webMcpTools = await this.bridge.listTools();
107
+ return {
108
+ tools: webMcpTools.map((t) => ({
109
+ name: t.name,
110
+ description: t.description,
111
+ inputSchema: t.inputSchema
112
+ // MCP SDK expects specific types but general JSON schema is supported
113
+ }))
114
+ };
115
+ } catch (err) {
116
+ console.error("Failed to list tools:", err);
117
+ return { tools: [] };
118
+ }
119
+ });
120
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
121
+ try {
122
+ const { name, arguments: args } = request.params;
123
+ const resultString = await this.bridge.executeTool(name, JSON.stringify(args || {}));
124
+ const parsed = JSON.parse(resultString);
125
+ return {
126
+ content: parsed.content || [{ type: "text", text: resultString }],
127
+ isError: parsed.isError || false
128
+ };
129
+ } catch (err) {
130
+ return {
131
+ content: [{ type: "text", text: `Error calling tool: ${err.message}` }],
132
+ isError: true
133
+ };
134
+ }
135
+ });
136
+ this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
137
+ return {
138
+ resources: [
139
+ {
140
+ uri: "olon://pages/home",
141
+ name: "Home Page Data",
142
+ description: "JSON data of the Home page. To fetch other pages, append the slug, e.g. olon://pages/about",
143
+ mimeType: "application/json"
144
+ }
145
+ ]
146
+ };
147
+ });
148
+ this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
149
+ try {
150
+ const uri = request.params.uri;
151
+ if (!uri.startsWith("olon://")) {
152
+ throw new Error(`Unsupported URI schema: ${uri}. Only olon:// is supported.`);
153
+ }
154
+ const dataString = await this.bridge.readResource(uri);
155
+ return {
156
+ contents: [
157
+ {
158
+ uri,
159
+ mimeType: "application/json",
160
+ text: dataString
161
+ }
162
+ ]
163
+ };
164
+ } catch (err) {
165
+ console.error("Failed to read resource:", err);
166
+ throw err;
167
+ }
168
+ });
169
+ }
170
+ async run() {
171
+ console.error("[OlonJsMcpServer] Connecting to WebMCP via Playwright...");
172
+ await this.bridge.connect();
173
+ console.error("[OlonJsMcpServer] WebMCP connected. Starting STDIO transport...");
174
+ const transport = new StdioServerTransport();
175
+ await this.server.connect(transport);
176
+ console.error("[OlonJsMcpServer] MCP Server is running and listening on STDIO.");
177
+ }
178
+ };
179
+
180
+ // src/cli.ts
181
+ import * as process2 from "process";
182
+ async function main() {
183
+ const targetUrl = process2.argv[2];
184
+ if (!targetUrl) {
185
+ console.error("====================================================");
186
+ console.error("\u274C Missing Target URL");
187
+ console.error("Usage: npx @olonjs/mcp <target-url>");
188
+ console.error("Example: npx @olonjs/mcp http://localhost:5173");
189
+ console.error("====================================================");
190
+ process2.exit(1);
191
+ }
192
+ const adminUrl = targetUrl.endsWith("/admin") ? targetUrl : `${targetUrl.replace(/\/$/, "")}/admin`;
193
+ const privateKey = process2.env.OLONJS_PRIVATE_KEY;
194
+ if (privateKey) {
195
+ console.error(`[OlonJs MCP] Starting bridge to ${adminUrl} with Auth Injection enabled.`);
196
+ } else {
197
+ console.error(`[OlonJs MCP] Starting bridge to ${adminUrl} without Auth Injection. Ensure /admin does not have an Auth Wall or WebMCP is unprotected.`);
198
+ }
199
+ try {
200
+ const bridge = new PlaywrightBridge(adminUrl, privateKey);
201
+ const server = new OlonJsMcpServer(bridge);
202
+ await server.run();
203
+ } catch (error) {
204
+ console.error("[OlonJs MCP] Fatal error starting server:", error);
205
+ process2.exit(1);
206
+ }
207
+ }
208
+ main();
209
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bridge.ts","../src/server.ts","../src/cli.ts"],"sourcesContent":["import { chromium, Browser, Page } from 'playwright';\n\nexport interface WebMcpToolInfo {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\nexport class PlaywrightBridge {\n private browser: Browser | null = null;\n public page: Page | null = null;\n\n constructor(\n private targetUrl: string,\n private privateKey?: string\n ) {}\n\n async connect() {\n console.error(`[PlaywrightBridge] Launching browser...`);\n this.browser = await chromium.launch({ headless: true });\n \n const context = await this.browser.newContext();\n this.page = await context.newPage();\n\n console.error(`[PlaywrightBridge] Navigating to ${this.targetUrl}...`);\n await this.page.goto(this.targetUrl, { waitUntil: 'domcontentloaded' });\n\n // Try to handle Crypto Auth Wall if it exists\n if (this.privateKey) {\n try {\n // We wait a bit to see if an auth input appears.\n // In the future, this selector should match the OlonJS AdminGuard.\n const authInputSelector = 'input[type=\"password\"]'; \n const authButtonSelector = 'button:has-text(\"Authenticate\")';\n\n const isAuthWall = await this.page.locator(authInputSelector).isVisible({ timeout: 2000 }).catch(() => false);\n \n if (isAuthWall) {\n console.error(`[PlaywrightBridge] Auth wall detected. Injecting private key...`);\n await this.page.fill(authInputSelector, this.privateKey);\n await this.page.click(authButtonSelector);\n }\n } catch (err) {\n console.error(`[PlaywrightBridge] Error during auth bypass:`, err);\n }\n }\n\n console.error(`[PlaywrightBridge] Waiting for WebMCP to initialize...`);\n \n // Wait for the navigator.modelContextProtocol to become available\n await this.page.waitForFunction(() => {\n return (window.navigator as any).modelContextProtocol !== undefined;\n }, undefined, { timeout: 15000 }).catch(() => {\n throw new Error(\"Timeout waiting for navigator.modelContextProtocol. Ensure you are navigating to an OlonJS Studio route (e.g. /admin) and WebMCP is enabled.\");\n });\n\n console.error(`[PlaywrightBridge] Connected to WebMCP successfully.`);\n }\n\n async listTools(): Promise<WebMcpToolInfo[]> {\n if (!this.page) throw new Error(\"Not connected\");\n return this.page.evaluate(() => {\n return (window.navigator as any).modelContextProtocol.listTools();\n });\n }\n\n async readResource(uri: string): Promise<string> {\n if (!this.page) throw new Error(\"Not connected\");\n return this.page.evaluate(async (targetUri) => {\n return (window.navigator as any).modelContextProtocol.readResource(targetUri);\n }, uri);\n }\n\n async executeTool(toolName: string, inputArgsJson: string): Promise<string> {\n if (!this.page) throw new Error(\"Not connected\");\n return this.page.evaluate(async ({ name, args }) => {\n return (window.navigator as any).modelContextProtocol.executeTool(name, args);\n }, { name: toolName, args: inputArgsJson });\n }\n\n async disconnect() {\n if (this.browser) {\n await this.browser.close();\n this.browser = null;\n this.page = null;\n console.error(`[PlaywrightBridge] Disconnected.`);\n }\n }\n}\n","import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n ListResourcesRequestSchema,\n ReadResourceRequestSchema\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { PlaywrightBridge } from \"./bridge.js\";\n\nexport class OlonJsMcpServer {\n private server: Server;\n\n constructor(private bridge: PlaywrightBridge) {\n this.server = new Server(\n {\n name: \"@olonjs/mcp\",\n version: \"1.0.0\",\n },\n {\n capabilities: {\n tools: {},\n resources: {}, // Enabling resources so Agent can read olon:// URIs\n },\n }\n );\n\n this.setupHandlers();\n \n // Error handling\n this.server.onerror = (error) => console.error(\"[MCP Error]\", error);\n process.on(\"SIGINT\", async () => {\n await this.bridge.disconnect();\n await this.server.close();\n process.exit(0);\n });\n }\n\n private setupHandlers() {\n this.server.setRequestHandler(ListToolsRequestSchema, async () => {\n try {\n const webMcpTools = await this.bridge.listTools();\n return {\n tools: webMcpTools.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema as any, // MCP SDK expects specific types but general JSON schema is supported\n })),\n };\n } catch (err: any) {\n console.error(\"Failed to list tools:\", err);\n return { tools: [] };\n }\n });\n\n this.server.setRequestHandler(CallToolRequestSchema, async (request) => {\n try {\n const { name, arguments: args } = request.params;\n const resultString = await this.bridge.executeTool(name, JSON.stringify(args || {}));\n const parsed = JSON.parse(resultString);\n \n return {\n content: parsed.content || [{ type: \"text\", text: resultString }],\n isError: parsed.isError || false,\n };\n } catch (err: any) {\n return {\n content: [{ type: \"text\", text: `Error calling tool: ${err.message}` }],\n isError: true,\n };\n }\n });\n\n this.server.setRequestHandler(ListResourcesRequestSchema, async () => {\n return {\n resources: [\n {\n uri: \"olon://pages/home\",\n name: \"Home Page Data\",\n description: \"JSON data of the Home page. To fetch other pages, append the slug, e.g. olon://pages/about\",\n mimeType: \"application/json\",\n }\n ]\n }\n });\n\n this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {\n try {\n const uri = request.params.uri;\n if (!uri.startsWith(\"olon://\")) {\n throw new Error(`Unsupported URI schema: ${uri}. Only olon:// is supported.`);\n }\n\n const dataString = await this.bridge.readResource(uri);\n return {\n contents: [\n {\n uri,\n mimeType: \"application/json\",\n text: dataString,\n },\n ],\n };\n } catch (err: any) {\n console.error(\"Failed to read resource:\", err);\n throw err;\n }\n });\n }\n\n async run() {\n console.error(\"[OlonJsMcpServer] Connecting to WebMCP via Playwright...\");\n await this.bridge.connect();\n \n console.error(\"[OlonJsMcpServer] WebMCP connected. Starting STDIO transport...\");\n const transport = new StdioServerTransport();\n await this.server.connect(transport);\n console.error(\"[OlonJsMcpServer] MCP Server is running and listening on STDIO.\");\n }\n}\n","#!/usr/bin/env node\r\nimport { PlaywrightBridge } from './bridge.js';\r\nimport { OlonJsMcpServer } from './server.js';\r\nimport * as process from 'node:process';\r\n\r\nasync function main() {\r\n const targetUrl = process.argv[2];\r\n if (!targetUrl) {\r\n console.error('====================================================');\r\n console.error('❌ Missing Target URL');\r\n console.error('Usage: npx @olonjs/mcp <target-url>');\r\n console.error('Example: npx @olonjs/mcp http://localhost:5173');\r\n console.error('====================================================');\r\n process.exit(1);\r\n }\r\n\r\n // Normalize URL to always point to /admin to access WebMCP\r\n const adminUrl = targetUrl.endsWith('/admin') ? targetUrl : `${targetUrl.replace(/\\/$/, '')}/admin`;\r\n\r\n const privateKey = process.env.OLONJS_PRIVATE_KEY;\r\n\r\n if (privateKey) {\r\n console.error(`[OlonJs MCP] Starting bridge to ${adminUrl} with Auth Injection enabled.`);\r\n } else {\r\n console.error(`[OlonJs MCP] Starting bridge to ${adminUrl} without Auth Injection. Ensure /admin does not have an Auth Wall or WebMCP is unprotected.`);\r\n }\r\n\r\n try {\r\n const bridge = new PlaywrightBridge(adminUrl, privateKey);\r\n const server = new OlonJsMcpServer(bridge);\r\n \r\n await server.run();\r\n } catch (error) {\r\n console.error('[OlonJs MCP] Fatal error starting server:', error);\r\n process.exit(1);\r\n }\r\n}\r\n\r\nmain();\r\n"],"mappings":"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","names":["process"]}
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@olonjs/mcp",
3
+ "version": "1.0.104",
4
+ "description": "Local MCP server bridge for OlonJS Tenant Zero-UI automation via Playwright",
5
+ "private": false,
6
+ "type": "module",
7
+ "main": "dist/cli.js",
8
+ "bin": {
9
+ "olonjs-mcp": "./dist/cli.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsup src/cli.ts --format esm --clean",
13
+ "dev": "tsup src/cli.ts --format esm --watch",
14
+ "start": "node dist/cli.js"
15
+ },
16
+ "dependencies": {
17
+ "@modelcontextprotocol/sdk": "^1.0.1",
18
+ "playwright": "^1.49.1",
19
+ "zod": "^3.24.1"
20
+ },
21
+ "devDependencies": {
22
+ "tsup": "^8.3.6",
23
+ "typescript": "^5.7.3",
24
+ "@types/node": "^22.10.2"
25
+ }
26
+ }
package/src/bridge.ts ADDED
@@ -0,0 +1,89 @@
1
+ import { chromium, Browser, Page } from 'playwright';
2
+
3
+ export interface WebMcpToolInfo {
4
+ name: string;
5
+ description: string;
6
+ inputSchema: Record<string, unknown>;
7
+ }
8
+
9
+ export class PlaywrightBridge {
10
+ private browser: Browser | null = null;
11
+ public page: Page | null = null;
12
+
13
+ constructor(
14
+ private targetUrl: string,
15
+ private privateKey?: string
16
+ ) {}
17
+
18
+ async connect() {
19
+ console.error(`[PlaywrightBridge] Launching browser...`);
20
+ this.browser = await chromium.launch({ headless: true });
21
+
22
+ const context = await this.browser.newContext();
23
+ this.page = await context.newPage();
24
+
25
+ console.error(`[PlaywrightBridge] Navigating to ${this.targetUrl}...`);
26
+ await this.page.goto(this.targetUrl, { waitUntil: 'domcontentloaded' });
27
+
28
+ // Try to handle Crypto Auth Wall if it exists
29
+ if (this.privateKey) {
30
+ try {
31
+ // We wait a bit to see if an auth input appears.
32
+ // In the future, this selector should match the OlonJS AdminGuard.
33
+ const authInputSelector = 'input[type="password"]';
34
+ const authButtonSelector = 'button:has-text("Authenticate")';
35
+
36
+ const isAuthWall = await this.page.locator(authInputSelector).isVisible({ timeout: 2000 }).catch(() => false);
37
+
38
+ if (isAuthWall) {
39
+ console.error(`[PlaywrightBridge] Auth wall detected. Injecting private key...`);
40
+ await this.page.fill(authInputSelector, this.privateKey);
41
+ await this.page.click(authButtonSelector);
42
+ }
43
+ } catch (err) {
44
+ console.error(`[PlaywrightBridge] Error during auth bypass:`, err);
45
+ }
46
+ }
47
+
48
+ console.error(`[PlaywrightBridge] Waiting for WebMCP to initialize...`);
49
+
50
+ // Wait for the navigator.modelContextProtocol to become available
51
+ await this.page.waitForFunction(() => {
52
+ return (window.navigator as any).modelContextProtocol !== undefined;
53
+ }, undefined, { timeout: 15000 }).catch(() => {
54
+ throw new Error("Timeout waiting for navigator.modelContextProtocol. Ensure you are navigating to an OlonJS Studio route (e.g. /admin) and WebMCP is enabled.");
55
+ });
56
+
57
+ console.error(`[PlaywrightBridge] Connected to WebMCP successfully.`);
58
+ }
59
+
60
+ async listTools(): Promise<WebMcpToolInfo[]> {
61
+ if (!this.page) throw new Error("Not connected");
62
+ return this.page.evaluate(() => {
63
+ return (window.navigator as any).modelContextProtocol.listTools();
64
+ });
65
+ }
66
+
67
+ async readResource(uri: string): Promise<string> {
68
+ if (!this.page) throw new Error("Not connected");
69
+ return this.page.evaluate(async (targetUri) => {
70
+ return (window.navigator as any).modelContextProtocol.readResource(targetUri);
71
+ }, uri);
72
+ }
73
+
74
+ async executeTool(toolName: string, inputArgsJson: string): Promise<string> {
75
+ if (!this.page) throw new Error("Not connected");
76
+ return this.page.evaluate(async ({ name, args }) => {
77
+ return (window.navigator as any).modelContextProtocol.executeTool(name, args);
78
+ }, { name: toolName, args: inputArgsJson });
79
+ }
80
+
81
+ async disconnect() {
82
+ if (this.browser) {
83
+ await this.browser.close();
84
+ this.browser = null;
85
+ this.page = null;
86
+ console.error(`[PlaywrightBridge] Disconnected.`);
87
+ }
88
+ }
89
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ import { PlaywrightBridge } from './bridge.js';
3
+ import { OlonJsMcpServer } from './server.js';
4
+ import * as process from 'node:process';
5
+
6
+ async function main() {
7
+ const targetUrl = process.argv[2];
8
+ if (!targetUrl) {
9
+ console.error('====================================================');
10
+ console.error('❌ Missing Target URL');
11
+ console.error('Usage: npx @olonjs/mcp <target-url>');
12
+ console.error('Example: npx @olonjs/mcp http://localhost:5173');
13
+ console.error('====================================================');
14
+ process.exit(1);
15
+ }
16
+
17
+ // Normalize URL to always point to /admin to access WebMCP
18
+ const adminUrl = targetUrl.endsWith('/admin') ? targetUrl : `${targetUrl.replace(/\/$/, '')}/admin`;
19
+
20
+ const privateKey = process.env.OLONJS_PRIVATE_KEY;
21
+
22
+ if (privateKey) {
23
+ console.error(`[OlonJs MCP] Starting bridge to ${adminUrl} with Auth Injection enabled.`);
24
+ } else {
25
+ console.error(`[OlonJs MCP] Starting bridge to ${adminUrl} without Auth Injection. Ensure /admin does not have an Auth Wall or WebMCP is unprotected.`);
26
+ }
27
+
28
+ try {
29
+ const bridge = new PlaywrightBridge(adminUrl, privateKey);
30
+ const server = new OlonJsMcpServer(bridge);
31
+
32
+ await server.run();
33
+ } catch (error) {
34
+ console.error('[OlonJs MCP] Fatal error starting server:', error);
35
+ process.exit(1);
36
+ }
37
+ }
38
+
39
+ main();
package/src/server.ts ADDED
@@ -0,0 +1,120 @@
1
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import {
4
+ CallToolRequestSchema,
5
+ ListToolsRequestSchema,
6
+ ListResourcesRequestSchema,
7
+ ReadResourceRequestSchema
8
+ } from "@modelcontextprotocol/sdk/types.js";
9
+ import type { PlaywrightBridge } from "./bridge.js";
10
+
11
+ export class OlonJsMcpServer {
12
+ private server: Server;
13
+
14
+ constructor(private bridge: PlaywrightBridge) {
15
+ this.server = new Server(
16
+ {
17
+ name: "@olonjs/mcp",
18
+ version: "1.0.0",
19
+ },
20
+ {
21
+ capabilities: {
22
+ tools: {},
23
+ resources: {}, // Enabling resources so Agent can read olon:// URIs
24
+ },
25
+ }
26
+ );
27
+
28
+ this.setupHandlers();
29
+
30
+ // Error handling
31
+ this.server.onerror = (error) => console.error("[MCP Error]", error);
32
+ process.on("SIGINT", async () => {
33
+ await this.bridge.disconnect();
34
+ await this.server.close();
35
+ process.exit(0);
36
+ });
37
+ }
38
+
39
+ private setupHandlers() {
40
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
41
+ try {
42
+ const webMcpTools = await this.bridge.listTools();
43
+ return {
44
+ tools: webMcpTools.map((t) => ({
45
+ name: t.name,
46
+ description: t.description,
47
+ inputSchema: t.inputSchema as any, // MCP SDK expects specific types but general JSON schema is supported
48
+ })),
49
+ };
50
+ } catch (err: any) {
51
+ console.error("Failed to list tools:", err);
52
+ return { tools: [] };
53
+ }
54
+ });
55
+
56
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
57
+ try {
58
+ const { name, arguments: args } = request.params;
59
+ const resultString = await this.bridge.executeTool(name, JSON.stringify(args || {}));
60
+ const parsed = JSON.parse(resultString);
61
+
62
+ return {
63
+ content: parsed.content || [{ type: "text", text: resultString }],
64
+ isError: parsed.isError || false,
65
+ };
66
+ } catch (err: any) {
67
+ return {
68
+ content: [{ type: "text", text: `Error calling tool: ${err.message}` }],
69
+ isError: true,
70
+ };
71
+ }
72
+ });
73
+
74
+ this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
75
+ return {
76
+ resources: [
77
+ {
78
+ uri: "olon://pages/home",
79
+ name: "Home Page Data",
80
+ description: "JSON data of the Home page. To fetch other pages, append the slug, e.g. olon://pages/about",
81
+ mimeType: "application/json",
82
+ }
83
+ ]
84
+ }
85
+ });
86
+
87
+ this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
88
+ try {
89
+ const uri = request.params.uri;
90
+ if (!uri.startsWith("olon://")) {
91
+ throw new Error(`Unsupported URI schema: ${uri}. Only olon:// is supported.`);
92
+ }
93
+
94
+ const dataString = await this.bridge.readResource(uri);
95
+ return {
96
+ contents: [
97
+ {
98
+ uri,
99
+ mimeType: "application/json",
100
+ text: dataString,
101
+ },
102
+ ],
103
+ };
104
+ } catch (err: any) {
105
+ console.error("Failed to read resource:", err);
106
+ throw err;
107
+ }
108
+ });
109
+ }
110
+
111
+ async run() {
112
+ console.error("[OlonJsMcpServer] Connecting to WebMCP via Playwright...");
113
+ await this.bridge.connect();
114
+
115
+ console.error("[OlonJsMcpServer] WebMCP connected. Starting STDIO transport...");
116
+ const transport = new StdioServerTransport();
117
+ await this.server.connect(transport);
118
+ console.error("[OlonJsMcpServer] MCP Server is running and listening on STDIO.");
119
+ }
120
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "lib": ["ES2022", "DOM"],
7
+ "outDir": "./dist",
8
+ "rootDir": "./src",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "declaration": true,
14
+ "sourceMap": true
15
+ },
16
+ "include": ["src/**/*"],
17
+ "exclude": ["node_modules", "dist"]
18
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entry: ['src/cli.ts'],
5
+ format: ['esm'],
6
+ target: 'es2022',
7
+ clean: true,
8
+ dts: true,
9
+ sourcemap: true,
10
+ outExtension: () => ({ js: '.js' }),
11
+ });