@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/dist/index.js
CHANGED
|
@@ -9492,7 +9492,7 @@ var init_provider_profile_seeds = __esm(() => {
|
|
|
9492
9492
|
provenance: {
|
|
9493
9493
|
source: "curated",
|
|
9494
9494
|
sourceUrl: "https://developers.notion.com/guides/mcp/build-mcp-client",
|
|
9495
|
-
verifiedAt: "2026-05-
|
|
9495
|
+
verifiedAt: "2026-05-11"
|
|
9496
9496
|
}
|
|
9497
9497
|
},
|
|
9498
9498
|
{
|
|
@@ -9524,7 +9524,384 @@ var init_provider_profile_seeds = __esm(() => {
|
|
|
9524
9524
|
provenance: {
|
|
9525
9525
|
source: "curated",
|
|
9526
9526
|
sourceUrl: "https://linear.app/docs/mcp",
|
|
9527
|
-
verifiedAt: "2026-05-
|
|
9527
|
+
verifiedAt: "2026-05-11"
|
|
9528
|
+
}
|
|
9529
|
+
},
|
|
9530
|
+
{
|
|
9531
|
+
id: "github",
|
|
9532
|
+
displayName: "GitHub",
|
|
9533
|
+
description: "Connect GitHub so agents can inspect repositories, manage issues and pull requests, analyze Actions runs, and work with project metadata.",
|
|
9534
|
+
endpoint: "https://api.githubcopilot.com/mcp/",
|
|
9535
|
+
transport: "streamable-http",
|
|
9536
|
+
authType: "oauth2",
|
|
9537
|
+
authMetadata: {
|
|
9538
|
+
oauthVersion: "2.0",
|
|
9539
|
+
pkce: true,
|
|
9540
|
+
dynamicClientRegistration: false,
|
|
9541
|
+
bearerToken: "optional",
|
|
9542
|
+
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."
|
|
9543
|
+
},
|
|
9544
|
+
scopes: ["repo", "read:org", "read:user", "user:email", "workflow", "notifications", "project"],
|
|
9545
|
+
tokenMode: "user",
|
|
9546
|
+
installFallback: {
|
|
9547
|
+
command: "docker",
|
|
9548
|
+
args: ["run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "ghcr.io/github/github-mcp-server"],
|
|
9549
|
+
env: { GITHUB_PERSONAL_ACCESS_TOKEN: "GITHUB_PERSONAL_ACCESS_TOKEN" },
|
|
9550
|
+
packageName: "ghcr.io/github/github-mcp-server",
|
|
9551
|
+
url: "https://api.githubcopilot.com/mcp/"
|
|
9552
|
+
},
|
|
9553
|
+
docsUrl: "https://github.com/github/github-mcp-server",
|
|
9554
|
+
safety: {
|
|
9555
|
+
requiresApproval: true,
|
|
9556
|
+
sensitiveScopes: ["repo", "workflow", "project"],
|
|
9557
|
+
dataClasses: ["repositories", "issues", "pull_requests", "actions", "security_alerts", "organization_members"],
|
|
9558
|
+
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."
|
|
9559
|
+
},
|
|
9560
|
+
provenance: {
|
|
9561
|
+
source: "curated",
|
|
9562
|
+
sourceUrl: "https://github.com/github/github-mcp-server",
|
|
9563
|
+
repositoryUrl: "https://github.com/github/github-mcp-server",
|
|
9564
|
+
packageName: "ghcr.io/github/github-mcp-server",
|
|
9565
|
+
verifiedAt: "2026-05-11"
|
|
9566
|
+
}
|
|
9567
|
+
},
|
|
9568
|
+
{
|
|
9569
|
+
id: "slack",
|
|
9570
|
+
displayName: "Slack",
|
|
9571
|
+
description: "Connect Slack so agents can search workspace messages and files, inspect conversations, read user context, and draft or send approved messages.",
|
|
9572
|
+
endpoint: "https://mcp.slack.com/mcp",
|
|
9573
|
+
transport: "streamable-http",
|
|
9574
|
+
authType: "oauth2",
|
|
9575
|
+
authMetadata: {
|
|
9576
|
+
oauthVersion: "2.0",
|
|
9577
|
+
pkce: true,
|
|
9578
|
+
dynamicClientRegistration: false,
|
|
9579
|
+
bearerToken: "none",
|
|
9580
|
+
notes: "Slack's remote MCP endpoint uses Streamable HTTP, requires a registered Slack app identity, and does not support SSE or dynamic client registration."
|
|
9581
|
+
},
|
|
9582
|
+
scopes: [
|
|
9583
|
+
"search:read.public",
|
|
9584
|
+
"search:read.private",
|
|
9585
|
+
"search:read.mpim",
|
|
9586
|
+
"search:read.im",
|
|
9587
|
+
"search:read.files",
|
|
9588
|
+
"search:read.users",
|
|
9589
|
+
"channels:history",
|
|
9590
|
+
"groups:history",
|
|
9591
|
+
"mpim:history",
|
|
9592
|
+
"im:history",
|
|
9593
|
+
"chat:write",
|
|
9594
|
+
"canvases:read",
|
|
9595
|
+
"canvases:write",
|
|
9596
|
+
"users:read",
|
|
9597
|
+
"users:read.email"
|
|
9598
|
+
],
|
|
9599
|
+
tokenMode: "user",
|
|
9600
|
+
docsUrl: "https://docs.slack.dev/ai/slack-mcp-server/",
|
|
9601
|
+
safety: {
|
|
9602
|
+
requiresApproval: true,
|
|
9603
|
+
sensitiveScopes: ["chat:write", "canvases:write", "users:read.email"],
|
|
9604
|
+
dataClasses: ["messages", "files", "channels", "users", "canvases"],
|
|
9605
|
+
notes: "Slack data often includes confidential team communication. Search/read access should stay tenant-scoped and message/canvas writes should require explicit approval."
|
|
9606
|
+
},
|
|
9607
|
+
provenance: {
|
|
9608
|
+
source: "curated",
|
|
9609
|
+
sourceUrl: "https://docs.slack.dev/ai/slack-mcp-server/",
|
|
9610
|
+
verifiedAt: "2026-05-11"
|
|
9611
|
+
}
|
|
9612
|
+
},
|
|
9613
|
+
{
|
|
9614
|
+
id: "gmail",
|
|
9615
|
+
displayName: "Gmail",
|
|
9616
|
+
description: "Connect Gmail through the open Workspace MCP server for mail search, message retrieval, drafts, labels, filters, and approved send workflows.",
|
|
9617
|
+
transport: "stdio",
|
|
9618
|
+
authType: "oauth2",
|
|
9619
|
+
authMetadata: {
|
|
9620
|
+
oauthVersion: "2.1",
|
|
9621
|
+
pkce: true,
|
|
9622
|
+
dynamicClientRegistration: false,
|
|
9623
|
+
bearerToken: "optional",
|
|
9624
|
+
notes: "Workspace MCP is an independent open-source Google Workspace server. Configure your own Google OAuth client and storage backend for Gmail access."
|
|
9625
|
+
},
|
|
9626
|
+
scopes: [
|
|
9627
|
+
"https://www.googleapis.com/auth/gmail.readonly",
|
|
9628
|
+
"https://www.googleapis.com/auth/gmail.modify",
|
|
9629
|
+
"https://www.googleapis.com/auth/gmail.compose",
|
|
9630
|
+
"https://www.googleapis.com/auth/gmail.send"
|
|
9631
|
+
],
|
|
9632
|
+
tokenMode: "user",
|
|
9633
|
+
installFallback: {
|
|
9634
|
+
command: "uvx",
|
|
9635
|
+
args: ["workspace-mcp"],
|
|
9636
|
+
env: { WORKSPACE_MCP_PERMISSIONS: "gmail:send" },
|
|
9637
|
+
packageName: "workspace-mcp",
|
|
9638
|
+
url: "http://127.0.0.1:8000/mcp"
|
|
9639
|
+
},
|
|
9640
|
+
docsUrl: "https://workspacemcp.com/docs",
|
|
9641
|
+
safety: {
|
|
9642
|
+
requiresApproval: true,
|
|
9643
|
+
sensitiveScopes: [
|
|
9644
|
+
"https://www.googleapis.com/auth/gmail.modify",
|
|
9645
|
+
"https://www.googleapis.com/auth/gmail.compose",
|
|
9646
|
+
"https://www.googleapis.com/auth/gmail.send"
|
|
9647
|
+
],
|
|
9648
|
+
dataClasses: ["email_messages", "threads", "attachments", "labels", "contacts"],
|
|
9649
|
+
notes: "Email content and sending are high-impact actions. Prefer read-only permissions until a user intentionally enables drafts or sends."
|
|
9650
|
+
},
|
|
9651
|
+
provenance: {
|
|
9652
|
+
source: "github",
|
|
9653
|
+
sourceUrl: "https://workspacemcp.com/docs",
|
|
9654
|
+
repositoryUrl: "https://github.com/taylorwilsdon/google_workspace_mcp",
|
|
9655
|
+
packageName: "workspace-mcp",
|
|
9656
|
+
verifiedAt: "2026-05-11"
|
|
9657
|
+
}
|
|
9658
|
+
},
|
|
9659
|
+
{
|
|
9660
|
+
id: "google-drive",
|
|
9661
|
+
displayName: "Google Drive",
|
|
9662
|
+
description: "Connect Google Drive through the open Workspace MCP server for file search, folder navigation, content reads, and approved file operations.",
|
|
9663
|
+
transport: "stdio",
|
|
9664
|
+
authType: "oauth2",
|
|
9665
|
+
authMetadata: {
|
|
9666
|
+
oauthVersion: "2.1",
|
|
9667
|
+
pkce: true,
|
|
9668
|
+
dynamicClientRegistration: false,
|
|
9669
|
+
bearerToken: "optional",
|
|
9670
|
+
notes: "Workspace MCP is an independent open-source Google Workspace server. Configure your own Google OAuth client and storage backend for Drive access."
|
|
9671
|
+
},
|
|
9672
|
+
scopes: [
|
|
9673
|
+
"https://www.googleapis.com/auth/drive.readonly",
|
|
9674
|
+
"https://www.googleapis.com/auth/drive.file",
|
|
9675
|
+
"https://www.googleapis.com/auth/drive"
|
|
9676
|
+
],
|
|
9677
|
+
tokenMode: "user",
|
|
9678
|
+
installFallback: {
|
|
9679
|
+
command: "uvx",
|
|
9680
|
+
args: ["workspace-mcp"],
|
|
9681
|
+
env: { WORKSPACE_MCP_PERMISSIONS: "drive:readonly" },
|
|
9682
|
+
packageName: "workspace-mcp",
|
|
9683
|
+
url: "http://127.0.0.1:8000/mcp"
|
|
9684
|
+
},
|
|
9685
|
+
docsUrl: "https://workspacemcp.com/docs",
|
|
9686
|
+
safety: {
|
|
9687
|
+
requiresApproval: true,
|
|
9688
|
+
sensitiveScopes: [
|
|
9689
|
+
"https://www.googleapis.com/auth/drive.file",
|
|
9690
|
+
"https://www.googleapis.com/auth/drive"
|
|
9691
|
+
],
|
|
9692
|
+
dataClasses: ["drive_files", "folders", "documents", "spreadsheets", "attachments", "sharing_permissions"],
|
|
9693
|
+
notes: "Drive access can expose broad business documents. Keep tenant allowlists and approval-gate creation, movement, and permission changes."
|
|
9694
|
+
},
|
|
9695
|
+
provenance: {
|
|
9696
|
+
source: "github",
|
|
9697
|
+
sourceUrl: "https://workspacemcp.com/docs",
|
|
9698
|
+
repositoryUrl: "https://github.com/taylorwilsdon/google_workspace_mcp",
|
|
9699
|
+
packageName: "workspace-mcp",
|
|
9700
|
+
verifiedAt: "2026-05-11"
|
|
9701
|
+
}
|
|
9702
|
+
},
|
|
9703
|
+
{
|
|
9704
|
+
id: "google-calendar",
|
|
9705
|
+
displayName: "Google Calendar",
|
|
9706
|
+
description: "Connect Google Calendar through the open Workspace MCP server for calendar discovery, event reads, and approved scheduling changes.",
|
|
9707
|
+
transport: "stdio",
|
|
9708
|
+
authType: "oauth2",
|
|
9709
|
+
authMetadata: {
|
|
9710
|
+
oauthVersion: "2.1",
|
|
9711
|
+
pkce: true,
|
|
9712
|
+
dynamicClientRegistration: false,
|
|
9713
|
+
bearerToken: "optional",
|
|
9714
|
+
notes: "Workspace MCP is an independent open-source Google Workspace server. Configure your own Google OAuth client and storage backend for Calendar access."
|
|
9715
|
+
},
|
|
9716
|
+
scopes: [
|
|
9717
|
+
"https://www.googleapis.com/auth/calendar.readonly",
|
|
9718
|
+
"https://www.googleapis.com/auth/calendar.events",
|
|
9719
|
+
"https://www.googleapis.com/auth/calendar"
|
|
9720
|
+
],
|
|
9721
|
+
tokenMode: "user",
|
|
9722
|
+
installFallback: {
|
|
9723
|
+
command: "uvx",
|
|
9724
|
+
args: ["workspace-mcp"],
|
|
9725
|
+
env: { WORKSPACE_MCP_PERMISSIONS: "calendar:readonly" },
|
|
9726
|
+
packageName: "workspace-mcp",
|
|
9727
|
+
url: "http://127.0.0.1:8000/mcp"
|
|
9728
|
+
},
|
|
9729
|
+
docsUrl: "https://workspacemcp.com/docs",
|
|
9730
|
+
safety: {
|
|
9731
|
+
requiresApproval: true,
|
|
9732
|
+
sensitiveScopes: [
|
|
9733
|
+
"https://www.googleapis.com/auth/calendar.events",
|
|
9734
|
+
"https://www.googleapis.com/auth/calendar"
|
|
9735
|
+
],
|
|
9736
|
+
dataClasses: ["calendars", "events", "attendees", "attachments", "availability"],
|
|
9737
|
+
notes: "Calendar writes can invite people, reveal availability, and alter operational schedules. Gate creates, updates, and deletes with user approval."
|
|
9738
|
+
},
|
|
9739
|
+
provenance: {
|
|
9740
|
+
source: "github",
|
|
9741
|
+
sourceUrl: "https://workspacemcp.com/docs",
|
|
9742
|
+
repositoryUrl: "https://github.com/taylorwilsdon/google_workspace_mcp",
|
|
9743
|
+
packageName: "workspace-mcp",
|
|
9744
|
+
verifiedAt: "2026-05-11"
|
|
9745
|
+
}
|
|
9746
|
+
},
|
|
9747
|
+
{
|
|
9748
|
+
id: "stripe",
|
|
9749
|
+
displayName: "Stripe",
|
|
9750
|
+
description: "Connect Stripe so agents can inspect accounts, balances, customers, prices, subscriptions, invoices, disputes, and perform approved Stripe API operations.",
|
|
9751
|
+
endpoint: "https://mcp.stripe.com",
|
|
9752
|
+
transport: "streamable-http",
|
|
9753
|
+
authType: "oauth2",
|
|
9754
|
+
authMetadata: {
|
|
9755
|
+
oauthVersion: "2.0",
|
|
9756
|
+
pkce: true,
|
|
9757
|
+
dynamicClientRegistration: false,
|
|
9758
|
+
bearerToken: "optional",
|
|
9759
|
+
notes: "Stripe supports OAuth MCP sessions and restricted API keys as Authorization bearer tokens for clients that do not support OAuth."
|
|
9760
|
+
},
|
|
9761
|
+
tokenMode: "workspace",
|
|
9762
|
+
installFallback: {
|
|
9763
|
+
command: "npx",
|
|
9764
|
+
args: ["-y", "@stripe/mcp"],
|
|
9765
|
+
env: { STRIPE_SECRET_KEY: "STRIPE_SECRET_KEY" },
|
|
9766
|
+
packageName: "@stripe/mcp",
|
|
9767
|
+
url: "https://mcp.stripe.com"
|
|
9768
|
+
},
|
|
9769
|
+
docsUrl: "https://docs.stripe.com/mcp",
|
|
9770
|
+
safety: {
|
|
9771
|
+
requiresApproval: true,
|
|
9772
|
+
sensitiveScopes: ["restricted_api_key", "live_mode"],
|
|
9773
|
+
dataClasses: ["payments", "customers", "subscriptions", "invoices", "disputes", "account_balances"],
|
|
9774
|
+
notes: "Use restricted keys, separate sandbox/live mode, and require approval for any operation that creates, updates, refunds, cancels, or exposes customer data."
|
|
9775
|
+
},
|
|
9776
|
+
provenance: {
|
|
9777
|
+
source: "curated",
|
|
9778
|
+
sourceUrl: "https://docs.stripe.com/mcp",
|
|
9779
|
+
repositoryUrl: "https://github.com/stripe/ai",
|
|
9780
|
+
packageName: "@stripe/mcp",
|
|
9781
|
+
verifiedAt: "2026-05-11"
|
|
9782
|
+
}
|
|
9783
|
+
},
|
|
9784
|
+
{
|
|
9785
|
+
id: "cloudflare",
|
|
9786
|
+
displayName: "Cloudflare",
|
|
9787
|
+
description: "Connect Cloudflare's remote MCP server for account, zone, developer platform, logs, analytics, and approved infrastructure operations.",
|
|
9788
|
+
endpoint: "https://mcp.cloudflare.com/mcp",
|
|
9789
|
+
transport: "streamable-http",
|
|
9790
|
+
authType: "oauth2",
|
|
9791
|
+
authMetadata: {
|
|
9792
|
+
oauthVersion: "2.0",
|
|
9793
|
+
pkce: true,
|
|
9794
|
+
dynamicClientRegistration: false,
|
|
9795
|
+
bearerToken: "none",
|
|
9796
|
+
notes: "Cloudflare's remote MCP server redirects users through Cloudflare authorization and permission selection."
|
|
9797
|
+
},
|
|
9798
|
+
tokenMode: "workspace",
|
|
9799
|
+
docsUrl: "https://developers.cloudflare.com/agents/model-context-protocol/mcp-servers-for-cloudflare/",
|
|
9800
|
+
safety: {
|
|
9801
|
+
requiresApproval: true,
|
|
9802
|
+
sensitiveScopes: ["account_admin", "zone_write", "workers_write", "dns_write"],
|
|
9803
|
+
dataClasses: ["accounts", "zones", "dns_records", "workers", "logs", "analytics", "security_events"],
|
|
9804
|
+
notes: "Cloudflare tools can affect live infrastructure. Require account scoping, least-privilege permissions, and approval for write operations."
|
|
9805
|
+
},
|
|
9806
|
+
provenance: {
|
|
9807
|
+
source: "curated",
|
|
9808
|
+
sourceUrl: "https://github.com/cloudflare/mcp",
|
|
9809
|
+
repositoryUrl: "https://github.com/cloudflare/mcp",
|
|
9810
|
+
verifiedAt: "2026-05-11"
|
|
9811
|
+
}
|
|
9812
|
+
},
|
|
9813
|
+
{
|
|
9814
|
+
id: "postgres",
|
|
9815
|
+
displayName: "PostgreSQL",
|
|
9816
|
+
description: "Connect a PostgreSQL database through the reference read-only MCP server for schema inspection and SQL query analysis.",
|
|
9817
|
+
transport: "stdio",
|
|
9818
|
+
authType: "api_key",
|
|
9819
|
+
authMetadata: {
|
|
9820
|
+
bearerToken: "none",
|
|
9821
|
+
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."
|
|
9822
|
+
},
|
|
9823
|
+
tokenMode: "service",
|
|
9824
|
+
installFallback: {
|
|
9825
|
+
command: "npx",
|
|
9826
|
+
args: ["-y", "@modelcontextprotocol/server-postgres", "${POSTGRES_URL}"],
|
|
9827
|
+
packageName: "@modelcontextprotocol/server-postgres"
|
|
9828
|
+
},
|
|
9829
|
+
docsUrl: "https://www.npmjs.com/package/@modelcontextprotocol/server-postgres",
|
|
9830
|
+
safety: {
|
|
9831
|
+
readOnly: true,
|
|
9832
|
+
requiresApproval: false,
|
|
9833
|
+
sensitiveScopes: ["database_connection_url"],
|
|
9834
|
+
dataClasses: ["database_schema", "table_rows", "query_results"],
|
|
9835
|
+
notes: "Use read-only database users and network allowlists. Treat query results as sensitive even when the server enforces read-only transactions."
|
|
9836
|
+
},
|
|
9837
|
+
provenance: {
|
|
9838
|
+
source: "npm",
|
|
9839
|
+
sourceUrl: "https://www.npmjs.com/package/@modelcontextprotocol/server-postgres",
|
|
9840
|
+
repositoryUrl: "https://github.com/modelcontextprotocol/servers",
|
|
9841
|
+
packageName: "@modelcontextprotocol/server-postgres",
|
|
9842
|
+
verifiedAt: "2026-05-11"
|
|
9843
|
+
}
|
|
9844
|
+
},
|
|
9845
|
+
{
|
|
9846
|
+
id: "filesystem",
|
|
9847
|
+
displayName: "Filesystem",
|
|
9848
|
+
description: "Connect the reference filesystem MCP server for scoped local file and directory reads, writes, searches, metadata, and move operations.",
|
|
9849
|
+
transport: "stdio",
|
|
9850
|
+
authType: "none",
|
|
9851
|
+
authMetadata: {
|
|
9852
|
+
bearerToken: "none",
|
|
9853
|
+
notes: "Filesystem access is bounded by explicit root directories passed to the local stdio server."
|
|
9854
|
+
},
|
|
9855
|
+
tokenMode: "none",
|
|
9856
|
+
installFallback: {
|
|
9857
|
+
command: "npx",
|
|
9858
|
+
args: ["-y", "@modelcontextprotocol/server-filesystem", "${workspaceFolder}"],
|
|
9859
|
+
packageName: "@modelcontextprotocol/server-filesystem"
|
|
9860
|
+
},
|
|
9861
|
+
docsUrl: "https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem",
|
|
9862
|
+
safety: {
|
|
9863
|
+
requiresApproval: true,
|
|
9864
|
+
destructiveTools: ["write_file", "delete_file", "move_file", "create_directory"],
|
|
9865
|
+
dataClasses: ["local_files", "source_code", "documents", "directory_metadata"],
|
|
9866
|
+
notes: "Always constrain roots to intended project directories and approval-gate write, delete, and move operations."
|
|
9867
|
+
},
|
|
9868
|
+
provenance: {
|
|
9869
|
+
source: "npm",
|
|
9870
|
+
sourceUrl: "https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem",
|
|
9871
|
+
repositoryUrl: "https://github.com/modelcontextprotocol/servers",
|
|
9872
|
+
packageName: "@modelcontextprotocol/server-filesystem",
|
|
9873
|
+
verifiedAt: "2026-05-11"
|
|
9874
|
+
}
|
|
9875
|
+
},
|
|
9876
|
+
{
|
|
9877
|
+
id: "browser",
|
|
9878
|
+
displayName: "Browser Automation",
|
|
9879
|
+
description: "Connect Playwright MCP so agents can navigate pages, inspect accessibility snapshots, fill forms, capture screenshots, and run browser automation.",
|
|
9880
|
+
transport: "stdio",
|
|
9881
|
+
authType: "none",
|
|
9882
|
+
authMetadata: {
|
|
9883
|
+
bearerToken: "none",
|
|
9884
|
+
notes: "Playwright MCP runs browser automation locally and may reuse browser profile state unless configured for isolation."
|
|
9885
|
+
},
|
|
9886
|
+
tokenMode: "none",
|
|
9887
|
+
installFallback: {
|
|
9888
|
+
command: "npx",
|
|
9889
|
+
args: ["-y", "@playwright/mcp"],
|
|
9890
|
+
packageName: "@playwright/mcp"
|
|
9891
|
+
},
|
|
9892
|
+
docsUrl: "https://playwright.dev/docs/getting-started-mcp",
|
|
9893
|
+
safety: {
|
|
9894
|
+
requiresApproval: true,
|
|
9895
|
+
destructiveTools: ["browser_click", "browser_type", "browser_file_upload", "browser_run_code"],
|
|
9896
|
+
dataClasses: ["web_pages", "forms", "cookies", "local_storage", "screenshots", "browser_profiles"],
|
|
9897
|
+
notes: "Browser automation can submit forms, alter accounts, and expose logged-in sessions. Prefer isolated profiles and require approval for side-effecting actions."
|
|
9898
|
+
},
|
|
9899
|
+
provenance: {
|
|
9900
|
+
source: "curated",
|
|
9901
|
+
sourceUrl: "https://playwright.dev/docs/getting-started-mcp",
|
|
9902
|
+
repositoryUrl: "https://github.com/microsoft/playwright-mcp",
|
|
9903
|
+
packageName: "@playwright/mcp",
|
|
9904
|
+
verifiedAt: "2026-05-11"
|
|
9528
9905
|
}
|
|
9529
9906
|
}
|
|
9530
9907
|
];
|
|
@@ -9547,6 +9924,7 @@ function getDb() {
|
|
|
9547
9924
|
command TEXT NOT NULL,
|
|
9548
9925
|
args TEXT NOT NULL DEFAULT '[]',
|
|
9549
9926
|
env TEXT NOT NULL DEFAULT '{}',
|
|
9927
|
+
credential_refs TEXT NOT NULL DEFAULT '{}',
|
|
9550
9928
|
transport TEXT NOT NULL DEFAULT 'stdio',
|
|
9551
9929
|
url TEXT,
|
|
9552
9930
|
source TEXT NOT NULL DEFAULT 'local',
|
|
@@ -9573,6 +9951,9 @@ function getDb() {
|
|
|
9573
9951
|
try {
|
|
9574
9952
|
db.exec("ALTER TABLE servers ADD COLUMN last_error TEXT");
|
|
9575
9953
|
} catch {}
|
|
9954
|
+
try {
|
|
9955
|
+
db.exec("ALTER TABLE servers ADD COLUMN credential_refs TEXT NOT NULL DEFAULT '{}'");
|
|
9956
|
+
} catch {}
|
|
9576
9957
|
db.exec(`
|
|
9577
9958
|
CREATE TABLE IF NOT EXISTS sources (
|
|
9578
9959
|
id TEXT PRIMARY KEY,
|
|
@@ -9643,22 +10024,19 @@ function getDb() {
|
|
|
9643
10024
|
db.exec("ALTER TABLE provider_profiles ADD COLUMN auth_metadata TEXT NOT NULL DEFAULT '{}'");
|
|
9644
10025
|
} catch {}
|
|
9645
10026
|
db.exec("CREATE INDEX IF NOT EXISTS idx_provider_profiles_enabled ON provider_profiles(enabled)");
|
|
9646
|
-
const
|
|
9647
|
-
|
|
9648
|
-
|
|
9649
|
-
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
|
|
9653
|
-
|
|
9654
|
-
|
|
9655
|
-
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
9659
|
-
});
|
|
9660
|
-
run();
|
|
9661
|
-
}
|
|
10027
|
+
const insertProviderProfile = db.prepare(`
|
|
10028
|
+
INSERT OR IGNORE INTO provider_profiles (
|
|
10029
|
+
id, display_name, description, endpoint, transport, fallback_endpoints,
|
|
10030
|
+
auth_type, auth_metadata, scopes, token_mode, install_fallback,
|
|
10031
|
+
docs_url, safety, provenance, enabled
|
|
10032
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
10033
|
+
`);
|
|
10034
|
+
const seedProviderProfiles = db.transaction(() => {
|
|
10035
|
+
for (const profile of DEFAULT_PROVIDER_PROFILE_SEEDS) {
|
|
10036
|
+
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);
|
|
10037
|
+
}
|
|
10038
|
+
});
|
|
10039
|
+
seedProviderProfiles();
|
|
9662
10040
|
db.exec(`
|
|
9663
10041
|
CREATE TABLE IF NOT EXISTS feedback (
|
|
9664
10042
|
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
|
@@ -16627,17 +17005,17 @@ __export(exports_sources, {
|
|
|
16627
17005
|
clearCache: () => clearCache,
|
|
16628
17006
|
addSource: () => addSource
|
|
16629
17007
|
});
|
|
16630
|
-
import { mkdirSync as mkdirSync6, existsSync as
|
|
16631
|
-
import { join as
|
|
17008
|
+
import { mkdirSync as mkdirSync6, existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync2, readdirSync as readdirSync3, unlinkSync } from "fs";
|
|
17009
|
+
import { join as join8 } from "path";
|
|
16632
17010
|
function getCacheFile(sourceId) {
|
|
16633
|
-
return
|
|
17011
|
+
return join8(CACHE_DIR, `${sourceId}.json`);
|
|
16634
17012
|
}
|
|
16635
17013
|
function readCache(sourceId) {
|
|
16636
17014
|
try {
|
|
16637
17015
|
const file = getCacheFile(sourceId);
|
|
16638
|
-
if (!
|
|
17016
|
+
if (!existsSync7(file))
|
|
16639
17017
|
return null;
|
|
16640
|
-
const data = JSON.parse(
|
|
17018
|
+
const data = JSON.parse(readFileSync3(file, "utf-8"));
|
|
16641
17019
|
return data;
|
|
16642
17020
|
} catch {
|
|
16643
17021
|
return null;
|
|
@@ -16651,7 +17029,7 @@ function writeCache(sourceId, results) {
|
|
|
16651
17029
|
}
|
|
16652
17030
|
function clearCache(sourceId) {
|
|
16653
17031
|
try {
|
|
16654
|
-
if (!
|
|
17032
|
+
if (!existsSync7(CACHE_DIR))
|
|
16655
17033
|
return;
|
|
16656
17034
|
const files = readdirSync3(CACHE_DIR);
|
|
16657
17035
|
for (const file of files) {
|
|
@@ -16659,7 +17037,7 @@ function clearCache(sourceId) {
|
|
|
16659
17037
|
continue;
|
|
16660
17038
|
if (!sourceId || file.startsWith(`${sourceId}.`)) {
|
|
16661
17039
|
try {
|
|
16662
|
-
unlinkSync(
|
|
17040
|
+
unlinkSync(join8(CACHE_DIR, file));
|
|
16663
17041
|
} catch {}
|
|
16664
17042
|
}
|
|
16665
17043
|
}
|
|
@@ -16905,12 +17283,166 @@ var CACHE_DIR, DEFAULT_TTL_MS;
|
|
|
16905
17283
|
var init_sources = __esm(() => {
|
|
16906
17284
|
init_db();
|
|
16907
17285
|
init_config2();
|
|
16908
|
-
CACHE_DIR =
|
|
17286
|
+
CACHE_DIR = join8(MCPS_DIR, "cache");
|
|
16909
17287
|
DEFAULT_TTL_MS = 10 * 60 * 1000;
|
|
16910
17288
|
});
|
|
16911
17289
|
|
|
16912
17290
|
// src/lib/registry.ts
|
|
16913
17291
|
init_db();
|
|
17292
|
+
|
|
17293
|
+
// src/lib/credentials.ts
|
|
17294
|
+
init_config2();
|
|
17295
|
+
import { existsSync as existsSync6, readFileSync as readFileSync2 } from "fs";
|
|
17296
|
+
import { join as join7 } from "path";
|
|
17297
|
+
|
|
17298
|
+
class CredentialReferenceError extends Error {
|
|
17299
|
+
constructor(message) {
|
|
17300
|
+
super(message);
|
|
17301
|
+
this.name = "CredentialReferenceError";
|
|
17302
|
+
}
|
|
17303
|
+
}
|
|
17304
|
+
var SECRET_KEY_PATTERN = /(?:^|[_-])(api[_-]?key|token|secret|password|passwd|credential|auth|private[_-]?key)(?:$|[_-])/i;
|
|
17305
|
+
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_-]+)$/;
|
|
17306
|
+
var REDACTED_CREDENTIAL_VALUE = "<redacted>";
|
|
17307
|
+
function normalizeKey(key) {
|
|
17308
|
+
return key.trim();
|
|
17309
|
+
}
|
|
17310
|
+
function isRecord(value) {
|
|
17311
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17312
|
+
}
|
|
17313
|
+
function isSecretLikeEnvKey(key) {
|
|
17314
|
+
return SECRET_KEY_PATTERN.test(key);
|
|
17315
|
+
}
|
|
17316
|
+
function isSecretLikeValue(value) {
|
|
17317
|
+
return SECRET_VALUE_PATTERN.test(value.trim());
|
|
17318
|
+
}
|
|
17319
|
+
function normalizeCredentialRef(ref) {
|
|
17320
|
+
const source = ref.source;
|
|
17321
|
+
if (source !== "env" && source !== "local-vault" && source !== "hosted") {
|
|
17322
|
+
throw new CredentialReferenceError(`Unsupported credential reference source: ${String(source)}`);
|
|
17323
|
+
}
|
|
17324
|
+
const name = ref.name?.trim();
|
|
17325
|
+
if (!name) {
|
|
17326
|
+
throw new CredentialReferenceError("Credential reference name is required");
|
|
17327
|
+
}
|
|
17328
|
+
return {
|
|
17329
|
+
source,
|
|
17330
|
+
name,
|
|
17331
|
+
required: ref.required !== false,
|
|
17332
|
+
...ref.description ? { description: ref.description } : {}
|
|
17333
|
+
};
|
|
17334
|
+
}
|
|
17335
|
+
function normalizeCredentialRefs(refs) {
|
|
17336
|
+
const normalized = {};
|
|
17337
|
+
for (const [rawKey, ref] of Object.entries(refs ?? {})) {
|
|
17338
|
+
const key = normalizeKey(rawKey);
|
|
17339
|
+
if (!key)
|
|
17340
|
+
throw new CredentialReferenceError("Credential reference env key is required");
|
|
17341
|
+
normalized[key] = normalizeCredentialRef(ref);
|
|
17342
|
+
}
|
|
17343
|
+
return normalized;
|
|
17344
|
+
}
|
|
17345
|
+
function parseCredentialRefs(value) {
|
|
17346
|
+
if (!isRecord(value))
|
|
17347
|
+
return {};
|
|
17348
|
+
const refs = {};
|
|
17349
|
+
for (const [key, ref] of Object.entries(value)) {
|
|
17350
|
+
if (!isRecord(ref))
|
|
17351
|
+
continue;
|
|
17352
|
+
const source = ref.source;
|
|
17353
|
+
const name = ref.name;
|
|
17354
|
+
if ((source === "env" || source === "local-vault" || source === "hosted") && typeof name === "string" && name.trim()) {
|
|
17355
|
+
refs[key] = normalizeCredentialRef({
|
|
17356
|
+
source,
|
|
17357
|
+
name,
|
|
17358
|
+
required: typeof ref.required === "boolean" ? ref.required : true,
|
|
17359
|
+
description: typeof ref.description === "string" ? ref.description : undefined
|
|
17360
|
+
});
|
|
17361
|
+
}
|
|
17362
|
+
}
|
|
17363
|
+
return refs;
|
|
17364
|
+
}
|
|
17365
|
+
function normalizeLiteralEnv(env) {
|
|
17366
|
+
const normalized = {};
|
|
17367
|
+
for (const [rawKey, rawValue] of Object.entries(env ?? {})) {
|
|
17368
|
+
const key = normalizeKey(rawKey);
|
|
17369
|
+
if (!key)
|
|
17370
|
+
continue;
|
|
17371
|
+
const value = String(rawValue);
|
|
17372
|
+
if (isSecretLikeEnvKey(key) || isSecretLikeValue(value)) {
|
|
17373
|
+
throw new CredentialReferenceError(`Refusing to store raw secret-like env value for "${key}". Use a credential reference instead.`);
|
|
17374
|
+
}
|
|
17375
|
+
normalized[key] = value;
|
|
17376
|
+
}
|
|
17377
|
+
return normalized;
|
|
17378
|
+
}
|
|
17379
|
+
function redactEnv(env) {
|
|
17380
|
+
const redacted = {};
|
|
17381
|
+
for (const [key, value] of Object.entries(env)) {
|
|
17382
|
+
redacted[key] = isSecretLikeEnvKey(key) || isSecretLikeValue(value) ? REDACTED_CREDENTIAL_VALUE : value;
|
|
17383
|
+
}
|
|
17384
|
+
return redacted;
|
|
17385
|
+
}
|
|
17386
|
+
function redactServerCredentials(server) {
|
|
17387
|
+
return {
|
|
17388
|
+
...server,
|
|
17389
|
+
env: redactEnv(server.env),
|
|
17390
|
+
credentialRefs: normalizeCredentialRefs(server.credentialRefs)
|
|
17391
|
+
};
|
|
17392
|
+
}
|
|
17393
|
+
function readLocalVault() {
|
|
17394
|
+
const path = process.env.HASNA_MCPS_CREDENTIAL_VAULT_PATH ?? join7(MCPS_DIR, "credentials.local.json");
|
|
17395
|
+
if (!existsSync6(path))
|
|
17396
|
+
return {};
|
|
17397
|
+
const parsed = JSON.parse(readFileSync2(path, "utf-8"));
|
|
17398
|
+
if (!isRecord(parsed))
|
|
17399
|
+
return {};
|
|
17400
|
+
const values = {};
|
|
17401
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
17402
|
+
if (typeof value === "string")
|
|
17403
|
+
values[key] = value;
|
|
17404
|
+
}
|
|
17405
|
+
return values;
|
|
17406
|
+
}
|
|
17407
|
+
function resolveCredentialRef(envKey, ref) {
|
|
17408
|
+
if (ref.source === "env") {
|
|
17409
|
+
const value = process.env[ref.name];
|
|
17410
|
+
if (value === undefined && ref.required !== false) {
|
|
17411
|
+
throw new CredentialReferenceError(`Missing required environment credential "${ref.name}" for "${envKey}"`);
|
|
17412
|
+
}
|
|
17413
|
+
return value;
|
|
17414
|
+
}
|
|
17415
|
+
if (ref.source === "local-vault") {
|
|
17416
|
+
const value = readLocalVault()[ref.name];
|
|
17417
|
+
if (value === undefined && ref.required !== false) {
|
|
17418
|
+
throw new CredentialReferenceError(`Missing required local vault credential "${ref.name}" for "${envKey}"`);
|
|
17419
|
+
}
|
|
17420
|
+
return value;
|
|
17421
|
+
}
|
|
17422
|
+
if (ref.required !== false) {
|
|
17423
|
+
throw new CredentialReferenceError(`Hosted credential "${ref.name}" for "${envKey}" cannot be resolved by the local runtime`);
|
|
17424
|
+
}
|
|
17425
|
+
return;
|
|
17426
|
+
}
|
|
17427
|
+
function resolveServerEnv(server) {
|
|
17428
|
+
const resolved = { ...server.env };
|
|
17429
|
+
const refs = normalizeCredentialRefs(server.credentialRefs);
|
|
17430
|
+
for (const [envKey, ref] of Object.entries(refs)) {
|
|
17431
|
+
const value = resolveCredentialRef(envKey, ref);
|
|
17432
|
+
if (value !== undefined)
|
|
17433
|
+
resolved[envKey] = value;
|
|
17434
|
+
}
|
|
17435
|
+
return resolved;
|
|
17436
|
+
}
|
|
17437
|
+
function credentialRefPlaceholders(refs) {
|
|
17438
|
+
const placeholders = {};
|
|
17439
|
+
for (const key of Object.keys(refs ?? {})) {
|
|
17440
|
+
placeholders[key] = REDACTED_CREDENTIAL_VALUE;
|
|
17441
|
+
}
|
|
17442
|
+
return placeholders;
|
|
17443
|
+
}
|
|
17444
|
+
|
|
17445
|
+
// src/lib/registry.ts
|
|
16914
17446
|
function parseRow(row) {
|
|
16915
17447
|
return {
|
|
16916
17448
|
id: row.id,
|
|
@@ -16919,6 +17451,7 @@ function parseRow(row) {
|
|
|
16919
17451
|
command: row.command,
|
|
16920
17452
|
args: safeJsonParse(row.args, []),
|
|
16921
17453
|
env: safeJsonParse(row.env, {}),
|
|
17454
|
+
credentialRefs: parseCredentialRefs(safeJsonParse(row.credential_refs, {})),
|
|
16922
17455
|
transport: row.transport,
|
|
16923
17456
|
url: row.url || null,
|
|
16924
17457
|
source: row.source,
|
|
@@ -16986,9 +17519,9 @@ function addServer(opts) {
|
|
|
16986
17519
|
if (!id) {
|
|
16987
17520
|
throw new Error("Unable to generate a valid server ID");
|
|
16988
17521
|
}
|
|
16989
|
-
const row = db2.prepare(`INSERT INTO servers (id, name, description, command, args, env, transport, url, source)
|
|
16990
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
16991
|
-
RETURNING *`).get(id, name, opts.description || null, command, JSON.stringify(opts.args || []), JSON.stringify(opts.env
|
|
17522
|
+
const row = db2.prepare(`INSERT INTO servers (id, name, description, command, args, env, credential_refs, transport, url, source)
|
|
17523
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
17524
|
+
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");
|
|
16992
17525
|
return parseRow(row);
|
|
16993
17526
|
}
|
|
16994
17527
|
function removeServer(id) {
|
|
@@ -17027,7 +17560,11 @@ function updateServer(id, updates) {
|
|
|
17027
17560
|
}
|
|
17028
17561
|
if (updates.env !== undefined) {
|
|
17029
17562
|
sets.push("env = ?");
|
|
17030
|
-
values.push(JSON.stringify(updates.env));
|
|
17563
|
+
values.push(JSON.stringify(normalizeLiteralEnv(updates.env)));
|
|
17564
|
+
}
|
|
17565
|
+
if (updates.credentialRefs !== undefined) {
|
|
17566
|
+
sets.push("credential_refs = ?");
|
|
17567
|
+
values.push(JSON.stringify(normalizeCredentialRefs(updates.credentialRefs)));
|
|
17031
17568
|
}
|
|
17032
17569
|
if (updates.transport !== undefined) {
|
|
17033
17570
|
sets.push("transport = ?");
|
|
@@ -17061,7 +17598,7 @@ function setServerEnv(id, key, value) {
|
|
|
17061
17598
|
if (!server)
|
|
17062
17599
|
throw new Error(`Server "${id}" not found`);
|
|
17063
17600
|
const env = { ...server.env, [key]: value };
|
|
17064
|
-
db2.prepare("UPDATE servers SET env = ?, updated_at = datetime('now') WHERE id = ?").run(JSON.stringify(env), id);
|
|
17601
|
+
db2.prepare("UPDATE servers SET env = ?, updated_at = datetime('now') WHERE id = ?").run(JSON.stringify(normalizeLiteralEnv(env)), id);
|
|
17065
17602
|
}
|
|
17066
17603
|
function unsetServerEnv(id, key) {
|
|
17067
17604
|
const db2 = getDb();
|
|
@@ -17072,6 +17609,23 @@ function unsetServerEnv(id, key) {
|
|
|
17072
17609
|
delete env[key];
|
|
17073
17610
|
db2.prepare("UPDATE servers SET env = ?, updated_at = datetime('now') WHERE id = ?").run(JSON.stringify(env), id);
|
|
17074
17611
|
}
|
|
17612
|
+
function setServerCredentialRef(id, key, ref) {
|
|
17613
|
+
const db2 = getDb();
|
|
17614
|
+
const server = getServer(id);
|
|
17615
|
+
if (!server)
|
|
17616
|
+
throw new Error(`Server "${id}" not found`);
|
|
17617
|
+
const credentialRefs = normalizeCredentialRefs({ ...server.credentialRefs ?? {}, [key]: ref });
|
|
17618
|
+
db2.prepare("UPDATE servers SET credential_refs = ?, updated_at = datetime('now') WHERE id = ?").run(JSON.stringify(credentialRefs), id);
|
|
17619
|
+
}
|
|
17620
|
+
function unsetServerCredentialRef(id, key) {
|
|
17621
|
+
const db2 = getDb();
|
|
17622
|
+
const server = getServer(id);
|
|
17623
|
+
if (!server)
|
|
17624
|
+
throw new Error(`Server "${id}" not found`);
|
|
17625
|
+
const credentialRefs = { ...server.credentialRefs ?? {} };
|
|
17626
|
+
delete credentialRefs[key];
|
|
17627
|
+
db2.prepare("UPDATE servers SET credential_refs = ?, updated_at = datetime('now') WHERE id = ?").run(JSON.stringify(normalizeCredentialRefs(credentialRefs)), id);
|
|
17628
|
+
}
|
|
17075
17629
|
function cacheTools(serverId, tools) {
|
|
17076
17630
|
const db2 = getDb();
|
|
17077
17631
|
const insert = db2.prepare("INSERT INTO tool_cache (server_id, name, description, input_schema) VALUES (?, ?, ?, ?)");
|
|
@@ -17111,6 +17665,7 @@ function cloneServer(id, newName) {
|
|
|
17111
17665
|
command: server.command,
|
|
17112
17666
|
args: server.args,
|
|
17113
17667
|
env: server.env,
|
|
17668
|
+
credentialRefs: server.credentialRefs,
|
|
17114
17669
|
transport: server.transport,
|
|
17115
17670
|
url: server.url ?? undefined,
|
|
17116
17671
|
source: server.source
|
|
@@ -25266,8 +25821,8 @@ var DESTRUCTIVE_COMMANDS = new Set([
|
|
|
25266
25821
|
]);
|
|
25267
25822
|
var SHELL_EVAL_FLAGS = new Set(["-c", "/c", "-Command", "-command", "-EncodedCommand", "-encodedcommand"]);
|
|
25268
25823
|
var SECRET_FLAG_PATTERN = /(?:^|[-_])(api[-_]?key|token|secret|password|passwd|credential|auth|private[-_]?key)(?:$|[-_])/i;
|
|
25269
|
-
var
|
|
25270
|
-
var
|
|
25824
|
+
var SECRET_KEY_PATTERN2 = /(?:^|[_-])(api[_-]?key|token|secret|password|passwd|credential|auth|private[_-]?key)(?:$|[_-])/i;
|
|
25825
|
+
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_-]+)$/;
|
|
25271
25826
|
var SHELL_META_PATTERN = /[;&|`<>]|\$\(/;
|
|
25272
25827
|
function commandBase(command) {
|
|
25273
25828
|
return command.trim().split(/[\\/]/).pop()?.toLowerCase() || command.trim().toLowerCase();
|
|
@@ -25276,10 +25831,10 @@ function normalizeArgs(args) {
|
|
|
25276
25831
|
return (args ?? []).map((arg) => String(arg));
|
|
25277
25832
|
}
|
|
25278
25833
|
function isSecretKey(key) {
|
|
25279
|
-
return
|
|
25834
|
+
return SECRET_KEY_PATTERN2.test(key) || SECRET_FLAG_PATTERN.test(key);
|
|
25280
25835
|
}
|
|
25281
25836
|
function isSecretValue(value) {
|
|
25282
|
-
return
|
|
25837
|
+
return SECRET_VALUE_PATTERN2.test(value.trim());
|
|
25283
25838
|
}
|
|
25284
25839
|
function isSecretAssignment(arg) {
|
|
25285
25840
|
const eqIdx = arg.indexOf("=");
|
|
@@ -25465,14 +26020,14 @@ async function connectToServer(entry, options = {}) {
|
|
|
25465
26020
|
assertLocalCommandConsent({
|
|
25466
26021
|
command: entry.command,
|
|
25467
26022
|
args: entry.args,
|
|
25468
|
-
env: entry.env,
|
|
26023
|
+
env: { ...entry.env, ...credentialRefPlaceholders(entry.credentialRefs) },
|
|
25469
26024
|
transport: entry.transport,
|
|
25470
26025
|
operation: "launch"
|
|
25471
26026
|
}, options.localCommandConsent);
|
|
25472
26027
|
transport = new StdioClientTransport({
|
|
25473
26028
|
command: entry.command,
|
|
25474
26029
|
args: entry.args,
|
|
25475
|
-
env: buildEnv(entry
|
|
26030
|
+
env: buildEnv(resolveServerEnv(entry))
|
|
25476
26031
|
});
|
|
25477
26032
|
} else if (entry.transport === "sse") {
|
|
25478
26033
|
transport = new SSEClientTransport(requireUrl(entry));
|
|
@@ -25612,7 +26167,7 @@ async function diagnoseServer(server, options = {}) {
|
|
|
25612
26167
|
assertLocalCommandConsent({
|
|
25613
26168
|
command: server.command,
|
|
25614
26169
|
args: server.args,
|
|
25615
|
-
env: server.env,
|
|
26170
|
+
env: { ...server.env, ...credentialRefPlaceholders(server.credentialRefs) },
|
|
25616
26171
|
transport: server.transport,
|
|
25617
26172
|
operation: "diagnose"
|
|
25618
26173
|
}, options.localCommandConsent);
|
|
@@ -25641,12 +26196,29 @@ async function diagnoseServer(server, options = {}) {
|
|
|
25641
26196
|
}
|
|
25642
26197
|
}
|
|
25643
26198
|
const missingEnv = Object.entries(server.env).filter(([, v]) => !v);
|
|
25644
|
-
|
|
25645
|
-
|
|
25646
|
-
|
|
25647
|
-
|
|
26199
|
+
const credentialRefCount = Object.keys(server.credentialRefs ?? {}).length;
|
|
26200
|
+
let credentialError = null;
|
|
26201
|
+
try {
|
|
26202
|
+
resolveServerEnv(server);
|
|
26203
|
+
} catch (err) {
|
|
26204
|
+
credentialError = err.message;
|
|
26205
|
+
}
|
|
26206
|
+
if (Object.keys(server.env).length === 0 && credentialRefCount === 0) {
|
|
26207
|
+
checks3.push({ name: "env vars", pass: true, message: "no env vars or credential refs required" });
|
|
26208
|
+
} else if (missingEnv.length > 0 || credentialError) {
|
|
26209
|
+
const parts = [];
|
|
26210
|
+
if (missingEnv.length > 0)
|
|
26211
|
+
parts.push(`missing literal values for: ${missingEnv.map(([k]) => k).join(", ")}`);
|
|
26212
|
+
if (credentialError)
|
|
26213
|
+
parts.push(credentialError);
|
|
26214
|
+
checks3.push({ name: "env vars", pass: false, message: parts.join("; ") });
|
|
25648
26215
|
} else {
|
|
25649
|
-
|
|
26216
|
+
const literalCount = Object.keys(server.env).length;
|
|
26217
|
+
checks3.push({
|
|
26218
|
+
name: "env vars",
|
|
26219
|
+
pass: true,
|
|
26220
|
+
message: `${literalCount} literal env var(s), ${credentialRefCount} credential ref(s) available`
|
|
26221
|
+
});
|
|
25650
26222
|
}
|
|
25651
26223
|
if (server.transport !== "stdio" && server.url) {
|
|
25652
26224
|
try {
|
|
@@ -26005,8 +26577,8 @@ init_provider_profile_seeds();
|
|
|
26005
26577
|
|
|
26006
26578
|
// src/lib/install.ts
|
|
26007
26579
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
26008
|
-
import { existsSync as
|
|
26009
|
-
import { join as
|
|
26580
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync7 } from "fs";
|
|
26581
|
+
import { join as join9 } from "path";
|
|
26010
26582
|
import { homedir as homedir8 } from "os";
|
|
26011
26583
|
function installToClaude(entry) {
|
|
26012
26584
|
try {
|
|
@@ -26018,7 +26590,7 @@ function installToClaude(entry) {
|
|
|
26018
26590
|
"--scope",
|
|
26019
26591
|
"user"
|
|
26020
26592
|
];
|
|
26021
|
-
for (const [k, v] of Object.entries(entry
|
|
26593
|
+
for (const [k, v] of Object.entries(assertAgentInstallEnv(entry))) {
|
|
26022
26594
|
args.push("--env", `${k}=${v}`);
|
|
26023
26595
|
}
|
|
26024
26596
|
args.push(entry.id, "--", entry.command, ...entry.args);
|
|
@@ -26030,9 +26602,9 @@ function installToClaude(entry) {
|
|
|
26030
26602
|
}
|
|
26031
26603
|
function installToCodex(entry) {
|
|
26032
26604
|
try {
|
|
26033
|
-
const configDir =
|
|
26034
|
-
const configPath =
|
|
26035
|
-
if (!
|
|
26605
|
+
const configDir = join9(homedir8(), ".codex");
|
|
26606
|
+
const configPath = join9(configDir, "config.toml");
|
|
26607
|
+
if (!existsSync8(configDir)) {
|
|
26036
26608
|
mkdirSync7(configDir, { recursive: true });
|
|
26037
26609
|
}
|
|
26038
26610
|
const block = `
|
|
@@ -26040,7 +26612,7 @@ function installToCodex(entry) {
|
|
|
26040
26612
|
` + `command = ${JSON.stringify(entry.command)}
|
|
26041
26613
|
` + `args = [${entry.args.map((a) => JSON.stringify(a)).join(", ")}]
|
|
26042
26614
|
`;
|
|
26043
|
-
const existing =
|
|
26615
|
+
const existing = existsSync8(configPath) ? readFileSync4(configPath, "utf-8") : "";
|
|
26044
26616
|
if (existing.includes(`[mcp_servers.${entry.id}]`)) {
|
|
26045
26617
|
return { agent: "codex", success: true };
|
|
26046
26618
|
}
|
|
@@ -26052,21 +26624,22 @@ function installToCodex(entry) {
|
|
|
26052
26624
|
}
|
|
26053
26625
|
function installToGemini(entry) {
|
|
26054
26626
|
try {
|
|
26055
|
-
const configDir =
|
|
26056
|
-
const configPath =
|
|
26057
|
-
if (!
|
|
26627
|
+
const configDir = join9(homedir8(), ".gemini");
|
|
26628
|
+
const configPath = join9(configDir, "settings.json");
|
|
26629
|
+
if (!existsSync8(configDir)) {
|
|
26058
26630
|
mkdirSync7(configDir, { recursive: true });
|
|
26059
26631
|
}
|
|
26060
26632
|
let settings = {};
|
|
26061
|
-
if (
|
|
26062
|
-
settings = JSON.parse(
|
|
26633
|
+
if (existsSync8(configPath)) {
|
|
26634
|
+
settings = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
26063
26635
|
}
|
|
26064
26636
|
if (!settings.mcpServers)
|
|
26065
26637
|
settings.mcpServers = {};
|
|
26638
|
+
const env = assertAgentInstallEnv(entry);
|
|
26066
26639
|
settings.mcpServers[entry.id] = {
|
|
26067
26640
|
command: entry.command,
|
|
26068
26641
|
args: entry.args,
|
|
26069
|
-
...Object.keys(
|
|
26642
|
+
...Object.keys(env).length > 0 ? { env } : {}
|
|
26070
26643
|
};
|
|
26071
26644
|
writeFileSync3(configPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
26072
26645
|
return { agent: "gemini", success: true };
|
|
@@ -26074,12 +26647,24 @@ function installToGemini(entry) {
|
|
|
26074
26647
|
return { agent: "gemini", success: false, error: err.message };
|
|
26075
26648
|
}
|
|
26076
26649
|
}
|
|
26650
|
+
function assertAgentInstallEnv(entry) {
|
|
26651
|
+
const refs = entry.credentialRefs ?? {};
|
|
26652
|
+
if (Object.keys(refs).length > 0) {
|
|
26653
|
+
throw new CredentialReferenceError(`Server "${entry.id}" uses credential references; refusing to materialize secrets into local agent config files`);
|
|
26654
|
+
}
|
|
26655
|
+
for (const [key, value] of Object.entries(entry.env)) {
|
|
26656
|
+
if (isSecretLikeEnvKey(key) || isSecretLikeValue(value)) {
|
|
26657
|
+
throw new CredentialReferenceError(`Server "${entry.id}" has legacy raw secret-like env "${key}"; move it to a credential reference before installing to agents`);
|
|
26658
|
+
}
|
|
26659
|
+
}
|
|
26660
|
+
return entry.env;
|
|
26661
|
+
}
|
|
26077
26662
|
function installToAgents(entry, targets = ["claude", "codex", "gemini"], options = {}) {
|
|
26078
26663
|
try {
|
|
26079
26664
|
assertLocalCommandConsent({
|
|
26080
26665
|
command: entry.command,
|
|
26081
26666
|
args: entry.args,
|
|
26082
|
-
env: entry.env,
|
|
26667
|
+
env: { ...entry.env, ...credentialRefPlaceholders(entry.credentialRefs) },
|
|
26083
26668
|
transport: entry.transport,
|
|
26084
26669
|
operation: "install"
|
|
26085
26670
|
}, options.localCommandConsent);
|
|
@@ -26090,6 +26675,15 @@ function installToAgents(entry, targets = ["claude", "codex", "gemini"], options
|
|
|
26090
26675
|
error: err.message
|
|
26091
26676
|
}));
|
|
26092
26677
|
}
|
|
26678
|
+
try {
|
|
26679
|
+
assertAgentInstallEnv(entry);
|
|
26680
|
+
} catch (err) {
|
|
26681
|
+
return targets.map((target) => ({
|
|
26682
|
+
agent: target,
|
|
26683
|
+
success: false,
|
|
26684
|
+
error: err.message
|
|
26685
|
+
}));
|
|
26686
|
+
}
|
|
26093
26687
|
return targets.map((target) => {
|
|
26094
26688
|
if (target === "claude")
|
|
26095
26689
|
return installToClaude(entry);
|
|
@@ -26299,11 +26893,11 @@ function seedDefaultMachines() {
|
|
|
26299
26893
|
// src/lib/fleet.ts
|
|
26300
26894
|
init_config2();
|
|
26301
26895
|
import { spawn as spawn2 } from "child_process";
|
|
26302
|
-
import { existsSync as
|
|
26303
|
-
import { join as
|
|
26896
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
26897
|
+
import { join as join10 } from "path";
|
|
26304
26898
|
var NPM_SEARCH_URL = "https://registry.npmjs.org/-/v1/search";
|
|
26305
26899
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
26306
|
-
var CATALOG_CACHE_PATH =
|
|
26900
|
+
var CATALOG_CACHE_PATH = join10(MCPS_DIR, "cache", "hasna-catalog.json");
|
|
26307
26901
|
var DEFAULT_CATALOG_CACHE_TTL_MS = 60 * 60 * 1000;
|
|
26308
26902
|
var DEFAULT_REMOTE_TIMEOUT_MS = 180000;
|
|
26309
26903
|
var DEFAULT_HANDSHAKE_TIMEOUT_MS = 2500;
|
|
@@ -26313,9 +26907,9 @@ function normalizeQueryList(values) {
|
|
|
26313
26907
|
}
|
|
26314
26908
|
function readCatalogCache(maxAgeMs) {
|
|
26315
26909
|
try {
|
|
26316
|
-
if (!
|
|
26910
|
+
if (!existsSync9(CATALOG_CACHE_PATH))
|
|
26317
26911
|
return null;
|
|
26318
|
-
const parsed = JSON.parse(
|
|
26912
|
+
const parsed = JSON.parse(readFileSync5(CATALOG_CACHE_PATH, "utf-8"));
|
|
26319
26913
|
if (!parsed.cachedAt || !Array.isArray(parsed.entries))
|
|
26320
26914
|
return null;
|
|
26321
26915
|
if (Date.now() - parsed.cachedAt > maxAgeMs)
|
|
@@ -26327,7 +26921,7 @@ function readCatalogCache(maxAgeMs) {
|
|
|
26327
26921
|
}
|
|
26328
26922
|
function writeCatalogCache(entries) {
|
|
26329
26923
|
try {
|
|
26330
|
-
mkdirSync8(
|
|
26924
|
+
mkdirSync8(join10(MCPS_DIR, "cache"), { recursive: true });
|
|
26331
26925
|
writeFileSync4(CATALOG_CACHE_PATH, JSON.stringify({ cachedAt: Date.now(), entries }, null, 2), "utf-8");
|
|
26332
26926
|
} catch {}
|
|
26333
26927
|
}
|
|
@@ -26998,22 +27592,22 @@ async function runFleetInstall(options = {}, dependencies = {}) {
|
|
|
26998
27592
|
}));
|
|
26999
27593
|
}
|
|
27000
27594
|
// src/lib/version.ts
|
|
27001
|
-
import { existsSync as
|
|
27002
|
-
import { dirname as dirname3, join as
|
|
27595
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
|
|
27596
|
+
import { dirname as dirname3, join as join11 } from "path";
|
|
27003
27597
|
import { fileURLToPath } from "url";
|
|
27004
27598
|
var FALLBACK_VERSION = "0.0.1";
|
|
27005
27599
|
function readPackageVersion(moduleUrl, fallback = FALLBACK_VERSION) {
|
|
27006
27600
|
const baseDir = dirname3(fileURLToPath(moduleUrl));
|
|
27007
27601
|
const candidates = [
|
|
27008
|
-
|
|
27009
|
-
|
|
27010
|
-
|
|
27602
|
+
join11(baseDir, "..", "..", "package.json"),
|
|
27603
|
+
join11(baseDir, "..", "package.json"),
|
|
27604
|
+
join11(baseDir, "package.json")
|
|
27011
27605
|
];
|
|
27012
27606
|
for (const candidate of candidates) {
|
|
27013
|
-
if (!
|
|
27607
|
+
if (!existsSync10(candidate))
|
|
27014
27608
|
continue;
|
|
27015
27609
|
try {
|
|
27016
|
-
const pkg = JSON.parse(
|
|
27610
|
+
const pkg = JSON.parse(readFileSync6(candidate, "utf-8"));
|
|
27017
27611
|
if (pkg.version)
|
|
27018
27612
|
return pkg.version;
|
|
27019
27613
|
} catch {}
|
|
@@ -27029,19 +27623,27 @@ export {
|
|
|
27029
27623
|
updateServer,
|
|
27030
27624
|
updateMachine,
|
|
27031
27625
|
unsetServerEnv,
|
|
27626
|
+
unsetServerCredentialRef,
|
|
27032
27627
|
setServerEnv,
|
|
27628
|
+
setServerCredentialRef,
|
|
27033
27629
|
seedDefaultProviderProfiles,
|
|
27034
27630
|
seedDefaultMachines,
|
|
27035
27631
|
searchRegistry,
|
|
27036
27632
|
searchProviderProfiles,
|
|
27037
27633
|
runFleetInstall,
|
|
27038
27634
|
runFleetHealthCheck,
|
|
27635
|
+
resolveServerEnv,
|
|
27039
27636
|
removeSource,
|
|
27040
27637
|
removeServer,
|
|
27041
27638
|
removeProviderProfile,
|
|
27042
27639
|
removeMachine,
|
|
27043
27640
|
refreshTools,
|
|
27641
|
+
redactServerCredentials,
|
|
27642
|
+
redactEnv,
|
|
27044
27643
|
readPackageVersion,
|
|
27644
|
+
normalizeLiteralEnv,
|
|
27645
|
+
normalizeCredentialRefs,
|
|
27646
|
+
normalizeCredentialRef,
|
|
27045
27647
|
listSources,
|
|
27046
27648
|
listServers,
|
|
27047
27649
|
listProviderProfiles,
|
|
@@ -27049,6 +27651,8 @@ export {
|
|
|
27049
27651
|
listHasnaMcpCatalog,
|
|
27050
27652
|
listAwesomeServers,
|
|
27051
27653
|
listAllTools,
|
|
27654
|
+
isSecretLikeValue,
|
|
27655
|
+
isSecretLikeEnvKey,
|
|
27052
27656
|
installToAgents,
|
|
27053
27657
|
installProviderProfile,
|
|
27054
27658
|
installFromRegistry,
|
|
@@ -27072,6 +27676,7 @@ export {
|
|
|
27072
27676
|
disableServer,
|
|
27073
27677
|
disableProviderProfile,
|
|
27074
27678
|
diagnoseServer,
|
|
27679
|
+
credentialRefPlaceholders,
|
|
27075
27680
|
connectToServer,
|
|
27076
27681
|
closeDb,
|
|
27077
27682
|
cloneServer,
|
|
@@ -27080,7 +27685,9 @@ export {
|
|
|
27080
27685
|
addSource,
|
|
27081
27686
|
addServer,
|
|
27082
27687
|
addMachine,
|
|
27688
|
+
REDACTED_CREDENTIAL_VALUE,
|
|
27083
27689
|
LocalCommandConsentError,
|
|
27084
27690
|
DEFAULT_PROVIDER_PROFILE_SEEDS,
|
|
27085
|
-
DEFAULT_MACHINE_SEEDS
|
|
27691
|
+
DEFAULT_MACHINE_SEEDS,
|
|
27692
|
+
CredentialReferenceError
|
|
27086
27693
|
};
|