@mcp-z/cli 1.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/LICENSE +21 -0
- package/README.md +166 -0
- package/bin/cli.js +5 -0
- package/dist/cjs/cli.d.cts +1 -0
- package/dist/cjs/cli.d.ts +1 -0
- package/dist/cjs/cli.js +576 -0
- package/dist/cjs/cli.js.map +1 -0
- package/dist/cjs/commands/call-tool.d.cts +26 -0
- package/dist/cjs/commands/call-tool.d.ts +26 -0
- package/dist/cjs/commands/call-tool.js +362 -0
- package/dist/cjs/commands/call-tool.js.map +1 -0
- package/dist/cjs/commands/get-prompt.d.cts +26 -0
- package/dist/cjs/commands/get-prompt.d.ts +26 -0
- package/dist/cjs/commands/get-prompt.js +335 -0
- package/dist/cjs/commands/get-prompt.js.map +1 -0
- package/dist/cjs/commands/inspect.d.cts +35 -0
- package/dist/cjs/commands/inspect.d.ts +35 -0
- package/dist/cjs/commands/inspect.js +896 -0
- package/dist/cjs/commands/inspect.js.map +1 -0
- package/dist/cjs/commands/manifest/env-prompting.d.cts +21 -0
- package/dist/cjs/commands/manifest/env-prompting.d.ts +21 -0
- package/dist/cjs/commands/manifest/env-prompting.js +657 -0
- package/dist/cjs/commands/manifest/env-prompting.js.map +1 -0
- package/dist/cjs/commands/manifest/generate.d.cts +124 -0
- package/dist/cjs/commands/manifest/generate.d.ts +124 -0
- package/dist/cjs/commands/manifest/generate.js +2541 -0
- package/dist/cjs/commands/manifest/generate.js.map +1 -0
- package/dist/cjs/commands/manifest/index.d.cts +2 -0
- package/dist/cjs/commands/manifest/index.d.ts +2 -0
- package/dist/cjs/commands/manifest/index.js +229 -0
- package/dist/cjs/commands/manifest/index.js.map +1 -0
- package/dist/cjs/commands/manifest/metadata-reader.d.cts +71 -0
- package/dist/cjs/commands/manifest/metadata-reader.d.ts +71 -0
- package/dist/cjs/commands/manifest/metadata-reader.js +441 -0
- package/dist/cjs/commands/manifest/metadata-reader.js.map +1 -0
- package/dist/cjs/commands/manifest/validate.d.cts +1 -0
- package/dist/cjs/commands/manifest/validate.d.ts +1 -0
- package/dist/cjs/commands/manifest/validate.js +525 -0
- package/dist/cjs/commands/manifest/validate.js.map +1 -0
- package/dist/cjs/commands/read-resource.d.cts +24 -0
- package/dist/cjs/commands/read-resource.d.ts +24 -0
- package/dist/cjs/commands/read-resource.js +311 -0
- package/dist/cjs/commands/read-resource.js.map +1 -0
- package/dist/cjs/commands/search.d.cts +31 -0
- package/dist/cjs/commands/search.d.ts +31 -0
- package/dist/cjs/commands/search.js +464 -0
- package/dist/cjs/commands/search.js.map +1 -0
- package/dist/cjs/commands/up.d.cts +49 -0
- package/dist/cjs/commands/up.d.ts +49 -0
- package/dist/cjs/commands/up.js +235 -0
- package/dist/cjs/commands/up.js.map +1 -0
- package/dist/cjs/index.d.cts +7 -0
- package/dist/cjs/index.d.ts +7 -0
- package/dist/cjs/index.js +85 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/lib/find-config.d.cts +14 -0
- package/dist/cjs/lib/find-config.d.ts +14 -0
- package/dist/cjs/lib/find-config.js +93 -0
- package/dist/cjs/lib/find-config.js.map +1 -0
- package/dist/cjs/lib/json-schema.d.cts +18 -0
- package/dist/cjs/lib/json-schema.d.ts +18 -0
- package/dist/cjs/lib/json-schema.js +306 -0
- package/dist/cjs/lib/json-schema.js.map +1 -0
- package/dist/cjs/lib/resolve-server-config.d.cts +50 -0
- package/dist/cjs/lib/resolve-server-config.d.ts +50 -0
- package/dist/cjs/lib/resolve-server-config.js +214 -0
- package/dist/cjs/lib/resolve-server-config.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/types.d.cts +21 -0
- package/dist/cjs/types.d.ts +21 -0
- package/dist/cjs/types.js +32 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/cli.d.ts +1 -0
- package/dist/esm/cli.js +129 -0
- package/dist/esm/cli.js.map +1 -0
- package/dist/esm/commands/call-tool.d.ts +26 -0
- package/dist/esm/commands/call-tool.js +151 -0
- package/dist/esm/commands/call-tool.js.map +1 -0
- package/dist/esm/commands/get-prompt.d.ts +26 -0
- package/dist/esm/commands/get-prompt.js +118 -0
- package/dist/esm/commands/get-prompt.js.map +1 -0
- package/dist/esm/commands/inspect.d.ts +35 -0
- package/dist/esm/commands/inspect.js +438 -0
- package/dist/esm/commands/inspect.js.map +1 -0
- package/dist/esm/commands/manifest/env-prompting.d.ts +21 -0
- package/dist/esm/commands/manifest/env-prompting.js +213 -0
- package/dist/esm/commands/manifest/env-prompting.js.map +1 -0
- package/dist/esm/commands/manifest/generate.d.ts +124 -0
- package/dist/esm/commands/manifest/generate.js +1087 -0
- package/dist/esm/commands/manifest/generate.js.map +1 -0
- package/dist/esm/commands/manifest/index.d.ts +2 -0
- package/dist/esm/commands/manifest/index.js +23 -0
- package/dist/esm/commands/manifest/index.js.map +1 -0
- package/dist/esm/commands/manifest/metadata-reader.d.ts +71 -0
- package/dist/esm/commands/manifest/metadata-reader.js +143 -0
- package/dist/esm/commands/manifest/metadata-reader.js.map +1 -0
- package/dist/esm/commands/manifest/validate.d.ts +1 -0
- package/dist/esm/commands/manifest/validate.js +167 -0
- package/dist/esm/commands/manifest/validate.js.map +1 -0
- package/dist/esm/commands/read-resource.d.ts +24 -0
- package/dist/esm/commands/read-resource.js +95 -0
- package/dist/esm/commands/read-resource.js.map +1 -0
- package/dist/esm/commands/search.d.ts +31 -0
- package/dist/esm/commands/search.js +145 -0
- package/dist/esm/commands/search.js.map +1 -0
- package/dist/esm/commands/up.d.ts +49 -0
- package/dist/esm/commands/up.js +74 -0
- package/dist/esm/commands/up.js.map +1 -0
- package/dist/esm/index.d.ts +7 -0
- package/dist/esm/index.js +11 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/lib/find-config.d.ts +14 -0
- package/dist/esm/lib/find-config.js +42 -0
- package/dist/esm/lib/find-config.js.map +1 -0
- package/dist/esm/lib/json-schema.d.ts +18 -0
- package/dist/esm/lib/json-schema.js +66 -0
- package/dist/esm/lib/json-schema.js.map +1 -0
- package/dist/esm/lib/resolve-server-config.d.ts +50 -0
- package/dist/esm/lib/resolve-server-config.js +154 -0
- package/dist/esm/lib/resolve-server-config.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/types.d.ts +21 -0
- package/dist/esm/types.js +11 -0
- package/dist/esm/types.js.map +1 -0
- package/package.json +99 -0
- package/schemas/server.schema.json +489 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import { isHttpServer, isStdioServer } from '../../types.js';
|
|
3
|
+
import { MetadataReader } from './metadata-reader.js';
|
|
4
|
+
const metadataReader = new MetadataReader();
|
|
5
|
+
export async function validateCommand(filePath) {
|
|
6
|
+
// 1. Check file exists
|
|
7
|
+
if (!fs.existsSync(filePath)) {
|
|
8
|
+
throw Object.assign(new Error(`File not found: ${filePath}`), {
|
|
9
|
+
code: 1
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
// 2. JSON syntax validation
|
|
13
|
+
let config;
|
|
14
|
+
try {
|
|
15
|
+
config = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
16
|
+
} catch (error) {
|
|
17
|
+
throw Object.assign(new Error(`Invalid JSON: ${error instanceof Error ? error.message : String(error)}`), {
|
|
18
|
+
code: 1
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
// 3. Schema validation - check basic structure
|
|
22
|
+
if (!config.mcpServers || typeof config.mcpServers !== 'object') {
|
|
23
|
+
throw Object.assign(new Error('Config must have "mcpServers" object'), {
|
|
24
|
+
code: 1
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
console.log(`✅ ${filePath} has valid JSON structure\n`);
|
|
28
|
+
console.log('Server Configuration Validation:\n');
|
|
29
|
+
// 4. Validate each server
|
|
30
|
+
let hasErrors = false;
|
|
31
|
+
let hasWarnings = false;
|
|
32
|
+
const servers = Object.entries(config.mcpServers);
|
|
33
|
+
if (servers.length === 0) {
|
|
34
|
+
console.log(' ⚠️ No servers configured');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
for (const [serverName, serverConfig] of servers){
|
|
38
|
+
const validation = await validateServerConfig(serverName, serverConfig);
|
|
39
|
+
if (validation.valid) {
|
|
40
|
+
console.log(` ✅ ${serverName}: Valid configuration`);
|
|
41
|
+
} else if (validation.severity === 'error') {
|
|
42
|
+
console.log(` ❌ ${serverName}: ${validation.message}`);
|
|
43
|
+
hasErrors = true;
|
|
44
|
+
} else {
|
|
45
|
+
console.log(` ⚠️ ${serverName}: ${validation.message}`);
|
|
46
|
+
hasWarnings = true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
console.log('');
|
|
50
|
+
if (hasErrors) {
|
|
51
|
+
throw Object.assign(new Error('Validation failed with errors'), {
|
|
52
|
+
code: 1
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (hasWarnings) {
|
|
56
|
+
console.log('⚠️ Validation passed with warnings');
|
|
57
|
+
} else {
|
|
58
|
+
console.log('✅ All validations passed');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function validateServerConfig(serverName, serverConfig) {
|
|
62
|
+
var _serverConfig_start, _serverConfig_start1;
|
|
63
|
+
// Check basic structure based on server type
|
|
64
|
+
if (isHttpServer(serverConfig)) {
|
|
65
|
+
// HTTP server validation
|
|
66
|
+
if (!serverConfig.url) {
|
|
67
|
+
return {
|
|
68
|
+
valid: false,
|
|
69
|
+
severity: 'error',
|
|
70
|
+
message: 'HTTP server missing required "url" field'
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// If has start block, validate it
|
|
74
|
+
if (serverConfig.start) {
|
|
75
|
+
if (!serverConfig.start.command) {
|
|
76
|
+
return {
|
|
77
|
+
valid: false,
|
|
78
|
+
severity: 'error',
|
|
79
|
+
message: 'HTTP server "start" block missing required "command" field'
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (serverConfig.start.args !== undefined && !Array.isArray(serverConfig.start.args)) {
|
|
83
|
+
return {
|
|
84
|
+
valid: false,
|
|
85
|
+
severity: 'error',
|
|
86
|
+
message: 'HTTP server "start.args" field must be an array'
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
// Stdio server validation
|
|
92
|
+
if (!serverConfig.command) {
|
|
93
|
+
return {
|
|
94
|
+
valid: false,
|
|
95
|
+
severity: 'error',
|
|
96
|
+
message: 'Stdio server missing required "command" field'
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (serverConfig.args !== undefined && !Array.isArray(serverConfig.args)) {
|
|
100
|
+
return {
|
|
101
|
+
valid: false,
|
|
102
|
+
severity: 'error',
|
|
103
|
+
message: 'Stdio server "args" field must be an array'
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Try to load server metadata
|
|
108
|
+
let metadata;
|
|
109
|
+
try {
|
|
110
|
+
metadata = await metadataReader.readServerMetadata(serverName);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
return {
|
|
113
|
+
valid: false,
|
|
114
|
+
severity: 'warning',
|
|
115
|
+
message: `Cannot verify: server package not found (${error instanceof Error ? error.message : String(error)})`
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
// Detect transport using type guard
|
|
119
|
+
const transport = isHttpServer(serverConfig) ? 'streamable-http' : 'stdio';
|
|
120
|
+
// Get package config from server.json
|
|
121
|
+
const pkg = metadataReader.getPackageForTransport(metadata, transport);
|
|
122
|
+
if (!pkg) {
|
|
123
|
+
return {
|
|
124
|
+
valid: false,
|
|
125
|
+
severity: 'error',
|
|
126
|
+
message: `No ${transport} transport configuration found in server.json`
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// Determine which env to check based on server type
|
|
130
|
+
const envToCheck = isStdioServer(serverConfig) ? serverConfig.env : (_serverConfig_start = serverConfig.start) === null || _serverConfig_start === void 0 ? void 0 : _serverConfig_start.env;
|
|
131
|
+
// Check for missing required env vars (based on server.json)
|
|
132
|
+
const missingEnvVars = pkg.environmentVariables.filter((v)=>{
|
|
133
|
+
if (!v.isRequired) return false; // Skip optional vars
|
|
134
|
+
const envValue = envToCheck === null || envToCheck === void 0 ? void 0 : envToCheck[v.name];
|
|
135
|
+
// Consider it missing if it's not set or is a placeholder like ${VAR_NAME}
|
|
136
|
+
return !envValue || envValue.match(/^\$\{.*\}$/);
|
|
137
|
+
});
|
|
138
|
+
if (missingEnvVars.length > 0) {
|
|
139
|
+
const varNames = missingEnvVars.map((v)=>v.name).join(', ');
|
|
140
|
+
return {
|
|
141
|
+
valid: false,
|
|
142
|
+
severity: 'warning',
|
|
143
|
+
message: `Missing or placeholder required env vars: ${varNames}`
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// Get args to validate based on server type
|
|
147
|
+
const argsToCheck = isStdioServer(serverConfig) ? serverConfig.args || [] : ((_serverConfig_start1 = serverConfig.start) === null || _serverConfig_start1 === void 0 ? void 0 : _serverConfig_start1.args) || [];
|
|
148
|
+
// Get valid args from server.json
|
|
149
|
+
const validArgNames = new Set(pkg.packageArguments.map((a)=>a.name));
|
|
150
|
+
// Check for unknown/outdated args
|
|
151
|
+
const unknownArgs = argsToCheck.filter((arg)=>{
|
|
152
|
+
if (!arg.startsWith('--')) return false; // Not a flag
|
|
153
|
+
const argName = arg.split('=')[0]; // Get just the flag name
|
|
154
|
+
if (!argName) return false; // Skip if somehow empty
|
|
155
|
+
return !validArgNames.has(argName);
|
|
156
|
+
});
|
|
157
|
+
if (unknownArgs.length > 0) {
|
|
158
|
+
return {
|
|
159
|
+
valid: false,
|
|
160
|
+
severity: 'warning',
|
|
161
|
+
message: `Unknown arguments: ${unknownArgs.join(', ')}. May be outdated - consider regenerating config.`
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
valid: true
|
|
166
|
+
};
|
|
167
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/cli/src/commands/manifest/validate.ts"],"sourcesContent":["import * as fs from 'fs';\nimport type { MCPConfiguration, ServerConfig } from '../../types.ts';\nimport { isHttpServer, isStdioServer } from '../../types.ts';\nimport { MetadataReader, type ServerMetadata } from './metadata-reader.ts';\n\ninterface ValidationResult {\n valid: boolean;\n severity?: 'error' | 'warning';\n message?: string;\n}\n\nconst metadataReader = new MetadataReader();\n\nexport async function validateCommand(filePath: string): Promise<void> {\n // 1. Check file exists\n if (!fs.existsSync(filePath)) {\n throw Object.assign(new Error(`File not found: ${filePath}`), { code: 1 });\n }\n\n // 2. JSON syntax validation\n let config: MCPConfiguration;\n try {\n config = JSON.parse(fs.readFileSync(filePath, 'utf8')) as MCPConfiguration;\n } catch (error) {\n throw Object.assign(new Error(`Invalid JSON: ${error instanceof Error ? error.message : String(error)}`), {\n code: 1,\n });\n }\n\n // 3. Schema validation - check basic structure\n if (!config.mcpServers || typeof config.mcpServers !== 'object') {\n throw Object.assign(new Error('Config must have \"mcpServers\" object'), { code: 1 });\n }\n\n console.log(`✅ ${filePath} has valid JSON structure\\n`);\n console.log('Server Configuration Validation:\\n');\n\n // 4. Validate each server\n let hasErrors = false;\n let hasWarnings = false;\n\n const servers = Object.entries(config.mcpServers);\n\n if (servers.length === 0) {\n console.log(' ⚠️ No servers configured');\n return;\n }\n\n for (const [serverName, serverConfig] of servers) {\n const validation = await validateServerConfig(serverName, serverConfig);\n\n if (validation.valid) {\n console.log(` ✅ ${serverName}: Valid configuration`);\n } else if (validation.severity === 'error') {\n console.log(` ❌ ${serverName}: ${validation.message}`);\n hasErrors = true;\n } else {\n console.log(` ⚠️ ${serverName}: ${validation.message}`);\n hasWarnings = true;\n }\n }\n\n console.log('');\n\n if (hasErrors) {\n throw Object.assign(new Error('Validation failed with errors'), { code: 1 });\n }\n\n if (hasWarnings) {\n console.log('⚠️ Validation passed with warnings');\n } else {\n console.log('✅ All validations passed');\n }\n}\n\nasync function validateServerConfig(serverName: string, serverConfig: ServerConfig): Promise<ValidationResult> {\n // Check basic structure based on server type\n if (isHttpServer(serverConfig)) {\n // HTTP server validation\n if (!serverConfig.url) {\n return {\n valid: false,\n severity: 'error',\n message: 'HTTP server missing required \"url\" field',\n };\n }\n // If has start block, validate it\n if (serverConfig.start) {\n if (!serverConfig.start.command) {\n return {\n valid: false,\n severity: 'error',\n message: 'HTTP server \"start\" block missing required \"command\" field',\n };\n }\n if (serverConfig.start.args !== undefined && !Array.isArray(serverConfig.start.args)) {\n return {\n valid: false,\n severity: 'error',\n message: 'HTTP server \"start.args\" field must be an array',\n };\n }\n }\n } else {\n // Stdio server validation\n if (!serverConfig.command) {\n return {\n valid: false,\n severity: 'error',\n message: 'Stdio server missing required \"command\" field',\n };\n }\n if (serverConfig.args !== undefined && !Array.isArray(serverConfig.args)) {\n return {\n valid: false,\n severity: 'error',\n message: 'Stdio server \"args\" field must be an array',\n };\n }\n }\n\n // Try to load server metadata\n let metadata: ServerMetadata;\n try {\n metadata = await metadataReader.readServerMetadata(serverName);\n } catch (error) {\n return {\n valid: false,\n severity: 'warning',\n message: `Cannot verify: server package not found (${error instanceof Error ? error.message : String(error)})`,\n };\n }\n\n // Detect transport using type guard\n const transport = isHttpServer(serverConfig) ? 'streamable-http' : 'stdio';\n\n // Get package config from server.json\n const pkg = metadataReader.getPackageForTransport(metadata, transport as 'stdio' | 'streamable-http');\n if (!pkg) {\n return {\n valid: false,\n severity: 'error',\n message: `No ${transport} transport configuration found in server.json`,\n };\n }\n\n // Determine which env to check based on server type\n const envToCheck = isStdioServer(serverConfig) ? serverConfig.env : serverConfig.start?.env;\n\n // Check for missing required env vars (based on server.json)\n const missingEnvVars = pkg.environmentVariables.filter((v) => {\n if (!v.isRequired) return false; // Skip optional vars\n const envValue = envToCheck?.[v.name];\n // Consider it missing if it's not set or is a placeholder like ${VAR_NAME}\n return !envValue || envValue.match(/^\\$\\{.*\\}$/);\n });\n\n if (missingEnvVars.length > 0) {\n const varNames = missingEnvVars.map((v) => v.name).join(', ');\n return {\n valid: false,\n severity: 'warning',\n message: `Missing or placeholder required env vars: ${varNames}`,\n };\n }\n\n // Get args to validate based on server type\n const argsToCheck = isStdioServer(serverConfig) ? serverConfig.args || [] : serverConfig.start?.args || [];\n\n // Get valid args from server.json\n const validArgNames = new Set(pkg.packageArguments.map((a) => a.name));\n\n // Check for unknown/outdated args\n const unknownArgs = argsToCheck.filter((arg: string) => {\n if (!arg.startsWith('--')) return false; // Not a flag\n const argName = arg.split('=')[0]; // Get just the flag name\n if (!argName) return false; // Skip if somehow empty\n return !validArgNames.has(argName);\n });\n\n if (unknownArgs.length > 0) {\n return {\n valid: false,\n severity: 'warning',\n message: `Unknown arguments: ${unknownArgs.join(', ')}. May be outdated - consider regenerating config.`,\n };\n }\n\n return { valid: true };\n}\n"],"names":["fs","isHttpServer","isStdioServer","MetadataReader","metadataReader","validateCommand","filePath","existsSync","Object","assign","Error","code","config","JSON","parse","readFileSync","error","message","String","mcpServers","console","log","hasErrors","hasWarnings","servers","entries","length","serverName","serverConfig","validation","validateServerConfig","valid","severity","url","start","command","args","undefined","Array","isArray","metadata","readServerMetadata","transport","pkg","getPackageForTransport","envToCheck","env","missingEnvVars","environmentVariables","filter","v","isRequired","envValue","name","match","varNames","map","join","argsToCheck","validArgNames","Set","packageArguments","a","unknownArgs","arg","startsWith","argName","split","has"],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* read-resource.ts
|
|
3
|
+
*
|
|
4
|
+
* Read MCP resources from the command line.
|
|
5
|
+
* Supports stdio (spawned) and http (remote) servers per MCP spec.
|
|
6
|
+
*/
|
|
7
|
+
import { type InlineConfigOptions } from '../lib/resolve-server-config.js';
|
|
8
|
+
export interface ReadResourceOptions extends InlineConfigOptions {
|
|
9
|
+
uri: string;
|
|
10
|
+
json?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Main read-resource command implementation.
|
|
14
|
+
*
|
|
15
|
+
* @param opts - Read resource options from CLI flags
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Read a resource by URI
|
|
19
|
+
* await readResourceCommand({
|
|
20
|
+
* server: 'gmail',
|
|
21
|
+
* uri: 'gmail://messages/abc123',
|
|
22
|
+
* });
|
|
23
|
+
*/
|
|
24
|
+
export declare function readResourceCommand(opts: ReadResourceOptions): Promise<void>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* read-resource.ts
|
|
3
|
+
*
|
|
4
|
+
* Read MCP resources from the command line.
|
|
5
|
+
* Supports stdio (spawned) and http (remote) servers per MCP spec.
|
|
6
|
+
*/ import { createServerRegistry } from '@mcp-z/client';
|
|
7
|
+
import { resolveServerConfig } from '../lib/resolve-server-config.js';
|
|
8
|
+
import { isHttpServer } from '../types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Main read-resource command implementation.
|
|
11
|
+
*
|
|
12
|
+
* @param opts - Read resource options from CLI flags
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // Read a resource by URI
|
|
16
|
+
* await readResourceCommand({
|
|
17
|
+
* server: 'gmail',
|
|
18
|
+
* uri: 'gmail://messages/abc123',
|
|
19
|
+
* });
|
|
20
|
+
*/ export async function readResourceCommand(opts) {
|
|
21
|
+
let registry;
|
|
22
|
+
let client;
|
|
23
|
+
try {
|
|
24
|
+
// 1. Resolve server configuration (from config file or inline options)
|
|
25
|
+
const { serverName, serverConfig, configDir } = resolveServerConfig(opts);
|
|
26
|
+
// 2. Create registry and connect
|
|
27
|
+
const start = Date.now();
|
|
28
|
+
if (isHttpServer(serverConfig)) {
|
|
29
|
+
// HTTP server - no spawning needed
|
|
30
|
+
if (!opts.json) {
|
|
31
|
+
console.log(`🔗 Connecting to ${serverName}...`);
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
// Stdio server - will be spawned
|
|
35
|
+
if (!opts.json) {
|
|
36
|
+
console.log(`🚀 Spawning ${serverName} server...`);
|
|
37
|
+
}
|
|
38
|
+
if (!serverConfig.command) {
|
|
39
|
+
throw new Error(`Stdio server ${serverName} missing required "command" field`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Create registry (spawns stdio servers, registers HTTP servers)
|
|
43
|
+
registry = createServerRegistry({
|
|
44
|
+
[serverName]: serverConfig
|
|
45
|
+
}, {
|
|
46
|
+
cwd: configDir
|
|
47
|
+
});
|
|
48
|
+
client = await registry.connect(serverName);
|
|
49
|
+
if (!isHttpServer(serverConfig) && !opts.json) {
|
|
50
|
+
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
|
|
51
|
+
console.log(`✓ Server ready in ${elapsed}s\n`);
|
|
52
|
+
}
|
|
53
|
+
// 5. Read resource
|
|
54
|
+
if (!opts.json) {
|
|
55
|
+
console.log(`📖 Reading ${opts.uri}...`);
|
|
56
|
+
}
|
|
57
|
+
const resourceResponse = await client.readResource(opts.uri);
|
|
58
|
+
const resource = resourceResponse.raw();
|
|
59
|
+
// Success case
|
|
60
|
+
if (opts.json) {
|
|
61
|
+
// JSON output mode
|
|
62
|
+
console.log(JSON.stringify(resource, null, 2));
|
|
63
|
+
} else {
|
|
64
|
+
// Human-readable output
|
|
65
|
+
console.log('✅ Read succeeded\n');
|
|
66
|
+
// Display contents
|
|
67
|
+
for (const content of resource.contents){
|
|
68
|
+
if ('text' in content) {
|
|
69
|
+
console.log('Content:');
|
|
70
|
+
console.log(content.text);
|
|
71
|
+
} else if ('blob' in content) {
|
|
72
|
+
console.log(`Blob content (${content.mimeType || 'unknown type'}): ${content.blob.length} bytes`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (opts.json) {
|
|
78
|
+
console.log(JSON.stringify({
|
|
79
|
+
error: error instanceof Error ? error.message : String(error)
|
|
80
|
+
}, null, 2));
|
|
81
|
+
} else {
|
|
82
|
+
console.error(`\n❌ ${error instanceof Error ? error.message : String(error)}`);
|
|
83
|
+
}
|
|
84
|
+
throw error;
|
|
85
|
+
} finally{
|
|
86
|
+
// 7. Cleanup - registry.close() handles both client and server close
|
|
87
|
+
if (registry) {
|
|
88
|
+
try {
|
|
89
|
+
await registry.close();
|
|
90
|
+
} catch (_) {
|
|
91
|
+
// Ignore close errors
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/cli/src/commands/read-resource.ts"],"sourcesContent":["/**\n * read-resource.ts\n *\n * Read MCP resources from the command line.\n * Supports stdio (spawned) and http (remote) servers per MCP spec.\n */\n\nimport { createServerRegistry, type ManagedClient, type ServerRegistry } from '@mcp-z/client';\nimport { type InlineConfigOptions, resolveServerConfig } from '../lib/resolve-server-config.ts';\nimport { isHttpServer } from '../types.ts';\n\nexport interface ReadResourceOptions extends InlineConfigOptions {\n uri: string; // Resource URI (positional)\n json?: boolean; // --json\n}\n\n/**\n * Main read-resource command implementation.\n *\n * @param opts - Read resource options from CLI flags\n *\n * @example\n * // Read a resource by URI\n * await readResourceCommand({\n * server: 'gmail',\n * uri: 'gmail://messages/abc123',\n * });\n */\nexport async function readResourceCommand(opts: ReadResourceOptions): Promise<void> {\n let registry: ServerRegistry | undefined;\n let client: ManagedClient | undefined;\n\n try {\n // 1. Resolve server configuration (from config file or inline options)\n const { serverName, serverConfig, configDir } = resolveServerConfig(opts);\n\n // 2. Create registry and connect\n const start = Date.now();\n\n if (isHttpServer(serverConfig)) {\n // HTTP server - no spawning needed\n if (!opts.json) {\n console.log(`🔗 Connecting to ${serverName}...`);\n }\n } else {\n // Stdio server - will be spawned\n if (!opts.json) {\n console.log(`🚀 Spawning ${serverName} server...`);\n }\n\n if (!serverConfig.command) {\n throw new Error(`Stdio server ${serverName} missing required \"command\" field`);\n }\n }\n\n // Create registry (spawns stdio servers, registers HTTP servers)\n registry = createServerRegistry({ [serverName]: serverConfig }, { cwd: configDir });\n client = await registry.connect(serverName);\n\n if (!isHttpServer(serverConfig) && !opts.json) {\n const elapsed = ((Date.now() - start) / 1000).toFixed(1);\n console.log(`✓ Server ready in ${elapsed}s\\n`);\n }\n\n // 5. Read resource\n if (!opts.json) {\n console.log(`📖 Reading ${opts.uri}...`);\n }\n\n const resourceResponse = await client.readResource(opts.uri);\n const resource = resourceResponse.raw();\n\n // Success case\n if (opts.json) {\n // JSON output mode\n console.log(JSON.stringify(resource, null, 2));\n } else {\n // Human-readable output\n console.log('✅ Read succeeded\\n');\n\n // Display contents\n for (const content of resource.contents) {\n if ('text' in content) {\n console.log('Content:');\n console.log(content.text);\n } else if ('blob' in content) {\n console.log(`Blob content (${content.mimeType || 'unknown type'}): ${content.blob.length} bytes`);\n }\n }\n }\n } catch (error) {\n if (opts.json) {\n console.log(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }, null, 2));\n } else {\n console.error(`\\n❌ ${error instanceof Error ? error.message : String(error)}`);\n }\n throw error;\n } finally {\n // 7. Cleanup - registry.close() handles both client and server close\n if (registry) {\n try {\n await registry.close();\n } catch (_) {\n // Ignore close errors\n }\n }\n }\n}\n"],"names":["createServerRegistry","resolveServerConfig","isHttpServer","readResourceCommand","opts","registry","client","serverName","serverConfig","configDir","start","Date","now","json","console","log","command","Error","cwd","connect","elapsed","toFixed","uri","resourceResponse","readResource","resource","raw","JSON","stringify","content","contents","text","mimeType","blob","length","error","message","String","close","_"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* search.ts
|
|
3
|
+
*
|
|
4
|
+
* Search MCP server capabilities (tools, prompts, resources) without loading full schemas.
|
|
5
|
+
* Designed for agent discovery workflows.
|
|
6
|
+
*/
|
|
7
|
+
export interface SearchCommandOptions {
|
|
8
|
+
config?: string;
|
|
9
|
+
servers?: string;
|
|
10
|
+
types?: string;
|
|
11
|
+
fields?: string;
|
|
12
|
+
limit?: number;
|
|
13
|
+
threshold?: number;
|
|
14
|
+
json?: boolean;
|
|
15
|
+
attach?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Main search command implementation.
|
|
19
|
+
*
|
|
20
|
+
* @param query - The search query string
|
|
21
|
+
* @param opts - Search options from CLI flags
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // Search for email-related capabilities
|
|
25
|
+
* await searchCommand('send email', {});
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // Search only tools in specific servers
|
|
29
|
+
* await searchCommand('spreadsheet', { types: 'tool', servers: 'sheets,drive' });
|
|
30
|
+
*/
|
|
31
|
+
export declare function searchCommand(query: string, opts?: SearchCommandOptions): Promise<void>;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* search.ts
|
|
3
|
+
*
|
|
4
|
+
* Search MCP server capabilities (tools, prompts, resources) without loading full schemas.
|
|
5
|
+
* Designed for agent discovery workflows.
|
|
6
|
+
*/ import { createServerRegistry } from '@mcp-z/client';
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { findConfigPath } from '../lib/find-config.js';
|
|
10
|
+
/**
|
|
11
|
+
* Main search command implementation.
|
|
12
|
+
*
|
|
13
|
+
* @param query - The search query string
|
|
14
|
+
* @param opts - Search options from CLI flags
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // Search for email-related capabilities
|
|
18
|
+
* await searchCommand('send email', {});
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // Search only tools in specific servers
|
|
22
|
+
* await searchCommand('spreadsheet', { types: 'tool', servers: 'sheets,drive' });
|
|
23
|
+
*/ export async function searchCommand(query, opts = {}) {
|
|
24
|
+
let registry;
|
|
25
|
+
try {
|
|
26
|
+
var _ref, _raw_mcpServers, _opts_limit, _opts_threshold, _searchOptions_servers;
|
|
27
|
+
const configPath = findConfigPath(opts.config);
|
|
28
|
+
const raw = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
29
|
+
const servers = (_ref = (_raw_mcpServers = raw.mcpServers) !== null && _raw_mcpServers !== void 0 ? _raw_mcpServers : raw.servers) !== null && _ref !== void 0 ? _ref : raw;
|
|
30
|
+
const configDir = path.dirname(configPath);
|
|
31
|
+
// Parse options
|
|
32
|
+
const searchOptions = {
|
|
33
|
+
types: parseTypes(opts.types),
|
|
34
|
+
servers: parseServers(opts.servers),
|
|
35
|
+
searchFields: parseFields(opts.fields),
|
|
36
|
+
limit: (_opts_limit = opts.limit) !== null && _opts_limit !== void 0 ? _opts_limit : 20,
|
|
37
|
+
threshold: (_opts_threshold = opts.threshold) !== null && _opts_threshold !== void 0 ? _opts_threshold : 0
|
|
38
|
+
};
|
|
39
|
+
// Create registry (spawns stdio servers, registers HTTP servers)
|
|
40
|
+
// In attach mode, don't spawn - just register for connection
|
|
41
|
+
registry = createServerRegistry(servers, {
|
|
42
|
+
cwd: configDir,
|
|
43
|
+
dialects: opts.attach ? [] : [
|
|
44
|
+
'servers',
|
|
45
|
+
'start'
|
|
46
|
+
]
|
|
47
|
+
});
|
|
48
|
+
// Connect to all servers (or filtered subset)
|
|
49
|
+
const serverNames = (_searchOptions_servers = searchOptions.servers) !== null && _searchOptions_servers !== void 0 ? _searchOptions_servers : Object.keys(servers || {});
|
|
50
|
+
for (const serverName of serverNames){
|
|
51
|
+
try {
|
|
52
|
+
await registry.connect(serverName);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
// Log connection errors but continue with other servers
|
|
55
|
+
if (!opts.json) {
|
|
56
|
+
console.error(`⚠ Failed to connect to ${serverName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Search across connected clients
|
|
61
|
+
const response = await registry.searchCapabilities(query, searchOptions);
|
|
62
|
+
// Output results
|
|
63
|
+
if (opts.json) {
|
|
64
|
+
outputJSON(response);
|
|
65
|
+
} else {
|
|
66
|
+
outputPretty(response);
|
|
67
|
+
}
|
|
68
|
+
} finally{
|
|
69
|
+
// Cleanup - registry.close() handles all clients and servers
|
|
70
|
+
if (registry) {
|
|
71
|
+
try {
|
|
72
|
+
await registry.close();
|
|
73
|
+
} catch (_) {
|
|
74
|
+
// Ignore close errors
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Parse comma-separated types into CapabilityType array
|
|
81
|
+
*/ function parseTypes(typesStr) {
|
|
82
|
+
if (!typesStr) return undefined;
|
|
83
|
+
const validTypes = [
|
|
84
|
+
'tool',
|
|
85
|
+
'prompt',
|
|
86
|
+
'resource'
|
|
87
|
+
];
|
|
88
|
+
const parsed = typesStr.split(',').map((t)=>t.trim().toLowerCase());
|
|
89
|
+
const invalid = parsed.filter((t)=>!validTypes.includes(t));
|
|
90
|
+
if (invalid.length > 0) {
|
|
91
|
+
throw new Error(`Invalid types: ${invalid.join(', ')}. Valid types: ${validTypes.join(', ')}`);
|
|
92
|
+
}
|
|
93
|
+
return parsed;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Parse comma-separated server names
|
|
97
|
+
*/ function parseServers(serversStr) {
|
|
98
|
+
if (!serversStr) return undefined;
|
|
99
|
+
return serversStr.split(',').map((s)=>s.trim());
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Parse comma-separated search fields
|
|
103
|
+
*/ function parseFields(fieldsStr) {
|
|
104
|
+
if (!fieldsStr) return undefined;
|
|
105
|
+
const validFields = [
|
|
106
|
+
'name',
|
|
107
|
+
'description',
|
|
108
|
+
'schema',
|
|
109
|
+
'server'
|
|
110
|
+
];
|
|
111
|
+
const parsed = fieldsStr.split(',').map((f)=>f.trim().toLowerCase());
|
|
112
|
+
const invalid = parsed.filter((f)=>!validFields.includes(f));
|
|
113
|
+
if (invalid.length > 0) {
|
|
114
|
+
throw new Error(`Invalid fields: ${invalid.join(', ')}. Valid fields: ${validFields.join(', ')}`);
|
|
115
|
+
}
|
|
116
|
+
return parsed;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Output results as JSON
|
|
120
|
+
*/ function outputJSON(response) {
|
|
121
|
+
console.log(JSON.stringify(response, null, 2));
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Output results in human-readable format
|
|
125
|
+
*/ function outputPretty(response) {
|
|
126
|
+
if (response.results.length === 0) {
|
|
127
|
+
console.log(`No results found for "${response.query}"`);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
console.log(`Found ${response.total} result${response.total !== 1 ? 's' : ''} for "${response.query}"${response.total > response.results.length ? ` (showing ${response.results.length})` : ''}:\n`);
|
|
131
|
+
for (const result of response.results){
|
|
132
|
+
const typeIcon = result.type === 'tool' ? '🔧' : result.type === 'prompt' ? '💬' : '📄';
|
|
133
|
+
const score = (result.score * 100).toFixed(0);
|
|
134
|
+
console.log(`${typeIcon} ${result.name} [${result.type}] (${score}%)`);
|
|
135
|
+
console.log(` Server: ${result.server}`);
|
|
136
|
+
if (result.description) {
|
|
137
|
+
const desc = result.description.length > 100 ? `${result.description.slice(0, 97)}...` : result.description;
|
|
138
|
+
console.log(` ${desc}`);
|
|
139
|
+
}
|
|
140
|
+
if (result.matchedOn.length > 0) {
|
|
141
|
+
console.log(` Matched: ${result.matchedOn.join(', ')}`);
|
|
142
|
+
}
|
|
143
|
+
console.log('');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/cli/src/commands/search.ts"],"sourcesContent":["/**\n * search.ts\n *\n * Search MCP server capabilities (tools, prompts, resources) without loading full schemas.\n * Designed for agent discovery workflows.\n */\n\nimport { type CapabilityType, createServerRegistry, type SearchField, type SearchOptions, type SearchResponse, type ServerRegistry } from '@mcp-z/client';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { findConfigPath } from '../lib/find-config.ts';\n\nexport interface SearchCommandOptions {\n config?: string;\n servers?: string;\n types?: string;\n fields?: string;\n limit?: number;\n threshold?: number;\n json?: boolean;\n attach?: boolean;\n}\n\n/**\n * Main search command implementation.\n *\n * @param query - The search query string\n * @param opts - Search options from CLI flags\n *\n * @example\n * // Search for email-related capabilities\n * await searchCommand('send email', {});\n *\n * @example\n * // Search only tools in specific servers\n * await searchCommand('spreadsheet', { types: 'tool', servers: 'sheets,drive' });\n */\nexport async function searchCommand(query: string, opts: SearchCommandOptions = {}): Promise<void> {\n let registry: ServerRegistry | undefined;\n\n try {\n const configPath = findConfigPath(opts.config);\n const raw = JSON.parse(fs.readFileSync(configPath, 'utf8'));\n const servers = raw.mcpServers ?? raw.servers ?? raw;\n const configDir = path.dirname(configPath);\n\n // Parse options\n const searchOptions: SearchOptions = {\n types: parseTypes(opts.types),\n servers: parseServers(opts.servers),\n searchFields: parseFields(opts.fields),\n limit: opts.limit ?? 20,\n threshold: opts.threshold ?? 0,\n };\n\n // Create registry (spawns stdio servers, registers HTTP servers)\n // In attach mode, don't spawn - just register for connection\n registry = createServerRegistry(servers, {\n cwd: configDir,\n dialects: opts.attach ? [] : ['servers', 'start'], // Empty dialects = no spawning\n });\n\n // Connect to all servers (or filtered subset)\n const serverNames = searchOptions.servers ?? Object.keys(servers || {});\n\n for (const serverName of serverNames) {\n try {\n await registry.connect(serverName);\n } catch (error) {\n // Log connection errors but continue with other servers\n if (!opts.json) {\n console.error(`⚠ Failed to connect to ${serverName}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n }\n\n // Search across connected clients\n const response = await registry.searchCapabilities(query, searchOptions);\n\n // Output results\n if (opts.json) {\n outputJSON(response);\n } else {\n outputPretty(response);\n }\n } finally {\n // Cleanup - registry.close() handles all clients and servers\n if (registry) {\n try {\n await registry.close();\n } catch (_) {\n // Ignore close errors\n }\n }\n }\n}\n\n/**\n * Parse comma-separated types into CapabilityType array\n */\nfunction parseTypes(typesStr?: string): CapabilityType[] | undefined {\n if (!typesStr) return undefined;\n\n const validTypes: CapabilityType[] = ['tool', 'prompt', 'resource'];\n const parsed = typesStr.split(',').map((t) => t.trim().toLowerCase()) as CapabilityType[];\n\n const invalid = parsed.filter((t) => !validTypes.includes(t));\n if (invalid.length > 0) {\n throw new Error(`Invalid types: ${invalid.join(', ')}. Valid types: ${validTypes.join(', ')}`);\n }\n\n return parsed;\n}\n\n/**\n * Parse comma-separated server names\n */\nfunction parseServers(serversStr?: string): string[] | undefined {\n if (!serversStr) return undefined;\n return serversStr.split(',').map((s) => s.trim());\n}\n\n/**\n * Parse comma-separated search fields\n */\nfunction parseFields(fieldsStr?: string): SearchField[] | undefined {\n if (!fieldsStr) return undefined;\n\n const validFields: SearchField[] = ['name', 'description', 'schema', 'server'];\n const parsed = fieldsStr.split(',').map((f) => f.trim().toLowerCase()) as SearchField[];\n\n const invalid = parsed.filter((f) => !validFields.includes(f));\n if (invalid.length > 0) {\n throw new Error(`Invalid fields: ${invalid.join(', ')}. Valid fields: ${validFields.join(', ')}`);\n }\n\n return parsed;\n}\n\n/**\n * Output results as JSON\n */\nfunction outputJSON(response: SearchResponse): void {\n console.log(JSON.stringify(response, null, 2));\n}\n\n/**\n * Output results in human-readable format\n */\nfunction outputPretty(response: SearchResponse): void {\n if (response.results.length === 0) {\n console.log(`No results found for \"${response.query}\"`);\n return;\n }\n\n console.log(`Found ${response.total} result${response.total !== 1 ? 's' : ''} for \"${response.query}\"${response.total > response.results.length ? ` (showing ${response.results.length})` : ''}:\\n`);\n\n for (const result of response.results) {\n const typeIcon = result.type === 'tool' ? '🔧' : result.type === 'prompt' ? '💬' : '📄';\n const score = (result.score * 100).toFixed(0);\n\n console.log(`${typeIcon} ${result.name} [${result.type}] (${score}%)`);\n console.log(` Server: ${result.server}`);\n\n if (result.description) {\n const desc = result.description.length > 100 ? `${result.description.slice(0, 97)}...` : result.description;\n console.log(` ${desc}`);\n }\n\n if (result.matchedOn.length > 0) {\n console.log(` Matched: ${result.matchedOn.join(', ')}`);\n }\n\n console.log('');\n }\n}\n"],"names":["createServerRegistry","fs","path","findConfigPath","searchCommand","query","opts","registry","raw","searchOptions","configPath","config","JSON","parse","readFileSync","servers","mcpServers","configDir","dirname","types","parseTypes","parseServers","searchFields","parseFields","fields","limit","threshold","cwd","dialects","attach","serverNames","Object","keys","serverName","connect","error","json","console","Error","message","String","response","searchCapabilities","outputJSON","outputPretty","close","_","typesStr","undefined","validTypes","parsed","split","map","t","trim","toLowerCase","invalid","filter","includes","length","join","serversStr","s","fieldsStr","validFields","f","log","stringify","results","total","result","typeIcon","type","score","toFixed","name","server","description","desc","slice","matchedOn"],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type ServerRegistry } from '@mcp-z/client';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration options for the upCommand function
|
|
4
|
+
* @public
|
|
5
|
+
*/
|
|
6
|
+
export interface UpOptions {
|
|
7
|
+
/** Optional path to custom .mcp.json configuration file */
|
|
8
|
+
config?: string;
|
|
9
|
+
/** Start only stdio servers (Claude Code compatible mode) */
|
|
10
|
+
stdioOnly?: boolean;
|
|
11
|
+
/** Start only HTTP servers with start blocks (for Claude Code Desktop) */
|
|
12
|
+
httpOnly?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* MCP server configuration entry per MCP specification.
|
|
16
|
+
*
|
|
17
|
+
* Supports two transport types:
|
|
18
|
+
* - stdio (default if no type): spawned process with stdin/stdout
|
|
19
|
+
* - http: remote HTTP server
|
|
20
|
+
*
|
|
21
|
+
* Transport can be inferred from URL protocol:
|
|
22
|
+
* - http:// or https:// → http
|
|
23
|
+
*
|
|
24
|
+
* @see https://modelcontextprotocol.io/specification/2025-06-18/basic/transports
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Start a cluster of MCP servers from a configuration file or object.
|
|
28
|
+
*
|
|
29
|
+
* @param opts - Configuration options
|
|
30
|
+
* @param opts.config - Optional path to custom .mcp.json file
|
|
31
|
+
* @returns ServerRegistry with servers map, config, connect method, and close function
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // Auto-discover .mcp.json in current directory
|
|
35
|
+
* const registry = await upCommand();
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // Load from specific config
|
|
39
|
+
* const registry = await upCommand({ config: '/path/to/.mcp.json' });
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* // Use in-memory config
|
|
43
|
+
* const registry = await upCommand({
|
|
44
|
+
* mcpServers: {
|
|
45
|
+
* 'echo-stdio': { command: 'node', args: ['test/lib/servers/echo-stdio.ts'] }
|
|
46
|
+
* }
|
|
47
|
+
* });
|
|
48
|
+
*/
|
|
49
|
+
export declare function upCommand(opts?: UpOptions): Promise<ServerRegistry>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { createServerRegistry } from '@mcp-z/client';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { findConfigPath } from '../lib/find-config.js';
|
|
5
|
+
import { hasStartBlock } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* MCP server configuration entry per MCP specification.
|
|
8
|
+
*
|
|
9
|
+
* Supports two transport types:
|
|
10
|
+
* - stdio (default if no type): spawned process with stdin/stdout
|
|
11
|
+
* - http: remote HTTP server
|
|
12
|
+
*
|
|
13
|
+
* Transport can be inferred from URL protocol:
|
|
14
|
+
* - http:// or https:// → http
|
|
15
|
+
*
|
|
16
|
+
* @see https://modelcontextprotocol.io/specification/2025-06-18/basic/transports
|
|
17
|
+
*/ /**
|
|
18
|
+
* Start a cluster of MCP servers from a configuration file or object.
|
|
19
|
+
*
|
|
20
|
+
* @param opts - Configuration options
|
|
21
|
+
* @param opts.config - Optional path to custom .mcp.json file
|
|
22
|
+
* @returns ServerRegistry with servers map, config, connect method, and close function
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // Auto-discover .mcp.json in current directory
|
|
26
|
+
* const registry = await upCommand();
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // Load from specific config
|
|
30
|
+
* const registry = await upCommand({ config: '/path/to/.mcp.json' });
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* // Use in-memory config
|
|
34
|
+
* const registry = await upCommand({
|
|
35
|
+
* mcpServers: {
|
|
36
|
+
* 'echo-stdio': { command: 'node', args: ['test/lib/servers/echo-stdio.ts'] }
|
|
37
|
+
* }
|
|
38
|
+
* });
|
|
39
|
+
*/ export async function upCommand(opts = {}) {
|
|
40
|
+
var _ref, _raw_mcpServers;
|
|
41
|
+
const configPath = findConfigPath(opts.config);
|
|
42
|
+
const raw = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
43
|
+
const servers = (_ref = (_raw_mcpServers = raw.mcpServers) !== null && _raw_mcpServers !== void 0 ? _raw_mcpServers : raw.servers) !== null && _ref !== void 0 ? _ref : raw;
|
|
44
|
+
const configDir = path.dirname(configPath);
|
|
45
|
+
// Determine dialects based on flags
|
|
46
|
+
// Default is ['servers', 'start'] (spawns everything)
|
|
47
|
+
let dialects = [
|
|
48
|
+
'servers',
|
|
49
|
+
'start'
|
|
50
|
+
];
|
|
51
|
+
if (opts.stdioOnly) {
|
|
52
|
+
dialects = [
|
|
53
|
+
'servers'
|
|
54
|
+
];
|
|
55
|
+
} else if (opts.httpOnly) {
|
|
56
|
+
dialects = [
|
|
57
|
+
'start'
|
|
58
|
+
];
|
|
59
|
+
// In http-only mode, check if there are any servers with start blocks
|
|
60
|
+
const hasStartBlocks = Object.values(servers || {}).some((entry)=>entry && hasStartBlock(entry));
|
|
61
|
+
if (!hasStartBlocks) {
|
|
62
|
+
console.log(' No HTTP servers found with start configuration');
|
|
63
|
+
console.log(' (stdio servers are spawned automatically by Claude Code)');
|
|
64
|
+
// Return empty registry
|
|
65
|
+
return createServerRegistry({}, {
|
|
66
|
+
cwd: configDir
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return createServerRegistry(servers, {
|
|
71
|
+
cwd: configDir,
|
|
72
|
+
dialects
|
|
73
|
+
});
|
|
74
|
+
}
|