@noetaris/harness-mcp 0.1.0 → 0.2.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/dist/index.d.ts CHANGED
@@ -30,7 +30,9 @@ declare class MCPClient {
30
30
  private cachedTools;
31
31
  private connected;
32
32
  private constructor();
33
- static fromHttp(url: string, options?: MCPClientOptions): Promise<MCPClient>;
33
+ static fromHttp(url: string, options?: MCPClientOptions & {
34
+ headers?: Record<string, string>;
35
+ }): Promise<MCPClient>;
34
36
  static fromStdio(params: MCPStdioParams): Promise<MCPClient>;
35
37
  discover(): Promise<void>;
36
38
  tools(): Tool[];
@@ -49,6 +51,7 @@ interface MCPWatchOptions {
49
51
  }
50
52
  interface MCPHttpParams extends MCPClientOptions {
51
53
  url: string;
54
+ headers?: Record<string, string>;
52
55
  }
53
56
  type LocalObserver = {
54
57
  onRunStart?: (...args: unknown[]) => void;
@@ -82,6 +85,7 @@ interface MCPHttpEntry {
82
85
  url: string;
83
86
  prefix?: string;
84
87
  rediscover?: 'per-session';
88
+ headers?: Record<string, string>;
85
89
  }
86
90
  interface MCPStdioEntry {
87
91
  transport: 'stdio';
package/dist/index.js CHANGED
@@ -24,8 +24,12 @@ var MCPClient = class _MCPClient {
24
24
  this.sdk = new Client({ name: "@noetaris/harness-mcp", version: "0.1.0" });
25
25
  }
26
26
  static async fromHttp(url, options = {}) {
27
- const client = new _MCPClient("http", options, url, void 0);
28
- const transport = new StreamableHTTPClientTransport(new URL(url));
27
+ const { headers, ...baseOptions } = options;
28
+ const client = new _MCPClient("http", baseOptions, url, void 0);
29
+ const transport = new StreamableHTTPClientTransport(
30
+ new URL(url),
31
+ headers !== void 0 ? { requestInit: { headers } } : void 0
32
+ );
29
33
  await client.sdk.connect(transport);
30
34
  await client.discover();
31
35
  return client;
@@ -120,9 +124,20 @@ function validateEntry(entry, index, configPath) {
120
124
  if (typeof raw["url"] !== "string") {
121
125
  throw new MCPConfigParseError(configPath, `servers[${index}]: url must be a string`);
122
126
  }
127
+ if (raw["headers"] !== void 0) {
128
+ const h = raw["headers"];
129
+ if (typeof h !== "object" || h === null || Array.isArray(h)) {
130
+ throw new MCPConfigParseError(configPath, `servers[${index}]: headers must be a plain object with string values`);
131
+ }
132
+ if (!Object.values(h).every((v) => typeof v === "string")) {
133
+ throw new MCPConfigParseError(configPath, `servers[${index}]: headers must be a plain object with string values`);
134
+ }
135
+ }
136
+ const headers = raw["headers"];
123
137
  const result2 = { url: raw["url"] };
124
138
  if (prefix !== void 0) result2.prefix = prefix;
125
139
  if (rediscover !== void 0) result2.rediscover = rediscover;
140
+ if (headers !== void 0) result2.headers = headers;
126
141
  return result2;
127
142
  }
128
143
  if (!("command" in raw) || raw["command"] === void 0) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mcp-client.ts","../src/mcp-manager.ts","../src/mcp-config-loader.ts"],"sourcesContent":["import { Client } from \"@modelcontextprotocol/sdk/client/index.js\"\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\"\nimport { StdioClientTransport } from \"@modelcontextprotocol/sdk/client/stdio.js\"\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\"\nimport type { Tool } from \"@noetaris/harness-types\"\n\nexport interface MCPClientOptions {\n prefix?: string\n rediscover?: \"per-session\"\n}\n\nexport interface MCPStdioParams extends MCPClientOptions {\n command: string\n args?: string[]\n env?: Record<string, string>\n}\n\ntype TransportKind = \"http\" | \"stdio\"\n\nexport type MCPCallToolResult = {\n content: Array<{ type: string; text?: string; [key: string]: unknown }>\n isError?: boolean\n}\n\nexport class MCPNotConnectedError extends Error {\n constructor() {\n super(\"not connected: client has been disconnected\")\n this.name = \"MCPNotConnectedError\"\n }\n}\n\nexport class MCPClient {\n readonly url: string | undefined\n readonly command: string | undefined\n readonly options: MCPClientOptions\n readonly transportKind: TransportKind\n\n private sdk: Client\n private cachedTools: Tool[] = []\n private connected = true\n\n private constructor(\n transportKind: TransportKind,\n options: MCPClientOptions,\n url?: string,\n command?: string,\n ) {\n this.transportKind = transportKind\n this.options = options\n this.url = url\n this.command = command\n this.sdk = new Client({ name: \"@noetaris/harness-mcp\", version: \"0.1.0\" })\n }\n\n static async fromHttp(url: string, options: MCPClientOptions = {}): Promise<MCPClient> {\n const client = new MCPClient(\"http\", options, url, undefined)\n const transport = new StreamableHTTPClientTransport(new URL(url))\n await client.sdk.connect(transport as Transport) // as: StreamableHTTPClientTransport implements Transport but the SDK typings don't extend the base interface directly\n await client.discover()\n return client\n }\n\n static async fromStdio(params: MCPStdioParams): Promise<MCPClient> {\n const { command, args, env, ...options } = params\n const client = new MCPClient(\"stdio\", options, undefined, command)\n const stdioParams = { command, ...(args !== undefined && { args }), ...(env !== undefined && { env }) }\n const transport = new StdioClientTransport(stdioParams)\n await client.sdk.connect(transport as Transport) // as: StdioClientTransport implements Transport but the SDK typings don't extend the base interface directly\n await client.discover()\n return client\n }\n\n async discover(): Promise<void> {\n const result = await this.sdk.listTools()\n const prefix = this.options.prefix\n this.cachedTools = result.tools.map(t => ({\n name: prefix !== undefined ? `${prefix}/${t.name}` : t.name,\n description: t.description ?? \"\",\n inputSchema: t.inputSchema as Record<string, unknown>, // as: MCP SDK types inputSchema as a specific JSON Schema type; harness Tool uses the wider Record<string, unknown>\n }))\n }\n\n tools(): Tool[] {\n if (!this.connected) {\n throw new MCPNotConnectedError()\n }\n return this.cachedTools\n }\n\n async callTool(params: { name: string; arguments?: Record<string, unknown> }): Promise<MCPCallToolResult> {\n if (!this.connected) {\n throw new MCPNotConnectedError()\n }\n const result = await this.sdk.callTool(params)\n return result as MCPCallToolResult // as: SDK callTool returns a wider union type; MCPCallToolResult matches the content structure\n }\n\n async disconnect(): Promise<void> {\n this.connected = false\n await this.sdk.close()\n }\n}\n","import { watch as fsWatch } from 'node:fs'\nimport { resolve } from 'node:path'\nimport type { Tool } from '@noetaris/harness-types'\nimport { MCPClient, type MCPClientOptions, type MCPStdioParams } from './mcp-client.js'\nimport { loadMCPConfig } from './mcp-config-loader.js'\n\nexport interface MCPManagerOptions {\n rediscover?: 'per-session'\n}\n\nexport interface MCPWatchOptions {\n onError?: (err: Error) => void\n}\n\nexport interface MCPHttpParams extends MCPClientOptions {\n url: string\n}\n\ntype LocalObserver = {\n onRunStart?: (...args: unknown[]) => void\n onRunEnd?: (...args: unknown[]) => void\n onStepStart?: (...args: unknown[]) => void\n onStepEnd?: (...args: unknown[]) => void\n onStepError?: (...args: unknown[]) => void\n onInterrupt?: (...args: unknown[]) => void\n onEvent?: (...args: unknown[]) => void\n}\n\nexport class MCPServerNotFoundError extends Error {\n readonly key: string\n constructor(key: string) {\n super(`no MCP server registered with key: ${key}`)\n this.name = 'MCPServerNotFoundError'\n this.key = key\n }\n}\n\nexport class MCPManager {\n private clients: MCPClient[]\n private readonly options: MCPManagerOptions\n\n constructor(clients: MCPClient[], options: MCPManagerOptions = {}) {\n this.clients = [...clients]\n this.options = options\n }\n\n tools(): Tool[] {\n const map = new Map<string, Tool>()\n for (const client of this.clients) {\n for (const tool of client.tools()) {\n map.set(tool.name, tool)\n }\n }\n return [...map.values()]\n }\n\n async addServer(params: MCPHttpParams | MCPStdioParams): Promise<void> {\n let client: MCPClient\n if ('url' in params) {\n const { url, ...clientOptions } = params\n client = await MCPClient.fromHttp(url, clientOptions)\n } else {\n client = await MCPClient.fromStdio(params)\n }\n this.clients.push(client)\n }\n\n async removeServer(key: string): Promise<void> {\n const index = this.clients.findIndex(c => c.url === key || c.command === key)\n if (index === -1) {\n throw new MCPServerNotFoundError(key)\n }\n // noUncheckedIndexedAccess: index is validated above so the value is defined\n const client = this.clients[index]!\n await client.disconnect()\n this.clients.splice(index, 1)\n }\n\n bindObserver(_observer: LocalObserver): void {\n const applicable = this.clients.filter(c => this.shouldRediscover(c))\n if (applicable.length > 0) {\n void Promise.all(applicable.map(c => c.discover()))\n }\n }\n\n async loadConfig(path: string): Promise<void> {\n const entries = await loadMCPConfig(path)\n for (const entry of entries) {\n await this.addServer(entry)\n }\n }\n\n async watch(path: string, options?: MCPWatchOptions): Promise<() => Promise<void>> {\n const resolvedPath = resolve(path)\n const onError = options?.onError\n let disposed = false\n let debounceTimer: ReturnType<typeof setTimeout> | undefined\n\n const reload = async (): Promise<void> => {\n let entries: Array<MCPHttpParams | MCPStdioParams>\n try {\n entries = await loadMCPConfig(resolvedPath)\n } catch (err) {\n onError?.(err as Error) // as: caught value is typed unknown; caller expects Error\n return\n }\n\n const currentKeys = new Set(this.clients.map(c => c.url ?? c.command ?? ''))\n const newKeys = new Set(entries.map(e => ('url' in e ? e.url : e.command) ?? ''))\n\n // entries whose key is not in current set, OR whose key is present but options differ\n const toAdd = entries.filter(e => {\n const key = ('url' in e ? e.url : e.command) ?? ''\n if (!currentKeys.has(key)) return true\n const existing = this.clients.find(c => (c.url ?? c.command) === key)\n if (existing === undefined) return true\n // compare prefix option: if different, treat as replace\n const existingPrefix = existing.options.prefix\n const newPrefix = e.prefix\n return existingPrefix !== newPrefix\n })\n // keys to remove: not in new set, OR same key but options differ (matched toAdd key)\n const toAddKeys = new Set(toAdd.map(e => ('url' in e ? e.url : e.command) ?? ''))\n const toRemoveKeys = [...currentKeys].filter(k => !newKeys.has(k) || toAddKeys.has(k))\n\n const addedKeys: string[] = []\n try {\n for (const entry of toAdd) {\n await this.addServer(entry)\n addedKeys.push(('url' in entry ? entry.url : entry.command) ?? '')\n }\n } catch (err) {\n // rollback successfully added servers in reverse order; swallow rollback errors to ensure onError is always called\n for (let i = addedKeys.length - 1; i >= 0; i--) {\n try { await this.removeServer(addedKeys[i]!) } catch { /* swallow: rollback best-effort; onError called below */ }\n }\n onError?.(err as Error) // as: caught value is typed unknown; caller expects Error\n return\n }\n\n for (const key of toRemoveKeys) {\n try {\n await this.removeServer(key)\n } catch (err) {\n onError?.(err as Error) // as: caught value is typed unknown; caller expects Error\n return\n }\n }\n }\n\n const watcher = fsWatch(resolvedPath)\n\n watcher.on('change', () => {\n if (disposed) return\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => {\n void reload()\n }, 100)\n })\n\n watcher.on('error', (err: Error) => {\n disposed = true\n clearTimeout(debounceTimer)\n watcher.close()\n onError?.(err)\n })\n\n return (): Promise<void> => {\n if (!disposed) {\n disposed = true\n clearTimeout(debounceTimer)\n watcher.close()\n }\n return Promise.resolve()\n }\n }\n\n static async fromConfig(path: string): Promise<MCPManager> {\n const manager = new MCPManager([])\n await manager.loadConfig(path)\n return manager\n }\n\n private shouldRediscover(client: MCPClient): boolean {\n if (client.options.rediscover === 'per-session') return true\n if (client.options.rediscover === undefined && this.options.rediscover === 'per-session') return true\n return false\n }\n}\n","import { readFileSync } from 'node:fs'\nimport { resolve, extname } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { MCPHttpParams } from './mcp-manager.js'\nimport type { MCPStdioParams } from './mcp-client.js'\n\nexport interface MCPHttpEntry {\n transport?: 'http'\n url: string\n prefix?: string\n rediscover?: 'per-session'\n}\n\nexport interface MCPStdioEntry {\n transport: 'stdio'\n command: string\n args?: string[]\n env?: Record<string, string>\n prefix?: string\n rediscover?: 'per-session'\n}\n\nexport type MCPServerEntry = MCPHttpEntry | MCPStdioEntry\n\nexport interface MCPConfigSchema {\n servers: MCPServerEntry[]\n}\n\nexport class MCPConfigParseError extends Error {\n readonly path: string\n readonly detail: string\n constructor(path: string, detail: string) {\n super(`invalid MCP config at ${path}: ${detail}`)\n this.name = 'MCPConfigParseError'\n this.path = path\n this.detail = detail\n }\n}\n\nexport class MCPConfigExtensionError extends Error {\n readonly path: string\n readonly extension: string\n constructor(path: string, extension: string) {\n super(`unsupported config file extension \"${extension}\" at ${path}: expected .json, .ts, .js, or .mjs`)\n this.name = 'MCPConfigExtensionError'\n this.path = path\n this.extension = extension\n }\n}\n\nfunction validateEntry(entry: unknown, index: number, configPath: string): MCPHttpParams | MCPStdioParams {\n if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {\n throw new MCPConfigParseError(configPath, `servers[${index}]: entry must be an object`)\n }\n\n const raw = entry as Record<string, unknown> // as: entry is not-null object (Array check above); Record<string,unknown> is the narrowest safe widening\n\n // default transport to 'http' if absent\n const transport = raw['transport'] === undefined ? 'http' : raw['transport']\n\n if (transport !== 'http' && transport !== 'stdio') {\n throw new MCPConfigParseError(configPath, `servers[${index}]: unrecognized transport value \"${String(transport)}\"`)\n }\n\n // validate shared optional fields\n if (raw['prefix'] !== undefined && typeof raw['prefix'] !== 'string') {\n throw new MCPConfigParseError(configPath, `servers[${index}]: prefix must be a string`)\n }\n\n if (raw['rediscover'] !== undefined && raw['rediscover'] !== 'per-session') {\n throw new MCPConfigParseError(configPath, `servers[${index}]: rediscover must be \"per-session\"`)\n }\n\n const prefix = raw['prefix'] as string | undefined // as: validated typeof === 'string' in the guard above\n const rediscover = raw['rediscover'] as 'per-session' | undefined // as: validated === 'per-session' in the guard above\n\n if (transport === 'http') {\n if (!('url' in raw) || raw['url'] === undefined) {\n throw new MCPConfigParseError(configPath, `servers[${index}]: http entry missing required field: url`)\n }\n if (typeof raw['url'] !== 'string') {\n throw new MCPConfigParseError(configPath, `servers[${index}]: url must be a string`)\n }\n\n const result: MCPHttpParams = { url: raw['url'] }\n if (prefix !== undefined) result.prefix = prefix\n if (rediscover !== undefined) result.rediscover = rediscover\n return result\n }\n\n // stdio\n if (!('command' in raw) || raw['command'] === undefined) {\n throw new MCPConfigParseError(configPath, `servers[${index}]: stdio entry missing required field: command`)\n }\n if (typeof raw['command'] !== 'string') {\n throw new MCPConfigParseError(configPath, `servers[${index}]: command must be a string`)\n }\n\n if (raw['args'] !== undefined) {\n if (!Array.isArray(raw['args'])) {\n throw new MCPConfigParseError(configPath, `servers[${index}]: args must be a string array`)\n }\n if (!(raw['args'] as unknown[]).every(a => typeof a === 'string')) { // as: Array.isArray confirmed above; unknown[] is safe for .every narrowing\n throw new MCPConfigParseError(configPath, `servers[${index}]: args must be a string array`)\n }\n }\n\n if (raw['env'] !== undefined) {\n const env = raw['env']\n if (typeof env !== 'object' || env === null || Array.isArray(env)) {\n throw new MCPConfigParseError(configPath, `servers[${index}]: env must be a plain object with string values`)\n }\n if (!Object.values(env as Record<string, unknown>).every(v => typeof v === 'string')) { // as: object/non-null/non-array confirmed above; Record<string,unknown> for Object.values narrowing\n throw new MCPConfigParseError(configPath, `servers[${index}]: env must be a plain object with string values`)\n }\n }\n\n const result: MCPStdioParams = { command: raw['command'] }\n if (raw['args'] !== undefined) result.args = raw['args'] as string[] // as: validated Array.isArray + every element is string above\n if (raw['env'] !== undefined) result.env = raw['env'] as Record<string, string> // as: validated plain object with all-string values above\n if (prefix !== undefined) result.prefix = prefix\n if (rediscover !== undefined) result.rediscover = rediscover\n return result\n}\n\nfunction validateServers(data: unknown, configPath: string): Array<MCPHttpParams | MCPStdioParams> {\n if (typeof data !== 'object' || data === null || Array.isArray(data)) {\n throw new MCPConfigParseError(configPath, 'config must be a plain object')\n }\n\n const obj = data as Record<string, unknown> // as: data is non-null non-array object (guards above); Record<string,unknown> is the narrowest safe widening\n\n if (!Array.isArray(obj['servers'])) {\n throw new MCPConfigParseError(configPath, 'servers must be an array')\n }\n\n return (obj['servers'] as unknown[]).map((entry, i) => validateEntry(entry, i, configPath)) // as: Array.isArray confirmed on line above; unknown[] is the safe element type for map\n}\n\nasync function loadFromDynamicImport(resolvedPath: string, originalPath: string): Promise<Array<MCPHttpParams | MCPStdioParams>> {\n const fileUrl = pathToFileURL(resolvedPath).href\n // dynamic import errors (missing file, syntax error) propagate as-is\n const mod = await import(fileUrl)\n const defaultExport: unknown = mod.default\n\n if (typeof defaultExport !== 'object' || defaultExport === null) {\n throw new MCPConfigParseError(originalPath, 'default export must be a non-null object (missing or invalid default export)')\n }\n\n return validateServers(defaultExport, originalPath)\n}\n\nfunction loadFromJson(resolvedPath: string, originalPath: string): Array<MCPHttpParams | MCPStdioParams> {\n let raw: string\n try {\n raw = readFileSync(resolvedPath, 'utf-8')\n } catch (err) {\n throw new MCPConfigParseError(originalPath, (err as Error).message) // as: catch binds unknown; fs errors are always Error instances with .message\n }\n\n let data: unknown\n try {\n data = JSON.parse(raw)\n } catch (err) {\n throw new MCPConfigParseError(originalPath, (err as Error).message) // as: catch binds unknown; JSON.parse throws SyntaxError (an Error) with .message\n }\n\n return validateServers(data, originalPath)\n}\n\nexport async function loadMCPConfig(configPath: string): Promise<Array<MCPHttpParams | MCPStdioParams>> {\n const resolvedPath = resolve(configPath)\n const ext = extname(configPath)\n\n if (ext === '.json') {\n return loadFromJson(resolvedPath, configPath)\n }\n\n if (ext === '.ts' || ext === '.js' || ext === '.mjs') {\n return loadFromDynamicImport(resolvedPath, configPath)\n }\n\n throw new MCPConfigExtensionError(configPath, ext)\n}\n"],"mappings":"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","names":["resolve","result","resolve"]}
1
+ {"version":3,"sources":["../src/mcp-client.ts","../src/mcp-manager.ts","../src/mcp-config-loader.ts"],"sourcesContent":["import { Client } from \"@modelcontextprotocol/sdk/client/index.js\"\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\"\nimport { StdioClientTransport } from \"@modelcontextprotocol/sdk/client/stdio.js\"\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\"\nimport type { Tool } from \"@noetaris/harness-types\"\n\nexport interface MCPClientOptions {\n prefix?: string\n rediscover?: \"per-session\"\n}\n\nexport interface MCPStdioParams extends MCPClientOptions {\n command: string\n args?: string[]\n env?: Record<string, string>\n}\n\ntype TransportKind = \"http\" | \"stdio\"\n\nexport type MCPCallToolResult = {\n content: Array<{ type: string; text?: string; [key: string]: unknown }>\n isError?: boolean\n}\n\nexport class MCPNotConnectedError extends Error {\n constructor() {\n super(\"not connected: client has been disconnected\")\n this.name = \"MCPNotConnectedError\"\n }\n}\n\nexport class MCPClient {\n readonly url: string | undefined\n readonly command: string | undefined\n readonly options: MCPClientOptions\n readonly transportKind: TransportKind\n\n private sdk: Client\n private cachedTools: Tool[] = []\n private connected = true\n\n private constructor(\n transportKind: TransportKind,\n options: MCPClientOptions,\n url?: string,\n command?: string,\n ) {\n this.transportKind = transportKind\n this.options = options\n this.url = url\n this.command = command\n this.sdk = new Client({ name: \"@noetaris/harness-mcp\", version: \"0.1.0\" })\n }\n\n static async fromHttp(url: string, options: MCPClientOptions & { headers?: Record<string, string> } = {}): Promise<MCPClient> {\n const { headers, ...baseOptions } = options\n const client = new MCPClient(\"http\", baseOptions, url, undefined)\n const transport = new StreamableHTTPClientTransport(\n new URL(url),\n headers !== undefined ? { requestInit: { headers } } : undefined,\n )\n await client.sdk.connect(transport as Transport) // as: StreamableHTTPClientTransport implements Transport but the SDK typings don't extend the base interface directly\n await client.discover()\n return client\n }\n\n static async fromStdio(params: MCPStdioParams): Promise<MCPClient> {\n const { command, args, env, ...options } = params\n const client = new MCPClient(\"stdio\", options, undefined, command)\n const stdioParams = { command, ...(args !== undefined && { args }), ...(env !== undefined && { env }) }\n const transport = new StdioClientTransport(stdioParams)\n await client.sdk.connect(transport as Transport) // as: StdioClientTransport implements Transport but the SDK typings don't extend the base interface directly\n await client.discover()\n return client\n }\n\n async discover(): Promise<void> {\n const result = await this.sdk.listTools()\n const prefix = this.options.prefix\n this.cachedTools = result.tools.map(t => ({\n name: prefix !== undefined ? `${prefix}/${t.name}` : t.name,\n description: t.description ?? \"\",\n inputSchema: t.inputSchema as Record<string, unknown>, // as: MCP SDK types inputSchema as a specific JSON Schema type; harness Tool uses the wider Record<string, unknown>\n }))\n }\n\n tools(): Tool[] {\n if (!this.connected) {\n throw new MCPNotConnectedError()\n }\n return this.cachedTools\n }\n\n async callTool(params: { name: string; arguments?: Record<string, unknown> }): Promise<MCPCallToolResult> {\n if (!this.connected) {\n throw new MCPNotConnectedError()\n }\n const result = await this.sdk.callTool(params)\n return result as MCPCallToolResult // as: SDK callTool returns a wider union type; MCPCallToolResult matches the content structure\n }\n\n async disconnect(): Promise<void> {\n this.connected = false\n await this.sdk.close()\n }\n}\n","import { watch as fsWatch } from 'node:fs'\nimport { resolve } from 'node:path'\nimport type { Tool } from '@noetaris/harness-types'\nimport { MCPClient, type MCPClientOptions, type MCPStdioParams } from './mcp-client.js'\nimport { loadMCPConfig } from './mcp-config-loader.js'\n\nexport interface MCPManagerOptions {\n rediscover?: 'per-session'\n}\n\nexport interface MCPWatchOptions {\n onError?: (err: Error) => void\n}\n\nexport interface MCPHttpParams extends MCPClientOptions {\n url: string\n headers?: Record<string, string>\n}\n\ntype LocalObserver = {\n onRunStart?: (...args: unknown[]) => void\n onRunEnd?: (...args: unknown[]) => void\n onStepStart?: (...args: unknown[]) => void\n onStepEnd?: (...args: unknown[]) => void\n onStepError?: (...args: unknown[]) => void\n onInterrupt?: (...args: unknown[]) => void\n onEvent?: (...args: unknown[]) => void\n}\n\nexport class MCPServerNotFoundError extends Error {\n readonly key: string\n constructor(key: string) {\n super(`no MCP server registered with key: ${key}`)\n this.name = 'MCPServerNotFoundError'\n this.key = key\n }\n}\n\nexport class MCPManager {\n private clients: MCPClient[]\n private readonly options: MCPManagerOptions\n\n constructor(clients: MCPClient[], options: MCPManagerOptions = {}) {\n this.clients = [...clients]\n this.options = options\n }\n\n tools(): Tool[] {\n const map = new Map<string, Tool>()\n for (const client of this.clients) {\n for (const tool of client.tools()) {\n map.set(tool.name, tool)\n }\n }\n return [...map.values()]\n }\n\n async addServer(params: MCPHttpParams | MCPStdioParams): Promise<void> {\n let client: MCPClient\n if ('url' in params) {\n const { url, ...clientOptions } = params\n client = await MCPClient.fromHttp(url, clientOptions)\n } else {\n client = await MCPClient.fromStdio(params)\n }\n this.clients.push(client)\n }\n\n async removeServer(key: string): Promise<void> {\n const index = this.clients.findIndex(c => c.url === key || c.command === key)\n if (index === -1) {\n throw new MCPServerNotFoundError(key)\n }\n // noUncheckedIndexedAccess: index is validated above so the value is defined\n const client = this.clients[index]!\n await client.disconnect()\n this.clients.splice(index, 1)\n }\n\n bindObserver(_observer: LocalObserver): void {\n const applicable = this.clients.filter(c => this.shouldRediscover(c))\n if (applicable.length > 0) {\n void Promise.all(applicable.map(c => c.discover()))\n }\n }\n\n async loadConfig(path: string): Promise<void> {\n const entries = await loadMCPConfig(path)\n for (const entry of entries) {\n await this.addServer(entry)\n }\n }\n\n async watch(path: string, options?: MCPWatchOptions): Promise<() => Promise<void>> {\n const resolvedPath = resolve(path)\n const onError = options?.onError\n let disposed = false\n let debounceTimer: ReturnType<typeof setTimeout> | undefined\n\n const reload = async (): Promise<void> => {\n let entries: Array<MCPHttpParams | MCPStdioParams>\n try {\n entries = await loadMCPConfig(resolvedPath)\n } catch (err) {\n onError?.(err as Error) // as: caught value is typed unknown; caller expects Error\n return\n }\n\n const currentKeys = new Set(this.clients.map(c => c.url ?? c.command ?? ''))\n const newKeys = new Set(entries.map(e => ('url' in e ? e.url : e.command) ?? ''))\n\n // entries whose key is not in current set, OR whose key is present but options differ\n const toAdd = entries.filter(e => {\n const key = ('url' in e ? e.url : e.command) ?? ''\n if (!currentKeys.has(key)) return true\n const existing = this.clients.find(c => (c.url ?? c.command) === key)\n if (existing === undefined) return true\n // compare prefix option: if different, treat as replace\n const existingPrefix = existing.options.prefix\n const newPrefix = e.prefix\n return existingPrefix !== newPrefix\n })\n // keys to remove: not in new set, OR same key but options differ (matched toAdd key)\n const toAddKeys = new Set(toAdd.map(e => ('url' in e ? e.url : e.command) ?? ''))\n const toRemoveKeys = [...currentKeys].filter(k => !newKeys.has(k) || toAddKeys.has(k))\n\n const addedKeys: string[] = []\n try {\n for (const entry of toAdd) {\n await this.addServer(entry)\n addedKeys.push(('url' in entry ? entry.url : entry.command) ?? '')\n }\n } catch (err) {\n // rollback successfully added servers in reverse order; swallow rollback errors to ensure onError is always called\n for (let i = addedKeys.length - 1; i >= 0; i--) {\n try { await this.removeServer(addedKeys[i]!) } catch { /* swallow: rollback best-effort; onError called below */ }\n }\n onError?.(err as Error) // as: caught value is typed unknown; caller expects Error\n return\n }\n\n for (const key of toRemoveKeys) {\n try {\n await this.removeServer(key)\n } catch (err) {\n onError?.(err as Error) // as: caught value is typed unknown; caller expects Error\n return\n }\n }\n }\n\n const watcher = fsWatch(resolvedPath)\n\n watcher.on('change', () => {\n if (disposed) return\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => {\n void reload()\n }, 100)\n })\n\n watcher.on('error', (err: Error) => {\n disposed = true\n clearTimeout(debounceTimer)\n watcher.close()\n onError?.(err)\n })\n\n return (): Promise<void> => {\n if (!disposed) {\n disposed = true\n clearTimeout(debounceTimer)\n watcher.close()\n }\n return Promise.resolve()\n }\n }\n\n static async fromConfig(path: string): Promise<MCPManager> {\n const manager = new MCPManager([])\n await manager.loadConfig(path)\n return manager\n }\n\n private shouldRediscover(client: MCPClient): boolean {\n if (client.options.rediscover === 'per-session') return true\n if (client.options.rediscover === undefined && this.options.rediscover === 'per-session') return true\n return false\n }\n}\n","import { readFileSync } from 'node:fs'\nimport { resolve, extname } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { MCPHttpParams } from './mcp-manager.js'\nimport type { MCPStdioParams } from './mcp-client.js'\n\nexport interface MCPHttpEntry {\n transport?: 'http'\n url: string\n prefix?: string\n rediscover?: 'per-session'\n headers?: Record<string, string>\n}\n\nexport interface MCPStdioEntry {\n transport: 'stdio'\n command: string\n args?: string[]\n env?: Record<string, string>\n prefix?: string\n rediscover?: 'per-session'\n}\n\nexport type MCPServerEntry = MCPHttpEntry | MCPStdioEntry\n\nexport interface MCPConfigSchema {\n servers: MCPServerEntry[]\n}\n\nexport class MCPConfigParseError extends Error {\n readonly path: string\n readonly detail: string\n constructor(path: string, detail: string) {\n super(`invalid MCP config at ${path}: ${detail}`)\n this.name = 'MCPConfigParseError'\n this.path = path\n this.detail = detail\n }\n}\n\nexport class MCPConfigExtensionError extends Error {\n readonly path: string\n readonly extension: string\n constructor(path: string, extension: string) {\n super(`unsupported config file extension \"${extension}\" at ${path}: expected .json, .ts, .js, or .mjs`)\n this.name = 'MCPConfigExtensionError'\n this.path = path\n this.extension = extension\n }\n}\n\nfunction validateEntry(entry: unknown, index: number, configPath: string): MCPHttpParams | MCPStdioParams {\n if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {\n throw new MCPConfigParseError(configPath, `servers[${index}]: entry must be an object`)\n }\n\n const raw = entry as Record<string, unknown> // as: entry is not-null object (Array check above); Record<string,unknown> is the narrowest safe widening\n\n // default transport to 'http' if absent\n const transport = raw['transport'] === undefined ? 'http' : raw['transport']\n\n if (transport !== 'http' && transport !== 'stdio') {\n throw new MCPConfigParseError(configPath, `servers[${index}]: unrecognized transport value \"${String(transport)}\"`)\n }\n\n // validate shared optional fields\n if (raw['prefix'] !== undefined && typeof raw['prefix'] !== 'string') {\n throw new MCPConfigParseError(configPath, `servers[${index}]: prefix must be a string`)\n }\n\n if (raw['rediscover'] !== undefined && raw['rediscover'] !== 'per-session') {\n throw new MCPConfigParseError(configPath, `servers[${index}]: rediscover must be \"per-session\"`)\n }\n\n const prefix = raw['prefix'] as string | undefined // as: validated typeof === 'string' in the guard above\n const rediscover = raw['rediscover'] as 'per-session' | undefined // as: validated === 'per-session' in the guard above\n\n if (transport === 'http') {\n if (!('url' in raw) || raw['url'] === undefined) {\n throw new MCPConfigParseError(configPath, `servers[${index}]: http entry missing required field: url`)\n }\n if (typeof raw['url'] !== 'string') {\n throw new MCPConfigParseError(configPath, `servers[${index}]: url must be a string`)\n }\n\n if (raw['headers'] !== undefined) {\n const h = raw['headers']\n if (typeof h !== 'object' || h === null || Array.isArray(h)) {\n throw new MCPConfigParseError(configPath, `servers[${index}]: headers must be a plain object with string values`)\n }\n if (!Object.values(h as Record<string, unknown>).every(v => typeof v === 'string')) { // as: object/non-null/non-array confirmed above; Record<string,unknown> for Object.values narrowing\n throw new MCPConfigParseError(configPath, `servers[${index}]: headers must be a plain object with string values`)\n }\n }\n\n const headers = raw['headers'] as Record<string, string> | undefined // as: validated plain object with all-string values in the guard above\n\n const result: MCPHttpParams = { url: raw['url'] }\n if (prefix !== undefined) result.prefix = prefix\n if (rediscover !== undefined) result.rediscover = rediscover\n if (headers !== undefined) result.headers = headers\n return result\n }\n\n // stdio\n if (!('command' in raw) || raw['command'] === undefined) {\n throw new MCPConfigParseError(configPath, `servers[${index}]: stdio entry missing required field: command`)\n }\n if (typeof raw['command'] !== 'string') {\n throw new MCPConfigParseError(configPath, `servers[${index}]: command must be a string`)\n }\n\n if (raw['args'] !== undefined) {\n if (!Array.isArray(raw['args'])) {\n throw new MCPConfigParseError(configPath, `servers[${index}]: args must be a string array`)\n }\n if (!(raw['args'] as unknown[]).every(a => typeof a === 'string')) { // as: Array.isArray confirmed above; unknown[] is safe for .every narrowing\n throw new MCPConfigParseError(configPath, `servers[${index}]: args must be a string array`)\n }\n }\n\n if (raw['env'] !== undefined) {\n const env = raw['env']\n if (typeof env !== 'object' || env === null || Array.isArray(env)) {\n throw new MCPConfigParseError(configPath, `servers[${index}]: env must be a plain object with string values`)\n }\n if (!Object.values(env as Record<string, unknown>).every(v => typeof v === 'string')) { // as: object/non-null/non-array confirmed above; Record<string,unknown> for Object.values narrowing\n throw new MCPConfigParseError(configPath, `servers[${index}]: env must be a plain object with string values`)\n }\n }\n\n const result: MCPStdioParams = { command: raw['command'] }\n if (raw['args'] !== undefined) result.args = raw['args'] as string[] // as: validated Array.isArray + every element is string above\n if (raw['env'] !== undefined) result.env = raw['env'] as Record<string, string> // as: validated plain object with all-string values above\n if (prefix !== undefined) result.prefix = prefix\n if (rediscover !== undefined) result.rediscover = rediscover\n return result\n}\n\nfunction validateServers(data: unknown, configPath: string): Array<MCPHttpParams | MCPStdioParams> {\n if (typeof data !== 'object' || data === null || Array.isArray(data)) {\n throw new MCPConfigParseError(configPath, 'config must be a plain object')\n }\n\n const obj = data as Record<string, unknown> // as: data is non-null non-array object (guards above); Record<string,unknown> is the narrowest safe widening\n\n if (!Array.isArray(obj['servers'])) {\n throw new MCPConfigParseError(configPath, 'servers must be an array')\n }\n\n return (obj['servers'] as unknown[]).map((entry, i) => validateEntry(entry, i, configPath)) // as: Array.isArray confirmed on line above; unknown[] is the safe element type for map\n}\n\nasync function loadFromDynamicImport(resolvedPath: string, originalPath: string): Promise<Array<MCPHttpParams | MCPStdioParams>> {\n const fileUrl = pathToFileURL(resolvedPath).href\n // dynamic import errors (missing file, syntax error) propagate as-is\n const mod = await import(fileUrl)\n const defaultExport: unknown = mod.default\n\n if (typeof defaultExport !== 'object' || defaultExport === null) {\n throw new MCPConfigParseError(originalPath, 'default export must be a non-null object (missing or invalid default export)')\n }\n\n return validateServers(defaultExport, originalPath)\n}\n\nfunction loadFromJson(resolvedPath: string, originalPath: string): Array<MCPHttpParams | MCPStdioParams> {\n let raw: string\n try {\n raw = readFileSync(resolvedPath, 'utf-8')\n } catch (err) {\n throw new MCPConfigParseError(originalPath, (err as Error).message) // as: catch binds unknown; fs errors are always Error instances with .message\n }\n\n let data: unknown\n try {\n data = JSON.parse(raw)\n } catch (err) {\n throw new MCPConfigParseError(originalPath, (err as Error).message) // as: catch binds unknown; JSON.parse throws SyntaxError (an Error) with .message\n }\n\n return validateServers(data, originalPath)\n}\n\nexport async function loadMCPConfig(configPath: string): Promise<Array<MCPHttpParams | MCPStdioParams>> {\n const resolvedPath = resolve(configPath)\n const ext = extname(configPath)\n\n if (ext === '.json') {\n return loadFromJson(resolvedPath, configPath)\n }\n\n if (ext === '.ts' || ext === '.js' || ext === '.mjs') {\n return loadFromDynamicImport(resolvedPath, configPath)\n }\n\n throw new MCPConfigExtensionError(configPath, ext)\n}\n"],"mappings":"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","names":["resolve","result","resolve"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noetaris/harness-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "MCP server integration for @noetaris/harness",
5
5
  "type": "module",
6
6
  "repository": {
@@ -39,7 +39,7 @@
39
39
  ]
40
40
  },
41
41
  "devDependencies": {
42
- "@noetaris/harness-types": "^0.2.0",
42
+ "@noetaris/harness-types": "^0.3.0",
43
43
  "@types/node": "^25.9.1",
44
44
  "tsup": "^8.5.1",
45
45
  "typescript": "^6.0.3",