Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save nwtgck/489811fdc79046cd422dfbd2c173a194 to your computer and use it in GitHub Desktop.
Save nwtgck/489811fdc79046cd422dfbd2c173a194 to your computer and use it in GitHub Desktop.
(async () => {
const modelcontextprotocolSdk = "https://esm.sh/@modelcontextprotocol/[email protected]/dist/esm";
const { McpServer } = await import(`${modelcontextprotocolSdk}/server/mcp.js`);
const { ReadBuffer, serializeMessage } = await import(`${modelcontextprotocolSdk}/shared/stdio.js`);
const { z } = await import("https://esm.sh/[email protected]/es2022/zod.mjs");
const { Buffer } = await import("https://esm.sh/node/buffer.mjs");
const sqliteDbPromise = (async () => {
const { default: sqlite3InitModule } = await import("https://esm.sh/@sqlite.org/[email protected]");
const sqlite3 = await sqlite3InitModule({ print: console.log, printErr: console.error });
return new sqlite3.oo1.DB();
})();
const server = new McpServer({
name: "Browser Page Control",
version: "0.1.0"
});
server.tool("browser_evaluate",
`Execute JavaScript in a browser page`,
{
script: z.string().describe(`\
The script is a JavaScript expression that will be evaluated in a page.
The result is wrapped in JSON.stringify() to convert the evaluated value to a JSON string.
A Promise is awaited using async/await syntax
The result of "(async () => { await new Promise(resolve => setTimeout(resolve, 1000)); return "hello" })()" is "hello"
No page refreshing or navigation is performed, as that would stop the MCP server.
`),
},
async ({ script }) => ({
content: [{ type: "text", text: JSON.stringify(await eval(script)) }]
}),
);
server.tool("sqlite_exec",
`SQLite execution`,
{
query: z.string().describe(`\
The result is wrapped in JSON.stringify() to convert the rows to a JSON string.
`),
},
async ({ query }) => {
const db = await sqliteDbPromise;
const rows = [];
db.exec(query, {
callback: (row) => rows.push(row),
});
return {
content: [{ type: "text", text: JSON.stringify(rows) }]
};
},
);
class PipingServerTransport {
sessionId;
onclose;
onerror;
onmessage;
pipingServer;
_csPath;
_scPath;
_readBuffer = new ReadBuffer();
_started = false;
scReadable;
scWriter;
csReader;
abortController = new AbortController();
constructor(pipingServer, csPath, scPath) {
this.sessionId = Math.random().toString(36);
this.pipingServer = pipingServer;
this._csPath = csPath;
this._scPath = scPath;
const transformStream = new TransformStream();
this.scReadable = transformStream.readable;
this.scWriter = transformStream.writable.getWriter();
}
async start() {
if (this._started) {
throw new Error("PipingServerTransport already started");
}
this._started = true;
try {
const scResPromise = fetch(this.pipingServer + "/" + this._scPath, {
method: "POST",
body: this.scReadable,
duplex: "half",
signal: this.abortController.signal,
});
const csRes = await fetch(this.pipingServer + "/" + this._csPath, {
method: "GET",
signal: this.abortController.signal,
});
if (csRes.status !== 200) {
throw new Error(`GET status is not 200: ${csRes.status}`);
}
(async () => {
this.csReader = csRes.body.getReader();
while (true) {
const { value, done } = await this.csReader.read();
if (done) {
break;
}
this._readBuffer.append(Buffer.from(value));
while (true) {
const message = this._readBuffer.readMessage();
if (message === null) {
break;
}
console.debug("message from client", message);
this.onmessage?.(message);
}
}
const postRes = await scResPromise;
if (postRes.status !== 200) {
throw new Error(`POST status is not 200: ${postRes.status}`);
}
await postRes.text();
this.onclose?.();
})().catch(err => {
console.error(err);
this.onerror?.(err);
});
} catch (err) {
this.onerror?.(err);
throw err;
}
}
async send(message) {
console.debug("message from server", message);
await this.scWriter.write(new TextEncoder().encode(serializeMessage(message)));
}
async close() {
this.abortController.abort();
await this.scWriter.abort();
await this.csReader.cancel();
}
}
console.log("Starting MCP server...");
const transport = new PipingServerTransport("https://ppng.io", <specify path (client to server)>, <specify path (server to client)>);
await server.connect(transport);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment