
Scry
io.github.epinnock由 GitHub 帳號 epinnock 發布v1.0.0更新於 Nov 2, 2026
Search your team's Storybook components by text or image, so agents reuse what exists.
概覽
Scry 讓助理依文字或圖片搜尋團隊的 Storybook UI 元件、取得元件截圖並生成圖片,以便重複使用既有元件。
- 可以做什麼
- 以語意加關鍵字的混合檢索依文字搜尋 UI 元件,可限定在單一專案或已選擇加入的兄弟專案
- 依圖片搜尋視覺相似的元件,支援相同的範圍與版本選項
- 取得元件截圖,回傳圖片區塊與限時連結
- 用 Gemini 以快速或高品質檔位生成圖片,依 AI 額度計費
- 查看目前登入的帳號,並向 Scry 團隊回報缺少的能力
- 適用情境
- 適合助理在既有設計系統下撰寫或審查前端程式碼、應重複使用既有元件而非另創新元件時使用。也適合從截圖或設計稿反查元件,或把某個元件的截圖帶進對話。
- 執行需求
- 遠端 MCP 端點,透過 Streamable HTTP 連線;必須經由 Firebase 完成 OAuth 登入,不支援遠端連線的用戶端可用 mcp-remote 代理。需要一個能存取相關專案的 Scry 帳號。用戶端不需設定 API 金鑰或環境變數。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 Scry,將其新增到工作區。
- 連接 所需的 憑證。它們會被私密儲存,絕不會在公開頁面上展示。
- 為需要使用其工具的對話啟用該服務。
Web executable,透過 Streamable HTTP。 遠端服務在工作區中設定後即可從網頁執行環境執行。
交給代理程式安裝
把這段提示詞貼上到 Claude Code、Codex、Cursor 等代理程式,或 SourceWeft 對話中。代理程式會讀取該伺服器的安裝指南,先向你展示發布者、端點和登入方式,經你同意後再把它新增到你的客戶端。金鑰不會寫進任何命令。
閱讀 https://sourceweft.com/mcp/io.github.epinnock%2Fscry-mcp/install.md,並按其中說明把這個 MCP 伺服器接入我的客戶端。修改任何設定之前,先告訴我它的發布者、端點和登入方式,等我確認。不要把 API 金鑰寫進命令或我會提交的檔案;問我金鑰應從哪裡取得。其他 MCP 客戶端
把它新增到你客戶端的 mcpServers 設定中。 客戶端會在首次連接時完成 OAuth 登入。
{
"mcpServers": {
"scry": {
"type": "http",
"url": "https://mcp.scrymore.com/mcp"
}
}
}README
Scry MCP Server
A spec-compliant remote MCP server on Cloudflare Workers that exposes multi-modal vector search over the Scry component database. Authenticates users via Firebase and issues its own OAuth tokens to MCP clients.
Compatible with Claude Desktop, ChatGPT, Cursor, and any MCP client via mcp-remote.
Architecture
The Worker acts as both an OAuth server to MCP clients (issuing its own tokens) and an OAuth client to Firebase (authenticating users upstream). The MCP client never sees the Firebase token.
Tools
Issue resolution tools (stage; ISSUE_TOOLS_ENABLED="1")
Design-drift issues a human has promoted in the dashboard, resolvable in code or in Figma.
All six call the dashboard's /api/agent/issues/* with a signed X-Scry-Caller
(audience scry-dashboard-agent, claims sub = uid and agent_client = the MCP client's
name); the dashboard checks membership and role and records actor_kind: "agent". Needs
SCRY_DASHBOARD_API_URL, SCRY_AGENT_ASSERTION_SECRET (a secret shared only with the
dashboard, separate from the search secret; unset = SERVER_MISCONFIGURED) and, for the protected stage
dashboard, the SCRY_DASHBOARD_BYPASS_TOKEN secret. Writes are capped at 30/min/user on top
of the 60 req/min limit.
Snip capture tools (stage; CAPTURE_TOOLS_ENABLED="1")
Screenshots the user took with Scry Snip, read by an agent ("fix the screenshot I just took").
Four tools, no more. They call the dashboard's /api/agent/captures/* over the same signed
X-Scry-Caller hop and the same env vars as the issue tools; the dashboard decides who may read a
snip (the owner, or an audience the owner turned on) and writes nothing about it to a log.
Production has no flag until the feature's Gate B. delete_capture is capped at 10/min/user.
Every result starts with a text block (capture id, how old, who took it, size), then the picture
(WebP, long edge at most 1280 px, about 70 KB, inlined only up to 75,000 bytes (about 100k base64 characters); both numbers live in
src/captures/constants.ts as CAPTURE_AGENT_IMAGE), and a signed link to the original that
expires in one hour. A picture that is over budget or cannot be fetched degrades to the link only.
The Sync app produces the rendition; this server does no image processing.
Errors are JSON {error, message, retryable} with isError: CAPTURE_STALE (newest is older than
the window; carries its id and age), AMBIGUOUS_PROJECT (the message lists the projects),
CAPTURE_NOT_READY (still uploading), CAPTURE_NOT_FOUND (a missing capture and one the caller may
not see are byte-identical), CAPTURE_NOT_OWNER (delete), plus the generic RATE_LIMITED,
SERVER_MISCONFIGURED, TIMEOUT, DASHBOARD_UNREACHABLE. The note a user typed is returned
quoted and labelled as untrusted data. No log line carries a capture id, note, app name or URL.
Stock picture search (stage; STOCK_TOOLS_ENABLED="1")
One tool, search_stock(query, type?, provider?, limit?): a live search of free stock libraries (Pixabay,
Unsplash, Openverse; Pexels when the service enables it) through the scry-stock Worker
(STOCK_SERVICE_URL, secret STOCK_SERVICE_TOKEN). The call carries the service bearer, a 60-second
X-Scry-Caller assertion with audience scry-stock (signed with SCRY_CALLER_ASSERTION_SECRET, the secret
the stock Worker verifies) and the call's request id, and is abandoned after 3 s. Each result has provider,
title, creditLine, creditParts (the credit as ordered {text, href?} parts), pageUrl, providerUrl,
previewUrl, type, licenseLabel and licenseUrl, plus a status per provider. The text output prints the
credit as markdown with its links (Unsplash photographer and Unsplash links carry the utm_source=scry pair), the
licence as a link when the service gives a deed URL, a Sources: line, and the provider notices the terms
require (Openverse: "made with Openverse, not endorsed or certified by Openverse"; Pexels: "Photos provided by
Pexels"); the same notices are in structuredContent.notices. The tool description tells the agent to show the
credit exactly as given, links included, and to open pictures on the provider's site. Nothing is
stored, and the query is never logged: it travels only in the POST body, and the tool does not get the injected
analytics context / conversation_id arguments (NO_AGENT_ARGS_TOOLS). Staging only; production sets neither
variable, so the tool is absent there until the feature's Gate B. Source: src/stock/.
Errors are JSON {error, message, retryable} with fixed text per status (never the service's body):
RATE_LIMITED, VALIDATION_ERROR, STOCK_TIMEOUT, STOCK_UNREACHABLE, STOCK_SERVICE_ERROR,
STOCK_PROVIDERS_UNAVAILABLE, SERVER_MISCONFIGURED.
Usage analytics
Each tool handler invocation records one Workers Analytics Engine data point for
use by the dashboard's staff-only KPI page. The only recorded values are the tool
name (blob1), environment (blob2), and Firebase uid (blob3 and index1), plus
double1 = 1 for counting. Missing environment/uid values become unknown and
anonymous. No query text, prompts, image data, tokens, or other tool data is sent
to Analytics Engine. Internal diagnostic logs do not add usage points. Missing or
failing analytics bindings never affect tool responses.
The MCP_USAGE binding writes to scry_mcp_usage in production and
scry_mcp_usage_staging in staging. Query production with the Analytics Engine SQL
API (substitute the staging dataset name for staging):
Analytics
Feature mcp-analytics. Every tool call produces ONE vendor-neutral event, mcp_tool_call.v1
(src/analytics/event.ts), built from an allow-list. The event goes to the sinks named in
ANALYTICS_SINKS (comma-separated). The log sink is always on, whatever the value says (none, off and unset all
mean log only), so keep it listed; an unknown name is ignored and logs one warning line.
What is collected: tool name, outcome, latency, error code, the confirmed project id, a salted hash of
the user id (uid_hash, never the uid or email), MCP client name/version/protocol, the calling model
when the client states it (Codex only), argument NAMES, the response SIZE, and a session id (the
Durable Object id). PostHog also gets the agent's optional context sentence, scrubbed of emails, URLs
and key-shaped strings and capped at 300 characters. Logs never carry it.
What is never collected: argument values, response bodies, image data, queries, raw uid, email, tokens.
Client labels (client name/version, protocol version, model) are sent only when they are a short safe token
(letters, digits and . _ - / ( ) + and spaces, at most 64 characters, unchanged by the log scrubber); anything
else is reported as other, in the logs and in PostHog alike. The intent is cut to 1000 characters, scrubbed
(emails, URLs, phone numbers, IPv4/IPv6, keys and long ids), cut to 300 and scrubbed again.
Agent-visible changes, only when ANALYTICS_AGENT_ARGS="on" (on in production and staging since
2026-10-08; absent or any other value = off, where events still flow but no tool changes): every tool accepts two optional arguments, context (why the agent is calling
the tool) and conversation_id (groups calls). The wrapper adds them to each tool's schema and removes
them before the handler runs, so tool code never sees them. A value that is not a string, or is longer than 4096
characters, is dropped from analytics and the call proceeds normally. A tool that declares its own context or
conversation_id keeps it (nothing is injected or stripped and it is never read as an intent). The get_more_tools tool lets an agent say
what it needed that Scry does not offer; it records a missing-capability event and does nothing else.
Add or replace a sink: write src/analytics/sinks/<name>.ts that implements AnalyticsSink
(toolCall, initialize, toolsList; never throw), add the name to KNOWN_SINKS in sinks.ts and the
case in createSinks (index.ts). To drop PostHog, delete sinks/posthog.ts, its case in index.ts, and
the @posthog/mcp and posthog-node dependencies. ESLint (no-restricted-imports) allows PostHog
imports in that one file only. Sinks are fire-and-forget with a time budget and run under waitUntil, so a
slow or failing sink never changes or delays a tool result.
Kill switch: remove posthog from ANALYTICS_SINKS (or delete the POSTHOG_PROJECT_TOKEN secret);
logging continues. Runbook: scry-management/docs/runbooks/mcp-analytics.md.
Request ids and error tracking
Two kinds of id, NOT joined (feature observability-request-id; contract in
scry-management/features/observability-request-id/briefs/_request-id-contract.md; runbook
docs/runbooks/observability-request-id.md):
- Edge id, one per HTTP request (
src/lib/edge-request.ts, F32/G3): the Worker mints a ULID for every request, sets it on EVERY response asx-scry-request-id(errors, redirects, OAuth, SSE; headers only, bodies and streams untouched) and writes onemsg:"request"line with the route pattern (fixed table, elseunmatched), status, ms andclient(the validatedx-scry-clientrequest header). The edge never reads or modifies a request body. - Tool id, one per tool call (
src/lib/tool-request.ts): the tool wrapper mints its OWN ULID for each tool call, logs it on the tool line (route= the tool name), forwards it on every Scry hop and returns it in the tool result (error bodies carry"request_id", below). One MCP HTTP request can hold several tool calls (batches, parallel calls on one session), so an MCP request has one edge id and each tool call inside it has a different tool id. They are not joined: the edge id is not in the tool result and the tool id is not in a response header. To follow a user-visible tool id, look it up (scry-logs.py --request-id <tool id>): the tool line, then the downstream search and credits lines that share that id. The edge id in thex-scry-request-idheader finds the HTTP request line only. - An inbound
x-scry-request-idis always ignored (both ids): the MCP server faces end users and API clients, so under the contract's trust rule it never accepts a caller-chosen id. - Langfuse sampling uses a server-side random draw per call
(
serverDrawinsrc/telemetry/producer.ts), never the id. - Forwarded on every Scry hop: search (
/api/search,/api/image/presign,/api/image/upload), the dashboard issue API, and the diff-service credits ledger. It is not sent to Google. - Tool errors carry it: every JSON error body gets
"request_id": "<id>"(added by the tool wrapper, so no tool has to remember), e.g.{"error":"SEARCH_API_500","message":"…","retryable":true,"request_id":"01M3…"}. A handler that throws becomesINTERNAL_ERROR/UPSTREAM_TIMEOUTwith the id. - One tool line per tool call, written at the end through the shared scry-log logger
(log schema v1, feature log-standardization; vendored at
src/lib/scry-log/, source of truthscry-management/lib/scry-log/, standardscry-management/skills/feature-workflow/references/logging-tracing-standard.md):{"v":1,"ts":"…","level":"info","service":"mcp","env":"staging","version":"<sha>","msg":"request","request_id":"01M…","route":"search_components","status":200,"ms":412,"project":"4vR5…","uid_hash":"<12 hex>"}.statusis 200 for ok, 500 for a server-side tool error and 400 for any other; errors carry a fixed lowercaseerr_code(search_api_500).err_codesplit:INSUFFICIENT_CREDITS402,ACCESS_DENIED403,RATE_LIMITED429.uid_hashis the first 12 hex of sha256(uid +SCRY_LOG_SALT), computed off the request path;SCRY_LOG_SALTis a secret (wrangler secret put SCRY_LOG_SALT [--env staging], one random value per tier, never a var in git) and when it is unset in staging or productionuid_hashis omitted (no public fallback).clientappears on the edge line only (it comes from thex-scry-clientheader, which the tool handlers never see). No raw uid, email, query text or body. Mid-call diagnostics (logDiagnostic) are fixed words plusstatus,ms,err_code,uid_hash; upstream error text is never logged. Staging ships the lines toscry-logsviatail_consumers; find a call withscry-management/scripts/scry-logs.py --request-id <id>. - generate_image uses the request id as its run id: the AI Gateway
runmetadata, the Langfuse trace (metadata.request_id; the trace id is the ULID's 128 bits in hex,traceIdForinsrc/telemetry/ids.ts). The request id is for tracing only and is never a billing or idempotency key: the credits holdref_idstays a server-minted UUID per call (mcp-image:<uuid>), so a reused inbound id cannot replay a hold or merge two charges. - Sentry (
src/lib/sentry-options.ts):environment = SCRY_ENV(staging|production),sendDefaultPii: false, no bodies, and a scrubber (src/lib/sentry-scrub.ts, copied from build-processing) on events and breadcrumbs. The tool Durable Object is instrumented too, so a tool that throws reaches Sentry taggedrequest_idandtool. WithoutSENTRY_DSNthe SDK is a no-op. Stage DSN:wrangler secret put SENTRY_DSN --env staging(the shared Workers project, taggedservice=scry-mcp).
Project Structure
Prerequisites
- Node.js v22+
- A Cloudflare account
- A Firebase project with Authentication enabled
- The Scry Next.js search API running (provides
/api/searchand/api/image/presign)
Local Development
1. Install dependencies
2. Create local environment file
Edit .dev.vars with your values:
SCRY_SEARCH_API_KEY is transport auth: it proves a request came from this
worker. SCRY_CALLER_ASSERTION_SECRET signs the short-lived X-Scry-Caller
JWT that tells the search API which user the worker is acting for; it is the
only channel that carries the uid, and the search API verifies it with the same
secret before it trusts the uid. Without the secret the search tools return
SERVER_MISCONFIGURED rather than searching anonymously.
3. KV configuration
The production and staging KV namespaces already exist and are configured in
wrangler.jsonc. Local development uses Wrangler's local KV emulator; no cloud
namespace creation is needed.
4. Start the dev server
The server runs at http://localhost:8787.
5. Test with MCP Inspector
Running Tests
Deployment
The shared workflow is .github/workflows/deploy.yml, using Node 22 and Wrangler
4.72.0 (the lockfile version). PRs targeting stage or main run typecheck,
lint, widget build and tests without deploying. Feature PRs target stage;
promotion advances main to the tested staging commit.
The connector URL is https://mcp.scrymore.com/mcp (staging:
https://mcp-stage.scrymore.com/mcp). The *.epinnock.workers.dev names are the
same workers and still resolve; they are not being retired. Each scrymore.com
hostname is a Workers custom domain and an explicit <host>/* route, because
scry-cdn-service owns a *.scrymore.com/* route that otherwise answers for
every proxied hostname in the zone. Full table and wiring rules:
scry-management/ENDPOINTS.md.
Pushes to these branches deploy after checks pass; Markdown and docs/** changes
alone do not trigger push CI. Use the workflow's environment choice for a manual
run, selecting the branch whose commit should deploy. Deployments are serialized
per ref and include the Phase 0 commit, branch, build time, run ID, actor and Sentry
release stamps. The smoke gate retries /healthz six times at 10-second intervals
until its commit matches the run SHA, then requires unauthenticated /mcp to return
401 and /.well-known/oauth-authorization-server to return 200.
For an authorized manual deploy, npm run deploy:staging selects staging and
npm run deploy:production (or npm run deploy) retains the existing production
worker and stamp flags. Production intentionally has no env.production block;
use a top-level deploy/dry-run, not --env production.
CI reuses the repository's existing
CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID. GitHub deployment environments
are staging and production, with their respective worker URLs. The existing
staging-OAUTH_KV namespace is reused, and Wrangler applies migration v1 to the
new worker's ScryMCP Durable Object; no KV creation or manual DO migration is
needed.
Phase 1 health and OAuth smoke checks work without worker secrets: health reads only stamp vars, the provider builds metadata from the request origin and endpoint configuration, and missing bearer auth returns 401 before accessing KV or services. Firebase sign-in, search and image generation need Phase 2 configuration:
- Add staging
FIREBASE_API_KEY,FIREBASE_AUTH_DOMAIN, andFIREBASE_PROJECT_IDforscry-dev-dashboard-stage. - Set
SCRY_SEARCH_API_URLtohttps://search-stage.scrymore.com(production useshttps://search.scrymore.com) andSCRY_SEARCH_API_KEYto its stage-only API key. Store that key on Vercel as a plain encrypted Preview variable, not a sensitive one: sensitive values cannot be read back, and the promotion smoke check (scry-management/smoke-search.py) pulls it withvercel env pull. - Set
SCRY_SEARCH_API_BYPASS_TOKENto the scry-nextjs project's Protection Bypass for Automation token. The stage search host is behind Vercel Deployment Protection and answers a bare worker request with a login page (its custom domain does not change that: a branch-assigned domain serves a preview deployment, and per-domain exceptions need a paid plan); the worker sends the token asx-vercel-protection-bypassonly when this secret exists. Production has no such secret and sends no header. - Set
SCRY_CALLER_ASSERTION_SECRETto the same value as the scry-nextjs Preview deployment'sSCRY_CALLER_ASSERTION_SECRET(wrangler secret put SCRY_CALLER_ASSERTION_SECRET --env staging; production is top-level,wrangler secret put SCRY_CALLER_ASSERTION_SECRET). Generate a distinct value per environment withopenssl rand -base64 48. Set it on scry-nextjs first, with itsALLOW_LEGACY_USER_HEADER=truetransition flag on, then deploy the worker, then turn the flag off. - Set
SCRY_AGENT_ASSERTION_SECRET(issue tools only) to the same value as the dashboard'sSCRY_AGENT_ASSERTION_SECRETfor that environment (wrangler secret put SCRY_AGENT_ASSERTION_SECRET --env staging; production top-level). It must differ fromSCRY_CALLER_ASSERTION_SECRETand between stage and production. - Add a staging
COOKIE_ENCRYPTION_KEY,GEMINI_API_KEYfor image generation, and optionallySENTRY_DSNfor error reporting. In Phase 2, usewrangler secret put <NAME> --env staging; production secrets remain top-level. - AI credits (
generate_imageholds 40 fast / 150 quality credits on the caller's wallet in the scry-diff-service ledger): setCREDITS_API_TOKENto that environment's diff-serviceSERVICE_AUTH_TOKEN(wrangler secret put CREDITS_API_TOKEN --env staging).CREDITS_MODE(vars) isoff|shadow|enforce: shadow writes the ledger but never refuses; enforce returnsINSUFFICIENT_CREDITS(no Gemini call) and fails closed withCREDITS_UNAVAILABLEwhen the ledger cannot be reached. Staging isenforce, productionenforcesince 2026-09-24 (the shadow week was skipped).CREDITS_API_URL(the diff-service ledger) andCREDITS_PAGE_URL(dashboard/credits, linked from the refusal message) are vars inwrangler.jsonc. The wallet is the caller's org (org:<users/{uid}.activeOrgId>if they are in itsmemberIds, elseorg:personal_<uid>), read from Firestore with the Firebase Admin service account: setFIREBASE_CLIENT_EMAILandFIREBASE_PRIVATE_KEY(secrets) from that environment's service-account JSON. - LLM telemetry:
LLM_GATEWAY_URL(vars) routes Gemini through the authenticated AI Gateway (scry-stage/scry-prod) and needs theCF_AIG_TOKENsecret; delete the var to call Google directly (kill switch).LANGFUSE_ENABLED/LANGFUSE_SAMPLE_RATEcontrol Langfuse spans, which are enqueued on theTELEMETRY_QUEUEproducer (scry-telemetry-staging/-production); the consumer is scry-diff-service, which archives to R2 and delivers to Langfuse. Sampling is adaptive: when telemetry is on, the Worker reads its rate (rates.mcp) from the diff-service unit-budget job atGET $CREDITS_API_URL/api/telemetry/sampling(bearerCREDITS_API_TOKEN, the same service credentials as credits; no extra secret), cached per isolate for the response'sttl_s(default 300 s). On a timeout (1.5 s), error, non-200 orrates: nullit falls back toLANGFUSE_SAMPLE_RATE(failures are cached for 60 s). SetLANGFUSE_DYNAMIC_SAMPLING=0to use the env var only. The sampling decision stays deterministic per run id. - In the staging Firebase console, authorize
scry-mcp-staging.epinnock.workers.devand enable the intended sign-in providers (Google and email/password alongside GitHub).
Do not enable DEV_BYPASS_AUTH on deployed workers. Full authenticated staging
verification follows in Phase 2 after those secrets and Firebase settings exist.
Firebase Setup
- Create a Firebase project at console.firebase.google.com
- Enable Authentication > Sign-in method > Google and/or Email/Password
- Add your Worker's domain to authorized domains: Authentication > Settings > Authorized domains > Add
scry-mcp.<your-account>.workers.dev - Copy config values from Project Settings > General > Web app config:
apiKey,authDomain,projectId
Client Setup (Claude Desktop)
Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
For local development:
Restart Claude Desktop. On first launch, a browser window opens for Firebase sign-in. After authenticating, the 5 tools appear in Claude's tool picker.
Auth Flow
Security
- Token isolation: Firebase tokens are never exposed to MCP clients. The Worker encrypts them in KV and issues separate MCP-scoped tokens.
- CSRF protection:
__Host-prefixed cookies withHttpOnly,Secure,SameSite=Lax. - Rate limiting: 60 requests/minute per user (sliding window in Durable Object).
- Request timeouts: 30s
AbortControllertimeout on all upstream calls. - Input validation: Zod schemas enforce query length (500 chars), image size (10MB), project ID length (128 chars).
- Presigned URLs: Time-limited (1 hour), generated server-side. R2 credentials never leave the Next.js service. The presign request carries the caller assertion, so the Next.js service signs only keys the user may read.
- Caller identity: the worker sends
X-Scry-Caller, an HS256 JWT overSCRY_CALLER_ASSERTION_SECRET({sub: uid, aud: "scry-search", iat, exp ≤ 60s}), so the sharedSCRY_SEARCH_API_KEYcannot be used to impersonate a user. This assertion is the only identity channel: the search API's transition flag is gone, the unsignedX-User-Idheader is no longer read there, and the worker no longer sends it. - Search scope: explicit
scopeon both search tools, defaultproject, which never widens.orgreturns another project's rows only when that project opted in (discoverableByOrg) and the user can read it.
MCP Registry
server.json publishes the server to the MCP registry as
io.github.epinnock/scry-mcp (remote: streamable HTTP at
https://mcp.scrymore.com/mcp).
Available Scripts
Log schema drift check
The scry-log-drift workflow (scripts/scry-log-drift.sh) fails a PR whose vendored src/lib/scry-log is ahead of the stage logs Worker ("deploy logs-service first"); re-vendor with scry-management/lib/scry-log/sync.sh <this repo>.
來源:README.md,提交 7a60688
工具
0版本歷史
1- v1.0.0最新Sep 16, 2026
評分
已滿足適用檢查項的 85%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通必需25 分
- 已宣告認證方式必需10 分
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分