@xfe-repo/cli-plugin-ai-mcp 2.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/dist/adapters.d.ts +91 -0
- package/dist/adapters.d.ts.map +1 -0
- package/dist/adapters.js +200 -0
- package/dist/adapters.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +177 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +22 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +204 -0
- package/dist/server.js.map +1 -0
- package/package.json +30 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @xfe-repo/cli-plugin-mcp - Headless IO 适配器
|
|
3
|
+
*
|
|
4
|
+
* MCP session 使用的无头适配器:
|
|
5
|
+
* - MemoryLogger: 内存日志收集器,支持 getEntries() 供日志查询
|
|
6
|
+
* - AutoPrompt: 非交互式 Prompt,列出可选项并提示 prefilled 用法
|
|
7
|
+
* - LogSpinner: 将 spinner 状态输出为日志
|
|
8
|
+
* - LogToast: 将 toast 消息输出为日志
|
|
9
|
+
*
|
|
10
|
+
* CLI 兼容:
|
|
11
|
+
* 当 MCP session 需要在 CLI 任务列表中以只读模式展示时,
|
|
12
|
+
* CLI 视图层会调用 subscribe/unreadCount/resetUnread/destroy 等方法。
|
|
13
|
+
* 这里补齐这些最小兼容方法,使 MCP adapter 可被 CLI 安全消费而不抛异常。
|
|
14
|
+
*/
|
|
15
|
+
import type { LoggerInterface, LogEntry, ColoredMessage, PromptInterface, PromptQuestion, SpinnerInterface, SpinnerInstance, ToastInterface, ToastLevel, ToastOptions } from '@xfe-repo/cli-core';
|
|
16
|
+
type LogListener = (entry: LogEntry) => void;
|
|
17
|
+
export declare class MemoryLogger implements LoggerInterface {
|
|
18
|
+
private entries;
|
|
19
|
+
private maxEntries;
|
|
20
|
+
private listeners;
|
|
21
|
+
private renderedCursor;
|
|
22
|
+
private readCursor;
|
|
23
|
+
constructor(maxEntries?: number);
|
|
24
|
+
/** 未读日志数量(CLI TaskCard 消费) */
|
|
25
|
+
get unreadCount(): number;
|
|
26
|
+
/** 重置未读计数(CLI suspend/resume 消费) */
|
|
27
|
+
resetUnread(): void;
|
|
28
|
+
getEntries(): LogEntry[];
|
|
29
|
+
/** 获取尚未被终端输出过的条目(CLI useLogs 消费) */
|
|
30
|
+
getUnrenderedEntries(): LogEntry[];
|
|
31
|
+
/** 将游标推进到当前末尾(CLI useLogs 消费) */
|
|
32
|
+
markRendered(): void;
|
|
33
|
+
/** 订阅日志更新(CLI useLogs 消费) */
|
|
34
|
+
subscribe(listener: LogListener): () => void;
|
|
35
|
+
/** 销毁实例,释放引用(CLI closeSession 消费) */
|
|
36
|
+
destroy(): void;
|
|
37
|
+
log(msg: string): void;
|
|
38
|
+
success(msg: string): void;
|
|
39
|
+
warn(msg: string): void;
|
|
40
|
+
error(msg: string): void;
|
|
41
|
+
debug(msg: string): void;
|
|
42
|
+
custom(segments: ColoredMessage[]): void;
|
|
43
|
+
private push;
|
|
44
|
+
}
|
|
45
|
+
type PromptStateListener = (request: null) => void;
|
|
46
|
+
export declare class AutoPrompt implements PromptInterface {
|
|
47
|
+
/** CLI TaskCard 消费:永远无待处理请求 */
|
|
48
|
+
get hasPendingRequest(): boolean;
|
|
49
|
+
/** CLI usePrompt 消费:永远无当前请求 */
|
|
50
|
+
get currentRequest(): null;
|
|
51
|
+
/** CLI usePrompt 消费:订阅提示状态(只读会话永远不会产生请求) */
|
|
52
|
+
subscribe(listener: PromptStateListener): () => void;
|
|
53
|
+
/** CLI suspend/resume 消费:空实现 */
|
|
54
|
+
suspend(): void;
|
|
55
|
+
resume(): void;
|
|
56
|
+
/** CLI killSession 消费:空实现 */
|
|
57
|
+
cancelAll(): void;
|
|
58
|
+
/** CLI closeSession 消费:空实现 */
|
|
59
|
+
destroy(): void;
|
|
60
|
+
ask<K extends string>(question: PromptQuestion<K>): Promise<Record<K, any>>;
|
|
61
|
+
}
|
|
62
|
+
interface SpinnerState {
|
|
63
|
+
active: boolean;
|
|
64
|
+
text: string;
|
|
65
|
+
variant?: 'loading' | 'success' | 'fail' | 'info';
|
|
66
|
+
}
|
|
67
|
+
export declare class LogSpinner implements SpinnerInterface {
|
|
68
|
+
private logger;
|
|
69
|
+
constructor(logger: LoggerInterface);
|
|
70
|
+
/** CLI useSpinnerState 消费:永远返回空状态 */
|
|
71
|
+
get state(): SpinnerState;
|
|
72
|
+
/** CLI useSpinnerState 消费:订阅 spinner 状态(MCP 不通过此通道推送) */
|
|
73
|
+
subscribe(_listener: (state: SpinnerState) => void): () => void;
|
|
74
|
+
/** CLI suspend/resume 消费:空实现 */
|
|
75
|
+
suspend(): void;
|
|
76
|
+
resume(): void;
|
|
77
|
+
/** CLI killSession 消费:空实现 */
|
|
78
|
+
clear(): void;
|
|
79
|
+
/** CLI closeSession 消费:空实现 */
|
|
80
|
+
destroy(): void;
|
|
81
|
+
create(text?: string): SpinnerInstance;
|
|
82
|
+
}
|
|
83
|
+
export declare class LogToast implements ToastInterface {
|
|
84
|
+
private logger;
|
|
85
|
+
constructor(logger: LoggerInterface);
|
|
86
|
+
show(message: string, level?: ToastLevel, _options?: ToastOptions): number;
|
|
87
|
+
update(_id: number, message: string, level?: ToastLevel, _options?: ToastOptions): number;
|
|
88
|
+
dismiss(_id: number): void;
|
|
89
|
+
}
|
|
90
|
+
export {};
|
|
91
|
+
//# sourceMappingURL=adapters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"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"}
|
package/dist/adapters.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @xfe-repo/cli-plugin-mcp - Headless IO 适配器
|
|
3
|
+
*
|
|
4
|
+
* MCP session 使用的无头适配器:
|
|
5
|
+
* - MemoryLogger: 内存日志收集器,支持 getEntries() 供日志查询
|
|
6
|
+
* - AutoPrompt: 非交互式 Prompt,列出可选项并提示 prefilled 用法
|
|
7
|
+
* - LogSpinner: 将 spinner 状态输出为日志
|
|
8
|
+
* - LogToast: 将 toast 消息输出为日志
|
|
9
|
+
*
|
|
10
|
+
* CLI 兼容:
|
|
11
|
+
* 当 MCP session 需要在 CLI 任务列表中以只读模式展示时,
|
|
12
|
+
* CLI 视图层会调用 subscribe/unreadCount/resetUnread/destroy 等方法。
|
|
13
|
+
* 这里补齐这些最小兼容方法,使 MCP adapter 可被 CLI 安全消费而不抛异常。
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_MAX_ENTRIES = 2000;
|
|
16
|
+
export class MemoryLogger {
|
|
17
|
+
entries = [];
|
|
18
|
+
maxEntries;
|
|
19
|
+
listeners = new Set();
|
|
20
|
+
renderedCursor = 0;
|
|
21
|
+
readCursor = 0;
|
|
22
|
+
constructor(maxEntries = DEFAULT_MAX_ENTRIES) {
|
|
23
|
+
this.maxEntries = maxEntries;
|
|
24
|
+
}
|
|
25
|
+
/** 未读日志数量(CLI TaskCard 消费) */
|
|
26
|
+
get unreadCount() {
|
|
27
|
+
return this.entries.length - this.readCursor;
|
|
28
|
+
}
|
|
29
|
+
/** 重置未读计数(CLI suspend/resume 消费) */
|
|
30
|
+
resetUnread() {
|
|
31
|
+
this.readCursor = this.entries.length;
|
|
32
|
+
}
|
|
33
|
+
getEntries() {
|
|
34
|
+
return [...this.entries];
|
|
35
|
+
}
|
|
36
|
+
/** 获取尚未被终端输出过的条目(CLI useLogs 消费) */
|
|
37
|
+
getUnrenderedEntries() {
|
|
38
|
+
return this.entries.slice(this.renderedCursor);
|
|
39
|
+
}
|
|
40
|
+
/** 将游标推进到当前末尾(CLI useLogs 消费) */
|
|
41
|
+
markRendered() {
|
|
42
|
+
this.renderedCursor = this.entries.length;
|
|
43
|
+
}
|
|
44
|
+
/** 订阅日志更新(CLI useLogs 消费) */
|
|
45
|
+
subscribe(listener) {
|
|
46
|
+
this.listeners.add(listener);
|
|
47
|
+
return () => this.listeners.delete(listener);
|
|
48
|
+
}
|
|
49
|
+
/** 销毁实例,释放引用(CLI closeSession 消费) */
|
|
50
|
+
destroy() {
|
|
51
|
+
this.listeners.clear();
|
|
52
|
+
this.entries = [];
|
|
53
|
+
this.renderedCursor = 0;
|
|
54
|
+
this.readCursor = 0;
|
|
55
|
+
}
|
|
56
|
+
log(msg) {
|
|
57
|
+
this.push({ level: 'log', message: msg, timestamp: Date.now() });
|
|
58
|
+
}
|
|
59
|
+
success(msg) {
|
|
60
|
+
this.push({ level: 'success', message: msg, timestamp: Date.now() });
|
|
61
|
+
}
|
|
62
|
+
warn(msg) {
|
|
63
|
+
this.push({ level: 'warn', message: msg, timestamp: Date.now() });
|
|
64
|
+
}
|
|
65
|
+
error(msg) {
|
|
66
|
+
this.push({ level: 'error', message: msg, timestamp: Date.now() });
|
|
67
|
+
}
|
|
68
|
+
debug(msg) {
|
|
69
|
+
this.push({ level: 'debug', message: msg, timestamp: Date.now() });
|
|
70
|
+
}
|
|
71
|
+
custom(segments) {
|
|
72
|
+
this.push({
|
|
73
|
+
level: 'custom',
|
|
74
|
+
message: segments.map((s) => s.text).join(''),
|
|
75
|
+
segments,
|
|
76
|
+
timestamp: Date.now(),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
push(entry) {
|
|
80
|
+
this.entries.push(entry);
|
|
81
|
+
if (this.entries.length > this.maxEntries) {
|
|
82
|
+
const trimCount = this.entries.length - this.maxEntries;
|
|
83
|
+
this.entries = this.entries.slice(trimCount);
|
|
84
|
+
this.renderedCursor = Math.max(0, this.renderedCursor - trimCount);
|
|
85
|
+
this.readCursor = Math.max(0, this.readCursor - trimCount);
|
|
86
|
+
}
|
|
87
|
+
for (const listener of this.listeners) {
|
|
88
|
+
listener(entry);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export class AutoPrompt {
|
|
93
|
+
/** CLI TaskCard 消费:永远无待处理请求 */
|
|
94
|
+
get hasPendingRequest() {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
/** CLI usePrompt 消费:永远无当前请求 */
|
|
98
|
+
get currentRequest() {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
/** CLI usePrompt 消费:订阅提示状态(只读会话永远不会产生请求) */
|
|
102
|
+
subscribe(listener) {
|
|
103
|
+
// 不存储,MCP prompt 不会变更状态
|
|
104
|
+
return () => { };
|
|
105
|
+
}
|
|
106
|
+
/** CLI suspend/resume 消费:空实现 */
|
|
107
|
+
suspend() { }
|
|
108
|
+
resume() { }
|
|
109
|
+
/** CLI killSession 消费:空实现 */
|
|
110
|
+
cancelAll() { }
|
|
111
|
+
/** CLI closeSession 消费:空实现 */
|
|
112
|
+
destroy() { }
|
|
113
|
+
async ask(question) {
|
|
114
|
+
throw new Error(buildPromptErrorMessage(question));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const EMPTY_SPINNER_STATE = { active: false, text: '' };
|
|
118
|
+
export class LogSpinner {
|
|
119
|
+
logger;
|
|
120
|
+
constructor(logger) {
|
|
121
|
+
this.logger = logger;
|
|
122
|
+
}
|
|
123
|
+
/** CLI useSpinnerState 消费:永远返回空状态 */
|
|
124
|
+
get state() {
|
|
125
|
+
return { ...EMPTY_SPINNER_STATE };
|
|
126
|
+
}
|
|
127
|
+
/** CLI useSpinnerState 消费:订阅 spinner 状态(MCP 不通过此通道推送) */
|
|
128
|
+
subscribe(_listener) {
|
|
129
|
+
return () => { };
|
|
130
|
+
}
|
|
131
|
+
/** CLI suspend/resume 消费:空实现 */
|
|
132
|
+
suspend() { }
|
|
133
|
+
resume() { }
|
|
134
|
+
/** CLI killSession 消费:空实现 */
|
|
135
|
+
clear() { }
|
|
136
|
+
/** CLI closeSession 消费:空实现 */
|
|
137
|
+
destroy() { }
|
|
138
|
+
create(text) {
|
|
139
|
+
const logger = this.logger;
|
|
140
|
+
return {
|
|
141
|
+
start(t) {
|
|
142
|
+
logger.log(`请等待... ${t ?? text ?? ''}`.trimEnd());
|
|
143
|
+
},
|
|
144
|
+
stop() { },
|
|
145
|
+
succeed(t) {
|
|
146
|
+
logger.success(t ?? '');
|
|
147
|
+
},
|
|
148
|
+
fail(t) {
|
|
149
|
+
logger.error(t ?? '');
|
|
150
|
+
},
|
|
151
|
+
info(t) {
|
|
152
|
+
logger.log(t ?? '');
|
|
153
|
+
},
|
|
154
|
+
text: text ?? '',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// ─── LogToast ──────────────────────────────────────────────
|
|
159
|
+
export class LogToast {
|
|
160
|
+
logger;
|
|
161
|
+
constructor(logger) {
|
|
162
|
+
this.logger = logger;
|
|
163
|
+
}
|
|
164
|
+
show(message, level, _options) {
|
|
165
|
+
this.logger.log(`[${level ?? 'info'}]: ${message}`);
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
update(_id, message, level, _options) {
|
|
169
|
+
this.logger.log(`[${level ?? 'info'}]: ${message}`);
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
dismiss(_id) { }
|
|
173
|
+
}
|
|
174
|
+
// ─── Helpers ───────────────────────────────────────────────
|
|
175
|
+
function buildPromptErrorMessage(question) {
|
|
176
|
+
const lines = [
|
|
177
|
+
`MCP session 不支持交互式提示。`,
|
|
178
|
+
` 问题: ${question.message}`,
|
|
179
|
+
` 参数: --${String(question.name)} (类型: ${question.type})`,
|
|
180
|
+
];
|
|
181
|
+
if (question.default !== undefined) {
|
|
182
|
+
lines.push(` 默认值: ${String(question.default)}`);
|
|
183
|
+
}
|
|
184
|
+
if (question.choices?.length) {
|
|
185
|
+
lines.push(` 可选值:`);
|
|
186
|
+
for (const choice of question.choices) {
|
|
187
|
+
if (typeof choice === 'string') {
|
|
188
|
+
lines.push(` - ${choice}`);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
const c = choice;
|
|
192
|
+
const valueStr = typeof c.value === 'object' ? JSON.stringify(c.value) : String(c.value);
|
|
193
|
+
lines.push(` - ${valueStr} (${c.name})`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
lines.push(`提示: 请通过 prefilled 传入 --${String(question.name)} <值>`);
|
|
198
|
+
return lines.join('\n');
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=adapters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapters.js","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @xfe-repo/cli-plugin-mcp - MCP Server 插件
|
|
3
|
+
*
|
|
4
|
+
* 将 xfe-cli 注册的 CLI 命令暴露为 MCP Tools,供外部 Coder Agent 调用。
|
|
5
|
+
* Agent 可通过 MCP 启动构建/调试任务并增量拉取编译日志。
|
|
6
|
+
* 支持动态端口:首选端口被占用时自动递增选择可用端口。
|
|
7
|
+
*
|
|
8
|
+
* 启用/停用通过 xfe mcp:enable / mcp:disable 控制,开关存储在本地 store(~/.xfe/state.json),
|
|
9
|
+
* 端口配置保留在 xfe.json(项目共享)。
|
|
10
|
+
*/
|
|
11
|
+
import type { XfePlugin } from '@xfe-repo/cli-core';
|
|
12
|
+
type McpStatus = 'off' | 'running' | 'error';
|
|
13
|
+
declare const mcpStoreInitial: {
|
|
14
|
+
status: McpStatus;
|
|
15
|
+
port: number;
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
};
|
|
18
|
+
declare module '@xfe-repo/cli-core' {
|
|
19
|
+
interface XfePluginRegistry {
|
|
20
|
+
'ai-mcp': XfePlugin<typeof mcpStoreInitial>;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export declare function aiMcpPlugin(): XfePlugin;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,oBAAoB,CAAA;AAgBlE,KAAK,SAAS,GAAG,KAAK,GAAG,SAAS,GAAG,OAAO,CAAA;AAE5C,QAAA,MAAM,eAAe;YACF,SAAS;;;CAG3B,CAAA;AAED,OAAO,QAAQ,oBAAoB,CAAC;IAClC,UAAU,iBAAiB;QACzB,QAAQ,EAAE,SAAS,CAAC,OAAO,eAAe,CAAC,CAAA;KAC5C;CACF;AAID,wBAAgB,WAAW,IAAI,SAAS,CAoJvC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @xfe-repo/cli-plugin-mcp - MCP Server 插件
|
|
3
|
+
*
|
|
4
|
+
* 将 xfe-cli 注册的 CLI 命令暴露为 MCP Tools,供外部 Coder Agent 调用。
|
|
5
|
+
* Agent 可通过 MCP 启动构建/调试任务并增量拉取编译日志。
|
|
6
|
+
* 支持动态端口:首选端口被占用时自动递增选择可用端口。
|
|
7
|
+
*
|
|
8
|
+
* 启用/停用通过 xfe mcp:enable / mcp:disable 控制,开关存储在本地 store(~/.xfe/state.json),
|
|
9
|
+
* 端口配置保留在 xfe.json(项目共享)。
|
|
10
|
+
*/
|
|
11
|
+
import getPort, { portNumbers } from 'get-port';
|
|
12
|
+
import { aiRulesPlugin, SyncItemType } from '@xfe-repo/cli-plugin-ai-rules';
|
|
13
|
+
import { startMcpServer, stopMcpServer } from './server.js';
|
|
14
|
+
// ─── Constants ─────────────────────────────────────────────
|
|
15
|
+
const DEFAULT_MCP_PORT = 30000;
|
|
16
|
+
const PORT_RANGE_SIZE = 100;
|
|
17
|
+
const mcpStoreInitial = {
|
|
18
|
+
status: 'off',
|
|
19
|
+
port: 0,
|
|
20
|
+
enabled: false,
|
|
21
|
+
};
|
|
22
|
+
// ─── Plugin ────────────────────────────────────────────────
|
|
23
|
+
export function aiMcpPlugin() {
|
|
24
|
+
return {
|
|
25
|
+
name: 'ai-mcp',
|
|
26
|
+
dependencies: [aiRulesPlugin],
|
|
27
|
+
setup(ctx, hooks) {
|
|
28
|
+
let serverState;
|
|
29
|
+
hooks.registerStore(mcpStoreInitial);
|
|
30
|
+
hooks.registerView({
|
|
31
|
+
name: 'mcp-server',
|
|
32
|
+
label: 'MCP',
|
|
33
|
+
icon: '⬡',
|
|
34
|
+
color: 'blue',
|
|
35
|
+
slot: 'footer',
|
|
36
|
+
value: (state) => {
|
|
37
|
+
const mcp = state['ai-mcp'];
|
|
38
|
+
if (!mcp || mcp.status === 'off')
|
|
39
|
+
return undefined;
|
|
40
|
+
if (mcp.status === 'error')
|
|
41
|
+
return '异常';
|
|
42
|
+
return `MCP:${mcp.port}`;
|
|
43
|
+
},
|
|
44
|
+
url: (state) => {
|
|
45
|
+
const mcp = state['ai-mcp'];
|
|
46
|
+
if (!mcp || mcp.status !== 'running')
|
|
47
|
+
return undefined;
|
|
48
|
+
return `http://localhost:${mcp.port}/health`;
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
// ── Runtime Helpers(启停由 onAppInit / onCleanup / mcp 命令共用) ──
|
|
52
|
+
async function startRuntime(ctx) {
|
|
53
|
+
if (serverState)
|
|
54
|
+
return;
|
|
55
|
+
const preferredPort = ctx.store.getState().xfe?.aiMcp?.port ?? DEFAULT_MCP_PORT;
|
|
56
|
+
const port = await getPort({ port: portNumbers(preferredPort, preferredPort + PORT_RANGE_SIZE) });
|
|
57
|
+
serverState = await startMcpServer(ctx, port);
|
|
58
|
+
ctx.store.setPluginState('ai-mcp', { status: 'running', port });
|
|
59
|
+
try {
|
|
60
|
+
const aiRules = ctx.inject('ai-rules');
|
|
61
|
+
aiRules.injectRules('mcp', buildMcpInjectionItems({ port }));
|
|
62
|
+
const result = await aiRules.applyInjections(ctx);
|
|
63
|
+
if (result.written > 0) {
|
|
64
|
+
ctx.toast.show(`MCP 已注入 (port:${port}), ${result.written} 个文件更新`, 'success');
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
ctx.toast.show(`MCP Server 已启动 (port:${port})`, 'success');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
ctx.toast.show(`MCP Server 已启动 (port:${port})`, 'success');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function stopRuntime(ctx) {
|
|
75
|
+
if (!serverState)
|
|
76
|
+
return;
|
|
77
|
+
try {
|
|
78
|
+
const aiRules = ctx.inject('ai-rules');
|
|
79
|
+
aiRules.removeInjectedRules('mcp');
|
|
80
|
+
await aiRules.applyInjections(ctx);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// 静默忽略清理失败
|
|
84
|
+
}
|
|
85
|
+
await stopMcpServer(serverState);
|
|
86
|
+
serverState = undefined;
|
|
87
|
+
ctx.store.setPluginState('ai-mcp', { status: 'off', port: 0 });
|
|
88
|
+
}
|
|
89
|
+
// ── Hooks ──
|
|
90
|
+
hooks.onAppInit.tap('ai-mcp', async (ctx) => {
|
|
91
|
+
const enabled = ctx.store.getState()['ai-mcp']?.enabled ?? true;
|
|
92
|
+
if (!enabled)
|
|
93
|
+
return;
|
|
94
|
+
try {
|
|
95
|
+
await startRuntime(ctx);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
99
|
+
const preferredPort = ctx.store.getState().xfe?.aiMcp?.port ?? DEFAULT_MCP_PORT;
|
|
100
|
+
ctx.toast.show(`MCP Server 启动失败: ${msg}`, 'error');
|
|
101
|
+
ctx.store.setPluginState('ai-mcp', { status: 'error', port: preferredPort });
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
hooks.onCleanup.tap('ai-mcp', async () => {
|
|
105
|
+
await stopRuntime(ctx);
|
|
106
|
+
});
|
|
107
|
+
// ── mcp:enable 命令 ──
|
|
108
|
+
hooks.registerCommand({
|
|
109
|
+
name: 'mcp:enable',
|
|
110
|
+
simpleDescription: '启用 MCP Server',
|
|
111
|
+
description: '启用当前项目的 MCP Server 并立即生效。开关状态存储在本地 (~/.xfe/state.json),不影响其他开发者。',
|
|
112
|
+
async execute(ctx) {
|
|
113
|
+
ctx.store.setPluginState('ai-mcp', { enabled: true });
|
|
114
|
+
if (serverState) {
|
|
115
|
+
ctx.logger.log('MCP Server 已处于运行状态');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
await startRuntime(ctx);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
123
|
+
ctx.logger.error(`MCP Server 启动失败: ${msg}`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// 直跑模式下 enable 需要常驻
|
|
127
|
+
if (ctx.appMode === 'direct') {
|
|
128
|
+
const port = ctx.store.getState()['ai-mcp']?.port ?? 0;
|
|
129
|
+
ctx.logger.success(`MCP Server 常驻运行中 (port:${port}),按 Ctrl+C 退出`);
|
|
130
|
+
await new Promise(() => { });
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
// ── mcp:disable 命令 ──
|
|
135
|
+
hooks.registerCommand({
|
|
136
|
+
name: 'mcp:disable',
|
|
137
|
+
simpleDescription: '停用 MCP Server',
|
|
138
|
+
description: '停用当前项目的 MCP Server 并立即生效。开关状态存储在本地 (~/.xfe/state.json),不影响其他开发者。',
|
|
139
|
+
async execute(ctx) {
|
|
140
|
+
ctx.store.setPluginState('ai-mcp', { enabled: false });
|
|
141
|
+
await stopRuntime(ctx);
|
|
142
|
+
ctx.logger.success('MCP Server 已停用');
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
// ─── Helpers ───────────────────────────────────────────────
|
|
149
|
+
function buildMcpInjectionItems(config) {
|
|
150
|
+
const mcpRulesContent = [
|
|
151
|
+
'## XFE 项目脚手架 (重要!!!)',
|
|
152
|
+
'',
|
|
153
|
+
'- 对于项目运行、构建、错误检查、环境切换,优先使用 `xfe-cli-mcp` 提供的工具,不要直接运行 pnpm、npm、yarn 等脚本命令。',
|
|
154
|
+
'- 所有命令工具都会立即返回 sessionId。拿到 sessionId 后,必须使用 xfe-get-session-logs 轮询日志和状态,直到 status 变为 completed 或 error。',
|
|
155
|
+
'- 除非用户明确要求停止任务,否则不要调用 xfe-kill-session 终止已有的开发任务。',
|
|
156
|
+
'- 项目错误、编译日志获取优先顺序是:`xfe-cli-mcp` dev session log > IDE TypeScript/ESLint 报错 > 相关模块代码链路',
|
|
157
|
+
].join('\n');
|
|
158
|
+
const serverConfig = {
|
|
159
|
+
type: 'http',
|
|
160
|
+
url: `http://localhost:${config.port}/mcp`,
|
|
161
|
+
};
|
|
162
|
+
return [
|
|
163
|
+
{
|
|
164
|
+
type: SyncItemType.Rule,
|
|
165
|
+
id: 'xfe-cli-rules',
|
|
166
|
+
name: 'XFE MCP Rules',
|
|
167
|
+
content: mcpRulesContent,
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
type: SyncItemType.McpServer,
|
|
171
|
+
id: 'xfe-cli-mcp',
|
|
172
|
+
name: 'XFE MCP Server',
|
|
173
|
+
serverConfig,
|
|
174
|
+
},
|
|
175
|
+
];
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @xfe-repo/cli-plugin-mcp - MCP Server
|
|
3
|
+
*
|
|
4
|
+
* 基于 Hono + Streamable HTTP Transport 的 MCP Server。
|
|
5
|
+
* 将 xfe-cli 注册的命令暴露为 MCP Tools,并提供 session 日志查询能力。
|
|
6
|
+
*/
|
|
7
|
+
import { serve } from '@hono/node-server';
|
|
8
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
|
+
import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
|
|
10
|
+
import type { PluginContext } from '@xfe-repo/cli-core';
|
|
11
|
+
interface SessionEntry {
|
|
12
|
+
mcpServer: McpServer;
|
|
13
|
+
transport: WebStandardStreamableHTTPServerTransport;
|
|
14
|
+
}
|
|
15
|
+
interface McpServerState {
|
|
16
|
+
httpServer: ReturnType<typeof serve>;
|
|
17
|
+
sessions: Map<string, SessionEntry>;
|
|
18
|
+
}
|
|
19
|
+
export declare function startMcpServer(ctx: PluginContext, port: number): Promise<McpServerState>;
|
|
20
|
+
export declare function stopMcpServer(state: McpServerState): Promise<void>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,EAAE,wCAAwC,EAAE,MAAM,+DAA+D,CAAA;AAKxH,OAAO,KAAK,EAAE,aAAa,EAAuC,MAAM,oBAAoB,CAAA;AAM5F,UAAU,YAAY;IACpB,SAAS,EAAE,SAAS,CAAA;IACpB,SAAS,EAAE,wCAAwC,CAAA;CACpD;AAED,UAAU,cAAc;IACtB,UAAU,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,CAAA;IACpC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;CACpC;AASD,wBAAsB,cAAc,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAM9F;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAUxE"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @xfe-repo/cli-plugin-mcp - MCP Server
|
|
3
|
+
*
|
|
4
|
+
* 基于 Hono + Streamable HTTP Transport 的 MCP Server。
|
|
5
|
+
* 将 xfe-cli 注册的命令暴露为 MCP Tools,并提供 session 日志查询能力。
|
|
6
|
+
*/
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
8
|
+
import { serve } from '@hono/node-server';
|
|
9
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
10
|
+
import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
|
|
11
|
+
import { Hono } from 'hono';
|
|
12
|
+
import { cors } from 'hono/cors';
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
import { MemoryLogger, AutoPrompt, LogSpinner } from './adapters.js';
|
|
15
|
+
const MCP_SERVER_INFO = { name: 'xfe-cli', version: '2.0.0' };
|
|
16
|
+
const LIST_SESSIONS_TOOL_NAME = 'xfe-list-sessions';
|
|
17
|
+
const GET_SESSION_LOGS_TOOL_NAME = 'xfe-get-session-logs';
|
|
18
|
+
const KILL_SESSION_TOOL_NAME = 'xfe-kill-session';
|
|
19
|
+
// ─── Public API ────────────────────────────────────────────
|
|
20
|
+
export async function startMcpServer(ctx, port) {
|
|
21
|
+
const sessions = new Map();
|
|
22
|
+
const app = createHttpApp(ctx, sessions);
|
|
23
|
+
const httpServer = serve({ fetch: app.fetch, port });
|
|
24
|
+
return { httpServer, sessions };
|
|
25
|
+
}
|
|
26
|
+
export async function stopMcpServer(state) {
|
|
27
|
+
for (const [, entry] of state.sessions) {
|
|
28
|
+
await entry.mcpServer.close();
|
|
29
|
+
}
|
|
30
|
+
state.sessions.clear();
|
|
31
|
+
await new Promise((resolve) => {
|
|
32
|
+
state.httpServer.close(() => resolve());
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
// ─── HTTP App ──────────────────────────────────────────────
|
|
36
|
+
function createMcpServer(ctx) {
|
|
37
|
+
const server = new McpServer(MCP_SERVER_INFO, { capabilities: { logging: {} } });
|
|
38
|
+
registerBuiltinTools(server, ctx);
|
|
39
|
+
registerCommandTools(server, ctx);
|
|
40
|
+
return server;
|
|
41
|
+
}
|
|
42
|
+
function createHttpApp(ctx, sessions) {
|
|
43
|
+
const app = new Hono();
|
|
44
|
+
app.use('*', cors({
|
|
45
|
+
origin: '*',
|
|
46
|
+
allowMethods: ['GET', 'POST', 'DELETE', 'OPTIONS'],
|
|
47
|
+
allowHeaders: ['Content-Type', 'mcp-session-id', 'Last-Event-ID', 'mcp-protocol-version'],
|
|
48
|
+
exposeHeaders: ['mcp-session-id', 'mcp-protocol-version'],
|
|
49
|
+
}));
|
|
50
|
+
app.get('/health', (c) => c.json({ status: 'ok', endpoint: '/mcp' }));
|
|
51
|
+
// MCP Streamable HTTP 端点
|
|
52
|
+
app.post('/mcp', async (c) => {
|
|
53
|
+
const sessionId = c.req.header('mcp-session-id');
|
|
54
|
+
if (sessionId && sessions.has(sessionId)) {
|
|
55
|
+
return sessions.get(sessionId).transport.handleRequest(c.req.raw);
|
|
56
|
+
}
|
|
57
|
+
const mcpServer = createMcpServer(ctx);
|
|
58
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
59
|
+
sessionIdGenerator: () => randomUUID(),
|
|
60
|
+
onsessioninitialized: (sid) => {
|
|
61
|
+
sessions.set(sid, { mcpServer, transport });
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
transport.onclose = () => {
|
|
65
|
+
const sid = transport.sessionId;
|
|
66
|
+
if (sid)
|
|
67
|
+
sessions.delete(sid);
|
|
68
|
+
};
|
|
69
|
+
await mcpServer.connect(transport);
|
|
70
|
+
return transport.handleRequest(c.req.raw);
|
|
71
|
+
});
|
|
72
|
+
app.get('/mcp', async (c) => {
|
|
73
|
+
const sessionId = c.req.header('mcp-session-id');
|
|
74
|
+
if (!sessionId || !sessions.has(sessionId)) {
|
|
75
|
+
return c.text('No active session', 400);
|
|
76
|
+
}
|
|
77
|
+
return sessions.get(sessionId).transport.handleRequest(c.req.raw);
|
|
78
|
+
});
|
|
79
|
+
app.on('DELETE', '/mcp', async (c) => {
|
|
80
|
+
const sessionId = c.req.header('mcp-session-id');
|
|
81
|
+
if (!sessionId || !sessions.has(sessionId)) {
|
|
82
|
+
return c.text('No active session', 400);
|
|
83
|
+
}
|
|
84
|
+
return sessions.get(sessionId).transport.handleRequest(c.req.raw);
|
|
85
|
+
});
|
|
86
|
+
return app;
|
|
87
|
+
}
|
|
88
|
+
// ─── Built-in Tools ────────────────────────────────────────
|
|
89
|
+
function registerBuiltinTools(mcpServer, ctx) {
|
|
90
|
+
mcpServer.registerTool(LIST_SESSIONS_TOOL_NAME, {
|
|
91
|
+
title: 'List XFE Sessions',
|
|
92
|
+
description: [
|
|
93
|
+
'列出当前项目中由 xfe-cli 管理的所有 session,包含 CLI 与 MCP 启动的任务。',
|
|
94
|
+
'返回字段包含 sessionId、command、source、status、startTime、endTime、errorMessage。',
|
|
95
|
+
'调用 dev/build/preview/upload 等长任务前,建议先查看是否已有可复用的 running session,避免重复启动。',
|
|
96
|
+
].join('\n'),
|
|
97
|
+
}, async () => {
|
|
98
|
+
const list = ctx.sessions.listSessions().map((s) => ({
|
|
99
|
+
sessionId: s.id,
|
|
100
|
+
command: s.commandName,
|
|
101
|
+
source: s.source,
|
|
102
|
+
status: s.status,
|
|
103
|
+
startTime: new Date(s.startTime).toISOString(),
|
|
104
|
+
endTime: s.endTime ? new Date(s.endTime).toISOString() : null,
|
|
105
|
+
errorMessage: s.errorMessage ?? null,
|
|
106
|
+
}));
|
|
107
|
+
return mcpResult(list);
|
|
108
|
+
});
|
|
109
|
+
mcpServer.registerTool(GET_SESSION_LOGS_TOOL_NAME, {
|
|
110
|
+
title: 'Get Session Logs',
|
|
111
|
+
description: [
|
|
112
|
+
'按 offset/limit 分页读取指定 session 的日志片段。',
|
|
113
|
+
'返回字段包含 logs、status、totalLines、hasMore。',
|
|
114
|
+
'长任务启动后应循环调用此工具,直到 status 变为 completed 或 error。',
|
|
115
|
+
].join('\n'),
|
|
116
|
+
inputSchema: z.object({
|
|
117
|
+
sessionId: z.string().describe('要读取日志的 sessionId,来自命令工具返回值或 xfe-list-sessions'),
|
|
118
|
+
offset: z.number().optional().describe('从第几条日志开始读取,默认 0;下一次可直接传上一次返回的 totalLines'),
|
|
119
|
+
limit: z.number().optional().describe('本次最多返回多少条日志,默认 200'),
|
|
120
|
+
}),
|
|
121
|
+
}, async ({ sessionId, offset, limit }) => {
|
|
122
|
+
const session = resolveSession(ctx, sessionId);
|
|
123
|
+
if (!session)
|
|
124
|
+
return mcpError(`Session "${sessionId}" 不存在`);
|
|
125
|
+
return mcpResult(ctx.sessions.getSessionLogs(sessionId, { offset, limit }));
|
|
126
|
+
});
|
|
127
|
+
mcpServer.registerTool(KILL_SESSION_TOOL_NAME, {
|
|
128
|
+
title: 'Kill Session',
|
|
129
|
+
description: ['终止指定的 running session。', '仅在任务确实需要停止时调用,不要终止仍需复用的 dev 等长运行任务。'].join('\n'),
|
|
130
|
+
inputSchema: z.object({
|
|
131
|
+
sessionId: z.string().describe('要终止的 Session ID'),
|
|
132
|
+
}),
|
|
133
|
+
}, async ({ sessionId }) => {
|
|
134
|
+
const session = resolveSession(ctx, sessionId);
|
|
135
|
+
if (!session)
|
|
136
|
+
return mcpError(`Session "${sessionId}" 不存在`);
|
|
137
|
+
if (session.status !== 'running')
|
|
138
|
+
return mcpError(`Session "${sessionId}" 已结束 (${session.status})`);
|
|
139
|
+
await ctx.sessions.killSession(sessionId);
|
|
140
|
+
return mcpResult(`Session "${sessionId}" 已终止`);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// ─── Command Tools ─────────────────────────────────────────
|
|
144
|
+
function registerCommandTools(mcpServer, ctx) {
|
|
145
|
+
for (const { command, source } of ctx.commands) {
|
|
146
|
+
registerSingleCommandTool(mcpServer, ctx, command, source);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function registerSingleCommandTool(mcpServer, ctx, command, pluginName) {
|
|
150
|
+
const toolName = `xfe-${command.name.replace(/:/g, '-')}`;
|
|
151
|
+
const description = buildCommandToolDescription(command, pluginName);
|
|
152
|
+
const toolConfig = {
|
|
153
|
+
title: `XFE: ${command.name}`,
|
|
154
|
+
description,
|
|
155
|
+
};
|
|
156
|
+
if (command.parameters) {
|
|
157
|
+
toolConfig.inputSchema = command.parameters;
|
|
158
|
+
}
|
|
159
|
+
mcpServer.registerTool(toolName, toolConfig, (params) => executeCommandAsTool(ctx, command, params));
|
|
160
|
+
}
|
|
161
|
+
function executeCommandAsTool(ctx, command, params) {
|
|
162
|
+
const logger = new MemoryLogger();
|
|
163
|
+
const sessionId = ctx.sessions.createSession({
|
|
164
|
+
commandName: command.name,
|
|
165
|
+
source: 'mcp',
|
|
166
|
+
createLogger: () => logger,
|
|
167
|
+
createPrompt: () => new AutoPrompt(),
|
|
168
|
+
createSpinner: () => new LogSpinner(logger),
|
|
169
|
+
prefilled: params,
|
|
170
|
+
});
|
|
171
|
+
return mcpResult({
|
|
172
|
+
sessionId,
|
|
173
|
+
command: command.name,
|
|
174
|
+
status: 'running',
|
|
175
|
+
source: 'mcp',
|
|
176
|
+
logTool: GET_SESSION_LOGS_TOOL_NAME,
|
|
177
|
+
listSessionsTool: LIST_SESSIONS_TOOL_NAME,
|
|
178
|
+
killSessionTool: KILL_SESSION_TOOL_NAME,
|
|
179
|
+
message: `命令 "${command.name}" 已启动。请使用 ${GET_SESSION_LOGS_TOOL_NAME} 基于 sessionId 拉取日志。`,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
function mcpResult(data) {
|
|
183
|
+
const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
|
|
184
|
+
return { content: [{ type: 'text', text }] };
|
|
185
|
+
}
|
|
186
|
+
function mcpError(message) {
|
|
187
|
+
return { content: [{ type: 'text', text: message }], isError: true };
|
|
188
|
+
}
|
|
189
|
+
function buildCommandToolDescription(command, pluginName) {
|
|
190
|
+
const lines = [
|
|
191
|
+
`[来源插件: ${pluginName}]`,
|
|
192
|
+
command.description ?? command.simpleDescription,
|
|
193
|
+
'调用该工具会异步创建一个新的 xfe session 并立即返回 sessionId,不会等待命令执行完成。',
|
|
194
|
+
`调用后请使用 ${GET_SESSION_LOGS_TOOL_NAME} 轮询日志;如需确认是否已有可复用任务,先使用 ${LIST_SESSIONS_TOOL_NAME}。`,
|
|
195
|
+
];
|
|
196
|
+
if (command.parameters) {
|
|
197
|
+
lines.push('若命令可能触发交互,请在本次调用中一次性传入所有相关参数。');
|
|
198
|
+
}
|
|
199
|
+
return lines.join('\n');
|
|
200
|
+
}
|
|
201
|
+
function resolveSession(ctx, sessionId) {
|
|
202
|
+
return ctx.sessions.getSession(sessionId);
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xfe-repo/cli-plugin-ai-mcp",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "XFE CLI MCP Server 插件 —— 将 CLI 命令暴露为 MCP Tools",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
10
|
+
"@hono/node-server": "^1.14.0",
|
|
11
|
+
"get-port": "^7.1.0",
|
|
12
|
+
"hono": "^4.7.0",
|
|
13
|
+
"zod": "^4.3.6",
|
|
14
|
+
"@xfe-repo/cli-core": "2.0.0",
|
|
15
|
+
"@xfe-repo/cli-plugin-ai-rules": "2.0.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^24.3.0",
|
|
19
|
+
"typescript": "^5.9.2"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"license": "ISC",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"dev": "tsc -p tsconfig.json -w",
|
|
27
|
+
"build": "tsc -p tsconfig.json",
|
|
28
|
+
"clean": "rm -rf dist"
|
|
29
|
+
}
|
|
30
|
+
}
|