Skip to main content
Glama
amitmohapatra

yourco-mcp

yourco-mcp — 面向 AI Registry 的 MCP SDK

构建一个 MCP 服务器,其工具元数据存放在 AI Registry 中,并在运行时热重载。你只需编写处理函数;其余一切——描述、schema、受众、按工具划分的 scope、暴露方式——都由管理员在 registry UI 中管理,并在毫秒级时间内到达正在运行的服务器,无需重新部署。

Registry (control plane)          Your server (data plane, this SDK)
  admins edit metadata   ──push──▶  in-memory manifest ──▶ answers MCP calls
  UI / RBAC / versions              your handlers      ──▶ your business logic

你的服务器永远不会因 registry 而阻塞:所有 MCP 流量都从内存中提供,即使 registry 宕机,你的服务器也能继续运行(参见「韧性」)。

安装

pip install "yourco-mcp[server,redis] @ git+https://github.com/amitmohapatra/mcp-sdk.git"

在生产环境固定一个 tag(...mcp-sdk.git@v0.1.0)。可选依赖:server 附带 uvicorn 以支持 server.run()redis 启用 Redis pub/sub(生产环境推荐——没有它时 SDK 会自动回退到 registry 的 SSE 流)。

Related MCP server: mcp-toolkit-hub

快速开始——完整集成

import os
from yourco_mcp import ProductServer

server = ProductServer(
    registry_url="https://registry.yourco.com",
    product_key="billing",                    # your product's key in the registry
    api_key=os.environ["REGISTRY_API_KEY"],   # issued in the UI: Manage -> SDK API keys
)

@server.tool("get_invoice")                   # bound by NAME — metadata comes from the registry
async def get_invoice(ctx, invoice_id: str, max_results: int = 100):
    return {"invoice_id": invoice_id, "max_results": max_results}

if __name__ == "__main__":
    server.run(port=8080)                     # stateless MCP over HTTP at POST /mcp

注意缺失了什么:没有描述、没有 JSON schema、没有 Redis 配置、没有认证样板代码。registry 拥有元数据;你的代码拥有行为。如果 registry 列出了某个你没有对应处理函数的工具,它会从 tools/list 中被排除并给出警告(故障安全,绝不故障崩溃)。

认证——你拥有身份,SDK 负责执行

每个产品为自己的工具处理认证。 SDK 永远不会看到你的密码、密钥或令牌格式——你只需实现一个方法:传入 headers,返回 user。

from yourco_mcp import ProductServer, AuthProvider, AuthUser

class MyProductAuth(AuthProvider):
    async def authenticate(self, headers) -> AuthUser | None:
        token = headers.get("authorization", "").removeprefix("Bearer ")
        claims = my_jwt_verify(token)          # YOUR auth: your JWT lib, your OAuth
        if not claims:                         # introspection, your session store
            return None
        return AuthUser(id=claims["sub"], scopes=claims.get("scopes", []))

server = ProductServer(..., auth=MyProductAuth())

普通的 async def fn(headers) -> AuthUser | None 也可以。

框架的职责止于接口。 authenticate 内部发生什么完全是你自己的业务逻辑——Firebase、Auth0、Keycloak、你自己的 JWT 签发器、会话表、mTLS、LDAP,任何方案都可以。SDK 从不导入、捆绑或偏向任何身份系统;它只消费你返回的 AuthUser。下面的示例恰好使用 Firebase,纯粹是为了说明:

import asyncio
import firebase_admin
from firebase_admin import auth as fb_auth
from yourco_mcp import AuthProvider, AuthUser

firebase_admin.initialize_app()                      # your service account creds

class FirebaseAuth(AuthProvider):
    async def authenticate(self, headers) -> AuthUser | None:
        token = headers.get("authorization", "").removeprefix("Bearer ").strip()
        try:                                          # verify_id_token is blocking:
            decoded = await asyncio.to_thread(fb_auth.verify_id_token, token)
        except Exception:
            return None
        roles = await my_db.fetch_roles(decoded["uid"])       # YOUR roles table
        return AuthUser(id=decoded["uid"],
                        scopes=[f"role:{r}" for r in roles],   # roles become scopes
                        claims=decoded)

然后在装饰器上按工具要求角色:

@server.tool("refund_payment", scopes=["role:finance-admin"])
async def refund_payment(ctx, payment_id: str, amount: float): ...

代码声明的 scopes 与 registry 设置的 required_scopes并集方式强制执行——任何一方都可以收紧工具,但任何一方都不能放宽另一方。内置提供者:ApiKeyAuthProvider({key: {...}})StaticTokenProvider({token: {...}})NoAuth()——这是对真正开放服务器的显式退出选项(任何东西都不会意外开放)。

一句话概括契约: 发现始终公开;执行的一切都是你产品可插拔的选择。

  • tools/list(以及 initialize/ping)永远不需要认证——这是一个不变量,而非默认值。网关和目录(例如 Bifrost)可以在零凭据的情况下枚举每个产品的工具。匿名调用者看到的是默认受众的视图。

  • 认证是可插拔的:你的 AuthProvider——或者对完全开放的服务器使用 NoAuth()(这是一个显式选择,绝非意外)。

  • 授权是可插拔的:你的 scopes 由你的认证系统签发,按工具与 registry 设置的 required_scopes 进行校验,再加上你的 @server.authorize 钩子来处理 scopes 无法表达的任何情况。

  • 按工具的执行认证由你决定

@server.tool("ping", public=True)          # executes without auth
async def ping(ctx): ...

@server.tool("refund_payment")             # gated (the default)
async def refund(ctx, payment_id: str): ...

安全规则:如果管理员在 registry 中为某个工具附加了 required_scopes,那么即使代码将其标记为公开,仍然需要认证——运行时收紧始终优先;代码侧的退出选项永远不能覆盖它。

一旦你的验证器就位,SDK 就会强制执行——你无需编写以下任何内容:

层级

行为

你的配置方式…

默认策略

tools/list 开放;tools/call 需要已认证用户(否则返回 -32001

从不(或替换为 policy=AllGatedPolicy()

受众授权

仅当用户的 scopes 包含 audience:internal 时才认可 x-tool-audience: internal;其他人被静默降级到默认受众

通过你的认证系统签发的 scopes

按工具 scopes

registry 中带有 required_scopes: ["payments:write"] 的工具会拒绝没有该 scope 的调用者(-32003)——管理员可在运行时收紧,无需重新部署

在 registry UI 中

业务规则

scope 检查之后的任意代码检查

@server.authorize 钩子

@server.authorize
async def gate(user, tool, args) -> bool:
    return not (tool == "refund_payment" and args["amount"] > 10_000
                and "payments:admin" not in user.scopes)

公司 scope 约定(全组织统一一次):

  • audience:<key> — 授予某个受众(例如内部代理使用 audience:internal

  • <domain>:<action> — 管理员在 registry 中设置的按工具要求(例如 payments:writeinvoices:read

受众、隐藏参数、固定值

管理员可以按受众(例如 externalinternal)以不同方式暴露同一个工具:不同的描述、额外的仅内部参数,或者对某个受众隐藏的参数,改为向你的处理函数发送固定值——调用者永远无法看到或覆盖它。你的处理函数只需用默认值声明该参数;SDK 会根据调用者的受众 schema 校验参数、剥离未知参数,并在你的代码运行之前注入固定值。

@server.tool("charge_card")
async def charge_card(ctx, card_id: str, amount: float, currency: str = "USD"):
    # external callers can't even see `currency` — the SDK always passes the
    # admin-fixed value; internal callers control it. ctx.audience tells you which.
    ...

ctx 为你提供 ctx.user(即 AuthUser)、ctx.audiencectx.tool

实时更新——registry 的保存如何到达你的服务器

  1. 管理员在 registry 中保存 → 一个事务递增产品的序列号,并发布携带已解析视图的事件。

  2. 你的服务器(自启动起即订阅——如果你的产品配置了 Redis 则使用 Redis,否则使用 registry 的 SSE 流;manifest 会告诉 SDK 使用哪种方式)接收该事件。

  3. 序列检查:按顺序的下一个 → 作为原子 manifest 交换应用;过期 → 忽略;有缺口 → 完全重新获取并协调。收敛是有保证的。

  4. 下一次 tools/list/tools/call 将提供新的元数据。典型延迟:个位数毫秒(Redis)到几百毫秒(SSE)。

韧性

  • Registry 宕机 → 你的服务器继续从内存提供服务,包括最新应用的更新。registry 是控制平面,绝不是运行时依赖。

  • Registry 宕机时的冷启动 → 从 SDK 自动维护的最近一次良好快照提供服务(缓存在 ~/.cache/yourco-mcp/ 下;如果你的运行时需要,可以用 YOURCO_MCP_CACHE_DIR 覆盖该位置)。

  • Pub/sub 中断 → 自动回退到廉价的带指数退避的条件轮询(ETag/304),同时持续尝试重新订阅——更新持续流动,只是慢几秒。

  • 错误/格式损坏的更新 → 记录日志、忽略、重新同步;完好的 manifest 永远不会被损坏的 manifest 替换。

  • 无状态 HTTP → 在任何负载均衡器后面运行 N 个副本;无需粘性会话。

新产品团队检查清单

  1. 请 registry 管理员将你的产品接入并给你一个 API key

  2. pip install(见上文),在部署环境中设置 REGISTRY_API_KEY

  3. 为你产品拥有的工具编写处理函数(名称必须与 registry 匹配)。

  4. 将你现有的认证接入一个 AuthProvider.authenticate 方法。

  5. 决定你的哪些令牌携带 audience:* scopes(内部代理等)。

  6. server.run() — 用 curl localhost:8080/healthz 和 MCP tools/list 验证。在 registry UI 中编辑一个描述,然后观察它实时变化。

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
    F
    maintenance
    A flexible, extensible framework for building MCP servers with API key authentication, user management, and dynamic tool sharing.
    10
    11
    MIT
  • A
    license
    B
    quality
    B
    maintenance
    Dynamic MCP server for Node.js enabling runtime tool creation, management, and execution in isolated sandboxes (Docker or Node).
    8
    17
    1
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Shared MCP HTTP server infrastructure for plugin projects, providing Express + Streamable HTTP transport, OAuth/OIDC auth, runtime configuration, tool registration, and widget support.
    11

View all related MCP servers

Related MCP Connectors

  • A MCP server built for developers enabling Git based project management with project and personal…

  • MCP Server for JFrog, providing tools for development and artifact management.

  • MCP server for the Inistate platform: module discovery, entry management, and activity submission.

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/amitmohapatra/mcp-sdk'

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