@hasna/mcps 0.0.13 → 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,12 +19412,12 @@ 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.13",
19415
+ version: "0.0.14",
19292
19416
  description: "Meta-MCP registry & CLI \u2014 discover, manage, and proxy MCP servers",
19293
19417
  type: "module",
19294
19418
  repository: {
19295
19419
  type: "git",
19296
- url: "https://github.com/hasna/mcps.git"
19420
+ url: "git+https://github.com/hasna/mcps.git"
19297
19421
  },
19298
19422
  main: "dist/index.js",
19299
19423
  types: "dist/index.d.ts",
@@ -36767,6 +36891,104 @@ async function runFleetInstall(options = {}, dependencies = {}) {
36767
36891
  }));
36768
36892
  }
36769
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
+
36770
36992
  // src/cli/index.tsx
36771
36993
  import * as readline from "readline";
36772
36994
 
@@ -38186,6 +38408,55 @@ function buildMcpTools() {
38186
38408
  limit: typeof limit === "number" ? limit : undefined
38187
38409
  }))
38188
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
+ },
38189
38460
  {
38190
38461
  name: "list_sources",
38191
38462
  description: "List all configured search sources for finding MCP servers",
@@ -40266,6 +40537,24 @@ var MACHINE_PLATFORMS = ["linux", "darwin", "unknown"];
40266
40537
  var MACHINE_ARCHES = ["arm64", "x64", "unknown"];
40267
40538
  var MACHINE_INSTALLERS = ["auto", "bun", "npm"];
40268
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
+ }
40269
40558
  function printJson(value) {
40270
40559
  console.log(JSON.stringify(value, null, 2));
40271
40560
  }
@@ -40438,6 +40727,85 @@ ${results.length} result(s). Use \`mcps add --from-registry <id>\` to install.`)
40438
40727
  closeDb();
40439
40728
  }
40440
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
+ });
40441
40809
  async function promptReadline(rl, question) {
40442
40810
  return new Promise((resolve2) => rl.question(question, resolve2));
40443
40811
  }
@@ -40816,7 +41184,7 @@ ${server.name} [${server.id}]`));
40816
41184
  closeDb();
40817
41185
  });
40818
41186
  program2.command("completion").argument("<shell>", "Shell type: bash, zsh, fish").description("Generate shell completion script").action((shell) => {
40819
- 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"];
40820
41188
  if (shell === "bash") {
40821
41189
  console.log(`# Add to ~/.bashrc: eval "$(mcps completion bash)"
40822
41190
  _mcps_complete() {