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.
- package/.github/instructions/ui.instructions.md +42 -0
- package/.github/workflows/ci.yml +35 -0
- package/.github/workflows/publish.yml +71 -0
- package/.github/workflows/release.yml +48 -0
- package/.playwright-mcp/console-2026-04-04T01-41-10-746Z.log +2 -0
- package/.playwright-mcp/console-2026-04-04T01-41-28-799Z.log +3 -0
- package/.playwright-mcp/console-2026-04-05T02-26-51-909Z.log +76 -0
- package/.playwright-mcp/page-2026-04-04T01-41-10-816Z.yml +1 -0
- package/.playwright-mcp/page-2026-04-04T01-41-29-141Z.yml +77 -0
- package/.playwright-mcp/page-2026-04-04T01-41-42-633Z.yml +190 -0
- package/.playwright-mcp/page-2026-04-04T01-42-03-929Z.yml +262 -0
- package/.playwright-mcp/page-2026-04-04T02-12-54-813Z.yml +6 -0
- package/.playwright-mcp/page-2026-04-04T02-14-58-600Z.yml +190 -0
- package/.playwright-mcp/page-2026-04-04T02-15-03-923Z.yml +190 -0
- package/.playwright-mcp/page-2026-04-04T02-15-07-426Z.yml +190 -0
- package/.playwright-mcp/page-2026-04-04T02-15-25-729Z.yml +262 -0
- package/.playwright-mcp/page-2026-04-04T02-16-22-984Z.yml +262 -0
- package/.playwright-mcp/page-2026-04-04T02-17-00-599Z.yml +190 -0
- package/.playwright-mcp/page-2026-04-04T02-17-50-874Z.yml +190 -0
- package/.playwright-mcp/page-2026-04-05T02-26-55-570Z.yml +6 -0
- package/AGENTS.md +48 -0
- package/CHANGELOG.md +131 -0
- package/README.md +552 -0
- package/assets/agent-mode.png +0 -0
- package/assets/categorize.png +0 -0
- package/assets/dashboard.png +0 -0
- package/assets/endpoint-proxy.png +0 -0
- package/assets/icon.png +0 -0
- package/assets/mcp-generate-image.png +0 -0
- package/assets/mcp-understand-image.png +0 -0
- package/assets/peek-token-flow.png +0 -0
- package/assets/playground.png +0 -0
- package/assets/sankey.png +0 -0
- package/cli/index.ts +2805 -0
- package/cli/legacyRewrite.ts +108 -0
- package/cli/modelRef.ts +24 -0
- package/dist/cli/index.js +2536 -0
- package/dist/cli/legacyRewrite.js +92 -0
- package/dist/cli/modelRef.js +20 -0
- package/dist/src/benchmark/artifacts.js +131 -0
- package/dist/src/benchmark/capabilityClassifier.js +81 -0
- package/dist/src/benchmark/capabilityStore.js +144 -0
- package/dist/src/benchmark/config.js +238 -0
- package/dist/src/benchmark/gates.js +118 -0
- package/dist/src/benchmark/jobs.js +252 -0
- package/dist/src/benchmark/runner.js +1847 -0
- package/dist/src/benchmark/schema.js +353 -0
- package/dist/src/benchmark/suites.js +314 -0
- package/dist/src/benchmark/tinyQaDataset.js +422 -0
- package/dist/src/benchmark/types.js +25 -0
- package/dist/src/config.js +47 -0
- package/dist/src/index.js +178 -0
- package/dist/src/mcp/client.js +215 -0
- package/dist/src/mcp/discovery.js +226 -0
- package/dist/src/mcp/policy.js +65 -0
- package/dist/src/mcp/registry.js +129 -0
- package/dist/src/mcp/service.js +460 -0
- package/dist/src/middleware/auth.js +179 -0
- package/dist/src/middleware/requestCapture.js +192 -0
- package/dist/src/middleware/requestStats.js +118 -0
- package/dist/src/pools/builder.js +132 -0
- package/dist/src/pools/repository.js +69 -0
- package/dist/src/pools/scheduler.js +360 -0
- package/dist/src/pools/types.js +2 -0
- package/dist/src/protocols/adapters/dashscope.js +267 -0
- package/dist/src/protocols/adapters/inferenceV2.js +346 -0
- package/dist/src/protocols/adapters/openai.js +27 -0
- package/dist/src/protocols/registry.js +99 -0
- package/dist/src/protocols/types.js +2 -0
- package/dist/src/providers/health.js +153 -0
- package/dist/src/providers/importer.js +289 -0
- package/dist/src/providers/modelRegistry.js +313 -0
- package/dist/src/providers/repository.js +361 -0
- package/dist/src/providers/types.js +2 -0
- package/dist/src/routes/admin.js +531 -0
- package/dist/src/routes/audio.js +295 -0
- package/dist/src/routes/chat.js +240 -0
- package/dist/src/routes/embeddings.js +157 -0
- package/dist/src/routes/images.js +288 -0
- package/dist/src/routes/mcp.js +256 -0
- package/dist/src/routes/mcpService.js +100 -0
- package/dist/src/routes/models.js +48 -0
- package/dist/src/routes/responses.js +711 -0
- package/dist/src/routes/sessions.js +450 -0
- package/dist/src/routes/stats.js +270 -0
- package/dist/src/routes/ui.js +97 -0
- package/dist/src/routes/videos.js +107 -0
- package/dist/src/routing/router.js +338 -0
- package/dist/src/services/imageGeneration.js +280 -0
- package/dist/src/services/imageUnderstanding.js +352 -0
- package/dist/src/services/videoGeneration.js +79 -0
- package/dist/src/storage/captureRepository.js +1591 -0
- package/dist/src/storage/files.js +157 -0
- package/dist/src/storage/imageCache.js +346 -0
- package/dist/src/storage/repositories.js +388 -0
- package/dist/src/storage/sessionRepository.js +370 -0
- package/dist/src/storage/statsRepository.js +204 -0
- package/dist/src/transport/httpClient.js +126 -0
- package/dist/src/types.js +2 -0
- package/dist/src/utils/messageMedia.js +285 -0
- package/dist/src/utils/modelCapabilities.js +108 -0
- package/dist/src/utils/modelDiscovery.js +170 -0
- package/dist/src/version.js +5 -0
- package/dist/src/workers/captureRetention.js +25 -0
- package/dist/src/workers/configWatcher.js +91 -0
- package/dist/src/workers/healthChecker.js +21 -0
- package/dist/src/workers/statsRotation.js +41 -0
- package/docs/LLM/output_schema.md +312 -0
- package/docs/benchmark.md +208 -0
- package/docs/mcp-guidelines.md +125 -0
- package/docs/mcp-service.md +178 -0
- package/docs/opencode.md +86 -0
- package/docs/providers.md +79 -0
- package/examples/benchmark.config.yaml +28 -0
- package/examples/providers/alibaba-dashscope.yaml +88 -0
- package/examples/providers/alibaba-llm.yaml +64 -0
- package/examples/providers/alibaba-registry.yaml +7 -0
- package/examples/providers/inference-v2-ray.yaml +29 -0
- package/examples/scenarios/assets/omni-call-sample.wav +0 -0
- package/examples/scenarios/custom.jsonl +5 -0
- package/examples/scenarios/custom.yaml +40 -0
- package/model-form-v2.png +0 -0
- package/package.json +66 -0
- package/provider-form-v2.png +0 -0
- package/provider-form.png +0 -0
- package/scripts/manual-test.sh +11 -0
- package/scripts/version-from-git.js +23 -0
- package/src/benchmark/artifacts.ts +149 -0
- package/src/benchmark/capabilityClassifier.ts +99 -0
- package/src/benchmark/capabilityStore.ts +174 -0
- package/src/benchmark/config.ts +337 -0
- package/src/benchmark/gates.ts +164 -0
- package/src/benchmark/jobs.ts +312 -0
- package/src/benchmark/runner.ts +2519 -0
- package/src/benchmark/schema.ts +443 -0
- package/src/benchmark/suites.ts +323 -0
- package/src/benchmark/tinyQaDataset.ts +428 -0
- package/src/benchmark/types.ts +442 -0
- package/src/config.ts +44 -0
- package/src/index.ts +195 -0
- package/src/mcp/client.ts +305 -0
- package/src/mcp/discovery.ts +266 -0
- package/src/mcp/policy.ts +105 -0
- package/src/mcp/registry.ts +164 -0
- package/src/mcp/service.ts +611 -0
- package/src/middleware/auth.ts +251 -0
- package/src/middleware/requestCapture.ts +245 -0
- package/src/middleware/requestStats.ts +163 -0
- package/src/pools/builder.ts +159 -0
- package/src/pools/repository.ts +71 -0
- package/src/pools/scheduler.ts +425 -0
- package/src/pools/types.ts +117 -0
- package/src/protocols/adapters/dashscope.ts +335 -0
- package/src/protocols/adapters/inferenceV2.ts +428 -0
- package/src/protocols/adapters/openai.ts +32 -0
- package/src/protocols/registry.ts +117 -0
- package/src/protocols/types.ts +81 -0
- package/src/providers/health.ts +207 -0
- package/src/providers/importer.ts +402 -0
- package/src/providers/modelRegistry.ts +415 -0
- package/src/providers/repository.ts +439 -0
- package/src/providers/types.ts +113 -0
- package/src/routes/admin.ts +666 -0
- package/src/routes/audio.ts +372 -0
- package/src/routes/chat.ts +301 -0
- package/src/routes/embeddings.ts +197 -0
- package/src/routes/images.ts +356 -0
- package/src/routes/mcp.ts +320 -0
- package/src/routes/mcpService.ts +114 -0
- package/src/routes/models.ts +50 -0
- package/src/routes/responses.ts +872 -0
- package/src/routes/sessions.ts +558 -0
- package/src/routes/stats.ts +312 -0
- package/src/routes/ui.ts +96 -0
- package/src/routes/videos.ts +132 -0
- package/src/routing/router.ts +501 -0
- package/src/services/imageGeneration.ts +396 -0
- package/src/services/imageUnderstanding.ts +449 -0
- package/src/services/videoGeneration.ts +127 -0
- package/src/storage/captureRepository.ts +1835 -0
- package/src/storage/files.ts +178 -0
- package/src/storage/imageCache.ts +405 -0
- package/src/storage/repositories.ts +494 -0
- package/src/storage/sessionRepository.ts +419 -0
- package/src/storage/statsRepository.ts +238 -0
- package/src/transport/httpClient.ts +145 -0
- package/src/types.ts +322 -0
- package/src/utils/messageMedia.ts +293 -0
- package/src/utils/modelCapabilities.ts +161 -0
- package/src/utils/modelDiscovery.ts +203 -0
- package/src/workers/captureRetention.ts +25 -0
- package/src/workers/configWatcher.ts +115 -0
- package/src/workers/healthChecker.ts +22 -0
- package/src/workers/statsRotation.ts +49 -0
- package/tests/benchmarkAdminRoutes.test.ts +82 -0
- package/tests/benchmarkBasics.test.ts +116 -0
- package/tests/captureAdminRoutes.test.ts +420 -0
- package/tests/captureRepository.test.ts +797 -0
- package/tests/cliLegacyRewrite.test.ts +45 -0
- package/tests/imageGeneration.service.test.ts +107 -0
- package/tests/imageUnderstanding.service.test.ts +123 -0
- package/tests/mcpPolicy.test.ts +105 -0
- package/tests/mcpService.test.ts +1245 -0
- package/tests/modelRef.test.ts +23 -0
- package/tests/modelsRoutes.test.ts +154 -0
- package/tests/sessionMediaCache.test.ts +167 -0
- package/tests/statsRoutes.test.ts +323 -0
- package/tsconfig.json +15 -0
- package/ui/index.html +16 -0
- package/ui/package-lock.json +8521 -0
- package/ui/package.json +52 -0
- package/ui/postcss.config.js +6 -0
- package/ui/public/assets/apple-touch-icon.png +0 -0
- package/ui/public/assets/favicon-16.png +0 -0
- package/ui/public/assets/favicon-32.png +0 -0
- package/ui/public/assets/icon-192.png +0 -0
- package/ui/public/assets/icon-512.png +0 -0
- package/ui/src/App.tsx +27 -0
- package/ui/src/api/client.ts +1503 -0
- package/ui/src/components/EndpointUsageGuide.tsx +361 -0
- package/ui/src/components/Layout.tsx +124 -0
- package/ui/src/components/MessageContent.tsx +365 -0
- package/ui/src/components/ToolCallMessage.tsx +179 -0
- package/ui/src/components/ToolPicker.tsx +442 -0
- package/ui/src/components/messageContentParser.test.ts +41 -0
- package/ui/src/components/messageContentParser.ts +73 -0
- package/ui/src/components/thinkingPreview.test.ts +27 -0
- package/ui/src/components/thinkingPreview.ts +15 -0
- package/ui/src/components/toMermaidSankey.test.ts +78 -0
- package/ui/src/components/toMermaidSankey.ts +56 -0
- package/ui/src/components/ui/button.tsx +58 -0
- package/ui/src/components/ui/input.tsx +21 -0
- package/ui/src/components/ui/textarea.tsx +21 -0
- package/ui/src/lib/utils.ts +6 -0
- package/ui/src/main.tsx +9 -0
- package/ui/src/pages/AgentPlayground.tsx +2010 -0
- package/ui/src/pages/Benchmark.tsx +988 -0
- package/ui/src/pages/Dashboard.tsx +581 -0
- package/ui/src/pages/Peek.tsx +962 -0
- package/ui/src/pages/Settings.tsx +2013 -0
- package/ui/src/pages/agentPlaygroundPayload.test.ts +109 -0
- package/ui/src/pages/agentPlaygroundPayload.ts +97 -0
- package/ui/src/pages/agentThinkingContent.test.ts +50 -0
- package/ui/src/pages/agentThinkingContent.ts +57 -0
- package/ui/src/pages/dashboardTokenUsage.test.ts +66 -0
- package/ui/src/pages/dashboardTokenUsage.ts +36 -0
- package/ui/src/pages/imageUpload.test.ts +39 -0
- package/ui/src/pages/imageUpload.ts +71 -0
- package/ui/src/pages/peekFilters.test.ts +29 -0
- package/ui/src/pages/peekFilters.ts +13 -0
- package/ui/src/pages/peekMedia.test.ts +58 -0
- package/ui/src/pages/peekMedia.ts +148 -0
- package/ui/src/pages/sessionAutoTitle.test.ts +128 -0
- package/ui/src/pages/sessionAutoTitle.ts +106 -0
- package/ui/src/stores/settings.ts +58 -0
- package/ui/src/styles/globals.css +223 -0
- package/ui/src/vite-env.d.ts +8 -0
- package/ui/tailwind.config.js +106 -0
- package/ui/tsconfig.json +32 -0
- 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
|
+
}
|