@hasna/mcps 0.0.15 → 0.0.17

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
@@ -16618,7 +16618,7 @@ var init_provider_profile_seeds = __esm(() => {
16618
16618
  provenance: {
16619
16619
  source: "curated",
16620
16620
  sourceUrl: "https://developers.notion.com/guides/mcp/build-mcp-client",
16621
- verifiedAt: "2026-05-10"
16621
+ verifiedAt: "2026-05-11"
16622
16622
  }
16623
16623
  },
16624
16624
  {
@@ -16650,7 +16650,384 @@ var init_provider_profile_seeds = __esm(() => {
16650
16650
  provenance: {
16651
16651
  source: "curated",
16652
16652
  sourceUrl: "https://linear.app/docs/mcp",
16653
- verifiedAt: "2026-05-10"
16653
+ verifiedAt: "2026-05-11"
16654
+ }
16655
+ },
16656
+ {
16657
+ id: "github",
16658
+ displayName: "GitHub",
16659
+ description: "Connect GitHub so agents can inspect repositories, manage issues and pull requests, analyze Actions runs, and work with project metadata.",
16660
+ endpoint: "https://api.githubcopilot.com/mcp/",
16661
+ transport: "streamable-http",
16662
+ authType: "oauth2",
16663
+ authMetadata: {
16664
+ oauthVersion: "2.0",
16665
+ pkce: true,
16666
+ dynamicClientRegistration: false,
16667
+ bearerToken: "optional",
16668
+ notes: "GitHub hosts the remote server. OAuth requires host-side GitHub App/OAuth App setup; clients that cannot complete OAuth can use an Authorization bearer GitHub PAT."
16669
+ },
16670
+ scopes: ["repo", "read:org", "read:user", "user:email", "workflow", "notifications", "project"],
16671
+ tokenMode: "user",
16672
+ installFallback: {
16673
+ command: "docker",
16674
+ args: ["run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "ghcr.io/github/github-mcp-server"],
16675
+ env: { GITHUB_PERSONAL_ACCESS_TOKEN: "GITHUB_PERSONAL_ACCESS_TOKEN" },
16676
+ packageName: "ghcr.io/github/github-mcp-server",
16677
+ url: "https://api.githubcopilot.com/mcp/"
16678
+ },
16679
+ docsUrl: "https://github.com/github/github-mcp-server",
16680
+ safety: {
16681
+ requiresApproval: true,
16682
+ sensitiveScopes: ["repo", "workflow", "project"],
16683
+ dataClasses: ["repositories", "issues", "pull_requests", "actions", "security_alerts", "organization_members"],
16684
+ notes: "GitHub tools can read private source and mutate repository objects. Prefer least-privilege OAuth scopes or fine-grained PATs and approval-gate write operations."
16685
+ },
16686
+ provenance: {
16687
+ source: "curated",
16688
+ sourceUrl: "https://github.com/github/github-mcp-server",
16689
+ repositoryUrl: "https://github.com/github/github-mcp-server",
16690
+ packageName: "ghcr.io/github/github-mcp-server",
16691
+ verifiedAt: "2026-05-11"
16692
+ }
16693
+ },
16694
+ {
16695
+ id: "slack",
16696
+ displayName: "Slack",
16697
+ description: "Connect Slack so agents can search workspace messages and files, inspect conversations, read user context, and draft or send approved messages.",
16698
+ endpoint: "https://mcp.slack.com/mcp",
16699
+ transport: "streamable-http",
16700
+ authType: "oauth2",
16701
+ authMetadata: {
16702
+ oauthVersion: "2.0",
16703
+ pkce: true,
16704
+ dynamicClientRegistration: false,
16705
+ bearerToken: "none",
16706
+ notes: "Slack's remote MCP endpoint uses Streamable HTTP, requires a registered Slack app identity, and does not support SSE or dynamic client registration."
16707
+ },
16708
+ scopes: [
16709
+ "search:read.public",
16710
+ "search:read.private",
16711
+ "search:read.mpim",
16712
+ "search:read.im",
16713
+ "search:read.files",
16714
+ "search:read.users",
16715
+ "channels:history",
16716
+ "groups:history",
16717
+ "mpim:history",
16718
+ "im:history",
16719
+ "chat:write",
16720
+ "canvases:read",
16721
+ "canvases:write",
16722
+ "users:read",
16723
+ "users:read.email"
16724
+ ],
16725
+ tokenMode: "user",
16726
+ docsUrl: "https://docs.slack.dev/ai/slack-mcp-server/",
16727
+ safety: {
16728
+ requiresApproval: true,
16729
+ sensitiveScopes: ["chat:write", "canvases:write", "users:read.email"],
16730
+ dataClasses: ["messages", "files", "channels", "users", "canvases"],
16731
+ notes: "Slack data often includes confidential team communication. Search/read access should stay tenant-scoped and message/canvas writes should require explicit approval."
16732
+ },
16733
+ provenance: {
16734
+ source: "curated",
16735
+ sourceUrl: "https://docs.slack.dev/ai/slack-mcp-server/",
16736
+ verifiedAt: "2026-05-11"
16737
+ }
16738
+ },
16739
+ {
16740
+ id: "gmail",
16741
+ displayName: "Gmail",
16742
+ description: "Connect Gmail through the open Workspace MCP server for mail search, message retrieval, drafts, labels, filters, and approved send workflows.",
16743
+ transport: "stdio",
16744
+ authType: "oauth2",
16745
+ authMetadata: {
16746
+ oauthVersion: "2.1",
16747
+ pkce: true,
16748
+ dynamicClientRegistration: false,
16749
+ bearerToken: "optional",
16750
+ notes: "Workspace MCP is an independent open-source Google Workspace server. Configure your own Google OAuth client and storage backend for Gmail access."
16751
+ },
16752
+ scopes: [
16753
+ "https://www.googleapis.com/auth/gmail.readonly",
16754
+ "https://www.googleapis.com/auth/gmail.modify",
16755
+ "https://www.googleapis.com/auth/gmail.compose",
16756
+ "https://www.googleapis.com/auth/gmail.send"
16757
+ ],
16758
+ tokenMode: "user",
16759
+ installFallback: {
16760
+ command: "uvx",
16761
+ args: ["workspace-mcp"],
16762
+ env: { WORKSPACE_MCP_PERMISSIONS: "gmail:send" },
16763
+ packageName: "workspace-mcp",
16764
+ url: "http://127.0.0.1:8000/mcp"
16765
+ },
16766
+ docsUrl: "https://workspacemcp.com/docs",
16767
+ safety: {
16768
+ requiresApproval: true,
16769
+ sensitiveScopes: [
16770
+ "https://www.googleapis.com/auth/gmail.modify",
16771
+ "https://www.googleapis.com/auth/gmail.compose",
16772
+ "https://www.googleapis.com/auth/gmail.send"
16773
+ ],
16774
+ dataClasses: ["email_messages", "threads", "attachments", "labels", "contacts"],
16775
+ notes: "Email content and sending are high-impact actions. Prefer read-only permissions until a user intentionally enables drafts or sends."
16776
+ },
16777
+ provenance: {
16778
+ source: "github",
16779
+ sourceUrl: "https://workspacemcp.com/docs",
16780
+ repositoryUrl: "https://github.com/taylorwilsdon/google_workspace_mcp",
16781
+ packageName: "workspace-mcp",
16782
+ verifiedAt: "2026-05-11"
16783
+ }
16784
+ },
16785
+ {
16786
+ id: "google-drive",
16787
+ displayName: "Google Drive",
16788
+ description: "Connect Google Drive through the open Workspace MCP server for file search, folder navigation, content reads, and approved file operations.",
16789
+ transport: "stdio",
16790
+ authType: "oauth2",
16791
+ authMetadata: {
16792
+ oauthVersion: "2.1",
16793
+ pkce: true,
16794
+ dynamicClientRegistration: false,
16795
+ bearerToken: "optional",
16796
+ notes: "Workspace MCP is an independent open-source Google Workspace server. Configure your own Google OAuth client and storage backend for Drive access."
16797
+ },
16798
+ scopes: [
16799
+ "https://www.googleapis.com/auth/drive.readonly",
16800
+ "https://www.googleapis.com/auth/drive.file",
16801
+ "https://www.googleapis.com/auth/drive"
16802
+ ],
16803
+ tokenMode: "user",
16804
+ installFallback: {
16805
+ command: "uvx",
16806
+ args: ["workspace-mcp"],
16807
+ env: { WORKSPACE_MCP_PERMISSIONS: "drive:readonly" },
16808
+ packageName: "workspace-mcp",
16809
+ url: "http://127.0.0.1:8000/mcp"
16810
+ },
16811
+ docsUrl: "https://workspacemcp.com/docs",
16812
+ safety: {
16813
+ requiresApproval: true,
16814
+ sensitiveScopes: [
16815
+ "https://www.googleapis.com/auth/drive.file",
16816
+ "https://www.googleapis.com/auth/drive"
16817
+ ],
16818
+ dataClasses: ["drive_files", "folders", "documents", "spreadsheets", "attachments", "sharing_permissions"],
16819
+ notes: "Drive access can expose broad business documents. Keep tenant allowlists and approval-gate creation, movement, and permission changes."
16820
+ },
16821
+ provenance: {
16822
+ source: "github",
16823
+ sourceUrl: "https://workspacemcp.com/docs",
16824
+ repositoryUrl: "https://github.com/taylorwilsdon/google_workspace_mcp",
16825
+ packageName: "workspace-mcp",
16826
+ verifiedAt: "2026-05-11"
16827
+ }
16828
+ },
16829
+ {
16830
+ id: "google-calendar",
16831
+ displayName: "Google Calendar",
16832
+ description: "Connect Google Calendar through the open Workspace MCP server for calendar discovery, event reads, and approved scheduling changes.",
16833
+ transport: "stdio",
16834
+ authType: "oauth2",
16835
+ authMetadata: {
16836
+ oauthVersion: "2.1",
16837
+ pkce: true,
16838
+ dynamicClientRegistration: false,
16839
+ bearerToken: "optional",
16840
+ notes: "Workspace MCP is an independent open-source Google Workspace server. Configure your own Google OAuth client and storage backend for Calendar access."
16841
+ },
16842
+ scopes: [
16843
+ "https://www.googleapis.com/auth/calendar.readonly",
16844
+ "https://www.googleapis.com/auth/calendar.events",
16845
+ "https://www.googleapis.com/auth/calendar"
16846
+ ],
16847
+ tokenMode: "user",
16848
+ installFallback: {
16849
+ command: "uvx",
16850
+ args: ["workspace-mcp"],
16851
+ env: { WORKSPACE_MCP_PERMISSIONS: "calendar:readonly" },
16852
+ packageName: "workspace-mcp",
16853
+ url: "http://127.0.0.1:8000/mcp"
16854
+ },
16855
+ docsUrl: "https://workspacemcp.com/docs",
16856
+ safety: {
16857
+ requiresApproval: true,
16858
+ sensitiveScopes: [
16859
+ "https://www.googleapis.com/auth/calendar.events",
16860
+ "https://www.googleapis.com/auth/calendar"
16861
+ ],
16862
+ dataClasses: ["calendars", "events", "attendees", "attachments", "availability"],
16863
+ notes: "Calendar writes can invite people, reveal availability, and alter operational schedules. Gate creates, updates, and deletes with user approval."
16864
+ },
16865
+ provenance: {
16866
+ source: "github",
16867
+ sourceUrl: "https://workspacemcp.com/docs",
16868
+ repositoryUrl: "https://github.com/taylorwilsdon/google_workspace_mcp",
16869
+ packageName: "workspace-mcp",
16870
+ verifiedAt: "2026-05-11"
16871
+ }
16872
+ },
16873
+ {
16874
+ id: "stripe",
16875
+ displayName: "Stripe",
16876
+ description: "Connect Stripe so agents can inspect accounts, balances, customers, prices, subscriptions, invoices, disputes, and perform approved Stripe API operations.",
16877
+ endpoint: "https://mcp.stripe.com",
16878
+ transport: "streamable-http",
16879
+ authType: "oauth2",
16880
+ authMetadata: {
16881
+ oauthVersion: "2.0",
16882
+ pkce: true,
16883
+ dynamicClientRegistration: false,
16884
+ bearerToken: "optional",
16885
+ notes: "Stripe supports OAuth MCP sessions and restricted API keys as Authorization bearer tokens for clients that do not support OAuth."
16886
+ },
16887
+ tokenMode: "workspace",
16888
+ installFallback: {
16889
+ command: "npx",
16890
+ args: ["-y", "@stripe/mcp"],
16891
+ env: { STRIPE_SECRET_KEY: "STRIPE_SECRET_KEY" },
16892
+ packageName: "@stripe/mcp",
16893
+ url: "https://mcp.stripe.com"
16894
+ },
16895
+ docsUrl: "https://docs.stripe.com/mcp",
16896
+ safety: {
16897
+ requiresApproval: true,
16898
+ sensitiveScopes: ["restricted_api_key", "live_mode"],
16899
+ dataClasses: ["payments", "customers", "subscriptions", "invoices", "disputes", "account_balances"],
16900
+ notes: "Use restricted keys, separate sandbox/live mode, and require approval for any operation that creates, updates, refunds, cancels, or exposes customer data."
16901
+ },
16902
+ provenance: {
16903
+ source: "curated",
16904
+ sourceUrl: "https://docs.stripe.com/mcp",
16905
+ repositoryUrl: "https://github.com/stripe/ai",
16906
+ packageName: "@stripe/mcp",
16907
+ verifiedAt: "2026-05-11"
16908
+ }
16909
+ },
16910
+ {
16911
+ id: "cloudflare",
16912
+ displayName: "Cloudflare",
16913
+ description: "Connect Cloudflare's remote MCP server for account, zone, developer platform, logs, analytics, and approved infrastructure operations.",
16914
+ endpoint: "https://mcp.cloudflare.com/mcp",
16915
+ transport: "streamable-http",
16916
+ authType: "oauth2",
16917
+ authMetadata: {
16918
+ oauthVersion: "2.0",
16919
+ pkce: true,
16920
+ dynamicClientRegistration: false,
16921
+ bearerToken: "none",
16922
+ notes: "Cloudflare's remote MCP server redirects users through Cloudflare authorization and permission selection."
16923
+ },
16924
+ tokenMode: "workspace",
16925
+ docsUrl: "https://developers.cloudflare.com/agents/model-context-protocol/mcp-servers-for-cloudflare/",
16926
+ safety: {
16927
+ requiresApproval: true,
16928
+ sensitiveScopes: ["account_admin", "zone_write", "workers_write", "dns_write"],
16929
+ dataClasses: ["accounts", "zones", "dns_records", "workers", "logs", "analytics", "security_events"],
16930
+ notes: "Cloudflare tools can affect live infrastructure. Require account scoping, least-privilege permissions, and approval for write operations."
16931
+ },
16932
+ provenance: {
16933
+ source: "curated",
16934
+ sourceUrl: "https://github.com/cloudflare/mcp",
16935
+ repositoryUrl: "https://github.com/cloudflare/mcp",
16936
+ verifiedAt: "2026-05-11"
16937
+ }
16938
+ },
16939
+ {
16940
+ id: "postgres",
16941
+ displayName: "PostgreSQL",
16942
+ description: "Connect a PostgreSQL database through the reference read-only MCP server for schema inspection and SQL query analysis.",
16943
+ transport: "stdio",
16944
+ authType: "api_key",
16945
+ authMetadata: {
16946
+ bearerToken: "none",
16947
+ notes: "The stdio server takes a PostgreSQL connection URL argument. Store the URL in a secret manager or environment variable and grant read-only database credentials."
16948
+ },
16949
+ tokenMode: "service",
16950
+ installFallback: {
16951
+ command: "npx",
16952
+ args: ["-y", "@modelcontextprotocol/server-postgres", "${POSTGRES_URL}"],
16953
+ packageName: "@modelcontextprotocol/server-postgres"
16954
+ },
16955
+ docsUrl: "https://www.npmjs.com/package/@modelcontextprotocol/server-postgres",
16956
+ safety: {
16957
+ readOnly: true,
16958
+ requiresApproval: false,
16959
+ sensitiveScopes: ["database_connection_url"],
16960
+ dataClasses: ["database_schema", "table_rows", "query_results"],
16961
+ notes: "Use read-only database users and network allowlists. Treat query results as sensitive even when the server enforces read-only transactions."
16962
+ },
16963
+ provenance: {
16964
+ source: "npm",
16965
+ sourceUrl: "https://www.npmjs.com/package/@modelcontextprotocol/server-postgres",
16966
+ repositoryUrl: "https://github.com/modelcontextprotocol/servers",
16967
+ packageName: "@modelcontextprotocol/server-postgres",
16968
+ verifiedAt: "2026-05-11"
16969
+ }
16970
+ },
16971
+ {
16972
+ id: "filesystem",
16973
+ displayName: "Filesystem",
16974
+ description: "Connect the reference filesystem MCP server for scoped local file and directory reads, writes, searches, metadata, and move operations.",
16975
+ transport: "stdio",
16976
+ authType: "none",
16977
+ authMetadata: {
16978
+ bearerToken: "none",
16979
+ notes: "Filesystem access is bounded by explicit root directories passed to the local stdio server."
16980
+ },
16981
+ tokenMode: "none",
16982
+ installFallback: {
16983
+ command: "npx",
16984
+ args: ["-y", "@modelcontextprotocol/server-filesystem", "${workspaceFolder}"],
16985
+ packageName: "@modelcontextprotocol/server-filesystem"
16986
+ },
16987
+ docsUrl: "https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem",
16988
+ safety: {
16989
+ requiresApproval: true,
16990
+ destructiveTools: ["write_file", "delete_file", "move_file", "create_directory"],
16991
+ dataClasses: ["local_files", "source_code", "documents", "directory_metadata"],
16992
+ notes: "Always constrain roots to intended project directories and approval-gate write, delete, and move operations."
16993
+ },
16994
+ provenance: {
16995
+ source: "npm",
16996
+ sourceUrl: "https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem",
16997
+ repositoryUrl: "https://github.com/modelcontextprotocol/servers",
16998
+ packageName: "@modelcontextprotocol/server-filesystem",
16999
+ verifiedAt: "2026-05-11"
17000
+ }
17001
+ },
17002
+ {
17003
+ id: "browser",
17004
+ displayName: "Browser Automation",
17005
+ description: "Connect Playwright MCP so agents can navigate pages, inspect accessibility snapshots, fill forms, capture screenshots, and run browser automation.",
17006
+ transport: "stdio",
17007
+ authType: "none",
17008
+ authMetadata: {
17009
+ bearerToken: "none",
17010
+ notes: "Playwright MCP runs browser automation locally and may reuse browser profile state unless configured for isolation."
17011
+ },
17012
+ tokenMode: "none",
17013
+ installFallback: {
17014
+ command: "npx",
17015
+ args: ["-y", "@playwright/mcp"],
17016
+ packageName: "@playwright/mcp"
17017
+ },
17018
+ docsUrl: "https://playwright.dev/docs/getting-started-mcp",
17019
+ safety: {
17020
+ requiresApproval: true,
17021
+ destructiveTools: ["browser_click", "browser_type", "browser_file_upload", "browser_run_code"],
17022
+ dataClasses: ["web_pages", "forms", "cookies", "local_storage", "screenshots", "browser_profiles"],
17023
+ notes: "Browser automation can submit forms, alter accounts, and expose logged-in sessions. Prefer isolated profiles and require approval for side-effecting actions."
17024
+ },
17025
+ provenance: {
17026
+ source: "curated",
17027
+ sourceUrl: "https://playwright.dev/docs/getting-started-mcp",
17028
+ repositoryUrl: "https://github.com/microsoft/playwright-mcp",
17029
+ packageName: "@playwright/mcp",
17030
+ verifiedAt: "2026-05-11"
16654
17031
  }
16655
17032
  }
16656
17033
  ];
@@ -16673,6 +17050,7 @@ function getDb() {
16673
17050
  command TEXT NOT NULL,
16674
17051
  args TEXT NOT NULL DEFAULT '[]',
16675
17052
  env TEXT NOT NULL DEFAULT '{}',
17053
+ credential_refs TEXT NOT NULL DEFAULT '{}',
16676
17054
  transport TEXT NOT NULL DEFAULT 'stdio',
16677
17055
  url TEXT,
16678
17056
  source TEXT NOT NULL DEFAULT 'local',
@@ -16699,6 +17077,9 @@ function getDb() {
16699
17077
  try {
16700
17078
  db.exec("ALTER TABLE servers ADD COLUMN last_error TEXT");
16701
17079
  } catch {}
17080
+ try {
17081
+ db.exec("ALTER TABLE servers ADD COLUMN credential_refs TEXT NOT NULL DEFAULT '{}'");
17082
+ } catch {}
16702
17083
  db.exec(`
16703
17084
  CREATE TABLE IF NOT EXISTS sources (
16704
17085
  id TEXT PRIMARY KEY,
@@ -16769,22 +17150,19 @@ function getDb() {
16769
17150
  db.exec("ALTER TABLE provider_profiles ADD COLUMN auth_metadata TEXT NOT NULL DEFAULT '{}'");
16770
17151
  } catch {}
16771
17152
  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
- }
17153
+ const insertProviderProfile = db.prepare(`
17154
+ INSERT OR IGNORE INTO provider_profiles (
17155
+ id, display_name, description, endpoint, transport, fallback_endpoints,
17156
+ auth_type, auth_metadata, scopes, token_mode, install_fallback,
17157
+ docs_url, safety, provenance, enabled
17158
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
17159
+ `);
17160
+ const seedProviderProfiles = db.transaction(() => {
17161
+ for (const profile of DEFAULT_PROVIDER_PROFILE_SEEDS) {
17162
+ 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);
17163
+ }
17164
+ });
17165
+ seedProviderProfiles();
16788
17166
  db.exec(`
16789
17167
  CREATE TABLE IF NOT EXISTS feedback (
16790
17168
  id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
@@ -16824,17 +17202,17 @@ __export(exports_sources, {
16824
17202
  clearCache: () => clearCache,
16825
17203
  addSource: () => addSource
16826
17204
  });
16827
- import { mkdirSync as mkdirSync6, existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync2, readdirSync as readdirSync3, unlinkSync } from "fs";
16828
- import { join as join8 } from "path";
17205
+ import { mkdirSync as mkdirSync6, existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync2, readdirSync as readdirSync3, unlinkSync } from "fs";
17206
+ import { join as join9 } from "path";
16829
17207
  function getCacheFile(sourceId) {
16830
- return join8(CACHE_DIR, `${sourceId}.json`);
17208
+ return join9(CACHE_DIR, `${sourceId}.json`);
16831
17209
  }
16832
17210
  function readCache(sourceId) {
16833
17211
  try {
16834
17212
  const file = getCacheFile(sourceId);
16835
- if (!existsSync7(file))
17213
+ if (!existsSync8(file))
16836
17214
  return null;
16837
- const data = JSON.parse(readFileSync3(file, "utf-8"));
17215
+ const data = JSON.parse(readFileSync4(file, "utf-8"));
16838
17216
  return data;
16839
17217
  } catch {
16840
17218
  return null;
@@ -16848,7 +17226,7 @@ function writeCache(sourceId, results) {
16848
17226
  }
16849
17227
  function clearCache(sourceId) {
16850
17228
  try {
16851
- if (!existsSync7(CACHE_DIR))
17229
+ if (!existsSync8(CACHE_DIR))
16852
17230
  return;
16853
17231
  const files = readdirSync3(CACHE_DIR);
16854
17232
  for (const file of files) {
@@ -16856,7 +17234,7 @@ function clearCache(sourceId) {
16856
17234
  continue;
16857
17235
  if (!sourceId || file.startsWith(`${sourceId}.`)) {
16858
17236
  try {
16859
- unlinkSync(join8(CACHE_DIR, file));
17237
+ unlinkSync(join9(CACHE_DIR, file));
16860
17238
  } catch {}
16861
17239
  }
16862
17240
  }
@@ -17102,7 +17480,7 @@ var CACHE_DIR, DEFAULT_TTL_MS;
17102
17480
  var init_sources = __esm(() => {
17103
17481
  init_db();
17104
17482
  init_config2();
17105
- CACHE_DIR = join8(MCPS_DIR, "cache");
17483
+ CACHE_DIR = join9(MCPS_DIR, "cache");
17106
17484
  DEFAULT_TTL_MS = 10 * 60 * 1000;
17107
17485
  });
17108
17486
 
@@ -30660,6 +31038,146 @@ function readPackageVersion(moduleUrl, fallback = FALLBACK_VERSION) {
30660
31038
 
30661
31039
  // src/lib/registry.ts
30662
31040
  init_db();
31041
+
31042
+ // src/lib/credentials.ts
31043
+ init_config2();
31044
+ import { existsSync as existsSync7, readFileSync as readFileSync3 } from "fs";
31045
+ import { join as join8 } from "path";
31046
+
31047
+ class CredentialReferenceError extends Error {
31048
+ constructor(message) {
31049
+ super(message);
31050
+ this.name = "CredentialReferenceError";
31051
+ }
31052
+ }
31053
+ var SECRET_KEY_PATTERN = /(?:^|[_-])(api[_-]?key|token|secret|password|passwd|credential|auth|private[_-]?key)(?:$|[_-])/i;
31054
+ var SECRET_VALUE_PATTERN = /^(sk_(?:live|test)_[A-Za-z0-9_]+|ghp_[A-Za-z0-9_]+|github_pat_[A-Za-z0-9_]+|xox[baprs]-[A-Za-z0-9-]+|AIza[A-Za-z0-9_-]{20,}|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)$/;
31055
+ var REDACTED_CREDENTIAL_VALUE = "<redacted>";
31056
+ function normalizeKey(key) {
31057
+ return key.trim();
31058
+ }
31059
+ function isRecord(value) {
31060
+ return typeof value === "object" && value !== null && !Array.isArray(value);
31061
+ }
31062
+ function isSecretLikeEnvKey(key) {
31063
+ return SECRET_KEY_PATTERN.test(key);
31064
+ }
31065
+ function isSecretLikeValue(value) {
31066
+ return SECRET_VALUE_PATTERN.test(value.trim());
31067
+ }
31068
+ function normalizeCredentialRef(ref) {
31069
+ const source = ref.source;
31070
+ if (source !== "env" && source !== "local-vault" && source !== "hosted") {
31071
+ throw new CredentialReferenceError(`Unsupported credential reference source: ${String(source)}`);
31072
+ }
31073
+ const name = ref.name?.trim();
31074
+ if (!name) {
31075
+ throw new CredentialReferenceError("Credential reference name is required");
31076
+ }
31077
+ return {
31078
+ source,
31079
+ name,
31080
+ required: ref.required !== false,
31081
+ ...ref.description ? { description: ref.description } : {}
31082
+ };
31083
+ }
31084
+ function normalizeCredentialRefs(refs) {
31085
+ const normalized = {};
31086
+ for (const [rawKey, ref] of Object.entries(refs ?? {})) {
31087
+ const key = normalizeKey(rawKey);
31088
+ if (!key)
31089
+ throw new CredentialReferenceError("Credential reference env key is required");
31090
+ normalized[key] = normalizeCredentialRef(ref);
31091
+ }
31092
+ return normalized;
31093
+ }
31094
+ function parseCredentialRefs(value) {
31095
+ if (!isRecord(value))
31096
+ return {};
31097
+ const refs = {};
31098
+ for (const [key, ref] of Object.entries(value)) {
31099
+ if (!isRecord(ref))
31100
+ continue;
31101
+ const source = ref.source;
31102
+ const name = ref.name;
31103
+ if ((source === "env" || source === "local-vault" || source === "hosted") && typeof name === "string" && name.trim()) {
31104
+ refs[key] = normalizeCredentialRef({
31105
+ source,
31106
+ name,
31107
+ required: typeof ref.required === "boolean" ? ref.required : true,
31108
+ description: typeof ref.description === "string" ? ref.description : undefined
31109
+ });
31110
+ }
31111
+ }
31112
+ return refs;
31113
+ }
31114
+ function normalizeLiteralEnv(env) {
31115
+ const normalized = {};
31116
+ for (const [rawKey, rawValue] of Object.entries(env ?? {})) {
31117
+ const key = normalizeKey(rawKey);
31118
+ if (!key)
31119
+ continue;
31120
+ const value = String(rawValue);
31121
+ if (isSecretLikeEnvKey(key) || isSecretLikeValue(value)) {
31122
+ throw new CredentialReferenceError(`Refusing to store raw secret-like env value for "${key}". Use a credential reference instead.`);
31123
+ }
31124
+ normalized[key] = value;
31125
+ }
31126
+ return normalized;
31127
+ }
31128
+ function readLocalVault() {
31129
+ const path = process.env.HASNA_MCPS_CREDENTIAL_VAULT_PATH ?? join8(MCPS_DIR, "credentials.local.json");
31130
+ if (!existsSync7(path))
31131
+ return {};
31132
+ const parsed = JSON.parse(readFileSync3(path, "utf-8"));
31133
+ if (!isRecord(parsed))
31134
+ return {};
31135
+ const values = {};
31136
+ for (const [key, value] of Object.entries(parsed)) {
31137
+ if (typeof value === "string")
31138
+ values[key] = value;
31139
+ }
31140
+ return values;
31141
+ }
31142
+ function resolveCredentialRef(envKey, ref) {
31143
+ if (ref.source === "env") {
31144
+ const value = process.env[ref.name];
31145
+ if (value === undefined && ref.required !== false) {
31146
+ throw new CredentialReferenceError(`Missing required environment credential "${ref.name}" for "${envKey}"`);
31147
+ }
31148
+ return value;
31149
+ }
31150
+ if (ref.source === "local-vault") {
31151
+ const value = readLocalVault()[ref.name];
31152
+ if (value === undefined && ref.required !== false) {
31153
+ throw new CredentialReferenceError(`Missing required local vault credential "${ref.name}" for "${envKey}"`);
31154
+ }
31155
+ return value;
31156
+ }
31157
+ if (ref.required !== false) {
31158
+ throw new CredentialReferenceError(`Hosted credential "${ref.name}" for "${envKey}" cannot be resolved by the local runtime`);
31159
+ }
31160
+ return;
31161
+ }
31162
+ function resolveServerEnv(server) {
31163
+ const resolved = { ...server.env };
31164
+ const refs = normalizeCredentialRefs(server.credentialRefs);
31165
+ for (const [envKey, ref] of Object.entries(refs)) {
31166
+ const value = resolveCredentialRef(envKey, ref);
31167
+ if (value !== undefined)
31168
+ resolved[envKey] = value;
31169
+ }
31170
+ return resolved;
31171
+ }
31172
+ function credentialRefPlaceholders(refs) {
31173
+ const placeholders = {};
31174
+ for (const key of Object.keys(refs ?? {})) {
31175
+ placeholders[key] = REDACTED_CREDENTIAL_VALUE;
31176
+ }
31177
+ return placeholders;
31178
+ }
31179
+
31180
+ // src/lib/registry.ts
30663
31181
  function parseRow(row) {
30664
31182
  return {
30665
31183
  id: row.id,
@@ -30668,6 +31186,7 @@ function parseRow(row) {
30668
31186
  command: row.command,
30669
31187
  args: safeJsonParse(row.args, []),
30670
31188
  env: safeJsonParse(row.env, {}),
31189
+ credentialRefs: parseCredentialRefs(safeJsonParse(row.credential_refs, {})),
30671
31190
  transport: row.transport,
30672
31191
  url: row.url || null,
30673
31192
  source: row.source,
@@ -30735,9 +31254,9 @@ function addServer(opts) {
30735
31254
  if (!id) {
30736
31255
  throw new Error("Unable to generate a valid server ID");
30737
31256
  }
30738
- const row = db2.prepare(`INSERT INTO servers (id, name, description, command, args, env, transport, url, source)
30739
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
30740
- RETURNING *`).get(id, name, opts.description || null, command, JSON.stringify(opts.args || []), JSON.stringify(opts.env || {}), opts.transport || "stdio", opts.url || null, opts.source || "local");
31257
+ const row = db2.prepare(`INSERT INTO servers (id, name, description, command, args, env, credential_refs, transport, url, source)
31258
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
31259
+ RETURNING *`).get(id, name, opts.description || null, command, JSON.stringify(opts.args || []), JSON.stringify(normalizeLiteralEnv(opts.env)), JSON.stringify(normalizeCredentialRefs(opts.credentialRefs)), opts.transport || "stdio", opts.url || null, opts.source || "local");
30741
31260
  return parseRow(row);
30742
31261
  }
30743
31262
  function removeServer(id) {
@@ -30776,7 +31295,11 @@ function updateServer(id, updates) {
30776
31295
  }
30777
31296
  if (updates.env !== undefined) {
30778
31297
  sets.push("env = ?");
30779
- values.push(JSON.stringify(updates.env));
31298
+ values.push(JSON.stringify(normalizeLiteralEnv(updates.env)));
31299
+ }
31300
+ if (updates.credentialRefs !== undefined) {
31301
+ sets.push("credential_refs = ?");
31302
+ values.push(JSON.stringify(normalizeCredentialRefs(updates.credentialRefs)));
30780
31303
  }
30781
31304
  if (updates.transport !== undefined) {
30782
31305
  sets.push("transport = ?");
@@ -30864,8 +31387,8 @@ var DESTRUCTIVE_COMMANDS = new Set([
30864
31387
  ]);
30865
31388
  var SHELL_EVAL_FLAGS = new Set(["-c", "/c", "-Command", "-command", "-EncodedCommand", "-encodedcommand"]);
30866
31389
  var SECRET_FLAG_PATTERN = /(?:^|[-_])(api[-_]?key|token|secret|password|passwd|credential|auth|private[-_]?key)(?:$|[-_])/i;
30867
- var SECRET_KEY_PATTERN = /(?:^|[_-])(api[_-]?key|token|secret|password|passwd|credential|auth|private[_-]?key)(?:$|[_-])/i;
30868
- var SECRET_VALUE_PATTERN = /^(sk_(?:live|test)_[A-Za-z0-9_]+|ghp_[A-Za-z0-9_]+|github_pat_[A-Za-z0-9_]+|xox[baprs]-[A-Za-z0-9-]+|AIza[A-Za-z0-9_-]{20,}|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)$/;
31390
+ var SECRET_KEY_PATTERN2 = /(?:^|[_-])(api[_-]?key|token|secret|password|passwd|credential|auth|private[_-]?key)(?:$|[_-])/i;
31391
+ var SECRET_VALUE_PATTERN2 = /^(sk_(?:live|test)_[A-Za-z0-9_]+|ghp_[A-Za-z0-9_]+|github_pat_[A-Za-z0-9_]+|xox[baprs]-[A-Za-z0-9-]+|AIza[A-Za-z0-9_-]{20,}|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)$/;
30869
31392
  var SHELL_META_PATTERN = /[;&|`<>]|\$\(/;
30870
31393
  function commandBase(command) {
30871
31394
  return command.trim().split(/[\\/]/).pop()?.toLowerCase() || command.trim().toLowerCase();
@@ -30874,10 +31397,10 @@ function normalizeArgs(args) {
30874
31397
  return (args ?? []).map((arg) => String(arg));
30875
31398
  }
30876
31399
  function isSecretKey(key) {
30877
- return SECRET_KEY_PATTERN.test(key) || SECRET_FLAG_PATTERN.test(key);
31400
+ return SECRET_KEY_PATTERN2.test(key) || SECRET_FLAG_PATTERN.test(key);
30878
31401
  }
30879
31402
  function isSecretValue(value) {
30880
- return SECRET_VALUE_PATTERN.test(value.trim());
31403
+ return SECRET_VALUE_PATTERN2.test(value.trim());
30881
31404
  }
30882
31405
  function isSecretAssignment(arg) {
30883
31406
  const eqIdx = arg.indexOf("=");
@@ -31101,8 +31624,8 @@ init_sources();
31101
31624
 
31102
31625
  // src/lib/install.ts
31103
31626
  import { execFileSync } from "child_process";
31104
- import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync7 } from "fs";
31105
- import { join as join9 } from "path";
31627
+ import { existsSync as existsSync9, readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync7 } from "fs";
31628
+ import { join as join10 } from "path";
31106
31629
  import { homedir as homedir8 } from "os";
31107
31630
  function installToClaude(entry) {
31108
31631
  try {
@@ -31114,7 +31637,7 @@ function installToClaude(entry) {
31114
31637
  "--scope",
31115
31638
  "user"
31116
31639
  ];
31117
- for (const [k, v] of Object.entries(entry.env)) {
31640
+ for (const [k, v] of Object.entries(assertAgentInstallEnv(entry))) {
31118
31641
  args.push("--env", `${k}=${v}`);
31119
31642
  }
31120
31643
  args.push(entry.id, "--", entry.command, ...entry.args);
@@ -31126,9 +31649,9 @@ function installToClaude(entry) {
31126
31649
  }
31127
31650
  function installToCodex(entry) {
31128
31651
  try {
31129
- const configDir = join9(homedir8(), ".codex");
31130
- const configPath = join9(configDir, "config.toml");
31131
- if (!existsSync8(configDir)) {
31652
+ const configDir = join10(homedir8(), ".codex");
31653
+ const configPath = join10(configDir, "config.toml");
31654
+ if (!existsSync9(configDir)) {
31132
31655
  mkdirSync7(configDir, { recursive: true });
31133
31656
  }
31134
31657
  const block = `
@@ -31136,7 +31659,7 @@ function installToCodex(entry) {
31136
31659
  ` + `command = ${JSON.stringify(entry.command)}
31137
31660
  ` + `args = [${entry.args.map((a) => JSON.stringify(a)).join(", ")}]
31138
31661
  `;
31139
- const existing = existsSync8(configPath) ? readFileSync4(configPath, "utf-8") : "";
31662
+ const existing = existsSync9(configPath) ? readFileSync5(configPath, "utf-8") : "";
31140
31663
  if (existing.includes(`[mcp_servers.${entry.id}]`)) {
31141
31664
  return { agent: "codex", success: true };
31142
31665
  }
@@ -31148,21 +31671,22 @@ function installToCodex(entry) {
31148
31671
  }
31149
31672
  function installToGemini(entry) {
31150
31673
  try {
31151
- const configDir = join9(homedir8(), ".gemini");
31152
- const configPath = join9(configDir, "settings.json");
31153
- if (!existsSync8(configDir)) {
31674
+ const configDir = join10(homedir8(), ".gemini");
31675
+ const configPath = join10(configDir, "settings.json");
31676
+ if (!existsSync9(configDir)) {
31154
31677
  mkdirSync7(configDir, { recursive: true });
31155
31678
  }
31156
31679
  let settings = {};
31157
- if (existsSync8(configPath)) {
31158
- settings = JSON.parse(readFileSync4(configPath, "utf-8"));
31680
+ if (existsSync9(configPath)) {
31681
+ settings = JSON.parse(readFileSync5(configPath, "utf-8"));
31159
31682
  }
31160
31683
  if (!settings.mcpServers)
31161
31684
  settings.mcpServers = {};
31685
+ const env = assertAgentInstallEnv(entry);
31162
31686
  settings.mcpServers[entry.id] = {
31163
31687
  command: entry.command,
31164
31688
  args: entry.args,
31165
- ...Object.keys(entry.env).length > 0 ? { env: entry.env } : {}
31689
+ ...Object.keys(env).length > 0 ? { env } : {}
31166
31690
  };
31167
31691
  writeFileSync3(configPath, JSON.stringify(settings, null, 2), "utf-8");
31168
31692
  return { agent: "gemini", success: true };
@@ -31170,12 +31694,24 @@ function installToGemini(entry) {
31170
31694
  return { agent: "gemini", success: false, error: err.message };
31171
31695
  }
31172
31696
  }
31697
+ function assertAgentInstallEnv(entry) {
31698
+ const refs = entry.credentialRefs ?? {};
31699
+ if (Object.keys(refs).length > 0) {
31700
+ throw new CredentialReferenceError(`Server "${entry.id}" uses credential references; refusing to materialize secrets into local agent config files`);
31701
+ }
31702
+ for (const [key, value] of Object.entries(entry.env)) {
31703
+ if (isSecretLikeEnvKey(key) || isSecretLikeValue(value)) {
31704
+ throw new CredentialReferenceError(`Server "${entry.id}" has legacy raw secret-like env "${key}"; move it to a credential reference before installing to agents`);
31705
+ }
31706
+ }
31707
+ return entry.env;
31708
+ }
31173
31709
  function installToAgents(entry, targets = ["claude", "codex", "gemini"], options = {}) {
31174
31710
  try {
31175
31711
  assertLocalCommandConsent({
31176
31712
  command: entry.command,
31177
31713
  args: entry.args,
31178
- env: entry.env,
31714
+ env: { ...entry.env, ...credentialRefPlaceholders(entry.credentialRefs) },
31179
31715
  transport: entry.transport,
31180
31716
  operation: "install"
31181
31717
  }, options.localCommandConsent);
@@ -31186,6 +31722,15 @@ function installToAgents(entry, targets = ["claude", "codex", "gemini"], options
31186
31722
  error: err.message
31187
31723
  }));
31188
31724
  }
31725
+ try {
31726
+ assertAgentInstallEnv(entry);
31727
+ } catch (err) {
31728
+ return targets.map((target) => ({
31729
+ agent: target,
31730
+ success: false,
31731
+ error: err.message
31732
+ }));
31733
+ }
31189
31734
  return targets.map((target) => {
31190
31735
  if (target === "claude")
31191
31736
  return installToClaude(entry);
@@ -33621,14 +34166,14 @@ async function connectToServer(entry, options = {}) {
33621
34166
  assertLocalCommandConsent({
33622
34167
  command: entry.command,
33623
34168
  args: entry.args,
33624
- env: entry.env,
34169
+ env: { ...entry.env, ...credentialRefPlaceholders(entry.credentialRefs) },
33625
34170
  transport: entry.transport,
33626
34171
  operation: "launch"
33627
34172
  }, options.localCommandConsent);
33628
34173
  transport = new StdioClientTransport({
33629
34174
  command: entry.command,
33630
34175
  args: entry.args,
33631
- env: buildEnv(entry.env)
34176
+ env: buildEnv(resolveServerEnv(entry))
33632
34177
  });
33633
34178
  } else if (entry.transport === "sse") {
33634
34179
  transport = new SSEClientTransport(requireUrl(entry));
@@ -33774,7 +34319,7 @@ async function diagnoseServer(server, options = {}) {
33774
34319
  assertLocalCommandConsent({
33775
34320
  command: server.command,
33776
34321
  args: server.args,
33777
- env: server.env,
34322
+ env: { ...server.env, ...credentialRefPlaceholders(server.credentialRefs) },
33778
34323
  transport: server.transport,
33779
34324
  operation: "diagnose"
33780
34325
  }, options.localCommandConsent);
@@ -33803,12 +34348,29 @@ async function diagnoseServer(server, options = {}) {
33803
34348
  }
33804
34349
  }
33805
34350
  const missingEnv = Object.entries(server.env).filter(([, v]) => !v);
33806
- if (Object.keys(server.env).length === 0) {
33807
- checks4.push({ name: "env vars", pass: true, message: "no env vars required" });
33808
- } else if (missingEnv.length > 0) {
33809
- checks4.push({ name: "env vars", pass: false, message: `missing values for: ${missingEnv.map(([k]) => k).join(", ")}` });
34351
+ const credentialRefCount = Object.keys(server.credentialRefs ?? {}).length;
34352
+ let credentialError = null;
34353
+ try {
34354
+ resolveServerEnv(server);
34355
+ } catch (err) {
34356
+ credentialError = err.message;
34357
+ }
34358
+ if (Object.keys(server.env).length === 0 && credentialRefCount === 0) {
34359
+ checks4.push({ name: "env vars", pass: true, message: "no env vars or credential refs required" });
34360
+ } else if (missingEnv.length > 0 || credentialError) {
34361
+ const parts = [];
34362
+ if (missingEnv.length > 0)
34363
+ parts.push(`missing literal values for: ${missingEnv.map(([k]) => k).join(", ")}`);
34364
+ if (credentialError)
34365
+ parts.push(credentialError);
34366
+ checks4.push({ name: "env vars", pass: false, message: parts.join("; ") });
33810
34367
  } else {
33811
- checks4.push({ name: "env vars", pass: true, message: `${Object.keys(server.env).length} env var(s) set` });
34368
+ const literalCount = Object.keys(server.env).length;
34369
+ checks4.push({
34370
+ name: "env vars",
34371
+ pass: true,
34372
+ message: `${literalCount} literal env var(s), ${credentialRefCount} credential ref(s) available`
34373
+ });
33812
34374
  }
33813
34375
  if (server.transport !== "stdio" && server.url) {
33814
34376
  try {
@@ -34045,11 +34607,11 @@ function seedDefaultMachines() {
34045
34607
  // src/lib/fleet.ts
34046
34608
  init_config2();
34047
34609
  import { spawn as spawn2 } from "child_process";
34048
- import { existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
34049
- import { join as join10 } from "path";
34610
+ import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
34611
+ import { join as join11 } from "path";
34050
34612
  var NPM_SEARCH_URL = "https://registry.npmjs.org/-/v1/search";
34051
34613
  var NPM_REGISTRY_URL = "https://registry.npmjs.org";
34052
- var CATALOG_CACHE_PATH = join10(MCPS_DIR, "cache", "hasna-catalog.json");
34614
+ var CATALOG_CACHE_PATH = join11(MCPS_DIR, "cache", "hasna-catalog.json");
34053
34615
  var DEFAULT_CATALOG_CACHE_TTL_MS = 60 * 60 * 1000;
34054
34616
  var DEFAULT_REMOTE_TIMEOUT_MS = 180000;
34055
34617
  var DEFAULT_HANDSHAKE_TIMEOUT_MS = 2500;
@@ -34059,9 +34621,9 @@ function normalizeQueryList(values) {
34059
34621
  }
34060
34622
  function readCatalogCache(maxAgeMs) {
34061
34623
  try {
34062
- if (!existsSync9(CATALOG_CACHE_PATH))
34624
+ if (!existsSync10(CATALOG_CACHE_PATH))
34063
34625
  return null;
34064
- const parsed = JSON.parse(readFileSync5(CATALOG_CACHE_PATH, "utf-8"));
34626
+ const parsed = JSON.parse(readFileSync6(CATALOG_CACHE_PATH, "utf-8"));
34065
34627
  if (!parsed.cachedAt || !Array.isArray(parsed.entries))
34066
34628
  return null;
34067
34629
  if (Date.now() - parsed.cachedAt > maxAgeMs)
@@ -34073,7 +34635,7 @@ function readCatalogCache(maxAgeMs) {
34073
34635
  }
34074
34636
  function writeCatalogCache(entries) {
34075
34637
  try {
34076
- mkdirSync8(join10(MCPS_DIR, "cache"), { recursive: true });
34638
+ mkdirSync8(join11(MCPS_DIR, "cache"), { recursive: true });
34077
34639
  writeFileSync4(CATALOG_CACHE_PATH, JSON.stringify({ cachedAt: Date.now(), entries }, null, 2), "utf-8");
34078
34640
  } catch {}
34079
34641
  }
@@ -34872,6 +35434,9 @@ function localConsent(input) {
34872
35434
  source: "mcp"
34873
35435
  };
34874
35436
  }
35437
+ function readCredentialRefs(input) {
35438
+ return normalizeCredentialRefs(input.credential_refs ?? input.credentialRefs);
35439
+ }
34875
35440
  function buildMcpTools() {
34876
35441
  const definitions = [
34877
35442
  {
@@ -34897,6 +35462,12 @@ function buildMcpTools() {
34897
35462
  transport: exports_external2.enum(["stdio", "sse", "streamable-http"]).optional().describe("Transport type"),
34898
35463
  url: exports_external2.string().optional().describe("URL for remote transports"),
34899
35464
  env: exports_external2.record(exports_external2.string()).optional().describe("Environment variables"),
35465
+ credential_refs: exports_external2.record(exports_external2.object({
35466
+ source: exports_external2.enum(["env", "local-vault", "hosted"]),
35467
+ name: exports_external2.string(),
35468
+ required: exports_external2.boolean().optional(),
35469
+ description: exports_external2.string().optional()
35470
+ })).optional().describe("Credential references by server env key"),
34900
35471
  allow_local_stdio: exports_external2.boolean().optional().describe("Approve registering this local stdio command"),
34901
35472
  allow_risky_command: exports_external2.boolean().optional().describe("Approve registering risky local command patterns")
34902
35473
  },
@@ -34904,9 +35475,16 @@ function buildMcpTools() {
34904
35475
  const command = String(input.command);
34905
35476
  const args = Array.isArray(input.args) ? input.args.map(String) : [];
34906
35477
  const env = isRecordOfStrings(input.env) ? input.env : {};
35478
+ const credentialRefs = readCredentialRefs(input);
34907
35479
  const transport = input.transport;
34908
35480
  try {
34909
- assertLocalCommandConsent({ command, args, env, transport, operation: "register" }, localConsent(input));
35481
+ assertLocalCommandConsent({
35482
+ command,
35483
+ args,
35484
+ env: { ...env, ...Object.fromEntries(Object.keys(credentialRefs).map((key) => [key, "<credential-ref>"])) },
35485
+ transport,
35486
+ operation: "register"
35487
+ }, localConsent(input));
34910
35488
  return jsonContent(addServer({
34911
35489
  command,
34912
35490
  args,
@@ -34914,7 +35492,8 @@ function buildMcpTools() {
34914
35492
  description: typeof input.description === "string" ? input.description : undefined,
34915
35493
  transport,
34916
35494
  url: typeof input.url === "string" ? input.url : undefined,
34917
- env
35495
+ env,
35496
+ credentialRefs
34918
35497
  }));
34919
35498
  } catch (err) {
34920
35499
  return errorContent(err.message);
@@ -34982,6 +35561,12 @@ function buildMcpTools() {
34982
35561
  args: exports_external2.array(exports_external2.string()).optional().describe("New args list"),
34983
35562
  transport: exports_external2.enum(["stdio", "sse", "streamable-http"]).optional().describe("New transport type"),
34984
35563
  url: exports_external2.string().optional().describe("New URL for remote transports"),
35564
+ credential_refs: exports_external2.record(exports_external2.object({
35565
+ source: exports_external2.enum(["env", "local-vault", "hosted"]),
35566
+ name: exports_external2.string(),
35567
+ required: exports_external2.boolean().optional(),
35568
+ description: exports_external2.string().optional()
35569
+ })).optional().describe("Credential references by server env key"),
34985
35570
  allow_local_stdio: exports_external2.boolean().optional().describe("Approve updating this local stdio command"),
34986
35571
  allow_risky_command: exports_external2.boolean().optional().describe("Approve risky local command patterns")
34987
35572
  },
@@ -34999,6 +35584,8 @@ function buildMcpTools() {
34999
35584
  fields.command = input.command;
35000
35585
  if (Array.isArray(input.args))
35001
35586
  fields.args = input.args.map(String);
35587
+ if (input.credential_refs !== undefined || input.credentialRefs !== undefined)
35588
+ fields.credentialRefs = readCredentialRefs(input);
35002
35589
  if (input.transport === "stdio" || input.transport === "sse" || input.transport === "streamable-http")
35003
35590
  fields.transport = input.transport;
35004
35591
  if (typeof input.url === "string")
@@ -35008,7 +35595,10 @@ function buildMcpTools() {
35008
35595
  assertLocalCommandConsent({
35009
35596
  command: fields.command ?? existing.command,
35010
35597
  args: fields.args ?? existing.args,
35011
- env: existing.env,
35598
+ env: {
35599
+ ...existing.env,
35600
+ ...Object.fromEntries(Object.keys(fields.credentialRefs ?? existing.credentialRefs ?? {}).map((key) => [key, "<credential-ref>"]))
35601
+ },
35012
35602
  transport: fields.transport ?? existing.transport,
35013
35603
  operation: "register"
35014
35604
  }, localConsent(input));
@@ -35063,7 +35653,7 @@ function buildMcpTools() {
35063
35653
  },
35064
35654
  {
35065
35655
  name: "list_provider_profiles",
35066
- description: "List curated provider profiles for hosted/common MCP integrations such as Notion and Linear.",
35656
+ description: "List curated provider profiles for hosted/common MCP integrations such as GitHub, Slack, Google Workspace, Stripe, Cloudflare, Postgres, filesystem, and browser automation.",
35067
35657
  paramsSchema: {
35068
35658
  enabled_only: exports_external2.boolean().optional().describe("Only include enabled provider profiles")
35069
35659
  },
@@ -35073,7 +35663,7 @@ function buildMcpTools() {
35073
35663
  name: "search_provider_profiles",
35074
35664
  description: "Search curated provider profiles separately from raw MCP registry/source search.",
35075
35665
  paramsSchema: {
35076
- query: exports_external2.string().describe("Search query such as 'notion', 'linear', or an endpoint URL"),
35666
+ query: exports_external2.string().describe("Search query such as 'github', 'slack', 'postgres', or an endpoint URL"),
35077
35667
  enabled_only: exports_external2.boolean().optional().describe("Only include enabled provider profiles")
35078
35668
  },
35079
35669
  run: ({ query, enabled_only }) => jsonContent(searchProviderProfiles(String(query), { enabledOnly: enabled_only === true }))