Skip to main content
Glama
mcp-resource-server

mcp-resource-server

by curtismu7

mcp-resource-server

一个 MCP 服务器,在十个模拟垂直领域(银行、医疗保健、政府、制造业、零售、体育用品、大学、劳动力、Abercrombie & Fitch、航空公司)中公开只读工具,外加一组投资工具。每个垂直领域都读取自己捆绑的 SQLite 数据库——运行它不需要其他服务。投资工具可以选择代理到你提供的银行 API(见下文)。

先决条件

  • Docker + Docker Compose

  • 一个用于铸造和验证 bearer token 的 PingOne 环境(或任何能够签发具有正确 aud/scope 声明的 JWT 并发布 JWKS 端点的 OAuth AS——此服务器仅依赖标准 OIDC 发现,不依赖任何 PingOne 特有的东西)

Related MCP server: mock-mcp

快速开始

cp .env.example .env

编辑 .env:为你自己的 PingOne 环境设置 MCP_RESOURCE_SERVER_RESOURCE_URI、PINGONE_ENVIRONMENT_ID 和 PINGONE_REGION。在你有真实 token 之前,保持 PINGONE_ISSUER 被注释掉(见“认证模式”)。

docker compose up --build

服务器监听在 http://localhost:8081。SQLite 数据库持久化在 ./data 中(首次使用时从 seed/ 播种;重启永远不会重新播种非空数据库)。

验证它正在运行

curl http://localhost:8081/health
curl http://localhost:8081/.well-known/oauth-protected-resource

第二个调用返回资源的广告作用域,如果设置了 PINGONE_ENVIRONMENT_ID/PINGONE_REGION,还返回其授权服务器——这是 MCP 客户端用于 OAuth 发现的 RFC 9728 元数据。

认证模式

token 的签名是否被验证由是否配置了 JWKS 源决定——而不是由 STRICT_AUTH 决定:

  • 设置了 JWKS 源(PINGONE_ISSUER、PINGONE_JWKS_URI 或 PINGONE_BASE_URL)——每个 token 都会根据你的 PingOne 环境的密钥进行验证,失败则拒绝。STRICT_AUTH 没有效果。一旦真实 token 开始流动,就以这种方式运行。

  • 没有 JWKS 源——STRICT_AUTH=false(随附的默认值)接受格式良好的 token 并带有控制台警告,这样你可以在 PingOne 接入之前用手工制作的 token 练习每个工具;STRICT_AUTH=true 则拒绝所有 token。不要让默认值被超过你预期的人访问。

.env.example 随附时所有三个 JWKS 变量都被注释掉,原因就在于此——当你有真实 token 时,取消注释其中一个。

获取 bearer token

每次工具调用都需要一个 bearer token,其 aud 声明必须匹配 MCP_RESOURCE_SERVER_RESOURCE_URI,其 scope 声明必须覆盖你正在调用的工具(参见 tools/list 获取权威的、当前的列表——它是从这个服务器自己的注册表生成的)。

本地测试(STRICT_AUTH=false)——任何具有正确声明的格式良好的 JWT 都可以;签名不会被检查。

node -e "
const b64 = s => Buffer.from(JSON.stringify(s)).toString('base64url');
const header = b64({alg:'none',typ:'JWT'});
const payload = b64({sub:'test-user',scope:'airlines:read',aud:'your-resource-uri',exp:Math.floor(Date.now()/1000)+3600});
console.log(header+'.'+payload+'.');
"

(将 aud 替换为你自己的 MCP_RESOURCE_SERVER_RESOURCE_URI 值,将 scope 替换为你正在测试的任何工具)

真实 token(STRICT_AUTH=true)——从你的 PingOne 环境铸造一个。针对你的 PingOne token 端点的 client-credentials 授权,请求此服务器的受众作为资源:

curl -s -X POST "https://auth.pingone.<region>/<env-id>/as/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=<your PingOne worker app client id>" \
  -d "client_secret=<your PingOne worker app client secret>" \
  -d "scope=<space-separated scopes, e.g. banking:read airlines:read>" \
  -d "resource=<MCP_RESOURCE_SERVER_RESOURCE_URI value>"

这要求该客户端的应用在 PingOne 中被授权访问此资源和这些作用域(Applications → your app → Resources)——这是 PingOne 端的设置步骤,此服务器不会为你完成。

直接调用工具(健全性检查)

TOKEN="<paste a token from above>"
curl -s -X POST http://localhost:8081/mcp \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_airline_bookings","arguments":{}}}'

连接 MCP 客户端

服务器在同一端口上通过 WebSocket 和 HTTP(可流式,POST /mcp)说 MCP——ws://localhost:8081 或 http://localhost:8081/mcp。

MCP Inspector(官方开发工具——适用于任何服务器,并允许你设置手动标头,因此是测试此服务器最可靠的方式):

npx @modelcontextprotocol/inspector

将传输设置为“Streamable HTTP”,URL 设置为 http://localhost:8081/mcp,并在 Inspector 的连接设置中添加 Authorization: Bearer <token> 标头,然后再连接。

Claude Desktop / Cursor / Windsurf(静态配置,HTTP 传输):

{
  "mcpServers": {
    "mcp-resource-server": {
      "url": "http://localhost:8081/mcp",
      "transport": "http"
    }
  }
}
  • Claude Desktop:~/Library/Application Support/Claude/claude_desktop_config.json

  • Cursor:项目根目录中的 .cursor/mcp.json

  • Windsurf:~/.codeium/windsurf/mcp_config.json

这些配置没有用于静态 bearer token 的字段——当客户端调用受保护的工具时,它会读取 /.well-known/oauth-protected-resource,找到你的 PingOne 环境作为授权服务器,并提示你登录。这只有在你的 PingOne 环境为该特定 MCP 客户端应用注册了 OAuth 客户端,并使用该客户端自己的文档指定的重定向 URI 时才有效——这是 PingOne 端的设置步骤,在此服务器之外。编辑配置后重启客户端。

投资工具

get_investment_accounts、get_investment_balance、get_portfolio_summary 和 get_investment_transactions 开箱即用,从捆绑的 SQLite 数据库(data/invest.db,首次使用时从 seed/invest.seed.json 播种)读取,就像其他每个垂直领域一样。

仅当你运行自己的银行 API 并希望这四个工具将调用者的 bearer token 转发给它并返回它返回的任何内容时,才设置 BANKING_API_BASE_URL。设置后,捆绑的投资数据库将不被使用。

添加工具

工具是代码,不是配置。目录(tools/list)、每个工具的作用域门以及 /.well-known/oauth-protected-resource 中广告的 scopes_supported 都源自 src/tools/registry.ts 中的 ALL_TOOLS,因此你添加到垂直领域列表中的工具在重建镜像后(快速开始中的 --build 命令)就会在所有地方生效。

1. 向现有垂直领域添加工具

两次编辑,无需注册表更改:

  1. 将工具定义追加到该垂直领域的 src/tools/<vertical>Tools.ts 数组中,例如在 sportingGoodsTools.ts 中:

    {
      name: 'gear_return_status',
      description: 'Show the status of a sporting-goods return.',
      inputSchema: {
        type: 'object',
        properties: { orderId: { type: 'string', description: 'Order ID' } },
        required: ['orderId'],
      },
      requiredScopes: ['read'],   // the bearer token must carry every scope listed
      readOnly: true,
      intentHints: ['check my gear return'],   // required — tests/registry.test.ts asserts it
    },
  2. 在 src/tools/<vertical>ToolHandler.ts 的 switch 中添加匹配的 case 'gear_return_status':。处理器只返回 JSON——从该垂直领域的 src/db/<vertical>Db.ts 读取,或任何其他内容。

2. 添加新的垂直领域

与上面相同的两个文件(src/tools/<vertical>Tools.ts 导出 <VERTICAL>_TOOLS: McpToolDef[],以及 src/tools/<vertical>ToolHandler.ts 导出 dispatch<Vertical>Tool),另外:

  • 数据(可选):src/db/<vertical>Db.ts + seed/<vertical>.seed.json。复制 sportingGoodsDb.ts——它打开 data/<vertical>.db,创建模式,并且仅在表为空时应用种子。Dockerfile 已经复制了 seed/,compose 文件已经挂载了 data/。

  • 注册在 src/tools/registry.ts 中:导入两个导出,将 <VERTICAL>_TOOLS 展开到 ALL_TOOLS 中,添加 const <VERTICAL>_TOOL_NAMES = new Set(<VERTICAL>_TOOLS.map((t) => t.name)),并在 dispatch() 中添加一行: if (<VERTICAL>_TOOL_NAMES.has(toolName)) return dispatch<Vertical>Tool(toolName, args, subject); (subject 是 token 的 sub——当读取必须限定到调用者时,在你的处理器中接受它,就像银行和航空公司所做的那样。)

  • 资源(可选):tools/list 是自动的,但 MCP 资源(resources/list、resources/read)来自 src/index.ts 中手工维护的 RESOURCE_CATALOG——如果垂直领域还应将其列表工具作为资源公开,请在那里添加条目。

容易踩的坑

  • requiredScopes 必须是你的 PingOne 资源实际授予的作用域。缺少任何一个的 token 在 tools/call 上会得到 403,并且 tools/list 会完全隐藏该 token 的工具。

  • 工具名称是全局的。dispatch() 按第一个匹配的名称集路由,因此跨两个垂直领域重用的名称会静默地转到先检查的那个。

  • 使用数据支持的垂直领域(例如体育用品)作为模板,而不是 invest——invest 是 dispatch() 的回退,并且带有你不需要的代理与 SQLite 切换。

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    A lightweight MCP server that simulates financial data interactions with dummy authentication and static JSON datasets for testing financial applications.
    2
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    A minimal local HTTP MCP mock server for development and testing, providing predictable tool responses with OAuth token support and zero dependencies.
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    A production-grade MCP server for a fictional digital bank, exposing tools for an AI copilot to service customers across the full risk spectrum from read-only lookups to money movement and destructive admin actions, with OAuth 2.1 security and a realistic dataset.
    23 npm
    1
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    A Test/Sandbox MCP server that integrates with the SnapTrade API Sandbox environment to provide portfolio oversight, market data, and trading capabilities.
    -