@weapp-vite/mcp 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dist/index.d.mts +65 -1
- package/dist/index.d.ts +65 -1
- package/dist/index.mjs +351 -8
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -35,6 +35,16 @@ const handle = await startWeappViteMcpServer({
|
|
|
35
35
|
await handle.close?.()
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
`streamable-http` 模式会同时暴露 DevTools runtime REST 接口,默认前缀为
|
|
39
|
+
`/api/weapp/devtools`。可以通过 `restEndpoint` 调整,或设为 `false` 关闭:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
const handle = await startWeappViteMcpServer({
|
|
43
|
+
transport: 'streamable-http',
|
|
44
|
+
restEndpoint: '/api/weapp/devtools',
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
38
48
|
## 主要 Tools
|
|
39
49
|
|
|
40
50
|
- `workspace_catalog`: 输出 `weapp-vite / wevu / wevu-compiler` 目录、版本、脚本
|
|
@@ -66,6 +76,31 @@ await handle.close?.()
|
|
|
66
76
|
|
|
67
77
|
建议调用顺序:先 `weapp_devtools_connect`,再 `weapp_devtools_active_page`,之后再执行 `weapp_devtools_capture` 或 `weapp_runtime_*`。
|
|
68
78
|
|
|
79
|
+
### DevTools Runtime REST
|
|
80
|
+
|
|
81
|
+
REST 接口和 MCP runtime tools 复用同一个 automator 会话管理器,适合脚本或其他非 MCP 客户端连续操控模拟器:
|
|
82
|
+
|
|
83
|
+
- `POST /api/weapp/devtools/connect`
|
|
84
|
+
- `POST /api/weapp/devtools/route`
|
|
85
|
+
- `GET|POST /api/weapp/devtools/active-page`
|
|
86
|
+
- `GET|POST /api/weapp/devtools/page-stack`
|
|
87
|
+
- `POST /api/weapp/devtools/capture`
|
|
88
|
+
- `POST /api/weapp/devtools/host-api`
|
|
89
|
+
- `GET|DELETE /api/weapp/devtools/console`
|
|
90
|
+
- `DELETE /api/weapp/devtools/session`
|
|
91
|
+
|
|
92
|
+
示例:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
curl -X POST http://127.0.0.1:3088/api/weapp/devtools/route \
|
|
96
|
+
-H 'content-type: application/json' \
|
|
97
|
+
-d '{"projectPath":"dist/build/mp-weixin","transition":"reLaunch","path":"pages/index/index"}'
|
|
98
|
+
|
|
99
|
+
curl -X POST http://127.0.0.1:3088/api/weapp/devtools/capture \
|
|
100
|
+
-H 'content-type: application/json' \
|
|
101
|
+
-d '{"projectPath":"dist/build/mp-weixin","outputPath":".tmp/home.png"}'
|
|
102
|
+
```
|
|
103
|
+
|
|
69
104
|
## 主要 Resources
|
|
70
105
|
|
|
71
106
|
- `weapp-vite://workspace/catalog`
|
package/dist/index.d.mts
CHANGED
|
@@ -86,15 +86,78 @@ declare function searchTextInDirectory(root: string, query: string, options?: {
|
|
|
86
86
|
maxResults?: number;
|
|
87
87
|
}): Promise<SearchMatch[]>;
|
|
88
88
|
|
|
89
|
+
interface MiniProgramElement {
|
|
90
|
+
tagName?: string;
|
|
91
|
+
$?: (selector: string) => Promise<MiniProgramElement | null>;
|
|
92
|
+
$$?: (selector: string) => Promise<MiniProgramElement[]>;
|
|
93
|
+
tap: () => Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
interface MiniProgramPage {
|
|
96
|
+
path: string;
|
|
97
|
+
query?: unknown;
|
|
98
|
+
$: (selector: string) => Promise<MiniProgramElement | null>;
|
|
99
|
+
$$?: (selector: string) => Promise<MiniProgramElement[]>;
|
|
100
|
+
data: (path?: string) => Promise<unknown>;
|
|
101
|
+
size: () => Promise<unknown>;
|
|
102
|
+
scrollTop: () => Promise<unknown>;
|
|
103
|
+
waitFor: (milliseconds: number) => Promise<void>;
|
|
104
|
+
}
|
|
105
|
+
interface MiniProgramLike {
|
|
106
|
+
on: (name: 'console' | 'exception', handler: (payload: unknown) => void) => void;
|
|
107
|
+
off?: (name: 'console' | 'exception', handler: (payload: unknown) => void) => void;
|
|
108
|
+
currentPage: () => Promise<MiniProgramPage>;
|
|
109
|
+
systemInfo: () => Promise<unknown>;
|
|
110
|
+
pageStack: () => Promise<MiniProgramPage[]>;
|
|
111
|
+
navigateTo: (url: string) => Promise<MiniProgramPage>;
|
|
112
|
+
redirectTo: (url: string) => Promise<MiniProgramPage>;
|
|
113
|
+
reLaunch: (url: string) => Promise<MiniProgramPage>;
|
|
114
|
+
switchTab: (url: string) => Promise<MiniProgramPage>;
|
|
115
|
+
navigateBack: () => Promise<MiniProgramPage>;
|
|
116
|
+
screenshot: () => Promise<string | Uint8Array>;
|
|
117
|
+
callWxMethod: (method: string, ...args: unknown[]) => Promise<unknown>;
|
|
118
|
+
}
|
|
119
|
+
interface RuntimeConnectionInput {
|
|
120
|
+
projectPath: string;
|
|
121
|
+
timeout?: number;
|
|
122
|
+
preferOpenedSession?: boolean;
|
|
123
|
+
}
|
|
124
|
+
interface RuntimeConsoleLogEntry {
|
|
125
|
+
level: string;
|
|
126
|
+
message: string;
|
|
127
|
+
timestamp: number;
|
|
128
|
+
raw: unknown;
|
|
129
|
+
}
|
|
130
|
+
declare class RuntimeSessionManager {
|
|
131
|
+
private readonly workspaceRoot;
|
|
132
|
+
private readonly runtimeHooks;
|
|
133
|
+
private readonly logs;
|
|
134
|
+
private readonly attachedSessions;
|
|
135
|
+
private readonly maxLogs;
|
|
136
|
+
constructor(workspaceRoot: string, runtimeHooks?: DevtoolsRuntimeHooks);
|
|
137
|
+
close(input: RuntimeConnectionInput): Promise<void>;
|
|
138
|
+
clearLogs(): void;
|
|
139
|
+
getLogs(): RuntimeConsoleLogEntry[];
|
|
140
|
+
resolveProjectPath(projectPath: string): string;
|
|
141
|
+
resolveWorkspacePath(filePath: string): string;
|
|
142
|
+
withMiniProgram<T>(input: RuntimeConnectionInput, runner: (miniProgram: MiniProgramLike) => Promise<T>): Promise<T>;
|
|
143
|
+
withPage<T>(input: RuntimeConnectionInput, runner: (page: MiniProgramPage, miniProgram: MiniProgramLike) => Promise<T>): Promise<T>;
|
|
144
|
+
private attach;
|
|
145
|
+
private detach;
|
|
146
|
+
private pushLog;
|
|
147
|
+
}
|
|
148
|
+
|
|
89
149
|
interface CreateServerOptions {
|
|
90
150
|
runtimeHooks?: DevtoolsRuntimeHooks;
|
|
91
151
|
workspaceRoot?: string;
|
|
92
152
|
}
|
|
93
153
|
declare function createWeappViteMcpServer(options?: CreateServerOptions): Promise<{
|
|
154
|
+
runtimeManager: RuntimeSessionManager;
|
|
94
155
|
server: McpServer;
|
|
95
156
|
workspaceRoot: string;
|
|
96
157
|
}>;
|
|
97
158
|
|
|
159
|
+
declare const DEFAULT_RUNTIME_REST_ENDPOINT = "/api/weapp/devtools";
|
|
160
|
+
|
|
98
161
|
declare const DEFAULT_MCP_HOST = "127.0.0.1";
|
|
99
162
|
declare const DEFAULT_MCP_PORT = 3088;
|
|
100
163
|
declare const DEFAULT_MCP_ENDPOINT = "/mcp";
|
|
@@ -103,6 +166,7 @@ interface StartMcpServerOptions extends CreateServerOptions {
|
|
|
103
166
|
host?: string;
|
|
104
167
|
port?: number;
|
|
105
168
|
endpoint?: string;
|
|
169
|
+
restEndpoint?: string | false;
|
|
106
170
|
unref?: boolean;
|
|
107
171
|
quiet?: boolean;
|
|
108
172
|
onReady?: (message: string) => void;
|
|
@@ -137,5 +201,5 @@ declare function resolveWorkspaceRoot(start?: string): string;
|
|
|
137
201
|
declare function assertInsideRoot(root: string, targetPath: string): string;
|
|
138
202
|
declare function resolveSubPath(root: string, relativePath: string): string;
|
|
139
203
|
|
|
140
|
-
export { DEFAULT_MAX_FILE_CHARS, DEFAULT_MAX_OUTPUT_CHARS, DEFAULT_MAX_RESULTS, DEFAULT_MCP_ENDPOINT, DEFAULT_MCP_HOST, DEFAULT_MCP_PORT, DEFAULT_TIMEOUT_MS, EXPOSED_PACKAGES, MCP_SERVER_NAME, MCP_SERVER_VERSION, SKIPPED_DIR_NAMES, assertInsideRoot, createWeappViteMcpServer, formatJson, listFilesInDirectory, loadExposedCatalog, loadPackageSummary, normalizeErrorMessage, readFileContent, resolveSubPath, resolveWorkspaceRoot, runCommand, searchTextInDirectory, startStdioServer, startWeappViteMcpServer, toToolError, toToolResult };
|
|
204
|
+
export { DEFAULT_MAX_FILE_CHARS, DEFAULT_MAX_OUTPUT_CHARS, DEFAULT_MAX_RESULTS, DEFAULT_MCP_ENDPOINT, DEFAULT_MCP_HOST, DEFAULT_MCP_PORT, DEFAULT_RUNTIME_REST_ENDPOINT, DEFAULT_TIMEOUT_MS, EXPOSED_PACKAGES, MCP_SERVER_NAME, MCP_SERVER_VERSION, SKIPPED_DIR_NAMES, assertInsideRoot, createWeappViteMcpServer, formatJson, listFilesInDirectory, loadExposedCatalog, loadPackageSummary, normalizeErrorMessage, readFileContent, resolveSubPath, resolveWorkspaceRoot, runCommand, searchTextInDirectory, startStdioServer, startWeappViteMcpServer, toToolError, toToolResult };
|
|
141
205
|
export type { CommandResult, CreateServerOptions, ExposedPackageId, ExposedPackageSummary, McpServerHandle, SearchMatch, StartMcpServerOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -86,15 +86,78 @@ declare function searchTextInDirectory(root: string, query: string, options?: {
|
|
|
86
86
|
maxResults?: number;
|
|
87
87
|
}): Promise<SearchMatch[]>;
|
|
88
88
|
|
|
89
|
+
interface MiniProgramElement {
|
|
90
|
+
tagName?: string;
|
|
91
|
+
$?: (selector: string) => Promise<MiniProgramElement | null>;
|
|
92
|
+
$$?: (selector: string) => Promise<MiniProgramElement[]>;
|
|
93
|
+
tap: () => Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
interface MiniProgramPage {
|
|
96
|
+
path: string;
|
|
97
|
+
query?: unknown;
|
|
98
|
+
$: (selector: string) => Promise<MiniProgramElement | null>;
|
|
99
|
+
$$?: (selector: string) => Promise<MiniProgramElement[]>;
|
|
100
|
+
data: (path?: string) => Promise<unknown>;
|
|
101
|
+
size: () => Promise<unknown>;
|
|
102
|
+
scrollTop: () => Promise<unknown>;
|
|
103
|
+
waitFor: (milliseconds: number) => Promise<void>;
|
|
104
|
+
}
|
|
105
|
+
interface MiniProgramLike {
|
|
106
|
+
on: (name: 'console' | 'exception', handler: (payload: unknown) => void) => void;
|
|
107
|
+
off?: (name: 'console' | 'exception', handler: (payload: unknown) => void) => void;
|
|
108
|
+
currentPage: () => Promise<MiniProgramPage>;
|
|
109
|
+
systemInfo: () => Promise<unknown>;
|
|
110
|
+
pageStack: () => Promise<MiniProgramPage[]>;
|
|
111
|
+
navigateTo: (url: string) => Promise<MiniProgramPage>;
|
|
112
|
+
redirectTo: (url: string) => Promise<MiniProgramPage>;
|
|
113
|
+
reLaunch: (url: string) => Promise<MiniProgramPage>;
|
|
114
|
+
switchTab: (url: string) => Promise<MiniProgramPage>;
|
|
115
|
+
navigateBack: () => Promise<MiniProgramPage>;
|
|
116
|
+
screenshot: () => Promise<string | Uint8Array>;
|
|
117
|
+
callWxMethod: (method: string, ...args: unknown[]) => Promise<unknown>;
|
|
118
|
+
}
|
|
119
|
+
interface RuntimeConnectionInput {
|
|
120
|
+
projectPath: string;
|
|
121
|
+
timeout?: number;
|
|
122
|
+
preferOpenedSession?: boolean;
|
|
123
|
+
}
|
|
124
|
+
interface RuntimeConsoleLogEntry {
|
|
125
|
+
level: string;
|
|
126
|
+
message: string;
|
|
127
|
+
timestamp: number;
|
|
128
|
+
raw: unknown;
|
|
129
|
+
}
|
|
130
|
+
declare class RuntimeSessionManager {
|
|
131
|
+
private readonly workspaceRoot;
|
|
132
|
+
private readonly runtimeHooks;
|
|
133
|
+
private readonly logs;
|
|
134
|
+
private readonly attachedSessions;
|
|
135
|
+
private readonly maxLogs;
|
|
136
|
+
constructor(workspaceRoot: string, runtimeHooks?: DevtoolsRuntimeHooks);
|
|
137
|
+
close(input: RuntimeConnectionInput): Promise<void>;
|
|
138
|
+
clearLogs(): void;
|
|
139
|
+
getLogs(): RuntimeConsoleLogEntry[];
|
|
140
|
+
resolveProjectPath(projectPath: string): string;
|
|
141
|
+
resolveWorkspacePath(filePath: string): string;
|
|
142
|
+
withMiniProgram<T>(input: RuntimeConnectionInput, runner: (miniProgram: MiniProgramLike) => Promise<T>): Promise<T>;
|
|
143
|
+
withPage<T>(input: RuntimeConnectionInput, runner: (page: MiniProgramPage, miniProgram: MiniProgramLike) => Promise<T>): Promise<T>;
|
|
144
|
+
private attach;
|
|
145
|
+
private detach;
|
|
146
|
+
private pushLog;
|
|
147
|
+
}
|
|
148
|
+
|
|
89
149
|
interface CreateServerOptions {
|
|
90
150
|
runtimeHooks?: DevtoolsRuntimeHooks;
|
|
91
151
|
workspaceRoot?: string;
|
|
92
152
|
}
|
|
93
153
|
declare function createWeappViteMcpServer(options?: CreateServerOptions): Promise<{
|
|
154
|
+
runtimeManager: RuntimeSessionManager;
|
|
94
155
|
server: McpServer;
|
|
95
156
|
workspaceRoot: string;
|
|
96
157
|
}>;
|
|
97
158
|
|
|
159
|
+
declare const DEFAULT_RUNTIME_REST_ENDPOINT = "/api/weapp/devtools";
|
|
160
|
+
|
|
98
161
|
declare const DEFAULT_MCP_HOST = "127.0.0.1";
|
|
99
162
|
declare const DEFAULT_MCP_PORT = 3088;
|
|
100
163
|
declare const DEFAULT_MCP_ENDPOINT = "/mcp";
|
|
@@ -103,6 +166,7 @@ interface StartMcpServerOptions extends CreateServerOptions {
|
|
|
103
166
|
host?: string;
|
|
104
167
|
port?: number;
|
|
105
168
|
endpoint?: string;
|
|
169
|
+
restEndpoint?: string | false;
|
|
106
170
|
unref?: boolean;
|
|
107
171
|
quiet?: boolean;
|
|
108
172
|
onReady?: (message: string) => void;
|
|
@@ -137,5 +201,5 @@ declare function resolveWorkspaceRoot(start?: string): string;
|
|
|
137
201
|
declare function assertInsideRoot(root: string, targetPath: string): string;
|
|
138
202
|
declare function resolveSubPath(root: string, relativePath: string): string;
|
|
139
203
|
|
|
140
|
-
export { DEFAULT_MAX_FILE_CHARS, DEFAULT_MAX_OUTPUT_CHARS, DEFAULT_MAX_RESULTS, DEFAULT_MCP_ENDPOINT, DEFAULT_MCP_HOST, DEFAULT_MCP_PORT, DEFAULT_TIMEOUT_MS, EXPOSED_PACKAGES, MCP_SERVER_NAME, MCP_SERVER_VERSION, SKIPPED_DIR_NAMES, assertInsideRoot, createWeappViteMcpServer, formatJson, listFilesInDirectory, loadExposedCatalog, loadPackageSummary, normalizeErrorMessage, readFileContent, resolveSubPath, resolveWorkspaceRoot, runCommand, searchTextInDirectory, startStdioServer, startWeappViteMcpServer, toToolError, toToolResult };
|
|
204
|
+
export { DEFAULT_MAX_FILE_CHARS, DEFAULT_MAX_OUTPUT_CHARS, DEFAULT_MAX_RESULTS, DEFAULT_MCP_ENDPOINT, DEFAULT_MCP_HOST, DEFAULT_MCP_PORT, DEFAULT_RUNTIME_REST_ENDPOINT, DEFAULT_TIMEOUT_MS, EXPOSED_PACKAGES, MCP_SERVER_NAME, MCP_SERVER_VERSION, SKIPPED_DIR_NAMES, assertInsideRoot, createWeappViteMcpServer, formatJson, listFilesInDirectory, loadExposedCatalog, loadPackageSummary, normalizeErrorMessage, readFileContent, resolveSubPath, resolveWorkspaceRoot, runCommand, searchTextInDirectory, startStdioServer, startWeappViteMcpServer, toToolError, toToolResult };
|
|
141
205
|
export type { CommandResult, CreateServerOptions, ExposedPackageId, ExposedPackageSummary, McpServerHandle, SearchMatch, StartMcpServerOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import process from 'node:process';
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
import { Buffer } from 'node:buffer';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
4
5
|
import http from 'node:http';
|
|
5
6
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
6
7
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
7
|
-
import
|
|
8
|
-
import { z } from 'zod';
|
|
8
|
+
import fs$1 from 'node:fs/promises';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { closeSharedMiniProgram, acquireSharedMiniProgram, releaseSharedMiniProgram } from '@weapp-vite/devtools-runtime';
|
|
12
|
+
import { ResourceTemplate, McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
10
13
|
import fs from 'node:fs';
|
|
11
|
-
import fs$1 from 'node:fs/promises';
|
|
12
14
|
import { createRequire } from 'node:module';
|
|
13
15
|
import { spawn } from 'node:child_process';
|
|
14
|
-
import { closeSharedMiniProgram, acquireSharedMiniProgram, releaseSharedMiniProgram } from '@weapp-vite/devtools-runtime';
|
|
15
16
|
|
|
16
17
|
const MCP_SERVER_NAME = "@weapp-vite/mcp";
|
|
17
18
|
const MCP_SERVER_VERSION = "2.0.0";
|
|
@@ -1625,10 +1626,11 @@ function registerRuntimePageTools(server, manager) {
|
|
|
1625
1626
|
}
|
|
1626
1627
|
|
|
1627
1628
|
function registerRuntimeTools(server, options) {
|
|
1628
|
-
const manager = new RuntimeSessionManager(options.workspaceRoot, options.runtimeHooks);
|
|
1629
|
+
const manager = options.manager ?? new RuntimeSessionManager(options.workspaceRoot, options.runtimeHooks);
|
|
1629
1630
|
registerDevtoolsRuntimeTools(server, manager);
|
|
1630
1631
|
registerRuntimePageTools(server, manager);
|
|
1631
1632
|
registerRuntimeNodeTools(server, manager);
|
|
1633
|
+
return manager;
|
|
1632
1634
|
}
|
|
1633
1635
|
|
|
1634
1636
|
async function loadPackageSummary(workspaceRoot, id) {
|
|
@@ -1907,12 +1909,14 @@ async function createWeappViteMcpServer(options) {
|
|
|
1907
1909
|
name: MCP_SERVER_NAME,
|
|
1908
1910
|
version: MCP_SERVER_VERSION
|
|
1909
1911
|
});
|
|
1912
|
+
const runtimeManager = new RuntimeSessionManager(workspaceRoot, options?.runtimeHooks);
|
|
1910
1913
|
registerServerTools(server, {
|
|
1911
1914
|
workspaceRoot,
|
|
1912
1915
|
packageIds,
|
|
1913
1916
|
packageIdSchema
|
|
1914
1917
|
});
|
|
1915
1918
|
registerRuntimeTools(server, {
|
|
1919
|
+
manager: runtimeManager,
|
|
1916
1920
|
runtimeHooks: options?.runtimeHooks,
|
|
1917
1921
|
workspaceRoot
|
|
1918
1922
|
});
|
|
@@ -1925,11 +1929,338 @@ async function createWeappViteMcpServer(options) {
|
|
|
1925
1929
|
packageIds
|
|
1926
1930
|
});
|
|
1927
1931
|
return {
|
|
1932
|
+
runtimeManager,
|
|
1928
1933
|
server,
|
|
1929
1934
|
workspaceRoot
|
|
1930
1935
|
};
|
|
1931
1936
|
}
|
|
1932
1937
|
|
|
1938
|
+
const DEFAULT_RUNTIME_REST_ENDPOINT = "/api/weapp/devtools";
|
|
1939
|
+
const connectionSchema = z.object({
|
|
1940
|
+
projectPath: z.string().trim().min(1),
|
|
1941
|
+
timeout: z.number().int().positive().optional(),
|
|
1942
|
+
preferOpenedSession: z.boolean().optional()
|
|
1943
|
+
});
|
|
1944
|
+
const routeBodySchema = connectionSchema.extend({
|
|
1945
|
+
path: z.string().trim().min(1).optional(),
|
|
1946
|
+
query: z.record(z.string(), z.string()).optional(),
|
|
1947
|
+
transition: z.enum(["navigateTo", "redirectTo", "reLaunch", "switchTab", "navigateBack"]).optional(),
|
|
1948
|
+
waitMs: z.number().int().nonnegative().optional()
|
|
1949
|
+
});
|
|
1950
|
+
const activePageBodySchema = connectionSchema.extend({
|
|
1951
|
+
withData: z.boolean().optional()
|
|
1952
|
+
});
|
|
1953
|
+
const captureBodySchema = connectionSchema.extend({
|
|
1954
|
+
outputPath: z.string().trim().min(1).optional()
|
|
1955
|
+
});
|
|
1956
|
+
const hostApiBodySchema = connectionSchema.extend({
|
|
1957
|
+
method: z.string().trim().min(1),
|
|
1958
|
+
args: z.array(z.unknown()).optional()
|
|
1959
|
+
});
|
|
1960
|
+
function normalizeRuntimeRestEndpoint(input) {
|
|
1961
|
+
if (input === false) {
|
|
1962
|
+
return false;
|
|
1963
|
+
}
|
|
1964
|
+
const value = typeof input === "string" ? input.trim() : "";
|
|
1965
|
+
const endpoint = value || DEFAULT_RUNTIME_REST_ENDPOINT;
|
|
1966
|
+
return endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
1967
|
+
}
|
|
1968
|
+
function writeJson$1(res, statusCode, payload) {
|
|
1969
|
+
if (res.headersSent) {
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1972
|
+
res.statusCode = statusCode;
|
|
1973
|
+
res.setHeader("content-type", "application/json");
|
|
1974
|
+
res.end(JSON.stringify(payload));
|
|
1975
|
+
}
|
|
1976
|
+
function writeOk(res, result) {
|
|
1977
|
+
writeJson$1(res, 200, {
|
|
1978
|
+
ok: true,
|
|
1979
|
+
result
|
|
1980
|
+
});
|
|
1981
|
+
}
|
|
1982
|
+
function writeError(res, statusCode, error) {
|
|
1983
|
+
writeJson$1(res, statusCode, {
|
|
1984
|
+
ok: false,
|
|
1985
|
+
error: {
|
|
1986
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1987
|
+
}
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
async function readJsonBody(req) {
|
|
1991
|
+
const chunks = [];
|
|
1992
|
+
for await (const chunk of req) {
|
|
1993
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
1994
|
+
}
|
|
1995
|
+
if (chunks.length === 0) {
|
|
1996
|
+
return {};
|
|
1997
|
+
}
|
|
1998
|
+
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
1999
|
+
if (!raw) {
|
|
2000
|
+
return {};
|
|
2001
|
+
}
|
|
2002
|
+
return JSON.parse(raw);
|
|
2003
|
+
}
|
|
2004
|
+
function readBooleanParam(value) {
|
|
2005
|
+
if (value == null) {
|
|
2006
|
+
return void 0;
|
|
2007
|
+
}
|
|
2008
|
+
return ["1", "true", "yes"].includes(value.toLowerCase());
|
|
2009
|
+
}
|
|
2010
|
+
function readNumberParam(value) {
|
|
2011
|
+
if (value == null || value.trim() === "") {
|
|
2012
|
+
return void 0;
|
|
2013
|
+
}
|
|
2014
|
+
const parsed = Number.parseInt(value, 10);
|
|
2015
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
2016
|
+
}
|
|
2017
|
+
function readConnectionFromQuery(url) {
|
|
2018
|
+
return connectionSchema.parse(compactObject({
|
|
2019
|
+
projectPath: url.searchParams.get("projectPath") ?? void 0,
|
|
2020
|
+
timeout: readNumberParam(url.searchParams.get("timeout")),
|
|
2021
|
+
preferOpenedSession: readBooleanParam(url.searchParams.get("preferOpenedSession"))
|
|
2022
|
+
}));
|
|
2023
|
+
}
|
|
2024
|
+
function getRouteContext(req, endpoint) {
|
|
2025
|
+
if (endpoint === false) {
|
|
2026
|
+
return void 0;
|
|
2027
|
+
}
|
|
2028
|
+
const hostHeader = req.headers.host ?? "127.0.0.1";
|
|
2029
|
+
const url = new URL(req.url ?? "/", `http://${hostHeader}`);
|
|
2030
|
+
const pathname = url.pathname.replace(/\/+$/, "") || "/";
|
|
2031
|
+
const normalizedEndpoint = endpoint.replace(/\/+$/, "") || "/";
|
|
2032
|
+
if (pathname !== normalizedEndpoint && !pathname.startsWith(`${normalizedEndpoint}/`)) {
|
|
2033
|
+
return void 0;
|
|
2034
|
+
}
|
|
2035
|
+
const requestPath = pathname === normalizedEndpoint ? "/" : pathname.slice(normalizedEndpoint.length);
|
|
2036
|
+
return {
|
|
2037
|
+
pathname,
|
|
2038
|
+
requestPath,
|
|
2039
|
+
url
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
function getAllowedMethods(pathname) {
|
|
2043
|
+
if (pathname === "/") {
|
|
2044
|
+
return ["GET"];
|
|
2045
|
+
}
|
|
2046
|
+
if (pathname === "/connect" || pathname === "/route" || pathname === "/capture" || pathname === "/host-api") {
|
|
2047
|
+
return ["POST"];
|
|
2048
|
+
}
|
|
2049
|
+
if (pathname === "/active-page" || pathname === "/page-stack") {
|
|
2050
|
+
return ["GET", "POST"];
|
|
2051
|
+
}
|
|
2052
|
+
if (pathname === "/console") {
|
|
2053
|
+
return ["GET", "DELETE"];
|
|
2054
|
+
}
|
|
2055
|
+
if (pathname === "/session") {
|
|
2056
|
+
return ["DELETE"];
|
|
2057
|
+
}
|
|
2058
|
+
return [];
|
|
2059
|
+
}
|
|
2060
|
+
function assertMethod(res, method, requestPath) {
|
|
2061
|
+
const allowedMethods = getAllowedMethods(requestPath);
|
|
2062
|
+
if (allowedMethods.length === 0) {
|
|
2063
|
+
writeError(res, 404, `Not Found: ${requestPath}`);
|
|
2064
|
+
return false;
|
|
2065
|
+
}
|
|
2066
|
+
if (!allowedMethods.includes(method)) {
|
|
2067
|
+
res.setHeader("allow", allowedMethods.join(", "));
|
|
2068
|
+
writeError(res, 405, `Method Not Allowed: ${method}`);
|
|
2069
|
+
return false;
|
|
2070
|
+
}
|
|
2071
|
+
return true;
|
|
2072
|
+
}
|
|
2073
|
+
async function readConnectionBody(req) {
|
|
2074
|
+
return connectionSchema.parse(await readJsonBody(req));
|
|
2075
|
+
}
|
|
2076
|
+
async function getActivePage(manager, input) {
|
|
2077
|
+
return await manager.withPage(input, async (page) => {
|
|
2078
|
+
const [size, scrollTop, data] = await Promise.all([
|
|
2079
|
+
page.size().catch(() => null),
|
|
2080
|
+
page.scrollTop().catch(() => null),
|
|
2081
|
+
input.withData ? page.data().catch(() => null) : Promise.resolve(void 0)
|
|
2082
|
+
]);
|
|
2083
|
+
return compactObject({
|
|
2084
|
+
path: page.path,
|
|
2085
|
+
query: toSerializableValue(page.query),
|
|
2086
|
+
size: toSerializableValue(size),
|
|
2087
|
+
scrollTop: toSerializableValue(scrollTop),
|
|
2088
|
+
data: toSerializableValue(data)
|
|
2089
|
+
});
|
|
2090
|
+
});
|
|
2091
|
+
}
|
|
2092
|
+
async function handleRoot(res, endpoint) {
|
|
2093
|
+
writeOk(res, {
|
|
2094
|
+
endpoint,
|
|
2095
|
+
routes: [
|
|
2096
|
+
"POST /connect",
|
|
2097
|
+
"POST /route",
|
|
2098
|
+
"GET|POST /active-page",
|
|
2099
|
+
"GET|POST /page-stack",
|
|
2100
|
+
"POST /capture",
|
|
2101
|
+
"POST /host-api",
|
|
2102
|
+
"GET|DELETE /console",
|
|
2103
|
+
"DELETE /session"
|
|
2104
|
+
]
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2107
|
+
async function handleConnect(req, res, manager) {
|
|
2108
|
+
const connection = await readConnectionBody(req);
|
|
2109
|
+
const result = await manager.withMiniProgram(connection, async (miniProgram) => {
|
|
2110
|
+
const page = await miniProgram.currentPage().catch(() => null);
|
|
2111
|
+
const systemInfo = await miniProgram.systemInfo().catch(() => null);
|
|
2112
|
+
return {
|
|
2113
|
+
connected: true,
|
|
2114
|
+
projectPath: connection.projectPath,
|
|
2115
|
+
resolvedProjectPath: manager.resolveProjectPath(connection.projectPath),
|
|
2116
|
+
currentPage: page ? { path: page.path, query: toSerializableValue(page.query) } : null,
|
|
2117
|
+
systemInfo: toSerializableValue(systemInfo)
|
|
2118
|
+
};
|
|
2119
|
+
});
|
|
2120
|
+
writeOk(res, result);
|
|
2121
|
+
}
|
|
2122
|
+
async function handleRoute(req, res, manager) {
|
|
2123
|
+
const input = routeBodySchema.parse(await readJsonBody(req));
|
|
2124
|
+
const result = await manager.withMiniProgram(input, async (miniProgram) => {
|
|
2125
|
+
const transition = input.transition ?? "navigateTo";
|
|
2126
|
+
if (transition === "navigateBack") {
|
|
2127
|
+
const page2 = await miniProgram.navigateBack();
|
|
2128
|
+
if (input.waitMs && page2) {
|
|
2129
|
+
await page2.waitFor(input.waitMs);
|
|
2130
|
+
}
|
|
2131
|
+
return {
|
|
2132
|
+
transition,
|
|
2133
|
+
activePage: page2 ? { path: page2.path, query: toSerializableValue(page2.query) } : null
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
if (!input.path) {
|
|
2137
|
+
throw new Error("transition \u4E0D\u662F navigateBack \u65F6\u5FC5\u987B\u63D0\u4F9B path\u3002");
|
|
2138
|
+
}
|
|
2139
|
+
const url = buildUrl(input.path, input.query);
|
|
2140
|
+
const page = await callRequiredMethod(
|
|
2141
|
+
miniProgram,
|
|
2142
|
+
transition,
|
|
2143
|
+
url
|
|
2144
|
+
);
|
|
2145
|
+
if (input.waitMs && page) {
|
|
2146
|
+
await page.waitFor(input.waitMs);
|
|
2147
|
+
}
|
|
2148
|
+
return {
|
|
2149
|
+
transition,
|
|
2150
|
+
url,
|
|
2151
|
+
activePage: page ? { path: page.path, query: toSerializableValue(page.query) } : null
|
|
2152
|
+
};
|
|
2153
|
+
});
|
|
2154
|
+
writeOk(res, result);
|
|
2155
|
+
}
|
|
2156
|
+
async function handleActivePage(req, res, context, manager) {
|
|
2157
|
+
const input = req.method === "GET" ? activePageBodySchema.parse({
|
|
2158
|
+
...readConnectionFromQuery(context.url),
|
|
2159
|
+
withData: readBooleanParam(context.url.searchParams.get("withData"))
|
|
2160
|
+
}) : activePageBodySchema.parse(await readJsonBody(req));
|
|
2161
|
+
const result = await getActivePage(manager, input);
|
|
2162
|
+
writeOk(res, result);
|
|
2163
|
+
}
|
|
2164
|
+
async function handlePageStack(req, res, context, manager) {
|
|
2165
|
+
const connection = req.method === "GET" ? readConnectionFromQuery(context.url) : await readConnectionBody(req);
|
|
2166
|
+
const result = await manager.withMiniProgram(connection, async (miniProgram) => {
|
|
2167
|
+
const stack = await miniProgram.pageStack();
|
|
2168
|
+
return stack.map((page) => ({
|
|
2169
|
+
path: page.path,
|
|
2170
|
+
query: toSerializableValue(page.query)
|
|
2171
|
+
}));
|
|
2172
|
+
});
|
|
2173
|
+
writeOk(res, result);
|
|
2174
|
+
}
|
|
2175
|
+
async function handleCapture(req, res, manager) {
|
|
2176
|
+
const input = captureBodySchema.parse(await readJsonBody(req));
|
|
2177
|
+
const result = await manager.withMiniProgram(input, async (miniProgram) => {
|
|
2178
|
+
const screenshot = await miniProgram.screenshot();
|
|
2179
|
+
const buffer = typeof screenshot === "string" ? Buffer.from(screenshot, "base64") : Buffer.from(screenshot);
|
|
2180
|
+
if (input.outputPath) {
|
|
2181
|
+
const resolvedOutputPath = manager.resolveWorkspacePath(input.outputPath);
|
|
2182
|
+
await fs$1.mkdir(path.dirname(resolvedOutputPath), { recursive: true });
|
|
2183
|
+
await fs$1.writeFile(resolvedOutputPath, buffer);
|
|
2184
|
+
return {
|
|
2185
|
+
path: resolvedOutputPath,
|
|
2186
|
+
bytes: buffer.length
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
return {
|
|
2190
|
+
base64: buffer.toString("base64"),
|
|
2191
|
+
bytes: buffer.length
|
|
2192
|
+
};
|
|
2193
|
+
});
|
|
2194
|
+
writeOk(res, result);
|
|
2195
|
+
}
|
|
2196
|
+
async function handleHostApi(req, res, manager) {
|
|
2197
|
+
const input = hostApiBodySchema.parse(await readJsonBody(req));
|
|
2198
|
+
const result = await manager.withMiniProgram(input, async (miniProgram) => {
|
|
2199
|
+
const args = input.args ?? [];
|
|
2200
|
+
return {
|
|
2201
|
+
method: input.method,
|
|
2202
|
+
args: toSerializableValue(args),
|
|
2203
|
+
result: toSerializableValue(await miniProgram.callWxMethod(input.method, ...args))
|
|
2204
|
+
};
|
|
2205
|
+
});
|
|
2206
|
+
writeOk(res, result);
|
|
2207
|
+
}
|
|
2208
|
+
async function handleConsole(req, res, manager) {
|
|
2209
|
+
const logs = manager.getLogs();
|
|
2210
|
+
if (req.method === "DELETE") {
|
|
2211
|
+
manager.clearLogs();
|
|
2212
|
+
}
|
|
2213
|
+
writeOk(res, {
|
|
2214
|
+
count: logs.length,
|
|
2215
|
+
logs,
|
|
2216
|
+
cleared: req.method === "DELETE"
|
|
2217
|
+
});
|
|
2218
|
+
}
|
|
2219
|
+
async function handleSession(req, res, context, manager) {
|
|
2220
|
+
const connection = context.url.searchParams.has("projectPath") ? readConnectionFromQuery(context.url) : await readConnectionBody(req);
|
|
2221
|
+
await manager.close(connection);
|
|
2222
|
+
writeOk(res, {
|
|
2223
|
+
closed: true,
|
|
2224
|
+
projectPath: connection.projectPath,
|
|
2225
|
+
resolvedProjectPath: manager.resolveProjectPath(connection.projectPath)
|
|
2226
|
+
});
|
|
2227
|
+
}
|
|
2228
|
+
async function handleRuntimeRestRequest(req, res, options) {
|
|
2229
|
+
const endpoint = normalizeRuntimeRestEndpoint(options.endpoint);
|
|
2230
|
+
const context = getRouteContext(req, endpoint);
|
|
2231
|
+
if (!context) {
|
|
2232
|
+
return false;
|
|
2233
|
+
}
|
|
2234
|
+
const method = req.method ?? "GET";
|
|
2235
|
+
if (!assertMethod(res, method, context.requestPath)) {
|
|
2236
|
+
return true;
|
|
2237
|
+
}
|
|
2238
|
+
try {
|
|
2239
|
+
if (context.requestPath === "/") {
|
|
2240
|
+
await handleRoot(res, endpoint);
|
|
2241
|
+
} else if (context.requestPath === "/connect") {
|
|
2242
|
+
await handleConnect(req, res, options.manager);
|
|
2243
|
+
} else if (context.requestPath === "/route") {
|
|
2244
|
+
await handleRoute(req, res, options.manager);
|
|
2245
|
+
} else if (context.requestPath === "/active-page") {
|
|
2246
|
+
await handleActivePage(req, res, context, options.manager);
|
|
2247
|
+
} else if (context.requestPath === "/page-stack") {
|
|
2248
|
+
await handlePageStack(req, res, context, options.manager);
|
|
2249
|
+
} else if (context.requestPath === "/capture") {
|
|
2250
|
+
await handleCapture(req, res, options.manager);
|
|
2251
|
+
} else if (context.requestPath === "/host-api") {
|
|
2252
|
+
await handleHostApi(req, res, options.manager);
|
|
2253
|
+
} else if (context.requestPath === "/console") {
|
|
2254
|
+
await handleConsole(req, res, options.manager);
|
|
2255
|
+
} else if (context.requestPath === "/session") {
|
|
2256
|
+
await handleSession(req, res, context, options.manager);
|
|
2257
|
+
}
|
|
2258
|
+
} catch (error) {
|
|
2259
|
+
writeError(res, error instanceof SyntaxError || error instanceof z.ZodError ? 400 : 500, error);
|
|
2260
|
+
}
|
|
2261
|
+
return true;
|
|
2262
|
+
}
|
|
2263
|
+
|
|
1933
2264
|
const DEFAULT_MCP_HOST = "127.0.0.1";
|
|
1934
2265
|
const DEFAULT_MCP_PORT = 3088;
|
|
1935
2266
|
const DEFAULT_MCP_ENDPOINT = "/mcp";
|
|
@@ -1991,6 +2322,7 @@ async function startStreamableHttpServer(options) {
|
|
|
1991
2322
|
endpoint = DEFAULT_MCP_ENDPOINT,
|
|
1992
2323
|
host = DEFAULT_MCP_HOST,
|
|
1993
2324
|
port = DEFAULT_MCP_PORT,
|
|
2325
|
+
restEndpoint = DEFAULT_RUNTIME_REST_ENDPOINT,
|
|
1994
2326
|
workspaceRoot,
|
|
1995
2327
|
runtimeHooks,
|
|
1996
2328
|
unref = false,
|
|
@@ -1999,15 +2331,23 @@ async function startStreamableHttpServer(options) {
|
|
|
1999
2331
|
} = options;
|
|
2000
2332
|
const normalizedEndpoint = normalizeEndpoint(endpoint);
|
|
2001
2333
|
const normalizedPort = normalizePort(port);
|
|
2002
|
-
const
|
|
2334
|
+
const normalizedRestEndpoint = normalizeRuntimeRestEndpoint(restEndpoint);
|
|
2335
|
+
const { runtimeManager, server: mcpServer } = await createWeappViteMcpServer({ runtimeHooks, workspaceRoot });
|
|
2003
2336
|
const transport = new StreamableHTTPServerTransport({
|
|
2004
|
-
sessionIdGenerator:
|
|
2337
|
+
sessionIdGenerator: randomUUID
|
|
2005
2338
|
});
|
|
2006
2339
|
await mcpServer.connect(transport);
|
|
2007
2340
|
const httpServer = http.createServer(async (req, res) => {
|
|
2008
2341
|
try {
|
|
2009
2342
|
const hostHeader = req.headers.host ?? `${host}:${normalizedPort}`;
|
|
2010
2343
|
const url = new URL(req.url ?? "/", `http://${hostHeader}`);
|
|
2344
|
+
const handledByRest = await handleRuntimeRestRequest(req, res, {
|
|
2345
|
+
endpoint: normalizedRestEndpoint,
|
|
2346
|
+
manager: runtimeManager
|
|
2347
|
+
});
|
|
2348
|
+
if (handledByRest) {
|
|
2349
|
+
return;
|
|
2350
|
+
}
|
|
2011
2351
|
if (url.pathname !== normalizedEndpoint) {
|
|
2012
2352
|
writeJson(res, 404, {
|
|
2013
2353
|
jsonrpc: "2.0",
|
|
@@ -2055,6 +2395,9 @@ async function startStreamableHttpServer(options) {
|
|
|
2055
2395
|
}
|
|
2056
2396
|
if (!quiet) {
|
|
2057
2397
|
onReady?.(`[mcp] streamable-http ready at http://${host}:${normalizedPort}${normalizedEndpoint}`);
|
|
2398
|
+
if (normalizedRestEndpoint !== false) {
|
|
2399
|
+
onReady?.(`[mcp] REST runtime ready at http://${host}:${normalizedPort}${normalizedRestEndpoint}`);
|
|
2400
|
+
}
|
|
2058
2401
|
}
|
|
2059
2402
|
return {
|
|
2060
2403
|
transport: "streamable-http",
|
|
@@ -2100,4 +2443,4 @@ ${message}
|
|
|
2100
2443
|
});
|
|
2101
2444
|
}
|
|
2102
2445
|
|
|
2103
|
-
export { DEFAULT_MAX_FILE_CHARS, DEFAULT_MAX_OUTPUT_CHARS, DEFAULT_MAX_RESULTS, DEFAULT_MCP_ENDPOINT, DEFAULT_MCP_HOST, DEFAULT_MCP_PORT, DEFAULT_TIMEOUT_MS, EXPOSED_PACKAGES, MCP_SERVER_NAME, MCP_SERVER_VERSION, SKIPPED_DIR_NAMES, assertInsideRoot, createWeappViteMcpServer, formatJson, listFilesInDirectory, loadExposedCatalog, loadPackageSummary, normalizeErrorMessage, readFileContent, resolveSubPath, resolveWorkspaceRoot, runCommand, searchTextInDirectory, startStdioServer, startWeappViteMcpServer, toToolError, toToolResult };
|
|
2446
|
+
export { DEFAULT_MAX_FILE_CHARS, DEFAULT_MAX_OUTPUT_CHARS, DEFAULT_MAX_RESULTS, DEFAULT_MCP_ENDPOINT, DEFAULT_MCP_HOST, DEFAULT_MCP_PORT, DEFAULT_RUNTIME_REST_ENDPOINT, DEFAULT_TIMEOUT_MS, EXPOSED_PACKAGES, MCP_SERVER_NAME, MCP_SERVER_VERSION, SKIPPED_DIR_NAMES, assertInsideRoot, createWeappViteMcpServer, formatJson, listFilesInDirectory, loadExposedCatalog, loadPackageSummary, normalizeErrorMessage, readFileContent, resolveSubPath, resolveWorkspaceRoot, runCommand, searchTextInDirectory, startStdioServer, startWeappViteMcpServer, toToolError, toToolResult };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weapp-vite/mcp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.2",
|
|
5
5
|
"description": "mcp",
|
|
6
6
|
"author": "ice breaker <1324318532@qq.com>",
|
|
7
7
|
"license": "ISC",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
35
|
-
"zod": "^4.3
|
|
36
|
-
"@weapp-vite/devtools-runtime": "0.2.
|
|
35
|
+
"zod": "^4.4.3",
|
|
36
|
+
"@weapp-vite/devtools-runtime": "0.2.2"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"dev": "unbuild --stub",
|