@ryuhq/sdk 0.1.14 → 0.2.0

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.
Files changed (90) hide show
  1. package/README.md +19 -6
  2. package/dist/agent-plugin.cjs +46 -0
  3. package/dist/agent-plugin.d.cts +42 -32
  4. package/dist/agent-plugin.d.ts +42 -32
  5. package/dist/agent-plugin.js +1 -1
  6. package/dist/agent.cjs +9 -0
  7. package/dist/agent.d.cts +377 -1
  8. package/dist/agent.d.ts +377 -1
  9. package/dist/agent.js +4 -2
  10. package/dist/app-Bkw7LlCK.d.ts +129 -0
  11. package/dist/app-DNaGmLVf.d.cts +129 -0
  12. package/dist/builder.cjs +1095 -0
  13. package/dist/builder.d.cts +212 -0
  14. package/dist/builder.d.ts +212 -0
  15. package/dist/builder.js +28 -0
  16. package/dist/chunk-A3RGEPDG.js +250 -0
  17. package/dist/chunk-FZSFZOIN.js +200 -0
  18. package/dist/{chunk-ODFEUVPW.js → chunk-HACAGK65.js} +1 -18
  19. package/dist/{chunk-AO2KJRDD.js → chunk-IEUQ3CDG.js} +125 -2
  20. package/dist/chunk-IKEDLLFY.js +19 -0
  21. package/dist/chunk-IOLP5FFE.js +354 -0
  22. package/dist/{chunk-G6FLVEC4.js → chunk-JX6DDRXV.js} +46 -0
  23. package/dist/chunk-T5676WL2.js +240 -0
  24. package/dist/chunk-TLDPEGC7.js +21 -0
  25. package/dist/chunk-TXSHHZF2.js +0 -0
  26. package/dist/{chunk-MTUBUPIV.js → chunk-ULSVL7EC.js} +8 -227
  27. package/dist/chunk-VLIRNNAE.js +154 -0
  28. package/dist/chunk-W3KPP4WN.js +135 -0
  29. package/dist/cli.cjs +258 -14
  30. package/dist/cli.js +96 -16
  31. package/dist/client-D5U6ssPc.d.cts +84 -0
  32. package/dist/client-D5U6ssPc.d.ts +84 -0
  33. package/dist/index.cjs +519 -5
  34. package/dist/index.d.cts +16 -632
  35. package/dist/index.d.ts +16 -632
  36. package/dist/index.js +63 -695
  37. package/dist/manifest.cjs +129 -3
  38. package/dist/manifest.d.cts +105 -2
  39. package/dist/manifest.d.ts +105 -2
  40. package/dist/manifest.js +7 -1
  41. package/dist/mcp/client.cjs +180 -0
  42. package/dist/mcp/client.d.cts +49 -0
  43. package/dist/mcp/client.d.ts +49 -0
  44. package/dist/mcp/client.js +10 -0
  45. package/dist/mcp/server.cjs +370 -0
  46. package/dist/mcp/server.d.cts +126 -0
  47. package/dist/mcp/server.d.ts +126 -0
  48. package/dist/mcp/server.js +9 -0
  49. package/dist/mcp.cjs +376 -0
  50. package/dist/mcp.d.cts +2 -0
  51. package/dist/mcp.d.ts +2 -0
  52. package/dist/mcp.js +17 -0
  53. package/dist/model.cjs +141 -0
  54. package/dist/model.d.cts +33 -0
  55. package/dist/model.d.ts +33 -0
  56. package/dist/model.js +18 -0
  57. package/dist/plugin.cjs +46 -0
  58. package/dist/plugin.d.cts +215 -0
  59. package/dist/plugin.d.ts +215 -0
  60. package/dist/plugin.js +8 -0
  61. package/dist/runnable.cjs +1230 -0
  62. package/dist/runnable.d.cts +271 -0
  63. package/dist/runnable.d.ts +271 -0
  64. package/dist/runnable.js +28 -0
  65. package/dist/{index-B6SkaAjJ.d.ts → tool-DSx2bFx8.d.ts} +35 -458
  66. package/dist/{index-BvAB5eMk.d.cts → tool-u-VR0fLF.d.cts} +35 -458
  67. package/package.json +38 -2
  68. package/src/agent/loop.test.ts +4 -4
  69. package/src/agent/tools.ts +3 -3
  70. package/src/agent-plugin.test.ts +58 -0
  71. package/src/agent-plugin.ts +108 -35
  72. package/src/cli/dev.test.ts +26 -47
  73. package/src/cli/dev.ts +10 -2
  74. package/src/cli.ts +98 -15
  75. package/src/exports-lockstep.test.ts +92 -0
  76. package/src/generated/plugin-manifest.ts +243 -27
  77. package/src/index.ts +35 -0
  78. package/src/manifest-schema.test.ts +30 -1
  79. package/src/manifest.fixtures.test.ts +13 -3
  80. package/src/manifest.test.ts +96 -10
  81. package/src/manifest.ts +334 -187
  82. package/src/mcp/index.ts +18 -0
  83. package/src/model/index.ts +22 -0
  84. package/src/plugin/ryu-plugin.ts +82 -0
  85. package/src/runnable/app.test.ts +2 -0
  86. package/src/runnable/app.ts +5 -2
  87. package/src/runnable/index.ts +2 -0
  88. package/src/runnable/primitives.ts +57 -0
  89. package/src/runnable/tool.ts +1 -1
  90. package/src/runnable/turn-hook.ts +4 -1
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/mcp/client.ts
21
+ var client_exports = {};
22
+ __export(client_exports, {
23
+ MCP_PROTOCOL_VERSION: () => MCP_PROTOCOL_VERSION,
24
+ callTool: () => callTool,
25
+ listTools: () => listTools
26
+ });
27
+ module.exports = __toCommonJS(client_exports);
28
+ var import_node_child_process = require("child_process");
29
+ var import_node_readline = require("readline");
30
+ var MCP_PROTOCOL_VERSION = "2024-11-05";
31
+ var RPC_TIMEOUT_MS = 6e4;
32
+ var McpConnection = class _McpConnection {
33
+ proc;
34
+ nextId = 1;
35
+ closed = false;
36
+ waiters = [];
37
+ constructor(proc) {
38
+ this.proc = proc;
39
+ }
40
+ /** Spawn the server and complete the MCP `initialize` handshake. */
41
+ static async connect(cmd) {
42
+ const env = { ...process.env, ...cmd.env ?? {} };
43
+ const proc = (0, import_node_child_process.spawn)(cmd.command, cmd.args ?? [], {
44
+ stdio: ["pipe", "pipe", "pipe"],
45
+ env
46
+ });
47
+ if (!(proc.stdin && proc.stdout)) {
48
+ proc.kill();
49
+ throw new Error(`MCP server '${cmd.command}' stdin/stdout unavailable`);
50
+ }
51
+ if (proc.stderr) {
52
+ proc.stderr.on("data", (chunk) => {
53
+ process.stderr.write(`[mcp-server] ${chunk.toString()}`);
54
+ });
55
+ }
56
+ const conn = new _McpConnection(proc);
57
+ const rl = (0, import_node_readline.createInterface)({
58
+ input: proc.stdout,
59
+ crlfDelay: Number.POSITIVE_INFINITY
60
+ });
61
+ rl.on("line", (rawLine) => {
62
+ const trimmed = rawLine.trim();
63
+ if (!trimmed) {
64
+ return;
65
+ }
66
+ let parsed;
67
+ try {
68
+ parsed = JSON.parse(trimmed);
69
+ } catch {
70
+ return;
71
+ }
72
+ if (parsed.id === void 0 || parsed.id === null) {
73
+ return;
74
+ }
75
+ const idx = conn.waiters.findIndex((w) => w.id === parsed.id);
76
+ if (idx === -1) {
77
+ return;
78
+ }
79
+ const [waiter] = conn.waiters.splice(idx, 1);
80
+ if (!waiter) {
81
+ return;
82
+ }
83
+ clearTimeout(waiter.timer);
84
+ if (parsed.error) {
85
+ waiter.reject(new Error(`MCP error: ${JSON.stringify(parsed.error)}`));
86
+ } else {
87
+ waiter.resolve(parsed.result ?? null);
88
+ }
89
+ });
90
+ rl.on("close", () => {
91
+ for (const waiter of conn.waiters.splice(0)) {
92
+ clearTimeout(waiter.timer);
93
+ waiter.reject(new Error("MCP server closed the connection"));
94
+ }
95
+ });
96
+ await conn.request("initialize", {
97
+ protocolVersion: MCP_PROTOCOL_VERSION,
98
+ capabilities: {},
99
+ clientInfo: { name: "ryu-sdk", version: "0.0.1" }
100
+ });
101
+ conn.notify("notifications/initialized", {});
102
+ return conn;
103
+ }
104
+ /** Send a JSON-RPC request and return the `result` field. */
105
+ request(method, params) {
106
+ const id = this.nextId++;
107
+ const frame = JSON.stringify({
108
+ jsonrpc: "2.0",
109
+ id,
110
+ method,
111
+ params
112
+ });
113
+ this.write(frame);
114
+ return new Promise((resolve, reject) => {
115
+ const timer = setTimeout(
116
+ () => reject(new Error(`MCP request '${method}' timed out`)),
117
+ RPC_TIMEOUT_MS
118
+ );
119
+ this.waiters.push({ id, resolve, reject, timer });
120
+ });
121
+ }
122
+ /** Send a JSON-RPC notification (no response expected). */
123
+ notify(method, params) {
124
+ const frame = JSON.stringify({ jsonrpc: "2.0", method, params });
125
+ this.write(frame);
126
+ }
127
+ write(frame) {
128
+ if (this.closed) {
129
+ return;
130
+ }
131
+ this.proc.stdin?.write(`${frame}
132
+ `);
133
+ }
134
+ /** Graceful shutdown: close stdin, then kill. */
135
+ async shutdown() {
136
+ this.closed = true;
137
+ this.proc.stdin?.end();
138
+ await new Promise((resolve) => {
139
+ this.proc.once("exit", () => resolve());
140
+ this.proc.kill();
141
+ setTimeout(resolve, 500);
142
+ });
143
+ }
144
+ };
145
+ async function listTools(cmd) {
146
+ const conn = await McpConnection.connect(cmd);
147
+ let result;
148
+ try {
149
+ result = await conn.request("tools/list", {});
150
+ } finally {
151
+ await conn.shutdown();
152
+ }
153
+ const tools = result?.tools ?? [];
154
+ return tools.map((t) => {
155
+ const tool = t;
156
+ const name = tool.name;
157
+ if (typeof name !== "string") {
158
+ return null;
159
+ }
160
+ return {
161
+ name,
162
+ description: typeof tool.description === "string" ? tool.description : void 0,
163
+ inputSchema: tool.inputSchema
164
+ };
165
+ }).filter((t) => t !== null);
166
+ }
167
+ async function callTool(cmd, tool, args) {
168
+ const conn = await McpConnection.connect(cmd);
169
+ try {
170
+ return await conn.request("tools/call", { name: tool, arguments: args });
171
+ } finally {
172
+ await conn.shutdown();
173
+ }
174
+ }
175
+ // Annotate the CommonJS export names for ESM import in node:
176
+ 0 && (module.exports = {
177
+ MCP_PROTOCOL_VERSION,
178
+ callTool,
179
+ listTools
180
+ });
@@ -0,0 +1,49 @@
1
+ /**
2
+ * SDK MCP stdio client — a TypeScript mirror of the wire contract in
3
+ * `apps/core/src/sidecar/mcp/client.rs`.
4
+ *
5
+ * This implements the same JSON-RPC 2.0 / newline-delimited transport:
6
+ * 1. Spawn the MCP server process.
7
+ * 2. Send `initialize` (protocolVersion "2024-11-05") and receive the result.
8
+ * 3. Send `notifications/initialized`.
9
+ * 4. Call `tools/list` or `tools/call` as needed.
10
+ * 5. Tear down the process.
11
+ *
12
+ * POLICY NOTE: this client does NOT implement tool-approval or request-level
13
+ * policy enforcement. Approval and policy live in the chat layer and the
14
+ * Gateway (per issue #86). Any policy that must run before a tool call must be
15
+ * wired upstream by the caller, not here.
16
+ */
17
+ /** MCP protocol version sent during `initialize`. Matches client.rs. */
18
+ declare const MCP_PROTOCOL_VERSION = "2024-11-05";
19
+ /** A tool entry from `tools/list`. */
20
+ interface McpTool {
21
+ description?: string;
22
+ /** JSON Schema object for the tool's input arguments. */
23
+ inputSchema?: unknown;
24
+ name: string;
25
+ }
26
+ /** Command descriptor for spawning an MCP stdio server. */
27
+ interface McpStdioCommand {
28
+ args?: string[];
29
+ command: string;
30
+ env?: Record<string, string>;
31
+ }
32
+ /**
33
+ * List the tools an MCP server advertises (`tools/list`).
34
+ *
35
+ * Spawns the server, completes the initialize handshake, calls `tools/list`,
36
+ * and tears down the process — matching the stateless per-request pattern in
37
+ * `apps/core/src/sidecar/mcp/client.rs`.
38
+ */
39
+ declare function listTools(cmd: McpStdioCommand): Promise<McpTool[]>;
40
+ /**
41
+ * Call a tool on an MCP server (`tools/call`) and return the raw result.
42
+ *
43
+ * The returned value is the full `tools/call` result object
44
+ * `{ content: [{type, text}], isError? }`. Callers that need the plain text
45
+ * output should extract `.content[0].text`.
46
+ */
47
+ declare function callTool(cmd: McpStdioCommand, tool: string, args: unknown): Promise<unknown>;
48
+
49
+ export { MCP_PROTOCOL_VERSION, type McpStdioCommand, type McpTool, callTool, listTools };
@@ -0,0 +1,49 @@
1
+ /**
2
+ * SDK MCP stdio client — a TypeScript mirror of the wire contract in
3
+ * `apps/core/src/sidecar/mcp/client.rs`.
4
+ *
5
+ * This implements the same JSON-RPC 2.0 / newline-delimited transport:
6
+ * 1. Spawn the MCP server process.
7
+ * 2. Send `initialize` (protocolVersion "2024-11-05") and receive the result.
8
+ * 3. Send `notifications/initialized`.
9
+ * 4. Call `tools/list` or `tools/call` as needed.
10
+ * 5. Tear down the process.
11
+ *
12
+ * POLICY NOTE: this client does NOT implement tool-approval or request-level
13
+ * policy enforcement. Approval and policy live in the chat layer and the
14
+ * Gateway (per issue #86). Any policy that must run before a tool call must be
15
+ * wired upstream by the caller, not here.
16
+ */
17
+ /** MCP protocol version sent during `initialize`. Matches client.rs. */
18
+ declare const MCP_PROTOCOL_VERSION = "2024-11-05";
19
+ /** A tool entry from `tools/list`. */
20
+ interface McpTool {
21
+ description?: string;
22
+ /** JSON Schema object for the tool's input arguments. */
23
+ inputSchema?: unknown;
24
+ name: string;
25
+ }
26
+ /** Command descriptor for spawning an MCP stdio server. */
27
+ interface McpStdioCommand {
28
+ args?: string[];
29
+ command: string;
30
+ env?: Record<string, string>;
31
+ }
32
+ /**
33
+ * List the tools an MCP server advertises (`tools/list`).
34
+ *
35
+ * Spawns the server, completes the initialize handshake, calls `tools/list`,
36
+ * and tears down the process — matching the stateless per-request pattern in
37
+ * `apps/core/src/sidecar/mcp/client.rs`.
38
+ */
39
+ declare function listTools(cmd: McpStdioCommand): Promise<McpTool[]>;
40
+ /**
41
+ * Call a tool on an MCP server (`tools/call`) and return the raw result.
42
+ *
43
+ * The returned value is the full `tools/call` result object
44
+ * `{ content: [{type, text}], isError? }`. Callers that need the plain text
45
+ * output should extract `.content[0].text`.
46
+ */
47
+ declare function callTool(cmd: McpStdioCommand, tool: string, args: unknown): Promise<unknown>;
48
+
49
+ export { MCP_PROTOCOL_VERSION, type McpStdioCommand, type McpTool, callTool, listTools };
@@ -0,0 +1,10 @@
1
+ import {
2
+ MCP_PROTOCOL_VERSION,
3
+ callTool,
4
+ listTools
5
+ } from "../chunk-VLIRNNAE.js";
6
+ export {
7
+ MCP_PROTOCOL_VERSION,
8
+ callTool,
9
+ listTools
10
+ };
@@ -0,0 +1,370 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/mcp/server.ts
21
+ var server_exports = {};
22
+ __export(server_exports, {
23
+ McpServer: () => McpServer,
24
+ unwrapContent: () => unwrapContent
25
+ });
26
+ module.exports = __toCommonJS(server_exports);
27
+ var import_node_readline2 = require("readline");
28
+
29
+ // src/mcp/client.ts
30
+ var import_node_child_process = require("child_process");
31
+ var import_node_readline = require("readline");
32
+ var MCP_PROTOCOL_VERSION = "2024-11-05";
33
+ var RPC_TIMEOUT_MS = 6e4;
34
+ var McpConnection = class _McpConnection {
35
+ proc;
36
+ nextId = 1;
37
+ closed = false;
38
+ waiters = [];
39
+ constructor(proc) {
40
+ this.proc = proc;
41
+ }
42
+ /** Spawn the server and complete the MCP `initialize` handshake. */
43
+ static async connect(cmd) {
44
+ const env = { ...process.env, ...cmd.env ?? {} };
45
+ const proc = (0, import_node_child_process.spawn)(cmd.command, cmd.args ?? [], {
46
+ stdio: ["pipe", "pipe", "pipe"],
47
+ env
48
+ });
49
+ if (!(proc.stdin && proc.stdout)) {
50
+ proc.kill();
51
+ throw new Error(`MCP server '${cmd.command}' stdin/stdout unavailable`);
52
+ }
53
+ if (proc.stderr) {
54
+ proc.stderr.on("data", (chunk) => {
55
+ process.stderr.write(`[mcp-server] ${chunk.toString()}`);
56
+ });
57
+ }
58
+ const conn = new _McpConnection(proc);
59
+ const rl = (0, import_node_readline.createInterface)({
60
+ input: proc.stdout,
61
+ crlfDelay: Number.POSITIVE_INFINITY
62
+ });
63
+ rl.on("line", (rawLine) => {
64
+ const trimmed = rawLine.trim();
65
+ if (!trimmed) {
66
+ return;
67
+ }
68
+ let parsed;
69
+ try {
70
+ parsed = JSON.parse(trimmed);
71
+ } catch {
72
+ return;
73
+ }
74
+ if (parsed.id === void 0 || parsed.id === null) {
75
+ return;
76
+ }
77
+ const idx = conn.waiters.findIndex((w) => w.id === parsed.id);
78
+ if (idx === -1) {
79
+ return;
80
+ }
81
+ const [waiter] = conn.waiters.splice(idx, 1);
82
+ if (!waiter) {
83
+ return;
84
+ }
85
+ clearTimeout(waiter.timer);
86
+ if (parsed.error) {
87
+ waiter.reject(new Error(`MCP error: ${JSON.stringify(parsed.error)}`));
88
+ } else {
89
+ waiter.resolve(parsed.result ?? null);
90
+ }
91
+ });
92
+ rl.on("close", () => {
93
+ for (const waiter of conn.waiters.splice(0)) {
94
+ clearTimeout(waiter.timer);
95
+ waiter.reject(new Error("MCP server closed the connection"));
96
+ }
97
+ });
98
+ await conn.request("initialize", {
99
+ protocolVersion: MCP_PROTOCOL_VERSION,
100
+ capabilities: {},
101
+ clientInfo: { name: "ryu-sdk", version: "0.0.1" }
102
+ });
103
+ conn.notify("notifications/initialized", {});
104
+ return conn;
105
+ }
106
+ /** Send a JSON-RPC request and return the `result` field. */
107
+ request(method, params) {
108
+ const id = this.nextId++;
109
+ const frame = JSON.stringify({
110
+ jsonrpc: "2.0",
111
+ id,
112
+ method,
113
+ params
114
+ });
115
+ this.write(frame);
116
+ return new Promise((resolve, reject) => {
117
+ const timer = setTimeout(
118
+ () => reject(new Error(`MCP request '${method}' timed out`)),
119
+ RPC_TIMEOUT_MS
120
+ );
121
+ this.waiters.push({ id, resolve, reject, timer });
122
+ });
123
+ }
124
+ /** Send a JSON-RPC notification (no response expected). */
125
+ notify(method, params) {
126
+ const frame = JSON.stringify({ jsonrpc: "2.0", method, params });
127
+ this.write(frame);
128
+ }
129
+ write(frame) {
130
+ if (this.closed) {
131
+ return;
132
+ }
133
+ this.proc.stdin?.write(`${frame}
134
+ `);
135
+ }
136
+ /** Graceful shutdown: close stdin, then kill. */
137
+ async shutdown() {
138
+ this.closed = true;
139
+ this.proc.stdin?.end();
140
+ await new Promise((resolve) => {
141
+ this.proc.once("exit", () => resolve());
142
+ this.proc.kill();
143
+ setTimeout(resolve, 500);
144
+ });
145
+ }
146
+ };
147
+ async function listTools(cmd) {
148
+ const conn = await McpConnection.connect(cmd);
149
+ let result;
150
+ try {
151
+ result = await conn.request("tools/list", {});
152
+ } finally {
153
+ await conn.shutdown();
154
+ }
155
+ const tools = result?.tools ?? [];
156
+ return tools.map((t) => {
157
+ const tool = t;
158
+ const name = tool.name;
159
+ if (typeof name !== "string") {
160
+ return null;
161
+ }
162
+ return {
163
+ name,
164
+ description: typeof tool.description === "string" ? tool.description : void 0,
165
+ inputSchema: tool.inputSchema
166
+ };
167
+ }).filter((t) => t !== null);
168
+ }
169
+ async function callTool(cmd, tool, args) {
170
+ const conn = await McpConnection.connect(cmd);
171
+ try {
172
+ return await conn.request("tools/call", { name: tool, arguments: args });
173
+ } finally {
174
+ await conn.shutdown();
175
+ }
176
+ }
177
+
178
+ // src/mcp/server.ts
179
+ function respond(id, result) {
180
+ return { jsonrpc: "2.0", id: id ?? null, result };
181
+ }
182
+ function respondError(id, code, message) {
183
+ return { jsonrpc: "2.0", id: id ?? null, error: { code, message } };
184
+ }
185
+ function wrapContent(value) {
186
+ return {
187
+ content: [
188
+ {
189
+ type: "text",
190
+ text: typeof value === "string" ? value : JSON.stringify(value)
191
+ }
192
+ ]
193
+ };
194
+ }
195
+ function unwrapContent(raw) {
196
+ const r = raw;
197
+ const text = r?.content?.[0]?.text;
198
+ if (text === void 0) {
199
+ return raw;
200
+ }
201
+ try {
202
+ return JSON.parse(text);
203
+ } catch {
204
+ return text;
205
+ }
206
+ }
207
+ var McpServer = class {
208
+ runnables = /* @__PURE__ */ new Map();
209
+ passthroughs = [];
210
+ /**
211
+ * Register an SDK Runnable as an MCP tool.
212
+ * Returns `this` for chaining.
213
+ */
214
+ register(runnable) {
215
+ this.runnables.set(runnable.name, runnable);
216
+ return this;
217
+ }
218
+ /**
219
+ * Register a passthrough to an external MCP stdio server (e.g. Ghost or
220
+ * Shadow). Tools from that server are fetched lazily and re-advertised.
221
+ * Returns `this` for chaining.
222
+ */
223
+ passthrough(registration) {
224
+ this.passthroughs.push(registration);
225
+ return this;
226
+ }
227
+ /** Fetch all tools: local Runnables + passthrough tools. */
228
+ async allTools() {
229
+ const local = [...this.runnables.values()].map((r) => ({
230
+ name: r.name,
231
+ description: r.description,
232
+ inputSchema: r.inputSchema
233
+ }));
234
+ const remote = [];
235
+ for (const pt of this.passthroughs) {
236
+ try {
237
+ const tools = await listTools(pt.command);
238
+ remote.push(...tools);
239
+ } catch (err) {
240
+ process.stderr.write(
241
+ `[mcp-server] passthrough '${pt.label}' list_tools failed: ${err}
242
+ `
243
+ );
244
+ }
245
+ }
246
+ return [...local, ...remote];
247
+ }
248
+ /** Handle a `tools/call` request. */
249
+ async handleCallTool(name, args) {
250
+ const local = this.runnables.get(name);
251
+ if (local) {
252
+ const result = await local.run(args);
253
+ return wrapContent(result);
254
+ }
255
+ for (const pt of this.passthroughs) {
256
+ try {
257
+ const tools = await listTools(pt.command);
258
+ if (tools.some((t) => t.name === name)) {
259
+ return await callTool(pt.command, name, args);
260
+ }
261
+ } catch {
262
+ }
263
+ }
264
+ throw new Error(`Unknown tool: ${name}`);
265
+ }
266
+ /**
267
+ * Handle a single parsed JSON-RPC request line. Returns the response to write
268
+ * (or null for notifications that require no response).
269
+ */
270
+ async handleRequest(req, initialized, write) {
271
+ const { id, method, params } = req;
272
+ if (method === "initialize") {
273
+ initialized.value = true;
274
+ write(
275
+ respond(id, {
276
+ protocolVersion: MCP_PROTOCOL_VERSION,
277
+ capabilities: { tools: {} },
278
+ serverInfo: { name: "ryu-sdk-server", version: "0.0.1" }
279
+ })
280
+ );
281
+ return;
282
+ }
283
+ if (method === "notifications/initialized") {
284
+ return;
285
+ }
286
+ if (!initialized.value) {
287
+ write(respondError(id, -32002, "Server not initialized"));
288
+ return;
289
+ }
290
+ if (method === "tools/list") {
291
+ try {
292
+ const tools = await this.allTools();
293
+ write(respond(id, { tools }));
294
+ } catch (err) {
295
+ write(respondError(id, -32603, String(err)));
296
+ }
297
+ return;
298
+ }
299
+ if (method === "tools/call") {
300
+ await this.handleToolsCall(id, params, write);
301
+ return;
302
+ }
303
+ write(respondError(id, -32601, `Method not found: ${method}`));
304
+ }
305
+ /** Handle a `tools/call` JSON-RPC request. */
306
+ async handleToolsCall(id, params, write) {
307
+ const p = params;
308
+ const toolName = p?.name;
309
+ const toolArgs = p?.arguments ?? {};
310
+ if (typeof toolName !== "string") {
311
+ write(respondError(id, -32602, "tools/call requires 'name'"));
312
+ return;
313
+ }
314
+ try {
315
+ const result = await this.handleCallTool(toolName, toolArgs);
316
+ write(respond(id, result));
317
+ } catch (err) {
318
+ write(
319
+ respond(id, {
320
+ content: [{ type: "text", text: String(err) }],
321
+ isError: true
322
+ })
323
+ );
324
+ }
325
+ }
326
+ /**
327
+ * Start reading JSON-RPC requests from the given readable stream and writing
328
+ * responses to the given writable stream.
329
+ *
330
+ * Defaults to `process.stdin` / `process.stdout`. Passing explicit streams
331
+ * lets tests inject a pair of in-process pipes.
332
+ *
333
+ * Resolves when the input stream ends (EOF).
334
+ */
335
+ serve(input = process.stdin, output = process.stdout) {
336
+ const write = (obj) => {
337
+ output.write(`${JSON.stringify(obj)}
338
+ `);
339
+ };
340
+ const initialized = { value: false };
341
+ return new Promise((resolve) => {
342
+ const rl = (0, import_node_readline2.createInterface)({
343
+ input,
344
+ crlfDelay: Number.POSITIVE_INFINITY
345
+ });
346
+ rl.on("line", (rawLine) => {
347
+ const line = rawLine.trim();
348
+ if (!line) {
349
+ return;
350
+ }
351
+ let req;
352
+ try {
353
+ req = JSON.parse(line);
354
+ } catch {
355
+ write(respondError(null, -32700, "Parse error"));
356
+ return;
357
+ }
358
+ this.handleRequest(req, initialized, write).catch((err) => {
359
+ write(respondError(req.id, -32603, String(err)));
360
+ });
361
+ });
362
+ rl.on("close", resolve);
363
+ });
364
+ }
365
+ };
366
+ // Annotate the CommonJS export names for ESM import in node:
367
+ 0 && (module.exports = {
368
+ McpServer,
369
+ unwrapContent
370
+ });