原型 · 预览版:收录官方注册中心里的全部服务器,每 30 分钟同步一次。评价和账户功能将在后续阶段推出。
PopNx MCP为使用 AI 的人准备的 MCP 服务器 · 用大白话讲清楚

Attest MCP

作者:chudah1版本 0.1.0i★ –i在你的电脑上运行
注册名称io.github.chudah1/attest-mcp

安装

概览

作者提供的说明(英文原文)

Credential enforcement middleware for MCP servers — verifies scoped tokens on every tool call

PopNx MCP 的三大支柱

这三项永远不会合并成一个分数:意见、测量和人气是不同的东西。

它能帮你做什么

我们还没有为它写通俗的介绍。下面是我们从注册中心读取到的事实。

  • 连接方式: 在你的电脑上运行
  • 传输方式: stdio
  • 分类: 其他

安装

选择你使用的 AI 应用,然后复制设置。

安装之前

你需要

  • npm已安装 Node.js(用于 npx 命令)

你用的是哪个应用?

在上方选择你的应用。我们会记住它,下次按钮会显示“安装到 …”。

打开 Claude Desktop → Settings → Developer → Edit Config,把下面的内容粘贴到 claude_desktop_config.json

{
  "mcpServers": {
    "attest": {
      "command": "npx",
      "args": [
        "-y",
        "@attest-dev/mcp"
      ],
      "env": {
        "ATTEST_BASE_URL": "your-value"
      }
    }
  }
}

在终端中运行这条命令

claude mcp add attest --env ATTEST_BASE_URL=your-value -- npx -y @attest-dev/mcp

运行前请先填写空着的值。

在 Cursor 中打开

或者自己复制设置

粘贴到项目中的 .cursor/mcp.json,或 ~/.cursor/mcp.json

{
  "mcpServers": {
    "attest": {
      "command": "npx",
      "args": [
        "-y",
        "@attest-dev/mcp"
      ],
      "env": {
        "ATTEST_BASE_URL": "your-value"
      }
    }
  }
}

浏览器会先询问是否打开应用,然后应用会显示设置并请你确认。密钥的值会留空,由你自己填写。

在 VS Code 中打开

或者自己复制设置

粘贴到项目中的 .vscode/mcp.json

{
  "servers": {
    "attest": {
      "type": "stdio",
      "command": "npx",
      "args": [
        "-y",
        "@attest-dev/mcp"
      ],
      "env": {
        "ATTEST_BASE_URL": "your-value"
      }
    }
  }
}

浏览器会先询问是否打开应用,然后应用会显示设置并请你确认。密钥的值会留空,由你自己填写。

这个服务器在你的电脑上运行,而 ChatGPT 只能连接互联网上的服务器。请选择其他应用。

你的选择通过 Cookie 保存在这台设备上,无需账户。

需要你填写的值

  • ATTEST_BASE_URL env
    Base URL of the Attest server

这些设置是根据作者在注册中心的条目生成的。如果 README 的写法不同,请以 README 为准。

它能做什么

它在你的电脑上运行;安装后,工具列表会显示在你的 AI 应用中。

基础检查i

我们检查的是任何人都能看到的信息;我们没有阅读代码。不适用的检查项不计入总数。 · 检查于 2026年11月9日

  • ✓ 通过

    作者与仓库一致

    注册名称已通过 GitHub 账户 chudah1 验证,该账户拥有此仓库

  • … 尚未检查

    仍在维护

    我们还没有从 GitHub 读取这个仓库

  • … 尚未检查

    有许可证

    我们还没有从 GitHub 读取这个仓库

  • – 不适用

    端点有响应

    它在你的电脑上运行,没有互联网端点

  • – 不适用

    使用 OAuth 登录

    在你的电脑上运行,改用本地设置

  • … 尚未检查

    工具描述中没有隐藏指令

    它在你的电脑上运行;我们只能读取互联网端点的工具列表

与“其他”中的其他服务器比较i

  • 星标数多于此分类中其他 18643 个服务器的 0%
  • 发布到注册中心的时间比此分类中其他 18643 个服务器的 5% 更新
  • 基础检查结果等于或优于此分类中其他 18643 个服务器的 100%

与注册中心里这个分类中所有活跃的服务器比较。只有当发布者拥有该仓库时,星标数才计入。

常见问题

Attest MCP 安全吗?

它在 1 项适用的基础检查中通过了 1 项。我们没有阅读代码。在添加密钥或令牌之前,请先看看这个服务器要求哪些权限,并使用只具备所需权限的密钥。

它需要哪些密钥或代码?

注册中心没有列出必需的密钥,不过有些服务器会在首次使用时要求你登录。

它在我的电脑上运行还是在云端?

它在你的电脑上运行。你的 AI 应用会在需要时启动它,数据不会经过作者的服务器(除非它自己调用在线服务)。

它还在维护吗?

GitHub 仓库最后更新于–,最新版本(0.1.0)于7个月前进入注册中心。

哪些应用可以使用它?

支持 MCP 的应用,例如 Claude Desktop、Claude Code、Cursor 和 VS Code。你的电脑上还需要有 Node.js。

有什么可以替代它?

同一分类中有:Spec Driven Development, Remote Desktop Commander, Cosas Guapas: buscador de regalos

替代选择

同一分类中其他作者的服务器,按星标数排序。

如何看卡片:↓ 每周下载量i★ GitHub 星标i三大支柱:评价 · 检查 · 使用情况i更新 = 最新版本发布到注册中心的时间i

Spec Driven Development

MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.

作者:formulahendry起步阶段↓ 66/周★ 44014个月前更新

在你的电脑上运行

Remote Desktop Commander

Remote Desktop Commander: use files, terminal and processes on your own computers from any AI app.

作者:desktop-commander正在增长★ 2113天前更新

在云端运行✓ desktopcommander.app

Cosas Guapas: buscador de regalos

Regalos originales por ocasión, persona y presupuesto, con enlaces a guías completas (España).

作者:cosasguapas.com起步阶段今天更新

在云端运行✓ cosasguapas.com

作者的 README

打开 README

按原文从作者的仓库复制。PopNx MCP 没有审查或背书其内容。HTML、图片和脚本已被移除。链接会离开本站。

@attest-dev/sdk/mcp

Attest credential enforcement middleware for Model Context Protocol servers.

Two lines to protect every tool call on an existing MCP server.

Install

npm install @attest-dev/sdk

@attest-dev/sdk ships both the core client and the MCP middleware as a single package with two entry points.

Two-line integration

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

import { withAttest } from "@attest-dev/sdk/mcp";

const server = new McpServer({ name: "my-tools", version: "1.0.0" }); const protectedServer = withAttest(server, { issuerUri: "https://api.attest.dev", });

// Register tools exactly as before — every call is now credential-gated. protectedServer.tool("send_email", schema, handler);

withAttest patches server.tool() in place and returns the same server object typed as the original. Everything else — connect(), close(), transports — works unchanged.

How it works

On every tool call, before the handler executes:

  1. Extract credential from extra.authInfo.token (MCP auth middleware path), extra.meta.attest_token, or extra.meta.authorization
  2. Verify RS256 signature + expiry offline against the issuer's JWKS (fetched once, cached for 1 hour by default)
  3. Map tool name → scope using scopeForTool() and check the credential's att_scope covers it
  4. Check revocation via GET /v1/revoked/{jti} (10-second in-process cache)
  5. Pass through to the original handler on success
  6. Return a structured error on any failure — agent frameworks receive isError: true with a typed JSON payload
  7. Fire audit event to POST /v1/audit — async, never blocks the tool call

Scope mapping

Tool names are automatically mapped to resource:action scope strings. The first word (before the first _) becomes the action; the remainder becomes the resource.

Tool nameRequired scopeNotes
send_emailemail:send
read_filefile:read
list_documentsdocuments:list → documents:readlist aliases to read
create_useruser:create → user:writecreate aliases to write
delete_recordrecord:delete
get_weather_forecastweather_forecast:readmulti-word resources keep underscores
run_queryquery:run → query:executerun aliases to execute

Action aliases — these raw prefixes map to canonical actions:

Raw prefixCanonical action
create, update, put, patch, set, upsertwrite
get, fetch, list, search, query, find, lookupread
remove, destroydelete
run, invoke, callexecute

Multi-word resources keep their underscores in the scope string. get_weather_forecast → weather_forecast:read. This is intentional — the full noun phrase is preserved as the resource. When issuing credentials, use the same string: scope: ["weather_forecast:read"].

Override at registration — pass a AttestToolOptions object as the final argument to server.tool(). This is the preferred way to set scope for non-standard tool names because the scope lives next to the tool definition:

import { withAttest } from "@attest-dev/sdk/mcp";

import type { AttestToolOptions } from "@attest-dev/sdk/mcp";

protectedServer.tool("gh_create_issue", schema, handler, { requiredScope: "github:write", } satisfies AttestToolOptions);

protectedServer.tool("stripe_charge", schema, handler, { requiredScope: "stripe:write", } satisfies AttestToolOptions);

// Auto-mapping still applies when requiredScope is omitted. protectedServer.tool("send_email", schema, handler); // → "email:send"

The options object is consumed by withAttest and stripped before the MCP SDK sees the arguments — the SDK is unaware of it.

Override globally using toolScopeMap in options (applies to all tools that don't declare requiredScope explicitly):

const protectedServer = withAttest(server, {

issuerUri: "https://api.attest.dev", toolScopeMap: { "send_message": "slack:send", "query_db": "postgres:read", }, });

Scope discovery endpoint

getAttestScopes() returns the live scope registry — every tool name mapped to its resolved scope string. Wire it to an HTTP endpoint so credential issuers can query what scopes a server requires without out-of-band coordination.

import { withAttest, getAttestScopes } from "@attest-dev/sdk/mcp";

const protectedServer = withAttest(server, { issuerUri: "https://api.attest.dev" });

protectedServer.tool("send_email", schema, handler); protectedServer.tool("gh_create_issue", schema, handler, { requiredScope: "github:write" });

// Express / Hono / any HTTP framework: app.get("/.well-known/attest-scopes", (_req, res) => { res.json({ tools: getAttestScopes(protectedServer) }); });

Response:

{

"tools": { "send_email": "email:send", "gh_create_issue": "github:write" } }

An orchestrator agent issues credentials by fetching this endpoint first:

// Before starting a task, discover what the MCP server needs:

const { tools } = await fetch("https://my-mcp-server/.well-known/attest-scopes") .then(r => r.json());

// Issue a credential with exactly those scopes: const { token } = await attestClient.issue({ agent_id: "my-agent", user_id: "usr_alice", scope: Object.values(tools), // ["email:send", "github:write"] instruction: "...", });

No spreadsheet of scope strings. No manual sync between server and client. The server is the source of truth.

What an agent receives when blocked

{

"content": [ { "type": "text", "text": "{\n \"error\": \"attest_violation\",\n \"reason\": \"scope_violation\",\n \"detail\": \"Credential (jti: abc-123) grants [research:read] but tool \\\"send_email\\\" requires \\\"email:send\\\".\",\n \"jti\": \"abc-123\",\n \"taskId\": \"tid-xyz\"\n}" } ], "isError": true }

isError: true signals the MCP framework that this is an error response. The reason field is machine-readable:

reasonMeaning
no_credentialNo Attest JWT found in the request
invalid_credentialJWT signature verification failed or malformed
credential_expiredJWT exp has passed
credential_revokedJTI is in the revocation list
scope_violationCredential does not grant the required scope

Options

withAttest(server, {

// Required: URI of your Attest server issuerUri: "https://api.attest.dev",

// How long to cache JWKS before re-fetching (seconds). Default: 3600 jwksCacheTTL: 3600,

// How long to cache revocation status per JTI (seconds). // Set 0 to disable caching (every call hits the server). Default: 10 revocationCacheTTL: 10,

// false = log violations via onViolation but don't block the call. // Useful for gradual rollout. Default: true requireCredential: true,

// Per-tool scope overrides. Keys are exact tool names. toolScopeMap: { "send_message": "slack:send", },

// Called on every scope violation (synchronous). // Use for metrics, alerting, structured logging. onViolation: (event) => { console.warn("Attest violation", { tool: event.toolName, reason: event.reason, required: event.requiredScope, granted: event.grantedScope, jti: event.jti, }); }, });

Reading credential claims inside a tool handler

After withAttest has already verified the credential, use getAttestContext to read the claims without re-verifying:

import { withAttest, getAttestContext } from "@attest-dev/sdk/mcp";

protectedServer.tool("send_email", schema, async (args, extra) => { const ctx = getAttestContext(extra); // ctx.att_uid — the human who initiated the task // ctx.att_tid — task tree ID (use for audit correlation) // ctx.att_scope — granted scopes // ctx.att_depth — delegation depth (0 = root) // ctx.jti — this credential's unique ID

await sendEmail(args.to, args.body, { actingAs: ctx?.att_uid }); return { content: [{ type: "text", text: "sent" }] }; });

Gradual rollout

Add Attest to an existing server without blocking traffic until you're confident the mapping is correct:

const protectedServer = withAttest(server, {

issuerUri: "https://api.attest.dev", requireCredential: false, // observe, don't block onViolation: (event) => metrics.increment("attest.violation", { tool: event.toolName, reason: event.reason, }), });

Flip requireCredential to true once violation rates drop to zero in your monitoring.

Audit logging

Every tool call — pass or fail — fires a POST /v1/audit to your Attest server. Delivery is async and never blocks tool execution.

Audit delivery is best-effort with structured error surfacing. For guaranteed delivery, configure `onAuditError` and implement your own retry queue.

Failures surface in priority order — nothing is ever silently dropped:

  1. `onAuditError(error, event)` — you handle it (retry queue, fallback log, pager)
  2. `onViolation({ reason: "audit_failure", ... })` — surfaces to your existing monitoring if onAuditError is not set
  3. `console.warn` — last resort if neither callback is configured
const protectedServer = withAttest(server, {

issuerUri: "https://api.attest.dev",

// Tier 1: implement a retry queue for guaranteed delivery onAuditError: (err, event) => { retryQueue.push({ event, attempts: 0, nextRetry: Date.now() + 5000 }); logger.error("attest audit delivery failed", { jti: event.jti, agent_id: event.agent_id, error: err.message, }); },

// Tier 2: violations (including audit_failure) go to your monitoring onViolation: (e) => metrics.increment("attest.event", { reason: e.reason }), });

Non-2xx responses from the audit endpoint are treated as delivery failures, not silent successes.

The audit event carries: event_type, jti, att_tid, att_uid, agent_id (mcp: ), scope, and an outcome meta field (allowed, scope_violation, blocked_revoked).

Query the audit chain for a task tree:

import { AttestClient } from "@attest-dev/sdk";

const client = new AttestClient({ baseUrl: "https://api.attest.dev", apiKey: "..." }); const chain = await client.audit(taskId); chain.events.forEach(e => console.log(e.event_type, e.jti, e.created_at));

Full example

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { withAttest } from "@attest-dev/sdk/mcp"; import { z } from "zod";

const server = new McpServer({ name: "email-server", version: "1.0.0" });

server.tool( "send_email", { to: z.string().email(), subject: z.string(), body: z.string() }, async ({ to, subject, body }) => { await sendEmail(to, subject, body); return { content: [{ type: "text", text: Sent to ${to} }] }; }, );

// Wrap — every tool now requires a valid credential with "email:send" scope. const protectedServer = withAttest(server, { issuerUri: process.env.ATTEST_URI ?? "http://localhost:8080", onViolation: (e) => console.warn("blocked:", e.reason, e.toolName), });

const transport = new StdioServerTransport(); await protectedServer.connect(transport);

License

Apache-2.0

问 PopNx