Skip to main content
Glama
curtismu7

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_URIPINGONE_ENVIRONMENT_IDPINGONE_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_ISSUERPINGONE_JWKS_URIPINGONE_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:8081http://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_accountsget_investment_balanceget_portfolio_summaryget_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.tsswitch 中添加匹配的 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/listresources/read)来自 src/index.ts 中手工维护的 RESOURCE_CATALOG——如果垂直领域还应将其列表工具作为资源公开,请在那里添加条目。

容易踩的坑

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

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

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

F
license - not found
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

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
    C
    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
    B
    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.
    13
    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.

View all related MCP servers

Related MCP Connectors

  • Hosted MCP endpoint with realistic fake data for prototyping agents. 12 tools, no setup.

  • Multi-tenant FastMCP server for Charles Schwab brokerage data, monetized via DPYC Tollbooth

  • MCP server exposing the Backtest360 engine API as tools for AI agents.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/curtismu7/mcp-resource-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server