@phidiassj/aiyoperps-mcp-bridge 0.8.1

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/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # AiyoPerps MCP Bridge
2
+
3
+ This package exposes a standard MCP `stdio` server that forwards requests to the local AiyoPerps HTTP MCP endpoint.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npx -y @phidiassj/aiyoperps-mcp-bridge
9
+ ```
10
+
11
+ Custom endpoint:
12
+
13
+ ```bash
14
+ npx -y @phidiassj/aiyoperps-mcp-bridge --url http://127.0.0.1:5078/mcp
15
+ ```
16
+
17
+ Or via environment variable:
18
+
19
+ ```bash
20
+ AIYOPERPS_MCP_URL=http://127.0.0.1:5078/mcp npx -y @phidiassj/aiyoperps-mcp-bridge
21
+ ```
22
+
23
+ ## Typical MCP config
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "aiyoperps": {
29
+ "command": "npx",
30
+ "args": [
31
+ "-y",
32
+ "@phidiassj/aiyoperps-mcp-bridge",
33
+ "--url",
34
+ "http://127.0.0.1:5078/mcp"
35
+ ]
36
+ }
37
+ }
38
+ }
39
+ ```
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { stdin, stdout, stderr, exit, argv, env } = require('node:process');
5
+
6
+ const endpoint = resolveEndpoint(argv.slice(2));
7
+ let buffer = Buffer.alloc(0);
8
+ let expectedBodyLength = null;
9
+
10
+ stderr.write(`[aiyoperps-mcp-bridge] forwarding stdio MCP to ${endpoint}\n`);
11
+
12
+ stdin.on('data', chunk => {
13
+ buffer = Buffer.concat([buffer, chunk]);
14
+ tryProcessBuffer();
15
+ });
16
+
17
+ stdin.on('end', () => {
18
+ stderr.write('[aiyoperps-mcp-bridge] stdin closed\n');
19
+ exit(0);
20
+ });
21
+
22
+ stdin.on('error', error => {
23
+ stderr.write(`[aiyoperps-mcp-bridge] stdin error: ${error.message}\n`);
24
+ exit(1);
25
+ });
26
+
27
+ function tryProcessBuffer() {
28
+ while (true) {
29
+ if (expectedBodyLength === null) {
30
+ const headerEndIndex = buffer.indexOf('\r\n\r\n');
31
+ if (headerEndIndex === -1) {
32
+ return;
33
+ }
34
+
35
+ const headerText = buffer.subarray(0, headerEndIndex).toString('utf8');
36
+ const lengthMatch = /content-length:\s*(\d+)/i.exec(headerText);
37
+ if (!lengthMatch) {
38
+ stderr.write('[aiyoperps-mcp-bridge] invalid MCP frame: missing Content-Length\n');
39
+ exit(1);
40
+ }
41
+
42
+ expectedBodyLength = Number.parseInt(lengthMatch[1], 10);
43
+ buffer = buffer.subarray(headerEndIndex + 4);
44
+ }
45
+
46
+ if (buffer.length < expectedBodyLength) {
47
+ return;
48
+ }
49
+
50
+ const body = buffer.subarray(0, expectedBodyLength);
51
+ buffer = buffer.subarray(expectedBodyLength);
52
+ expectedBodyLength = null;
53
+
54
+ handleMessage(body).catch(error => {
55
+ stderr.write(`[aiyoperps-mcp-bridge] message handling failed: ${error.stack || error.message}\n`);
56
+ exit(1);
57
+ });
58
+ }
59
+ }
60
+
61
+ async function handleMessage(bodyBuffer) {
62
+ const bodyText = bodyBuffer.toString('utf8');
63
+ let payload;
64
+
65
+ try {
66
+ payload = JSON.parse(bodyText);
67
+ } catch (error) {
68
+ stderr.write(`[aiyoperps-mcp-bridge] invalid JSON from client: ${error.message}\n`);
69
+ writeFrame(JSON.stringify({
70
+ jsonrpc: '2.0',
71
+ error: {
72
+ code: -32700,
73
+ message: 'Invalid JSON received by bridge.'
74
+ },
75
+ id: null
76
+ }));
77
+ return;
78
+ }
79
+
80
+ let response;
81
+ try {
82
+ response = await postJson(endpoint, payload);
83
+ } catch (error) {
84
+ stderr.write(`[aiyoperps-mcp-bridge] upstream request failed: ${error.message}\n`);
85
+ writeFrame(JSON.stringify({
86
+ jsonrpc: '2.0',
87
+ error: {
88
+ code: -32000,
89
+ message: `AiyoPerps MCP endpoint unavailable: ${error.message}`
90
+ },
91
+ id: payload && typeof payload === 'object' && 'id' in payload ? payload.id : null
92
+ }));
93
+ return;
94
+ }
95
+
96
+ writeFrame(JSON.stringify(response));
97
+ }
98
+
99
+ async function postJson(url, payload) {
100
+ const response = await fetch(url, {
101
+ method: 'POST',
102
+ headers: {
103
+ 'Content-Type': 'application/json'
104
+ },
105
+ body: JSON.stringify(payload)
106
+ });
107
+
108
+ const text = await response.text();
109
+ if (!response.ok) {
110
+ throw new Error(`HTTP ${response.status}: ${text || response.statusText}`);
111
+ }
112
+
113
+ try {
114
+ return JSON.parse(text);
115
+ } catch (error) {
116
+ throw new Error(`Invalid JSON from AiyoPerps MCP endpoint: ${error.message}`);
117
+ }
118
+ }
119
+
120
+ function writeFrame(jsonText) {
121
+ const body = Buffer.from(jsonText, 'utf8');
122
+ const header = Buffer.from(`Content-Length: ${body.length}\r\n\r\n`, 'utf8');
123
+ stdout.write(Buffer.concat([header, body]));
124
+ }
125
+
126
+ function resolveEndpoint(args) {
127
+ const fallback = env.AIYOPERPS_MCP_URL || 'http://127.0.0.1:5078/mcp';
128
+
129
+ for (let index = 0; index < args.length; index += 1) {
130
+ const arg = args[index];
131
+ if (arg.startsWith('--url=')) {
132
+ return arg.slice('--url='.length);
133
+ }
134
+
135
+ if (arg === '--url' && index + 1 < args.length) {
136
+ return args[index + 1];
137
+ }
138
+ }
139
+
140
+ return fallback;
141
+ }
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@phidiassj/aiyoperps-mcp-bridge",
3
+ "version": "0.8.1",
4
+ "description": "Stdio MCP bridge for the AiyoPerps local HTTP MCP endpoint.",
5
+ "license": "MIT",
6
+ "bin": {
7
+ "aiyoperps-mcp-bridge": "./bin/aiyoperps-mcp-bridge.js"
8
+ },
9
+ "type": "commonjs",
10
+ "engines": {
11
+ "node": ">=18"
12
+ },
13
+ "files": [
14
+ "bin"
15
+ ],
16
+ "keywords": [
17
+ "mcp",
18
+ "model-context-protocol",
19
+ "aiyoperps",
20
+ "bridge",
21
+ "stdio"
22
+ ]
23
+ }