Token导航 LogoToken导航TokenDH.com
Vire Portal logo
安全风控stdio官方级别未说明来源级核验

Vire Portal

MCP Server

Vire Portal是一个用于Vire投资平台的Web应用程序和MCP服务器,提供登录页面、MCP工具端点以及将所有工具调用代理到vire-server的功能。

工具数

0

提示词数

0

GitHub Stars

0

资源数

0
代理服务GoClaudeClaude DesktopClaude

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

作者 / 组织

bobmcallan

提供方

bobmcallan

最后核验

2026/5/17 20:20

运行时

Docker

快速接入

先看主来源和安装命令,再打开仓库或文档;下面只保留这个条目的关键接入事实。

命令预览

docker run -p 8881:8080 \

详细介绍

Vire门户

Vire投资平台的Web应用程序和MCP服务器。托管登录页面,在以下位置为MCP工具端点提供服务 /mcp,并将所有工具调用代理到vire服务器。

该门户是一个Go服务器,它使用Alpine.js渲染HTML模板以实现交互性,并为Claude和其他MCP客户端提供MCP(模型上下文协议)端点。它将工具调用代理到vire服务器,为用户上下文注入X-vire-\*标头。从Docker容器和vire服务器一起提供服务。

存储库布局: 此仓库包含门户服务器和MCP网桥代码。Docker镜像(ghcr.io/bobmcallan/vire-portal:latestghcr.io/bobmcallan/vire-mcp:latest)已发布给GHCR。入口沿着 ghcr.io/bobmcallan/vire-server:latest 在Docker Compose堆栈中。 vire-mcp 是Claude Desktop推出的stdio到HTTP桥,它通过OAuth 2.1连接到vire门户的MCP端点,并通过stdio重新公开工具。门户基础设施(云运行部署)由以下人员管理 病毒 地形(infra/modules/portal/).

技术栈

  • 转到1.25+ 符合标准 net/http (无框架)
  • html/template 用于服务器端渲染
  • Alpine.js (CDN)用于客户端交互
  • Chart.js v4 (CDN)用于投资组合增长图
  • 无状态 --由病毒服务器通过REST API管理的所有用户数据
  • TOML 具有优先级的配置:默认值\环境变量>TOML文件>默认值。
设置TOML键环境变量CLI标志默认值
服务器端口server.portVIRE_SERVER_PORT-port, -p8080
服务器主机server.hostVIRE_SERVER_HOST-hostlocalhost
API URLapi.urlVIRE_API_URL--http://localhost:8080
JWT机密auth.jwt_secretVIRE_AUTH_JWT_SECRET--""
OAuth回调URLauth.callback_urlVIRE_AUTH_CALLBACK_URL--http://localhost:8080/auth/callback
门户URLauth.portal_urlVIRE_PORTAL_URL--""
管理员用户admin_usersVIRE_ADMIN_USERS--""
服务密钥service.keyVIRE_SERVICE_KEY--""
门户IDservice.portal_idVIRE_PORTAL_ID--主机名
环境environmentVIRE_ENV--prod
日志级别logging.levelVIRE_LOG_LEVEL--info
日志格式logging.formatVIRE_LOG_FORMAT--text
日志输出logging.outputs----["console", "file"]
日志文件路径logging.file_path----logs/vire-portal.log
日志最大大小(MB)logging.max_size_mb----10
记录最大备份数logging.max_backups----5

配置文件是从以下位置自动发现的 vire-portal.tomldocker/vire-portal.toml。用以下方式明确指定 -c path/to/config.toml.

[api] 部分配置MCP代理。 api.url 指向vire服务器实例。用户上下文作为X-Vire-\*标头注入到每个代理请求中。所有用户数据均由vire服务器管理。

MCP端点

门户在以下位置托管MCP(模型上下文协议)服务器 POST /mcp 使用 mcp走 使用流式HTTP传输。Claude和其他MCP客户端连接到此端点以访问投资工具。

建筑

Claude Code / CLI                Claude Desktop
  |                                |
  | POST /mcp (Streamable HTTP)    | stdio
  |                                |
  |                          vire-mcp (bridge)
  |                                |
  |                                | POST /mcp (HTTP + OAuth)
  v                                v
vire-portal (:8881)
  |  internal/mcp/ package
  |  - Dynamic tool catalog from GET /api/mcp/tools
  |  - Generic proxy handler (path/query/body params)
  |  - X-Vire-* header injection from JWT sub claim
  |  - OAuth 2.1 (DCR, PKCE, token exchange)
  v
vire-server (:8080)

启动时,门户从vire服务器获取工具目录 GET /api/mcp/tools 端点重试(3次尝试,2秒回退)。每个目录条目都定义了工具名称、描述、HTTP方法、URL路径模板和参数。门户验证每个条目(非空名称/方法/路径、方法白名单、, /api/ 路径前缀,无路径遍历)并跳过重复项。有效的工具会动态注册为MCP工具,并路由到适当的REST端点。如果在所有重试后都无法访问vire服务器,则门户将以0个工具(非致命)启动。

所有工具调用都代理到vire服务器。门户不解析或格式化响应——它从vire服务器返回原始JSON,让MCP客户端(Claude)格式化输出。

连接克劳德

该门户通过Streamable HTTP为MCP提供服务,网址为 POST /mcp 使用OAuth 2.1身份验证。所有工具逻辑、目录管理和版本处理都在vire门户中。客户端可以直接通过HTTP(Claude Code、Claude CLI)或通过 vire-mcp stdio桥接器(克劳德桌面)。

Claude Desktop → stdio → vire-mcp → HTTP + OAuth → vire-portal (:8881/mcp) → vire-server
Claude CLI/Web → HTTP + OAuth → vire-portal (:8881/mcp) → vire-server

运输兼容性

客户端传输如何连接
Claude Code(CLI)可流式HTTP将HTTP URL直接发送到门户
克劳德桌面(claude_desktop_config.json) 。 vire-mcp 二进制或Docker
克劳德桌面(连接器UI)HTTPS公共HTTPS URL(生产)

克劳德代码(CLI)

Claude Code原生支持流式HTTP和MCP OAuth。在第一次连接时,Claude Code会自动运行OAuth流(打开浏览器、用户登录、接收令牌)。

添加 ~/.claude.json (全球)或项目 .mcp.json (每个项目):

{
  "mcpServers": {
    "vire": {
      "type": "url",
      "url": "http://localhost:8881/mcp"
    }
  }
}

对于已部署的HTTPS门户:

{
  "mcpServers": {
    "vire": {
      "type": "url",
      "url": "https://portal.vire.app/mcp"
    }
  }
}

Claude Desktop(通过vire mcp进行本地开发)

Claude Desktop需要本地MCP服务器的stdio传输。 vire-mcp 是一个stdio到HTTP的网桥,它作为MCP客户端连接到vire门户,发现工具,并通过stdio重新公开它们。

两种连接方式:

模式环境变量Auth用例
OAuthVIRE_PORTAL_URL浏览器OAuth流,缓存令牌具有交互式登录的本地开发人员
直接的VIRE_MCP_URLURL中的加密UID(无浏览器)Docker、CI/CD、无头环境

配置:

vire-mcp 自动从以下位置发现其TOML配置 vire-mcp.tomlconfig/vire-mcp.toml (首先检查二进制相对路径)。环境变量覆盖配置值:

环境变量默认值描述
VIRE_PORTAL_URLhttp://localhost:8080vire门户URL(OAuth模式)
VIRE_MCP_URL--带有加密UID的完整MCP端点URL(直接模式,绕过OAuth)
VIRE_LOG_LEVELinfo日志级别(调试、信息、警告、错误)

日志被写入 bin/logs/vire-mcp.log (相对于二进制)。启动日志显示解析的URL和模式:

level=INF message="loaded configuration" direct_mode=true mcp_url="http://localhost:8881/mcp/abc123..."
level=INF message="loaded configuration" direct_mode=false portal_url="http://localhost:8881"

直接模式(推荐用于Docker/WSL)

直接模式使用嵌入用户身份的加密MCP端点URL,完全绕过OAuth。从vire门户仪表板(配置文件>MCP配置)获取您的唯一端点URL。

{
  "mcpServers": {
    "vire": {
      "command": "/path/to/bin/vire-mcp",
      "env": {
        "VIRE_MCP_URL": "http://localhost:8881/mcp/YOUR_ENCRYPTED_UID",
        "VIRE_LOG_LEVEL": "error"
      }
    }
  }
}

OAuth模式(交互式登录)

OAuth模式在首次启动时运行浏览器流;令牌缓存在 ~/.vire/credentials.json.

{
  "mcpServers": {
    "vire": {
      "command": "/path/to/bin/vire-mcp",
      "env": {
        "VIRE_PORTAL_URL": "http://localhost:8881"
      }
    }
  }
}

Windows上的WSL(直接模式)

在WSL中使用vire-mcp在Windows上运行时,使用shell包装器传递环境变量( env 对象未通过 wsl -e):

{
  "mcpServers": {
    "vire": {
      "command": "wsl",
      "args": [
        "-e",
        "/bin/bash",
        "-c",
        "VIRE_MCP_URL=http://localhost:8881/mcp/YOUR_ENCRYPTED_UID VIRE_LOG_LEVEL=error /home/bobmc/development/vire-portal/bin/vire-mcp"
      ]
    }
  }
}
注:env Claude Desktop配置中的对象不传递变量 wsl -e.使用 /bin/bash -c "VAR=val command" 相反。

Docker(直接模式-短暂)

直接模式非常适合Docker:没有OAuth,没有凭据挂载,完全是短暂的:

{
  "mcpServers": {
    "vire": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "--network", "host",
        "-e", "VIRE_MCP_URL=http://localhost:8881/mcp/YOUR_ENCRYPTED_UID",
        "-e", "VIRE_LOG_LEVEL=error",
        "ghcr.io/bobmcallan/vire-mcp:latest"
      ]
    }
  }
}

Docker(OAuth模式-持久凭据)

OAuth模式要求卷装载以在容器运行中持久化令牌:

{
  "mcpServers": {
    "vire": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "--network", "host",
        "-e", "VIRE_PORTAL_URL=http://localhost:8881",
        "-v", "~/.vire:/root/.vire",
        "ghcr.io/bobmcallan/vire-mcp:latest"
      ]
    }
  }
}
注: 对于远程门户,请替换 localhost:8881 使用您的门户URL(例如。, https://portal.vire.app).

克劳德桌面(通过连接器制作)

对于已部署的HTTPS门户(例如。 https://portal.vire.app),通过添加服务器 设置>连接器 在克劳德桌面。连接器支持HTTPS URL并原生处理OAuth——否 vire-mcp 桥需要。

MCP OAuth 2.1流程

该门户使用PKCE S256和动态客户端注册实现了OAuth 2.1(RFC 9728)。此流由以下因素自动触发 mcp-remote 或克劳德桌面连接器:

1. Client discovers endpoints: GET /.well-known/oauth-authorization-server
2. Client registers: POST /register (Dynamic Client Registration)
3. Client redirects user: GET /authorize?client_id=...&code_challenge=...&state=...
4. Portal creates a pending session, sets mcp_session_id cookie, redirects to /
5. User logs in (email/password or Google/GitHub OAuth)
6. On login success, portal detects mcp_session_id cookie
7. Portal creates auth code, redirects to client's redirect_uri
8. Client exchanges code + code_verifier: POST /token
9. Portal verifies PKCE S256, mints JWT access token (1h) + refresh token (7d)
10. Client sends Authorization: Bearer  on all subsequent MCP requests

身份验证链

Claude (Bearer token or cookie)
  -> POST /mcp
    -> vire-portal: extract user ID from JWT "sub" claim
      -> proxy to vire-server with X-Vire-User-ID header
        -> vire-server: look up user's Navexa key from DB
          -> call Navexa API with user's key

门户从以下任一来源提取用户身份:

  1. Authorization: Bearer 标题(Claude Desktop通过mcp远程/连接器)
  2. vire_session cookie(网络仪表板)

承载令牌优先。如果两者都不存在,则请求将在没有用户上下文的情况下继续进行。

工具

工具从vire服务器动态注册 GET /api/mcp/tools 启动时的目录。目录定义了每个工具的名称、描述、HTTP方法、URL路径模板和参数(路径、查询、正文)。门户从目录条目构建MCP工具定义和通用处理程序。请参阅 vire服务器README 查看完整的工具目录。

X-Vire-\*接头

代理在每次向vire服务器发出请求时都会注入这些标头:

标题来源描述
X-Vire-PortfoliosVIRE_DEFAULT_PORTFOLIO env var逗号分隔的投资组合名称
X-Vire-Display-CurrencyVIRE_DISPLAY_CURRENCY env var显示值的货币
X-Vire-User-ID会话cookie(按请求)JWT子声明中的用户名

静态标头是在每次请求时从环境变量中设置的。当 vire_session cookie存在——处理程序解码JWT子声明并注入用户ID。vire服务器从用户ID内部解析用户的navexa密钥。

认证流程

门户通过vire服务器对用户进行身份验证。支持三种登录方式:电子邮件/密码、Google OAuth和GitHub OAuth。vire服务器处理凭证验证和令牌交换;门户从不直接接触密码或OAuth机密。

电子邮件/密码登录

1. User submits email + password on the landing page
2. Portal forwards to vire-server: POST /api/auth/login { username, password }
3. vire-server validates credentials, returns a signed JWT
4. Portal sets the JWT as an httpOnly "vire_session" cookie
5. User is redirected to /dashboard

OAuth登录(谷歌/GitHub)

1. User clicks "Sign in with Google" (or GitHub) on /
   -> Browser navigates to portal: GET /api/auth/login/google
2. Portal makes a server-side request to vire-server: GET {API_URL}/api/auth/login/google?callback={callbackURL}
3. vire-server returns a 302 redirect to the OAuth provider's consent screen
4. Portal forwards the redirect Location to the browser (never exposing internal API URLs)
5. User authorises, provider redirects back to vire-server
6. vire-server exchanges code for tokens, creates/updates user, mints a JWT
7. vire-server redirects to portal: GET /auth/callback?token=
8. Portal sets the JWT as an httpOnly "vire_session" cookie
9. User is redirected to /dashboard

服务器端代理阻止内部Docker地址(如 http://server:8080)避免暴露在浏览器中。

callback_url 配置设置告诉门户在OAuth完成后,病毒服务器应重定向到何处。这必须与每个OAuth提供者注册的URL匹配。

MCP OAuth 2.1流程(克劳德桌面版)

当Claude Desktop连接时,它会使用PKCE执行完整的OAuth 2.1流程:

1. Claude Desktop discovers endpoints: GET /.well-known/oauth-authorization-server
2. Claude Desktop registers: POST /register (Dynamic Client Registration)
3. Claude Desktop redirects user: GET /authorize?client_id=...&code_challenge=...&state=...
4. Portal creates a pending session, sets mcp_session_id cookie, redirects to /
5. User logs in (email/password or OAuth — same flows as above)
6. On login success, portal detects mcp_session_id cookie
7. Portal calls CompleteAuthorization: creates auth code, redirects to Claude Desktop's redirect_uri
8. Claude Desktop exchanges code + code_verifier: POST /token
9. Portal verifies PKCE S256, mints a JWT access token (1h) + refresh token (7d)
10. Claude Desktop sends Bearer token on all subsequent MCP requests

JWT索赔

门户OAuth服务器生成的令牌包含:

索赔描述
sub用户ID(来自病毒服务器)
scope授权范围(例如。 openid portfolio:read tools:invoke)
client_idOAuth客户端ID
iss门户基础URL
iat / exp签发/到期(1小时)

令牌是使用HMAC-SHA256签名的 auth.jwt_secret 配置值。

OAuth提供程序配置

提供商范围
谷歌 openid, email, profile
GitHubread:user, user:email

API与病毒服务器的合同

门户只与病毒服务器的REST API进行通信。所有请求/响应体都是JSON。

错误响应格式

所有端点都以一致的形状返回错误:

{
  "error": {
    "code": "invalid_key",
    "message": "EODHD API returned 401 -- check that the key is correct and has an active subscription"
  }
}
HTTP状态含义门户操作
400错误请求(缺少必填字段)显示验证错误
401JWT已过期或无效尝试刷新令牌;如果刷新失败,请重定向到 /
403禁止(帐户已挂起)显示帐户状态消息
404找不到资源显示“找不到”状态
409冲突(例如,已配置)幂等处理(显示现有资源)
422验证失败(例如,API密钥无效)显示响应中的字段级错误
429速率受限显示重试消息
500服务器错误显示带有重试选项的一般错误

身份验证路由(未经身份验证)

GET /api/auth/login/:provider

通过vire服务器代理OAuth重定向。门户向vire服务器发出服务器端HTTP请求,vire服务器向OAuth提供者返回302重定向。门户将重定向位置转发到浏览器。这可以防止内部Docker地址到达浏览器。

参数类型说明
:provider路径googlegithub

答复: 302重定向到提供商OAuth URL(从vire服务器转发)。错误:302重定向到 /error?reason=auth_unavailable/error?reason=auth_failed.

POST /api/auth/callback

为会话交换OAuth授权码。这 state 参数由网关在以下期间生成 GET /api/auth/login/:provider 并通过OAuth流程。

请求正文:

{
  "provider": "google",
  "code": "4/0AY0e-g...",
  "state": "value-from-callback-query-params"
}

答复(200):

{
  "token": "eyJhbG...",
  "user": {
    "user_id": "uuid",
    "email": "alice@example.com",
    "display_name": "Alice",
    "avatar_url": "https://...",
    "auth_provider": "google",
    "created_at": "2026-02-09T10:00:00Z",
    "status": "active",
    "keys_configured": false,
    "plan": "free"
  }
}

还设置 refresh_token 如httpOnly、Secure、SameSite=Lax cookie。

POST /api/auth/refresh

使用httpOnly刷新令牌cookie刷新过期的JWT。

请求正文: 无(cookie自动发送)。

答复(200):

{
  "token": "eyJhbG..."
}

回复(401): 刷新令牌已过期或无效。用户必须重新进行身份验证。

POST /api/auth/logout

清除刷新令牌cookie并使会话无效。

答复(200):

{
  "status": "ok"
}

配置文件路线(需要JWT)

GET /api/profile

返回经过身份验证的用户的个人资料。

答复(200):

{
  "user_id": "uuid",
  "email": "alice@example.com",
  "display_name": "Alice",
  "avatar_url": "https://...",
  "auth_provider": "google",
  "created_at": "2026-02-09T10:00:00Z",
  "status": "active",
  "keys_configured": true,
  "default_portfolio": "SMSF",
  "portfolios": ["SMSF", "Personal"],
  "exchange": "AU",
  "plan": "pro",
  "proxy_url": "https://vire-mcp-a1b2c3-xyz.run.app",
  "proxy_status": "running",
  "provisioned_at": "2026-02-09T10:01:00Z"
}

PUT /api/profile

更新用户首选项。

请求正文(部分更新——仅包括要更改的字段):

{
  "default_portfolio": "Personal",
  "portfolios": ["SMSF", "Personal"],
  "exchange": "AU"
}

可编辑字段: default_portfolio, portfolios, exchange.其他配置文件字段(log_level, display_name, avatar_url)存在于Firestore数据模型中,但在第一阶段中不会公开用于门户编辑。标识字段(email, auth_provider, user_id)基础设施领域(proxy_url, provisioned_at)始终是只读的。

答复(200): 更新了配置文件对象(形状与GET/api/profile相同)。

DELETE /api/profile

删除用户帐户。触发MCP代理的取消配置、秘密清理和数据删除(30天宽限期)。

答复(200):

{
  "status": "deleted",
  "grace_period_ends": "2026-03-11T10:00:00Z"
}

API关键路线(需要JWT)

PUT /api/profile/keys

设置或更新一个或多个API密钥(BYOK)。网关在存储之前根据提供程序的API验证每个密钥。密钥存储在密钥管理器中,而不是存储在Firestore中。

请求正文:

{
  "eodhd_key": "abc123...",
  "navexa_key": "def456...",
  "gemini_key": "ghi789..."
}

所有字段都是可选的——只包括要设置或更新的键。

答复(200):

{
  "eodhd_key": {
    "status": "valid",
    "last4": "c123",
    "validated_at": "2026-02-09T10:05:00Z"
  },
  "navexa_key": {
    "status": "valid",
    "last4": "f456",
    "validated_at": "2026-02-09T10:05:00Z",
    "portfolios_found": 2
  },
  "gemini_key": {
    "status": "valid",
    "last4": "i789",
    "validated_at": "2026-02-09T10:05:00Z"
  }
}

响应(422)--验证失败:

{
  "eodhd_key": {
    "status": "invalid",
    "error": "API returned 401 -- check that the key is correct and has an active subscription"
  }
}

关键验证方法:

提供者验证终结点
EODHDGET /api/exchanges-list/?api_token={key}eostoricaldata.com
航海GET /v1/portfolios 随着 X-API-Key 标题navexa.io
双子座models.list 通过SDKgenerativelanguage.googleapis.com

DELETE /api/profile/keys/:id

删除特定的API密钥。

参数类型说明
:id路径eodhd_key, navexa_key,或 gemini_key

答复(200):

{
  "status": "removed",
  "key_name": "navexa_key"
}

供应路线(需要JWT)

POST /api/profile/provision

为用户提供专用的MCP代理(云运行服务)。至少需要 eodhd_key 待配置。

答复(200):

{
  "status": "provisioned",
  "proxy_url": "https://vire-mcp-a1b2c3-xyz123abc-ts.a.run.app",
  "provisioned_at": "2026-02-09T10:01:00Z"
}

答复(400): EODHD密钥未配置。

答复(409): 已配置(返回现有的proxy_url)。

GET /api/profile/mcp

返回已准备好进行复制粘贴的MCP连接配置块。

答复(200):

{
  "proxy_url": "https://vire-mcp-a1b2c3-xyz123abc-ts.a.run.app",
  "claude_code_config": {
    "mcpServers": {
      "vire": {
        "type": "http",
        "url": "https://vire-mcp-a1b2c3-xyz123abc-ts.a.run.app/mcp"
      }
    }
  },
  "claude_desktop_config": {
    "mcpServers": {
      "vire": {
        "url": "https://vire-mcp-a1b2c3-xyz123abc-ts.a.run.app/mcp"
      }
    }
  }
}

GET /api/profile/status

返回用户MCP代理的运行状况和活动状态。

答复(200):

{
  "proxy_status": "running",
  "last_activity": "2026-02-09T12:30:00Z",
  "proxy_url": "https://vire-mcp-a1b2c3-xyz123abc-ts.a.run.app"
}

可能的 proxy_status 值: running, stopped, not_provisioned, throttled.

使用路线(需要JWT)

GET /api/usage

返回当前计费周期的使用统计信息。

答复(200):

{
  "period": "2026-02",
  "total_requests": 3421,
  "quota_limit": 10000,
  "quota_remaining": 6579,
  "status": "active",
  "daily_counts": [
    { "date": "2026-02-01", "count": 120 },
    { "date": "2026-02-02", "count": 185 }
  ],
  "top_endpoints": [
    { "endpoint": "portfolio_compliance", "count": 842 },
    { "endpoint": "get_summary", "count": 521 },
    { "endpoint": "compute_indicators", "count": 498 }
  ]
}

计费路线(需要JWT)

POST /api/billing/checkout

创建Stripe Checkout会话以升级到Pro。

答复(200):

{
  "checkout_url": "https://checkout.stripe.com/c/pay/..."
}

前端将浏览器重定向到 checkout_url 通过 window.location.href = checkout_urlStripe处理付款。完成后,Stripe将重定向回 https://${DOMAIN}/billing?session_id={CHECKOUT_SESSION_ID} --网关在创建会话时配置此返回URL。网关接收Stripe webhook以更新用户的计划。

POST /api/billing/portal

创建条纹计费门户会话,用于管理订阅、查看发票和取消。

答复(200):

{
  "portal_url": "https://billing.stripe.com/p/session/..."
}

前端将浏览器重定向到 portal_url.

好的,带上你自己的钥匙

用户必须提供自己的API密钥。Vire不代理或转售API访问权限。

关键配置用户体验

配置文件页面显示三个关键字段:

服务必需提供什么从哪里获取密钥
EODHD股票价格、基本面、筛选Eodhisions数据网 ->定价->API密钥
纳维克萨从您的经纪商同步投资组合航空公司.io ->设置->API
谷歌双子座人工智能驱动的文件分析、新闻情报aistudio.google.com/apikey

显示状态

每个关键字段显示一个(80年代黑白设计——无颜色,仅文本指示器):

  • 未配置 --虚线边框,“未设置”标签,带“添加”按钮的空输入
  • 已配置 --实心边界, [OK] ::::abcd 显示最后4个字符,“更新”和“删除”按钮
  • 无效 --实心边界, [ERR] 带有验证错误消息的标签

安全

  • 密钥使用云KMS加密并存储在GCP密钥管理器中
  • 密钥从不存储在Firestore中(配置文件仅包含一个引用)
  • 密钥从不被记录或共享
  • 输入后,只有最后4个字符可见
  • 每个密钥都直接注入到用户的专用MCP代理中,作为Cloud Run秘密参考

Dockerfile

位于 docker/Dockerfile与病毒生态系统模式相匹配的多阶段构建。第一阶段构建Go二进制文件;第二阶段在阿尔卑斯山进行。

# Build stage
FROM golang:1.25-alpine AS builder
WORKDIR /build
ARG VERSION=dev
ARG BUILD=unknown
ARG GIT_COMMIT=unknown
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build \
    -ldflags="-s -w \
    -X 'github.com/bobmcallan/vire-portal/internal/config.Version=${VERSION}' \
    -X 'github.com/bobmcallan/vire-portal/internal/config.Build=${BUILD}' \
    -X 'github.com/bobmcallan/vire-portal/internal/config.GitCommit=${GIT_COMMIT}'" \
    -o vire-portal ./cmd/vire-portal

# Runtime stage
FROM alpine:3.21
LABEL org.opencontainers.image.source="https://github.com/bobmcallan/vire-portal"
WORKDIR /app
RUN apk --no-cache add ca-certificates wget
COPY --from=builder /build/vire-portal .
COPY --from=builder /build/pages ./pages
COPY --from=builder /build/docker/vire-portal.toml .
COPY .version .
RUN mkdir -p /app/logs
EXPOSE 8080
HEALTHCHECK NONE
ENTRYPOINT ["./vire-portal"]

GitHub操作工作流

测试 (.github/workflows/test.yml):在PR上运行并推送到main。两份工作: unit-test (去兽医+去测试。/内部/…)和 ui-test (无头Chrome浏览器通过 ./scripts/ui-test.sh all).测试工件在失败时上传。

发布 (.github/workflows/release.yml):在推送到主标签或版本标签时,为vire门户和vire mcp构建Docker镜像并将其推送到GHCR(矩阵策略):

  • 从中提取版本 .version 文件
  • 将VERSION、BUILD、GIT_COMMIT作为Docker构建参数传递
  • 标签: latest (上主),语义版本,短SHA
  • 将GitHub Actions缓存用于Docker层

.version文件

.version 项目根目录下的文件是唯一的事实来源:

version: 0.1.2
build: 02-14-20-27-29
  • version: 是语义版本
  • build: 是上次构建的时间戳,由构建/部署脚本自动更新
  • 两者 deploy.shbuild.sh 将VERSION、BUILD和GIT_COMMIT作为Docker构建参数注入
  • CI工作流程(release.yml)使用相同的版本提取模式

数据层

门户是无状态的,它不在本地存储任何用户数据。所有用户配置文件和设置都由vire-server通过其REST API进行管理:

  • GET /api/users/{id} --获取用户配置文件(用户名、电子邮件、角色、navexa密钥状态)
  • PUT /api/users/{id} --更新用户字段(例如,navexa键)

API客户端位于 internal/client/vire_client.go.

项目结构

vire-portal/
├── .github/
│   └── workflows/
│       ├── release.yml              # Docker build + GHCR push (matrix: portal + mcp)
│       └── test.yml                 # CI: go vet, unit tests, UI tests on PR/push
├── cmd/
│   ├── vire-portal/
│   │   └── main.go                  # Portal entry point (flag parsing, config, graceful shutdown)
│   └── vire-mcp/
│       ├── main.go                  # Stdio-to-HTTP bridge (connects to vire-portal)
│       ├── oauth.go                 # OAuth 2.1 browser flow + local callback server
│       └── tokenstore.go            # File-based OAuth token persistence (~/.vire/credentials.json)
├── internal/
│   ├── app/
│   │   └── app.go                   # Dependency container (Config, Logger, Handlers, OAuthServer)
│   ├── auth/
│   │   ├── authorize.go             # GET /authorize handler (PKCE, session tracking, auto-register)
│   │   ├── dcr.go                   # POST /register handler (RFC 7591 Dynamic Client Registration)
│   │   ├── discovery.go             # .well-known OAuth discovery endpoints
│   │   ├── pkce.go                  # PKCE S256 verification (constant-time compare)
│   │   ├── server.go                # OAuthServer (central state, JWT minting, auth completion)
│   │   ├── session.go               # SessionStore for pending MCP auth sessions (TTL 10 min)
│   │   ├── store.go                 # ClientStore, CodeStore, TokenStore (in-memory, mutex-protected)
│   │   └── token.go                 # POST /token handler (auth_code + refresh_token grants)
│   ├── config/
│   │   ├── config.go                # TOML loading with defaults -> file -> env -> CLI priority
│   │   ├── config_test.go
│   │   ├── defaults.go              # Default configuration values
│   │   ├── version.go               # Version info (ldflags + .version file)
│   │   └── version_test.go
│   ├── handlers/
│   │   ├── auth.go                  # OAuth auth handlers (dev login, Google/GitHub redirects, callback, logout, JWT validation)
│   │   ├── auth_test.go             # Auth handler tests (ValidateJWT, IsLoggedIn, OAuth flows)
│   │   ├── auth_integration_test.go # Integration tests (full login round-trip, OAuth chains)
│   │   ├── auth_stress_test.go      # Security stress tests (alg:none attack, tampering, timing, hostile inputs)
│   │   ├── dashboard.go             # GET /dashboard (portfolio management, holdings)
│   │   ├── strategy.go             # GET /strategy (portfolio strategy and plan editors)
│   │   ├── mcp_page.go             # GET /mcp-info (MCP connection config, tools catalog)
│   │   ├── handlers_test.go
│   │   ├── health.go                # GET /api/health
│   │   ├── helpers.go               # WriteJSON, RequireMethod, WriteError
│   │   ├── landing.go               # PageHandler (template rendering + static file serving)
│   │   ├── profile.go               # GET/POST /profile (user info + Navexa API key management)
│   │   └── version.go               # GET /api/version
│   ├── cache/
│   │   ├── cache.go                 # API response cache (TTL, max entries, prefix invalidation)
│   │   └── cache_test.go
│   ├── client/
│   │   ├── vire_client.go           # HTTP client for vire-server user API (GetUser, UpdateUser)
│   │   └── vire_client_test.go
│   ├── mcp/
│   │   ├── catalog.go               # Dynamic tool catalog types, FetchCatalog, BuildMCPTool, GenericToolHandler
│   │   ├── context.go               # UserContext (per-request user identity for proxy headers)
│   │   ├── handler.go               # MCP HTTP handler (Streamable HTTP + JWT auth, catalog fetch at startup)
│   │   ├── handler_test.go          # Tests: withUserContext, extractJWTSub
│   │   ├── handler_stress_test.go   # Stress tests: hostile cookies, concurrent access, binary garbage
│   │   ├── handlers.go              # errorResult helper, resolvePortfolio
│   │   ├── mcp_test.go              # Tests: catalog, validation, tools, handlers, proxy, integration
│   │   ├── proxy.go                 # HTTP proxy to vire-server with X-Vire-* headers
│   │   ├── tools.go                 # RegisterToolsFromCatalog (dynamic registration)
│   │   ├── version.go               # Combined get_version handler (vire_portal + vire_server)
│   │   └── version_test.go          # Version handler tests
│   ├── server/
│   │   ├── middleware.go             # Correlation ID, logging, CORS, recovery
│   │   ├── middleware_test.go
│   │   ├── route_helpers.go          # RouteByMethod, RouteResourceCollection
│   │   ├── route_helpers_test.go
│   │   ├── routes.go                 # Route registration
│   │   ├── routes_test.go
│   │   └── server.go                 # HTTP server (net/http, timeouts, graceful shutdown)
│   └── vire/                         # Shared packages (migrated from vire repo)
│       ├── common/                   # Version, logging, config, formatting helpers
│       ├── interfaces/               # Service and storage interface contracts
│       └── models/                   # Data structures (portfolio, market, strategy, etc.)
├── tests/
│   ├── common/                       # Test utilities (browser, config, containers, screenshot)
│   │   ├── browser.go                # chromedp helpers (NewBrowserContext, NavigateAndWait, etc.)
│   │   ├── containers.go             # Docker test env (SurrealDB + vire-server + portal via testcontainers)
│   │   ├── testconfig.go             # Test config loader (TOML, results dir, timestamps)
│   │   └── screenshot.go             # Screenshot capture utility
│   ├── docker/                       # Docker configs for test environment
│   │   ├── docker-compose.yml        # Full test stack (SurrealDB + vire-server + portal)
│   │   ├── Dockerfile.server         # Portal test image (multi-stage build)
│   │   └── portal-test.toml          # Portal test configuration
│   └── ui/                           # UI browser tests
│       ├── test_config.toml          # Test configuration (server URL, browser settings)
│       ├── ui_helpers_test.go        # Test helpers (newBrowser, isVisible, etc.)
│       ├── smoke_test.go             # Smoke tests (landing, dashboard, branding)
│       ├── dashboard_test.go         # Dashboard tests (sections, panels, growth chart, design rules)
│       ├── strategy_test.go         # Strategy page tests (editors, nav, portfolio selector)
│       ├── capital_test.go          # Capital page tests (cash transactions, pagination, summary)
│       ├── nav_test.go               # Navigation tests (hamburger, dropdown, mobile)
│       └── auth_test.go              # Auth tests (Google/GitHub login redirects)
├── pages/
│   ├── dashboard.html                # Dashboard page (portfolio selector, holdings, capital performance, indicators, growth chart, refresh)
│   ├── strategy.html                # Strategy page (portfolio strategy and plan editors)
│   ├── cash.html                     # Cash page (cash transactions ledger, paged table)
│   ├── mcp.html                     # MCP info page (connection details, tools table)
│   ├── landing.html                  # Landing page (Go html/template)
│   ├── profile.html                  # Profile page (user info + Navexa API key management)
│   ├── partials/
│   │   ├── head.html                 # HTML head (IBM Plex Mono, Chart.js CDN, Alpine.js CDN)
│   │   ├── nav.html                  # Navigation bar
│   │   └── footer.html               # Footer
│   └── static/
│       ├── css/
│       │   └── portal.css            # 80s B&W aesthetic (no border-radius, no box-shadow)
│       └── common.js                 # Client logging, Alpine.js init, vireStore (fetch cache/dedup), portfolioDashboard() (growth chart), cashTransactions(), portfolioStrategy()
├── docker/
│   ├── Dockerfile                    # Portal multi-stage build (golang:1.25 -> alpine)
│   ├── Dockerfile.mcp               # MCP stdio binary build (golang:1.25 -> alpine)
│   ├── docker-compose.yml            # Portal + vire-server stack
│   ├── docker-compose.dev.yml        # Dev overlay (VIRE_ENV=dev, used by deploy.sh local)
│   ├── docker-compose.ghcr.yml       # GHCR pull + watchtower auto-update
│   ├── vire-portal.toml              # Portal configuration
│   └── README.md                     # Docker usage documentation
├── docs/
│   ├── requirements.md               # API contracts and architecture
│   ├── architecture-comparison.md
│   └── authentication/
│       └── mcp-oauth-implementation-steps.md  # MCP OAuth implementation plan (Phase 1 complete)
├── scripts/
│   ├── deploy.sh                     # Deploy orchestration (local/ghcr/down/prune)
│   ├── build.sh                      # Docker image builder (--portal, --mcp, or both)
│   ├── run.sh                        # Build + start/stop/restart server locally
│   ├── ui-test.sh                    # UI test runner (smoke, dashboard, nav, auth, all)
│   ├── verify-auth.sh                # Auth endpoint validation (health, login, OAuth, MCP)
│   └── test-scripts.sh               # Validation suite for scripts and configs
├── .dockerignore
├── .version                          # Version metadata (source of truth)
├── go.mod
├── go.sum
├── .gitignore
├── LICENSE
└── README.md

Docker(本地)

该项目包括与以下内容匹配的部署脚本 维尔 项目模式。看 docker/README.md 了解全部细节。

服务栈(门户+病毒服务器)

推荐的部署通过docker compose将门户和vire服务器一起运行:

# Build portal and start services (uses dev compose overlay)
./scripts/deploy.sh local

这将开始:

  • vire门户 在端口8881上--登录页+MCP端点(开发模式)
  • vire服务器 在端口8882上--后端API(从GHCR获取)

本地部署自动使用 docker-compose.dev.yml 作为一个合成叠加,它设置 VIRE_ENV=dev 启用开发模式(开发登录等)。基础 docker-compose.yml 对于类似prod的构建保持不变。

门户通过以下方式连接到vire服务器 VIRE_API_URL=http://vire-server:8080 (Docker内部网络)。克劳德代码连接到 http://localhost:8881/mcp (门户)。Claude Desktop使用 vire-mcp 通过stdio(见上文“连接克劳德”一节)。

仅限门户

# Build and run portal standalone (MCP proxy will fail without vire-server)
./scripts/deploy.sh local

# Build with forced rebuild (no cache)
./scripts/deploy.sh local --force

# Deploy from GHCR with watchtower auto-update
./scripts/deploy.sh ghcr

# Stop all containers
./scripts/deploy.sh down

# Prune stopped containers and dangling images
./scripts/deploy.sh prune

或者,构建一个没有docker compose的独立镜像:

# Build Docker image with version injection
./scripts/build.sh

# Build with verbose output
./scripts/build.sh --verbose

# Clean existing images and rebuild
./scripts/build.sh --clean

或者直接使用docker:

# Build the Docker image
docker build -f docker/Dockerfile -t vire-portal:latest .

# Run on host port 8881
docker run -p 8881:8080 \
  -e VIRE_SERVER_HOST=0.0.0.0 \
  -e VIRE_API_URL=http://host.docker.internal:8080 \
  vire-portal:latest

云运行部署

该门户在Cloud Run上运行,通过vire-infraTerraform部署。Terraform模块位于 infra/modules/portal/main.tf 在vire-infrarepo中。

从Terraform配置

# Simplified from vire-infra/infra/modules/portal/main.tf
resource "google_cloud_run_v2_service" "portal" {
  name     = "vire-portal"
  location = var.region
  ingress  = "INGRESS_TRAFFIC_ALL"

  template {
    scaling {
      min_instance_count = 0
      max_instance_count = 3
    }
    containers {
      image = "ghcr.io/bobmcallan/vire-portal:latest"
      ports { container_port = 8080 }
      env { name = "VIRE_SERVER_HOST"; value = "0.0.0.0" }
      env { name = "VIRE_SERVER_PORT"; value = "8080" }
      env { name = "VIRE_LOG_LEVEL";   value = "info" }
      resources {
        limits = { cpu = "1", memory = "256Mi" }
      }
    }
  }
}

关键属性:

  • 图片: ghcr.io/bobmcallan/vire-portal:latest (由该仓库的GitHub Actions发布)
  • 端口: 8080
  • 进入: 全部(公共网站)
  • 认证: 未经身份验证(通过IAM allUsers公开访问)
  • 缩放比例: 0-3个实例(空闲时缩放为零)
  • 资源: 1个CPU,256Mi内存

释放

推送版本标签以触发GitHub Actions工作流:

git tag v0.1.2
git push origin v0.1.2

这建立并推动 ghcr.io/bobmcallan/vire-portal 带有版本标签和 :latest GHCR。

推到 main 也会触发一个构建 :latest 标签。您可以使用“运行工作流”从“操作”选项卡手动触发构建。

vire infraTerraform参考 ghcr.io/bobmcallan/vire-portal:latest推送新映像后,重新应用Terraform或更新Cloud Run服务以拉取新映像。

体系结构上下文

该门户在Docker Compose堆栈中与vire服务器一起运行。 vire-mcp 是Claude Desktop推出的临时stdio到HTTP网桥:

     ┌───────────────┐    ┌──────────────────┐    ┌──────────────────┐
     │     User      │    │  Claude Code /    │    │  Claude Desktop  │
     │   (browser)   │    │  Claude CLI       │    │                  │
     └───────┬───────┘    └────────┬──────────┘    └────────┬─────────┘
             │ GET /               │ HTTP + OAuth           │ stdio
             │                     │                        │
             │                     │               ┌────────┴─────────┐
             │                     │               │  vire-mcp        │
             │                     │               │  - stdio ↔ HTTP  │
             │                     │               │  - OAuth 2.1     │
             │                     │               │  - Token cache   │
             │                     │               └────────┬─────────┘
             │                     │                        │ HTTP + OAuth
    ┌────────┴─────────────────────┴────────────────────────┴─────────┐
    │  vire-portal (:8881)                                            │
    │  - Landing page, dashboard, strategy, profile                    │
    │  - MCP endpoint (Streamable HTTP at POST /mcp)                  │
    │  - OAuth 2.1 (DCR, PKCE, token exchange)                        │
    │  - Dynamic tool catalog from vire-server                        │
    │  - X-Vire-* header injection, JWT auth                          │
    └────────────────────────────┬────────────────────────────────────┘
                                 │ REST API proxy
    ┌────────────────────────────┴────────────────────────────────────┐
    │  vire-server (:8080)                                            │
    │  - Portfolio analysis, market data                              │
    │  - Report generation, stock screening                           │
    │  - Strategy and plan management                                 │
    └─────────────────────────────────────────────────────────────────┘

所有工具逻辑都存在于vire门户中。 vire-mcp 是一个纯传输适配器,它不导入 internal/mcp 或直接联系vire服务器。该门户实现了OAuth 2.1,因此Claude Code和Claude Desktop(通过vire mcp)都对用户进行身份验证,并接收每个用户的JWT令牌。vire-server从中的用户ID解析用户的API密钥(Navexa、EODHD等) X-Vire-User-ID 头球

许可证

私有存储库。

目录标签

目录标签

代理服务GoClaude投资平台本地部署MCP服务器Web应用OAuth2.1

支持客户端

Claude DesktopClaude

接入字段

传输方式(transport,传输协议)

stdio

鉴权方式(authType,认证方式)

oauth

运行时(runtime,运行环境)

Docker

工具数量(toolCount,工具数)

0

资源数量(resourceCount,资源数)

0

提示词数量(promptCount,提示词数)

0

权限和风险

stdiooauth部署方式未说明

接入前请确认传输方式、认证方式和部署位置,并根据实际工具能力限制访问范围。

安装前确认

不要直接授予不必要的文件、网络或账号权限;先核对安装命令和配置内容。

来源信息

继续浏览同类 MCP