linkshell-cli 0.10.92 → 0.10.94
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 +4 -4
- package/dist/cli/src/commands/desktop.d.ts +3 -0
- package/dist/cli/src/commands/desktop.js +2 -46
- package/dist/cli/src/commands/desktop.js.map +1 -1
- package/dist/cli/src/commands/host.d.ts +2 -2
- package/dist/cli/src/commands/host.js +3 -3
- package/dist/cli/src/commands/host.js.map +1 -1
- package/dist/cli/src/commands/login.js +1 -1
- package/dist/cli/src/commands/login.js.map +1 -1
- package/dist/cli/src/commands/setup.js +1 -1
- package/dist/cli/src/commands/setup.js.map +1 -1
- package/dist/cli/src/commands/sign-in.d.ts +2 -0
- package/dist/cli/src/commands/sign-in.js +4 -6
- package/dist/cli/src/commands/sign-in.js.map +1 -1
- package/dist/cli/src/pro-membership.d.ts +8 -8
- package/dist/cli/src/pro-membership.js +21 -27
- package/dist/cli/src/pro-membership.js.map +1 -1
- package/dist/cli/tsconfig.tsbuildinfo +1 -1
- package/dist/client-core/src/pro-membership.d.ts +37 -9
- package/dist/client-core/src/pro-membership.js +98 -70
- package/dist/client-core/src/pro-membership.js.map +1 -1
- package/dist/gateway/src/relay.js +17 -17
- package/dist/gateway/src/relay.js.map +1 -1
- package/dist/gateway/src/subscription.d.ts +4 -1
- package/dist/gateway/src/subscription.js +25 -13
- package/dist/gateway/src/subscription.js.map +1 -1
- package/dist/host/src/children.d.ts +43 -0
- package/dist/host/src/children.js +213 -0
- package/dist/host/src/children.js.map +1 -0
- package/dist/host/src/drivers/acp/antigravity.js +1 -1
- package/dist/host/src/drivers/acp/antigravity.js.map +1 -1
- package/dist/host/src/drivers/acp/client-services.js +2 -0
- package/dist/host/src/drivers/acp/client-services.js.map +1 -1
- package/dist/host/src/drivers/acp/connection.js +8 -2
- package/dist/host/src/drivers/acp/connection.js.map +1 -1
- package/dist/host/src/drivers/acp/driver.d.ts +1 -5
- package/dist/host/src/drivers/acp/driver.js +11 -16
- package/dist/host/src/drivers/acp/driver.js.map +1 -1
- package/dist/host/src/drivers/acp/mcp.js +2 -0
- package/dist/host/src/drivers/acp/mcp.js.map +1 -1
- package/dist/host/src/drivers/claude/activity.js +13 -0
- package/dist/host/src/drivers/claude/activity.js.map +1 -1
- package/dist/host/src/drivers/claude/driver.d.ts +15 -0
- package/dist/host/src/drivers/claude/driver.js +44 -2
- package/dist/host/src/drivers/claude/driver.js.map +1 -1
- package/dist/host/src/drivers/claude/mcp-apps.d.ts +70 -0
- package/dist/host/src/drivers/claude/mcp-apps.js +399 -0
- package/dist/host/src/drivers/claude/mcp-apps.js.map +1 -0
- package/dist/host/src/drivers/claude/task-process.d.ts +23 -0
- package/dist/host/src/drivers/claude/task-process.js +121 -0
- package/dist/host/src/drivers/claude/task-process.js.map +1 -0
- package/dist/host/src/drivers/claude/tasks.d.ts +8 -1
- package/dist/host/src/drivers/claude/tasks.js +34 -6
- package/dist/host/src/drivers/claude/tasks.js.map +1 -1
- package/dist/host/src/drivers/codex/driver.d.ts +21 -0
- package/dist/host/src/drivers/codex/driver.js +99 -12
- package/dist/host/src/drivers/codex/driver.js.map +1 -1
- package/dist/host/src/drivers/queue.d.ts +4 -0
- package/dist/host/src/drivers/queue.js +9 -0
- package/dist/host/src/drivers/queue.js.map +1 -0
- package/dist/host/src/drivers/types.d.ts +1 -1
- package/dist/host/src/fs.js +8 -8
- package/dist/host/src/fs.js.map +1 -1
- package/dist/host/src/gateway.js +3 -3
- package/dist/host/src/gateway.js.map +1 -1
- package/dist/host/src/host.js +6 -0
- package/dist/host/src/host.js.map +1 -1
- package/dist/host/src/hub.js +28 -14
- package/dist/host/src/hub.js.map +1 -1
- package/dist/host/src/ports.js +3 -3
- package/dist/host/src/ports.js.map +1 -1
- package/dist/host/src/process.d.ts +8 -2
- package/dist/host/src/process.js +37 -2
- package/dist/host/src/process.js.map +1 -1
- package/dist/host/src/rpc/mcp.d.ts +1 -1
- package/dist/host/src/rpc/mcp.js +2 -2
- package/dist/host/src/rpc/mcp.js.map +1 -1
- package/dist/host/src/rpc/server.js +5 -5
- package/dist/host/src/rpc/server.js.map +1 -1
- package/dist/host/src/screen-viewer.js +7 -7
- package/dist/host/src/screen.js +3 -3
- package/dist/host/src/screen.js.map +1 -1
- package/dist/host/src/slim.js +16 -4
- package/dist/host/src/slim.js.map +1 -1
- package/dist/host/src/store.js +8 -4
- package/dist/host/src/store.js.map +1 -1
- package/dist/host/src/terminals.js +15 -8
- package/dist/host/src/terminals.js.map +1 -1
- package/dist/wire/src/model.d.ts +4 -0
- package/dist/wire/src/model.js +4 -0
- package/dist/wire/src/model.js.map +1 -1
- package/dist/wire/src/peer.js +4 -4
- package/dist/wire/src/peer.js.map +1 -1
- package/dist/wire/src/relay-client.js +9 -9
- package/dist/wire/src/relay-client.js.map +1 -1
- package/dist/wire/src/relay.js +2 -2
- package/dist/wire/src/relay.js.map +1 -1
- package/dist/wire/src/rpc-mcp.js +5 -4
- package/dist/wire/src/rpc-mcp.js.map +1 -1
- package/dist/wire/src/rpc.d.ts +5 -0
- package/dist/wire/src/updates.js +1 -1
- package/package.json +4 -4
- package/src/commands/desktop.ts +2 -40
- package/src/commands/host.ts +3 -3
- package/src/commands/login.ts +1 -1
- package/src/commands/setup.ts +1 -1
- package/src/commands/sign-in.ts +6 -6
- package/src/pro-membership.ts +23 -23
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import { type McpApp, type MethodParams, type MethodResult, type ToolDetail } from "@linkshell/wire";
|
|
3
|
+
import type { McpAppsSupport } from "../types.js";
|
|
4
|
+
export type ClaudeMcpServerConfig = {
|
|
5
|
+
type: "stdio";
|
|
6
|
+
command: string;
|
|
7
|
+
args: string[];
|
|
8
|
+
env: Record<string, string>;
|
|
9
|
+
} | {
|
|
10
|
+
type: "http" | "sse";
|
|
11
|
+
url: string;
|
|
12
|
+
headers: Record<string, string>;
|
|
13
|
+
};
|
|
14
|
+
/** Claude Code's name for a server in its tool names (`mcp__<name>__<tool>`). */
|
|
15
|
+
export declare const normalizedServerName: (name: string) => string;
|
|
16
|
+
/** Where Claude Code keeps its global state: beside the config directory's parent, unless CLAUDE_CONFIG_DIR moved both. */
|
|
17
|
+
export declare function claudeStatePath(configDir: string, env: NodeJS.ProcessEnv): string;
|
|
18
|
+
/**
|
|
19
|
+
* The MCP servers a Claude session in `cwd` has, by name, as Claude Code
|
|
20
|
+
* resolves them: the user's servers, then the project's own entry in
|
|
21
|
+
* `.claude.json`, then the project's `.mcp.json` where the user approved it.
|
|
22
|
+
*/
|
|
23
|
+
export declare function claudeMcpServers(statePath: string, configDir: string, cwd: string | undefined, env: NodeJS.ProcessEnv): Map<string, ClaudeMcpServerConfig>;
|
|
24
|
+
export interface ClaudeMcpAppsOptions {
|
|
25
|
+
env(): NodeJS.ProcessEnv;
|
|
26
|
+
configDir: string;
|
|
27
|
+
/** The session's working directory, where the host knows it. */
|
|
28
|
+
cwdOf(nativeId: string): string | undefined;
|
|
29
|
+
/** A call's page, found after its card went out. */
|
|
30
|
+
lateDetail(nativeId: string, toolCallId: string, detail: Extract<ToolDetail, {
|
|
31
|
+
type: "mcp";
|
|
32
|
+
}>, parentToolCallId?: string): void;
|
|
33
|
+
log(message: string): void;
|
|
34
|
+
/** For tests: a client already connected. */
|
|
35
|
+
connect?(config: ClaudeMcpServerConfig): Promise<Client>;
|
|
36
|
+
listWaitMs?: number;
|
|
37
|
+
}
|
|
38
|
+
export declare class ClaudeMcpApps implements McpAppsSupport {
|
|
39
|
+
private readonly options;
|
|
40
|
+
private readonly connections;
|
|
41
|
+
private sweep?;
|
|
42
|
+
private readonly looking;
|
|
43
|
+
constructor(options: ClaudeMcpAppsOptions);
|
|
44
|
+
apps(nativeId: string): Promise<McpApp[]>;
|
|
45
|
+
resource(nativeId: string, request: Omit<MethodParams<"mcp.resource">, "sessionId">): Promise<MethodResult<"mcp.resource">>;
|
|
46
|
+
call(nativeId: string, request: Omit<MethodParams<"mcp.call">, "sessionId">): Promise<MethodResult<"mcp.call">>;
|
|
47
|
+
/**
|
|
48
|
+
* The page of a call's tool, where it is known now. An unknown tool of a
|
|
49
|
+
* configured server has the server's tools read, and a page found then
|
|
50
|
+
* reaches the call's card late (`lateDetail`).
|
|
51
|
+
*/
|
|
52
|
+
page(nativeId: string, toolCallId: string, detail: Extract<ToolDetail, {
|
|
53
|
+
type: "mcp";
|
|
54
|
+
}>, parentToolCallId?: string): Extract<ToolDetail, {
|
|
55
|
+
type: "mcp";
|
|
56
|
+
}>["app"];
|
|
57
|
+
stop(): Promise<void>;
|
|
58
|
+
/** The servers configured for the session, one connection per distinct configuration. */
|
|
59
|
+
private configured;
|
|
60
|
+
private server;
|
|
61
|
+
/** The server a tool call names, by Claude Code's normalized form of the name. */
|
|
62
|
+
private byToolName;
|
|
63
|
+
private tools;
|
|
64
|
+
private client;
|
|
65
|
+
private open;
|
|
66
|
+
/** A request to the server, its failures said in the user's words; a dropped connection is opened again once. */
|
|
67
|
+
private attempt;
|
|
68
|
+
private startSweep;
|
|
69
|
+
private close;
|
|
70
|
+
}
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
5
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
6
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
7
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
8
|
+
import { RpcError } from "@linkshell/wire";
|
|
9
|
+
import { trackPid } from "../../children.js";
|
|
10
|
+
import { descriptorPage, isPageUri } from "../codex/mapper.js";
|
|
11
|
+
const str = (value) => (typeof value === "string" ? value : undefined);
|
|
12
|
+
const obj = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
13
|
+
const arr = (value) => (Array.isArray(value) ? value : []);
|
|
14
|
+
const MAX_RESOURCE = 4 * 1024 * 1024;
|
|
15
|
+
/** How old a server's tool list may be before it's read again. */
|
|
16
|
+
const TOOLS_MAX_AGE_MS = 30_000;
|
|
17
|
+
/** A server that couldn't be reached isn't tried again for this long. */
|
|
18
|
+
const FAILED_RETRY_MS = 60_000;
|
|
19
|
+
const CONNECT_TIMEOUT_MS = 15_000;
|
|
20
|
+
const CALL_TIMEOUT_MS = 60_000;
|
|
21
|
+
/** How long the menu waits for servers: a slow one (an `npx` download) joins the list the next time it's opened. */
|
|
22
|
+
const LIST_WAIT_MS = 4000;
|
|
23
|
+
/** A connection no page has used for this long is closed. */
|
|
24
|
+
const IDLE_MS = 10 * 60_000;
|
|
25
|
+
/** Claude Code's name for a server in its tool names (`mcp__<name>__<tool>`). */
|
|
26
|
+
export const normalizedServerName = (name) => name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
27
|
+
/** `${VAR}` and `${VAR:-default}`, as Claude Code expands them in its MCP configuration. */
|
|
28
|
+
function expand(value, env) {
|
|
29
|
+
return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g, (_, name, fallback) => env[name] ?? fallback ?? "");
|
|
30
|
+
}
|
|
31
|
+
function strings(value, env) {
|
|
32
|
+
const out = {};
|
|
33
|
+
for (const [key, entry] of Object.entries(obj(value) ?? {}))
|
|
34
|
+
if (typeof entry === "string")
|
|
35
|
+
out[key] = expand(entry, env);
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
function parseServer(raw, env) {
|
|
39
|
+
const entry = obj(raw);
|
|
40
|
+
if (!entry)
|
|
41
|
+
return undefined;
|
|
42
|
+
const type = str(entry.type) ?? (entry.url ? "http" : "stdio");
|
|
43
|
+
if (type === "stdio") {
|
|
44
|
+
const command = str(entry.command);
|
|
45
|
+
if (!command)
|
|
46
|
+
return undefined;
|
|
47
|
+
return { type, command: expand(command, env), args: arr(entry.args).flatMap((arg) => (typeof arg === "string" ? [expand(arg, env)] : [])), env: strings(entry.env, env) };
|
|
48
|
+
}
|
|
49
|
+
if (type === "http" || type === "sse") {
|
|
50
|
+
const url = str(entry.url);
|
|
51
|
+
return url ? { type, url: expand(url, env), headers: strings(entry.headers, env) } : undefined;
|
|
52
|
+
}
|
|
53
|
+
// (`sdk` and `ws` servers live inside another process: nothing to connect to.)
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
function readJson(path) {
|
|
57
|
+
try {
|
|
58
|
+
return existsSync(path) ? obj(JSON.parse(readFileSync(path, "utf8"))) : undefined;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Where Claude Code keeps its global state: beside the config directory's parent, unless CLAUDE_CONFIG_DIR moved both. */
|
|
65
|
+
export function claudeStatePath(configDir, env) {
|
|
66
|
+
return env.CLAUDE_CONFIG_DIR ? join(configDir, ".claude.json") : join(dirname(configDir) || homedir(), ".claude.json");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The MCP servers a Claude session in `cwd` has, by name, as Claude Code
|
|
70
|
+
* resolves them: the user's servers, then the project's own entry in
|
|
71
|
+
* `.claude.json`, then the project's `.mcp.json` where the user approved it.
|
|
72
|
+
*/
|
|
73
|
+
export function claudeMcpServers(statePath, configDir, cwd, env) {
|
|
74
|
+
const servers = new Map();
|
|
75
|
+
const state = readJson(statePath);
|
|
76
|
+
const project = cwd ? obj(obj(state?.projects)?.[cwd]) : undefined;
|
|
77
|
+
const add = (map, allowed = () => true) => {
|
|
78
|
+
for (const [name, raw] of Object.entries(obj(map) ?? {})) {
|
|
79
|
+
const parsed = parseServer(raw, env);
|
|
80
|
+
if (parsed && allowed(name))
|
|
81
|
+
servers.set(name, parsed);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
add(state?.mcpServers);
|
|
85
|
+
if (cwd) {
|
|
86
|
+
const settings = { ...readJson(join(configDir, "settings.json")), ...readJson(join(cwd, ".claude", "settings.json")), ...readJson(join(cwd, ".claude", "settings.local.json")) };
|
|
87
|
+
const enabled = new Set([...arr(project?.enabledMcpjsonServers), ...arr(settings.enabledMcpjsonServers)].flatMap((name) => (typeof name === "string" ? [name] : [])));
|
|
88
|
+
const disabled = new Set([...arr(project?.disabledMcpjsonServers), ...arr(settings.disabledMcpjsonServers)].flatMap((name) => (typeof name === "string" ? [name] : [])));
|
|
89
|
+
const all = project?.enableAllProjectMcpServers === true || settings.enableAllProjectMcpServers === true;
|
|
90
|
+
// Project-scoped servers ask before they run: only the ones the user said yes to.
|
|
91
|
+
add(obj(readJson(join(cwd, ".mcp.json"))?.mcpServers), (name) => !disabled.has(name) && (all || enabled.has(name)));
|
|
92
|
+
add(project?.mcpServers);
|
|
93
|
+
}
|
|
94
|
+
for (const name of arr(project?.disabledMcpServers))
|
|
95
|
+
if (typeof name === "string")
|
|
96
|
+
servers.delete(name);
|
|
97
|
+
return servers;
|
|
98
|
+
}
|
|
99
|
+
export class ClaudeMcpApps {
|
|
100
|
+
options;
|
|
101
|
+
connections = new Map();
|
|
102
|
+
sweep;
|
|
103
|
+
looking = new Set();
|
|
104
|
+
constructor(options) {
|
|
105
|
+
this.options = options;
|
|
106
|
+
}
|
|
107
|
+
async apps(nativeId) {
|
|
108
|
+
const connections = this.configured(nativeId);
|
|
109
|
+
const apps = [];
|
|
110
|
+
let timer;
|
|
111
|
+
const deadline = new Promise((resolve) => (timer = setTimeout(resolve, this.options.listWaitMs ?? LIST_WAIT_MS, undefined)));
|
|
112
|
+
await Promise.all(connections.map(async (connection) => {
|
|
113
|
+
const tools = await Promise.race([this.tools(connection).catch(() => undefined), deadline]);
|
|
114
|
+
for (const tool of tools?.values() ?? []) {
|
|
115
|
+
const meta = tool._meta;
|
|
116
|
+
const page = descriptorPage(meta);
|
|
117
|
+
if (!page)
|
|
118
|
+
continue;
|
|
119
|
+
const visibility = arr(obj(meta?.ui)?.visibility).filter((entry) => typeof entry === "string");
|
|
120
|
+
// A page only the server's own pages open isn't one to start from the menu.
|
|
121
|
+
if (visibility.length && !visibility.includes("model"))
|
|
122
|
+
continue;
|
|
123
|
+
const entrypoints = arr(obj(meta?.["openai/ui"])?.entrypoints).flatMap((entry) => str(obj(entry)?.type) ?? []);
|
|
124
|
+
// Codex's rule where the tool states one; otherwise a page that needs nothing to start.
|
|
125
|
+
if (entrypoints.length ? !entrypoints.includes("thread") : !tool.bare)
|
|
126
|
+
continue;
|
|
127
|
+
apps.push({
|
|
128
|
+
server: connection.name,
|
|
129
|
+
tool: tool.name,
|
|
130
|
+
title: tool.title ?? connection.name,
|
|
131
|
+
...(tool.description ? { description: tool.description } : {}),
|
|
132
|
+
resourceUri: page.resourceUri,
|
|
133
|
+
entrypoints: entrypoints.length ? entrypoints : ["thread"],
|
|
134
|
+
...(visibility.length ? { visibility } : {}),
|
|
135
|
+
...(page.displayMode ? { displayMode: page.displayMode } : {}),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}));
|
|
139
|
+
clearTimeout(timer);
|
|
140
|
+
return apps.sort((a, b) => a.title.localeCompare(b.title));
|
|
141
|
+
}
|
|
142
|
+
async resource(nativeId, request) {
|
|
143
|
+
if (!isPageUri(request.uri))
|
|
144
|
+
throw RpcError.app("invalid_params", "只能读取插件页面(ui:// 开头的地址)");
|
|
145
|
+
const connection = this.server(nativeId, request.server);
|
|
146
|
+
const client = await this.client(connection);
|
|
147
|
+
const result = await this.attempt(connection, "读取插件页面", () => client.readResource({ uri: request.uri }, { timeout: CALL_TIMEOUT_MS }));
|
|
148
|
+
const first = obj(arr(result.contents)[0]);
|
|
149
|
+
if (!first)
|
|
150
|
+
throw RpcError.app("not_found", "插件没有给出这个页面");
|
|
151
|
+
const text = str(first.text);
|
|
152
|
+
const blob = str(first.blob);
|
|
153
|
+
const size = (text?.length ?? 0) + (blob?.length ?? 0);
|
|
154
|
+
if (size > MAX_RESOURCE)
|
|
155
|
+
throw RpcError.app("too_large", `插件页面有 ${(size / 1024 / 1024).toFixed(1)} MB,超过了 ${MAX_RESOURCE / 1024 / 1024} MB 的上限`);
|
|
156
|
+
return {
|
|
157
|
+
...(str(first.mimeType) ? { mimeType: str(first.mimeType) } : {}),
|
|
158
|
+
...(text !== undefined ? { text } : {}),
|
|
159
|
+
...(blob !== undefined ? { blob } : {}),
|
|
160
|
+
...(first._meta !== undefined ? { meta: first._meta } : {}),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
async call(nativeId, request) {
|
|
164
|
+
const connection = this.server(nativeId, request.server);
|
|
165
|
+
let tools = await this.tools(connection);
|
|
166
|
+
let tool = tools.get(request.tool);
|
|
167
|
+
if (!tool) {
|
|
168
|
+
// Added since the list was read.
|
|
169
|
+
connection.toolsAt = undefined;
|
|
170
|
+
tools = await this.tools(connection);
|
|
171
|
+
tool = tools.get(request.tool);
|
|
172
|
+
}
|
|
173
|
+
if (!tool)
|
|
174
|
+
throw RpcError.app("not_found", `MCP 服务 ${request.server} 没有工具 ${request.tool}`);
|
|
175
|
+
// Least privilege, as for Codex: only a server that has pages, and only tools its pages may call.
|
|
176
|
+
if (![...tools.values()].some((entry) => descriptorPage(entry._meta)))
|
|
177
|
+
throw RpcError.app("forbidden", `MCP 服务 ${request.server} 没有插件页面,不能从页面调用它的工具`);
|
|
178
|
+
const meta = tool._meta;
|
|
179
|
+
if (meta?.["openai/fileParams"] !== undefined)
|
|
180
|
+
throw RpcError.app("forbidden", "这个工具要读写电脑上的文件,插件页面不能直接调用");
|
|
181
|
+
const visibility = obj(meta?.ui)?.visibility;
|
|
182
|
+
if (Array.isArray(visibility) && !visibility.includes("app"))
|
|
183
|
+
throw RpcError.app("forbidden", "这个工具只给 Claude 自己调用,插件页面不能调用");
|
|
184
|
+
const client = await this.client(connection);
|
|
185
|
+
const result = await this.attempt(connection, "调用插件工具", () => client.callTool({ name: tool.name, arguments: request.arguments ?? {} }, undefined, { timeout: CALL_TIMEOUT_MS }));
|
|
186
|
+
return {
|
|
187
|
+
content: arr(result.content),
|
|
188
|
+
...(result.structuredContent != null ? { structuredContent: result.structuredContent } : {}),
|
|
189
|
+
...(typeof result.isError === "boolean" ? { isError: result.isError } : {}),
|
|
190
|
+
...(result._meta != null ? { meta: result._meta } : {}),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* The page of a call's tool, where it is known now. An unknown tool of a
|
|
195
|
+
* configured server has the server's tools read, and a page found then
|
|
196
|
+
* reaches the call's card late (`lateDetail`).
|
|
197
|
+
*/
|
|
198
|
+
page(nativeId, toolCallId, detail, parentToolCallId) {
|
|
199
|
+
if (detail.app)
|
|
200
|
+
return detail.app;
|
|
201
|
+
const connection = this.byToolName(nativeId, detail.server);
|
|
202
|
+
if (!connection)
|
|
203
|
+
return undefined;
|
|
204
|
+
const known = connection.tools?.get(detail.tool);
|
|
205
|
+
if (known || (connection.tools && connection.toolsAt && Date.now() - connection.toolsAt < TOOLS_MAX_AGE_MS))
|
|
206
|
+
return known && descriptorPage(known._meta);
|
|
207
|
+
if (connection.failedAt && Date.now() - connection.failedAt < FAILED_RETRY_MS)
|
|
208
|
+
return undefined;
|
|
209
|
+
const key = `${nativeId}\n${toolCallId}`;
|
|
210
|
+
if (this.looking.has(key))
|
|
211
|
+
return undefined;
|
|
212
|
+
this.looking.add(key);
|
|
213
|
+
void this.tools(connection)
|
|
214
|
+
.then((tools) => {
|
|
215
|
+
const tool = tools.get(detail.tool);
|
|
216
|
+
const app = tool && descriptorPage(tool._meta);
|
|
217
|
+
if (app)
|
|
218
|
+
this.options.lateDetail(nativeId, toolCallId, { ...detail, app }, parentToolCallId);
|
|
219
|
+
})
|
|
220
|
+
.catch(() => { })
|
|
221
|
+
.finally(() => this.looking.delete(key));
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
async stop() {
|
|
225
|
+
clearInterval(this.sweep);
|
|
226
|
+
this.sweep = undefined;
|
|
227
|
+
const open = [...this.connections.values()];
|
|
228
|
+
this.connections.clear();
|
|
229
|
+
await Promise.all(open.map((connection) => this.close(connection)));
|
|
230
|
+
}
|
|
231
|
+
/** The servers configured for the session, one connection per distinct configuration. */
|
|
232
|
+
configured(nativeId) {
|
|
233
|
+
const env = this.options.env();
|
|
234
|
+
const servers = claudeMcpServers(claudeStatePath(this.options.configDir, env), this.options.configDir, this.options.cwdOf(nativeId), env);
|
|
235
|
+
return [...servers].map(([name, config]) => {
|
|
236
|
+
const key = `${name}\n${JSON.stringify(config)}`;
|
|
237
|
+
let connection = this.connections.get(key);
|
|
238
|
+
if (!connection) {
|
|
239
|
+
connection = { key, name, config, usedAt: Date.now() };
|
|
240
|
+
this.connections.set(key, connection);
|
|
241
|
+
}
|
|
242
|
+
return connection;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
server(nativeId, name) {
|
|
246
|
+
// (A call's card names the server as Claude's tool names do.)
|
|
247
|
+
const connection = this.byToolName(nativeId, name);
|
|
248
|
+
if (!connection)
|
|
249
|
+
throw RpcError.app("not_found", `这个会话的 Claude 没有配置 MCP 服务 ${name}`);
|
|
250
|
+
return connection;
|
|
251
|
+
}
|
|
252
|
+
/** The server a tool call names, by Claude Code's normalized form of the name. */
|
|
253
|
+
byToolName(nativeId, server) {
|
|
254
|
+
const connections = this.configured(nativeId);
|
|
255
|
+
return connections.find((entry) => entry.name === server) ?? connections.find((entry) => normalizedServerName(entry.name) === server);
|
|
256
|
+
}
|
|
257
|
+
async tools(connection) {
|
|
258
|
+
if (connection.tools && connection.toolsAt && Date.now() - connection.toolsAt < TOOLS_MAX_AGE_MS)
|
|
259
|
+
return connection.tools;
|
|
260
|
+
if (connection.failedAt && Date.now() - connection.failedAt < FAILED_RETRY_MS)
|
|
261
|
+
throw RpcError.app("agent_error", connection.failure ?? `连不上 MCP 服务 ${connection.name}`);
|
|
262
|
+
connection.listing ??= (async () => {
|
|
263
|
+
const client = await this.client(connection);
|
|
264
|
+
const tools = new Map();
|
|
265
|
+
let cursor;
|
|
266
|
+
for (let page = 0; page < 20; page++) {
|
|
267
|
+
const result = await this.attempt(connection, "列出 MCP 工具", () => client.listTools(cursor ? { cursor } : {}, { timeout: CONNECT_TIMEOUT_MS }));
|
|
268
|
+
for (const raw of result.tools) {
|
|
269
|
+
const required = arr(obj(raw.inputSchema)?.required);
|
|
270
|
+
tools.set(raw.name, {
|
|
271
|
+
name: raw.name,
|
|
272
|
+
...(raw.title ? { title: raw.title } : {}),
|
|
273
|
+
...(raw.description ? { description: raw.description } : {}),
|
|
274
|
+
...(obj(raw._meta) ? { _meta: obj(raw._meta) } : {}),
|
|
275
|
+
bare: required.length === 0,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
cursor = result.nextCursor;
|
|
279
|
+
if (!cursor)
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
connection.tools = tools;
|
|
283
|
+
connection.toolsAt = Date.now();
|
|
284
|
+
return tools;
|
|
285
|
+
})().finally(() => {
|
|
286
|
+
connection.listing = undefined;
|
|
287
|
+
});
|
|
288
|
+
return connection.listing;
|
|
289
|
+
}
|
|
290
|
+
client(connection) {
|
|
291
|
+
connection.usedAt = Date.now();
|
|
292
|
+
this.startSweep();
|
|
293
|
+
if (connection.client)
|
|
294
|
+
return connection.client;
|
|
295
|
+
if (connection.failedAt && Date.now() - connection.failedAt < FAILED_RETRY_MS) {
|
|
296
|
+
return Promise.reject(RpcError.app("agent_error", connection.failure ?? `连不上 MCP 服务 ${connection.name}`));
|
|
297
|
+
}
|
|
298
|
+
const opening = this.open(connection).catch((error) => {
|
|
299
|
+
if (connection.client === opening)
|
|
300
|
+
connection.client = undefined;
|
|
301
|
+
connection.failedAt = Date.now();
|
|
302
|
+
connection.failure = `连不上 MCP 服务 ${connection.name}:${describe(error)}`;
|
|
303
|
+
this.options.log(`[claude] MCP ${connection.name}: ${describe(error)}`);
|
|
304
|
+
throw RpcError.app("agent_error", connection.failure);
|
|
305
|
+
});
|
|
306
|
+
connection.client = opening;
|
|
307
|
+
return opening;
|
|
308
|
+
}
|
|
309
|
+
async open(connection) {
|
|
310
|
+
if (this.options.connect)
|
|
311
|
+
return this.options.connect(connection.config);
|
|
312
|
+
const { config } = connection;
|
|
313
|
+
const client = new Client({ name: "linkshell", version: "1" },
|
|
314
|
+
// Tells servers that render pages that this client shows them.
|
|
315
|
+
{ capabilities: { extensions: { "io.modelcontextprotocol/ui": { mimeTypes: ["text/html;profile=mcp-app"] } } } });
|
|
316
|
+
const env = this.options.env();
|
|
317
|
+
const transport = config.type === "stdio"
|
|
318
|
+
? new StdioClientTransport({ command: config.command, args: config.args, env: { ...definedOnly(env), ...config.env }, stderr: "ignore" })
|
|
319
|
+
: config.type === "http"
|
|
320
|
+
? new StreamableHTTPClientTransport(new URL(config.url), { requestInit: { headers: config.headers } })
|
|
321
|
+
: new SSEClientTransport(new URL(config.url), { requestInit: { headers: config.headers } });
|
|
322
|
+
connection.transport = transport;
|
|
323
|
+
client.onclose = () => {
|
|
324
|
+
connection.untrack?.();
|
|
325
|
+
connection.untrack = undefined;
|
|
326
|
+
// The next use opens it again.
|
|
327
|
+
if (connection.client)
|
|
328
|
+
connection.client = undefined;
|
|
329
|
+
connection.toolsAt = undefined;
|
|
330
|
+
};
|
|
331
|
+
let timer;
|
|
332
|
+
try {
|
|
333
|
+
await Promise.race([
|
|
334
|
+
client.connect(transport),
|
|
335
|
+
new Promise((_, reject) => (timer = setTimeout(() => reject(new Error("连接超时")), CONNECT_TIMEOUT_MS))),
|
|
336
|
+
]);
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
await client.close().catch(() => { });
|
|
340
|
+
throw error;
|
|
341
|
+
}
|
|
342
|
+
finally {
|
|
343
|
+
clearTimeout(timer);
|
|
344
|
+
}
|
|
345
|
+
if (transport instanceof StdioClientTransport && transport.pid) {
|
|
346
|
+
// A crashed host leaves no MCP servers behind (children.ts).
|
|
347
|
+
connection.untrack = trackPid(transport.pid, "MCP server", config.type === "stdio" ? config.command : "", config.type === "stdio" ? config.args : [], false);
|
|
348
|
+
}
|
|
349
|
+
connection.failedAt = undefined;
|
|
350
|
+
connection.failure = undefined;
|
|
351
|
+
return client;
|
|
352
|
+
}
|
|
353
|
+
/** A request to the server, its failures said in the user's words; a dropped connection is opened again once. */
|
|
354
|
+
async attempt(connection, what, run) {
|
|
355
|
+
try {
|
|
356
|
+
return await run();
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
if (error instanceof RpcError)
|
|
360
|
+
throw error;
|
|
361
|
+
throw RpcError.app("agent_error", `MCP 服务 ${connection.name} 没能${what}:${describe(error)}`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
startSweep() {
|
|
365
|
+
if (this.sweep)
|
|
366
|
+
return;
|
|
367
|
+
this.sweep = setInterval(() => {
|
|
368
|
+
for (const connection of this.connections.values()) {
|
|
369
|
+
if (connection.client && Date.now() - connection.usedAt > IDLE_MS)
|
|
370
|
+
void this.close(connection);
|
|
371
|
+
}
|
|
372
|
+
}, 60_000);
|
|
373
|
+
this.sweep.unref?.();
|
|
374
|
+
}
|
|
375
|
+
async close(connection) {
|
|
376
|
+
connection.client = undefined;
|
|
377
|
+
connection.toolsAt = undefined;
|
|
378
|
+
const transport = connection.transport;
|
|
379
|
+
connection.transport = undefined;
|
|
380
|
+
// (Closing the transport ends a connect still waiting on it, too.)
|
|
381
|
+
await transport?.close().catch(() => { });
|
|
382
|
+
connection.untrack?.();
|
|
383
|
+
connection.untrack = undefined;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
function definedOnly(env) {
|
|
387
|
+
const out = {};
|
|
388
|
+
for (const [key, value] of Object.entries(env))
|
|
389
|
+
if (typeof value === "string")
|
|
390
|
+
out[key] = value;
|
|
391
|
+
return out;
|
|
392
|
+
}
|
|
393
|
+
function describe(error) {
|
|
394
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
395
|
+
if (/\b401\b|unauthori[sz]ed|invalid_token/i.test(message))
|
|
396
|
+
return "需要登录授权,LinkShell 不能代为登录这个服务";
|
|
397
|
+
return message.slice(0, 200);
|
|
398
|
+
}
|
|
399
|
+
//# sourceMappingURL=mcp-apps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-apps.js","sourceRoot":"","sources":["../../../../../../host/src/drivers/claude/mcp-apps.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** A process with a file open for writing on its stdout. */
|
|
2
|
+
export interface WriterProcess {
|
|
3
|
+
pid: number;
|
|
4
|
+
pgid: number;
|
|
5
|
+
ppid: number;
|
|
6
|
+
}
|
|
7
|
+
/** Where a background task's process can be found: macOS (lsof) and Linux (/proc). */
|
|
8
|
+
export declare const canFindTaskProcesses: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* The processes whose stdout is `path` (resolved), opened for writing. Claude
|
|
11
|
+
* runs a background shell with its stdout and stderr on the task's output
|
|
12
|
+
* file, so this finds that shell and whatever it started.
|
|
13
|
+
*/
|
|
14
|
+
export declare function stdoutWriters(path: string): Promise<WriterProcess[]>;
|
|
15
|
+
/**
|
|
16
|
+
* Ends the process group of the shell writing `path`: the group's leader must
|
|
17
|
+
* write the file on its stdout and be a child of one of `parents` (the Claude
|
|
18
|
+
* processes that have the session), so nothing else is ever signalled.
|
|
19
|
+
* Resolves whether there was such a group.
|
|
20
|
+
*/
|
|
21
|
+
export declare function stopTaskGroup(path: string, parents: Set<number>, options?: {
|
|
22
|
+
graceMs?: number;
|
|
23
|
+
}): Promise<boolean>;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { readFileSync, readdirSync, readlinkSync } from "node:fs";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
const execFileAsync = promisify(execFile);
|
|
5
|
+
/** Where a background task's process can be found: macOS (lsof) and Linux (/proc). */
|
|
6
|
+
export const canFindTaskProcesses = process.platform === "darwin" || process.platform === "linux";
|
|
7
|
+
/**
|
|
8
|
+
* The processes whose stdout is `path` (resolved), opened for writing. Claude
|
|
9
|
+
* runs a background shell with its stdout and stderr on the task's output
|
|
10
|
+
* file, so this finds that shell and whatever it started.
|
|
11
|
+
*/
|
|
12
|
+
export async function stdoutWriters(path) {
|
|
13
|
+
if (process.platform === "linux")
|
|
14
|
+
return linuxWriters(path);
|
|
15
|
+
if (process.platform !== "darwin")
|
|
16
|
+
return [];
|
|
17
|
+
let stdout;
|
|
18
|
+
try {
|
|
19
|
+
({ stdout } = await execFileAsync("lsof", ["-a", "-d", "1", "-F", "pgfa", "--", path], { timeout: 5000 }));
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
// lsof exits 1 when nothing has the file open.
|
|
23
|
+
stdout = String(error.stdout ?? "");
|
|
24
|
+
}
|
|
25
|
+
const found = [];
|
|
26
|
+
let pid = 0, pgid = 0, fd = "";
|
|
27
|
+
for (const line of stdout.split("\n")) {
|
|
28
|
+
const value = line.slice(1);
|
|
29
|
+
if (line[0] === "p") {
|
|
30
|
+
pid = Number(value);
|
|
31
|
+
pgid = 0;
|
|
32
|
+
fd = "";
|
|
33
|
+
}
|
|
34
|
+
else if (line[0] === "g")
|
|
35
|
+
pgid = Number(value);
|
|
36
|
+
else if (line[0] === "f")
|
|
37
|
+
fd = value;
|
|
38
|
+
else if (line[0] === "a" && fd === "1" && (value === "w" || value === "u") && pid > 0 && pgid > 0)
|
|
39
|
+
found.push({ pid, pgid });
|
|
40
|
+
}
|
|
41
|
+
const writers = [];
|
|
42
|
+
for (const entry of found) {
|
|
43
|
+
try {
|
|
44
|
+
const { stdout: ps } = await execFileAsync("ps", ["-o", "ppid=", "-p", String(entry.pid)], { timeout: 5000 });
|
|
45
|
+
const ppid = Number(ps.trim());
|
|
46
|
+
if (ppid > 0)
|
|
47
|
+
writers.push({ ...entry, ppid });
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Gone since lsof looked.
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return writers;
|
|
54
|
+
}
|
|
55
|
+
function linuxWriters(path) {
|
|
56
|
+
const writers = [];
|
|
57
|
+
let pids;
|
|
58
|
+
try {
|
|
59
|
+
pids = readdirSync("/proc").filter((name) => /^\d+$/.test(name));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
for (const pid of pids) {
|
|
65
|
+
try {
|
|
66
|
+
if (readlinkSync(`/proc/${pid}/fd/1`) !== path)
|
|
67
|
+
continue;
|
|
68
|
+
const flags = /flags:\s*(\d+)/.exec(readFileSync(`/proc/${pid}/fdinfo/1`, "utf8"))?.[1];
|
|
69
|
+
// O_WRONLY or O_RDWR.
|
|
70
|
+
if (!flags || (parseInt(flags, 8) & 3) === 0)
|
|
71
|
+
continue;
|
|
72
|
+
const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
73
|
+
const [, ppid, pgid] = stat.slice(stat.lastIndexOf(")") + 2).split(" ");
|
|
74
|
+
writers.push({ pid: Number(pid), ppid: Number(ppid), pgid: Number(pgid) });
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Not ours to read, or gone.
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return writers;
|
|
81
|
+
}
|
|
82
|
+
function groupAlive(pgid) {
|
|
83
|
+
try {
|
|
84
|
+
process.kill(-pgid, 0);
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
return error.code === "EPERM";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Ends the process group of the shell writing `path`: the group's leader must
|
|
93
|
+
* write the file on its stdout and be a child of one of `parents` (the Claude
|
|
94
|
+
* processes that have the session), so nothing else is ever signalled.
|
|
95
|
+
* Resolves whether there was such a group.
|
|
96
|
+
*/
|
|
97
|
+
export async function stopTaskGroup(path, parents, options = {}) {
|
|
98
|
+
const writers = await stdoutWriters(path);
|
|
99
|
+
const leaders = writers.filter((writer) => writer.pid === writer.pgid && writer.pgid > 1 && writer.pid !== process.pid && parents.has(writer.ppid));
|
|
100
|
+
if (leaders.length === 0)
|
|
101
|
+
return false;
|
|
102
|
+
for (const leader of leaders) {
|
|
103
|
+
try {
|
|
104
|
+
process.kill(-leader.pgid, "SIGTERM");
|
|
105
|
+
}
|
|
106
|
+
catch { /* already gone */ }
|
|
107
|
+
}
|
|
108
|
+
const deadline = Date.now() + (options.graceMs ?? 3000);
|
|
109
|
+
while (leaders.some((leader) => groupAlive(leader.pgid)) && Date.now() < deadline)
|
|
110
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
111
|
+
for (const leader of leaders) {
|
|
112
|
+
if (!groupAlive(leader.pgid))
|
|
113
|
+
continue;
|
|
114
|
+
try {
|
|
115
|
+
process.kill(-leader.pgid, "SIGKILL");
|
|
116
|
+
}
|
|
117
|
+
catch { /* already gone */ }
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=task-process.js.map
|