@kirosnn/mosaic 0.73.0 → 0.75.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 +1 -1
- package/package.json +2 -2
- package/src/agent/prompts/systemPrompt.ts +1 -1
- package/src/agent/prompts/toolsPrompt.ts +75 -5
- package/src/agent/tools/explore.ts +4 -4
- package/src/agent/tools/exploreExecutor.ts +56 -4
- package/src/components/Main.tsx +1480 -1459
- package/src/components/main/ChatPage.tsx +858 -858
- package/src/index.tsx +32 -5
- package/src/mcp/approvalPolicy.ts +155 -147
- package/src/mcp/cli/doctor.ts +0 -3
- package/src/mcp/config.ts +234 -223
- package/src/mcp/processManager.ts +303 -298
- package/src/mcp/servers/navigation/browser.ts +151 -0
- package/src/mcp/servers/navigation/index.ts +23 -0
- package/src/mcp/servers/navigation/tools.ts +263 -0
- package/src/mcp/servers/navigation/types.ts +17 -0
- package/src/mcp/servers/navigation/utils.ts +20 -0
- package/src/mcp/toolCatalog.ts +181 -168
- package/src/mcp/types.ts +115 -94
- package/src/utils/history.ts +82 -82
- package/src/utils/markdown.tsx +60 -26
- package/src/utils/toolFormatting.ts +55 -6
- package/src/web/components/MessageItem.tsx +44 -13
- package/src/mcp/servers/navigation.ts +0 -854
package/src/mcp/toolCatalog.ts
CHANGED
|
@@ -1,169 +1,182 @@
|
|
|
1
|
-
import { tool, type CoreTool } from 'ai';
|
|
2
|
-
import type { McpToolInfo, McpServerConfig } from './types';
|
|
3
|
-
import { parseSafeId } from './types';
|
|
4
|
-
import { McpProcessManager } from './processManager';
|
|
5
|
-
import { McpApprovalPolicy } from './approvalPolicy';
|
|
6
|
-
import { jsonSchemaObjectToZodObject } from './schemaConverter';
|
|
7
|
-
|
|
8
|
-
function matchGlobList(name: string, patterns: string[]): boolean {
|
|
9
|
-
for (const pattern of patterns) {
|
|
10
|
-
if (pattern === '*') return true;
|
|
11
|
-
if (pattern === name) return true;
|
|
12
|
-
|
|
13
|
-
const regex = new RegExp(
|
|
14
|
-
'^' + pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*').replace(/\?/g, '.') + '$'
|
|
15
|
-
);
|
|
16
|
-
if (regex.test(name)) return true;
|
|
17
|
-
}
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export class McpToolCatalog {
|
|
22
|
-
private processManager: McpProcessManager;
|
|
23
|
-
private approvalPolicy: McpApprovalPolicy;
|
|
24
|
-
private configs: McpServerConfig[];
|
|
25
|
-
private exposedTools = new Map<string, CoreTool>();
|
|
26
|
-
private toolInfoMap = new Map<string, McpToolInfo>();
|
|
27
|
-
private safeToCanonical = new Map<string, string>();
|
|
28
|
-
private canonicalToSafe = new Map<string, string>();
|
|
29
|
-
|
|
30
|
-
constructor(processManager: McpProcessManager, approvalPolicy: McpApprovalPolicy, configs: McpServerConfig[]) {
|
|
31
|
-
this.processManager = processManager;
|
|
32
|
-
this.approvalPolicy = approvalPolicy;
|
|
33
|
-
this.configs = configs;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
refreshTools(serverId?: string): void {
|
|
37
|
-
if (serverId) {
|
|
38
|
-
this.refreshServerTools(serverId);
|
|
39
|
-
} else {
|
|
40
|
-
for (const config of this.configs) {
|
|
41
|
-
if (config.enabled) {
|
|
42
|
-
this.refreshServerTools(config.id);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
private refreshServerTools(serverId: string): void {
|
|
49
|
-
for (const [safeId, info] of this.toolInfoMap) {
|
|
50
|
-
if (info.serverId === serverId) {
|
|
51
|
-
this.exposedTools.delete(safeId);
|
|
52
|
-
this.toolInfoMap.delete(safeId);
|
|
53
|
-
this.safeToCanonical.delete(safeId);
|
|
54
|
-
this.canonicalToSafe.delete(info.canonicalId);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const config = this.configs.find(c => c.id === serverId);
|
|
59
|
-
if (!config || !config.enabled) return;
|
|
60
|
-
|
|
61
|
-
const rawTools = this.processManager.listTools(serverId);
|
|
62
|
-
|
|
63
|
-
for (const toolInfo of rawTools) {
|
|
64
|
-
if (config.tools.deny && config.tools.deny.length > 0) {
|
|
65
|
-
if (matchGlobList(toolInfo.name, config.tools.deny)) continue;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (config.tools.allow && config.tools.allow.length > 0) {
|
|
69
|
-
if (!matchGlobList(toolInfo.name, config.tools.allow)) continue;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const coreTool = this.convertToCoreToolDef(toolInfo, config);
|
|
73
|
-
this.exposedTools.set(toolInfo.safeId, coreTool);
|
|
74
|
-
this.toolInfoMap.set(toolInfo.safeId, toolInfo);
|
|
75
|
-
this.safeToCanonical.set(toolInfo.safeId, toolInfo.canonicalId);
|
|
76
|
-
this.canonicalToSafe.set(toolInfo.canonicalId, toolInfo.safeId);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
private convertToCoreToolDef(toolInfo: McpToolInfo, config: McpServerConfig): CoreTool {
|
|
81
|
-
const schema = toolInfo.inputSchema || { type: 'object', properties: {} };
|
|
82
|
-
const zodParams = jsonSchemaObjectToZodObject(schema as Record<string, unknown>);
|
|
83
|
-
|
|
84
|
-
const pm = this.processManager;
|
|
85
|
-
const ap = this.approvalPolicy;
|
|
86
|
-
const serverConfig = config;
|
|
87
|
-
|
|
88
|
-
return tool({
|
|
89
|
-
description: toolInfo.description || `MCP tool: ${toolInfo.name} (${config.name})`,
|
|
90
|
-
parameters: zodParams,
|
|
91
|
-
execute: async (args: Record<string, unknown>) => {
|
|
92
|
-
try {
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
1
|
+
import { tool, type CoreTool } from 'ai';
|
|
2
|
+
import type { McpToolInfo, McpServerConfig } from './types';
|
|
3
|
+
import { parseSafeId, isNativeMcpServer } from './types';
|
|
4
|
+
import { McpProcessManager } from './processManager';
|
|
5
|
+
import { McpApprovalPolicy } from './approvalPolicy';
|
|
6
|
+
import { jsonSchemaObjectToZodObject } from './schemaConverter';
|
|
7
|
+
|
|
8
|
+
function matchGlobList(name: string, patterns: string[]): boolean {
|
|
9
|
+
for (const pattern of patterns) {
|
|
10
|
+
if (pattern === '*') return true;
|
|
11
|
+
if (pattern === name) return true;
|
|
12
|
+
|
|
13
|
+
const regex = new RegExp(
|
|
14
|
+
'^' + pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*').replace(/\?/g, '.') + '$'
|
|
15
|
+
);
|
|
16
|
+
if (regex.test(name)) return true;
|
|
17
|
+
}
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class McpToolCatalog {
|
|
22
|
+
private processManager: McpProcessManager;
|
|
23
|
+
private approvalPolicy: McpApprovalPolicy;
|
|
24
|
+
private configs: McpServerConfig[];
|
|
25
|
+
private exposedTools = new Map<string, CoreTool>();
|
|
26
|
+
private toolInfoMap = new Map<string, McpToolInfo>();
|
|
27
|
+
private safeToCanonical = new Map<string, string>();
|
|
28
|
+
private canonicalToSafe = new Map<string, string>();
|
|
29
|
+
|
|
30
|
+
constructor(processManager: McpProcessManager, approvalPolicy: McpApprovalPolicy, configs: McpServerConfig[]) {
|
|
31
|
+
this.processManager = processManager;
|
|
32
|
+
this.approvalPolicy = approvalPolicy;
|
|
33
|
+
this.configs = configs;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
refreshTools(serverId?: string): void {
|
|
37
|
+
if (serverId) {
|
|
38
|
+
this.refreshServerTools(serverId);
|
|
39
|
+
} else {
|
|
40
|
+
for (const config of this.configs) {
|
|
41
|
+
if (config.enabled) {
|
|
42
|
+
this.refreshServerTools(config.id);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private refreshServerTools(serverId: string): void {
|
|
49
|
+
for (const [safeId, info] of this.toolInfoMap) {
|
|
50
|
+
if (info.serverId === serverId) {
|
|
51
|
+
this.exposedTools.delete(safeId);
|
|
52
|
+
this.toolInfoMap.delete(safeId);
|
|
53
|
+
this.safeToCanonical.delete(safeId);
|
|
54
|
+
this.canonicalToSafe.delete(info.canonicalId);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const config = this.configs.find(c => c.id === serverId);
|
|
59
|
+
if (!config || !config.enabled) return;
|
|
60
|
+
|
|
61
|
+
const rawTools = this.processManager.listTools(serverId);
|
|
62
|
+
|
|
63
|
+
for (const toolInfo of rawTools) {
|
|
64
|
+
if (config.tools.deny && config.tools.deny.length > 0) {
|
|
65
|
+
if (matchGlobList(toolInfo.name, config.tools.deny)) continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (config.tools.allow && config.tools.allow.length > 0) {
|
|
69
|
+
if (!matchGlobList(toolInfo.name, config.tools.allow)) continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const coreTool = this.convertToCoreToolDef(toolInfo, config);
|
|
73
|
+
this.exposedTools.set(toolInfo.safeId, coreTool);
|
|
74
|
+
this.toolInfoMap.set(toolInfo.safeId, toolInfo);
|
|
75
|
+
this.safeToCanonical.set(toolInfo.safeId, toolInfo.canonicalId);
|
|
76
|
+
this.canonicalToSafe.set(toolInfo.canonicalId, toolInfo.safeId);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private convertToCoreToolDef(toolInfo: McpToolInfo, config: McpServerConfig): CoreTool {
|
|
81
|
+
const schema = toolInfo.inputSchema || { type: 'object', properties: {} };
|
|
82
|
+
const zodParams = jsonSchemaObjectToZodObject(schema as Record<string, unknown>);
|
|
83
|
+
|
|
84
|
+
const pm = this.processManager;
|
|
85
|
+
const ap = this.approvalPolicy;
|
|
86
|
+
const serverConfig = config;
|
|
87
|
+
|
|
88
|
+
return tool({
|
|
89
|
+
description: toolInfo.description || `MCP tool: ${toolInfo.name} (${config.name})`,
|
|
90
|
+
parameters: zodParams,
|
|
91
|
+
execute: async (args: Record<string, unknown>) => {
|
|
92
|
+
try {
|
|
93
|
+
const effectiveApproval = serverConfig.toolApproval?.[toolInfo.name] ?? serverConfig.approval;
|
|
94
|
+
const approvalResult = await ap.requestMcpApproval({
|
|
95
|
+
serverId: serverConfig.id,
|
|
96
|
+
serverName: serverConfig.name,
|
|
97
|
+
toolName: toolInfo.name,
|
|
98
|
+
canonicalId: toolInfo.canonicalId,
|
|
99
|
+
args,
|
|
100
|
+
approvalMode: effectiveApproval,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (!approvalResult.approved) {
|
|
104
|
+
if (approvalResult.customResponse) {
|
|
105
|
+
return {
|
|
106
|
+
error: `OPERATION REJECTED BY USER with custom instructions: "${approvalResult.customResponse}"`,
|
|
107
|
+
userMessage: 'Operation cancelled by user',
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
error: `OPERATION REJECTED BY USER: calling MCP tool ${toolInfo.canonicalId}`,
|
|
112
|
+
userMessage: 'Operation cancelled by user',
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const result = await pm.callTool(serverConfig.id, toolInfo.name, args);
|
|
117
|
+
|
|
118
|
+
if (result.isError) {
|
|
119
|
+
return { error: result.content };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return result.content;
|
|
123
|
+
} catch (error) {
|
|
124
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
125
|
+
return { error: `MCP tool call failed: ${message}` };
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getExposedTools(): Record<string, CoreTool> {
|
|
132
|
+
const result: Record<string, CoreTool> = {};
|
|
133
|
+
for (const [safeId, coreTool] of this.exposedTools) {
|
|
134
|
+
result[safeId] = coreTool;
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
getMcpToolInfos(): McpToolInfo[] {
|
|
140
|
+
return Array.from(this.toolInfoMap.values());
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
getToolInfo(safeId: string): McpToolInfo | null {
|
|
144
|
+
return this.toolInfoMap.get(safeId) || null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
getSafeIdFromCanonical(canonicalId: string): string | null {
|
|
148
|
+
return this.canonicalToSafe.get(canonicalId) || null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
getCanonicalFromSafeId(safeId: string): string | null {
|
|
152
|
+
return this.safeToCanonical.get(safeId) || null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
isMcpTool(toolName: string): boolean {
|
|
156
|
+
return toolName.startsWith('mcp__');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
parseMcpToolName(safeId: string): { serverId: string; toolName: string } | null {
|
|
160
|
+
return parseSafeId(safeId);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
isNative(safeId: string): boolean {
|
|
164
|
+
const parsed = parseSafeId(safeId);
|
|
165
|
+
if (!parsed) return false;
|
|
166
|
+
const config = this.configs.find(c => c.id === parsed.serverId);
|
|
167
|
+
if (config?.native) return true;
|
|
168
|
+
return isNativeMcpServer(parsed.serverId);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
getServerConfig(serverId: string): McpServerConfig | null {
|
|
172
|
+
return this.configs.find(c => c.id === serverId) || null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
updateConfigs(configs: McpServerConfig[]): void {
|
|
176
|
+
this.configs = configs;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
static mergeTools(internal: Record<string, CoreTool>, mcp: Record<string, CoreTool>): Record<string, CoreTool> {
|
|
180
|
+
return { ...internal, ...mcp };
|
|
181
|
+
}
|
|
169
182
|
}
|
package/src/mcp/types.ts
CHANGED
|
@@ -1,95 +1,116 @@
|
|
|
1
|
-
export interface McpTransportConfig {
|
|
2
|
-
type: 'stdio';
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export interface McpServerConfig {
|
|
6
|
-
id: string;
|
|
7
|
-
name: string;
|
|
8
|
-
enabled: boolean;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
export type
|
|
38
|
-
|
|
39
|
-
export
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
export type
|
|
60
|
-
|
|
61
|
-
export
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
1
|
+
export interface McpTransportConfig {
|
|
2
|
+
type: 'stdio';
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface McpServerConfig {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
native?: boolean;
|
|
10
|
+
transport: McpTransportConfig;
|
|
11
|
+
command: string;
|
|
12
|
+
args: string[];
|
|
13
|
+
cwd?: string;
|
|
14
|
+
env?: Record<string, string>;
|
|
15
|
+
autostart: 'startup' | 'on-demand' | 'never';
|
|
16
|
+
timeouts: {
|
|
17
|
+
initialize: number;
|
|
18
|
+
call: number;
|
|
19
|
+
};
|
|
20
|
+
limits: {
|
|
21
|
+
maxCallsPerMinute: number;
|
|
22
|
+
maxPayloadBytes: number;
|
|
23
|
+
};
|
|
24
|
+
logs: {
|
|
25
|
+
persist: boolean;
|
|
26
|
+
path?: string;
|
|
27
|
+
bufferSize: number;
|
|
28
|
+
};
|
|
29
|
+
tools: {
|
|
30
|
+
allow?: string[];
|
|
31
|
+
deny?: string[];
|
|
32
|
+
};
|
|
33
|
+
approval: McpApprovalMode;
|
|
34
|
+
toolApproval?: Record<string, McpApprovalMode>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type McpApprovalMode = 'always' | 'once-per-tool' | 'once-per-server' | 'never';
|
|
38
|
+
|
|
39
|
+
export type McpServerStatus = 'stopped' | 'starting' | 'running' | 'error';
|
|
40
|
+
|
|
41
|
+
export interface McpServerState {
|
|
42
|
+
status: McpServerStatus;
|
|
43
|
+
pid?: number;
|
|
44
|
+
initLatencyMs?: number;
|
|
45
|
+
toolCount: number;
|
|
46
|
+
lastError?: string;
|
|
47
|
+
lastCallAt?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface McpToolInfo {
|
|
51
|
+
serverId: string;
|
|
52
|
+
name: string;
|
|
53
|
+
description: string;
|
|
54
|
+
inputSchema: Record<string, unknown>;
|
|
55
|
+
canonicalId: string;
|
|
56
|
+
safeId: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type McpRiskHint = 'read' | 'write' | 'execute' | 'network' | 'unknown';
|
|
60
|
+
|
|
61
|
+
export type McpApprovalScope = 'toolArgs' | 'tool' | 'server';
|
|
62
|
+
|
|
63
|
+
export interface McpApprovalCacheEntry {
|
|
64
|
+
scope: McpApprovalScope;
|
|
65
|
+
key: string;
|
|
66
|
+
expiresAt: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface McpGlobalConfig {
|
|
70
|
+
servers: McpServerConfig[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function toCanonicalId(serverId: string, toolName: string): string {
|
|
74
|
+
return `mcp:${serverId}:${toolName}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function toSafeId(serverId: string, toolName: string): string {
|
|
78
|
+
return `mcp__${serverId}__${toolName}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function parseSafeId(safeId: string): { serverId: string; toolName: string } | null {
|
|
82
|
+
if (!safeId.startsWith('mcp__')) return null;
|
|
83
|
+
const parts = safeId.slice(5).split('__');
|
|
84
|
+
if (parts.length < 2) return null;
|
|
85
|
+
const toolName = parts.pop()!;
|
|
86
|
+
const serverId = parts.join('__');
|
|
87
|
+
return { serverId, toolName };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function parseCanonicalId(canonicalId: string): { serverId: string; toolName: string } | null {
|
|
91
|
+
if (!canonicalId.startsWith('mcp:')) return null;
|
|
92
|
+
const parts = canonicalId.slice(4).split(':');
|
|
93
|
+
if (parts.length < 2) return null;
|
|
94
|
+
const toolName = parts.pop()!;
|
|
95
|
+
const serverId = parts.join(':');
|
|
96
|
+
return { serverId, toolName };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const NATIVE_SERVER_IDS = new Set(['navigation']);
|
|
100
|
+
|
|
101
|
+
export function isNativeMcpServer(serverId: string): boolean {
|
|
102
|
+
return NATIVE_SERVER_IDS.has(serverId);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function isNativeMcpTool(safeId: string): boolean {
|
|
106
|
+
const parsed = parseSafeId(safeId);
|
|
107
|
+
if (!parsed) return false;
|
|
108
|
+
return NATIVE_SERVER_IDS.has(parsed.serverId);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function getNativeMcpToolName(safeId: string): string | null {
|
|
112
|
+
const parsed = parseSafeId(safeId);
|
|
113
|
+
if (!parsed) return null;
|
|
114
|
+
if (!NATIVE_SERVER_IDS.has(parsed.serverId)) return null;
|
|
115
|
+
return parsed.toolName;
|
|
95
116
|
}
|