diff --git a/cmd/mcpproxy/main.go b/cmd/mcpproxy/main.go
index d0c28176..14d0da78 100644
--- a/cmd/mcpproxy/main.go
+++ b/cmd/mcpproxy/main.go
@@ -38,6 +38,7 @@ import (
bbolterrors "go.etcd.io/bbolt/errors"
"go.uber.org/zap"
+ "github.com/smart-mcp-proxy/mcpproxy-go/internal/branding"
clioutput "github.com/smart-mcp-proxy/mcpproxy-go/internal/cli/output"
"github.com/smart-mcp-proxy/mcpproxy-go/internal/cliclient"
"github.com/smart-mcp-proxy/mcpproxy-go/internal/config"
@@ -98,8 +99,15 @@ func main() {
config.SetRegistriesInitCallback(registries.SetRegistriesFromConfig)
rootCmd := &cobra.Command{
- Use: "mcpproxy",
- Short: "Smart MCP Proxy - Intelligent tool discovery and proxying for Model Context Protocol servers",
+ Use: "mcpproxy",
+ Short: "Smart MCP Proxy - Intelligent tool discovery and proxying for Model Context Protocol servers",
+ // Long is shown in `mcpproxy --help`. It carries the project links
+ // (discussion #948) so the way back to the homepage/repo/docs is always
+ // one --help away, no matter how the binary was installed.
+ Long: "Smart MCP Proxy - Intelligent tool discovery and proxying for Model Context Protocol servers.\n\n" +
+ "Homepage: " + branding.Homepage + "\n" +
+ "GitHub: " + branding.Repo + "\n" +
+ "Docs: " + branding.Docs,
Version: version,
}
rootCmd.SetVersionTemplate(versionLine())
diff --git a/cmd/mcpproxy/version_cmd.go b/cmd/mcpproxy/version_cmd.go
index 696dc42d..aec416ea 100644
--- a/cmd/mcpproxy/version_cmd.go
+++ b/cmd/mcpproxy/version_cmd.go
@@ -8,6 +8,7 @@ import (
"github.com/spf13/cobra"
+ "github.com/smart-mcp-proxy/mcpproxy-go/internal/branding"
clioutput "github.com/smart-mcp-proxy/mcpproxy-go/internal/cli/output"
)
@@ -19,9 +20,13 @@ var (
)
// versionLine returns the human-readable version string shared by
-// `mcpproxy version` and the root command's --version flag.
+// `mcpproxy version` and the root command's --version flag. It carries the
+// project links (discussion #948) so a user who found the binary via Homebrew /
+// a plugin / an AI recommendation can always find the homepage and repo.
func versionLine() string {
- return fmt.Sprintf("MCPProxy %s (%s) %s/%s\n", version, Edition, runtime.GOOS, runtime.GOARCH)
+ return fmt.Sprintf("MCPProxy %s (%s) %s/%s\nHomepage: %s\nGitHub: %s\nDocs: %s\n",
+ version, Edition, runtime.GOOS, runtime.GOARCH,
+ branding.Homepage, branding.Repo, branding.Docs)
}
// VersionInfo is the machine-readable version payload for -o json/yaml.
diff --git a/cmd/mcpproxy/version_cmd_test.go b/cmd/mcpproxy/version_cmd_test.go
index 5ef1bf6d..03507081 100644
--- a/cmd/mcpproxy/version_cmd_test.go
+++ b/cmd/mcpproxy/version_cmd_test.go
@@ -21,9 +21,17 @@ func TestVersionCommandTableOutput(t *testing.T) {
t.Fatalf("Execute() error: %v", err)
}
- want := fmt.Sprintf("MCPProxy %s (%s) %s/%s\n", version, Edition, runtime.GOOS, runtime.GOARCH)
- if got := buf.String(); got != want {
- t.Errorf("table output mismatch:\n got: %q\nwant: %q", got, want)
+ got := buf.String()
+ // First line is the canonical version string.
+ wantFirst := fmt.Sprintf("MCPProxy %s (%s) %s/%s", version, Edition, runtime.GOOS, runtime.GOARCH)
+ if !strings.HasPrefix(got, wantFirst+"\n") {
+ t.Errorf("table output should start with %q, got: %q", wantFirst, got)
+ }
+ // Discussion #948: the version output carries the project links.
+ for _, link := range []string{"https://mcpproxy.app", "https://github.com/smart-mcp-proxy/mcpproxy-go", "https://docs.mcpproxy.app"} {
+ if !strings.Contains(got, link) {
+ t.Errorf("version output must contain project link %q, got: %q", link, got)
+ }
}
}
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index 8577dc29..1d23fbf6 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -6,7 +6,7 @@
state so the content fluidly reclaims space when the sidebar shrinks
to its icon rail. -->
@@ -43,6 +46,7 @@
import { onMounted, onUnmounted, reactive, ref } from 'vue'
import SidebarNav from '@/components/SidebarNav.vue'
import TopHeader from '@/components/TopHeader.vue'
+import AppFooter from '@/components/AppFooter.vue'
import ToastContainer from '@/components/ToastContainer.vue'
import ConnectionStatus from '@/components/ConnectionStatus.vue'
import AuthErrorModal from '@/components/AuthErrorModal.vue'
diff --git a/frontend/src/components/AppFooter.vue b/frontend/src/components/AppFooter.vue
new file mode 100644
index 00000000..0f2f9d4b
--- /dev/null
+++ b/frontend/src/components/AppFooter.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
diff --git a/frontend/src/config/links.ts b/frontend/src/config/links.ts
new file mode 100644
index 00000000..e639c4cc
--- /dev/null
+++ b/frontend/src/config/links.ts
@@ -0,0 +1,10 @@
+// Canonical project links (discussion #948). Kept in sync with the Go
+// (internal/branding) and Swift (ProjectLinks) copies. Users arriving via
+// Homebrew / plugins / AI recommendations often never see the repo, so every
+// surface of the running app must carry the way back to the project.
+export const PROJECT_LINKS = {
+ homepage: 'https://mcpproxy.app',
+ github: 'https://github.com/smart-mcp-proxy/mcpproxy-go',
+ docs: 'https://docs.mcpproxy.app',
+ discussions: 'https://github.com/smart-mcp-proxy/mcpproxy-go/discussions',
+} as const
diff --git a/internal/branding/branding.go b/internal/branding/branding.go
new file mode 100644
index 00000000..7624bb58
--- /dev/null
+++ b/internal/branding/branding.go
@@ -0,0 +1,30 @@
+// Package branding holds the canonical, user-facing project identifiers and
+// URLs (homepage, repo, docs, issues) so every surface that points a user back
+// to the project — CLI --version/--help, the MCP serverInfo instructions, docs
+// links in errors — uses one source of truth. The macOS tray (Swift) and Web UI
+// (TypeScript) keep their own copies of these strings; keep all three in sync.
+//
+// Added for discussion #948: users arriving via Homebrew / plugins / AI
+// recommendations often never see the GitHub repo, so the running app must
+// carry the way back to the project.
+package branding
+
+const (
+ // ProductName is the single product name used everywhere.
+ ProductName = "MCPProxy"
+
+ // Homepage is the marketing/landing site.
+ Homepage = "https://mcpproxy.app"
+
+ // Repo is the GitHub source repository.
+ Repo = "https://github.com/smart-mcp-proxy/mcpproxy-go"
+
+ // Docs is the documentation site.
+ Docs = "https://docs.mcpproxy.app"
+
+ // Issues is the GitHub issue tracker (for "Report an issue").
+ Issues = "https://github.com/smart-mcp-proxy/mcpproxy-go/issues"
+
+ // Discussions is the GitHub Discussions board.
+ Discussions = "https://github.com/smart-mcp-proxy/mcpproxy-go/discussions"
+)
diff --git a/internal/server/mcp.go b/internal/server/mcp.go
index e6f92547..c4dd7d40 100644
--- a/internal/server/mcp.go
+++ b/internal/server/mcp.go
@@ -15,6 +15,7 @@ import (
"unicode"
"github.com/smart-mcp-proxy/mcpproxy-go/internal/auth"
+ "github.com/smart-mcp-proxy/mcpproxy-go/internal/branding"
"github.com/smart-mcp-proxy/mcpproxy-go/internal/cache"
"github.com/smart-mcp-proxy/mcpproxy-go/internal/config"
"github.com/smart-mcp-proxy/mcpproxy-go/internal/contracts"
@@ -75,7 +76,10 @@ const (
"When 'code_execution' is exposed, you may instead orchestrate several discovered tools in a single step with JavaScript. " +
"When upstream tools are listed directly (named 'server__tool'), just call them by name. " +
"Do NOT use 'search_servers' to find existing tools — it searches EXTERNAL registries for adding NEW servers only. " +
- "Use 'upstream_servers' with operation 'list' to see currently connected servers and their status."
+ "Use 'upstream_servers' with operation 'list' to see currently connected servers and their status. " +
+ // Discussion #948: carry the project links at the protocol level so an
+ // agent (and anyone reading its logs) can always find the project.
+ "ABOUT: MCPProxy homepage " + branding.Homepage + ", source " + branding.Repo + ", docs " + branding.Docs + "."
// Connection status constants
statusError = "error"
diff --git a/internal/server/mcp_instructions_test.go b/internal/server/mcp_instructions_test.go
index ab31c808..4615d9e8 100644
--- a/internal/server/mcp_instructions_test.go
+++ b/internal/server/mcp_instructions_test.go
@@ -1,6 +1,7 @@
package server
import (
+ "github.com/smart-mcp-proxy/mcpproxy-go/internal/branding"
"testing"
"github.com/stretchr/testify/assert"
@@ -36,3 +37,12 @@ func TestDefaultInstructions_ContainsKeyTerms(t *testing.T) {
assert.Contains(t, defaultInstructions, "code_execution")
assert.Contains(t, defaultInstructions, "server__tool")
}
+
+// TestServer_DefaultInstructions_CarriesProjectLinks verifies discussion #948:
+// the protocol-level serverInfo instructions carry the homepage/repo/docs so an
+// agent (and anyone reading its logs) can find the project.
+func TestServer_DefaultInstructions_CarriesProjectLinks(t *testing.T) {
+ assert.Contains(t, defaultInstructions, branding.Homepage)
+ assert.Contains(t, defaultInstructions, branding.Repo)
+ assert.Contains(t, defaultInstructions, branding.Docs)
+}
diff --git a/native/macos/MCPProxy/MCPProxy/MCPProxyApp.swift b/native/macos/MCPProxy/MCPProxy/MCPProxyApp.swift
index f9fca9e5..ddbe0bac 100644
--- a/native/macos/MCPProxy/MCPProxy/MCPProxyApp.swift
+++ b/native/macos/MCPProxy/MCPProxy/MCPProxyApp.swift
@@ -1326,7 +1326,24 @@ final class AppController: NSObject, NSApplicationDelegate, NSWindowDelegate, NS
menu.addItem(stop)
}
+ // Help / project links (discussion #948): a running-app user must always
+ // have a way back to the homepage, docs, and issue tracker.
+ menu.addItem(.separator())
+
+ let docsItem = NSMenuItem(title: "Documentation", action: #selector(openDocumentation), keyEquivalent: "")
+ docsItem.target = self
+ menu.addItem(docsItem)
+
+ let issueItem = NSMenuItem(title: "Report an Issue…", action: #selector(reportIssue), keyEquivalent: "")
+ issueItem.target = self
+ menu.addItem(issueItem)
+
+ let aboutItem = NSMenuItem(title: "About MCPProxy", action: #selector(showAboutPanel), keyEquivalent: "")
+ aboutItem.target = self
+ menu.addItem(aboutItem)
+
// Quit
+ menu.addItem(.separator())
let quit = NSMenuItem(title: "Quit MCPProxy", action: #selector(quitApp), keyEquivalent: "q")
quit.target = self
menu.addItem(quit)
@@ -1620,6 +1637,48 @@ final class AppController: NSObject, NSApplicationDelegate, NSWindowDelegate, NS
NSWorkspace.shared.open(InstancePaths.configFileURL)
}
+ // MARK: - Project links (discussion #948)
+
+ @objc private func openDocumentation() {
+ NSWorkspace.shared.open(ProjectLinks.docs)
+ }
+
+ @objc private func reportIssue() {
+ NSWorkspace.shared.open(ProjectLinks.issues)
+ }
+
+ /// Shows the standard About panel (app name + version) with a credits block
+ /// that links back to the homepage, source, and docs — so "About MCPProxy"
+ /// carries the way back to the project, not just a version string.
+ @objc private func showAboutPanel() {
+ NSApp.activate(ignoringOtherApps: true)
+
+ let credits = NSMutableAttributedString()
+ let body: [NSAttributedString.Key: Any] = [
+ .font: NSFont.systemFont(ofSize: 11),
+ .foregroundColor: NSColor.secondaryLabelColor,
+ ]
+ credits.append(NSAttributedString(
+ string: "Smart MCP proxy — intelligent tool discovery, token savings, and security quarantine.\n\n",
+ attributes: body))
+ appendLink(to: credits, label: "Homepage", url: ProjectLinks.homepage)
+ credits.append(NSAttributedString(string: " ", attributes: body))
+ appendLink(to: credits, label: "GitHub", url: ProjectLinks.github)
+ credits.append(NSAttributedString(string: " ", attributes: body))
+ appendLink(to: credits, label: "Documentation", url: ProjectLinks.docs)
+
+ NSApp.orderFrontStandardAboutPanel(options: [
+ .credits: credits
+ ])
+ }
+
+ private func appendLink(to string: NSMutableAttributedString, label: String, url: URL) {
+ string.append(NSAttributedString(string: label, attributes: [
+ .link: url,
+ .font: NSFont.systemFont(ofSize: 11),
+ ]))
+ }
+
@objc private func openLogsDirectory() {
let home = FileManager.default.homeDirectoryForCurrentUser
NSWorkspace.shared.open(home.appendingPathComponent("Library/Logs/mcpproxy"))
diff --git a/native/macos/MCPProxy/MCPProxy/ProjectLinks.swift b/native/macos/MCPProxy/MCPProxy/ProjectLinks.swift
new file mode 100644
index 00000000..a3a72169
--- /dev/null
+++ b/native/macos/MCPProxy/MCPProxy/ProjectLinks.swift
@@ -0,0 +1,15 @@
+import Foundation
+
+/// Canonical, user-facing project links (discussion #948). Kept in sync with the
+/// Go (`internal/branding`) and Web UI (`frontend/src/config/links.ts`) copies.
+///
+/// Users arrive via Homebrew / plugins / AI recommendations and often never see
+/// the GitHub repo, so the running app — the tray especially, its main surface —
+/// must carry the way back to the project.
+enum ProjectLinks {
+ static let homepage = URL(string: "https://mcpproxy.app")!
+ static let github = URL(string: "https://github.com/smart-mcp-proxy/mcpproxy-go")!
+ static let docs = URL(string: "https://docs.mcpproxy.app")!
+ static let issues = URL(string: "https://github.com/smart-mcp-proxy/mcpproxy-go/issues")!
+ static let discussions = URL(string: "https://github.com/smart-mcp-proxy/mcpproxy-go/discussions")!
+}