@mcp-html-bridge/mcp-client 0.5.0 → 0.5.2
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/client.d.ts +19 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +114 -0
- package/dist/client.js.map +1 -0
- package/{src/index.ts → dist/index.d.ts} +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +4 -1
- package/src/client.ts +0 -125
- package/src/types.ts +0 -18
- package/tsconfig.json +0 -11
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { MCPConnectionOptions, MCPServerCapabilities, MCPToolParam } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Lightweight MCP client that connects via stdio transport.
|
|
4
|
+
* Implements the minimum protocol handshake to list tools.
|
|
5
|
+
*/
|
|
6
|
+
export declare class MCPClient {
|
|
7
|
+
private process;
|
|
8
|
+
private buffer;
|
|
9
|
+
private requestId;
|
|
10
|
+
private pending;
|
|
11
|
+
connect(options: MCPConnectionOptions): Promise<void>;
|
|
12
|
+
listTools(): Promise<MCPToolParam[]>;
|
|
13
|
+
getServerInfo(): Promise<MCPServerCapabilities>;
|
|
14
|
+
disconnect(): Promise<void>;
|
|
15
|
+
private sendRequest;
|
|
16
|
+
private sendNotification;
|
|
17
|
+
private processBuffer;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5F;;;GAGG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,OAAO,CAAoF;IAE7F,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BrD,SAAS,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAKpC,aAAa,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAS/C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQjC,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,aAAa;CAmCtB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MCPClient = void 0;
|
|
4
|
+
// ── MCP Protocol Client: spawn subprocess & extract schemas ──
|
|
5
|
+
const node_child_process_1 = require("node:child_process");
|
|
6
|
+
/**
|
|
7
|
+
* Lightweight MCP client that connects via stdio transport.
|
|
8
|
+
* Implements the minimum protocol handshake to list tools.
|
|
9
|
+
*/
|
|
10
|
+
class MCPClient {
|
|
11
|
+
process = null;
|
|
12
|
+
buffer = '';
|
|
13
|
+
requestId = 0;
|
|
14
|
+
pending = new Map();
|
|
15
|
+
async connect(options) {
|
|
16
|
+
const [cmd, ...defaultArgs] = options.command.split(/\s+/);
|
|
17
|
+
const args = [...defaultArgs, ...(options.args ?? [])];
|
|
18
|
+
this.process = (0, node_child_process_1.spawn)(cmd, args, {
|
|
19
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
20
|
+
env: { ...process.env, ...options.env },
|
|
21
|
+
});
|
|
22
|
+
this.process.stdout?.on('data', (chunk) => {
|
|
23
|
+
this.buffer += chunk.toString();
|
|
24
|
+
this.processBuffer();
|
|
25
|
+
});
|
|
26
|
+
this.process.stderr?.on('data', (chunk) => {
|
|
27
|
+
// Log MCP server stderr for debugging
|
|
28
|
+
process.stderr.write(`[mcp-server] ${chunk.toString()}`);
|
|
29
|
+
});
|
|
30
|
+
// Initialize handshake
|
|
31
|
+
await this.sendRequest('initialize', {
|
|
32
|
+
protocolVersion: '2024-11-05',
|
|
33
|
+
capabilities: {},
|
|
34
|
+
clientInfo: { name: 'mcp-html-bridge', version: '0.1.0' },
|
|
35
|
+
});
|
|
36
|
+
// Send initialized notification
|
|
37
|
+
this.sendNotification('notifications/initialized', {});
|
|
38
|
+
}
|
|
39
|
+
async listTools() {
|
|
40
|
+
const result = await this.sendRequest('tools/list', {});
|
|
41
|
+
return result.tools ?? [];
|
|
42
|
+
}
|
|
43
|
+
async getServerInfo() {
|
|
44
|
+
const tools = await this.listTools();
|
|
45
|
+
return {
|
|
46
|
+
name: 'mcp-server',
|
|
47
|
+
version: '0.0.0',
|
|
48
|
+
tools,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async disconnect() {
|
|
52
|
+
if (this.process) {
|
|
53
|
+
this.process.stdin?.end();
|
|
54
|
+
this.process.kill();
|
|
55
|
+
this.process = null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
sendRequest(method, params) {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
const id = ++this.requestId;
|
|
61
|
+
this.pending.set(id, { resolve, reject });
|
|
62
|
+
const msg = JSON.stringify({ jsonrpc: '2.0', id, method, params });
|
|
63
|
+
this.process?.stdin?.write(`Content-Length: ${Buffer.byteLength(msg)}\r\n\r\n${msg}`);
|
|
64
|
+
// Timeout after 30s
|
|
65
|
+
setTimeout(() => {
|
|
66
|
+
if (this.pending.has(id)) {
|
|
67
|
+
this.pending.delete(id);
|
|
68
|
+
reject(new Error(`Request ${method} timed out`));
|
|
69
|
+
}
|
|
70
|
+
}, 30000);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
sendNotification(method, params) {
|
|
74
|
+
const msg = JSON.stringify({ jsonrpc: '2.0', method, params });
|
|
75
|
+
this.process?.stdin?.write(`Content-Length: ${Buffer.byteLength(msg)}\r\n\r\n${msg}`);
|
|
76
|
+
}
|
|
77
|
+
processBuffer() {
|
|
78
|
+
while (true) {
|
|
79
|
+
const headerEnd = this.buffer.indexOf('\r\n\r\n');
|
|
80
|
+
if (headerEnd === -1)
|
|
81
|
+
break;
|
|
82
|
+
const header = this.buffer.slice(0, headerEnd);
|
|
83
|
+
const match = header.match(/Content-Length:\s*(\d+)/i);
|
|
84
|
+
if (!match) {
|
|
85
|
+
this.buffer = this.buffer.slice(headerEnd + 4);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const contentLength = parseInt(match[1], 10);
|
|
89
|
+
const bodyStart = headerEnd + 4;
|
|
90
|
+
if (this.buffer.length < bodyStart + contentLength)
|
|
91
|
+
break;
|
|
92
|
+
const body = this.buffer.slice(bodyStart, bodyStart + contentLength);
|
|
93
|
+
this.buffer = this.buffer.slice(bodyStart + contentLength);
|
|
94
|
+
try {
|
|
95
|
+
const msg = JSON.parse(body);
|
|
96
|
+
if (msg.id !== undefined && this.pending.has(msg.id)) {
|
|
97
|
+
const handler = this.pending.get(msg.id);
|
|
98
|
+
this.pending.delete(msg.id);
|
|
99
|
+
if (msg.error) {
|
|
100
|
+
handler.reject(new Error(msg.error.message));
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
handler.resolve(msg.result);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Ignore malformed messages
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
exports.MCPClient = MCPClient;
|
|
114
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MCPClient = void 0;
|
|
4
|
+
var client_js_1 = require("./client.js");
|
|
5
|
+
Object.defineProperty(exports, "MCPClient", { enumerable: true, get: function () { return client_js_1.MCPClient; } });
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yCAAwC;AAA/B,sGAAA,SAAS,OAAA"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface MCPToolParam {
|
|
2
|
+
name: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
inputSchema: Record<string, unknown>;
|
|
5
|
+
}
|
|
6
|
+
export interface MCPConnectionOptions {
|
|
7
|
+
command: string;
|
|
8
|
+
args?: string[];
|
|
9
|
+
env?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
export interface MCPServerCapabilities {
|
|
12
|
+
name: string;
|
|
13
|
+
version: string;
|
|
14
|
+
tools: MCPToolParam[];
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-html-bridge/mcp-client",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "MCP protocol client for schema extraction",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
"default": "./dist/index.js"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist/"
|
|
15
|
+
],
|
|
13
16
|
"scripts": {
|
|
14
17
|
"build": "tsc -p tsconfig.json",
|
|
15
18
|
"clean": "rm -rf dist"
|
package/src/client.ts
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
// ── MCP Protocol Client: spawn subprocess & extract schemas ──
|
|
2
|
-
import { spawn, type ChildProcess } from 'node:child_process';
|
|
3
|
-
import type { MCPConnectionOptions, MCPServerCapabilities, MCPToolParam } from './types.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Lightweight MCP client that connects via stdio transport.
|
|
7
|
-
* Implements the minimum protocol handshake to list tools.
|
|
8
|
-
*/
|
|
9
|
-
export class MCPClient {
|
|
10
|
-
private process: ChildProcess | null = null;
|
|
11
|
-
private buffer = '';
|
|
12
|
-
private requestId = 0;
|
|
13
|
-
private pending = new Map<number, { resolve: (v: unknown) => void; reject: (e: Error) => void }>();
|
|
14
|
-
|
|
15
|
-
async connect(options: MCPConnectionOptions): Promise<void> {
|
|
16
|
-
const [cmd, ...defaultArgs] = options.command.split(/\s+/);
|
|
17
|
-
const args = [...defaultArgs, ...(options.args ?? [])];
|
|
18
|
-
|
|
19
|
-
this.process = spawn(cmd, args, {
|
|
20
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
21
|
-
env: { ...process.env, ...options.env },
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
this.process.stdout?.on('data', (chunk: Buffer) => {
|
|
25
|
-
this.buffer += chunk.toString();
|
|
26
|
-
this.processBuffer();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
this.process.stderr?.on('data', (chunk: Buffer) => {
|
|
30
|
-
// Log MCP server stderr for debugging
|
|
31
|
-
process.stderr.write(`[mcp-server] ${chunk.toString()}`);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
// Initialize handshake
|
|
35
|
-
await this.sendRequest('initialize', {
|
|
36
|
-
protocolVersion: '2024-11-05',
|
|
37
|
-
capabilities: {},
|
|
38
|
-
clientInfo: { name: 'mcp-html-bridge', version: '0.1.0' },
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// Send initialized notification
|
|
42
|
-
this.sendNotification('notifications/initialized', {});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async listTools(): Promise<MCPToolParam[]> {
|
|
46
|
-
const result = await this.sendRequest('tools/list', {}) as { tools: MCPToolParam[] };
|
|
47
|
-
return result.tools ?? [];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async getServerInfo(): Promise<MCPServerCapabilities> {
|
|
51
|
-
const tools = await this.listTools();
|
|
52
|
-
return {
|
|
53
|
-
name: 'mcp-server',
|
|
54
|
-
version: '0.0.0',
|
|
55
|
-
tools,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async disconnect(): Promise<void> {
|
|
60
|
-
if (this.process) {
|
|
61
|
-
this.process.stdin?.end();
|
|
62
|
-
this.process.kill();
|
|
63
|
-
this.process = null;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
private sendRequest(method: string, params: Record<string, unknown>): Promise<unknown> {
|
|
68
|
-
return new Promise((resolve, reject) => {
|
|
69
|
-
const id = ++this.requestId;
|
|
70
|
-
this.pending.set(id, { resolve, reject });
|
|
71
|
-
|
|
72
|
-
const msg = JSON.stringify({ jsonrpc: '2.0', id, method, params });
|
|
73
|
-
this.process?.stdin?.write(`Content-Length: ${Buffer.byteLength(msg)}\r\n\r\n${msg}`);
|
|
74
|
-
|
|
75
|
-
// Timeout after 30s
|
|
76
|
-
setTimeout(() => {
|
|
77
|
-
if (this.pending.has(id)) {
|
|
78
|
-
this.pending.delete(id);
|
|
79
|
-
reject(new Error(`Request ${method} timed out`));
|
|
80
|
-
}
|
|
81
|
-
}, 30000);
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
private sendNotification(method: string, params: Record<string, unknown>): void {
|
|
86
|
-
const msg = JSON.stringify({ jsonrpc: '2.0', method, params });
|
|
87
|
-
this.process?.stdin?.write(`Content-Length: ${Buffer.byteLength(msg)}\r\n\r\n${msg}`);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
private processBuffer(): void {
|
|
91
|
-
while (true) {
|
|
92
|
-
const headerEnd = this.buffer.indexOf('\r\n\r\n');
|
|
93
|
-
if (headerEnd === -1) break;
|
|
94
|
-
|
|
95
|
-
const header = this.buffer.slice(0, headerEnd);
|
|
96
|
-
const match = header.match(/Content-Length:\s*(\d+)/i);
|
|
97
|
-
if (!match) {
|
|
98
|
-
this.buffer = this.buffer.slice(headerEnd + 4);
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const contentLength = parseInt(match[1], 10);
|
|
103
|
-
const bodyStart = headerEnd + 4;
|
|
104
|
-
if (this.buffer.length < bodyStart + contentLength) break;
|
|
105
|
-
|
|
106
|
-
const body = this.buffer.slice(bodyStart, bodyStart + contentLength);
|
|
107
|
-
this.buffer = this.buffer.slice(bodyStart + contentLength);
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
const msg = JSON.parse(body) as { id?: number; result?: unknown; error?: { message: string } };
|
|
111
|
-
if (msg.id !== undefined && this.pending.has(msg.id)) {
|
|
112
|
-
const handler = this.pending.get(msg.id)!;
|
|
113
|
-
this.pending.delete(msg.id);
|
|
114
|
-
if (msg.error) {
|
|
115
|
-
handler.reject(new Error(msg.error.message));
|
|
116
|
-
} else {
|
|
117
|
-
handler.resolve(msg.result);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
} catch {
|
|
121
|
-
// Ignore malformed messages
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
// ── MCP Client types ──
|
|
2
|
-
export interface MCPToolParam {
|
|
3
|
-
name: string;
|
|
4
|
-
description?: string;
|
|
5
|
-
inputSchema: Record<string, unknown>;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface MCPConnectionOptions {
|
|
9
|
-
command: string;
|
|
10
|
-
args?: string[];
|
|
11
|
-
env?: Record<string, string>;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface MCPServerCapabilities {
|
|
15
|
-
name: string;
|
|
16
|
-
version: string;
|
|
17
|
-
tools: MCPToolParam[];
|
|
18
|
-
}
|