Epoch

tech.kyanitelabs由 kyanitelabs.tech 發布v0.6.0更新於 Oct 30, 2026

Epoch time estimation MCP server for PERT, COCOMO, Monte Carlo, calibration, cost, and risk.

概覽

AI 產生的概覽

本機 MCP 伺服器,為助理提供 26 個工具,用於時間換算、工期估算、進度風險與 LLM token 成本比較。

可以做什麼
  • 取得目前時間、轉換時區、解析時長並進行日期運算
  • 依美國、英國、法國、德國與日本假日曆加減或計算工作日
  • 以 PERT、COCOMO II、衝刺速度、關鍵路徑與蒙地卡羅模擬估算工作量與工期
  • 依參考類別進行預測、將自由文字任務描述分類並評估進度風險
  • 把 token 預算換算成實際耗時,並比較各 LLM 模型的成本與速度
  • 記錄實際工時,檢視估算準確度趨勢與回饋循環健康狀態
適用情境
當助理需要回答任務或專案要多久、截止日期是否實際、還剩多少個工作日,或某個 token 預算下哪個模型最省錢時適用。也適合想記錄實際工時並長期追蹤估算準確度的團隊。
執行需求
透過 npx 從 npm 套件以 stdio 方式在本機執行,需要 Node.js。不需帳號、API 金鑰或身分驗證。選用環境變數可設定 HTTP 傳輸、連接埠、綁定位址、資料目錄、速率限制、CORS 與遙測。
安裝前請注意
記錄實際工時會把估算與回饋資料寫入本機資料目錄。匿名遙測預設關閉,須由人員透過命令列或設定 EPOCH_TELEMETRY 主動開啟;開啟後會分享任務類型、複雜度、工具名稱、估算與實際工時、比值與日期。啟用 HTTP 傳輸並放寬 CORS 或代理信任會擴大暴露範圍。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Epoch,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

[Epoch — estimation MCP server for AI agents: PERT, COCOMO II, Monte Carlo, schedule-risk tools]

Epoch -- Time Estimation MCP Server

[epoch icon]

Epoch is a time estimation MCP server that helps engineering leads, agents, and planners who need calibrated duration estimates produce PERT/reference-class estimates and record actuals.

TL;DR: Epoch — time estimation MCP server. Best for engineering leads, agents, and planners who need calibrated duration estimates.

[CI] [License: Apache-2.0] [MCP] [npm version] [MCP Registry]

Epoch helps AI agents understand time.

AI can write code, analyze data, and build apps -- but it can't tell you how long something will take, what it'll cost, or whether a deadline is realistic. Epoch fixes that.


Show Me

You: "How long will a 15,000 line project take with a small team?"
Claude (using Epoch):  COCOMO II nominal: 100 person-months (traditional team)  AI-adjusted: 9 person-months (LLM-assisted workflow)  Schedule risk: medium (MAPE 15%, based on 28 historical features)  Cost estimate (Claude Sonnet): ~$340 in API tokens
You: "How many working days between now and launch?"
Claude (using Epoch):  Start: May 1, 2026  End: July 15, 2026  Business days: 54  Weekend days: 16  Holidays: 1 (Independence Day)  Total calendar days: 75
You: "Which AI model should we use for a 50k-token job?"
Claude (using Epoch):  gemini-2.0-flash:  $0.02 -- fast, budget-friendly  gpt-4o-mini:       $0.02 -- fast, budget-friendly  gemini-2.5-pro:    $0.37 -- standard, high quality  claude-sonnet-5:    $0.57 -- standard, high quality  Recommendation: gemini-2.0-flash for cost, claude-sonnet-5 for quality

Why Epoch?

Every AI agent hallucinates timelines. "This should take about 2 hours" becomes 2 days. Epoch gives AI grounded, data-driven estimates instead of guesses. It packages established estimation methods (PERT, COCOMO II, Monte Carlo, reference class forecasting) into 26 tools any AI can call -- so your assistant stops guessing and starts calculating.

Works out of the box. Epoch ships with a bundled reference database: task-type correction factors and scope baselines from the maintainer's recorded estimate-vs-actual history plus curated public research, and tool-usage/latency telemetry samples (117,791 across the bundled canary-test and session benchmarks) that keep the token-time bridge grounded. You get reasonable estimates from day one — no data collection or account setup required. If you choose to record your actuals, Epoch's self-improvement engine learns your patterns and gets even more precise over time.

What is MCP?

MCP (Model Context Protocol) is how AI assistants like Claude connect to external tools. Think of it like a plugin system -- you add Epoch with one command, and suddenly your AI assistant can estimate timelines, calculate business days, compare model costs, and predict whether your project will finish on time.

Quick Start

30-second setup -- works in Claude Code, Cursor, VS Code, and Windsurf:

bash
claude mcp add epoch -- npx -y @kyanitelabs/[email protected]

That's it. Your AI assistant now has 26 time estimation tools.

Or add it to your project's .mcp.json:

json
{  "mcpServers": {    "epoch": {      "command": "npx",      "args": ["-y", "@kyanitelabs/[email protected]"]    }  }}

Limit Epoch tools in Hermes

Use Hermes tools.include to expose only these example tools:

yaml
mcp_servers:  epoch:    tools: { include: [pert_estimate, cocomo_estimate] }

Agent Skill

Epoch also ships a public agent skill at skills/epoch/SKILL.md. Use $epoch in compatible agent hosts when you want the agent to choose the right Epoch MCP or CLI workflow for time estimates, business-day math, model-cost comparison, schedule risk, and estimate-vs-actual feedback.

What Can Epoch Do?

What you wantWhat Epoch doesNo jargon
"How long will this take?"Gives you a realistic estimate with best/worst case rangesEstimates
"Can we hit this deadline?"Tells you if your timeline is realistic or riskySchedule risk
"How much will the AI calls cost?"Calculates token costs across 16 AI models side-by-sideCost comparison
"How many business days between now and launch?"Counts days excluding weekends and holidays (5 countries)Calendar math
"Are our estimates getting better?"Tracks your accuracy over time and auto-correctsSelf-improving
"What model should we use?"Compares speed, cost, and quality across all major AI modelsModel comparison

Technical Reference

Everything below is for developers who want to understand the internals, use the CLI or REST API, or contribute to Epoch.

Architecture

Six-layer design with 26 tools for time estimation, scheduling, cost analysis, and feedback:

LayerPurposeTools
1. Core TemporalTime, timezones, duration, date mathget_current_time, convert_timezone, parse_duration, time_math
2. Calendar MathBusiness days, holidays (US/UK/FR/DE/JP)add_business_days, count_business_days
3. EstimationPERT, COCOMO II, sprint, CPM, Monte Carlopert_estimate, cocomo_estimate, sprint_forecast, critical_path, monte_carlo_schedule
4. AnalyticsReference class, context classification, calibration, token-time bridge, wait-mechanism deadlinesreference_class_estimate, estimate_from_context, calibrate_estimates, token_time_bridge, wait_bound
5. Cost & RiskToken cost, model comparison, accuracy trends, risk, COCOMO validationtoken_cost_estimate, compare_models, accuracy_trend, schedule_risk, cocomo_validate, cocomo_ground_truth
6. FeedbackRecord actuals, track pending estimates, batch operations, health checksrecord_actual, get_pending_estimates, batch_record_actuals, feedback_health

Tool Reference

Layer 1 -- Core Temporal

get_current_time -- Current wall-clock time in any IANA timezone

Input:  { timezone: "America/New_York" }Output: {  iso: "2026-05-01T08:30:00.000-04:00",  humanReadable: "Fri, May 1, 2026, 8:30 AM EDT",  timezone: "America/New_York",  utcOffset: "-04:00"}

convert_timezone -- Convert a timestamp between IANA timezones

Input:  { timestamp: "2026-05-01T12:00:00Z", target_tz: "Asia/Tokyo" }Output: {  iso: "2026-05-01T21:00:00.000+09:00",  timezone: "Asia/Tokyo",  utcOffset: "+09:00",  humanReadable: "Fri, May 1, 2026, 9:00 PM JST"}

parse_duration -- Parse human-readable duration strings

Input:  { duration_string: "2h30m" }Output: {  input: "2h30m",  totalSeconds: 9000,  humanReadable: "2 hours 30 minutes"}

time_math -- Date arithmetic operations

Input:  { operation: "add_days", operands: { start_date: "2026-05-01", days: 7 } }Output: "2026-05-08"

Supported operations: add_days, add_business_days, diff, convert_tz, parse_nl, format_duration

Layer 2 -- Calendar Math

add_business_days -- Add N business days with holiday awareness (US, UK, FR, DE, JP)

Input:  { start_date: "2026-05-01", days: 5, country: "US" }Output: {  startDate: "2026-05-01",  endDate: "2026-05-08",  businessDays: 5,  countryCode: "US",  holidaySupport: "holiday_calendar",  humanReadable: "5 business days from 2026-05-01 to 2026-05-08 (US)."}

count_business_days -- Count business days between two dates

Input:  { start_date: "2026-05-01", end_date: "2026-05-15", country: "US" }Output: {  startDate: "2026-05-01",  endDate: "2026-05-15",  businessDays: 10,  countryCode: "US",  holidaySupport: "holiday_calendar",  humanReadable: "10 business days between 2026-05-01 and 2026-05-15 (US)."}

Both tools always include holidaySupport ("holiday_calendar" when the country has a bundled holiday set — US/UK/FR/DE/JP — otherwise "weekends_only") in successful output; country must match ^[A-Za-z]{2}$ — "USA"-style codes are rejected with a readable error.

Layer 3 -- Estimation

pert_estimate -- PERT three-point estimation with confidence intervals and urgency scoring

Input:  {  optimistic: 2,  most_likely: 4,  pessimistic: 12,  unit: "hours"}Output: {  expected: 5,  variance: 2.78,  stdDeviation: 1.67,  confidence95: [1.67, 8.33],  confidence99: [0, 10],  unit: "hours",  urgencyCategory: "medium",  riskLevel: "high",  humanReadable: "Expected 2.86–7.14 hours (80% confidence interval); point estimate 5 hours (ledger-recorded basis; adjustedEstimate 5 applies the correction factor). No task_type was supplied, so this interval is derived from the PERT variance (optimistic/most_likely/pessimistic spread) instead of empirical data.",  developerProfile: { mode: "ai_native", correctionFactor: 1 },  adjustedEstimate: 5,  rawEstimate: 5,  correctionFactor: 1,  n: 0,  interval: { p50: { lower: 3.87, upper: 6.13 }, p80: { lower: 2.86, upper: 7.14 }, p90: { lower: 2.25, upper: 7.75 }, source: "pert_variance" },  intervalNote: "No task_type was supplied, so this interval is derived from the PERT variance (optimistic/most_likely/pessimistic spread) instead of empirical data.",  basisNote: "Interval and point estimate are on the ledger-recorded basis (raw PERT expected × unit factor). adjustedEstimate (5 hours) additionally applies the correction factor (1) and is display-only — it is never recorded or calibrated against."}

(Example from a fresh install with no accumulated feedback; with ≥5 exclusion-filtered matched pairs for the task type, interval.source becomes "empirical_ratio_quantile", n reports the sample size, and intervalPopulation names the ratio population used.)

cocomo_estimate -- COCOMO II software sizing with LLM-adapted cost drivers

Input:  {  kloc: 15,  reasoning_complexity: 1.2,  context_completeness: 1.0,  transformation_impact: 0.8,  iterative_cycles: 1.5,  human_oversight: 1.2}Output: {  kloc: 15,  personMonthsNominal: 99.9,  personMonthsLlmAdjusted: 8.9,  effortMultipliers: {    reasoning_complexity: 1.2,    context_completeness: 1.0,    transformation_impact: 0.8,    iterative_cycles: 1.5,    human_oversight: 1.2,    product: 1.728  },  developerProfile: { mode: "ai_native", correctionFactor: 1.45 }}

LLM-adapted cost drivers include reasoning complexity, context completeness, transformation impact, iterative cycles, and human oversight requirements.

sprint_forecast -- Sprint velocity forecasting from historical data

Input:  {  backlog_points: 100,  velocity_history: [20, 25, 22, 23],  sprint_length_days: 14,  hours_per_sprint: 80}Output: {  backlogPoints: 100,  averageVelocity: 22.5,  requiredSprints: 4.4,  pessimisticSprints: 4.9,  hoursPerPoint: 3.56,  totalHours: 355.6,  completionDays: 62,  sprintLengthDays: 14,  developerProfile: { mode: "ai_native", sprintVelocityPoints: 80, correctionFactor: 1.45 }}

critical_path -- Critical Path Method with merge-bias adjustment for parallel tasks

Input:  {  tasks: [    { name: "A", duration: 5, predecessors: [] },    { name: "B", duration: 3, predecessors: ["A"] },    { name: "C", duration: 4, predecessors: ["A"] }  ]}Output: {  critical_path: ["A", "C"],  total_duration: 9,  slack_per_task: { A: 0, B: 1, C: 0 },  merge_bias_adjustment: 0}

monte_carlo_schedule -- Monte Carlo simulation with seeded PRNG for deterministic, reproducible results

Input:  {  tasks: [    { name: "A", optimistic: 2, most_likely: 4, pessimistic: 8 },    { name: "B", optimistic: 1, most_likely: 3, pessimistic: 6 }  ],  iterations: 10000}Output: {  p10: "5.9",  p50: "7.91",  p80: "9.39",  p95: "10.75",  riskEvents: [    { description: "Task \"A\" exceeded 1.5x PERT expected in 10% of simulations", probability: 0.1, impactDays: 0.05 },    { description: "Task \"B\" exceeded 1.5x PERT expected in 10% of simulations", probability: 0.1, impactDays: 0.04 }  ],  criticalPathProbability: null}

(criticalPathProbability is null unless a target_hours deadline is supplied — then it is the real P(total ≤ target); riskEvents[].impactDays is per-task expected overrun, sorted by impact.)

Layer 4 -- Analytics

reference_class_estimate -- Reference class forecasting with planning fallacy correction

Input:  {  task_type: "feature",  complexity: 3}Output: {  rawEstimate: 2,  correctedEstimate: 2,  correctionFactor: 1,  sampleSize: 0,  baselineSource: "inferred_scope_medium_real_tasks",  scopeUsed: "medium",  scopeInferred: true,  confidence: "pessimistic",  developerProfile: { mode: "ai_native", estimationMape: 15, underestimationBias: 0.2, correctionFactor: 1 },  adjustedEstimate: 2,  basisNote: "correctedEstimate (2 hours) is the ledger-recorded and displayed basis (rawEstimate × correctionFactor). adjustedEstimate (2 hours) additionally applies the developerProfile factor (1) and is display-only — it is never recorded or calibrated against.",  intervalNote: "Fewer than 5 exclusion-filtered historical \"feature\" reference_class_estimate pairs are available yet, so no empirical confidence interval could be computed."}

(Fresh-install output; with accumulated feedback the correction factor and sample size come from your own matched pairs and the empirical interval is populated — correctedEstimate is always the recorded basis.)

Valid task_type values: feature, bugfix, refactor, migration, infrastructure, documentation, testing, design.

estimate_from_context -- Classify a free-text task description and delegate to reference class estimation

Input:  {  context: "Add OAuth2 login support to the API, including refresh token rotation and a new /auth/callback endpoint"}Output: {  tool: "estimate_from_context",  rawEstimate: 2,  correctedEstimate: 2,  correctionFactor: 1,  sampleSize: 0,  baselineSource: "inferred_scope_medium_real_tasks",  scopeUsed: "medium",  scopeGuide: "For feature tasks: small=~2.3h, medium=~6h, large=~10.6h, xl=~17h",  classification: {    classified_task_type: "feature",    classified_complexity: 3,    confidence: "medium",    signals: ["task_type_matched:feature"],    task_type_from_hint: false,    complexity_from_hint: false  },  note: "Using reference database correction factors. Submit actuals via record_actual to improve accuracy."}

Classifies task_type and complexity from free text (an issue body, PR/diff description, or task summary) using a local, deterministic keyword/signal heuristic -- no LLM call is made. Caller-supplied task_type/complexity hints always override the classification. The resolved inputs are then delegated to the same reference-class-forecasting path used by reference_class_estimate, so the response carries the same estimate fields plus a classification provenance block explaining how the tool read the context. When classification confidence is low, an additional lowConfidenceNote field is returned rather than silently guessing.

calibrate_estimates -- Team-specific accuracy calibration from historical estimated vs actual data

Input:  {  task_type: "feature",  team_id: "backend"}Output: {  correctionFactor: 1.45,  accuracyTrend: "stable",  velocityTrend: "stable",  recommendations: [    "Using reference database correction factor (1.45x) — 8 matched samples, need 5.",    "Record actuals via POST /v1/feedback/record-actual to refine for your team's patterns."  ]}

token_time_bridge -- Map LLM token budgets to wall-clock time for 16 model families

Input:  {  tokens: 50000,  model: "claude-sonnet-4-20250514",  tool_calls: 10,  reasoning_depth: "deep"}Output: {  estimatedSeconds: 697,  estimatedMinutes: 11.6,  confidence: "likely",  urgency: "short",  breakdown: {    promptTokens: 15000,    completionTokens: 35000,    toolOverheadSeconds: 2  }}

Layer 5 -- Cost & Risk

token_cost_estimate -- Token cost estimation for LLM API calls

Input:  {  tokens: 50000,  model: "claude-sonnet-5"}Output: {  tokens: 50000,  model: "claude-sonnet-5",  estimatedSeconds: 695,  estimatedMinutes: 11.6,  estimatedCost: 0.57,  costBreakdown: { inputCost: 0.045, outputCost: 0.525, toolCallOverheadCost: 0 },  confidence: "likely"}

compare_models -- Side-by-side cost and capability comparison across LLM models

Input:  {  tokens: 50000,  sort_by: "cost"}Output: {  tokens: 50000,  models: [    { model: "gemini-2.0-flash", estimatedCost: 0.0155, qualityTier: "fast", tokensPerSecond: 230 },    { model: "deepseek-v3", estimatedCost: 0.0189, qualityTier: "standard", tokensPerSecond: 97 },    { model: "gpt-4o-mini", estimatedCost: 0.0233, qualityTier: "fast", tokensPerSecond: 180 }  ],  sortBy: "cost"}

accuracy_trend -- Track estimation accuracy over time from recorded feedback data

Input:  { team_id: "backend", window_size: 50 }Output: {  overallTrend: "improving",  currentMape: 26.5,  industryBaselineMape: 25,  totalEstimates: 1049,  totalWithActuals: 1049,  windows: [{ period: "Window 1 (estimates 1-50)", mape: 32, bias: 5.3, sampleSize: 50 }]}

schedule_risk -- Schedule risk scoring for project timelines

Input:  {  estimated_hours: 40,  task_type: "feature"}Output: {  estimatedHours: 40,  riskLevel: "low",  confidenceIntervals: { p50: 40, p80: 45.1, p95: 49.9 },  historicalAccuracy: { mape: 15, sampleSize: 42 },  recommendation: "Low risk. Estimate is within normal variance.",  humanReadable: "Schedule risk: low. MAPE: 15% (based on 42 historical records). Confidence intervals: p50=40h, p80=45.1h, p95=49.9h."}

cocomo_validate -- Validate COCOMO II estimates against reference data

Input:  {}Output: {  projectsEvaluated: 182,  mape: 85.55,  bias: 53.5,  byProjectType: {    organic: { mape: 86.57, count: 22 },    semidetached: { mape: 84.75, count: 106 },    embedded: { mape: 86.71, count: 54 }  },  recommendedAdjustments: []}

cocomo_ground_truth -- Benchmark all COCOMO variants (Basic, COCOMO II nominal, AI 12x speedup, AI + developer-profile gradients) against the same real historical projects, with per-dataset and per-type breakdowns

Input:  {}Output: {  projectsEvaluated: 182,  models: [    { name: "COCOMO Basic", mape: 85.55, mmre: 0.856, pred25: 0.313, pred50: 0.544, bias: 53.5, count: 182 },    ...  ],  byDataset: { ... },  byType: { ... },  winner: "AI + Profile (human)",  conclusion: "Best model: AI + Profile (human) (MAPE=79.66%). ...",  humanReadable: "..."}

ai_native Mode

Epoch tools support dual estimation modes to account for the fundamentally different velocity of AI-assisted vs human-only development.

When ai_native=true (default), tools use Epoch's reference database with tool-aware correction factors. These baselines reflect AI agent workflows: faster iteration, higher output volume, and different error profiles.

When ai_native=false, tools apply human developer baselines:

ParameterHuman BaselineAI-Native Baseline
Feature development14 calendar days (industry data)5.7h median (126K+ real tasks)
Bug fix turnaround72 hours (industry data)6.2h median (139 matched estimate-actual pairs; source: src/lib/supplementary-data.ts)
Sprint velocity35 story points (industry data)80 story points
Estimation accuracy (MAPE)25% (Jorgensen 2004)15% (from AI-native profiles)
Correction factor1.8x (industry standard)1.07-1.45x (from reference DB)

Tools that support ai_native: pert_estimate, cocomo_estimate, sprint_forecast, reference_class_estimate, schedule_risk.

Hybrid workflows: ai_native accepts a float from 0.0 (fully human) to 1.0 (fully AI-native). Values like 0.5 produce interpolated profiles for mixed AI/human workflows. Boolean values (true/false) remain supported for backward compatibility.

Self-Improvement Engine

Epoch learns your patterns the more you use it. The bundled reference database combines a small set of maintainer estimate-vs-actual pairs (the seed of the correction factors), curated public research baselines, and 117,791 tool-usage/latency telemetry samples (canary-test and session benchmarks — these ground latency and token-time figures, not estimation accuracy). It gives you a sane starting point on day one — and it gets sharper as you record actuals.

If you record your actuals, Epoch personalizes further:

  1. Estimate -- Generate an initial estimate with any estimation tool
  2. Record -- Track the actual outcome (record_actual)
  3. Learn -- Self-improvement computes personalized correction factors from your data
  4. Improve -- Future estimates apply your team's actual patterns
  5. Trend -- accuracy_trend tracks whether your accuracy is improving over time
Your estimates + your actuals -> Your correction factors -> Better estimates -> Repeat

The loop can close itself. Recording actuals is the step everyone forgets, so Epoch can do it for you: epoch auto-actuals --session <id> records wall-clock-derived actuals for a session's unfinished estimates (agent hosts can wire it into a session-end hook). Auto-recorded actuals are sanity-bounded (0.05–12h, <10x the estimate), provenance-labeled auto_wallclock, never overwrite a real actual, and feedback_health reports them separately (byProvenance) so automated data can't silently skew your calibration.

If the estimate writer cannot stamp inputs.session_id, pass the session's real time span explicitly: epoch auto-actuals --session <id> --window-start <ts> --window-end <ts>. Each <ts> may be epoch milliseconds or an ISO timestamp, and both flags must be supplied together with the end no earlier than the start. Epoch uses this window only when the durable ledger contains no estimate with the exact session ID, including a finalized or expired estimate, and only for pending estimates with no session stamp; estimates stamped to another session are never selected. Window-selected candidates still pass the same sanity and duplicate guards as exact session matches.

Your git history is another actuals source. epoch mine-git --repo <path> --since <date> walks a local git repository — offline and read-only by construction — and joins pending estimates to closed work (merged PRs and plain merges) via the identity keys planning flows already stamp: task_label, branch, or issue_ref. Two cycle-time windows are derived per unit and reported separately, never blended: first-commit→merge (provenance git_derived) and open→merge (provenance git_derived_review_inclusive, computed wherever a branch-creation anchor survives locally — plain clones have none, and the window is never fabricated). Pick the recorded window with --window dev|review-inclusive; the other rides along in the actual's notes. Mined actuals are sanity-bounded ([0.05h, 720h] — calendar windows legitimately span nights and weekends — and <10x the estimate), never overwrite a real actual, and feedback_health segments them in its own byProvenance bucket (gitDerived) so calendar-window noise cannot silently skew calibration.

Estimates lead with honest ranges. When at least 5 matched pairs exist for a task type, pert_estimate and reference_class_estimate open with a calibrated 80% interval ("Expected 1.6–4.2 hours (80% confidence interval); point estimate 2.5 hours") derived from your own historical estimate-vs-actual ratios — and say plainly when there isn't enough data yet.

The engine detects systematic biases (chronic under-estimation, accuracy degradation) and surfaces actionable recommendations.

You do not need to share data with anyone for this to work. Self-improvement runs entirely locally using your own ~/.epoch/ data.

The correction loop, measured

The self-improvement claim above isn't marketing copy -- it's backed by a runnable receipt. scripts/backtest-pert-correction.mjs makes a read-only temp copy of your ~/.epoch ledger, chronologically splits matched pert_estimate (estimate, actual) pairs 80/20, trains the learned per-(tool, task_type) correction factor on the training split only, and reports MdAPE on the held-out test split it never trained on:

bash
npx tsx scripts/backtest-pert-correction.mjs

Measured on the maintainers' production ledger (697 held-out matched pairs at time of writing): MdAPE improved from 105.2% (uncorrected) to 80.5% (learned correction) on data the correction factor never saw during training. This is the mechanism EPOCH_PERT_LEARNED_CORRECTION gates behind before it's recommended on by default -- the script also checks that the corrected median actual/predicted ratio lands in [0.7, 1.3], and reports HOLD (not recommended yet) when that second guard hasn't cleared, so the flag doesn't ship as "on" until both hold. Run the script against your own ledger for your own numbers; they move as more actuals get recorded, which is the point.

reference_class_estimate's correction factors are the same learned mechanism applied to a different tool. Track its current calibration with epoch data status or feedback_health (per-tool MAPE/MdAPE, bias, and trend), or generate a full calibration decision-surface report with node scripts/build-calibration-dashboard.mjs -- also strictly read-only against your ledger.

Data Pipeline

Epoch uses a three-layer data strategy so it's accurate from the start and gets better over time:

1. Bundled reference database (works immediately, no setup): Epoch ships with a pre-built reference database: correction factors seeded from the maintainer's own estimate-vs-actual history, scope baselines from curated public research, and 117,791 tool-usage/latency telemetry samples (canary tests and session benchmarks across 8 task types and 5 complexity levels). Telemetry sample counts are reported separately from matched estimate-actual pair counts everywhere Epoch shows a data size.

2. Local self-improvement (automatic, private): As you use Epoch and record actuals, the self-improvement engine recalibrates correction factors from your data. This runs entirely locally in ~/.epoch/ — nothing leaves your machine. The engine triggers automatically every 100 tool calls or 24 hours.

  • Auto-recording: Use scripts/auto-record-actual.mjs to automatically record actual time against pending estimates.
  • Source tagging: Set EPOCH_SOURCE=<project-name> to tag estimates by project.
  • Inspect your data: epoch data where and epoch data status show what's stored locally.

3. Community contributions (optional, opt-in): You can optionally share anonymized data to help improve baselines for all users. Community data is stripped of all identifying information — only task type, complexity, estimated hours, actual hours, and date remain. See CONTRIBUTING-data.md for format and privacy requirements.

bash
epoch share-data --validate --description "My anonymized estimation data"

This is completely optional. Epoch works great without it.

Surfaces

Epoch exposes the same 26 tools through three interfaces:

SurfaceTransportUse Case
MCP ServerstdioClaude Code, Cursor, VS Code, Windsurf
CLIDirect invocationScripts, CI/CD, quick lookups
REST APIHTTP (Hono)Web apps, AI agents, integrations

Default behavior: running epoch with no arguments starts the MCP stdio server.

CLI

bash
# PERT estimateepoch pert-estimate --optimistic 2 --most-likely 4 --pessimistic 12 --unit hours
# Token-to-time bridgeepoch token-time-bridge --tokens 50000 --model claude-sonnet-4-20250514
# Monte Carlo simulationepoch monte-carlo-schedule --tasks '[{"name":"A","optimistic":2,"most_likely":4,"pessimistic":8}]'
# COCOMO II estimateepoch cocomo-estimate --kloc 15
# Schedule risk scoreepoch schedule-risk --estimated-hours 40
# List all toolsepoch list-tools
# Pretty table outputepoch pert-estimate --optimistic 2 --most-likely 4 --pessimistic 12 --pretty

From a source checkout, after pnpm install and pnpm run build, the same CLI is dist/index.js. CI runs this block on every change, and each # expect: line must appear in the output, so these examples cannot drift from the code:

bash
node dist/index.js --versionnode dist/index.js parse-duration --duration 2h30m# expect: "totalSeconds": 9000node dist/index.js count-business-days --start-date 2026-05-01 --end-date 2026-05-15 --country US# expect: "businessDays": 10node dist/index.js add-business-days --start-date 2026-05-01 --days 5 --country US# expect: "endDate": "2026-05-08"node dist/index.js time-math --operation add_days --date 2026-05-01 --days 7# expect: "data": "2026-05-08"

REST API

bash
# Start the serverepoch serve --port 3099# or: EPOCH_TRANSPORT=http EPOCH_PORT=3099 epoch
# Call any toolcurl -X POST http://localhost:3099/v1/tools/pert_estimate \  -H "Content-Type: application/json" \  -d '{"optimistic": 2, "most_likely": 4, "pessimistic": 12, "unit": "hours"}'
# Health checkcurl http://localhost:3099/health
# OpenAPI speccurl http://localhost:3099/openapi.json
Feedback REST routes

/v1/feedback/* is a stable REST API for closing the estimate-to-actual loop. It is not an alias for the feedback tools: request validation, response shapes and status codes differ from calling the same tools through POST /v1/tools/<name> (or MCP or the CLI). Code against the surface you call.

RouteREST contractTool via /v1/tools/<name>
POST /v1/feedback/record-actual{estimate_id, actual_hours, notes?}. Success is {estimateId, actualHours, recorded, flagged?, flagHint?} (camelCase). Status: 400 for bad input or a caller-fixable rejection (below_threshold, synthetic_id, unknown_tool, a failed auto or git sanity gate); 409 for a duplicate; 500 for a storage failure.record_actual: success is {recorded, estimate_id, actual_hours, message, flagged?, flagHint?} (snake_case). Every failure is a 422 whose message names the reason.
GET /v1/feedback/pending?limit=limit defaults to 50, is clamped to 1–200, and a non-numeric value falls back to 50. Returns every pending estimate up to the limit, as full records.get_pending_estimates: limit defaults to 20, max 100. Returns {count, summary, estimates}, where count covers the limit but estimates holds at most 10, the most recent.
POST /v1/feedback/batch-record-actuals1–500 entries. Partial success: valid entries are recorded, and each invalid one is reported in errors by index. If none succeed, 422, or 500 when the cause is storage.batch_record_actuals: Zod validates every entry first, so one invalid entry rejects the whole batch (422) before anything is recorded.
GET /v1/feedback/healthThe feedback health report.feedback_health: the same report plus intervalCoverage (P80 prediction-interval calibration).

Agent-First

Epoch is built for agents as first-class callers, not humans typing in a terminal as an afterthought.

Why agents need time-sense. An LLM has no grounded sense of duration or cost -- it will say "quick fix" for a two-day migration and "big project" for a two-hour config change with equal confidence, because it has no feedback loop telling it otherwise. That's fine for a chat answer; it breaks down the moment an agent is planning multi-step work, sequencing a sprint, or deciding whether a deadline is realistic. Epoch gives the agent a calculator instead of a guess: PERT/COCOMO/Monte Carlo math, a reference-class baseline built from real task data, and a feedback loop that corrects itself as the agent (or its operator) records actuals.

EPOCH_TELEMETRY=1 for headless/agent operators. Telemetry is off by default and requires informed consent. For a human at a terminal, that consent is epoch telemetry enable, which shows the data and asks for confirmation. An agent should never be the one clicking "yes" to that prompt on its own behalf -- there is deliberately no MCP tool that enables telemetry, so an agent cannot self-consent. For headless or agent-operated deployments, the operator opts in out-of-band by setting EPOCH_TELEMETRY=1 in the server's environment (for example, the env block of the MCP server config) before the agent ever starts. Consent stays with the human who configures the deployment, not the agent that runs inside it.

MCP client qualification. Epoch's telemetry schema (v2) records client_name/client_version from the MCP clientInfo your host reports at connection time, plus transport (stdio/http). This is agent qualification, not agent identification: it lets aggregate accuracy stats count "5.7h median across N agent-driven feature estimates" as first-class agent data rather than lumping it in with anonymous CLI usage, without adding any new per-user identifying signal. MCP clients that report clientInfo (Claude Code, Cursor, and most current hosts do) get this for free; clients that don't are still fully functional, they just show up as client_name: null.

Epoch also provides built-in discoverability endpoints so agents can find and use the HTTP API without prior configuration:

EndpointDescription
GET /.well-known/ai-plugin.jsonOpenAI plugin manifest
GET /llms.txtLLM-consumable documentation
GET /openapi.jsonOpenAPI 3.1 specification
GET /healthService health and version

Installation

bash
git clone https://github.com/KyaniteLabs/Epoch.gitcd Epochpnpm installpnpm run build

Development

bash
pnpm test          # Run the Vitest suitepnpm run build     # Build with tsuppnpm run typecheck # TypeScript strict mode checkpnpm run dev       # Run development serverpnpm run inspector # Open MCP Inspector for interactive testing

Tech Stack

  • Runtime: Node.js 22+ (ESM; engines.node >=22 — Node 20 reached EOL April 2026)
  • Language: TypeScript 6 (strict mode, noUncheckedIndexedAccess, verbatimModuleSyntax)
  • Validation: Zod 4 with .describe() on every field
  • MCP SDK: @modelcontextprotocol/sdk 1.12+
  • HTTP: Hono (lightweight, multi-runtime)
  • CLI: Commander.js
  • Date Handling: date-fns 4.x + date-fns-tz 3.x
  • Build: tsup (ESM output)
  • Testing: vitest 4.x with v8 coverage (97% statements, 88% branches)

Configuration

VariableDefaultDescription
EPOCH_TRANSPORTstdioTransport mode: stdio or http
EPOCH_PORT3000HTTP server port
EPOCH_HOST127.0.0.1HTTP server bind address
EPOCH_DATA_DIR~/.epoch/Data directory for feedback and self-improvement
EPOCH_COMMUNITY_DIRdata/community/Community data directory
EPOCH_RATE_LIMIT100Max requests per minute per client (HTTP only). 0 disables limiting; invalid or negative values fall back to 100 with a warning. 429 responses carry a Retry-After header.
EPOCH_TRUST_PROXY0Set to 1 only when running behind a trusted reverse proxy: rate limiting then keys on X-Forwarded-For/X-Real-IP instead of the connection address (those headers are client-spoofable, so they are ignored by default).
EPOCH_CORS_ORIGINS(none)Comma-separated origins allowed by the HTTP API's CORS handling (e.g. https://app.example.com,http://localhost:5173), or * to allow any origin. Default: no CORS headers at all — same-origin tools, curl, and MCP clients are unaffected; cross-origin browser requests fail. Preflight OPTIONS requests are always answered.
EPOCH_SOURCE(none)Project/source tag attached to estimate records
EPOCH_TELEMETRY0Set to 1 to enable anonymous telemetry. See Telemetry & Privacy.
EPOCH_TELEMETRY_ENDPOINT(none)Override the configured telemetry receiver endpoint for status/submission.

Telemetry & Privacy

Epoch can share anonymized estimate/actual pairs to improve accuracy for all users. This is off by default and requires explicit opt-in.

Agent-operator consent model: there is deliberately no MCP tool that enables telemetry -- an agent must not be able to self-consent on a human's behalf. Humans opt in interactively with epoch telemetry enable. Agent/headless operators opt in out-of-band by setting EPOCH_TELEMETRY=1 in the server's environment before the agent starts (see Agent-First). Either way, consent belongs to the person who configures the deployment.

bash
epoch telemetry enable     # Opt in (shows exactly what will be shared)epoch telemetry preview    # Preview anonymized data before enablingepoch telemetry status     # Show current settingsepoch telemetry set-endpoint --endpoint https://your-server.example.com/v1/telemetryepoch telemetry submit     # Submit queued anonymized records to the configured endpointepoch telemetry disable    # Opt outepoch telemetry export     # Export all local data as anonymized JSON

What is shared: task type, complexity, tool name, estimated hours, actual hours, ratio, date (YYYY-MM-DD only).

What is NEVER shared: project names, notes, team IDs, IP addresses, timestamps with time-of-day, source code, descriptions.

See Privacy Policy and Telemetry Documentation for full details.

Where Your Data Lives

By default, Epoch stores local data under ~/.epoch/ or EPOCH_DATA_DIR. Your local usage data is not automatically committed to GitHub and is not automatically submitted anywhere.

bash
epoch data where     # Show local data file locationsepoch data status    # Show data file counts, feedback health, telemetry config

Sharing Data

Use epoch share-data --validate to create a community-data JSON file suitable for data/community/. Review the file before opening a PR.

bash
epoch share-data --description "Anonymized Epoch usage export" --validate

Machine Labels

Fleet host inventories are not published in this repository. docs/ops/machines.md documents the schema used to track machines internally; actual hostnames, addresses, and SSH users are supplied at runtime via environment variables (see scripts/ and docs/ops/epoch-fleet-audit.md). windows-receiver is a historical label only.

License

Apache License 2.0. See LICENSE for full terms. Dated relicensing history (including npm 0.2.x MIT consumers): LICENSING.md.


Part of KyaniteLabs

More from KyaniteLabs. Related projects:

  • kinocut — guardrailed video-editing MCP server for AI agents (formerly mcp-video)
  • DialectOS — Spanish dialect localization MCP server & CLI
  • checkyourself — local-first production-readiness checks for AI-built code

→ More at kyanitelabs.tech

What is Epoch?

Epoch is a time estimation MCP server that helps engineering leads, agents, and planners who need calibrated duration estimates produce PERT/reference-class estimates and record actuals.

ProductEpoch
Categorytime estimation MCP server
Best forengineering leads, agents, and planners who need calibrated duration estimates
Nota calendar or project tracker
SourceGitHub
Keywordstime estimation MCP, PERT, reference class forecasting

Who it's for

  • Primary: engineering leads, agents, and planners who need calibrated duration estimates
  • Use when you need to produce PERT/reference-class estimates and record actuals
  • Skip if you need a calendar or project tracker

FAQ

What is Epoch?

Epoch is a time estimation MCP server. It helps engineering leads, agents, and planners who need calibrated duration estimates produce PERT/reference-class estimates and record actuals.

Who should use Epoch?

engineering leads, agents, and planners who need calibrated duration estimates.

How is Epoch different?

Unlike vibes-based hour guesses, Epoch forces structured estimate + actual feedback.

Is Epoch production software?

Treat the README status and release tags as source of truth for maturity. Validate against your own requirements before production use.

Status

  • Maintained as of 2026 on the default branch
  • Prefer release tags when pinning dependencies
  • Report issues on GitHub

Agent surface

  • Coding agents: read this README first, then repo docs/AGENTS.md if present
  • Prefer machine-readable briefs (llms.txt) when the repo ships one
  • MCP or skill entrypoints are documented in-repo when applicable

Contributing

Issues and PRs welcome on GitHub. Keep public docs free of secrets and machine-local paths.

License

See LICENSE in this repository (or package metadata if license is package-only).

來源:README.md,提交 36de521

工具

26

get_current_time

Returns the current date and time in the specified IANA timezone. Useful for grounding the LLM in the user's local time. Example timezones: 'UTC', 'America/New_York', 'Europe/London', 'Asia/Tokyo'.

輸入參數timezone

convert_timezone

Converts an ISO-8601 timestamp to a target IANA timezone. The input timestamp must include timezone information or be in UTC. Returns the localised time, UTC offset, and human-readable format. Use when you need to display or compare a moment in another region's local time.

輸入參數target_tz必填timestamp必填

parse_duration

Parses a human-readable duration string into structured seconds. Supports combinations of y (years), mo (months), w (weeks), d (days), h (hours), m (minutes), s (seconds). Examples: '2h30m', '1d6h', '1w3d', '45m'. Returns the total seconds for the duration. Use when normalising a free-text duration for arithmetic or comparison.

輸入參數duration_string必填

wait_bound

Derives a deadline from the mechanism a wait actually rides (three-clock law v1.1 A2): fleet = k x cycle_seconds + PERT(turn) + 1 sd; world = PERT(turn) with a REQUIRED named external anchor; ceo = the named ladder rung (machine time never consumes a CEO rung). Returns the bound in seconds, the deadline in UTC and local (PT by default), and an envelope-ready TTL integer. Round human hours are unexamined slack.

輸入參數ceo_rungcycles_ktime_zoneclock_class必填cycle_secondsfrom_timestampexternal_anchorturn_optimistic_minutesturn_most_likely_minutesturn_pessimistic_minutes

time_math

Performs compound time-math operations. Dispatches to the appropriate sub-operation based on the 'operation' parameter. Operations: add_days, add_business_days, diff, convert_tz, parse_nl, format_duration. Use this for multi-step or dynamic time operations; for single-purpose calls use get_current_time, convert_timezone, parse_duration, add_business_days, or count_business_days.

輸入參數operands必填operation必填

add_business_days

Adds N business (working) days to a start date, skipping weekends and country-specific public holidays. Supports US, UK, FR, DE, and JP holidays. Returns the resulting date. Use when computing a deadline that excludes non-working days.

輸入參數days必填countrystart_date必填

count_business_days

Counts the number of business (working) days between two dates, excluding weekends and country-specific public holidays. The count is exclusive of the start date and inclusive of the end date. Returns the integer day count. Use when measuring working-time span between two dates.

輸入參數countryend_date必填start_date必填

pert_estimate

Calculate PERT expected duration from three-point estimates using Beta distribution. Formula: E = (O + 4M + P) / 6. Returns expected value, variance, standard deviation, and 95%/99% confidence bounds with urgency categorization. Use when estimating task duration with uncertain outcomes.

輸入參數unitprojectai_nativetask_typecomplexityoptimistic必填session_idtask_labelmost_likely必填pessimistic必填

cocomo_estimate

LLM-adapted COCOMO II parametric effort estimation. Replaces traditional 17 human-labor cost drivers with 5 LLM-specific factors: reasoning complexity, context completeness, transformation impact, iterative cycles, and human oversight. Returns both nominal and LLM-adjusted person-months. Use when estimating effort for a codebase you can size in KLOC. iterative_cycles: values <= 2.0 are literal multipliers (0.5 = one-shot, 1.0 = typical debug loop, 2.0 = heavy back-and-forth); values above 2.0 are literal cycle counts, each additional cycle adding 0.1 of multiplier anchored at 2.0 (2.0 -> 2.0x, 3 -> 2.1x, 10 -> 2.8x) — monotonic with no cliff at 2.0.

輸入參數kloc必填projectai_nativetask_typesession_idtask_labelhuman_oversightiterative_cyclescontext_completenessreasoning_complexitytransformation_impact

sprint_forecast

Forecast sprint completion date from backlog size and historical velocity. Computes average velocity from sprint history, converts story points to hours, and returns required sprints with pessimistic estimate based on velocity variance. Use when planning a sprint completion date from backlog size and velocity history.

輸入參數projectai_nativetask_typesession_idtask_labelbacklog_points必填hours_per_sprintvelocity_history必填sprint_length_days

critical_path

Compute critical path with merge-bias adjustment for project schedules. Performs forward/backward pass to identify critical tasks and slack. Applies merge bias: tasks with >2 predecessors get 5% duration increase per extra predecessor. Returns the critical path, task slack, and project duration. Use when sequencing dependent tasks to find the longest path and available slack.

輸入參數tasks必填projecttask_typesession_idtask_label

monte_carlo_schedule

Run Monte Carlo simulation for probabilistic schedule risk analysis. Samples task durations from triangular distributions and returns P10/P50/P80/P95 completion estimates with identified risk events. Use seed for reproducible results. Supply target_hours (working hours; durations are 8-hour days) to get criticalPathProbability = P(total <= target_hours); without it that field is null.

輸入參數seedtasks必填projecttask_typeiterationssession_idtask_labeltarget_hours

reference_class_estimate

Data-driven estimate using reference class forecasting. Applies historical correction factors based on actual-vs-estimated ratios. When no historical data exists, uses industry averages (1.3-2.2x for software tasks). Prioritize this over algorithmic models when historical data is available.

輸入參數scopeprojectteam_idai_nativetask_type必填complexitysession_idtask_label

calibrate_estimates

Recalculate team-specific correction factors from historical estimation data. Compares estimated vs actual hours to compute a correction multiplier. Requires PM system integration for best results. Returns recommendations for improving estimation accuracy. Use when you have accumulated actuals and want to refresh team calibration factors.

輸入參數team_id必填period_daysminimum_samples

token_time_bridge

Map LLM token budgets to estimated wall-clock time. Uses model-specific calibration data (tokens/second, reasoning overhead, tool-call latency) to estimate how long a task will actually take. Bridges the gap between token-space (how agents reason) and time-space (what humans need). Use token_cost_estimate instead when dollar cost matters too.

輸入參數model必填tokens必填projecttask_typesession_idtask_labeltool_callsreasoning_depth

token_cost_estimate

Estimate wall-clock time AND dollar cost for LLM token usage. Combines token-to-time mapping with model-specific pricing data. Returns cost breakdown (input/output/overhead) alongside the time estimate. Use token_time_bridge when you only need wall-clock time and not dollar cost.

輸入參數model必填tokens必填task_typetool_callsreasoning_depth

compare_models

Compare all LLM models side-by-side for a given token budget. Ranks models by estimated cost or time. Shows quality tier for each model. Use when choosing which model to use for a task.

輸入參數tokens必填sort_bytool_callsreasoning_depth

accuracy_trend

Track estimation accuracy improvement over time. Computes sliding-window MAPE and compares against industry baseline (25%). Shows whether your estimates are improving, degrading, or stable. Industry research shows estimation accuracy does NOT improve with experience (Cao 2022) — self-correcting systems like Epoch can buck this trend.

輸入參數team_idwindow_size

schedule_risk

Assess schedule risk for an estimate using historical accuracy data. Computes confidence intervals (p50/p80/p95) based on your team's MAPE. Returns risk level and actionable recommendations. Uses industry baseline (25% MAPE) when no historical data is available.

輸入參數projectteam_idai_nativetask_typecomplexitysession_idtask_labelestimated_hours必填

cocomo_validate

Validate COCOMO estimation model against 195 real historical projects. Runs the COCOMO Basic formula against projects from NASA93, COCOMO81, Albrecht, and Kemerer datasets. Reports overall MAPE, bias, per-type accuracy, and recommended coefficient adjustments. Use cocomo_ground_truth for the full multi-model benchmark across all COCOMO and AI-adjusted models.

輸入參數dataset_filter

cocomo_ground_truth

Validate all COCOMO estimation models against 240 real historical projects with known effort. Runs 6 models in parallel: COCOMO Basic, COCOMO II Nominal, COCOMO II + AI 12x speedup, and AI + developer profile at human/hybrid/ai_native gradients. Reports MAPE, MMRE, PRED(25), PRED(50), bias per model, with breakdowns by dataset and project type. Use cocomo_validate for a quicker Basic COCOMO-only validation pass.

輸入參數dataset_filter

record_actual

Submit actual hours for a previous estimate to improve future accuracy. Pairs with any estimation tool. The estimate_id comes from the estimate response. Actuals feed into the self-improvement loop — after enough samples, correction factors update automatically to reduce estimation bias.

輸入參數unitnotesestimate_id必填actual_hours必填calibration_provenance

get_pending_estimates

List recent estimates that have not yet received actual-hour feedback. Returns estimates awaiting actuals so you can submit feedback via record_actual. Use this to close the estimation feedback loop and improve accuracy over time.

輸入參數limit

batch_record_actuals

Record actual hours for multiple estimates in a single call. Efficient for bulk feedback submission — accepts 1 to 500 entries at once. Each entry pairs an estimate ID with the actual hours spent. Returns total/succeeded/failed counts. Use when closing the feedback loop for many estimates at once; pass estimate_id values from get_pending_estimates.

輸入參數entries必填

feedback_health

Get a health report on the estimation feedback loop. Shows total estimates, actuals, match rate, MAPE by tool and task type, and self-improvement readiness (which types have enough data for auto-calibration). Use when checking whether you have enough recorded actuals for calibration to kick in.

無需輸入參數。

estimate_from_context

Classify a free-text task description and delegate to reference-class estimation. Classifies task_type and complexity from free text (issue body, PR/diff description, or task summary) using a LOCAL, deterministic keyword/signal heuristic — no LLM call (see src/lib/context-estimate.ts). Caller-supplied task_type/complexity hints always override the classification. Delegates the resolved inputs to the same reference-class-forecasting path used by reference_class_estimate, and returns classification provenance (classified_task_type, classified_complexity, confidence, signals) alongside the estimate so callers can judge how much to trust it.

輸入參數context必填team_idtask_typecomplexity

版本歷史

2
  1. v0.6.0最新Oct 30, 2026
  2. v0.5.2Oct 13, 2026

評分

已滿足適用檢查項的 90%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分
  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具必需15 分
  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分