@hasna/mcps 0.0.12 → 0.0.14

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/bin/index.js CHANGED
@@ -11944,6 +11944,85 @@ var init_config2 = __esm(() => {
11944
11944
  DB_PATH = process.env.HASNA_MCPS_DB_PATH ?? process.env.MCPS_DB_PATH ?? join5(MCPS_DIR, "registry.db");
11945
11945
  });
11946
11946
 
11947
+ // src/lib/provider-profile-seeds.ts
11948
+ var DEFAULT_PROVIDER_PROFILE_SEEDS;
11949
+ var init_provider_profile_seeds = __esm(() => {
11950
+ DEFAULT_PROVIDER_PROFILE_SEEDS = [
11951
+ {
11952
+ id: "notion",
11953
+ displayName: "Notion",
11954
+ description: "Connect a Notion workspace so agents can search, read, create, and update workspace content.",
11955
+ endpoint: "https://mcp.notion.com/mcp",
11956
+ transport: "streamable-http",
11957
+ fallbackEndpoints: [
11958
+ {
11959
+ transport: "sse",
11960
+ url: "https://mcp.notion.com/sse",
11961
+ notes: "Fallback for clients that do not support Streamable HTTP."
11962
+ }
11963
+ ],
11964
+ authType: "oauth2",
11965
+ authMetadata: {
11966
+ oauthVersion: "2.0",
11967
+ pkce: true,
11968
+ dynamicClientRegistration: true,
11969
+ bearerToken: "none",
11970
+ notes: "Remote Notion MCP uses OAuth with PKCE. Bearer-token authentication is only appropriate for self-hosted/local fallback deployments."
11971
+ },
11972
+ tokenMode: "workspace",
11973
+ installFallback: {
11974
+ command: "npx",
11975
+ args: ["-y", "mcp-remote", "https://mcp.notion.com/sse", "--transport", "sse-only"],
11976
+ packageName: "mcp-remote",
11977
+ url: "https://mcp.notion.com/sse"
11978
+ },
11979
+ docsUrl: "https://developers.notion.com/guides/mcp/build-mcp-client",
11980
+ safety: {
11981
+ requiresApproval: true,
11982
+ dataClasses: ["workspace_content", "pages", "databases", "comments"],
11983
+ notes: "Connected agents operate with the authorizing user's workspace access. Human confirmation is recommended for write-capable workflows."
11984
+ },
11985
+ provenance: {
11986
+ source: "curated",
11987
+ sourceUrl: "https://developers.notion.com/guides/mcp/build-mcp-client",
11988
+ verifiedAt: "2026-05-10"
11989
+ }
11990
+ },
11991
+ {
11992
+ id: "linear",
11993
+ displayName: "Linear",
11994
+ description: "Connect Linear so agents can find, create, and update issues, projects, comments, and related workspace objects.",
11995
+ endpoint: "https://mcp.linear.app/mcp",
11996
+ transport: "streamable-http",
11997
+ authType: "oauth2",
11998
+ authMetadata: {
11999
+ oauthVersion: "2.1",
12000
+ dynamicClientRegistration: true,
12001
+ bearerToken: "optional",
12002
+ notes: "Linear supports interactive OAuth 2.1 with dynamic client registration and optional Authorization: Bearer tokens for OAuth tokens or API keys."
12003
+ },
12004
+ tokenMode: "workspace",
12005
+ installFallback: {
12006
+ command: "npx",
12007
+ args: ["-y", "mcp-remote", "https://mcp.linear.app/mcp"],
12008
+ packageName: "mcp-remote",
12009
+ url: "https://mcp.linear.app/mcp"
12010
+ },
12011
+ docsUrl: "https://linear.app/docs/mcp",
12012
+ safety: {
12013
+ requiresApproval: true,
12014
+ dataClasses: ["issues", "projects", "comments", "teams", "users"],
12015
+ notes: "Linear tools can create and update workspace objects, so write actions should be policy-gated by the platform."
12016
+ },
12017
+ provenance: {
12018
+ source: "curated",
12019
+ sourceUrl: "https://linear.app/docs/mcp",
12020
+ verifiedAt: "2026-05-10"
12021
+ }
12022
+ }
12023
+ ];
12024
+ });
12025
+
11947
12026
  // src/lib/db.ts
11948
12027
  import { mkdirSync as mkdirSync5 } from "fs";
11949
12028
  function getDb() {
@@ -12029,6 +12108,50 @@ function getDb() {
12029
12108
  ('github-mcp-topic', 'GitHub MCP Topic', 'github-topic', 'https://api.github.com/search/repositories', 'GitHub repositories tagged with mcp-server topic')
12030
12109
  `);
12031
12110
  }
12111
+ db.exec(`
12112
+ CREATE TABLE IF NOT EXISTS provider_profiles (
12113
+ id TEXT PRIMARY KEY,
12114
+ display_name TEXT NOT NULL,
12115
+ description TEXT,
12116
+ endpoint TEXT,
12117
+ transport TEXT NOT NULL,
12118
+ fallback_endpoints TEXT NOT NULL DEFAULT '[]',
12119
+ auth_type TEXT NOT NULL,
12120
+ auth_metadata TEXT NOT NULL DEFAULT '{}',
12121
+ scopes TEXT NOT NULL DEFAULT '[]',
12122
+ token_mode TEXT NOT NULL DEFAULT 'none',
12123
+ install_fallback TEXT NOT NULL DEFAULT '{}',
12124
+ docs_url TEXT,
12125
+ safety TEXT NOT NULL DEFAULT '{}',
12126
+ provenance TEXT NOT NULL DEFAULT '{"source":"manual"}',
12127
+ enabled INTEGER NOT NULL DEFAULT 1,
12128
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
12129
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
12130
+ )
12131
+ `);
12132
+ try {
12133
+ db.exec("ALTER TABLE provider_profiles ADD COLUMN fallback_endpoints TEXT NOT NULL DEFAULT '[]'");
12134
+ } catch {}
12135
+ try {
12136
+ db.exec("ALTER TABLE provider_profiles ADD COLUMN auth_metadata TEXT NOT NULL DEFAULT '{}'");
12137
+ } catch {}
12138
+ db.exec("CREATE INDEX IF NOT EXISTS idx_provider_profiles_enabled ON provider_profiles(enabled)");
12139
+ const providerProfileCount = db.query("SELECT COUNT(*) as c FROM provider_profiles").get().c;
12140
+ if (providerProfileCount === 0) {
12141
+ const insertProviderProfile = db.prepare(`
12142
+ INSERT OR IGNORE INTO provider_profiles (
12143
+ id, display_name, description, endpoint, transport, fallback_endpoints,
12144
+ auth_type, auth_metadata, scopes, token_mode, install_fallback,
12145
+ docs_url, safety, provenance, enabled
12146
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
12147
+ `);
12148
+ const run = db.transaction(() => {
12149
+ for (const profile of DEFAULT_PROVIDER_PROFILE_SEEDS) {
12150
+ insertProviderProfile.run(profile.id, profile.displayName, profile.description ?? null, profile.endpoint ?? null, profile.transport, JSON.stringify(profile.fallbackEndpoints ?? []), profile.authType, JSON.stringify(profile.authMetadata ?? {}), JSON.stringify(profile.scopes ?? []), profile.tokenMode ?? "none", JSON.stringify(profile.installFallback ?? null), profile.docsUrl ?? null, JSON.stringify(profile.safety ?? {}), JSON.stringify(profile.provenance), profile.enabled === false ? 0 : 1);
12151
+ }
12152
+ });
12153
+ run();
12154
+ }
12032
12155
  db.exec(`
12033
12156
  CREATE TABLE IF NOT EXISTS feedback (
12034
12157
  id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
@@ -12059,6 +12182,7 @@ var db = null, _adapter = null;
12059
12182
  var init_db = __esm(() => {
12060
12183
  init_dist();
12061
12184
  init_config2();
12185
+ init_provider_profile_seeds();
12062
12186
  });
12063
12187
 
12064
12188
  // node_modules/ajv/dist/compile/codegen/code.js
@@ -19288,15 +19412,23 @@ var init_sources = __esm(() => {
19288
19412
  var require_package = __commonJS((exports, module) => {
19289
19413
  module.exports = {
19290
19414
  name: "@hasna/mcps",
19291
- version: "0.0.12",
19415
+ version: "0.0.14",
19292
19416
  description: "Meta-MCP registry & CLI \u2014 discover, manage, and proxy MCP servers",
19293
19417
  type: "module",
19418
+ repository: {
19419
+ type: "git",
19420
+ url: "git+https://github.com/hasna/mcps.git"
19421
+ },
19294
19422
  main: "dist/index.js",
19295
19423
  types: "dist/index.d.ts",
19296
19424
  exports: {
19297
19425
  ".": {
19298
19426
  import: "./dist/index.js",
19299
19427
  types: "./dist/index.d.ts"
19428
+ },
19429
+ "./mcp": {
19430
+ import: "./dist/mcp/index.js",
19431
+ types: "./dist/mcp/index.d.ts"
19300
19432
  }
19301
19433
  },
19302
19434
  bin: {
@@ -19311,7 +19443,7 @@ var require_package = __commonJS((exports, module) => {
19311
19443
  "README.md"
19312
19444
  ],
19313
19445
  scripts: {
19314
- build: "bun run build:dashboard && bun build ./src/cli/index.tsx --outdir ./bin --target bun --external ink --external react --external chalk && bun build ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun run scripts/fix-shebangs.ts && bun build ./src/index.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
19446
+ build: "bun run build:dashboard && bun build ./src/cli/index.tsx --outdir ./bin --target bun --external ink --external react --external chalk && bun build ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun run scripts/fix-shebangs.ts && bun build ./src/index.ts --outdir ./dist --target bun && bun build ./src/mcp/index.ts --outdir ./dist/mcp --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
19315
19447
  "build:dashboard": "cd dashboard && bun install && bun run build",
19316
19448
  dev: "bun run src/cli/index.tsx",
19317
19449
  "dev:mcp": "bun run src/mcp/index.ts",
@@ -36759,9 +36891,169 @@ async function runFleetInstall(options = {}, dependencies = {}) {
36759
36891
  }));
36760
36892
  }
36761
36893
 
36894
+ // src/lib/provider-profiles.ts
36895
+ init_db();
36896
+ init_provider_profile_seeds();
36897
+ var TRANSPORTS = new Set(["stdio", "sse", "streamable-http"]);
36898
+ var AUTH_TYPES = new Set(["none", "oauth2", "api_key", "bearer_token", "custom"]);
36899
+ var TOKEN_MODES = new Set(["none", "user", "workspace", "service"]);
36900
+ var BEARER_TOKEN_MODES = new Set(["none", "optional", "required"]);
36901
+ var PROVENANCE_SOURCES = new Set(["curated", "official-registry", "npm", "github", "manual"]);
36902
+ function safeJsonParse2(value, fallback) {
36903
+ if (typeof value !== "string")
36904
+ return fallback;
36905
+ try {
36906
+ return JSON.parse(value);
36907
+ } catch {
36908
+ return fallback;
36909
+ }
36910
+ }
36911
+ function normalizeId(id) {
36912
+ const normalized = id.trim().toLowerCase();
36913
+ if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/.test(normalized)) {
36914
+ throw new Error("Provider profile id must be lowercase kebab-case");
36915
+ }
36916
+ return normalized;
36917
+ }
36918
+ function parseRow3(row) {
36919
+ const installFallback = safeJsonParse2(row.install_fallback, null);
36920
+ return {
36921
+ id: row.id,
36922
+ displayName: row.display_name,
36923
+ description: row.description || null,
36924
+ endpoint: row.endpoint || null,
36925
+ transport: row.transport,
36926
+ fallbackEndpoints: safeJsonParse2(row.fallback_endpoints, []),
36927
+ authType: row.auth_type,
36928
+ authMetadata: safeJsonParse2(row.auth_metadata, {}),
36929
+ scopes: safeJsonParse2(row.scopes, []),
36930
+ tokenMode: row.token_mode,
36931
+ installFallback,
36932
+ docsUrl: row.docs_url || null,
36933
+ safety: safeJsonParse2(row.safety, {}),
36934
+ provenance: safeJsonParse2(row.provenance, { source: "manual" }),
36935
+ enabled: row.enabled === 1 || row.enabled === true,
36936
+ created_at: row.created_at,
36937
+ updated_at: row.updated_at
36938
+ };
36939
+ }
36940
+ function listProviderProfiles(options = {}) {
36941
+ const db2 = getDb();
36942
+ const sql = options.enabledOnly ? "SELECT * FROM provider_profiles WHERE enabled = 1 ORDER BY display_name" : "SELECT * FROM provider_profiles ORDER BY display_name";
36943
+ return db2.prepare(sql).all().map(parseRow3);
36944
+ }
36945
+ function searchProviderProfiles(query, options = {}) {
36946
+ const normalizedQuery = query.trim().toLowerCase();
36947
+ if (!normalizedQuery)
36948
+ return listProviderProfiles(options);
36949
+ return listProviderProfiles(options).filter((profile) => {
36950
+ const searchable = [
36951
+ profile.id,
36952
+ profile.displayName,
36953
+ profile.description ?? "",
36954
+ profile.endpoint ?? "",
36955
+ profile.docsUrl ?? "",
36956
+ profile.provenance.sourceUrl ?? "",
36957
+ profile.provenance.packageName ?? ""
36958
+ ].join(`
36959
+ `).toLowerCase();
36960
+ return searchable.includes(normalizedQuery);
36961
+ });
36962
+ }
36963
+ function getProviderProfile(id) {
36964
+ const db2 = getDb();
36965
+ const row = db2.prepare("SELECT * FROM provider_profiles WHERE id = ?").get(normalizeId(id));
36966
+ return row ? parseRow3(row) : null;
36967
+ }
36968
+ function installProviderProfile(id, options = {}) {
36969
+ const profile = getProviderProfile(id);
36970
+ if (!profile)
36971
+ throw new Error(`Provider profile "${id}" not found`);
36972
+ if (!profile.enabled)
36973
+ throw new Error(`Provider profile "${id}" is disabled`);
36974
+ const fallback = profile.installFallback;
36975
+ const useFallback = options.useFallback || !profile.endpoint;
36976
+ const command = useFallback ? fallback?.command : fallback?.command ?? "npx";
36977
+ const args = useFallback ? fallback?.args ?? [] : fallback?.args ?? [];
36978
+ if (!command) {
36979
+ throw new Error(`Provider profile "${id}" does not define an install fallback command`);
36980
+ }
36981
+ return addServer({
36982
+ name: options.name ?? profile.displayName,
36983
+ description: profile.description ?? undefined,
36984
+ command,
36985
+ args,
36986
+ transport: useFallback ? "stdio" : profile.transport,
36987
+ url: useFallback ? fallback?.url : profile.endpoint ?? undefined,
36988
+ source: "provider-profile"
36989
+ });
36990
+ }
36991
+
36762
36992
  // src/cli/index.tsx
36763
36993
  import * as readline from "readline";
36764
36994
 
36995
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
36996
+ import process3 from "process";
36997
+ class StdioServerTransport {
36998
+ constructor(_stdin = process3.stdin, _stdout = process3.stdout) {
36999
+ this._stdin = _stdin;
37000
+ this._stdout = _stdout;
37001
+ this._readBuffer = new ReadBuffer;
37002
+ this._started = false;
37003
+ this._ondata = (chunk) => {
37004
+ this._readBuffer.append(chunk);
37005
+ this.processReadBuffer();
37006
+ };
37007
+ this._onerror = (error2) => {
37008
+ this.onerror?.(error2);
37009
+ };
37010
+ }
37011
+ async start() {
37012
+ if (this._started) {
37013
+ throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
37014
+ }
37015
+ this._started = true;
37016
+ this._stdin.on("data", this._ondata);
37017
+ this._stdin.on("error", this._onerror);
37018
+ }
37019
+ processReadBuffer() {
37020
+ while (true) {
37021
+ try {
37022
+ const message = this._readBuffer.readMessage();
37023
+ if (message === null) {
37024
+ break;
37025
+ }
37026
+ this.onmessage?.(message);
37027
+ } catch (error2) {
37028
+ this.onerror?.(error2);
37029
+ }
37030
+ }
37031
+ }
37032
+ async close() {
37033
+ this._stdin.off("data", this._ondata);
37034
+ this._stdin.off("error", this._onerror);
37035
+ const remainingDataListeners = this._stdin.listenerCount("data");
37036
+ if (remainingDataListeners === 0) {
37037
+ this._stdin.pause();
37038
+ }
37039
+ this._readBuffer.clear();
37040
+ this.onclose?.();
37041
+ }
37042
+ send(message) {
37043
+ return new Promise((resolve) => {
37044
+ const json = serializeMessage(message);
37045
+ if (this._stdout.write(json)) {
37046
+ resolve();
37047
+ } else {
37048
+ this._stdout.once("drain", resolve);
37049
+ }
37050
+ });
37051
+ }
37052
+ }
37053
+
37054
+ // src/mcp/server.ts
37055
+ init_dist();
37056
+
36765
37057
  // node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
36766
37058
  class ExperimentalServerTasks {
36767
37059
  constructor(_server) {
@@ -37923,71 +38215,6 @@ var EMPTY_COMPLETION_RESULT = {
37923
38215
  }
37924
38216
  };
37925
38217
 
37926
- // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
37927
- import process3 from "process";
37928
- class StdioServerTransport {
37929
- constructor(_stdin = process3.stdin, _stdout = process3.stdout) {
37930
- this._stdin = _stdin;
37931
- this._stdout = _stdout;
37932
- this._readBuffer = new ReadBuffer;
37933
- this._started = false;
37934
- this._ondata = (chunk) => {
37935
- this._readBuffer.append(chunk);
37936
- this.processReadBuffer();
37937
- };
37938
- this._onerror = (error2) => {
37939
- this.onerror?.(error2);
37940
- };
37941
- }
37942
- async start() {
37943
- if (this._started) {
37944
- throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
37945
- }
37946
- this._started = true;
37947
- this._stdin.on("data", this._ondata);
37948
- this._stdin.on("error", this._onerror);
37949
- }
37950
- processReadBuffer() {
37951
- while (true) {
37952
- try {
37953
- const message = this._readBuffer.readMessage();
37954
- if (message === null) {
37955
- break;
37956
- }
37957
- this.onmessage?.(message);
37958
- } catch (error2) {
37959
- this.onerror?.(error2);
37960
- }
37961
- }
37962
- }
37963
- async close() {
37964
- this._stdin.off("data", this._ondata);
37965
- this._stdin.off("error", this._onerror);
37966
- const remainingDataListeners = this._stdin.listenerCount("data");
37967
- if (remainingDataListeners === 0) {
37968
- this._stdin.pause();
37969
- }
37970
- this._readBuffer.clear();
37971
- this.onclose?.();
37972
- }
37973
- send(message) {
37974
- return new Promise((resolve) => {
37975
- const json = serializeMessage(message);
37976
- if (this._stdout.write(json)) {
37977
- resolve();
37978
- } else {
37979
- this._stdout.once("drain", resolve);
37980
- }
37981
- });
37982
- }
37983
- }
37984
-
37985
- // src/mcp/index.ts
37986
- init_dist();
37987
- init_sources();
37988
- init_config2();
37989
- init_db();
37990
-
37991
38218
  // src/lib/version.ts
37992
38219
  import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
37993
38220
  import { dirname as dirname3, join as join10 } from "path";
@@ -38012,463 +38239,659 @@ function readPackageVersion(moduleUrl, fallback = FALLBACK_VERSION) {
38012
38239
  return fallback;
38013
38240
  }
38014
38241
 
38015
- // src/mcp/index.ts
38242
+ // src/mcp/tools.ts
38243
+ init_sources();
38244
+ init_config2();
38245
+ init_db();
38246
+ var VERSION = readPackageVersion(import.meta.url);
38247
+ var mcpsAgents = new Map;
38016
38248
  function redactServerEnv(server) {
38017
38249
  return { ...server, env: {} };
38018
38250
  }
38019
- var VERSION = (() => {
38020
- return readPackageVersion(import.meta.url);
38021
- })();
38022
- var server = new McpServer({
38023
- name: "mcps",
38024
- version: VERSION
38025
- });
38026
- var _mcpsAgents = new Map;
38027
- server.tool("list_servers", "List all registered MCP servers", {}, async () => {
38028
- const servers = listServers();
38029
- return {
38030
- content: [{ type: "text", text: JSON.stringify(servers.map(redactServerEnv), null, 2) }]
38031
- };
38032
- });
38033
- server.tool("search_registry", "Search the official MCP registry for servers", { query: exports_external2.string().describe("Search query") }, async ({ query }) => {
38034
- const results = await searchRegistry(query);
38035
- return {
38036
- content: [{ type: "text", text: JSON.stringify(results, null, 2) }]
38037
- };
38038
- });
38039
- server.tool("add_server", "Register a new MCP server", {
38040
- command: exports_external2.string().describe("Command to run the server (e.g., npx, bunx, node)"),
38041
- args: exports_external2.array(exports_external2.string()).optional().describe("Arguments for the command"),
38042
- name: exports_external2.string().optional().describe("Display name"),
38043
- description: exports_external2.string().optional().describe("Description"),
38044
- transport: exports_external2.enum(["stdio", "sse", "streamable-http"]).optional().describe("Transport type"),
38045
- url: exports_external2.string().optional().describe("URL for remote transports"),
38046
- env: exports_external2.record(exports_external2.string()).optional().describe("Environment variables")
38047
- }, async ({ command, args, name, description, transport, url: url2, env }) => {
38048
- const entry = addServer({
38049
- command,
38050
- args: args || [],
38051
- name,
38052
- description,
38053
- transport,
38054
- url: url2,
38055
- env: env || {}
38056
- });
38057
- return {
38058
- content: [{ type: "text", text: JSON.stringify(entry, null, 2) }]
38059
- };
38060
- });
38061
- server.tool("install_from_registry", "Install an MCP server from the official registry", { id: exports_external2.string().describe("Registry server ID") }, async ({ id }) => {
38062
- const entry = await installFromRegistry(id);
38063
- return {
38064
- content: [{ type: "text", text: JSON.stringify(entry, null, 2) }]
38065
- };
38066
- });
38067
- server.tool("remove_server", "Remove a registered MCP server", { id: exports_external2.string().describe("Server ID to remove") }, async ({ id }) => {
38068
- const existing = getServer(id);
38069
- if (!existing) {
38070
- return {
38071
- content: [{ type: "text", text: `Server "${id}" not found.` }],
38072
- isError: true
38073
- };
38074
- }
38075
- removeServer(id);
38076
- return {
38077
- content: [{ type: "text", text: `Removed server: ${existing.name} [${id}]` }]
38078
- };
38079
- });
38080
- server.tool("enable_server", "Enable a registered MCP server", { id: exports_external2.string().describe("Server ID to enable") }, async ({ id }) => {
38081
- const existing = getServer(id);
38082
- if (!existing) {
38083
- return {
38084
- content: [{ type: "text", text: `Server "${id}" not found.` }],
38085
- isError: true
38086
- };
38087
- }
38088
- const entry = enableServer(id);
38089
- return {
38090
- content: [{ type: "text", text: JSON.stringify(entry, null, 2) }]
38091
- };
38092
- });
38093
- server.tool("disable_server", "Disable a registered MCP server", { id: exports_external2.string().describe("Server ID to disable") }, async ({ id }) => {
38094
- const existing = getServer(id);
38095
- if (!existing) {
38096
- return {
38097
- content: [{ type: "text", text: `Server "${id}" not found.` }],
38098
- isError: true
38099
- };
38100
- }
38101
- const entry = disableServer(id);
38102
- return {
38103
- content: [{ type: "text", text: JSON.stringify(entry, null, 2) }]
38104
- };
38105
- });
38106
- server.tool("update_server", "Update fields of a registered MCP server", {
38107
- id: exports_external2.string().describe("Server ID to update"),
38108
- name: exports_external2.string().optional().describe("New display name"),
38109
- description: exports_external2.string().optional().describe("New description"),
38110
- command: exports_external2.string().optional().describe("New command"),
38111
- args: exports_external2.array(exports_external2.string()).optional().describe("New args list"),
38112
- transport: exports_external2.enum(["stdio", "sse", "streamable-http"]).optional().describe("New transport type"),
38113
- url: exports_external2.string().optional().describe("New URL for remote transports")
38114
- }, async ({ id, name, description, command, args, transport, url: url2 }) => {
38115
- const existing = getServer(id);
38116
- if (!existing) {
38117
- return {
38118
- content: [{ type: "text", text: `Server "${id}" not found.` }],
38119
- isError: true
38120
- };
38121
- }
38122
- const fields = {};
38123
- if (name !== undefined)
38124
- fields.name = name;
38125
- if (description !== undefined)
38126
- fields.description = description;
38127
- if (command !== undefined)
38128
- fields.command = command;
38129
- if (args !== undefined)
38130
- fields.args = args;
38131
- if (transport !== undefined)
38132
- fields.transport = transport;
38133
- if (url2 !== undefined)
38134
- fields.url = url2;
38135
- const updated = updateServer(id, fields);
38136
- return {
38137
- content: [{ type: "text", text: JSON.stringify(redactServerEnv(updated), null, 2) }]
38138
- };
38139
- });
38140
- server.tool("list_tools", "List all cached tools across registered servers without connecting. Optionally filter by server_id.", { server_id: exports_external2.string().optional().describe("Server ID to filter by (optional)") }, async ({ server_id }) => {
38141
- if (server_id) {
38142
- const tools = getCachedTools(server_id);
38143
- return {
38144
- content: [{ type: "text", text: JSON.stringify(tools.map((t) => ({ ...t, server_id })), null, 2) }]
38145
- };
38146
- }
38147
- const servers = listServers();
38148
- const allTools = [];
38149
- for (const s of servers) {
38150
- const tools = getCachedTools(s.id);
38151
- for (const t of tools) {
38152
- allTools.push({ server_id: s.id, ...t });
38153
- }
38154
- }
38155
- return {
38156
- content: [{ type: "text", text: JSON.stringify(allTools, null, 2) }]
38157
- };
38158
- });
38159
- server.tool("get_server_info", "Get detailed information about a registered MCP server", { id: exports_external2.string().describe("Server ID") }, async ({ id }) => {
38160
- const entry = getServer(id);
38161
- if (!entry) {
38162
- return {
38163
- content: [{ type: "text", text: `Server "${id}" not found.` }],
38164
- isError: true
38165
- };
38251
+ function textContent(text) {
38252
+ return { content: [{ type: "text", text }] };
38253
+ }
38254
+ function jsonContent(value) {
38255
+ return textContent(JSON.stringify(value, null, 2));
38256
+ }
38257
+ function errorContent(text) {
38258
+ return { ...textContent(text), isError: true };
38259
+ }
38260
+ function buildMcpTools() {
38261
+ const definitions = [
38262
+ {
38263
+ name: "list_servers",
38264
+ description: "List all registered MCP servers",
38265
+ paramsSchema: {},
38266
+ run: () => jsonContent(listServers().map(redactServerEnv))
38267
+ },
38268
+ {
38269
+ name: "search_registry",
38270
+ description: "Search the official MCP registry for servers",
38271
+ paramsSchema: { query: exports_external2.string().describe("Search query") },
38272
+ run: async ({ query }) => jsonContent(await searchRegistry(String(query)))
38273
+ },
38274
+ {
38275
+ name: "add_server",
38276
+ description: "Register a new MCP server",
38277
+ paramsSchema: {
38278
+ command: exports_external2.string().describe("Command to run the server (e.g., npx, bunx, node)"),
38279
+ args: exports_external2.array(exports_external2.string()).optional().describe("Arguments for the command"),
38280
+ name: exports_external2.string().optional().describe("Display name"),
38281
+ description: exports_external2.string().optional().describe("Description"),
38282
+ transport: exports_external2.enum(["stdio", "sse", "streamable-http"]).optional().describe("Transport type"),
38283
+ url: exports_external2.string().optional().describe("URL for remote transports"),
38284
+ env: exports_external2.record(exports_external2.string()).optional().describe("Environment variables")
38285
+ },
38286
+ run: ({ command, args, name, description, transport, url: url2, env }) => jsonContent(addServer({
38287
+ command: String(command),
38288
+ args: Array.isArray(args) ? args.map(String) : [],
38289
+ name: typeof name === "string" ? name : undefined,
38290
+ description: typeof description === "string" ? description : undefined,
38291
+ transport,
38292
+ url: typeof url2 === "string" ? url2 : undefined,
38293
+ env: isRecordOfStrings(env) ? env : {}
38294
+ }))
38295
+ },
38296
+ {
38297
+ name: "install_from_registry",
38298
+ description: "Install an MCP server from the official registry",
38299
+ paramsSchema: { id: exports_external2.string().describe("Registry server ID") },
38300
+ run: async ({ id }) => jsonContent(await installFromRegistry(String(id)))
38301
+ },
38302
+ {
38303
+ name: "remove_server",
38304
+ description: "Remove a registered MCP server",
38305
+ paramsSchema: { id: exports_external2.string().describe("Server ID to remove") },
38306
+ run: ({ id }) => {
38307
+ const existing = getServer(String(id));
38308
+ if (!existing)
38309
+ return errorContent(`Server "${String(id)}" not found.`);
38310
+ removeServer(String(id));
38311
+ return textContent(`Removed server: ${existing.name} [${String(id)}]`);
38312
+ }
38313
+ },
38314
+ {
38315
+ name: "enable_server",
38316
+ description: "Enable a registered MCP server",
38317
+ paramsSchema: { id: exports_external2.string().describe("Server ID to enable") },
38318
+ run: ({ id }) => {
38319
+ const existing = getServer(String(id));
38320
+ if (!existing)
38321
+ return errorContent(`Server "${String(id)}" not found.`);
38322
+ return jsonContent(enableServer(String(id)));
38323
+ }
38324
+ },
38325
+ {
38326
+ name: "disable_server",
38327
+ description: "Disable a registered MCP server",
38328
+ paramsSchema: { id: exports_external2.string().describe("Server ID to disable") },
38329
+ run: ({ id }) => {
38330
+ const existing = getServer(String(id));
38331
+ if (!existing)
38332
+ return errorContent(`Server "${String(id)}" not found.`);
38333
+ return jsonContent(disableServer(String(id)));
38334
+ }
38335
+ },
38336
+ {
38337
+ name: "update_server",
38338
+ description: "Update fields of a registered MCP server",
38339
+ paramsSchema: {
38340
+ id: exports_external2.string().describe("Server ID to update"),
38341
+ name: exports_external2.string().optional().describe("New display name"),
38342
+ description: exports_external2.string().optional().describe("New description"),
38343
+ command: exports_external2.string().optional().describe("New command"),
38344
+ args: exports_external2.array(exports_external2.string()).optional().describe("New args list"),
38345
+ transport: exports_external2.enum(["stdio", "sse", "streamable-http"]).optional().describe("New transport type"),
38346
+ url: exports_external2.string().optional().describe("New URL for remote transports")
38347
+ },
38348
+ run: ({ id, name, description, command, args, transport, url: url2 }) => {
38349
+ const serverId = String(id);
38350
+ const existing = getServer(serverId);
38351
+ if (!existing)
38352
+ return errorContent(`Server "${serverId}" not found.`);
38353
+ const fields = {};
38354
+ if (typeof name === "string")
38355
+ fields.name = name;
38356
+ if (typeof description === "string")
38357
+ fields.description = description;
38358
+ if (typeof command === "string")
38359
+ fields.command = command;
38360
+ if (Array.isArray(args))
38361
+ fields.args = args.map(String);
38362
+ if (transport === "stdio" || transport === "sse" || transport === "streamable-http")
38363
+ fields.transport = transport;
38364
+ if (typeof url2 === "string")
38365
+ fields.url = url2;
38366
+ return jsonContent(redactServerEnv(updateServer(serverId, fields)));
38367
+ }
38368
+ },
38369
+ {
38370
+ name: "list_tools",
38371
+ description: "List all cached tools across registered servers without connecting. Optionally filter by server_id.",
38372
+ paramsSchema: { server_id: exports_external2.string().optional().describe("Server ID to filter by (optional)") },
38373
+ run: ({ server_id }) => {
38374
+ if (typeof server_id === "string" && server_id) {
38375
+ const toolsForServer = getCachedTools(server_id);
38376
+ return jsonContent(toolsForServer.map((tool) => ({ ...tool, server_id })));
38377
+ }
38378
+ const allTools = [];
38379
+ for (const server of listServers()) {
38380
+ for (const tool of getCachedTools(server.id)) {
38381
+ allTools.push({ server_id: server.id, ...tool });
38382
+ }
38383
+ }
38384
+ return jsonContent(allTools);
38385
+ }
38386
+ },
38387
+ {
38388
+ name: "get_server_info",
38389
+ description: "Get detailed information about a registered MCP server",
38390
+ paramsSchema: { id: exports_external2.string().describe("Server ID") },
38391
+ run: ({ id }) => {
38392
+ const entry = getServer(String(id));
38393
+ if (!entry)
38394
+ return errorContent(`Server "${String(id)}" not found.`);
38395
+ return jsonContent(redactServerEnv(entry));
38396
+ }
38397
+ },
38398
+ {
38399
+ name: "find_mcp_servers",
38400
+ description: "Search for MCP servers across configured sources (official registry, npm, GitHub topics, awesome lists). Use list_sources to see available source IDs.",
38401
+ paramsSchema: {
38402
+ query: exports_external2.string().describe("Search query (e.g., 'filesystem', 'postgres', 'browser')"),
38403
+ sources: exports_external2.array(exports_external2.string()).optional().describe("Source IDs to search (default: all enabled). Use list_sources to get IDs."),
38404
+ limit: exports_external2.number().optional().describe("Max results per source (default: 20)")
38405
+ },
38406
+ run: async ({ query, sources, limit }) => jsonContent(await findServers(String(query), {
38407
+ sources: Array.isArray(sources) ? sources.map(String) : undefined,
38408
+ limit: typeof limit === "number" ? limit : undefined
38409
+ }))
38410
+ },
38411
+ {
38412
+ name: "list_provider_profiles",
38413
+ description: "List curated provider profiles for hosted/common MCP integrations such as Notion and Linear.",
38414
+ paramsSchema: {
38415
+ enabled_only: exports_external2.boolean().optional().describe("Only include enabled provider profiles")
38416
+ },
38417
+ run: ({ enabled_only }) => jsonContent(listProviderProfiles({ enabledOnly: enabled_only === true }))
38418
+ },
38419
+ {
38420
+ name: "search_provider_profiles",
38421
+ description: "Search curated provider profiles separately from raw MCP registry/source search.",
38422
+ paramsSchema: {
38423
+ query: exports_external2.string().describe("Search query such as 'notion', 'linear', or an endpoint URL"),
38424
+ enabled_only: exports_external2.boolean().optional().describe("Only include enabled provider profiles")
38425
+ },
38426
+ run: ({ query, enabled_only }) => jsonContent(searchProviderProfiles(String(query), { enabledOnly: enabled_only === true }))
38427
+ },
38428
+ {
38429
+ name: "get_provider_profile",
38430
+ description: "Get one curated provider profile by ID.",
38431
+ paramsSchema: {
38432
+ id: exports_external2.string().describe("Provider profile ID")
38433
+ },
38434
+ run: ({ id }) => {
38435
+ const profile = getProviderProfile(String(id));
38436
+ if (!profile)
38437
+ return errorContent(`Provider profile "${String(id)}" not found.`);
38438
+ return jsonContent(profile);
38439
+ }
38440
+ },
38441
+ {
38442
+ name: "install_provider_profile",
38443
+ description: "Register a curated provider profile as an MCP server.",
38444
+ paramsSchema: {
38445
+ id: exports_external2.string().describe("Provider profile ID"),
38446
+ name: exports_external2.string().optional().describe("Override registered server name"),
38447
+ use_fallback: exports_external2.boolean().optional().describe("Install the stdio fallback command instead of the direct remote transport")
38448
+ },
38449
+ run: ({ id, name, use_fallback }) => {
38450
+ try {
38451
+ return jsonContent(redactServerEnv(installProviderProfile(String(id), {
38452
+ name: typeof name === "string" ? name : undefined,
38453
+ useFallback: use_fallback === true
38454
+ })));
38455
+ } catch (err) {
38456
+ return errorContent(err.message);
38457
+ }
38458
+ }
38459
+ },
38460
+ {
38461
+ name: "list_sources",
38462
+ description: "List all configured search sources for finding MCP servers",
38463
+ paramsSchema: {},
38464
+ run: () => jsonContent(listSources())
38465
+ },
38466
+ {
38467
+ name: "add_source",
38468
+ description: "Add a new search source for finding MCP servers",
38469
+ paramsSchema: {
38470
+ name: exports_external2.string().describe("Source name"),
38471
+ type: exports_external2.enum(["mcp-registry", "awesome-list", "npm-search", "github-topic"]).describe("Source type"),
38472
+ url: exports_external2.string().describe("Source URL endpoint"),
38473
+ description: exports_external2.string().optional().describe("Description")
38474
+ },
38475
+ run: ({ name, type, url: url2, description }) => jsonContent(addSource({
38476
+ name: String(name),
38477
+ type,
38478
+ url: String(url2),
38479
+ description: typeof description === "string" ? description : undefined
38480
+ }))
38481
+ },
38482
+ {
38483
+ name: "remove_source",
38484
+ description: "Remove a search source by ID",
38485
+ paramsSchema: { id: exports_external2.string().describe("Source ID to remove") },
38486
+ run: ({ id }) => {
38487
+ const sourceId = String(id);
38488
+ const existing = getSource(sourceId);
38489
+ if (!existing)
38490
+ return errorContent(`Source "${sourceId}" not found.`);
38491
+ removeSource(sourceId);
38492
+ return textContent(`Removed source: ${existing.name} [${sourceId}]`);
38493
+ }
38494
+ },
38495
+ {
38496
+ name: "enable_source_finder",
38497
+ description: "Enable a search source",
38498
+ paramsSchema: { id: exports_external2.string().describe("Source ID to enable") },
38499
+ run: ({ id }) => {
38500
+ const sourceId = String(id);
38501
+ const existing = getSource(sourceId);
38502
+ if (!existing)
38503
+ return errorContent(`Source "${sourceId}" not found.`);
38504
+ enableSource(sourceId);
38505
+ return textContent(`Enabled source: ${existing.name}`);
38506
+ }
38507
+ },
38508
+ {
38509
+ name: "disable_source_finder",
38510
+ description: "Disable a search source",
38511
+ paramsSchema: { id: exports_external2.string().describe("Source ID to disable") },
38512
+ run: ({ id }) => {
38513
+ const sourceId = String(id);
38514
+ const existing = getSource(sourceId);
38515
+ if (!existing)
38516
+ return errorContent(`Source "${sourceId}" not found.`);
38517
+ disableSource(sourceId);
38518
+ return textContent(`Disabled source: ${existing.name}`);
38519
+ }
38520
+ },
38521
+ {
38522
+ name: "install_to_agents",
38523
+ description: "Install a registered MCP server into Claude Code, Codex, and/or Gemini",
38524
+ paramsSchema: {
38525
+ id: exports_external2.string().describe("Server ID to install (from list_servers)"),
38526
+ targets: exports_external2.array(exports_external2.enum(["claude", "codex", "gemini"])).optional().describe("Target agents to install into (default: all)")
38527
+ },
38528
+ run: ({ id, targets }) => {
38529
+ const serverId = String(id);
38530
+ const entry = getServer(serverId);
38531
+ if (!entry)
38532
+ return errorContent(`Server "${serverId}" not found.`);
38533
+ const agentTargets = Array.isArray(targets) ? targets : undefined;
38534
+ return jsonContent(installToAgents(entry, agentTargets ?? ["claude", "codex", "gemini"]));
38535
+ }
38536
+ },
38537
+ {
38538
+ name: "list_awesome_servers",
38539
+ description: "List all MCP servers from the curated punkpeye/awesome-mcp-servers GitHub list",
38540
+ paramsSchema: {},
38541
+ run: async () => jsonContent(await listAwesomeServers())
38542
+ },
38543
+ {
38544
+ name: "connect_and_list_tools",
38545
+ description: "Connect to all enabled MCP servers and list their available tools",
38546
+ paramsSchema: {},
38547
+ run: async () => {
38548
+ let liveTools = [];
38549
+ try {
38550
+ await connectAllEnabled();
38551
+ liveTools = listAllTools();
38552
+ } finally {
38553
+ await disconnectAll().catch(() => {
38554
+ return;
38555
+ });
38556
+ }
38557
+ return jsonContent(liveTools);
38558
+ }
38559
+ },
38560
+ {
38561
+ name: "call_upstream_tool",
38562
+ description: `Call a tool on a connected upstream MCP server. Tool name format: server_id${TOOL_PREFIX_SEPARATOR}tool_name`,
38563
+ paramsSchema: {
38564
+ tool_name: exports_external2.string().describe(`Prefixed tool name (server_id${TOOL_PREFIX_SEPARATOR}tool_name)`),
38565
+ arguments: exports_external2.record(exports_external2.unknown()).optional().describe("Tool arguments as key-value pairs")
38566
+ },
38567
+ run: async ({ tool_name, arguments: args }) => {
38568
+ try {
38569
+ const toolName = String(tool_name);
38570
+ const sepIdx = toolName.indexOf(TOOL_PREFIX_SEPARATOR);
38571
+ if (sepIdx === -1)
38572
+ return errorContent(`Error: Invalid tool name "${toolName}"`);
38573
+ const serverId = toolName.slice(0, sepIdx);
38574
+ const entry = getServer(serverId);
38575
+ if (!entry)
38576
+ return errorContent(`Error: Server "${serverId}" not found.`);
38577
+ if (!entry.enabled)
38578
+ return errorContent(`Error: Server "${serverId}" is disabled.`);
38579
+ await connectToServer(entry);
38580
+ const result = await callTool(toolName, readRecord(args));
38581
+ return { content: result.content };
38582
+ } catch (error2) {
38583
+ return errorContent(`Error: ${error2.message}`);
38584
+ }
38585
+ }
38586
+ },
38587
+ {
38588
+ name: "diagnose_server",
38589
+ description: "Run health checks on a registered MCP server",
38590
+ paramsSchema: { id: exports_external2.string().describe("Server ID") },
38591
+ run: async ({ id }) => {
38592
+ const serverId = String(id);
38593
+ const entry = getServer(serverId);
38594
+ if (!entry)
38595
+ return errorContent(`Server "${serverId}" not found.`);
38596
+ return jsonContent(await diagnoseServer(entry));
38597
+ }
38598
+ },
38599
+ {
38600
+ name: "list_machines",
38601
+ description: "List registered fleet machines",
38602
+ paramsSchema: {
38603
+ enabled_only: exports_external2.boolean().optional().describe("When true, only return enabled machines")
38604
+ },
38605
+ run: ({ enabled_only }) => jsonContent(listMachines().filter((machine) => enabled_only === true ? machine.enabled : true))
38606
+ },
38607
+ {
38608
+ name: "add_machine",
38609
+ description: "Register a machine for fleet health checks and installs",
38610
+ paramsSchema: {
38611
+ host: exports_external2.string().describe("Hostname or SSH target"),
38612
+ id: exports_external2.string().optional().describe("Stable machine ID"),
38613
+ name: exports_external2.string().optional().describe("Display name"),
38614
+ username: exports_external2.string().optional().describe("SSH username"),
38615
+ port: exports_external2.number().int().min(1).max(65535).optional().describe("SSH port"),
38616
+ platform: exports_external2.enum(["linux", "darwin", "unknown"]).optional().describe("Machine platform"),
38617
+ arch: exports_external2.enum(["arm64", "x64", "unknown"]).optional().describe("Machine architecture"),
38618
+ bun_path: exports_external2.string().optional().describe("Explicit path to bun on the remote machine"),
38619
+ npm_path: exports_external2.string().optional().describe("Explicit path to npm on the remote machine"),
38620
+ installer: exports_external2.enum(["auto", "bun", "npm"]).optional().describe("Preferred installer"),
38621
+ ssh_key_path: exports_external2.string().optional().describe("SSH private key path"),
38622
+ enabled: exports_external2.boolean().optional().describe("Whether the machine should be enabled")
38623
+ },
38624
+ run: ({ host, id, name, username, port, platform: platform2, arch, bun_path, npm_path, installer, ssh_key_path, enabled }) => jsonContent(addMachine({
38625
+ host: String(host),
38626
+ id: typeof id === "string" ? id : undefined,
38627
+ name: typeof name === "string" ? name : undefined,
38628
+ username: typeof username === "string" ? username : undefined,
38629
+ port: typeof port === "number" ? port : undefined,
38630
+ platform: platform2,
38631
+ arch,
38632
+ bun_path: typeof bun_path === "string" ? bun_path : undefined,
38633
+ npm_path: typeof npm_path === "string" ? npm_path : undefined,
38634
+ installer,
38635
+ ssh_key_path: typeof ssh_key_path === "string" ? ssh_key_path : undefined,
38636
+ enabled: typeof enabled === "boolean" ? enabled : undefined
38637
+ }))
38638
+ },
38639
+ {
38640
+ name: "remove_machine",
38641
+ description: "Remove a registered machine",
38642
+ paramsSchema: { id: exports_external2.string().describe("Machine ID to remove") },
38643
+ run: ({ id }) => {
38644
+ const machineId = String(id);
38645
+ const machine = getMachine(machineId);
38646
+ if (!machine)
38647
+ return errorContent(`Machine "${machineId}" not found.`);
38648
+ removeMachine(machineId);
38649
+ return jsonContent({ removed: true, machine });
38650
+ }
38651
+ },
38652
+ {
38653
+ name: "seed_default_machines",
38654
+ description: "Seed the standard spark/apple machine inventory",
38655
+ paramsSchema: {},
38656
+ run: () => jsonContent(seedDefaultMachines())
38657
+ },
38658
+ {
38659
+ name: "list_hasna_mcp_catalog",
38660
+ description: "List the discovered @hasna MCP package catalog",
38661
+ paramsSchema: {
38662
+ packages: exports_external2.array(exports_external2.string()).optional().describe("Optional package-name filter"),
38663
+ refresh: exports_external2.boolean().optional().describe("Refresh npm metadata instead of using cache")
38664
+ },
38665
+ run: async ({ packages, refresh }) => {
38666
+ const catalog = await listHasnaMcpCatalog({ refresh: refresh === true });
38667
+ const filtered = Array.isArray(packages) && packages.length > 0 ? catalog.filter((entry) => packages.map(String).includes(entry.name)) : catalog;
38668
+ return jsonContent(filtered);
38669
+ }
38670
+ },
38671
+ {
38672
+ name: "fleet_health",
38673
+ description: "Run fleet-wide MCP health checks across registered machines",
38674
+ paramsSchema: {
38675
+ machine_ids: exports_external2.array(exports_external2.string()).optional().describe("Optional machine IDs to check"),
38676
+ packages: exports_external2.array(exports_external2.string()).optional().describe("Optional @hasna package-name filter"),
38677
+ refresh_catalog: exports_external2.boolean().optional().describe("Refresh npm metadata before checking"),
38678
+ timeout_ms: exports_external2.number().int().min(1000).optional().describe("Remote timeout in milliseconds")
38679
+ },
38680
+ run: async ({ machine_ids, packages, refresh_catalog, timeout_ms }) => jsonContent(await runFleetHealthCheck({
38681
+ machineIds: Array.isArray(machine_ids) ? machine_ids.map(String) : undefined,
38682
+ packages: Array.isArray(packages) ? packages.map(String) : undefined,
38683
+ refreshCatalog: refresh_catalog === true,
38684
+ timeoutMs: typeof timeout_ms === "number" ? timeout_ms : undefined
38685
+ }))
38686
+ },
38687
+ {
38688
+ name: "fleet_install",
38689
+ description: "Batch-install missing or outdated @hasna MCP packages across machines",
38690
+ paramsSchema: {
38691
+ machine_ids: exports_external2.array(exports_external2.string()).optional().describe("Optional machine IDs to target"),
38692
+ packages: exports_external2.array(exports_external2.string()).optional().describe("Optional @hasna package-name filter"),
38693
+ mode: exports_external2.enum(["missing", "missing-or-outdated", "all"]).optional().describe("Install selection mode"),
38694
+ installer: exports_external2.enum(["auto", "bun", "npm"]).optional().describe("Override installer"),
38695
+ refresh_catalog: exports_external2.boolean().optional().describe("Refresh npm metadata before installing"),
38696
+ timeout_ms: exports_external2.number().int().min(1000).optional().describe("Remote timeout in milliseconds")
38697
+ },
38698
+ run: async ({ machine_ids, packages, mode, installer, refresh_catalog, timeout_ms }) => jsonContent(await runFleetInstall({
38699
+ machineIds: Array.isArray(machine_ids) ? machine_ids.map(String) : undefined,
38700
+ packages: Array.isArray(packages) ? packages.map(String) : undefined,
38701
+ mode: mode === "missing" || mode === "missing-or-outdated" || mode === "all" ? mode : undefined,
38702
+ installer: installer === "auto" || installer === "bun" || installer === "npm" ? installer : undefined,
38703
+ refreshCatalog: refresh_catalog === true,
38704
+ timeoutMs: typeof timeout_ms === "number" ? timeout_ms : undefined
38705
+ }))
38706
+ },
38707
+ {
38708
+ name: "send_feedback",
38709
+ description: "Send feedback about this service",
38710
+ paramsSchema: {
38711
+ message: exports_external2.string().describe("Feedback message"),
38712
+ email: exports_external2.string().optional().describe("Contact email (optional)"),
38713
+ category: exports_external2.enum(["bug", "feature", "general"]).optional().describe("Feedback category")
38714
+ },
38715
+ run: ({ message, email: email2, category }) => {
38716
+ const adapter = getAdapter();
38717
+ adapter.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", String(message), typeof email2 === "string" ? email2 : null, typeof category === "string" ? category : "general", VERSION);
38718
+ return textContent("Feedback saved. Thank you!");
38719
+ }
38720
+ },
38721
+ {
38722
+ name: "register_agent",
38723
+ description: "Register an agent session. Returns agent_id. Auto-triggers a heartbeat.",
38724
+ paramsSchema: {
38725
+ name: exports_external2.string(),
38726
+ session_id: exports_external2.string().optional()
38727
+ },
38728
+ run: ({ name, session_id }) => {
38729
+ const agentName = String(name);
38730
+ const existing = [...mcpsAgents.values()].find((agent2) => agent2.name === agentName);
38731
+ if (existing) {
38732
+ existing.last_seen_at = new Date().toISOString();
38733
+ if (typeof session_id === "string")
38734
+ existing.session_id = session_id;
38735
+ return jsonContent(existing);
38736
+ }
38737
+ const id = Math.random().toString(36).slice(2, 10);
38738
+ const agent = {
38739
+ id,
38740
+ name: agentName,
38741
+ session_id: typeof session_id === "string" ? session_id : undefined,
38742
+ last_seen_at: new Date().toISOString()
38743
+ };
38744
+ mcpsAgents.set(id, agent);
38745
+ return jsonContent(agent);
38746
+ }
38747
+ },
38748
+ {
38749
+ name: "heartbeat",
38750
+ description: "Update last_seen_at to signal agent is active.",
38751
+ paramsSchema: { agent_id: exports_external2.string() },
38752
+ run: ({ agent_id }) => {
38753
+ const agentId = String(agent_id);
38754
+ const agent = mcpsAgents.get(agentId);
38755
+ if (!agent)
38756
+ return errorContent(`Agent not found: ${agentId}`);
38757
+ agent.last_seen_at = new Date().toISOString();
38758
+ return jsonContent({ agent_id: agent.id, last_seen_at: agent.last_seen_at });
38759
+ }
38760
+ },
38761
+ {
38762
+ name: "set_focus",
38763
+ description: "Set active project context for this agent session.",
38764
+ paramsSchema: {
38765
+ agent_id: exports_external2.string(),
38766
+ project_id: exports_external2.string().optional()
38767
+ },
38768
+ run: ({ agent_id, project_id }) => {
38769
+ const agentId = String(agent_id);
38770
+ const agent = mcpsAgents.get(agentId);
38771
+ if (!agent)
38772
+ return errorContent(`Agent not found: ${agentId}`);
38773
+ agent.project_id = typeof project_id === "string" ? project_id : undefined;
38774
+ return jsonContent({ agent_id: agent.id, project_id: agent.project_id ?? null });
38775
+ }
38776
+ },
38777
+ {
38778
+ name: "list_agents",
38779
+ description: "List all registered agents.",
38780
+ paramsSchema: {},
38781
+ run: () => jsonContent([...mcpsAgents.values()])
38782
+ }
38783
+ ];
38784
+ return definitions.map((definition) => ({
38785
+ ...definition,
38786
+ inputSchema: zodRawShapeToJsonSchema(definition.paramsSchema)
38787
+ }));
38788
+ }
38789
+ var tools = buildMcpTools();
38790
+ function registerMcpTools(server, toolDefinitions = buildMcpTools()) {
38791
+ for (const tool of toolDefinitions) {
38792
+ server.tool(tool.name, tool.description, tool.paramsSchema ?? {}, async (input) => tool.run(readRecord(input)));
38166
38793
  }
38167
- return {
38168
- content: [{ type: "text", text: JSON.stringify(redactServerEnv(entry), null, 2) }]
38169
- };
38170
- });
38171
- server.tool("find_mcp_servers", "Search for MCP servers across configured sources (official registry, npm, GitHub topics, awesome lists). Use list_sources to see available source IDs.", {
38172
- query: exports_external2.string().describe("Search query (e.g. 'filesystem', 'postgres', 'browser')"),
38173
- sources: exports_external2.array(exports_external2.string()).optional().describe("Source IDs to search (default: all enabled). Use list_sources to get IDs."),
38174
- limit: exports_external2.number().optional().describe("Max results per source (default: 20)")
38175
- }, async ({ query, sources, limit }) => {
38176
- const results = await findServers(query, { sources, limit });
38177
- return {
38178
- content: [{ type: "text", text: JSON.stringify(results, null, 2) }]
38179
- };
38180
- });
38181
- server.tool("list_sources", "List all configured search sources for finding MCP servers", {}, async () => {
38182
- const sources = listSources();
38183
- return {
38184
- content: [{ type: "text", text: JSON.stringify(sources, null, 2) }]
38185
- };
38186
- });
38187
- server.tool("add_source", "Add a new search source for finding MCP servers", {
38188
- name: exports_external2.string().describe("Source name"),
38189
- type: exports_external2.enum(["mcp-registry", "awesome-list", "npm-search", "github-topic"]).describe("Source type"),
38190
- url: exports_external2.string().describe("Source URL endpoint"),
38191
- description: exports_external2.string().optional().describe("Description")
38192
- }, async ({ name, type, url: url2, description }) => {
38193
- const source = addSource({ name, type, url: url2, description });
38194
- return {
38195
- content: [{ type: "text", text: JSON.stringify(source, null, 2) }]
38196
- };
38197
- });
38198
- server.tool("remove_source", "Remove a search source by ID", { id: exports_external2.string().describe("Source ID to remove") }, async ({ id }) => {
38199
- const existing = getSource(id);
38200
- if (!existing) {
38201
- return {
38202
- content: [{ type: "text", text: `Source "${id}" not found.` }],
38203
- isError: true
38204
- };
38794
+ return toolDefinitions;
38795
+ }
38796
+ function zodRawShapeToJsonSchema(shape) {
38797
+ const properties = {};
38798
+ const required2 = [];
38799
+ for (const [key, schema] of Object.entries(shape)) {
38800
+ properties[key] = zodSchemaToJsonSchema(schema);
38801
+ if (!isOptionalSchema(schema))
38802
+ required2.push(key);
38205
38803
  }
38206
- removeSource(id);
38207
38804
  return {
38208
- content: [{ type: "text", text: `Removed source: ${existing.name} [${id}]` }]
38805
+ type: "object",
38806
+ properties,
38807
+ ...required2.length > 0 ? { required: required2 } : {},
38808
+ additionalProperties: false
38209
38809
  };
38210
- });
38211
- server.tool("enable_source_finder", "Enable a search source", { id: exports_external2.string().describe("Source ID to enable") }, async ({ id }) => {
38212
- const existing = getSource(id);
38213
- if (!existing) {
38214
- return {
38215
- content: [{ type: "text", text: `Source "${id}" not found.` }],
38216
- isError: true
38217
- };
38810
+ }
38811
+ function zodSchemaToJsonSchema(schema) {
38812
+ const def = schema._def;
38813
+ const typeName = String(def?.typeName ?? "");
38814
+ const description = schema.description ? { description: schema.description } : {};
38815
+ if (typeName === exports_external2.ZodFirstPartyTypeKind.ZodOptional || typeName === exports_external2.ZodFirstPartyTypeKind.ZodDefault) {
38816
+ return { ...asJsonSchemaObject(zodSchemaToJsonSchema(def.innerType)), ...description };
38218
38817
  }
38219
- enableSource(id);
38220
- return {
38221
- content: [{ type: "text", text: `Enabled source: ${existing.name}` }]
38222
- };
38223
- });
38224
- server.tool("disable_source_finder", "Disable a search source", { id: exports_external2.string().describe("Source ID to disable") }, async ({ id }) => {
38225
- const existing = getSource(id);
38226
- if (!existing) {
38227
- return {
38228
- content: [{ type: "text", text: `Source "${id}" not found.` }],
38229
- isError: true
38230
- };
38818
+ if (typeName === exports_external2.ZodFirstPartyTypeKind.ZodNullable) {
38819
+ return { ...asJsonSchemaObject(zodSchemaToJsonSchema(def.innerType)), nullable: true, ...description };
38231
38820
  }
38232
- disableSource(id);
38233
- return {
38234
- content: [{ type: "text", text: `Disabled source: ${existing.name}` }]
38235
- };
38236
- });
38237
- server.tool("install_to_agents", "Install a registered MCP server into Claude Code, Codex, and/or Gemini", {
38238
- id: exports_external2.string().describe("Server ID to install (from list_servers)"),
38239
- targets: exports_external2.array(exports_external2.enum(["claude", "codex", "gemini"])).optional().describe("Target agents to install into (default: all)")
38240
- }, async ({ id, targets }) => {
38241
- const entry = getServer(id);
38242
- if (!entry) {
38243
- return {
38244
- content: [{ type: "text", text: `Server "${id}" not found.` }],
38245
- isError: true
38246
- };
38821
+ if (typeName === exports_external2.ZodFirstPartyTypeKind.ZodString) {
38822
+ return { type: "string", ...description };
38247
38823
  }
38248
- const agentTargets = targets ?? ["claude", "codex", "gemini"];
38249
- const results = installToAgents(entry, agentTargets);
38250
- return {
38251
- content: [{ type: "text", text: JSON.stringify(results, null, 2) }]
38252
- };
38253
- });
38254
- server.tool("list_awesome_servers", "List all MCP servers from the curated punkpeye/awesome-mcp-servers GitHub list", {}, async () => {
38255
- const results = await listAwesomeServers();
38256
- return {
38257
- content: [{ type: "text", text: JSON.stringify(results, null, 2) }]
38258
- };
38259
- });
38260
- server.tool("connect_and_list_tools", "Connect to all enabled MCP servers and list their available tools", {}, async () => {
38261
- let tools = [];
38262
- try {
38263
- await connectAllEnabled();
38264
- tools = listAllTools();
38265
- } finally {
38266
- await disconnectAll().catch(() => {
38267
- return;
38268
- });
38824
+ if (typeName === exports_external2.ZodFirstPartyTypeKind.ZodBoolean) {
38825
+ return { type: "boolean", ...description };
38269
38826
  }
38270
- return {
38271
- content: [{ type: "text", text: JSON.stringify(tools, null, 2) }]
38272
- };
38273
- });
38274
- server.tool("call_upstream_tool", `Call a tool on a connected upstream MCP server. Tool name format: server_id${TOOL_PREFIX_SEPARATOR}tool_name`, {
38275
- tool_name: exports_external2.string().describe(`Prefixed tool name (server_id${TOOL_PREFIX_SEPARATOR}tool_name)`),
38276
- arguments: exports_external2.record(exports_external2.unknown()).optional().describe("Tool arguments as key-value pairs")
38277
- }, async ({ tool_name, arguments: args }) => {
38278
- try {
38279
- const sepIdx = tool_name.indexOf(TOOL_PREFIX_SEPARATOR);
38280
- if (sepIdx === -1) {
38281
- return {
38282
- content: [{ type: "text", text: `Error: Invalid tool name "${tool_name}"` }],
38283
- isError: true
38284
- };
38285
- }
38286
- const serverId = tool_name.slice(0, sepIdx);
38287
- const entry = getServer(serverId);
38288
- if (!entry) {
38289
- return {
38290
- content: [{ type: "text", text: `Error: Server "${serverId}" not found.` }],
38291
- isError: true
38292
- };
38293
- }
38294
- if (!entry.enabled) {
38295
- return {
38296
- content: [{ type: "text", text: `Error: Server "${serverId}" is disabled.` }],
38297
- isError: true
38298
- };
38299
- }
38300
- await connectToServer(entry);
38301
- const result = await callTool(tool_name, args || {});
38302
- return { content: result.content };
38303
- } catch (err) {
38304
- return {
38305
- content: [{ type: "text", text: `Error: ${err.message}` }],
38306
- isError: true
38307
- };
38827
+ if (typeName === exports_external2.ZodFirstPartyTypeKind.ZodUnknown || typeName === exports_external2.ZodFirstPartyTypeKind.ZodAny) {
38828
+ return Object.keys(description).length > 0 ? description : true;
38308
38829
  }
38309
- });
38310
- server.tool("diagnose_server", "Run health checks on a registered MCP server", { id: exports_external2.string().describe("Server ID") }, async ({ id }) => {
38311
- const entry = getServer(id);
38312
- if (!entry)
38313
- return { content: [{ type: "text", text: `Server "${id}" not found.` }], isError: true };
38314
- const report = await diagnoseServer(entry);
38315
- return { content: [{ type: "text", text: JSON.stringify(report, null, 2) }] };
38316
- });
38317
- server.tool("list_machines", "List registered fleet machines", {
38318
- enabled_only: exports_external2.boolean().optional().describe("When true, only return enabled machines")
38319
- }, async ({ enabled_only }) => {
38320
- const machines = listMachines().filter((machine) => enabled_only ? machine.enabled : true);
38321
- return {
38322
- content: [{ type: "text", text: JSON.stringify(machines, null, 2) }]
38323
- };
38324
- });
38325
- server.tool("add_machine", "Register a machine for fleet health checks and installs", {
38326
- host: exports_external2.string().describe("Hostname or SSH target"),
38327
- id: exports_external2.string().optional().describe("Stable machine ID"),
38328
- name: exports_external2.string().optional().describe("Display name"),
38329
- username: exports_external2.string().optional().describe("SSH username"),
38330
- port: exports_external2.number().int().min(1).max(65535).optional().describe("SSH port"),
38331
- platform: exports_external2.enum(["linux", "darwin", "unknown"]).optional().describe("Machine platform"),
38332
- arch: exports_external2.enum(["arm64", "x64", "unknown"]).optional().describe("Machine architecture"),
38333
- bun_path: exports_external2.string().optional().describe("Explicit path to bun on the remote machine"),
38334
- npm_path: exports_external2.string().optional().describe("Explicit path to npm on the remote machine"),
38335
- installer: exports_external2.enum(["auto", "bun", "npm"]).optional().describe("Preferred installer"),
38336
- ssh_key_path: exports_external2.string().optional().describe("SSH private key path"),
38337
- enabled: exports_external2.boolean().optional().describe("Whether the machine should be enabled")
38338
- }, async ({ host, id, name, username, port, platform: platform2, arch, bun_path, npm_path, installer, ssh_key_path, enabled }) => {
38339
- const machine = addMachine({
38340
- host,
38341
- id,
38342
- name,
38343
- username,
38344
- port,
38345
- platform: platform2,
38346
- arch,
38347
- bun_path,
38348
- npm_path,
38349
- installer,
38350
- ssh_key_path,
38351
- enabled
38352
- });
38353
- return {
38354
- content: [{ type: "text", text: JSON.stringify(machine, null, 2) }]
38355
- };
38356
- });
38357
- server.tool("remove_machine", "Remove a registered machine", { id: exports_external2.string().describe("Machine ID to remove") }, async ({ id }) => {
38358
- const machine = getMachine(id);
38359
- if (!machine) {
38360
- return {
38361
- content: [{ type: "text", text: `Machine "${id}" not found.` }],
38362
- isError: true
38830
+ if (typeName === exports_external2.ZodFirstPartyTypeKind.ZodNumber) {
38831
+ const checks4 = Array.isArray(def.checks) ? def.checks : [];
38832
+ const schemaJson = {
38833
+ type: checks4.some((check2) => check2.kind === "int") ? "integer" : "number",
38834
+ ...description
38363
38835
  };
38836
+ for (const check2 of checks4) {
38837
+ if (check2.kind === "min")
38838
+ schemaJson.minimum = check2.value;
38839
+ if (check2.kind === "max")
38840
+ schemaJson.maximum = check2.value;
38841
+ }
38842
+ return schemaJson;
38364
38843
  }
38365
- removeMachine(id);
38366
- return {
38367
- content: [{ type: "text", text: JSON.stringify({ removed: true, machine }, null, 2) }]
38368
- };
38369
- });
38370
- server.tool("seed_default_machines", "Seed the standard spark/apple machine inventory", {}, async () => {
38371
- const machines = seedDefaultMachines();
38372
- return {
38373
- content: [{ type: "text", text: JSON.stringify(machines, null, 2) }]
38374
- };
38375
- });
38376
- server.tool("list_hasna_mcp_catalog", "List the discovered @hasna MCP package catalog", {
38377
- packages: exports_external2.array(exports_external2.string()).optional().describe("Optional package-name filter"),
38378
- refresh: exports_external2.boolean().optional().describe("Refresh npm metadata instead of using cache")
38379
- }, async ({ packages, refresh }) => {
38380
- const catalog = await listHasnaMcpCatalog({ refresh });
38381
- const filtered = packages?.length ? catalog.filter((entry) => packages.includes(entry.name)) : catalog;
38382
- return {
38383
- content: [{ type: "text", text: JSON.stringify(filtered, null, 2) }]
38384
- };
38385
- });
38386
- server.tool("fleet_health", "Run fleet-wide MCP health checks across registered machines", {
38387
- machine_ids: exports_external2.array(exports_external2.string()).optional().describe("Optional machine IDs to check"),
38388
- packages: exports_external2.array(exports_external2.string()).optional().describe("Optional @hasna package-name filter"),
38389
- refresh_catalog: exports_external2.boolean().optional().describe("Refresh npm metadata before checking"),
38390
- timeout_ms: exports_external2.number().int().min(1000).optional().describe("Remote timeout in milliseconds")
38391
- }, async ({ machine_ids, packages, refresh_catalog, timeout_ms }) => {
38392
- const reports = await runFleetHealthCheck({
38393
- machineIds: machine_ids,
38394
- packages,
38395
- refreshCatalog: refresh_catalog,
38396
- timeoutMs: timeout_ms
38397
- });
38398
- return {
38399
- content: [{ type: "text", text: JSON.stringify(reports, null, 2) }]
38400
- };
38401
- });
38402
- server.tool("fleet_install", "Batch-install missing or outdated @hasna MCP packages across machines", {
38403
- machine_ids: exports_external2.array(exports_external2.string()).optional().describe("Optional machine IDs to target"),
38404
- packages: exports_external2.array(exports_external2.string()).optional().describe("Optional @hasna package-name filter"),
38405
- mode: exports_external2.enum(["missing", "missing-or-outdated", "all"]).optional().describe("Install selection mode"),
38406
- installer: exports_external2.enum(["auto", "bun", "npm"]).optional().describe("Override installer"),
38407
- refresh_catalog: exports_external2.boolean().optional().describe("Refresh npm metadata before installing"),
38408
- timeout_ms: exports_external2.number().int().min(1000).optional().describe("Remote timeout in milliseconds")
38409
- }, async ({ machine_ids, packages, mode, installer, refresh_catalog, timeout_ms }) => {
38410
- const reports = await runFleetInstall({
38411
- machineIds: machine_ids,
38412
- packages,
38413
- mode,
38414
- installer,
38415
- refreshCatalog: refresh_catalog,
38416
- timeoutMs: timeout_ms
38844
+ if (typeName === exports_external2.ZodFirstPartyTypeKind.ZodEnum) {
38845
+ return { type: "string", enum: def.values, ...description };
38846
+ }
38847
+ if (typeName === exports_external2.ZodFirstPartyTypeKind.ZodArray) {
38848
+ return { type: "array", items: zodSchemaToJsonSchema(def.type), ...description };
38849
+ }
38850
+ if (typeName === exports_external2.ZodFirstPartyTypeKind.ZodRecord) {
38851
+ const valueType = def.valueType ? zodSchemaToJsonSchema(def.valueType) : true;
38852
+ return { type: "object", additionalProperties: valueType, ...description };
38853
+ }
38854
+ if (typeName === exports_external2.ZodFirstPartyTypeKind.ZodObject) {
38855
+ const objectShape = typeof def.shape === "function" ? def.shape() : def.shape;
38856
+ return { ...zodRawShapeToJsonSchema(objectShape ?? {}), ...description };
38857
+ }
38858
+ return Object.keys(description).length > 0 ? description : {};
38859
+ }
38860
+ function asJsonSchemaObject(schema) {
38861
+ return typeof schema === "boolean" ? {} : schema;
38862
+ }
38863
+ function isOptionalSchema(schema) {
38864
+ const def = schema._def;
38865
+ const typeName = String(def?.typeName ?? "");
38866
+ return typeName === exports_external2.ZodFirstPartyTypeKind.ZodOptional || typeName === exports_external2.ZodFirstPartyTypeKind.ZodDefault;
38867
+ }
38868
+ function readRecord(value) {
38869
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
38870
+ }
38871
+ function isRecordOfStrings(value) {
38872
+ if (!value || typeof value !== "object" || Array.isArray(value))
38873
+ return false;
38874
+ return Object.values(value).every((item) => typeof item === "string");
38875
+ }
38876
+
38877
+ // src/mcp/server.ts
38878
+ var VERSION2 = readPackageVersion(import.meta.url);
38879
+ function createMcpServer(options = {}) {
38880
+ const server = new McpServer({
38881
+ name: options.name ?? "mcps",
38882
+ version: options.version ?? VERSION2
38417
38883
  });
38418
- return {
38419
- content: [{ type: "text", text: JSON.stringify(reports, null, 2) }]
38420
- };
38421
- });
38422
- server.tool("send_feedback", "Send feedback about this service", {
38423
- message: exports_external2.string().describe("Feedback message"),
38424
- email: exports_external2.string().optional().describe("Contact email (optional)"),
38425
- category: exports_external2.enum(["bug", "feature", "general"]).optional().describe("Feedback category")
38426
- }, async (params) => {
38427
- const adapter = getAdapter();
38428
- adapter.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", params.message, params.email || null, params.category || "general", VERSION);
38429
- return { content: [{ type: "text", text: "Feedback saved. Thank you!" }] };
38430
- });
38431
- server.tool("register_agent", "Register an agent session. Returns agent_id. Auto-triggers a heartbeat.", {
38432
- name: exports_external2.string(),
38433
- session_id: exports_external2.string().optional()
38434
- }, async (params) => {
38435
- const existing = [..._mcpsAgents.values()].find((a) => a.name === params.name);
38436
- if (existing) {
38437
- existing.last_seen_at = new Date().toISOString();
38438
- if (params.session_id)
38439
- existing.session_id = params.session_id;
38440
- return { content: [{ type: "text", text: JSON.stringify(existing) }] };
38441
- }
38442
- const id = Math.random().toString(36).slice(2, 10);
38443
- const ag = { id, name: params.name, session_id: params.session_id, last_seen_at: new Date().toISOString() };
38444
- _mcpsAgents.set(id, ag);
38445
- return { content: [{ type: "text", text: JSON.stringify(ag) }] };
38446
- });
38447
- server.tool("heartbeat", "Update last_seen_at to signal agent is active.", {
38448
- agent_id: exports_external2.string()
38449
- }, async (params) => {
38450
- const ag = _mcpsAgents.get(params.agent_id);
38451
- if (!ag)
38452
- return { content: [{ type: "text", text: `Agent not found: ${params.agent_id}` }], isError: true };
38453
- ag.last_seen_at = new Date().toISOString();
38454
- return { content: [{ type: "text", text: JSON.stringify({ agent_id: ag.id, last_seen_at: ag.last_seen_at }) }] };
38455
- });
38456
- server.tool("set_focus", "Set active project context for this agent session.", {
38457
- agent_id: exports_external2.string(),
38458
- project_id: exports_external2.string().optional()
38459
- }, async (params) => {
38460
- const ag = _mcpsAgents.get(params.agent_id);
38461
- if (!ag)
38462
- return { content: [{ type: "text", text: `Agent not found: ${params.agent_id}` }], isError: true };
38463
- ag.project_id = params.project_id;
38464
- return { content: [{ type: "text", text: JSON.stringify({ agent_id: ag.id, project_id: ag.project_id ?? null }) }] };
38465
- });
38466
- server.tool("list_agents", "List all registered agents.", {}, async () => {
38467
- return { content: [{ type: "text", text: JSON.stringify([..._mcpsAgents.values()]) }] };
38468
- });
38884
+ registerMcpTools(server, options.tools);
38885
+ if (options.cloudTools !== false) {
38886
+ registerCloudTools(server, "mcps");
38887
+ }
38888
+ return server;
38889
+ }
38890
+
38891
+ // src/mcp/index.ts
38469
38892
  async function startMcpServer() {
38893
+ const server = createMcpServer();
38470
38894
  const transport = new StdioServerTransport;
38471
- registerCloudTools(server, "mcps");
38472
38895
  await server.connect(transport);
38473
38896
  }
38474
38897
  var isDirectRun = import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith("/mcp/index.ts") || process.argv[1]?.endsWith("/bin/mcp.js");
@@ -38485,8 +38908,8 @@ import { join as join11, dirname as dirname4, extname, resolve, relative as rela
38485
38908
  import { fileURLToPath as fileURLToPath2 } from "url";
38486
38909
  init_sources();
38487
38910
  init_db();
38488
- function redactServer(server2) {
38489
- return { ...server2, env: {} };
38911
+ function redactServer(server) {
38912
+ return { ...server, env: {} };
38490
38913
  }
38491
38914
  function resolveDashboardDir() {
38492
38915
  const candidates = [];
@@ -38635,7 +39058,7 @@ Dashboard not found at: ${dashboardDir}`);
38635
39058
  console.error(` cd dashboard && bun install && bun run build
38636
39059
  `);
38637
39060
  }
38638
- const server2 = Bun.serve({
39061
+ const server = Bun.serve({
38639
39062
  port,
38640
39063
  hostname: host,
38641
39064
  async fetch(req) {
@@ -38706,8 +39129,8 @@ Dashboard not found at: ${dashboardDir}`);
38706
39129
  const entry = getServer(id);
38707
39130
  if (!entry)
38708
39131
  return json({ error: `Server '${id}' not found` }, 404, port);
38709
- const tools = getCachedTools(id);
38710
- return json({ ...redactServer(entry), toolCount: tools.length, tools }, 200, port);
39132
+ const tools2 = getCachedTools(id);
39133
+ return json({ ...redactServer(entry), toolCount: tools2.length, tools: tools2 }, 200, port);
38711
39134
  }
38712
39135
  if (singleMatch && method === "DELETE") {
38713
39136
  const id = singleMatch[1];
@@ -38835,8 +39258,8 @@ Dashboard not found at: ${dashboardDir}`);
38835
39258
  const entry = getServer(id);
38836
39259
  if (!entry)
38837
39260
  return json({ error: `Server '${id}' not found` }, 404, port);
38838
- const tools = getCachedTools(id);
38839
- return json(tools, 200, port);
39261
+ const tools2 = getCachedTools(id);
39262
+ return json(tools2, 200, port);
38840
39263
  }
38841
39264
  const serverCallMatch = path.match(/^\/api\/servers\/([^/]+)\/call$/);
38842
39265
  if (serverCallMatch && method === "POST") {
@@ -39019,7 +39442,7 @@ Dashboard not found at: ${dashboardDir}`);
39019
39442
  }
39020
39443
  });
39021
39444
  const shutdown = () => {
39022
- server2.stop();
39445
+ server.stop();
39023
39446
  closeDb();
39024
39447
  process.exit(0);
39025
39448
  };
@@ -39532,18 +39955,18 @@ var build_default = Spinner;
39532
39955
 
39533
39956
  // src/cli/components/ServerDetail.tsx
39534
39957
  import { jsxDEV as jsxDEV2, Fragment } from "react/jsx-dev-runtime";
39535
- function ServerDetail({ server: server2, onSelectTool, onBack }) {
39536
- const [tools, setTools] = useState3([]);
39958
+ function ServerDetail({ server, onSelectTool, onBack }) {
39959
+ const [tools2, setTools] = useState3([]);
39537
39960
  const [loading, setLoading] = useState3(false);
39538
39961
  const [error2, setError] = useState3(null);
39539
- const cachedTools = getCachedTools(server2.id);
39962
+ const cachedTools = getCachedTools(server.id);
39540
39963
  const cachedKey = cachedTools.map((t) => `${t.name}|${t.description}|${JSON.stringify(t.input_schema)}`).join(";");
39541
39964
  useEffect3(() => {
39542
39965
  setLoading(false);
39543
39966
  setError(null);
39544
39967
  if (cachedTools.length > 0) {
39545
39968
  setTools(cachedTools.map((t) => ({
39546
- server_id: server2.id,
39969
+ server_id: server.id,
39547
39970
  name: t.name,
39548
39971
  description: t.description,
39549
39972
  input_schema: t.input_schema
@@ -39551,12 +39974,12 @@ function ServerDetail({ server: server2, onSelectTool, onBack }) {
39551
39974
  } else {
39552
39975
  setTools([]);
39553
39976
  }
39554
- }, [server2.id, cachedKey]);
39977
+ }, [server.id, cachedKey]);
39555
39978
  const handleConnect = async () => {
39556
39979
  setLoading(true);
39557
39980
  setError(null);
39558
39981
  try {
39559
- const conn = await connectToServer(server2);
39982
+ const conn = await connectToServer(server);
39560
39983
  setTools(conn.tools);
39561
39984
  } catch (err) {
39562
39985
  setError(err.message);
@@ -39565,8 +39988,8 @@ function ServerDetail({ server: server2, onSelectTool, onBack }) {
39565
39988
  }
39566
39989
  };
39567
39990
  const items = [
39568
- ...tools.length === 0 && !loading ? [{ label: "Connect & fetch tools", value: "__connect" }] : [],
39569
- ...tools.map((t) => ({
39991
+ ...tools2.length === 0 && !loading ? [{ label: "Connect & fetch tools", value: "__connect" }] : [],
39992
+ ...tools2.map((t) => ({
39570
39993
  label: `${t.name} \u2014 ${t.description || "No description"}`,
39571
39994
  value: t.name,
39572
39995
  tool: t
@@ -39575,7 +39998,7 @@ function ServerDetail({ server: server2, onSelectTool, onBack }) {
39575
39998
  ];
39576
39999
  const handleSelect = (item) => {
39577
40000
  if (item.value === "__back") {
39578
- disconnectServer(server2.id);
40001
+ disconnectServer(server.id);
39579
40002
  onBack();
39580
40003
  } else if (item.value === "__connect") {
39581
40004
  handleConnect();
@@ -39588,36 +40011,36 @@ function ServerDetail({ server: server2, onSelectTool, onBack }) {
39588
40011
  children: [
39589
40012
  /* @__PURE__ */ jsxDEV2(Text5, {
39590
40013
  bold: true,
39591
- children: server2.name
40014
+ children: server.name
39592
40015
  }, undefined, false, undefined, this),
39593
40016
  /* @__PURE__ */ jsxDEV2(Text5, {
39594
40017
  dimColor: true,
39595
40018
  children: [
39596
40019
  "ID: ",
39597
- server2.id
40020
+ server.id
39598
40021
  ]
39599
40022
  }, undefined, true, undefined, this),
39600
40023
  /* @__PURE__ */ jsxDEV2(Text5, {
39601
40024
  dimColor: true,
39602
40025
  children: [
39603
40026
  "Status: ",
39604
- server2.enabled ? "enabled" : "disabled",
40027
+ server.enabled ? "enabled" : "disabled",
39605
40028
  " | Transport: ",
39606
- server2.transport
40029
+ server.transport
39607
40030
  ]
39608
40031
  }, undefined, true, undefined, this),
39609
40032
  /* @__PURE__ */ jsxDEV2(Text5, {
39610
40033
  dimColor: true,
39611
40034
  children: [
39612
40035
  "Command: ",
39613
- server2.command,
40036
+ server.command,
39614
40037
  " ",
39615
- server2.args.join(" ")
40038
+ server.args.join(" ")
39616
40039
  ]
39617
40040
  }, undefined, true, undefined, this),
39618
- server2.description && /* @__PURE__ */ jsxDEV2(Text5, {
40041
+ server.description && /* @__PURE__ */ jsxDEV2(Text5, {
39619
40042
  dimColor: true,
39620
- children: server2.description
40043
+ children: server.description
39621
40044
  }, undefined, false, undefined, this),
39622
40045
  /* @__PURE__ */ jsxDEV2(Box4, {
39623
40046
  marginTop: 1,
@@ -39639,7 +40062,7 @@ function ServerDetail({ server: server2, onSelectTool, onBack }) {
39639
40062
  children: [
39640
40063
  /* @__PURE__ */ jsxDEV2(Text5, {
39641
40064
  bold: true,
39642
- children: tools.length > 0 ? `Tools (${tools.length}):` : "Tools:"
40065
+ children: tools2.length > 0 ? `Tools (${tools2.length}):` : "Tools:"
39643
40066
  }, undefined, false, undefined, this),
39644
40067
  /* @__PURE__ */ jsxDEV2(SelectInput_default, {
39645
40068
  items,
@@ -39783,8 +40206,8 @@ function SearchView({ onBack }) {
39783
40206
  setInstalling(item.value);
39784
40207
  setMessage(null);
39785
40208
  try {
39786
- const server2 = await installFromRegistry(item.value);
39787
- setMessage(`Installed: ${server2.name} [${server2.id}]`);
40209
+ const server = await installFromRegistry(item.value);
40210
+ setMessage(`Installed: ${server.name} [${server.id}]`);
39788
40211
  } catch (err) {
39789
40212
  setMessage(`Install failed: ${err.message}`);
39790
40213
  } finally {
@@ -39877,7 +40300,7 @@ import { useState as useState6 } from "react";
39877
40300
  import { Box as Box6, Text as Text8 } from "ink";
39878
40301
  init_config2();
39879
40302
  import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
39880
- function ToolCall({ server: server2, tool, onBack }) {
40303
+ function ToolCall({ server, tool, onBack }) {
39881
40304
  const [argsInput, setArgsInput] = useState6("{}");
39882
40305
  const [calling, setCalling] = useState6(false);
39883
40306
  const [result, setResult] = useState6(null);
@@ -39894,8 +40317,8 @@ function ToolCall({ server: server2, tool, onBack }) {
39894
40317
  setError(null);
39895
40318
  setResult(null);
39896
40319
  try {
39897
- await connectToServer(server2);
39898
- const prefixed = `${server2.id}${TOOL_PREFIX_SEPARATOR}${tool.name}`;
40320
+ await connectToServer(server);
40321
+ const prefixed = `${server.id}${TOOL_PREFIX_SEPARATOR}${tool.name}`;
39899
40322
  const res = await callTool(prefixed, args);
39900
40323
  const text = res.content.map((c) => c.text).join(`
39901
40324
  `);
@@ -39923,9 +40346,9 @@ function ToolCall({ server: server2, tool, onBack }) {
39923
40346
  dimColor: true,
39924
40347
  children: [
39925
40348
  "Server: ",
39926
- server2.name,
40349
+ server.name,
39927
40350
  " [",
39928
- server2.id,
40351
+ server.id,
39929
40352
  "]"
39930
40353
  ]
39931
40354
  }, undefined, true, undefined, this),
@@ -40038,8 +40461,8 @@ function App() {
40038
40461
  }).finally(() => closeDb());
40039
40462
  };
40040
40463
  }, []);
40041
- const handleSelectServer = (server2) => {
40042
- setSelectedServer(server2);
40464
+ const handleSelectServer = (server) => {
40465
+ setSelectedServer(server);
40043
40466
  setView("detail");
40044
40467
  };
40045
40468
  const handleSearch = () => {
@@ -40107,13 +40530,31 @@ function App() {
40107
40530
  }
40108
40531
 
40109
40532
  // src/cli/index.tsx
40110
- var VERSION2 = (() => {
40533
+ var VERSION3 = (() => {
40111
40534
  return readPackageVersion(import.meta.url);
40112
40535
  })();
40113
40536
  var MACHINE_PLATFORMS = ["linux", "darwin", "unknown"];
40114
40537
  var MACHINE_ARCHES = ["arm64", "x64", "unknown"];
40115
40538
  var MACHINE_INSTALLERS = ["auto", "bun", "npm"];
40116
40539
  var FLEET_INSTALL_MODES = ["missing", "missing-or-outdated", "all"];
40540
+ function printProviderProfile(profile) {
40541
+ const status = profile.enabled ? chalk2.green("enabled") : chalk2.red("disabled");
40542
+ console.log(` ${chalk2.bold(profile.displayName)} ${chalk2.dim(`[${profile.id}]`)} \u2014 ${chalk2.dim(profile.transport)} \u2014 ${status}`);
40543
+ if (profile.description)
40544
+ console.log(` ${chalk2.dim(profile.description)}`);
40545
+ if (profile.endpoint)
40546
+ console.log(` ${chalk2.cyan(profile.endpoint)}`);
40547
+ if (profile.authMetadata.bearerToken === "optional") {
40548
+ console.log(` ${chalk2.dim("Auth: OAuth with optional bearer token/API key support")}`);
40549
+ } else if (profile.authMetadata.pkce || profile.authMetadata.dynamicClientRegistration) {
40550
+ const parts = [
40551
+ profile.authMetadata.oauthVersion ? `OAuth ${profile.authMetadata.oauthVersion}` : "OAuth",
40552
+ profile.authMetadata.pkce ? "PKCE" : null,
40553
+ profile.authMetadata.dynamicClientRegistration ? "dynamic client registration" : null
40554
+ ].filter(Boolean);
40555
+ console.log(` ${chalk2.dim(`Auth: ${parts.join(", ")}`)}`);
40556
+ }
40557
+ }
40117
40558
  function printJson(value) {
40118
40559
  console.log(JSON.stringify(value, null, 2));
40119
40560
  }
@@ -40212,7 +40653,7 @@ function renderFleetInstall(reports) {
40212
40653
  }
40213
40654
  }
40214
40655
  var program2 = new Command;
40215
- program2.name("mcps").description("Meta-MCP registry & CLI \u2014 discover, manage, and proxy MCP servers").version(VERSION2).enablePositionalOptions();
40656
+ program2.name("mcps").description("Meta-MCP registry & CLI \u2014 discover, manage, and proxy MCP servers").version(VERSION3).enablePositionalOptions();
40216
40657
  program2.command("list").description("List registered MCP servers").option("--json", "Output as JSON").option("--verbose", "Show detailed info including health, command, and transport").action((opts) => {
40217
40658
  const servers = listServers();
40218
40659
  if (opts.json) {
@@ -40286,6 +40727,85 @@ ${results.length} result(s). Use \`mcps add --from-registry <id>\` to install.`)
40286
40727
  closeDb();
40287
40728
  }
40288
40729
  });
40730
+ var providersCmd = program2.command("providers").description("Discover and install curated MCP provider profiles");
40731
+ providersCmd.command("list").description("List curated provider profiles").option("--json", "Output as JSON").option("--enabled-only", "Only include enabled profiles").action((opts) => {
40732
+ const profiles = listProviderProfiles({ enabledOnly: opts.enabledOnly === true });
40733
+ if (opts.json) {
40734
+ printJson(profiles);
40735
+ closeDb();
40736
+ return;
40737
+ }
40738
+ if (profiles.length === 0) {
40739
+ console.log(chalk2.dim("No curated provider profiles available."));
40740
+ closeDb();
40741
+ return;
40742
+ }
40743
+ for (const profile of profiles)
40744
+ printProviderProfile(profile);
40745
+ closeDb();
40746
+ });
40747
+ providersCmd.command("search").argument("<query>", "Search query").description("Search curated provider profiles").option("--json", "Output as JSON").option("--enabled-only", "Only include enabled profiles").action((query, opts) => {
40748
+ const profiles = searchProviderProfiles(query, { enabledOnly: opts.enabledOnly === true });
40749
+ if (opts.json) {
40750
+ printJson(profiles);
40751
+ closeDb();
40752
+ return;
40753
+ }
40754
+ if (profiles.length === 0) {
40755
+ console.log(chalk2.dim("No curated provider profiles found."));
40756
+ closeDb();
40757
+ return;
40758
+ }
40759
+ for (const profile of profiles)
40760
+ printProviderProfile(profile);
40761
+ console.log(chalk2.dim(`
40762
+ ${profiles.length} provider profile(s). Use \`mcps providers install <id>\` to register one.`));
40763
+ closeDb();
40764
+ });
40765
+ providersCmd.command("info").argument("<id>", "Provider profile ID").description("Show a curated provider profile").option("--json", "Output as JSON").action((id, opts) => {
40766
+ const profile = getProviderProfile(id);
40767
+ if (!profile) {
40768
+ console.error(chalk2.red(`Provider profile "${id}" not found.`));
40769
+ closeDb();
40770
+ process.exit(1);
40771
+ }
40772
+ if (opts.json) {
40773
+ printJson(profile);
40774
+ closeDb();
40775
+ return;
40776
+ }
40777
+ printProviderProfile(profile);
40778
+ if (profile.fallbackEndpoints.length > 0) {
40779
+ console.log(chalk2.bold(" Fallback endpoints:"));
40780
+ for (const fallback of profile.fallbackEndpoints) {
40781
+ console.log(` ${fallback.transport}: ${chalk2.cyan(fallback.url)}`);
40782
+ }
40783
+ }
40784
+ if (profile.docsUrl)
40785
+ console.log(` Docs: ${chalk2.cyan(profile.docsUrl)}`);
40786
+ closeDb();
40787
+ });
40788
+ providersCmd.command("install").argument("<id>", "Provider profile ID").description("Register a curated provider profile as an MCP server").option("--name <name>", "Override registered server name").option("--fallback", "Install the stdio fallback command instead of direct remote transport").option("--json", "Output as JSON").action((id, opts) => {
40789
+ try {
40790
+ const server = installProviderProfile(id, {
40791
+ name: opts.name,
40792
+ useFallback: opts.fallback === true
40793
+ });
40794
+ if (opts.json) {
40795
+ printJson(server);
40796
+ } else {
40797
+ console.log(chalk2.green(`Installed provider profile: ${server.name} [${server.id}]`));
40798
+ console.log(chalk2.dim(` Transport: ${server.transport}`));
40799
+ if (server.url)
40800
+ console.log(chalk2.dim(` URL: ${server.url}`));
40801
+ }
40802
+ closeDb();
40803
+ } catch (err) {
40804
+ console.error(chalk2.red(`Failed to install provider profile: ${err.message}`));
40805
+ closeDb();
40806
+ process.exit(1);
40807
+ }
40808
+ });
40289
40809
  async function promptReadline(rl, question) {
40290
40810
  return new Promise((resolve2) => rl.question(question, resolve2));
40291
40811
  }
@@ -40304,9 +40824,9 @@ program2.command("add").passThroughOptions().argument("[command]", "Command to r
40304
40824
  try {
40305
40825
  if (opts.fromRegistry) {
40306
40826
  console.log(chalk2.dim(`Installing "${opts.fromRegistry}" from registry...`));
40307
- const server3 = await installFromRegistry(opts.fromRegistry);
40308
- console.log(chalk2.green(`Added server: ${server3.name} [${server3.id}]`));
40309
- console.log(chalk2.dim(` ${server3.command} ${server3.args.join(" ")}`));
40827
+ const server2 = await installFromRegistry(opts.fromRegistry);
40828
+ console.log(chalk2.green(`Added server: ${server2.name} [${server2.id}]`));
40829
+ console.log(chalk2.dim(` ${server2.command} ${server2.args.join(" ")}`));
40310
40830
  closeDb();
40311
40831
  return;
40312
40832
  }
@@ -40355,7 +40875,7 @@ Server to add:`));
40355
40875
  closeDb();
40356
40876
  return;
40357
40877
  }
40358
- const server3 = addServer({
40878
+ const server2 = addServer({
40359
40879
  command: wizardCommand,
40360
40880
  args: wizardArgs,
40361
40881
  name: wizardName || undefined,
@@ -40363,7 +40883,7 @@ Server to add:`));
40363
40883
  transport,
40364
40884
  env
40365
40885
  });
40366
- console.log(chalk2.green(`Added: ${server3.name} [${server3.id}]`));
40886
+ console.log(chalk2.green(`Added: ${server2.name} [${server2.id}]`));
40367
40887
  closeDb();
40368
40888
  return;
40369
40889
  }
@@ -40391,7 +40911,7 @@ Server to add:`));
40391
40911
  envMap[key] = rest.join("=");
40392
40912
  }
40393
40913
  }
40394
- const server2 = addServer({
40914
+ const server = addServer({
40395
40915
  name: opts.name,
40396
40916
  description: opts.description,
40397
40917
  command,
@@ -40400,8 +40920,8 @@ Server to add:`));
40400
40920
  url: opts.url,
40401
40921
  env: envMap
40402
40922
  });
40403
- console.log(chalk2.green(`Added server: ${server2.name} [${server2.id}]`));
40404
- console.log(chalk2.dim(` ${server2.command} ${server2.args.join(" ")}`));
40923
+ console.log(chalk2.green(`Added server: ${server.name} [${server.id}]`));
40924
+ console.log(chalk2.dim(` ${server.command} ${server.args.join(" ")}`));
40405
40925
  } catch (err) {
40406
40926
  if (err.message?.includes("UNIQUE constraint")) {
40407
40927
  console.error(chalk2.red("A server with that ID already exists."));
@@ -40414,8 +40934,8 @@ Server to add:`));
40414
40934
  closeDb();
40415
40935
  });
40416
40936
  program2.command("update-server").argument("<id>", "Server ID to update").description("Update fields of a registered server").option("--name <name>", "New display name").option("--description <desc>", "New description").option("--command <cmd>", "New command").option("--args <args...>", "New args list").option("--transport <type>", "New transport type").option("--url <url>", "New URL").action((id, opts) => {
40417
- const server2 = getServer(id);
40418
- if (!server2) {
40937
+ const server = getServer(id);
40938
+ if (!server) {
40419
40939
  console.error(chalk2.red(`Server "${id}" not found.`));
40420
40940
  closeDb();
40421
40941
  process.exit(1);
@@ -40450,53 +40970,53 @@ program2.command("clone").argument("<id>", "Server ID to clone").argument("<new-
40450
40970
  closeDb();
40451
40971
  });
40452
40972
  program2.command("remove").argument("<id>", "Server ID to remove").description("Remove a registered server").action((id) => {
40453
- const server2 = getServer(id);
40454
- if (!server2) {
40973
+ const server = getServer(id);
40974
+ if (!server) {
40455
40975
  console.error(chalk2.red(`Server "${id}" not found.`));
40456
40976
  closeDb();
40457
40977
  process.exit(1);
40458
40978
  }
40459
40979
  removeServer(id);
40460
- console.log(chalk2.green(`Removed server: ${server2.name} [${id}]`));
40980
+ console.log(chalk2.green(`Removed server: ${server.name} [${id}]`));
40461
40981
  closeDb();
40462
40982
  });
40463
40983
  program2.command("enable").argument("<id>", "Server ID to enable").description("Enable a server").action((id) => {
40464
- const server2 = getServer(id);
40465
- if (!server2) {
40984
+ const server = getServer(id);
40985
+ if (!server) {
40466
40986
  console.error(chalk2.red(`Server "${id}" not found.`));
40467
40987
  closeDb();
40468
40988
  process.exit(1);
40469
40989
  }
40470
40990
  enableServer(id);
40471
- console.log(chalk2.green(`Enabled server: ${server2.name}`));
40991
+ console.log(chalk2.green(`Enabled server: ${server.name}`));
40472
40992
  closeDb();
40473
40993
  });
40474
40994
  program2.command("disable").argument("<id>", "Server ID to disable").description("Disable a server").action((id) => {
40475
- const server2 = getServer(id);
40476
- if (!server2) {
40995
+ const server = getServer(id);
40996
+ if (!server) {
40477
40997
  console.error(chalk2.red(`Server "${id}" not found.`));
40478
40998
  closeDb();
40479
40999
  process.exit(1);
40480
41000
  }
40481
41001
  disableServer(id);
40482
- console.log(chalk2.yellow(`Disabled server: ${server2.name}`));
41002
+ console.log(chalk2.yellow(`Disabled server: ${server.name}`));
40483
41003
  closeDb();
40484
41004
  });
40485
41005
  program2.command("tools").argument("[server-id]", "Optional server ID to filter by").description("List tools (all or per server)").option("--connect", "Connect to servers to fetch live tools").action(async (serverId, opts) => {
40486
41006
  if (opts.connect) {
40487
41007
  console.log(chalk2.dim("Connecting to enabled servers..."));
40488
41008
  await connectAllEnabled();
40489
- const tools = listAllTools();
40490
- if (tools.length === 0) {
41009
+ const tools2 = listAllTools();
41010
+ if (tools2.length === 0) {
40491
41011
  console.log(chalk2.dim("No tools available."));
40492
41012
  } else {
40493
- for (const t of tools) {
41013
+ for (const t of tools2) {
40494
41014
  console.log(` ${chalk2.bold(t.name)}`);
40495
41015
  if (t.description)
40496
41016
  console.log(` ${chalk2.dim(t.description)}`);
40497
41017
  }
40498
41018
  console.log(chalk2.dim(`
40499
- ${tools.length} tool(s) available.`));
41019
+ ${tools2.length} tool(s) available.`));
40500
41020
  }
40501
41021
  await disconnectAll();
40502
41022
  } else if (serverId) {
@@ -40576,26 +41096,26 @@ program2.command("call").argument("<tool>", "Tool name (server_id__tool_name)").
40576
41096
  process.exit(exitCode);
40577
41097
  });
40578
41098
  program2.command("info").argument("<id>", "Server ID").description("Show server details & tools").action((id) => {
40579
- const server2 = getServer(id);
40580
- if (!server2) {
41099
+ const server = getServer(id);
41100
+ if (!server) {
40581
41101
  console.error(chalk2.red(`Server "${id}" not found.`));
40582
41102
  closeDb();
40583
41103
  process.exit(1);
40584
41104
  }
40585
- console.log(chalk2.bold(server2.name) + " " + chalk2.dim(`[${server2.id}]`));
40586
- console.log(` Status: ${server2.enabled ? chalk2.green("enabled") : chalk2.red("disabled")}`);
40587
- console.log(` Source: ${server2.source}`);
40588
- console.log(` Transport: ${server2.transport}`);
40589
- console.log(` Command: ${server2.command} ${server2.args.join(" ")}`);
40590
- if (server2.url)
40591
- console.log(` URL: ${server2.url}`);
40592
- if (server2.description)
40593
- console.log(` Desc: ${server2.description}`);
40594
- if (Object.keys(server2.env).length > 0) {
40595
- console.log(` Env: ${Object.entries(server2.env).map(([k, v]) => `${k}=${v}`).join(", ")}`);
40596
- }
40597
- console.log(` Created: ${server2.created_at}`);
40598
- console.log(` Updated: ${server2.updated_at}`);
41105
+ console.log(chalk2.bold(server.name) + " " + chalk2.dim(`[${server.id}]`));
41106
+ console.log(` Status: ${server.enabled ? chalk2.green("enabled") : chalk2.red("disabled")}`);
41107
+ console.log(` Source: ${server.source}`);
41108
+ console.log(` Transport: ${server.transport}`);
41109
+ console.log(` Command: ${server.command} ${server.args.join(" ")}`);
41110
+ if (server.url)
41111
+ console.log(` URL: ${server.url}`);
41112
+ if (server.description)
41113
+ console.log(` Desc: ${server.description}`);
41114
+ if (Object.keys(server.env).length > 0) {
41115
+ console.log(` Env: ${Object.entries(server.env).map(([k, v]) => `${k}=${v}`).join(", ")}`);
41116
+ }
41117
+ console.log(` Created: ${server.created_at}`);
41118
+ console.log(` Updated: ${server.updated_at}`);
40599
41119
  const cached2 = getCachedTools(id);
40600
41120
  if (cached2.length > 0) {
40601
41121
  console.log(chalk2.bold(`
@@ -40635,10 +41155,10 @@ program2.command("doctor").argument("[id]", "Server ID to check (omit to check a
40635
41155
  return;
40636
41156
  }
40637
41157
  let allHealthy = true;
40638
- for (const server2 of servers) {
41158
+ for (const server of servers) {
40639
41159
  console.log(chalk2.bold(`
40640
- ${server2.name} [${server2.id}]`));
40641
- const report = await diagnoseServer(server2);
41160
+ ${server.name} [${server.id}]`));
41161
+ const report = await diagnoseServer(server);
40642
41162
  for (const check2 of report.checks) {
40643
41163
  const icon = check2.pass ? chalk2.green("\u2713") : chalk2.red("\u2717");
40644
41164
  console.log(` ${icon} ${check2.name}: ${chalk2.dim(check2.message)}`);
@@ -40664,7 +41184,7 @@ ${server2.name} [${server2.id}]`));
40664
41184
  closeDb();
40665
41185
  });
40666
41186
  program2.command("completion").argument("<shell>", "Shell type: bash, zsh, fish").description("Generate shell completion script").action((shell) => {
40667
- const commands = ["list", "search", "find", "add", "remove", "enable", "disable", "info", "status", "tools", "call", "doctor", "install", "machines", "fleet", "export", "import", "env", "sources", "clone", "update-server", "serve", "update", "mcp", "completion"];
41187
+ const commands = ["list", "search", "providers", "find", "add", "remove", "enable", "disable", "info", "status", "tools", "call", "doctor", "install", "machines", "fleet", "export", "import", "env", "sources", "clone", "update-server", "serve", "update", "mcp", "completion"];
40668
41188
  if (shell === "bash") {
40669
41189
  console.log(`# Add to ~/.bashrc: eval "$(mcps completion bash)"
40670
41190
  _mcps_complete() {
@@ -40834,14 +41354,14 @@ Enter number to install (1-${results.length}), or 0 to cancel: `), resolve2);
40834
41354
  return;
40835
41355
  }
40836
41356
  console.log(chalk2.dim(`Installing ${chosen.name}...`));
40837
- const server2 = addServer({
41357
+ const server = addServer({
40838
41358
  command: "npx",
40839
41359
  args: ["-y", pkg],
40840
41360
  name: chosen.name,
40841
41361
  description: chosen.description,
40842
41362
  transport: "stdio"
40843
41363
  });
40844
- const results2 = installToAgents(server2, ["claude", "codex", "gemini"]);
41364
+ const results2 = installToAgents(server, ["claude", "codex", "gemini"]);
40845
41365
  for (const r of results2) {
40846
41366
  if (r.success) {
40847
41367
  console.log(chalk2.green(` \u2713 ${r.agent}`));
@@ -40850,7 +41370,7 @@ Enter number to install (1-${results.length}), or 0 to cancel: `), resolve2);
40850
41370
  }
40851
41371
  }
40852
41372
  console.log(chalk2.green(`
40853
- Installed ${server2.name} [${server2.id}]`));
41373
+ Installed ${server.name} [${server.id}]`));
40854
41374
  }
40855
41375
  } catch (err) {
40856
41376
  console.error(chalk2.red(`Find failed: ${err.message}`));
@@ -41008,12 +41528,12 @@ program2.command("install").argument("[id]", "Server ID (from `mcps list`) to in
41008
41528
  if (!targets.includes("gemini"))
41009
41529
  targets.push("gemini");
41010
41530
  }
41011
- let server2;
41531
+ let server;
41012
41532
  if (opts.fromRegistry) {
41013
41533
  console.log(chalk2.dim(`Installing "${opts.fromRegistry}" from registry...`));
41014
41534
  try {
41015
- server2 = await installFromRegistry(opts.fromRegistry);
41016
- console.log(chalk2.green(`Added server: ${server2.name} [${server2.id}]`));
41535
+ server = await installFromRegistry(opts.fromRegistry);
41536
+ console.log(chalk2.green(`Added server: ${server.name} [${server.id}]`));
41017
41537
  } catch (err) {
41018
41538
  console.error(chalk2.red(`Failed to install from registry: ${err.message}`));
41019
41539
  closeDb();
@@ -41021,23 +41541,23 @@ program2.command("install").argument("[id]", "Server ID (from `mcps list`) to in
41021
41541
  }
41022
41542
  } else if (opts.npm) {
41023
41543
  const pkg = opts.npm;
41024
- server2 = addServer({ command: "npx", args: ["-y", pkg], name: pkg, transport: "stdio" });
41025
- console.log(chalk2.green(`Added server: ${server2.name} [${server2.id}]`));
41544
+ server = addServer({ command: "npx", args: ["-y", pkg], name: pkg, transport: "stdio" });
41545
+ console.log(chalk2.green(`Added server: ${server.name} [${server.id}]`));
41026
41546
  } else {
41027
41547
  if (!id) {
41028
41548
  console.error(chalk2.red("Error: server ID is required (or use --from-registry or --npm)"));
41029
41549
  closeDb();
41030
41550
  process.exit(1);
41031
41551
  }
41032
- server2 = getServer(id);
41033
- if (!server2) {
41552
+ server = getServer(id);
41553
+ if (!server) {
41034
41554
  console.error(chalk2.red(`Server "${id}" not found. Use \`mcps add\` first.`));
41035
41555
  closeDb();
41036
41556
  process.exit(1);
41037
41557
  }
41038
41558
  }
41039
- console.log(chalk2.dim(`Installing "${server2.name}" to: ${targets.join(", ")}...`));
41040
- const results = installToAgents(server2, targets);
41559
+ console.log(chalk2.dim(`Installing "${server.name}" to: ${targets.join(", ")}...`));
41560
+ const results = installToAgents(server, targets);
41041
41561
  for (const r of results) {
41042
41562
  if (r.success) {
41043
41563
  console.log(chalk2.green(` \u2713 ${r.agent}`));
@@ -41280,13 +41800,13 @@ program2.command("import").argument("<file>", "Path to the export JSON file").de
41280
41800
  });
41281
41801
  var envCmd = program2.command("env").description("Manage server environment variables");
41282
41802
  envCmd.command("list").argument("<id>").description("List env vars for a server").action((id) => {
41283
- const server2 = getServer(id);
41284
- if (!server2) {
41803
+ const server = getServer(id);
41804
+ if (!server) {
41285
41805
  console.error(chalk2.red(`Server "${id}" not found.`));
41286
41806
  closeDb();
41287
41807
  process.exit(1);
41288
41808
  }
41289
- const entries = Object.entries(server2.env);
41809
+ const entries = Object.entries(server.env);
41290
41810
  if (entries.length === 0) {
41291
41811
  console.log(chalk2.dim("No env vars set."));
41292
41812
  closeDb();
@@ -41331,7 +41851,7 @@ program2.command("mcp").description("Start meta-MCP server (stdio)").action(asyn
41331
41851
  });
41332
41852
  program2.command("feedback <message>").description("Send feedback").option("--email <email>", "Contact email").option("--category <category>", "Category: bug, feature, general").action((message, opts) => {
41333
41853
  const adapter = getAdapter();
41334
- adapter.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", message, opts.email || null, opts.category || "general", VERSION2);
41854
+ adapter.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", message, opts.email || null, opts.category || "general", VERSION3);
41335
41855
  console.log(chalk2.green("Feedback saved. Thank you!"));
41336
41856
  closeDb();
41337
41857
  });