@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/mcp.js CHANGED
@@ -16577,6 +16577,85 @@ var init_config2 = __esm(() => {
16577
16577
  DB_PATH = process.env.HASNA_MCPS_DB_PATH ?? process.env.MCPS_DB_PATH ?? join7(MCPS_DIR, "registry.db");
16578
16578
  });
16579
16579
 
16580
+ // src/lib/provider-profile-seeds.ts
16581
+ var DEFAULT_PROVIDER_PROFILE_SEEDS;
16582
+ var init_provider_profile_seeds = __esm(() => {
16583
+ DEFAULT_PROVIDER_PROFILE_SEEDS = [
16584
+ {
16585
+ id: "notion",
16586
+ displayName: "Notion",
16587
+ description: "Connect a Notion workspace so agents can search, read, create, and update workspace content.",
16588
+ endpoint: "https://mcp.notion.com/mcp",
16589
+ transport: "streamable-http",
16590
+ fallbackEndpoints: [
16591
+ {
16592
+ transport: "sse",
16593
+ url: "https://mcp.notion.com/sse",
16594
+ notes: "Fallback for clients that do not support Streamable HTTP."
16595
+ }
16596
+ ],
16597
+ authType: "oauth2",
16598
+ authMetadata: {
16599
+ oauthVersion: "2.0",
16600
+ pkce: true,
16601
+ dynamicClientRegistration: true,
16602
+ bearerToken: "none",
16603
+ notes: "Remote Notion MCP uses OAuth with PKCE. Bearer-token authentication is only appropriate for self-hosted/local fallback deployments."
16604
+ },
16605
+ tokenMode: "workspace",
16606
+ installFallback: {
16607
+ command: "npx",
16608
+ args: ["-y", "mcp-remote", "https://mcp.notion.com/sse", "--transport", "sse-only"],
16609
+ packageName: "mcp-remote",
16610
+ url: "https://mcp.notion.com/sse"
16611
+ },
16612
+ docsUrl: "https://developers.notion.com/guides/mcp/build-mcp-client",
16613
+ safety: {
16614
+ requiresApproval: true,
16615
+ dataClasses: ["workspace_content", "pages", "databases", "comments"],
16616
+ notes: "Connected agents operate with the authorizing user's workspace access. Human confirmation is recommended for write-capable workflows."
16617
+ },
16618
+ provenance: {
16619
+ source: "curated",
16620
+ sourceUrl: "https://developers.notion.com/guides/mcp/build-mcp-client",
16621
+ verifiedAt: "2026-05-10"
16622
+ }
16623
+ },
16624
+ {
16625
+ id: "linear",
16626
+ displayName: "Linear",
16627
+ description: "Connect Linear so agents can find, create, and update issues, projects, comments, and related workspace objects.",
16628
+ endpoint: "https://mcp.linear.app/mcp",
16629
+ transport: "streamable-http",
16630
+ authType: "oauth2",
16631
+ authMetadata: {
16632
+ oauthVersion: "2.1",
16633
+ dynamicClientRegistration: true,
16634
+ bearerToken: "optional",
16635
+ notes: "Linear supports interactive OAuth 2.1 with dynamic client registration and optional Authorization: Bearer tokens for OAuth tokens or API keys."
16636
+ },
16637
+ tokenMode: "workspace",
16638
+ installFallback: {
16639
+ command: "npx",
16640
+ args: ["-y", "mcp-remote", "https://mcp.linear.app/mcp"],
16641
+ packageName: "mcp-remote",
16642
+ url: "https://mcp.linear.app/mcp"
16643
+ },
16644
+ docsUrl: "https://linear.app/docs/mcp",
16645
+ safety: {
16646
+ requiresApproval: true,
16647
+ dataClasses: ["issues", "projects", "comments", "teams", "users"],
16648
+ notes: "Linear tools can create and update workspace objects, so write actions should be policy-gated by the platform."
16649
+ },
16650
+ provenance: {
16651
+ source: "curated",
16652
+ sourceUrl: "https://linear.app/docs/mcp",
16653
+ verifiedAt: "2026-05-10"
16654
+ }
16655
+ }
16656
+ ];
16657
+ });
16658
+
16580
16659
  // src/lib/db.ts
16581
16660
  import { mkdirSync as mkdirSync5 } from "fs";
16582
16661
  function getDb() {
@@ -16662,6 +16741,50 @@ function getDb() {
16662
16741
  ('github-mcp-topic', 'GitHub MCP Topic', 'github-topic', 'https://api.github.com/search/repositories', 'GitHub repositories tagged with mcp-server topic')
16663
16742
  `);
16664
16743
  }
16744
+ db.exec(`
16745
+ CREATE TABLE IF NOT EXISTS provider_profiles (
16746
+ id TEXT PRIMARY KEY,
16747
+ display_name TEXT NOT NULL,
16748
+ description TEXT,
16749
+ endpoint TEXT,
16750
+ transport TEXT NOT NULL,
16751
+ fallback_endpoints TEXT NOT NULL DEFAULT '[]',
16752
+ auth_type TEXT NOT NULL,
16753
+ auth_metadata TEXT NOT NULL DEFAULT '{}',
16754
+ scopes TEXT NOT NULL DEFAULT '[]',
16755
+ token_mode TEXT NOT NULL DEFAULT 'none',
16756
+ install_fallback TEXT NOT NULL DEFAULT '{}',
16757
+ docs_url TEXT,
16758
+ safety TEXT NOT NULL DEFAULT '{}',
16759
+ provenance TEXT NOT NULL DEFAULT '{"source":"manual"}',
16760
+ enabled INTEGER NOT NULL DEFAULT 1,
16761
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
16762
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
16763
+ )
16764
+ `);
16765
+ try {
16766
+ db.exec("ALTER TABLE provider_profiles ADD COLUMN fallback_endpoints TEXT NOT NULL DEFAULT '[]'");
16767
+ } catch {}
16768
+ try {
16769
+ db.exec("ALTER TABLE provider_profiles ADD COLUMN auth_metadata TEXT NOT NULL DEFAULT '{}'");
16770
+ } catch {}
16771
+ db.exec("CREATE INDEX IF NOT EXISTS idx_provider_profiles_enabled ON provider_profiles(enabled)");
16772
+ const providerProfileCount = db.query("SELECT COUNT(*) as c FROM provider_profiles").get().c;
16773
+ if (providerProfileCount === 0) {
16774
+ const insertProviderProfile = db.prepare(`
16775
+ INSERT OR IGNORE INTO provider_profiles (
16776
+ id, display_name, description, endpoint, transport, fallback_endpoints,
16777
+ auth_type, auth_metadata, scopes, token_mode, install_fallback,
16778
+ docs_url, safety, provenance, enabled
16779
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
16780
+ `);
16781
+ const run = db.transaction(() => {
16782
+ for (const profile of DEFAULT_PROVIDER_PROFILE_SEEDS) {
16783
+ 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);
16784
+ }
16785
+ });
16786
+ run();
16787
+ }
16665
16788
  db.exec(`
16666
16789
  CREATE TABLE IF NOT EXISTS feedback (
16667
16790
  id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
@@ -16685,6 +16808,7 @@ var db = null, _adapter = null;
16685
16808
  var init_db = __esm(() => {
16686
16809
  init_dist();
16687
16810
  init_config2();
16811
+ init_provider_profile_seeds();
16688
16812
  });
16689
16813
 
16690
16814
  // src/lib/sources.ts
@@ -34395,6 +34519,104 @@ async function runFleetInstall(options = {}, dependencies = {}) {
34395
34519
  }));
34396
34520
  }
34397
34521
 
34522
+ // src/lib/provider-profiles.ts
34523
+ init_db();
34524
+ init_provider_profile_seeds();
34525
+ var TRANSPORTS = new Set(["stdio", "sse", "streamable-http"]);
34526
+ var AUTH_TYPES = new Set(["none", "oauth2", "api_key", "bearer_token", "custom"]);
34527
+ var TOKEN_MODES = new Set(["none", "user", "workspace", "service"]);
34528
+ var BEARER_TOKEN_MODES = new Set(["none", "optional", "required"]);
34529
+ var PROVENANCE_SOURCES = new Set(["curated", "official-registry", "npm", "github", "manual"]);
34530
+ function safeJsonParse2(value, fallback) {
34531
+ if (typeof value !== "string")
34532
+ return fallback;
34533
+ try {
34534
+ return JSON.parse(value);
34535
+ } catch {
34536
+ return fallback;
34537
+ }
34538
+ }
34539
+ function normalizeId(id) {
34540
+ const normalized = id.trim().toLowerCase();
34541
+ if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/.test(normalized)) {
34542
+ throw new Error("Provider profile id must be lowercase kebab-case");
34543
+ }
34544
+ return normalized;
34545
+ }
34546
+ function parseRow3(row) {
34547
+ const installFallback = safeJsonParse2(row.install_fallback, null);
34548
+ return {
34549
+ id: row.id,
34550
+ displayName: row.display_name,
34551
+ description: row.description || null,
34552
+ endpoint: row.endpoint || null,
34553
+ transport: row.transport,
34554
+ fallbackEndpoints: safeJsonParse2(row.fallback_endpoints, []),
34555
+ authType: row.auth_type,
34556
+ authMetadata: safeJsonParse2(row.auth_metadata, {}),
34557
+ scopes: safeJsonParse2(row.scopes, []),
34558
+ tokenMode: row.token_mode,
34559
+ installFallback,
34560
+ docsUrl: row.docs_url || null,
34561
+ safety: safeJsonParse2(row.safety, {}),
34562
+ provenance: safeJsonParse2(row.provenance, { source: "manual" }),
34563
+ enabled: row.enabled === 1 || row.enabled === true,
34564
+ created_at: row.created_at,
34565
+ updated_at: row.updated_at
34566
+ };
34567
+ }
34568
+ function listProviderProfiles(options = {}) {
34569
+ const db2 = getDb();
34570
+ const sql = options.enabledOnly ? "SELECT * FROM provider_profiles WHERE enabled = 1 ORDER BY display_name" : "SELECT * FROM provider_profiles ORDER BY display_name";
34571
+ return db2.prepare(sql).all().map(parseRow3);
34572
+ }
34573
+ function searchProviderProfiles(query, options = {}) {
34574
+ const normalizedQuery = query.trim().toLowerCase();
34575
+ if (!normalizedQuery)
34576
+ return listProviderProfiles(options);
34577
+ return listProviderProfiles(options).filter((profile) => {
34578
+ const searchable = [
34579
+ profile.id,
34580
+ profile.displayName,
34581
+ profile.description ?? "",
34582
+ profile.endpoint ?? "",
34583
+ profile.docsUrl ?? "",
34584
+ profile.provenance.sourceUrl ?? "",
34585
+ profile.provenance.packageName ?? ""
34586
+ ].join(`
34587
+ `).toLowerCase();
34588
+ return searchable.includes(normalizedQuery);
34589
+ });
34590
+ }
34591
+ function getProviderProfile(id) {
34592
+ const db2 = getDb();
34593
+ const row = db2.prepare("SELECT * FROM provider_profiles WHERE id = ?").get(normalizeId(id));
34594
+ return row ? parseRow3(row) : null;
34595
+ }
34596
+ function installProviderProfile(id, options = {}) {
34597
+ const profile = getProviderProfile(id);
34598
+ if (!profile)
34599
+ throw new Error(`Provider profile "${id}" not found`);
34600
+ if (!profile.enabled)
34601
+ throw new Error(`Provider profile "${id}" is disabled`);
34602
+ const fallback = profile.installFallback;
34603
+ const useFallback = options.useFallback || !profile.endpoint;
34604
+ const command = useFallback ? fallback?.command : fallback?.command ?? "npx";
34605
+ const args = useFallback ? fallback?.args ?? [] : fallback?.args ?? [];
34606
+ if (!command) {
34607
+ throw new Error(`Provider profile "${id}" does not define an install fallback command`);
34608
+ }
34609
+ return addServer({
34610
+ name: options.name ?? profile.displayName,
34611
+ description: profile.description ?? undefined,
34612
+ command,
34613
+ args,
34614
+ transport: useFallback ? "stdio" : profile.transport,
34615
+ url: useFallback ? fallback?.url : profile.endpoint ?? undefined,
34616
+ source: "provider-profile"
34617
+ });
34618
+ }
34619
+
34398
34620
  // src/mcp/tools.ts
34399
34621
  var VERSION = readPackageVersion(import.meta.url);
34400
34622
  var mcpsAgents = new Map;
@@ -34561,6 +34783,55 @@ function buildMcpTools() {
34561
34783
  limit: typeof limit === "number" ? limit : undefined
34562
34784
  }))
34563
34785
  },
34786
+ {
34787
+ name: "list_provider_profiles",
34788
+ description: "List curated provider profiles for hosted/common MCP integrations such as Notion and Linear.",
34789
+ paramsSchema: {
34790
+ enabled_only: exports_external2.boolean().optional().describe("Only include enabled provider profiles")
34791
+ },
34792
+ run: ({ enabled_only }) => jsonContent(listProviderProfiles({ enabledOnly: enabled_only === true }))
34793
+ },
34794
+ {
34795
+ name: "search_provider_profiles",
34796
+ description: "Search curated provider profiles separately from raw MCP registry/source search.",
34797
+ paramsSchema: {
34798
+ query: exports_external2.string().describe("Search query such as 'notion', 'linear', or an endpoint URL"),
34799
+ enabled_only: exports_external2.boolean().optional().describe("Only include enabled provider profiles")
34800
+ },
34801
+ run: ({ query, enabled_only }) => jsonContent(searchProviderProfiles(String(query), { enabledOnly: enabled_only === true }))
34802
+ },
34803
+ {
34804
+ name: "get_provider_profile",
34805
+ description: "Get one curated provider profile by ID.",
34806
+ paramsSchema: {
34807
+ id: exports_external2.string().describe("Provider profile ID")
34808
+ },
34809
+ run: ({ id }) => {
34810
+ const profile = getProviderProfile(String(id));
34811
+ if (!profile)
34812
+ return errorContent(`Provider profile "${String(id)}" not found.`);
34813
+ return jsonContent(profile);
34814
+ }
34815
+ },
34816
+ {
34817
+ name: "install_provider_profile",
34818
+ description: "Register a curated provider profile as an MCP server.",
34819
+ paramsSchema: {
34820
+ id: exports_external2.string().describe("Provider profile ID"),
34821
+ name: exports_external2.string().optional().describe("Override registered server name"),
34822
+ use_fallback: exports_external2.boolean().optional().describe("Install the stdio fallback command instead of the direct remote transport")
34823
+ },
34824
+ run: ({ id, name, use_fallback }) => {
34825
+ try {
34826
+ return jsonContent(redactServerEnv(installProviderProfile(String(id), {
34827
+ name: typeof name === "string" ? name : undefined,
34828
+ useFallback: use_fallback === true
34829
+ })));
34830
+ } catch (err) {
34831
+ return errorContent(err.message);
34832
+ }
34833
+ }
34834
+ },
34564
34835
  {
34565
34836
  name: "list_sources",
34566
34837
  description: "List all configured search sources for finding MCP servers",
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { McpServerEntry, AddServerOptions, McpTool, RegistryServer, ConnectedServer, FinderResult, MachineEntry, AddMachineOptions, MachinePlatform, MachineArch, MachineInstaller, HasnaMcpCatalogEntry, MachinePackageHealth, FleetHealthReport, FleetInstallPackageResult, FleetInstallReport, } from "./types.js";
1
+ export type { McpServerEntry, AddServerOptions, McpTool, RegistryServer, ConnectedServer, FinderResult, MachineEntry, AddMachineOptions, MachinePlatform, MachineArch, MachineInstaller, HasnaMcpCatalogEntry, MachinePackageHealth, FleetHealthReport, FleetInstallPackageResult, FleetInstallReport, ProviderAuthMetadata, ProviderEndpointFallback, ProviderInstallFallback, ProviderProfile, ProviderProfileAuthType, ProviderProfileBearerTokenMode, ProviderProfileSource, ProviderProfileTokenMode, ProviderProfileTransport, ProviderSafetyMetadata, ProviderSourceProvenance, InstallProviderProfileOptions, UpsertProviderProfileOptions, } from "./types.js";
2
2
  export { addServer, removeServer, listServers, getServer, updateServer, enableServer, disableServer, getToolCounts, getCachedTools, setServerEnv, unsetServerEnv, cloneServer, } from "./lib/registry.js";
3
3
  export { diagnoseServer } from "./lib/doctor.js";
4
4
  export type { DoctorReport, DoctorCheck } from "./lib/doctor.js";
@@ -6,6 +6,8 @@ export { searchRegistry, getRegistryServer, installFromRegistry } from "./lib/re
6
6
  export { findServers, listAwesomeServers } from "./lib/finder.js";
7
7
  export type { FindOptions } from "./lib/finder.js";
8
8
  export { listSources, getSource, addSource, removeSource, enableSource, disableSource, } from "./lib/sources.js";
9
+ export { upsertProviderProfile, listProviderProfiles, searchProviderProfiles, getProviderProfile, installProviderProfile, removeProviderProfile, enableProviderProfile, disableProviderProfile, seedDefaultProviderProfiles, } from "./lib/provider-profiles.js";
10
+ export { DEFAULT_PROVIDER_PROFILE_SEEDS } from "./lib/provider-profile-seeds.js";
9
11
  export { installToAgents } from "./lib/install.js";
10
12
  export type { AgentTarget, InstallResult } from "./lib/install.js";
11
13
  export type { McpSource, AddSourceOptions } from "./types.js";