waypoi 0.0.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 (260) hide show
  1. package/.github/instructions/ui.instructions.md +42 -0
  2. package/.github/workflows/ci.yml +35 -0
  3. package/.github/workflows/publish.yml +71 -0
  4. package/.github/workflows/release.yml +48 -0
  5. package/.playwright-mcp/console-2026-04-04T01-41-10-746Z.log +2 -0
  6. package/.playwright-mcp/console-2026-04-04T01-41-28-799Z.log +3 -0
  7. package/.playwright-mcp/console-2026-04-05T02-26-51-909Z.log +76 -0
  8. package/.playwright-mcp/page-2026-04-04T01-41-10-816Z.yml +1 -0
  9. package/.playwright-mcp/page-2026-04-04T01-41-29-141Z.yml +77 -0
  10. package/.playwright-mcp/page-2026-04-04T01-41-42-633Z.yml +190 -0
  11. package/.playwright-mcp/page-2026-04-04T01-42-03-929Z.yml +262 -0
  12. package/.playwright-mcp/page-2026-04-04T02-12-54-813Z.yml +6 -0
  13. package/.playwright-mcp/page-2026-04-04T02-14-58-600Z.yml +190 -0
  14. package/.playwright-mcp/page-2026-04-04T02-15-03-923Z.yml +190 -0
  15. package/.playwright-mcp/page-2026-04-04T02-15-07-426Z.yml +190 -0
  16. package/.playwright-mcp/page-2026-04-04T02-15-25-729Z.yml +262 -0
  17. package/.playwright-mcp/page-2026-04-04T02-16-22-984Z.yml +262 -0
  18. package/.playwright-mcp/page-2026-04-04T02-17-00-599Z.yml +190 -0
  19. package/.playwright-mcp/page-2026-04-04T02-17-50-874Z.yml +190 -0
  20. package/.playwright-mcp/page-2026-04-05T02-26-55-570Z.yml +6 -0
  21. package/AGENTS.md +48 -0
  22. package/CHANGELOG.md +131 -0
  23. package/README.md +552 -0
  24. package/assets/agent-mode.png +0 -0
  25. package/assets/categorize.png +0 -0
  26. package/assets/dashboard.png +0 -0
  27. package/assets/endpoint-proxy.png +0 -0
  28. package/assets/icon.png +0 -0
  29. package/assets/mcp-generate-image.png +0 -0
  30. package/assets/mcp-understand-image.png +0 -0
  31. package/assets/peek-token-flow.png +0 -0
  32. package/assets/playground.png +0 -0
  33. package/assets/sankey.png +0 -0
  34. package/cli/index.ts +2805 -0
  35. package/cli/legacyRewrite.ts +108 -0
  36. package/cli/modelRef.ts +24 -0
  37. package/dist/cli/index.js +2536 -0
  38. package/dist/cli/legacyRewrite.js +92 -0
  39. package/dist/cli/modelRef.js +20 -0
  40. package/dist/src/benchmark/artifacts.js +131 -0
  41. package/dist/src/benchmark/capabilityClassifier.js +81 -0
  42. package/dist/src/benchmark/capabilityStore.js +144 -0
  43. package/dist/src/benchmark/config.js +238 -0
  44. package/dist/src/benchmark/gates.js +118 -0
  45. package/dist/src/benchmark/jobs.js +252 -0
  46. package/dist/src/benchmark/runner.js +1847 -0
  47. package/dist/src/benchmark/schema.js +353 -0
  48. package/dist/src/benchmark/suites.js +314 -0
  49. package/dist/src/benchmark/tinyQaDataset.js +422 -0
  50. package/dist/src/benchmark/types.js +25 -0
  51. package/dist/src/config.js +47 -0
  52. package/dist/src/index.js +178 -0
  53. package/dist/src/mcp/client.js +215 -0
  54. package/dist/src/mcp/discovery.js +226 -0
  55. package/dist/src/mcp/policy.js +65 -0
  56. package/dist/src/mcp/registry.js +129 -0
  57. package/dist/src/mcp/service.js +460 -0
  58. package/dist/src/middleware/auth.js +179 -0
  59. package/dist/src/middleware/requestCapture.js +192 -0
  60. package/dist/src/middleware/requestStats.js +118 -0
  61. package/dist/src/pools/builder.js +132 -0
  62. package/dist/src/pools/repository.js +69 -0
  63. package/dist/src/pools/scheduler.js +360 -0
  64. package/dist/src/pools/types.js +2 -0
  65. package/dist/src/protocols/adapters/dashscope.js +267 -0
  66. package/dist/src/protocols/adapters/inferenceV2.js +346 -0
  67. package/dist/src/protocols/adapters/openai.js +27 -0
  68. package/dist/src/protocols/registry.js +99 -0
  69. package/dist/src/protocols/types.js +2 -0
  70. package/dist/src/providers/health.js +153 -0
  71. package/dist/src/providers/importer.js +289 -0
  72. package/dist/src/providers/modelRegistry.js +313 -0
  73. package/dist/src/providers/repository.js +361 -0
  74. package/dist/src/providers/types.js +2 -0
  75. package/dist/src/routes/admin.js +531 -0
  76. package/dist/src/routes/audio.js +295 -0
  77. package/dist/src/routes/chat.js +240 -0
  78. package/dist/src/routes/embeddings.js +157 -0
  79. package/dist/src/routes/images.js +288 -0
  80. package/dist/src/routes/mcp.js +256 -0
  81. package/dist/src/routes/mcpService.js +100 -0
  82. package/dist/src/routes/models.js +48 -0
  83. package/dist/src/routes/responses.js +711 -0
  84. package/dist/src/routes/sessions.js +450 -0
  85. package/dist/src/routes/stats.js +270 -0
  86. package/dist/src/routes/ui.js +97 -0
  87. package/dist/src/routes/videos.js +107 -0
  88. package/dist/src/routing/router.js +338 -0
  89. package/dist/src/services/imageGeneration.js +280 -0
  90. package/dist/src/services/imageUnderstanding.js +352 -0
  91. package/dist/src/services/videoGeneration.js +79 -0
  92. package/dist/src/storage/captureRepository.js +1591 -0
  93. package/dist/src/storage/files.js +157 -0
  94. package/dist/src/storage/imageCache.js +346 -0
  95. package/dist/src/storage/repositories.js +388 -0
  96. package/dist/src/storage/sessionRepository.js +370 -0
  97. package/dist/src/storage/statsRepository.js +204 -0
  98. package/dist/src/transport/httpClient.js +126 -0
  99. package/dist/src/types.js +2 -0
  100. package/dist/src/utils/messageMedia.js +285 -0
  101. package/dist/src/utils/modelCapabilities.js +108 -0
  102. package/dist/src/utils/modelDiscovery.js +170 -0
  103. package/dist/src/version.js +5 -0
  104. package/dist/src/workers/captureRetention.js +25 -0
  105. package/dist/src/workers/configWatcher.js +91 -0
  106. package/dist/src/workers/healthChecker.js +21 -0
  107. package/dist/src/workers/statsRotation.js +41 -0
  108. package/docs/LLM/output_schema.md +312 -0
  109. package/docs/benchmark.md +208 -0
  110. package/docs/mcp-guidelines.md +125 -0
  111. package/docs/mcp-service.md +178 -0
  112. package/docs/opencode.md +86 -0
  113. package/docs/providers.md +79 -0
  114. package/examples/benchmark.config.yaml +28 -0
  115. package/examples/providers/alibaba-dashscope.yaml +88 -0
  116. package/examples/providers/alibaba-llm.yaml +64 -0
  117. package/examples/providers/alibaba-registry.yaml +7 -0
  118. package/examples/providers/inference-v2-ray.yaml +29 -0
  119. package/examples/scenarios/assets/omni-call-sample.wav +0 -0
  120. package/examples/scenarios/custom.jsonl +5 -0
  121. package/examples/scenarios/custom.yaml +40 -0
  122. package/model-form-v2.png +0 -0
  123. package/package.json +66 -0
  124. package/provider-form-v2.png +0 -0
  125. package/provider-form.png +0 -0
  126. package/scripts/manual-test.sh +11 -0
  127. package/scripts/version-from-git.js +23 -0
  128. package/src/benchmark/artifacts.ts +149 -0
  129. package/src/benchmark/capabilityClassifier.ts +99 -0
  130. package/src/benchmark/capabilityStore.ts +174 -0
  131. package/src/benchmark/config.ts +337 -0
  132. package/src/benchmark/gates.ts +164 -0
  133. package/src/benchmark/jobs.ts +312 -0
  134. package/src/benchmark/runner.ts +2519 -0
  135. package/src/benchmark/schema.ts +443 -0
  136. package/src/benchmark/suites.ts +323 -0
  137. package/src/benchmark/tinyQaDataset.ts +428 -0
  138. package/src/benchmark/types.ts +442 -0
  139. package/src/config.ts +44 -0
  140. package/src/index.ts +195 -0
  141. package/src/mcp/client.ts +305 -0
  142. package/src/mcp/discovery.ts +266 -0
  143. package/src/mcp/policy.ts +105 -0
  144. package/src/mcp/registry.ts +164 -0
  145. package/src/mcp/service.ts +611 -0
  146. package/src/middleware/auth.ts +251 -0
  147. package/src/middleware/requestCapture.ts +245 -0
  148. package/src/middleware/requestStats.ts +163 -0
  149. package/src/pools/builder.ts +159 -0
  150. package/src/pools/repository.ts +71 -0
  151. package/src/pools/scheduler.ts +425 -0
  152. package/src/pools/types.ts +117 -0
  153. package/src/protocols/adapters/dashscope.ts +335 -0
  154. package/src/protocols/adapters/inferenceV2.ts +428 -0
  155. package/src/protocols/adapters/openai.ts +32 -0
  156. package/src/protocols/registry.ts +117 -0
  157. package/src/protocols/types.ts +81 -0
  158. package/src/providers/health.ts +207 -0
  159. package/src/providers/importer.ts +402 -0
  160. package/src/providers/modelRegistry.ts +415 -0
  161. package/src/providers/repository.ts +439 -0
  162. package/src/providers/types.ts +113 -0
  163. package/src/routes/admin.ts +666 -0
  164. package/src/routes/audio.ts +372 -0
  165. package/src/routes/chat.ts +301 -0
  166. package/src/routes/embeddings.ts +197 -0
  167. package/src/routes/images.ts +356 -0
  168. package/src/routes/mcp.ts +320 -0
  169. package/src/routes/mcpService.ts +114 -0
  170. package/src/routes/models.ts +50 -0
  171. package/src/routes/responses.ts +872 -0
  172. package/src/routes/sessions.ts +558 -0
  173. package/src/routes/stats.ts +312 -0
  174. package/src/routes/ui.ts +96 -0
  175. package/src/routes/videos.ts +132 -0
  176. package/src/routing/router.ts +501 -0
  177. package/src/services/imageGeneration.ts +396 -0
  178. package/src/services/imageUnderstanding.ts +449 -0
  179. package/src/services/videoGeneration.ts +127 -0
  180. package/src/storage/captureRepository.ts +1835 -0
  181. package/src/storage/files.ts +178 -0
  182. package/src/storage/imageCache.ts +405 -0
  183. package/src/storage/repositories.ts +494 -0
  184. package/src/storage/sessionRepository.ts +419 -0
  185. package/src/storage/statsRepository.ts +238 -0
  186. package/src/transport/httpClient.ts +145 -0
  187. package/src/types.ts +322 -0
  188. package/src/utils/messageMedia.ts +293 -0
  189. package/src/utils/modelCapabilities.ts +161 -0
  190. package/src/utils/modelDiscovery.ts +203 -0
  191. package/src/workers/captureRetention.ts +25 -0
  192. package/src/workers/configWatcher.ts +115 -0
  193. package/src/workers/healthChecker.ts +22 -0
  194. package/src/workers/statsRotation.ts +49 -0
  195. package/tests/benchmarkAdminRoutes.test.ts +82 -0
  196. package/tests/benchmarkBasics.test.ts +116 -0
  197. package/tests/captureAdminRoutes.test.ts +420 -0
  198. package/tests/captureRepository.test.ts +797 -0
  199. package/tests/cliLegacyRewrite.test.ts +45 -0
  200. package/tests/imageGeneration.service.test.ts +107 -0
  201. package/tests/imageUnderstanding.service.test.ts +123 -0
  202. package/tests/mcpPolicy.test.ts +105 -0
  203. package/tests/mcpService.test.ts +1245 -0
  204. package/tests/modelRef.test.ts +23 -0
  205. package/tests/modelsRoutes.test.ts +154 -0
  206. package/tests/sessionMediaCache.test.ts +167 -0
  207. package/tests/statsRoutes.test.ts +323 -0
  208. package/tsconfig.json +15 -0
  209. package/ui/index.html +16 -0
  210. package/ui/package-lock.json +8521 -0
  211. package/ui/package.json +52 -0
  212. package/ui/postcss.config.js +6 -0
  213. package/ui/public/assets/apple-touch-icon.png +0 -0
  214. package/ui/public/assets/favicon-16.png +0 -0
  215. package/ui/public/assets/favicon-32.png +0 -0
  216. package/ui/public/assets/icon-192.png +0 -0
  217. package/ui/public/assets/icon-512.png +0 -0
  218. package/ui/src/App.tsx +27 -0
  219. package/ui/src/api/client.ts +1503 -0
  220. package/ui/src/components/EndpointUsageGuide.tsx +361 -0
  221. package/ui/src/components/Layout.tsx +124 -0
  222. package/ui/src/components/MessageContent.tsx +365 -0
  223. package/ui/src/components/ToolCallMessage.tsx +179 -0
  224. package/ui/src/components/ToolPicker.tsx +442 -0
  225. package/ui/src/components/messageContentParser.test.ts +41 -0
  226. package/ui/src/components/messageContentParser.ts +73 -0
  227. package/ui/src/components/thinkingPreview.test.ts +27 -0
  228. package/ui/src/components/thinkingPreview.ts +15 -0
  229. package/ui/src/components/toMermaidSankey.test.ts +78 -0
  230. package/ui/src/components/toMermaidSankey.ts +56 -0
  231. package/ui/src/components/ui/button.tsx +58 -0
  232. package/ui/src/components/ui/input.tsx +21 -0
  233. package/ui/src/components/ui/textarea.tsx +21 -0
  234. package/ui/src/lib/utils.ts +6 -0
  235. package/ui/src/main.tsx +9 -0
  236. package/ui/src/pages/AgentPlayground.tsx +2010 -0
  237. package/ui/src/pages/Benchmark.tsx +988 -0
  238. package/ui/src/pages/Dashboard.tsx +581 -0
  239. package/ui/src/pages/Peek.tsx +962 -0
  240. package/ui/src/pages/Settings.tsx +2013 -0
  241. package/ui/src/pages/agentPlaygroundPayload.test.ts +109 -0
  242. package/ui/src/pages/agentPlaygroundPayload.ts +97 -0
  243. package/ui/src/pages/agentThinkingContent.test.ts +50 -0
  244. package/ui/src/pages/agentThinkingContent.ts +57 -0
  245. package/ui/src/pages/dashboardTokenUsage.test.ts +66 -0
  246. package/ui/src/pages/dashboardTokenUsage.ts +36 -0
  247. package/ui/src/pages/imageUpload.test.ts +39 -0
  248. package/ui/src/pages/imageUpload.ts +71 -0
  249. package/ui/src/pages/peekFilters.test.ts +29 -0
  250. package/ui/src/pages/peekFilters.ts +13 -0
  251. package/ui/src/pages/peekMedia.test.ts +58 -0
  252. package/ui/src/pages/peekMedia.ts +148 -0
  253. package/ui/src/pages/sessionAutoTitle.test.ts +128 -0
  254. package/ui/src/pages/sessionAutoTitle.ts +106 -0
  255. package/ui/src/stores/settings.ts +58 -0
  256. package/ui/src/styles/globals.css +223 -0
  257. package/ui/src/vite-env.d.ts +8 -0
  258. package/ui/tailwind.config.js +106 -0
  259. package/ui/tsconfig.json +32 -0
  260. package/ui/vite.config.ts +37 -0
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.McpClient = exports.McpError = void 0;
4
+ exports.createMcpClient = createMcpClient;
5
+ class McpError extends Error {
6
+ code;
7
+ data;
8
+ constructor(code, message, data) {
9
+ super(message);
10
+ this.code = code;
11
+ this.data = data;
12
+ this.name = "McpError";
13
+ }
14
+ }
15
+ exports.McpError = McpError;
16
+ class McpClient {
17
+ url;
18
+ timeout;
19
+ requestId = 0;
20
+ sessionId = null;
21
+ constructor(options) {
22
+ this.url = options.url;
23
+ this.timeout = options.timeout ?? 30000;
24
+ }
25
+ /**
26
+ * Initialize the MCP connection.
27
+ */
28
+ async initialize() {
29
+ const response = await this.sendRequest("initialize", {
30
+ protocolVersion: "2024-11-05",
31
+ capabilities: {
32
+ tools: {},
33
+ },
34
+ clientInfo: {
35
+ name: "waypoi",
36
+ version: "0.2.0",
37
+ },
38
+ });
39
+ // Store session ID if provided
40
+ const initResponse = response;
41
+ if (initResponse._meta?.sessionId) {
42
+ this.sessionId = initResponse._meta.sessionId;
43
+ }
44
+ // Send initialized notification
45
+ await this.sendNotification("notifications/initialized");
46
+ }
47
+ /**
48
+ * List available tools from the MCP server.
49
+ */
50
+ async listTools() {
51
+ const result = await this.sendRequest("tools/list", {});
52
+ const toolsResult = result;
53
+ return toolsResult.tools ?? [];
54
+ }
55
+ /**
56
+ * Call a tool on the MCP server.
57
+ */
58
+ async callTool(name, args) {
59
+ const result = await this.sendRequest("tools/call", {
60
+ name,
61
+ arguments: args,
62
+ });
63
+ return result;
64
+ }
65
+ /**
66
+ * Close the MCP connection.
67
+ */
68
+ async close() {
69
+ try {
70
+ await this.sendNotification("notifications/cancelled", {
71
+ requestId: "session",
72
+ reason: "Client closing",
73
+ });
74
+ }
75
+ catch {
76
+ // Ignore errors during close
77
+ }
78
+ this.sessionId = null;
79
+ }
80
+ /**
81
+ * Send a JSON-RPC request and wait for response.
82
+ */
83
+ async sendRequest(method, params) {
84
+ const id = ++this.requestId;
85
+ const request = {
86
+ jsonrpc: "2.0",
87
+ id,
88
+ method,
89
+ params,
90
+ };
91
+ const controller = new AbortController();
92
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
93
+ try {
94
+ // MCP Streamable HTTP spec requires accepting both JSON and SSE
95
+ const headers = {
96
+ "Content-Type": "application/json",
97
+ "Accept": "application/json, text/event-stream",
98
+ };
99
+ if (this.sessionId) {
100
+ headers["Mcp-Session-Id"] = this.sessionId;
101
+ }
102
+ const response = await fetch(this.url, {
103
+ method: "POST",
104
+ headers,
105
+ body: JSON.stringify(request),
106
+ signal: controller.signal,
107
+ });
108
+ if (!response.ok) {
109
+ throw new McpError(-32000, `HTTP error: ${response.status} ${response.statusText}`);
110
+ }
111
+ // Check for session ID in response headers
112
+ const newSessionId = response.headers.get("Mcp-Session-Id");
113
+ if (newSessionId) {
114
+ this.sessionId = newSessionId;
115
+ }
116
+ // Handle both JSON and SSE responses per MCP Streamable HTTP spec
117
+ const contentType = response.headers.get("Content-Type") ?? "";
118
+ if (contentType.includes("text/event-stream")) {
119
+ // Parse SSE response - collect events and find our response
120
+ return await this.parseSSEResponse(response, id);
121
+ }
122
+ const jsonResponse = await response.json();
123
+ if (jsonResponse.error) {
124
+ throw new McpError(jsonResponse.error.code, jsonResponse.error.message, jsonResponse.error.data);
125
+ }
126
+ return jsonResponse.result;
127
+ }
128
+ finally {
129
+ clearTimeout(timeoutId);
130
+ }
131
+ }
132
+ /**
133
+ * Parse Server-Sent Events response to extract JSON-RPC response.
134
+ * MCP Streamable HTTP can return SSE for streaming responses.
135
+ */
136
+ async parseSSEResponse(response, expectedId) {
137
+ const text = await response.text();
138
+ const lines = text.split("\n");
139
+ let currentData = "";
140
+ for (const line of lines) {
141
+ if (line.startsWith("data:")) {
142
+ currentData += line.slice(5).trim();
143
+ }
144
+ else if (line === "" && currentData) {
145
+ // End of event, try to parse
146
+ try {
147
+ const jsonResponse = JSON.parse(currentData);
148
+ // Check if this is the response to our request
149
+ if (jsonResponse.id === expectedId) {
150
+ if (jsonResponse.error) {
151
+ throw new McpError(jsonResponse.error.code, jsonResponse.error.message, jsonResponse.error.data);
152
+ }
153
+ return jsonResponse.result;
154
+ }
155
+ }
156
+ catch (e) {
157
+ if (e instanceof McpError)
158
+ throw e;
159
+ // Ignore parsing errors, continue to next event
160
+ }
161
+ currentData = "";
162
+ }
163
+ }
164
+ // Try parsing any remaining data
165
+ if (currentData) {
166
+ try {
167
+ const jsonResponse = JSON.parse(currentData);
168
+ if (jsonResponse.id === expectedId) {
169
+ if (jsonResponse.error) {
170
+ throw new McpError(jsonResponse.error.code, jsonResponse.error.message, jsonResponse.error.data);
171
+ }
172
+ return jsonResponse.result;
173
+ }
174
+ }
175
+ catch (e) {
176
+ if (e instanceof McpError)
177
+ throw e;
178
+ }
179
+ }
180
+ throw new McpError(-32000, "No response found in SSE stream");
181
+ }
182
+ /**
183
+ * Send a JSON-RPC notification (no response expected).
184
+ */
185
+ async sendNotification(method, params) {
186
+ const headers = {
187
+ "Content-Type": "application/json",
188
+ };
189
+ if (this.sessionId) {
190
+ headers["Mcp-Session-Id"] = this.sessionId;
191
+ }
192
+ const notification = {
193
+ jsonrpc: "2.0",
194
+ method,
195
+ params,
196
+ };
197
+ await fetch(this.url, {
198
+ method: "POST",
199
+ headers,
200
+ body: JSON.stringify(notification),
201
+ });
202
+ }
203
+ get serverUrl() {
204
+ return this.url;
205
+ }
206
+ }
207
+ exports.McpClient = McpClient;
208
+ /**
209
+ * Create and initialize an MCP client.
210
+ */
211
+ async function createMcpClient(url) {
212
+ const client = new McpClient({ url });
213
+ await client.initialize();
214
+ return client;
215
+ }
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BUILTIN_SERVER_ID = void 0;
4
+ exports.discoverBuiltinTools = discoverBuiltinTools;
5
+ exports.getBuiltinVirtualServer = getBuiltinVirtualServer;
6
+ exports.discoverServerTools = discoverServerTools;
7
+ exports.discoverAllTools = discoverAllTools;
8
+ exports.summarizeMcpError = summarizeMcpError;
9
+ exports.getCachedTools = getCachedTools;
10
+ exports.getCachedToolsForServer = getCachedToolsForServer;
11
+ exports.findTool = findTool;
12
+ exports.executeTool = executeTool;
13
+ exports.disconnectServer = disconnectServer;
14
+ exports.disconnectAllServers = disconnectAllServers;
15
+ exports.isServerConnected = isServerConnected;
16
+ const registry_1 = require("./registry");
17
+ const client_1 = require("./client");
18
+ // In-memory cache of discovered tools
19
+ const toolsCache = new Map();
20
+ // Active client connections
21
+ const activeClients = new Map();
22
+ // ─────────────────────────────────────────────────────────────────────────────
23
+ // Built-in server (the waypoi /mcp endpoint itself)
24
+ // ─────────────────────────────────────────────────────────────────────────────
25
+ /** Reserved ID for the built-in waypoi MCP server. */
26
+ exports.BUILTIN_SERVER_ID = "builtin";
27
+ /** URL of the built-in MCP server (set once after app.listen). */
28
+ let builtinUrl = null;
29
+ /**
30
+ * Connect to the built-in /mcp endpoint and cache its tools.
31
+ * Called once in src/index.ts after app.listen().
32
+ */
33
+ async function discoverBuiltinTools(paths, serverUrl) {
34
+ builtinUrl = serverUrl;
35
+ const now = new Date();
36
+ const server = {
37
+ id: exports.BUILTIN_SERVER_ID,
38
+ name: "waypoi",
39
+ url: serverUrl,
40
+ enabled: true,
41
+ status: "unknown",
42
+ createdAt: now,
43
+ updatedAt: now,
44
+ };
45
+ return discoverServerTools(paths, server);
46
+ }
47
+ /**
48
+ * Return virtual server metadata for the built-in server.
49
+ * Returns null if discoverBuiltinTools hasn't been called yet.
50
+ */
51
+ function getBuiltinVirtualServer() {
52
+ if (!builtinUrl)
53
+ return null;
54
+ const now = new Date();
55
+ const connected = isServerConnected(exports.BUILTIN_SERVER_ID);
56
+ return {
57
+ id: exports.BUILTIN_SERVER_ID,
58
+ name: "waypoi",
59
+ url: builtinUrl,
60
+ enabled: true,
61
+ status: connected ? "connected" : "error",
62
+ toolCount: getCachedToolsForServer(exports.BUILTIN_SERVER_ID).length,
63
+ createdAt: now,
64
+ updatedAt: now,
65
+ connected,
66
+ };
67
+ }
68
+ /**
69
+ * Connect to a single MCP server and discover its tools.
70
+ */
71
+ async function discoverServerTools(paths, server) {
72
+ if (!server.enabled) {
73
+ return [];
74
+ }
75
+ try {
76
+ // Create client and connect
77
+ const client = await (0, client_1.createMcpClient)(server.url);
78
+ activeClients.set(server.id, client);
79
+ // Discover tools
80
+ const toolSchemas = await client.listTools();
81
+ const tools = toolSchemas.map((schema) => ({
82
+ name: schema.name,
83
+ description: schema.description,
84
+ inputSchema: schema.inputSchema,
85
+ serverId: server.id,
86
+ serverName: server.name,
87
+ serverUrl: server.url,
88
+ }));
89
+ // Update server status
90
+ await (0, registry_1.updateMcpServerStatus)(paths, server.id, "connected", tools.length);
91
+ // Cache tools
92
+ toolsCache.set(server.id, tools);
93
+ return tools;
94
+ }
95
+ catch (error) {
96
+ const summary = summarizeMcpError(error);
97
+ console.error(`[waypoi] MCP discovery failed for ${server.name}: ${summary}`);
98
+ if (process.env.WAYPOI_DEBUG_ERRORS === "1") {
99
+ console.error(error);
100
+ }
101
+ await (0, registry_1.updateMcpServerStatus)(paths, server.id, "error");
102
+ toolsCache.delete(server.id);
103
+ return [];
104
+ }
105
+ }
106
+ /**
107
+ * Discover tools from all enabled MCP servers.
108
+ */
109
+ async function discoverAllTools(paths) {
110
+ const servers = await (0, registry_1.listMcpServers)(paths);
111
+ const enabledServers = servers.filter((s) => s.enabled);
112
+ // Discover tools from all servers in parallel
113
+ const results = await Promise.allSettled(enabledServers.map((server) => discoverServerTools(paths, server)));
114
+ // Collect all successful discoveries
115
+ const allTools = [];
116
+ for (const result of results) {
117
+ if (result.status === "fulfilled") {
118
+ allTools.push(...result.value);
119
+ }
120
+ }
121
+ return allTools;
122
+ }
123
+ function summarizeMcpError(error) {
124
+ if (error instanceof client_1.McpError) {
125
+ return error.message;
126
+ }
127
+ const err = error;
128
+ if (err?.code) {
129
+ return err.code;
130
+ }
131
+ const cause = err?.cause;
132
+ if (cause?.code) {
133
+ return cause.code;
134
+ }
135
+ const message = err?.message;
136
+ if (typeof message === "string" && message.trim().length > 0) {
137
+ return message;
138
+ }
139
+ return "unknown error";
140
+ }
141
+ /**
142
+ * Get all cached tools (does not re-discover).
143
+ */
144
+ function getCachedTools() {
145
+ const allTools = [];
146
+ for (const tools of toolsCache.values()) {
147
+ allTools.push(...tools);
148
+ }
149
+ return allTools;
150
+ }
151
+ /**
152
+ * Get tools for a specific server.
153
+ */
154
+ function getCachedToolsForServer(serverId) {
155
+ return toolsCache.get(serverId) ?? [];
156
+ }
157
+ /**
158
+ * Find a tool by name across all servers.
159
+ */
160
+ function findTool(name) {
161
+ for (const tools of toolsCache.values()) {
162
+ const tool = tools.find((t) => t.name === name);
163
+ if (tool)
164
+ return tool;
165
+ }
166
+ return undefined;
167
+ }
168
+ /**
169
+ * Execute a tool on its source MCP server.
170
+ */
171
+ async function executeTool(toolName, args) {
172
+ const tool = findTool(toolName);
173
+ if (!tool) {
174
+ return { content: `Tool not found: ${toolName}`, isError: true };
175
+ }
176
+ const client = activeClients.get(tool.serverId);
177
+ if (!client) {
178
+ return { content: `Server not connected: ${tool.serverName}`, isError: true };
179
+ }
180
+ try {
181
+ const result = await client.callTool(toolName, args);
182
+ // Extract text content from result
183
+ const textParts = result.content
184
+ .filter((c) => c.type === "text" && c.text)
185
+ .map((c) => c.text);
186
+ return {
187
+ content: textParts.join("\n") || "Tool executed successfully (no output)",
188
+ isError: result.isError,
189
+ };
190
+ }
191
+ catch (error) {
192
+ const errorMessage = error instanceof client_1.McpError
193
+ ? error.message
194
+ : error.message;
195
+ return { content: `Tool execution failed: ${errorMessage}`, isError: true };
196
+ }
197
+ }
198
+ /**
199
+ * Disconnect from a specific server.
200
+ */
201
+ async function disconnectServer(serverId) {
202
+ const client = activeClients.get(serverId);
203
+ if (client) {
204
+ await client.close();
205
+ activeClients.delete(serverId);
206
+ }
207
+ toolsCache.delete(serverId);
208
+ }
209
+ /**
210
+ * Disconnect from all servers.
211
+ */
212
+ async function disconnectAllServers() {
213
+ const closePromises = [];
214
+ for (const [serverId, client] of activeClients) {
215
+ closePromises.push(client.close());
216
+ activeClients.delete(serverId);
217
+ toolsCache.delete(serverId);
218
+ }
219
+ await Promise.allSettled(closePromises);
220
+ }
221
+ /**
222
+ * Check if a server is currently connected.
223
+ */
224
+ function isServerConnected(serverId) {
225
+ return activeClients.has(serverId);
226
+ }
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.typedError = typedError;
7
+ exports.resolveBinaryOutputPolicy = resolveBinaryOutputPolicy;
8
+ exports.validateSingleImageInput = validateSingleImageInput;
9
+ exports.validateAtMostOneImageInput = validateAtMostOneImageInput;
10
+ const path_1 = __importDefault(require("path"));
11
+ function typedError(type, message) {
12
+ const error = new Error(message);
13
+ error.type = type;
14
+ return error;
15
+ }
16
+ function resolveBinaryOutputPolicy(input, options = {}) {
17
+ const env = options.env ?? process.env;
18
+ const defaultBaseDir = options.defaultBaseDir ?? process.cwd();
19
+ const strict = parseBooleanEnv(env.WAYPOI_MCP_STRICT_OUTPUT_ROOT);
20
+ const configuredRoot = env.WAYPOI_MCP_OUTPUT_ROOT?.trim();
21
+ if (strict && !configuredRoot) {
22
+ throw typedError("invalid_request", "WAYPOI_MCP_STRICT_OUTPUT_ROOT=true requires WAYPOI_MCP_OUTPUT_ROOT to be set.");
23
+ }
24
+ let baseRoot = defaultBaseDir;
25
+ if (configuredRoot) {
26
+ if (!path_1.default.isAbsolute(configuredRoot)) {
27
+ if (strict) {
28
+ throw typedError("invalid_request", `WAYPOI_MCP_OUTPUT_ROOT must be an absolute path, got '${configuredRoot}'.`);
29
+ }
30
+ }
31
+ else {
32
+ baseRoot = path_1.default.resolve(configuredRoot);
33
+ }
34
+ }
35
+ const configuredSubdir = env.WAYPOI_MCP_OUTPUT_SUBDIR?.trim();
36
+ if (configuredSubdir && path_1.default.isAbsolute(configuredSubdir)) {
37
+ throw typedError("invalid_request", `WAYPOI_MCP_OUTPUT_SUBDIR must be relative, got '${configuredSubdir}'.`);
38
+ }
39
+ const outputDir = configuredSubdir
40
+ ? path_1.default.resolve(baseRoot, configuredSubdir)
41
+ : path_1.default.join(baseRoot, "generated-images");
42
+ return {
43
+ outputDir,
44
+ includeData: input.include_data ?? false,
45
+ outputBaseRoot: baseRoot,
46
+ };
47
+ }
48
+ function parseBooleanEnv(value) {
49
+ if (!value) {
50
+ return false;
51
+ }
52
+ return value === "1" || value.toLowerCase() === "true";
53
+ }
54
+ function validateSingleImageInput(input) {
55
+ const hasPath = Boolean(input.image_path);
56
+ const hasUrl = Boolean(input.image_url);
57
+ if ((hasPath && hasUrl) || (!hasPath && !hasUrl)) {
58
+ throw typedError("invalid_request", "Exactly one image source is required: provide either image_path or image_url.");
59
+ }
60
+ }
61
+ function validateAtMostOneImageInput(input) {
62
+ if (input.image_path && input.image_url) {
63
+ throw typedError("invalid_request", "Provide either image_path or image_url, not both.");
64
+ }
65
+ }
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.listMcpServers = listMcpServers;
7
+ exports.getMcpServer = getMcpServer;
8
+ exports.addMcpServer = addMcpServer;
9
+ exports.updateMcpServer = updateMcpServer;
10
+ exports.removeMcpServer = removeMcpServer;
11
+ exports.updateMcpServerStatus = updateMcpServerStatus;
12
+ const fs_1 = require("fs");
13
+ const path_1 = __importDefault(require("path"));
14
+ const crypto_1 = __importDefault(require("crypto"));
15
+ const yaml_1 = __importDefault(require("yaml"));
16
+ const files_1 = require("../storage/files");
17
+ function mcpServersPath(paths) {
18
+ return path_1.default.join(paths.baseDir, "mcp-servers.yaml");
19
+ }
20
+ async function ensureMcpDir(paths) {
21
+ await (0, files_1.ensureStorageDir)(paths);
22
+ }
23
+ async function loadServersFile(paths) {
24
+ await ensureMcpDir(paths);
25
+ const filePath = mcpServersPath(paths);
26
+ try {
27
+ const raw = await fs_1.promises.readFile(filePath, "utf8");
28
+ const doc = yaml_1.default.parse(raw);
29
+ if (doc?.servers) {
30
+ return doc;
31
+ }
32
+ }
33
+ catch (error) {
34
+ if (error.code !== "ENOENT") {
35
+ throw error;
36
+ }
37
+ }
38
+ return { servers: [] };
39
+ }
40
+ async function saveServersFile(paths, data) {
41
+ const filePath = mcpServersPath(paths);
42
+ const yaml = yaml_1.default.stringify(data, { indent: 2 });
43
+ await fs_1.promises.writeFile(filePath, yaml, "utf8");
44
+ }
45
+ /**
46
+ * List all registered MCP servers.
47
+ */
48
+ async function listMcpServers(paths) {
49
+ const file = await loadServersFile(paths);
50
+ return file.servers;
51
+ }
52
+ /**
53
+ * Get a single MCP server by ID.
54
+ */
55
+ async function getMcpServer(paths, id) {
56
+ const servers = await listMcpServers(paths);
57
+ return servers.find((s) => s.id === id) ?? null;
58
+ }
59
+ /**
60
+ * Add a new MCP server to the registry.
61
+ */
62
+ async function addMcpServer(paths, input) {
63
+ const file = await loadServersFile(paths);
64
+ // Check for duplicate URL
65
+ if (file.servers.some((s) => s.url === input.url)) {
66
+ throw new Error(`Server with URL ${input.url} already exists`);
67
+ }
68
+ const now = new Date();
69
+ const server = {
70
+ id: crypto_1.default.randomUUID(),
71
+ name: input.name,
72
+ url: input.url,
73
+ enabled: input.enabled ?? true,
74
+ status: "unknown",
75
+ createdAt: now,
76
+ updatedAt: now,
77
+ };
78
+ file.servers.push(server);
79
+ await saveServersFile(paths, file);
80
+ return server;
81
+ }
82
+ /**
83
+ * Update an existing MCP server.
84
+ */
85
+ async function updateMcpServer(paths, id, patch) {
86
+ const file = await loadServersFile(paths);
87
+ const index = file.servers.findIndex((s) => s.id === id);
88
+ if (index === -1) {
89
+ return null;
90
+ }
91
+ const server = file.servers[index];
92
+ const updated = {
93
+ ...server,
94
+ ...patch,
95
+ updatedAt: new Date(),
96
+ };
97
+ file.servers[index] = updated;
98
+ await saveServersFile(paths, file);
99
+ return updated;
100
+ }
101
+ /**
102
+ * Remove an MCP server from the registry.
103
+ */
104
+ async function removeMcpServer(paths, id) {
105
+ const file = await loadServersFile(paths);
106
+ const index = file.servers.findIndex((s) => s.id === id);
107
+ if (index === -1) {
108
+ return false;
109
+ }
110
+ file.servers.splice(index, 1);
111
+ await saveServersFile(paths, file);
112
+ return true;
113
+ }
114
+ /**
115
+ * Update server status after connection attempt.
116
+ */
117
+ async function updateMcpServerStatus(paths, id, status, toolCount) {
118
+ const file = await loadServersFile(paths);
119
+ const server = file.servers.find((s) => s.id === id);
120
+ if (server) {
121
+ server.status = status;
122
+ if (toolCount !== undefined) {
123
+ server.toolCount = toolCount;
124
+ }
125
+ server.lastConnectedAt = status === "connected" ? new Date() : server.lastConnectedAt;
126
+ server.updatedAt = new Date();
127
+ await saveServersFile(paths, file);
128
+ }
129
+ }