@skillfm/local 2.0.10 → 2.1.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/README.md CHANGED
@@ -14,11 +14,12 @@ This package sidesteps the problem: instead of asking the agent to install a new
14
14
  npx -y @skillfm/local@latest start
15
15
  ```
16
16
 
17
- > The package exposes three bin aliases: `local` (natural npx form — works because
18
- > npx invokes the file path directly and never hits bash's `local` keyword),
19
- > `skillfm-local` (same entry use this if you `npm i -g @skillfm/local`, since
20
- > bash would shadow a global `local` command with its reserved keyword), and
21
- > `skillfm-guard` (hook enforcement shim used by harness integrations).
17
+ > The package exposes four bin aliases:
18
+ > - `local` — natural npx form (npx invokes the file path directly, never hits bash's `local` keyword).
19
+ > - `skillfm-local` same HTTP sidecar entry; use this if `npm i -g @skillfm/local`, since bash would shadow a global `local`.
20
+ > - `skillfm-guard` hook enforcement shim used by harness integrations.
21
+ > - `skillfm-mcp-stdio` — **stdio MCP server entry** (new in 2.1.0), for stdio-only agent runtimes like Claude Desktop. See [stdio MCP entry](#stdio-mcp-entry-new-in-210) below.
22
+ >
22
23
  > Explicit equivalent: `npx -y --package @skillfm/local@latest skillfm-local start`.
23
24
 
24
25
  Output is a single line of JSON the agent can parse:
@@ -80,14 +81,47 @@ All of this happens in a single conversation turn sequence. No MCP client reload
80
81
  - **Zero runtime dependencies**: only Node built-ins (`http`, `fs`, `os`, `crypto`). The entire source is ~400 lines in one file. Audit it yourself: [sdk/skillfm-local/src/index.ts](https://github.com/ericm1018/skillfm/blob/main/sdk/skillfm-local/src/index.ts).
81
82
  - **Kill switch**: `skillfm-local stop`, or just `kill <pid>`. The settings file is removed on exit.
82
83
 
83
- ## Relationship to @skillfm/mcp-server
84
+ ## stdio MCP entry (new in 2.1.0)
84
85
 
85
- This package is the **fast-path activation** companion to [@skillfm/mcp-server](https://www.npmjs.com/package/@skillfm/mcp-server). They are complementary, not competing:
86
+ Starting with 2.1.0, `@skillfm/local` also ships a stdio MCP server entry
87
+ `skillfm-mcp-stdio` — so stdio-only agent runtimes (Claude Desktop, VS Code
88
+ Claude extension stdio mode, etc.) no longer need a separate npm package.
86
89
 
87
- - **`@skillfm/local`** (this package) — immediate use in the current conversation via localhost HTTP. Runs in seconds, no config file edits, no runtime restart.
88
- - **`@skillfm/mcp-server`** — proper MCP integration with schema-validated tools. Installed via `claude mcp add` or equivalent, activates after the next runtime cold start for a permanent upgrade.
90
+ Claude Desktop config example:
89
91
 
90
- Both share `~/.skillfm/config.json` so once you activate via `@skillfm/local`, the next time you restart your MCP runtime the MCP path picks up the same brain_key and the 7 MCP tools just work.
92
+ ```jsonc
93
+ {
94
+ "mcpServers": {
95
+ "skillfm": {
96
+ "command": "npx",
97
+ "args": ["-y", "--package", "@skillfm/local@latest", "skillfm-mcp-stdio"]
98
+ }
99
+ }
100
+ }
101
+ ```
102
+
103
+ This exposes the same 7 tools (`list_skills`, `brain_run`, `continuation_abort`,
104
+ `charter_get`, `charter_ack`, `subscribe`, `my_status`) + 2 resources
105
+ (`skill-manifest`, `skill-catalog`) as the previously-separate
106
+ `@skillfm/mcp-server`. brain_key is read from `~/.skillfm/config.json` — the
107
+ same file written by `skillfm-local start`, so a single activation serves both
108
+ transports.
109
+
110
+ ### Which entry to use
111
+
112
+ | Agent runtime | Entry |
113
+ |---|---|
114
+ | Claude Code / Cursor / Cline / OpenClaw / Aider (HTTP-capable) | `skillfm-local start` (HTTP sidecar, 127.0.0.1:19821) |
115
+ | Claude Desktop (stdio-only) | `skillfm-mcp-stdio` |
116
+
117
+ Both share `~/.skillfm/config.json` — activate once, either path works.
118
+
119
+ ## Deprecation of @skillfm/mcp-server
120
+
121
+ `@skillfm/mcp-server@3.5.0` (released 2026-04-20) is the last standalone release.
122
+ From 2.1.0 onward use `@skillfm/local`'s `skillfm-mcp-stdio` bin instead.
123
+ `@skillfm/mcp-server` will receive a `npm deprecate` tag pointing to this
124
+ package once 2.1.0 has been stable for 48h.
91
125
 
92
126
  ## License
93
127
 
package/dist/guard/bin.js CHANGED
File without changes
package/dist/index.js CHANGED
File without changes
@@ -0,0 +1,28 @@
1
+ import type { RequestContext } from './request-context.js';
2
+ interface RequestOptions {
3
+ method?: 'GET' | 'POST';
4
+ body?: Record<string, unknown>;
5
+ useAuth?: 'brain-key' | 'jwt' | 'none';
6
+ /**
7
+ * 可选:per-request 上下文。HTTP 多租户模式下必传。
8
+ * 未传时回退到模块级 config + 全局 conv-state(stdio 单租户 / tests)。
9
+ */
10
+ ctx?: RequestContext;
11
+ }
12
+ export declare function getConversationState(): string | null;
13
+ export declare function setConversationState(s: string | null): void;
14
+ /**
15
+ * 把一份反馈结构(nudge / seed / proof)排入下一次出站请求。
16
+ * 一次性消费 — 注入后立即清空, 避免重复发送。
17
+ */
18
+ export declare function pushConversationFeedback(payload: unknown): void;
19
+ export declare function _resetConversationState(): void;
20
+ export declare class ApiError extends Error {
21
+ status: number;
22
+ envelope: unknown;
23
+ body: unknown;
24
+ constructor(message: string, status: number, envelope?: unknown, body?: unknown);
25
+ }
26
+ export declare function apiCall<T = unknown>(path: string, options?: RequestOptions): Promise<T>;
27
+ export {};
28
+ //# sourceMappingURL=api-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/mcp-stdio/api-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,WAAW,GAAG,KAAK,GAAG,MAAM,CAAC;IACvC;;;OAGG;IACH,GAAG,CAAC,EAAE,cAAc,CAAC;CACtB;AAaD,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,IAAI,CAEpD;AAED,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAE3D;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE/D;AAED,wBAAgB,uBAAuB,IAAI,IAAI,CAG9C;AAMD,qBAAa,QAAS,SAAQ,KAAK;IAGxB,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,OAAO;IACjB,IAAI,EAAE,OAAO;gBAHpB,OAAO,EAAE,MAAM,EACR,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,OAAc,EACxB,IAAI,GAAE,OAAc;CAK9B;AAED,wBAAsB,OAAO,CAAC,CAAC,GAAG,OAAO,EACvC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,CAAC,CAAC,CAiGZ"}
@@ -0,0 +1,123 @@
1
+ import { config } from './config.js';
2
+ const MAX_RETRIES = 2;
3
+ const TIMEOUT_MS = 120_000; // Brain 管线可能耗时较长
4
+ // ============================================================================
5
+ // Module-level conv-state — 仅给 stdio 单租户/tests 使用
6
+ // HTTP 多租户模式下走 RequestContext.convState(ConvStateStore 按 brainKey 隔离)
7
+ // ============================================================================
8
+ let _convState = null;
9
+ let _convFeedback = null;
10
+ export function getConversationState() {
11
+ return _convState;
12
+ }
13
+ export function setConversationState(s) {
14
+ _convState = s;
15
+ }
16
+ /**
17
+ * 把一份反馈结构(nudge / seed / proof)排入下一次出站请求。
18
+ * 一次性消费 — 注入后立即清空, 避免重复发送。
19
+ */
20
+ export function pushConversationFeedback(payload) {
21
+ _convFeedback = payload === null ? null : JSON.stringify(payload);
22
+ }
23
+ export function _resetConversationState() {
24
+ _convState = null;
25
+ _convFeedback = null;
26
+ }
27
+ // ============================================================================
28
+ // ApiError — 把 server envelope 透传给调用方, 让 MCP 工具可以渲染 dialogue_flow
29
+ // ============================================================================
30
+ export class ApiError extends Error {
31
+ status;
32
+ envelope;
33
+ body;
34
+ constructor(message, status, envelope = null, body = null) {
35
+ super(message);
36
+ this.status = status;
37
+ this.envelope = envelope;
38
+ this.body = body;
39
+ this.name = 'ApiError';
40
+ }
41
+ }
42
+ export async function apiCall(path, options = {}) {
43
+ const { method = 'GET', body, useAuth = 'none', ctx } = options;
44
+ // 选源: ctx 优先 (HTTP 多租户), 否则 config + 模块 globals (stdio / tests)
45
+ const apiBaseUrl = ctx?.apiBaseUrl ?? config.apiBaseUrl;
46
+ const brainKey = ctx?.brainKey ?? config.brainKey;
47
+ const jwtToken = ctx?.jwtToken ?? config.token;
48
+ const headers = {
49
+ 'Content-Type': 'application/json',
50
+ 'User-Agent': 'SkillFMMCP/0.1.0',
51
+ };
52
+ if (useAuth === 'brain-key' && brainKey) {
53
+ headers['X-Brain-Key'] = brainKey;
54
+ }
55
+ else if (useAuth === 'jwt' && jwtToken) {
56
+ headers['Authorization'] = `Bearer ${jwtToken}`;
57
+ }
58
+ // 透传 conversation-state header(来自上一次 server 响应)
59
+ const convStateRead = ctx?.convState.get() ?? _convState;
60
+ if (convStateRead) {
61
+ headers['X-Conversation-State'] = convStateRead;
62
+ }
63
+ // feedback 一次性消费
64
+ if (ctx) {
65
+ const f = ctx.convState.consumeFeedback();
66
+ if (f)
67
+ headers['X-Conversation-Feedback'] = f;
68
+ }
69
+ else if (_convFeedback) {
70
+ headers['X-Conversation-Feedback'] = _convFeedback;
71
+ _convFeedback = null;
72
+ }
73
+ const url = `${apiBaseUrl}${path}`;
74
+ let lastError = null;
75
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
76
+ const controller = new AbortController();
77
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
78
+ try {
79
+ const res = await fetch(url, {
80
+ method,
81
+ headers,
82
+ body: body ? JSON.stringify(body) : undefined,
83
+ signal: controller.signal,
84
+ });
85
+ clearTimeout(timer);
86
+ // 5xx 可重试
87
+ if (res.status >= 500 && attempt < MAX_RETRIES) {
88
+ const errData = await res.json().catch(() => ({ error: res.statusText }));
89
+ lastError = new Error(`API 错误 ${res.status}: ${errData.error || res.statusText}`);
90
+ continue;
91
+ }
92
+ // 不论成功还是软错误, 都要把 server 写回来的 conversation-state 拿走
93
+ const newState = res.headers.get('x-conversation-state');
94
+ if (newState) {
95
+ if (ctx)
96
+ ctx.convState.set(newState);
97
+ else
98
+ _convState = newState;
99
+ }
100
+ if (!res.ok) {
101
+ const errBody = (await res.json().catch(() => ({ error: res.statusText })));
102
+ const message = errBody?.error || res.statusText;
103
+ // 把 envelope 透传给调用方 — Agent 工具可以渲染 dialogue_flow
104
+ throw new ApiError(`API 错误 ${res.status}: ${message}`, res.status, errBody?.envelope ?? null, errBody);
105
+ }
106
+ return res.json();
107
+ }
108
+ catch (err) {
109
+ clearTimeout(timer);
110
+ if (err.name === 'AbortError') {
111
+ throw new Error('请求超时');
112
+ }
113
+ // 网络错误可重试
114
+ if (attempt < MAX_RETRIES && !err.message?.startsWith('API 错误')) {
115
+ lastError = err;
116
+ continue;
117
+ }
118
+ throw err;
119
+ }
120
+ }
121
+ throw lastError || new Error('请求失败');
122
+ }
123
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/mcp-stdio/api-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAcrC,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,iBAAiB;AAE7C,+EAA+E;AAC/E,kDAAkD;AAClD,sEAAsE;AACtE,+EAA+E;AAE/E,IAAI,UAAU,GAAkB,IAAI,CAAC;AACrC,IAAI,aAAa,GAAkB,IAAI,CAAC;AAExC,MAAM,UAAU,oBAAoB;IAClC,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,CAAgB;IACnD,UAAU,GAAG,CAAC,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAgB;IACvD,aAAa,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,UAAU,GAAG,IAAI,CAAC;IAClB,aAAa,GAAG,IAAI,CAAC;AACvB,CAAC;AAED,+EAA+E;AAC/E,kEAAkE;AAClE,+EAA+E;AAE/E,MAAM,OAAO,QAAS,SAAQ,KAAK;IAGxB;IACA;IACA;IAJT,YACE,OAAe,EACR,MAAc,EACd,WAAoB,IAAI,EACxB,OAAgB,IAAI;QAE3B,KAAK,CAAC,OAAO,CAAC,CAAC;QAJR,WAAM,GAAN,MAAM,CAAQ;QACd,aAAQ,GAAR,QAAQ,CAAgB;QACxB,SAAI,GAAJ,IAAI,CAAgB;QAG3B,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,IAAY,EACZ,UAA0B,EAAE;IAE5B,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IAEhE,gEAAgE;IAChE,MAAM,UAAU,GAAG,GAAG,EAAE,UAAU,IAAI,MAAM,CAAC,UAAU,CAAC;IACxD,MAAM,QAAQ,GAAG,GAAG,EAAE,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC;IAClD,MAAM,QAAQ,GAAG,GAAG,EAAE,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC;IAE/C,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;QAClC,YAAY,EAAE,kBAAkB;KACjC,CAAC;IAEF,IAAI,OAAO,KAAK,WAAW,IAAI,QAAQ,EAAE,CAAC;QACxC,OAAO,CAAC,aAAa,CAAC,GAAG,QAAQ,CAAC;IACpC,CAAC;SAAM,IAAI,OAAO,KAAK,KAAK,IAAI,QAAQ,EAAE,CAAC;QACzC,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,QAAQ,EAAE,CAAC;IAClD,CAAC;IAED,gDAAgD;IAChD,MAAM,aAAa,GAAG,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,IAAI,UAAU,CAAC;IACzD,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,CAAC,sBAAsB,CAAC,GAAG,aAAa,CAAC;IAClD,CAAC;IACD,iBAAiB;IACjB,IAAI,GAAG,EAAE,CAAC;QACR,MAAM,CAAC,GAAG,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,CAAC;QAC1C,IAAI,CAAC;YAAE,OAAO,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC;IAChD,CAAC;SAAM,IAAI,aAAa,EAAE,CAAC;QACzB,OAAO,CAAC,yBAAyB,CAAC,GAAG,aAAa,CAAC;QACnD,aAAa,GAAG,IAAI,CAAC;IACvB,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,UAAU,GAAG,IAAI,EAAE,CAAC;IACnC,IAAI,SAAS,GAAiB,IAAI,CAAC;IAEnC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QACxD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,UAAU,CAAC,CAAC;QAE/D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC3B,MAAM;gBACN,OAAO;gBACP,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;gBAC7C,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CAAC;YAEH,YAAY,CAAC,KAAK,CAAC,CAAC;YAEpB,UAAU;YACV,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;gBAC/C,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;gBAC1E,SAAS,GAAG,IAAI,KAAK,CACnB,UAAU,GAAG,CAAC,MAAM,KAAM,OAAe,CAAC,KAAK,IAAI,GAAG,CAAC,UAAU,EAAE,CACpE,CAAC;gBACF,SAAS;YACX,CAAC;YAED,mDAAmD;YACnD,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;YACzD,IAAI,QAAQ,EAAE,CAAC;gBACb,IAAI,GAAG;oBAAE,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;;oBAChC,UAAU,GAAG,QAAQ,CAAC;YAC7B,CAAC;YAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAQ,CAAC;gBACnF,MAAM,OAAO,GAAG,OAAO,EAAE,KAAK,IAAI,GAAG,CAAC,UAAU,CAAC;gBACjD,iDAAiD;gBACjD,MAAM,IAAI,QAAQ,CAChB,UAAU,GAAG,CAAC,MAAM,KAAK,OAAO,EAAE,EAClC,GAAG,CAAC,MAAM,EACV,OAAO,EAAE,QAAQ,IAAI,IAAI,EACzB,OAAO,CACR,CAAC;YACJ,CAAC;YAED,OAAO,GAAG,CAAC,IAAI,EAAgB,CAAC;QAClC,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,YAAY,CAAC,KAAK,CAAC,CAAC;YAEpB,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC9B,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;YAC1B,CAAC;YAED,UAAU;YACV,IAAI,OAAO,GAAG,WAAW,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAChE,SAAS,GAAG,GAAG,CAAC;gBAChB,SAAS;YACX,CAAC;YAED,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,MAAM,SAAS,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../../src/mcp-stdio/bin.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ // @skillfm/local — skillfm-mcp-stdio bin
3
+ //
4
+ // stdio MCP server 入口,供 stdio-only agent runtime(Claude Desktop 等)
5
+ // 作为 MCP transport 直接 fork:
6
+ // "command": "npx", "args": ["-y", "@skillfm/local", "skillfm-mcp-stdio"]
7
+ // 或 npx 直装后链接:
8
+ // "command": "skillfm-mcp-stdio"
9
+ //
10
+ // 等价路径(HTTP sidecar 模式)是 `skillfm-local start`,用户主入口统一在
11
+ // @skillfm/local 这一个 npm 包内,按 agent runtime 选 bin 即可。
12
+ import { main } from './server.js';
13
+ main().catch((err) => {
14
+ // stderr 不影响 stdio JSON-RPC 信道
15
+ process.stderr.write(`[skillfm-mcp-stdio] fatal: ${err?.message || err}\n`);
16
+ process.exit(1);
17
+ });
18
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../../src/mcp-stdio/bin.ts"],"names":[],"mappings":";AACA,yCAAyC;AACzC,EAAE;AACF,mEAAmE;AACnE,4BAA4B;AAC5B,4EAA4E;AAC5E,eAAe;AACf,mCAAmC;AACnC,EAAE;AACF,wDAAwD;AACxD,sDAAsD;AAEtD,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,+BAA+B;IAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,8BAA8B,GAAG,EAAE,OAAO,IAAI,GAAG,IAAI,CACtD,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare const config: {
2
+ apiBaseUrl: string;
3
+ brainKey: string;
4
+ token: string;
5
+ };
6
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/mcp-stdio/config.ts"],"names":[],"mappings":"AAyBA,eAAO,MAAM,MAAM;;;;CAOlB,CAAC"}
@@ -0,0 +1,26 @@
1
+ // 配置管理 — 优先级:
2
+ // 1. 环境变量 (SKILLFM_BRAIN_KEY / SKILLFM_TOKEN / SKILLFM_API_URL)
3
+ // 2. ~/.skillfm/config.json (skillfm install 写入)
4
+ import { readFileSync, existsSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import { homedir } from 'node:os';
7
+ function loadFileConfig() {
8
+ try {
9
+ const path = join(homedir(), '.skillfm', 'config.json');
10
+ if (!existsSync(path))
11
+ return {};
12
+ return JSON.parse(readFileSync(path, 'utf-8'));
13
+ }
14
+ catch {
15
+ return {};
16
+ }
17
+ }
18
+ const file = loadFileConfig();
19
+ export const config = {
20
+ apiBaseUrl: process.env.SKILLFM_API_URL ||
21
+ file.apiBaseUrl ||
22
+ 'https://api.skillfm.ai/api/v1',
23
+ brainKey: process.env.SKILLFM_BRAIN_KEY || file.agentToken || '',
24
+ token: process.env.SKILLFM_TOKEN || '',
25
+ };
26
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/mcp-stdio/config.ts"],"names":[],"mappings":"AAAA,cAAc;AACd,kEAAkE;AAClE,mDAAmD;AAEnD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAOlC,SAAS,cAAc;IACrB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;QACxD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAe,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;AAE9B,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,UAAU,EACR,OAAO,CAAC,GAAG,CAAC,eAAe;QAC3B,IAAI,CAAC,UAAU;QACf,+BAA+B;IACjC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,UAAU,IAAI,EAAE;IAChE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE;CACvC,CAAC"}
@@ -0,0 +1,53 @@
1
+ interface LocalizedText {
2
+ zh?: string;
3
+ en?: string;
4
+ }
5
+ interface DialogueFlow {
6
+ flow_id: string;
7
+ current_turn: number;
8
+ total_turns: number;
9
+ this_turn: {
10
+ semantic_goal: string;
11
+ talk_points: string[];
12
+ end_with: string;
13
+ };
14
+ next_turn_hints: {
15
+ if_user_engages: string;
16
+ if_user_hesitates: string;
17
+ if_user_declines: string;
18
+ };
19
+ gentle_exit: {
20
+ semantic_goal: string;
21
+ talk_points: string[];
22
+ };
23
+ success_criterion: string;
24
+ }
25
+ interface EnhancedError {
26
+ code: string;
27
+ severity: string;
28
+ user_message?: LocalizedText;
29
+ agent_directive?: LocalizedText;
30
+ dialogue_flow?: DialogueFlow | null;
31
+ }
32
+ interface Envelope {
33
+ ok: boolean;
34
+ error?: EnhancedError | null;
35
+ dialogue_flow?: DialogueFlow | null;
36
+ conversation_hints?: Array<{
37
+ text?: LocalizedText;
38
+ }>;
39
+ }
40
+ /**
41
+ * 把一份 dialogue_flow 渲染成对 Agent 友好的 markdown 块。
42
+ *
43
+ * 目标读者: Agent (Claude Code / Cursor 等), 不是终端用户。
44
+ * 因此用 [Guide / 引导剧本] 开头, 让 Agent 知道这是元信息而非用户话语。
45
+ */
46
+ export declare function renderDialogueFlow(flow: DialogueFlow): string;
47
+ /**
48
+ * 渲染整个 envelope 的"软错误 + dialogue_flow"区段。
49
+ * 用在 MCP 工具的 catch 路径里。
50
+ */
51
+ export declare function renderEnvelopeError(envelope: Envelope | null): string | null;
52
+ export {};
53
+ //# sourceMappingURL=render-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-flow.d.ts","sourceRoot":"","sources":["../../src/mcp-stdio/render-flow.ts"],"names":[],"mappings":"AAOA,UAAU,aAAa;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,UAAU,YAAY;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE;QACT,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,eAAe,EAAE;QACf,eAAe,EAAE,MAAM,CAAC;QACxB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,WAAW,EAAE;QACX,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC;IACF,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,eAAe,CAAC,EAAE,aAAa,CAAC;IAChC,aAAa,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CACrC;AAED,UAAU,QAAQ;IAChB,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAC7B,aAAa,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,kBAAkB,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC;CACtD;AAMD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAuB7D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAsB5E"}
@@ -0,0 +1,70 @@
1
+ // Render envelope.dialogue_flow + EnhancedError 成 Agent 可读文本
2
+ //
3
+ // PRD Book 3 §3.2: MCP 工具不翻译业务话术, 只把服务端 envelope 完整呈现给 Agent。
4
+ // Agent 自己根据 dialogue_flow.this_turn / next_turn_hints / gentle_exit 决定怎么说。
5
+ //
6
+ // 核心约束: 不在 MCP 这层注入额外文案, 只做"忠实搬运 + 结构化展示"。
7
+ function pickText(loc) {
8
+ return loc?.zh || loc?.en || '';
9
+ }
10
+ /**
11
+ * 把一份 dialogue_flow 渲染成对 Agent 友好的 markdown 块。
12
+ *
13
+ * 目标读者: Agent (Claude Code / Cursor 等), 不是终端用户。
14
+ * 因此用 [Guide / 引导剧本] 开头, 让 Agent 知道这是元信息而非用户话语。
15
+ */
16
+ export function renderDialogueFlow(flow) {
17
+ const lines = [];
18
+ lines.push(`[引导剧本 · ${flow.flow_id} · turn ${flow.current_turn}/${flow.total_turns}]`);
19
+ lines.push('');
20
+ lines.push(`本轮目标: ${flow.this_turn.semantic_goal}`);
21
+ if (flow.this_turn.talk_points?.length) {
22
+ lines.push('可以用的话题点:');
23
+ for (const p of flow.this_turn.talk_points)
24
+ lines.push(` - ${p}`);
25
+ }
26
+ lines.push(`本轮收尾方式: ${flow.this_turn.end_with}`);
27
+ lines.push('');
28
+ lines.push('下一轮根据 ta 的反应分支:');
29
+ lines.push(` · ta 接茬: ${flow.next_turn_hints.if_user_engages}`);
30
+ lines.push(` · ta 犹豫: ${flow.next_turn_hints.if_user_hesitates}`);
31
+ lines.push(` · ta 拒绝: ${flow.next_turn_hints.if_user_declines}`);
32
+ lines.push('');
33
+ lines.push(`温柔退路 (gentle_exit): ${flow.gentle_exit.semantic_goal}`);
34
+ if (flow.gentle_exit.talk_points?.length) {
35
+ for (const p of flow.gentle_exit.talk_points)
36
+ lines.push(` - ${p}`);
37
+ }
38
+ lines.push('');
39
+ lines.push(`本轮成功判定: ${flow.success_criterion}`);
40
+ return lines.join('\n');
41
+ }
42
+ /**
43
+ * 渲染整个 envelope 的"软错误 + dialogue_flow"区段。
44
+ * 用在 MCP 工具的 catch 路径里。
45
+ */
46
+ export function renderEnvelopeError(envelope) {
47
+ if (!envelope || !envelope.error)
48
+ return null;
49
+ const err = envelope.error;
50
+ const blocks = [];
51
+ const userMsg = pickText(err.user_message);
52
+ if (userMsg)
53
+ blocks.push(userMsg);
54
+ const directive = pickText(err.agent_directive);
55
+ if (directive)
56
+ blocks.push(`[agent 心法] ${directive}`);
57
+ const flow = envelope.dialogue_flow || err.dialogue_flow;
58
+ if (flow)
59
+ blocks.push(renderDialogueFlow(flow));
60
+ if (envelope.conversation_hints?.length) {
61
+ blocks.push('—');
62
+ for (const h of envelope.conversation_hints) {
63
+ const t = pickText(h.text);
64
+ if (t)
65
+ blocks.push(`[hint] ${t}`);
66
+ }
67
+ }
68
+ return blocks.join('\n\n');
69
+ }
70
+ //# sourceMappingURL=render-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-flow.js","sourceRoot":"","sources":["../../src/mcp-stdio/render-flow.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,EAAE;AACF,8DAA8D;AAC9D,4EAA4E;AAC5E,EAAE;AACF,2CAA2C;AA2C3C,SAAS,QAAQ,CAAC,GAAmB;IACnC,OAAO,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC;AAClC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAkB;IACnD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,OAAO,WAAW,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;IACvF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,CAAC,CAAC;IACpD,IAAI,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,WAAW;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC9B,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,eAAe,CAAC,eAAe,EAAE,CAAC,CAAC;IACjE,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,eAAe,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACnE,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,eAAe,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAClE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,uBAAuB,IAAI,CAAC,WAAW,CAAC,aAAa,EAAE,CAAC,CAAC;IACpE,IAAI,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;QACzC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,WAAW;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAChD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAyB;IAC3D,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAC9C,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC;IAC3B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC3C,IAAI,OAAO;QAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAElC,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAChD,IAAI,SAAS;QAAE,MAAM,CAAC,IAAI,CAAC,cAAc,SAAS,EAAE,CAAC,CAAC;IAEtD,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,IAAI,GAAG,CAAC,aAAa,CAAC;IACzD,IAAI,IAAI;QAAE,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;IAEhD,IAAI,QAAQ,CAAC,kBAAkB,EAAE,MAAM,EAAE,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjB,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,kBAAkB,EAAE,CAAC;YAC5C,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC3B,IAAI,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,30 @@
1
+ export interface RequestContext {
2
+ apiBaseUrl: string;
3
+ brainKey: string;
4
+ jwtToken: string;
5
+ /** 同一个 brainKey 下的 conversation-state 读写器(HTTP 模式下从 store 借用,stdio 下可以是全局单例) */
6
+ convState: ConvStateHandle;
7
+ }
8
+ export interface ConvStateHandle {
9
+ get(): string | null;
10
+ set(v: string | null): void;
11
+ getFeedback(): string | null;
12
+ consumeFeedback(): string | null;
13
+ pushFeedback(v: unknown): void;
14
+ }
15
+ export declare class ConvStateStore {
16
+ private map;
17
+ getHandle(brainKey: string): ConvStateHandle;
18
+ private getEntry;
19
+ private touch;
20
+ private evictIfNeeded;
21
+ /** 测试辅助 */
22
+ _clear(): void;
23
+ _size(): number;
24
+ }
25
+ /**
26
+ * 全局单例 store — stdio 模式/tests 也会用到,HTTP 模式的 app 实例也直接用它。
27
+ * 不同 brainKey 天然隔离。
28
+ */
29
+ export declare const globalConvStore: ConvStateStore;
30
+ //# sourceMappingURL=request-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/mcp-stdio/request-context.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,SAAS,EAAE,eAAe,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,IAAI,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAC5B,WAAW,IAAI,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,IAAI,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAChC;AASD,qBAAa,cAAc;IACzB,OAAO,CAAC,GAAG,CAAgC;IAE3C,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe;IAwB5C,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,KAAK;IAMb,OAAO,CAAC,aAAa;IAWrB,WAAW;IACX,MAAM,IAAI,IAAI;IAId,KAAK,IAAI,MAAM;CAGhB;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,gBAAuB,CAAC"}
@@ -0,0 +1,78 @@
1
+ // RequestContext — 每请求独立的客户端上下文,HTTP Streamable 模式下使用。
2
+ //
3
+ // 为什么需要这个?
4
+ // stdio 模式是单租户进程,全局 config + 全局 conv-state 没问题。
5
+ // HTTP 模式是多租户(多个 agent 带各自的 Bearer 打到同一个 process),
6
+ // 必须按 brainKey 隔离出站状态,避免 conv-state bleed across agents。
7
+ //
8
+ // 设计:
9
+ // - RequestContext 不可变 (per-request frozen)
10
+ // - ConvStateStore 是 brainKey → {state, feedback} 的 Map
11
+ // - LRU-ish cap(512 条)防止内存无界增长;真出问题再换 Redis
12
+ //
13
+ // BYOK 红线 (Principle 24): 这里永不读/存任何 LLM key, 只存平台自己的 brainKey + JWT。
14
+ const MAX_ENTRIES = 512;
15
+ export class ConvStateStore {
16
+ map = new Map();
17
+ getHandle(brainKey) {
18
+ const store = this;
19
+ return {
20
+ get: () => store.getEntry(brainKey).state,
21
+ set: (v) => {
22
+ const e = store.getEntry(brainKey);
23
+ e.state = v;
24
+ store.touch(brainKey, e);
25
+ },
26
+ getFeedback: () => store.getEntry(brainKey).feedback,
27
+ consumeFeedback: () => {
28
+ const e = store.getEntry(brainKey);
29
+ const f = e.feedback;
30
+ e.feedback = null;
31
+ return f;
32
+ },
33
+ pushFeedback: (v) => {
34
+ const e = store.getEntry(brainKey);
35
+ e.feedback = v === null ? null : JSON.stringify(v);
36
+ store.touch(brainKey, e);
37
+ },
38
+ };
39
+ }
40
+ getEntry(brainKey) {
41
+ let e = this.map.get(brainKey);
42
+ if (!e) {
43
+ e = { state: null, feedback: null };
44
+ this.map.set(brainKey, e);
45
+ this.evictIfNeeded();
46
+ }
47
+ return e;
48
+ }
49
+ touch(brainKey, e) {
50
+ // LRU: delete + re-set 让它移到 Map 末尾
51
+ this.map.delete(brainKey);
52
+ this.map.set(brainKey, e);
53
+ }
54
+ evictIfNeeded() {
55
+ while (this.map.size > MAX_ENTRIES) {
56
+ const oldest = this.map.keys().next().value;
57
+ if (oldest !== undefined) {
58
+ this.map.delete(oldest);
59
+ }
60
+ else {
61
+ break;
62
+ }
63
+ }
64
+ }
65
+ /** 测试辅助 */
66
+ _clear() {
67
+ this.map.clear();
68
+ }
69
+ _size() {
70
+ return this.map.size;
71
+ }
72
+ }
73
+ /**
74
+ * 全局单例 store — stdio 模式/tests 也会用到,HTTP 模式的 app 实例也直接用它。
75
+ * 不同 brainKey 天然隔离。
76
+ */
77
+ export const globalConvStore = new ConvStateStore();
78
+ //# sourceMappingURL=request-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-context.js","sourceRoot":"","sources":["../../src/mcp-stdio/request-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { type RequestContext } from './request-context.js';
4
+ type ToolContextProvider = (extra: {
5
+ authInfo?: {
6
+ token?: string;
7
+ };
8
+ } | undefined) => RequestContext;
9
+ /**
10
+ * 构造一个注册了所有工具的 McpServer。
11
+ * 工具回调在运行时调用 getCtx(extra) 拿到 per-request RequestContext。
12
+ */
13
+ declare function buildMcpServer(getCtx: ToolContextProvider): McpServer;
14
+ export { buildMcpServer };
15
+ export { globalConvStore } from './request-context.js';
16
+ export type { RequestContext } from './request-context.js';
17
+ export type { ToolContextProvider };
18
+ export declare function main(): Promise<void>;
19
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp-stdio/server.ts"],"names":[],"mappings":";AAoBA,OAAO,EAAE,SAAS,EAAoB,MAAM,yCAAyC,CAAC;AAOtF,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAQ5E,KAAK,mBAAmB,GAAG,CAAC,KAAK,EAAE;IAAE,QAAQ,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,SAAS,KAAK,cAAc,CAAC;AAEpG;;;GAGG;AACH,iBAAS,cAAc,CAAC,MAAM,EAAE,mBAAmB,GAAG,SAAS,CAqkB9D;AA4BD,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAU1C"}