@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.
Files changed (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +166 -0
  3. package/bin/cli.js +5 -0
  4. package/dist/cjs/cli.d.cts +1 -0
  5. package/dist/cjs/cli.d.ts +1 -0
  6. package/dist/cjs/cli.js +576 -0
  7. package/dist/cjs/cli.js.map +1 -0
  8. package/dist/cjs/commands/call-tool.d.cts +26 -0
  9. package/dist/cjs/commands/call-tool.d.ts +26 -0
  10. package/dist/cjs/commands/call-tool.js +362 -0
  11. package/dist/cjs/commands/call-tool.js.map +1 -0
  12. package/dist/cjs/commands/get-prompt.d.cts +26 -0
  13. package/dist/cjs/commands/get-prompt.d.ts +26 -0
  14. package/dist/cjs/commands/get-prompt.js +335 -0
  15. package/dist/cjs/commands/get-prompt.js.map +1 -0
  16. package/dist/cjs/commands/inspect.d.cts +35 -0
  17. package/dist/cjs/commands/inspect.d.ts +35 -0
  18. package/dist/cjs/commands/inspect.js +896 -0
  19. package/dist/cjs/commands/inspect.js.map +1 -0
  20. package/dist/cjs/commands/manifest/env-prompting.d.cts +21 -0
  21. package/dist/cjs/commands/manifest/env-prompting.d.ts +21 -0
  22. package/dist/cjs/commands/manifest/env-prompting.js +657 -0
  23. package/dist/cjs/commands/manifest/env-prompting.js.map +1 -0
  24. package/dist/cjs/commands/manifest/generate.d.cts +124 -0
  25. package/dist/cjs/commands/manifest/generate.d.ts +124 -0
  26. package/dist/cjs/commands/manifest/generate.js +2541 -0
  27. package/dist/cjs/commands/manifest/generate.js.map +1 -0
  28. package/dist/cjs/commands/manifest/index.d.cts +2 -0
  29. package/dist/cjs/commands/manifest/index.d.ts +2 -0
  30. package/dist/cjs/commands/manifest/index.js +229 -0
  31. package/dist/cjs/commands/manifest/index.js.map +1 -0
  32. package/dist/cjs/commands/manifest/metadata-reader.d.cts +71 -0
  33. package/dist/cjs/commands/manifest/metadata-reader.d.ts +71 -0
  34. package/dist/cjs/commands/manifest/metadata-reader.js +441 -0
  35. package/dist/cjs/commands/manifest/metadata-reader.js.map +1 -0
  36. package/dist/cjs/commands/manifest/validate.d.cts +1 -0
  37. package/dist/cjs/commands/manifest/validate.d.ts +1 -0
  38. package/dist/cjs/commands/manifest/validate.js +525 -0
  39. package/dist/cjs/commands/manifest/validate.js.map +1 -0
  40. package/dist/cjs/commands/read-resource.d.cts +24 -0
  41. package/dist/cjs/commands/read-resource.d.ts +24 -0
  42. package/dist/cjs/commands/read-resource.js +311 -0
  43. package/dist/cjs/commands/read-resource.js.map +1 -0
  44. package/dist/cjs/commands/search.d.cts +31 -0
  45. package/dist/cjs/commands/search.d.ts +31 -0
  46. package/dist/cjs/commands/search.js +464 -0
  47. package/dist/cjs/commands/search.js.map +1 -0
  48. package/dist/cjs/commands/up.d.cts +49 -0
  49. package/dist/cjs/commands/up.d.ts +49 -0
  50. package/dist/cjs/commands/up.js +235 -0
  51. package/dist/cjs/commands/up.js.map +1 -0
  52. package/dist/cjs/index.d.cts +7 -0
  53. package/dist/cjs/index.d.ts +7 -0
  54. package/dist/cjs/index.js +85 -0
  55. package/dist/cjs/index.js.map +1 -0
  56. package/dist/cjs/lib/find-config.d.cts +14 -0
  57. package/dist/cjs/lib/find-config.d.ts +14 -0
  58. package/dist/cjs/lib/find-config.js +93 -0
  59. package/dist/cjs/lib/find-config.js.map +1 -0
  60. package/dist/cjs/lib/json-schema.d.cts +18 -0
  61. package/dist/cjs/lib/json-schema.d.ts +18 -0
  62. package/dist/cjs/lib/json-schema.js +306 -0
  63. package/dist/cjs/lib/json-schema.js.map +1 -0
  64. package/dist/cjs/lib/resolve-server-config.d.cts +50 -0
  65. package/dist/cjs/lib/resolve-server-config.d.ts +50 -0
  66. package/dist/cjs/lib/resolve-server-config.js +214 -0
  67. package/dist/cjs/lib/resolve-server-config.js.map +1 -0
  68. package/dist/cjs/package.json +1 -0
  69. package/dist/cjs/types.d.cts +21 -0
  70. package/dist/cjs/types.d.ts +21 -0
  71. package/dist/cjs/types.js +32 -0
  72. package/dist/cjs/types.js.map +1 -0
  73. package/dist/esm/cli.d.ts +1 -0
  74. package/dist/esm/cli.js +129 -0
  75. package/dist/esm/cli.js.map +1 -0
  76. package/dist/esm/commands/call-tool.d.ts +26 -0
  77. package/dist/esm/commands/call-tool.js +151 -0
  78. package/dist/esm/commands/call-tool.js.map +1 -0
  79. package/dist/esm/commands/get-prompt.d.ts +26 -0
  80. package/dist/esm/commands/get-prompt.js +118 -0
  81. package/dist/esm/commands/get-prompt.js.map +1 -0
  82. package/dist/esm/commands/inspect.d.ts +35 -0
  83. package/dist/esm/commands/inspect.js +438 -0
  84. package/dist/esm/commands/inspect.js.map +1 -0
  85. package/dist/esm/commands/manifest/env-prompting.d.ts +21 -0
  86. package/dist/esm/commands/manifest/env-prompting.js +213 -0
  87. package/dist/esm/commands/manifest/env-prompting.js.map +1 -0
  88. package/dist/esm/commands/manifest/generate.d.ts +124 -0
  89. package/dist/esm/commands/manifest/generate.js +1087 -0
  90. package/dist/esm/commands/manifest/generate.js.map +1 -0
  91. package/dist/esm/commands/manifest/index.d.ts +2 -0
  92. package/dist/esm/commands/manifest/index.js +23 -0
  93. package/dist/esm/commands/manifest/index.js.map +1 -0
  94. package/dist/esm/commands/manifest/metadata-reader.d.ts +71 -0
  95. package/dist/esm/commands/manifest/metadata-reader.js +143 -0
  96. package/dist/esm/commands/manifest/metadata-reader.js.map +1 -0
  97. package/dist/esm/commands/manifest/validate.d.ts +1 -0
  98. package/dist/esm/commands/manifest/validate.js +167 -0
  99. package/dist/esm/commands/manifest/validate.js.map +1 -0
  100. package/dist/esm/commands/read-resource.d.ts +24 -0
  101. package/dist/esm/commands/read-resource.js +95 -0
  102. package/dist/esm/commands/read-resource.js.map +1 -0
  103. package/dist/esm/commands/search.d.ts +31 -0
  104. package/dist/esm/commands/search.js +145 -0
  105. package/dist/esm/commands/search.js.map +1 -0
  106. package/dist/esm/commands/up.d.ts +49 -0
  107. package/dist/esm/commands/up.js +74 -0
  108. package/dist/esm/commands/up.js.map +1 -0
  109. package/dist/esm/index.d.ts +7 -0
  110. package/dist/esm/index.js +11 -0
  111. package/dist/esm/index.js.map +1 -0
  112. package/dist/esm/lib/find-config.d.ts +14 -0
  113. package/dist/esm/lib/find-config.js +42 -0
  114. package/dist/esm/lib/find-config.js.map +1 -0
  115. package/dist/esm/lib/json-schema.d.ts +18 -0
  116. package/dist/esm/lib/json-schema.js +66 -0
  117. package/dist/esm/lib/json-schema.js.map +1 -0
  118. package/dist/esm/lib/resolve-server-config.d.ts +50 -0
  119. package/dist/esm/lib/resolve-server-config.js +154 -0
  120. package/dist/esm/lib/resolve-server-config.js.map +1 -0
  121. package/dist/esm/package.json +1 -0
  122. package/dist/esm/types.d.ts +21 -0
  123. package/dist/esm/types.js +11 -0
  124. package/dist/esm/types.js.map +1 -0
  125. package/package.json +99 -0
  126. 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":"AAAA;;;;;CAKC,GAED,SAASA,oBAAoB,QAAiD,gBAAgB;AAC9F,SAAmCC,mBAAmB,QAAQ,kCAAkC;AAChG,SAASC,YAAY,QAAQ,cAAc;AAO3C;;;;;;;;;;;CAWC,GACD,OAAO,eAAeC,oBAAoBC,IAAyB;IACjE,IAAIC;IACJ,IAAIC;IAEJ,IAAI;QACF,uEAAuE;QACvE,MAAM,EAAEC,UAAU,EAAEC,YAAY,EAAEC,SAAS,EAAE,GAAGR,oBAAoBG;QAEpE,iCAAiC;QACjC,MAAMM,QAAQC,KAAKC,GAAG;QAEtB,IAAIV,aAAaM,eAAe;YAC9B,mCAAmC;YACnC,IAAI,CAACJ,KAAKS,IAAI,EAAE;gBACdC,QAAQC,GAAG,CAAC,CAAC,iBAAiB,EAAER,WAAW,GAAG,CAAC;YACjD;QACF,OAAO;YACL,iCAAiC;YACjC,IAAI,CAACH,KAAKS,IAAI,EAAE;gBACdC,QAAQC,GAAG,CAAC,CAAC,YAAY,EAAER,WAAW,UAAU,CAAC;YACnD;YAEA,IAAI,CAACC,aAAaQ,OAAO,EAAE;gBACzB,MAAM,IAAIC,MAAM,CAAC,aAAa,EAAEV,WAAW,iCAAiC,CAAC;YAC/E;QACF;QAEA,iEAAiE;QACjEF,WAAWL,qBAAqB;YAAE,CAACO,WAAW,EAAEC;QAAa,GAAG;YAAEU,KAAKT;QAAU;QACjFH,SAAS,MAAMD,SAASc,OAAO,CAACZ;QAEhC,IAAI,CAACL,aAAaM,iBAAiB,CAACJ,KAAKS,IAAI,EAAE;YAC7C,MAAMO,UAAU,AAAC,CAAA,AAACT,CAAAA,KAAKC,GAAG,KAAKF,KAAI,IAAK,IAAG,EAAGW,OAAO,CAAC;YACtDP,QAAQC,GAAG,CAAC,CAAC,kBAAkB,EAAEK,QAAQ,GAAG,CAAC;QAC/C;QAEA,mBAAmB;QACnB,IAAI,CAAChB,KAAKS,IAAI,EAAE;YACdC,QAAQC,GAAG,CAAC,CAAC,WAAW,EAAEX,KAAKkB,GAAG,CAAC,GAAG,CAAC;QACzC;QAEA,MAAMC,mBAAmB,MAAMjB,OAAOkB,YAAY,CAACpB,KAAKkB,GAAG;QAC3D,MAAMG,WAAWF,iBAAiBG,GAAG;QAErC,eAAe;QACf,IAAItB,KAAKS,IAAI,EAAE;YACb,mBAAmB;YACnBC,QAAQC,GAAG,CAACY,KAAKC,SAAS,CAACH,UAAU,MAAM;QAC7C,OAAO;YACL,wBAAwB;YACxBX,QAAQC,GAAG,CAAC;YAEZ,mBAAmB;YACnB,KAAK,MAAMc,WAAWJ,SAASK,QAAQ,CAAE;gBACvC,IAAI,UAAUD,SAAS;oBACrBf,QAAQC,GAAG,CAAC;oBACZD,QAAQC,GAAG,CAACc,QAAQE,IAAI;gBAC1B,OAAO,IAAI,UAAUF,SAAS;oBAC5Bf,QAAQC,GAAG,CAAC,CAAC,cAAc,EAAEc,QAAQG,QAAQ,IAAI,eAAe,GAAG,EAAEH,QAAQI,IAAI,CAACC,MAAM,CAAC,MAAM,CAAC;gBAClG;YACF;QACF;IACF,EAAE,OAAOC,OAAO;QACd,IAAI/B,KAAKS,IAAI,EAAE;YACbC,QAAQC,GAAG,CAACY,KAAKC,SAAS,CAAC;gBAAEO,OAAOA,iBAAiBlB,QAAQkB,MAAMC,OAAO,GAAGC,OAAOF;YAAO,GAAG,MAAM;QACtG,OAAO;YACLrB,QAAQqB,KAAK,CAAC,CAAC,IAAI,EAAEA,iBAAiBlB,QAAQkB,MAAMC,OAAO,GAAGC,OAAOF,QAAQ;QAC/E;QACA,MAAMA;IACR,SAAU;QACR,qEAAqE;QACrE,IAAI9B,UAAU;YACZ,IAAI;gBACF,MAAMA,SAASiC,KAAK;YACtB,EAAE,OAAOC,GAAG;YACV,sBAAsB;YACxB;QACF;IACF;AACF"}
@@ -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
+ }