Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -1178,3 +1178,4 @@ Legend: `shipped` ≥95% checked · `in-flight` 1–94% · `drafted` 0% · `—`
| [118-pin-visibility-boundary](./specs/118-pin-visibility-boundary/) | — | — |
| [119-scan-default-trust](./specs/119-scan-default-trust/) | — | — |
| [120-scan-structural-gate](./specs/120-scan-structural-gate/) | — | — |
| [122-day-two-activation](./specs/122-day-two-activation/) | `drafted` | 0/78 (0%) |
34 changes: 34 additions & 0 deletions specs/122-day-two-activation/checklists/requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Specification Quality Checklist: Day-Two Activation (E3)

**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2026-10-20
**Feature**: [spec.md](../spec.md)

## Content Quality

- [x] No implementation details (languages, frameworks, APIs) — code references are context only (house style); requirements state outcomes
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed

## Requirement Completeness

- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified

## Feature Readiness

- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification

## Notes

- House style (Specs 108-117) keeps file:line context tables in the spec; FRs stay outcome-level. Accepted deviation from the template's "no implementation details" rule.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
openapi: 3.0.3
info:
title: MCPProxy Spec 122 - activity summary digest (additive)
version: "122"
paths:
/api/v1/activity/summary:
get:
summary: Existing endpoint. Spec 122 adds `view` (telemetry view counting, FR-032) and the `digest` response section (FR-018). Existing params and fields unchanged.
parameters:
- { name: period, in: query, schema: { type: string, enum: ["1h", "24h", "7d", "30d"] } }
- { name: view, in: query, required: false, schema: { type: string, enum: [web, tray, cli] }, description: "Unknown values ignored (no error). Counted at most once per surface per hour." }
responses:
"200":
description: Summary (existing fields + digest)
content:
application/json:
schema:
type: object
description: Standard envelope; existing summary fields and the new digest live under data.
properties:
success: { type: boolean }
data:
type: object
properties:
digest: { $ref: "#/components/schemas/ActivityDigest" }
components:
schemas:
ActivityDigest:
type: object
properties:
calls_brokered:
type: object
properties:
total: { type: integer }
by_client: { type: array, maxItems: 3, items: { type: object, properties: { client: {type: string}, count: {type: integer} } } }
other: { type: integer }
unknown: { type: integer }
coverage: { type: string, enum: [complete, partial], description: "partial while legacy tool_call records without the dispatched key are in the window" }
held_for_review:
type: object
description: "null figures + reason=not_attributable when profile/client/token filter is set (FR-023). Scoped callers: only allowed servers."
properties:
entered: { type: integer, nullable: true }
new: { type: integer, nullable: true }
changed: { type: integer, nullable: true }
currently_awaiting: { type: integer, nullable: true }
coverage: { type: string, enum: [complete, partial] }
reason: { type: string, nullable: true, enum: [not_attributable] }
blocked:
type: object
properties:
total: { type: integer }
quarantine: { type: integer }
profile: { type: integer }
other: { type: integer }
responses_blocked: { type: integer, description: "post-dispatch output blocks (output_sanitisation, output_schema); not in total; those calls are brokered" }
sensitive_data:
type: object
properties: { calls_with_detections: { type: integer } }
coverage:
type: object
properties:
state: { type: string, enum: [complete, partial, unavailable], description: "unavailable for scoped callers (watermark is installation-wide)" }
complete_since: { type: string, format: date-time, nullable: true }
window:
type: object
description: "Shared source for the 'this week' vs 'since <date> (N days)' label. effective_start = max(start, install anchor); anchorless installs always label=period."
properties:
start: { type: string, format: date-time }
effective_start: { type: string, format: date-time }
label: { type: string, enum: [period, since_install] }
days: { type: integer }
102 changes: 102 additions & 0 deletions specs/122-day-two-activation/contracts/onboarding-starter.openapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
openapi: 3.0.3
info:
title: MCPProxy Spec 122 - starter offer and onboarding marks (additive)
version: "122"
description: All responses use the standard envelope {success, data} (internal/httpapi/server.go writeSuccess); schemas below describe `data`. Errors are {success:false, error, code}.
paths:
/api/v1/onboarding/starter:
get:
summary: Starter offer decision (FR-012). Gated by requireAdminRead. Personal edition; server edition returns eligible=false reason=server_edition.
responses:
"200":
description: Offer
content:
application/json:
schema:
type: object
properties:
success: { type: boolean }
data: { $ref: "#/components/schemas/StarterOffer" }
post:
summary: Add the selected starter through the catalog door's add step, using ONE resolved entry snapshot that was validated against shown_command (FR-003, research R3). Gated by requireServerOp(ServerOpAddFromRegistry), exactly like the catalog door; agent tokens get 403 with no side effects. Never approves.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [kind, surface, shown_command]
properties:
kind: { type: string, enum: [time, sequentialthinking] }
surface: { type: string, enum: [wizard, home_web, home_native] }
shown_command: { type: string, description: "Exact command the user saw; must equal the add-time resolution." }
responses:
"200":
description: Added (quarantined unless quarantine is globally off)
content:
application/json:
schema:
type: object
properties:
success: { type: boolean }
data:
type: object
properties:
server: { type: object, properties: { name: {type: string}, quarantined: {type: boolean}, incarnation: {type: integer} } }
offer: { $ref: "#/components/schemas/StarterOffer" }
"409":
description: "code=starter_changed (resolved command differs from shown_command; nothing added; body carries fresh offer) or code=not_eligible"
"400":
description: "code=duplicate_name (passed through from the catalog door) or invalid kind/surface"
"403":
description: "agent token / insufficient server-op permission; nothing resolved, added or recorded"
/api/v1/onboarding/mark:
post:
summary: Existing endpoint; Spec 122 adds optional sticky booleans and a failure enum.
requestBody:
content:
application/json:
schema:
type: object
properties:
starter_offered: { type: boolean, enum: [true] }
starter_declined: { type: boolean, enum: [true] }
starter_failure: { type: string, enum: [connect_failed, capture_timeout] }
second_client_offered: { type: boolean, enum: [true] }
second_client_none_detected: { type: boolean, enum: [true] }
second_client_skipped: { type: boolean, enum: [true] }
/api/v1/onboarding/state:
get:
summary: Existing endpoint; response gains `day_two` (local facts, same shape as data-model 4c minus summary_views) and `detected_unconnected_clients` (registry ids, FR-013).
components:
schemas:
StarterOffer:
type: object
required: [eligible, reason, preselect, facts]
properties:
eligible: { type: boolean }
reason: { type: string, enum: [eligible, launcher_missing, name_taken, reference_overridden, tools_present, held_tools, already_configured, server_edition] }
preselect: { type: boolean }
launcher: { type: string, enum: [uvx, npx_only, none] }
candidate:
nullable: true
type: object
properties:
kind: { type: string, enum: [time, sequentialthinking] }
catalog_id: { type: string }
command: { type: string }
isolation: { type: string, enum: [host, docker] }
expected_tools: { type: array, items: { type: string } }
first_prompt: { type: string }
starter_server:
nullable: true
type: object
properties: { name: {type: string}, quarantined: {type: boolean} }
facts:
type: object
properties:
offered: { type: boolean }
added: { type: boolean }
live: { type: boolean }
declined: { type: boolean }
last_failure: { type: string, enum: ["", connect_failed, capture_timeout, approval_abandoned] }
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "mcpproxy/telemetry/schema-17/day_two",
"title": "Heartbeat schema 17 additions (Spec 122)",
"type": "object",
"properties": {
"activation_additions": {
"type": "object",
"additionalProperties": false,
"properties": {
"first_tool_indexed_ever": { "type": "boolean" },
"first_tool_indexed_after": { "enum": ["", "lt_10m", "10m_1h", "1h_24h", "1d_7d", "gt_7d"] }
}
},
"day_two": {
"type": "object",
"additionalProperties": false,
"properties": {
"starter_eligibility": { "enum": ["", "eligible", "launcher_missing", "name_taken", "reference_overridden", "already_configured", "tools_present", "held_tools", "not_fresh"] },
"starter_launcher": { "enum": ["", "uvx", "npx_only", "none"] },
"starter_offered": { "type": "boolean" },
"starter_added": { "type": "boolean" },
"starter_live": { "type": "boolean" },
"starter_declined": { "type": "boolean" },
"starter_last_failure": { "enum": ["", "connect_failed", "capture_timeout", "approval_abandoned"] },
"starter_kind": { "enum": ["", "time", "sequentialthinking"] },
"starter_surface": { "enum": ["", "wizard", "home_web", "home_native"] },
"second_client_offered": { "type": "boolean" },
"second_client_none_detected": { "type": "boolean" },
"second_client_skipped": { "type": "boolean" },
"second_client_connected": { "type": "boolean" },
"clients_day0": { "enum": ["", "0", "1", "2", "3_plus"] },
"clients_day0_final": { "type": "boolean" },
"summary_views": {
"type": "object",
"additionalProperties": false,
"properties": {
"web": { "enum": ["0", "1", "2_5", "6_plus"] },
"tray": { "enum": ["0", "1", "2_5", "6_plus"] },
"cli": { "enum": ["0", "1", "2_5", "6_plus"] }
}
}
}
}
}
}
Loading
Loading