@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/README.md +41 -0
- package/bin/index.js +842 -118
- package/bin/mcp.js +657 -67
- package/dist/index.d.ts +3 -2
- package/dist/index.js +678 -71
- package/dist/lib/credentials.d.ts +18 -0
- package/dist/lib/registry.d.ts +4 -2
- package/dist/mcp/index.js +657 -67
- package/dist/types.d.ts +10 -0
- package/package.json +1 -1
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-
|
|
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-
|
|
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
|
|
16773
|
-
|
|
16774
|
-
|
|
16775
|
-
|
|
16776
|
-
|
|
16777
|
-
|
|
16778
|
-
|
|
16779
|
-
|
|
16780
|
-
|
|
16781
|
-
|
|
16782
|
-
|
|
16783
|
-
|
|
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
|
|
16828
|
-
import { join as
|
|
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
|
|
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 (!
|
|
17213
|
+
if (!existsSync8(file))
|
|
16836
17214
|
return null;
|
|
16837
|
-
const data = JSON.parse(
|
|
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 (!
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
|
30868
|
-
var
|
|
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
|
|
31400
|
+
return SECRET_KEY_PATTERN2.test(key) || SECRET_FLAG_PATTERN.test(key);
|
|
30878
31401
|
}
|
|
30879
31402
|
function isSecretValue(value) {
|
|
30880
|
-
return
|
|
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
|
|
31105
|
-
import { join as
|
|
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
|
|
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 =
|
|
31130
|
-
const configPath =
|
|
31131
|
-
if (!
|
|
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 =
|
|
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 =
|
|
31152
|
-
const configPath =
|
|
31153
|
-
if (!
|
|
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 (
|
|
31158
|
-
settings = JSON.parse(
|
|
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(
|
|
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
|
|
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
|
-
|
|
33807
|
-
|
|
33808
|
-
|
|
33809
|
-
|
|
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
|
-
|
|
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
|
|
34049
|
-
import { join as
|
|
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 =
|
|
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 (!
|
|
34624
|
+
if (!existsSync10(CATALOG_CACHE_PATH))
|
|
34063
34625
|
return null;
|
|
34064
|
-
const parsed = JSON.parse(
|
|
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(
|
|
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({
|
|
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:
|
|
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
|
|
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 '
|
|
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 }))
|