gasp-standard-mcp 1.14.2 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/build/index.js +1 -1
- package/build/loader.d.ts +2 -2
- package/build/loader.js +8 -6
- package/build/standard-data.json +1 -1
- package/build/standard-version.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ Restart your MCP client after adding the config.
|
|
|
33
33
|
|
|
34
34
|
| Tool | Description |
|
|
35
35
|
|------|-------------|
|
|
36
|
-
| `lookup_metric` | Full record for one metric: department, category, signal type, definition, formulas as written, the canonical formula with unit, forms, variants and breakdowns, benchmarks, validation rules, common mistakes, sources, aliases, the entities it measures, operating and market forms, the recommended sensitivity tier and access scope and the page URL. Matches exactly, ignoring case: formula id, graph node id, name with or without its abbreviation, abbreviation, graph label, alias or slug, with the full record preferred over a cross-reference stub. A name two departments define (
|
|
36
|
+
| `lookup_metric` | Full record for one metric: department, category, signal type, definition, formulas as written, the canonical formula with unit, forms, variants and breakdowns, benchmarks, validation rules, common mistakes, sources, aliases, the entities it measures, operating and market forms, the recommended sensitivity tier and access scope and the page URL. Matches exactly, ignoring case: formula id, graph node id, name with or without its abbreviation, abbreviation, graph label, alias or slug, with the full record preferred over a cross-reference stub. A name two departments define (Activation Rate) lists each candidate with department, formula id and URL; prefix the department ("Product Activation Rate") or pass the formula id. A glossary term returns its definition and a miss returns close names (misspellings included) |
|
|
37
37
|
| `lookup_definition` | Glossary term lookup (e.g. Signal Type, Sensitivity Tier, Access Scope, Metric Owner, CEL): full definition, section, canonical or reference link, where it is used and page URL. Matches the term, its slug or either part of "Term (Abbreviation)" exactly; a metric name points to `lookup_metric` and a miss returns close terms |
|
|
38
38
|
| `list_metrics` | Metric names grouped by department and category, optionally filtered by department (case-insensitive, spaces or hyphens; an unknown name returns close names and the department list) and signal type (`outcome`, `leading`, `efficiency`, `operational`, primary or secondary). The Core tier is the Core department |
|
|
39
39
|
| `get_formula` | Canonical formula in the GASP formula grammar, with unit, O/M forms, named variants, breakdowns (field, bucket and total mode), notes, input metrics with role and polarity, the fields and datasets the formula and each form read and the enum filters each form applies (CAC-O reads `cost_function in (gtm, implementation, onboarding)`, CAC-M `cost_function = gtm`), then the formula as written in the standard. Metrics that cannot be computed carry the reason |
|
|
@@ -50,21 +50,21 @@ The traversal tools (`infer_drivers`, `explain_path`, `coverage_gap`) return pat
|
|
|
50
50
|
|
|
51
51
|
## Data
|
|
52
52
|
|
|
53
|
-
Version 1.
|
|
53
|
+
Version 1.16.0.
|
|
54
54
|
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
55
|
+
- 306 metrics across 13 departments
|
|
56
|
+
- 94 glossary definitions (81 SaaS domain + 13 GASP framework)
|
|
57
|
+
- 100 relationship nodes (65 metrics + 28 entities + 7 concepts), 200 edges
|
|
58
58
|
- 21 relationship types: metric signal (drives, reduces, predicts, component, measures), entity lifecycle (converts_to, parent_of, belongs_to), entity ownership (owns, grants, priced_by), entity interactions (manages, serves, engages_via, submits, represented_by, sources), entity financial flows (generates, produces, contributes_to, incurs)
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
- The test suite executes every compiled template against a seeded fixture in an in-process Postgres (PGlite) and checks
|
|
59
|
+
- 279 data requirements, one per metric with a formula, compiled from the formula at load time: fields with types, datasets, grain and a compiled example query
|
|
60
|
+
- 279 query templates, one per metric with a formula, compiled by the same step. None of the SQL is hand-written. Every field placeholder names the dataset it is read from (`{{customer_status@customer}}`), so `generate_query` resolves per-dataset adapter mappings by tag, inside cohort subqueries too. The grammar's `window(expr, n unit)` gives Daily, Weekly and Monthly Active Users (DAU, WAU, MAU) their 1, 7 and 30 day windows. `all_time(expr)` lets retention numerators read past the period end. A multi-argument `count_distinct` counts distinct tuples (Breadth of Adoption counts account and feature pairs). A formula with a `breakdown` field compiles to one row per value of that field, with `total(expr)` reading the whole population for shares of total (Ticket Distribution by Type, CAC by Channel)
|
|
61
|
+
- The test suite executes every compiled template against a seeded fixture in an in-process Postgres (PGlite) and checks 25 metrics against independently computed values
|
|
62
62
|
- Apache Ossie (incubating) export, written by `npm run derive` to `standard/exports/ossie/` and served at https://gaspwiki.com/downloads/ossie/: `gasp.ontology.yaml` (every dataset and GASP concept as an entity type, every field as a value type or relationship, enum values as constraints), `gasp.semantic_model.yaml` (every dataset with `OSSIE_SQL_2026` field expressions, the foreign-key relationships between them and every canonical formula as a metric, either a direct expression or staged through a per-period dataset that carries the compiled SQL; O/M forms and variants are separate metrics with the form in `custom_extensions`), `gasp.mapping.yaml` (the ontology mapped onto the model) and `coverage.json` (how each formula was exported and why). All three documents pass the upstream validator (vendored at `ossie-validator/`) and GASP's own cross-reference checks in `src/ossie/check.ts`, which cover references the upstream validator leaves alone
|
|
63
63
|
- 28 business entities (Customer, Subscription, License, Lead, Opportunity, User, Ticket, Employee, Invoice, Partner, Project, Plan, Product, Campaign, Contact, Incident, Deployment, Release, Issue, Vulnerability, Feature, Requisition, Project Milestone, Deliverable, Quote, Order, Knowledge Article and Deal Registration). Every dataset with its own identity maps to an entity; ledger and period-level facts (cost events, financials, forecasts, web traffic) stay entity-free
|
|
64
64
|
- 7 business concepts (Revenue, Bookings, COGS, Cohort, Pipeline, Segment and TCV)
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
- Canonical formulas for every metric in the GASP formula grammar (`standard/formulas.json`):
|
|
65
|
+
- 268 canonical fields across 52 datasets (event, snapshot and fact tables with date and key fields, entities, CEL class and activation rules). Enum fields declare their allowed values
|
|
66
|
+
- 65 structured formulas with decomposed inputs (metric refs + field refs, with baseline inputs marked)
|
|
67
|
+
- Canonical formulas for every metric in the GASP formula grammar (`standard/formulas.json`): 294 entries covering all 306 metrics (279 with formulas, 15 marked not computable with a reason, 2 department-qualified ids for shared headings), with units, O/M forms for ARR, NRR, CAC, LTV, LTV:CAC, CAC Payback and Gross Margin (a form can reference another metric's form, `metric.gross_margin.O`) and variants such as Adjusted NRR (served by `get_formula` and compiled by `generate_query` and `get_data_requirements`)
|
|
68
68
|
- Access control classifications for every graph metric (sensitivity tier + access scope)
|
|
69
69
|
- CEL `change_nature` field for revenue quality analysis (Adjusted NRR/GRR) and `segment` on revenue, contract and amendment events, so MRR, retention and churn metrics break down by customer segment
|
|
70
70
|
- Per-department cross-references (NRR under Customer Success, CAC under Marketing) resolve to the full canonical record, with its access classification, structured formula and entity mappings
|
package/build/index.js
CHANGED
|
@@ -129,7 +129,7 @@ function notAMetric(name, resolved) {
|
|
|
129
129
|
return `No metric is named "${name}".${suggestMatches(data, name)}`;
|
|
130
130
|
}
|
|
131
131
|
// --- Tool 1: lookup_metric ---
|
|
132
|
-
server.tool("lookup_metric", "The full record of one GASP Standard metric: department, category, signal type, page URL, definition, the formulas as written, the canonical GASP grammar formula (unit, forms, variants, breakdowns, fields and datasets), benchmarks, validation rules, common mistakes, sources, aliases, the entities it measures, operating and market forms, any adjusted variant and the recommended sensitivity tier and access scope. The name must match exactly, ignoring case: formula id, graph node id, full name, name without its abbreviation, abbreviation, graph label, alias or slug. A name several departments use (e.g. '
|
|
132
|
+
server.tool("lookup_metric", "The full record of one GASP Standard metric: department, category, signal type, page URL, definition, the formulas as written, the canonical GASP grammar formula (unit, forms, variants, breakdowns, fields and datasets), benchmarks, validation rules, common mistakes, sources, aliases, the entities it measures, operating and market forms, any adjusted variant and the recommended sensitivity tier and access scope. The name must match exactly, ignoring case: formula id, graph node id, full name, name without its abbreviation, abbreviation, graph label, alias or slug. A name several departments use (e.g. 'Activation Rate') returns each candidate with department, formula id and URL; prefix the department ('Product Activation Rate') or pass the formula id. A glossary term (e.g. 'Signal Type') returns its definition and a miss returns close names to retry with. Use get_formula for the formula alone and search when you do not know the name.", { name: z.string().describe("Exact metric name, abbreviation, alias, slug or formula id, case-insensitive (e.g. 'MRR', 'Net Revenue Retention', 'CAC Payback', 'product_activation_rate'). Prefix the department to pick between metrics that share a name.") }, async ({ name }) => {
|
|
133
133
|
const resolved = resolveMetric(data, name);
|
|
134
134
|
if (resolved.kind === "ambiguous") {
|
|
135
135
|
return { content: [{ type: "text", text: formatAmbiguous(data, name, resolved.candidates) }] };
|
package/build/loader.d.ts
CHANGED
|
@@ -73,13 +73,13 @@ export type Resolved = {
|
|
|
73
73
|
};
|
|
74
74
|
/**
|
|
75
75
|
* Exact matches only, never a substring guess. Five tiers, first hit wins:
|
|
76
|
-
* 1. the formula id or graph node id as typed ("
|
|
76
|
+
* 1. the formula id or graph node id as typed ("product_activation_rate",
|
|
77
77
|
* "nps"): unique keys, so never ambiguous;
|
|
78
78
|
* 2. a name: the full name, the name without its abbreviation, the
|
|
79
79
|
* abbreviation, or the label of the metric's graph node;
|
|
80
80
|
* 3. an alias;
|
|
81
81
|
* 4. the slug;
|
|
82
|
-
* 5. the formula id with spaces for underscores ("
|
|
82
|
+
* 5. the formula id with spaces for underscores ("Product Activation Rate").
|
|
83
83
|
* Non-stub metrics are tried through every tier first, then as a name
|
|
84
84
|
* qualified by department; a cross-reference stub answers only when no full
|
|
85
85
|
* record matches. Two full records matching in the same tier are ambiguous.
|
package/build/loader.js
CHANGED
|
@@ -343,9 +343,11 @@ export function metricFormulaId(data, metric) {
|
|
|
343
343
|
let bare = metric.slug.replace(/-/g, "_").replace(/^(\d+)_day_/, "day_$1_");
|
|
344
344
|
if (/^\d/.test(bare))
|
|
345
345
|
bare = `n_${bare}`;
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
// the
|
|
346
|
+
// A metric is qualified by department when its bare id would be shared: two
|
|
347
|
+
// departments defining metrics under one heading (no heading is shared today,
|
|
348
|
+
// the rule stays as a guard), or a bare id that is another metric's graph node
|
|
349
|
+
// (Product's "Activation Rate" against the Onboarding node activation_rate,
|
|
350
|
+
// hence product_activation_rate). The node owner keeps the bare id.
|
|
349
351
|
const rivals = data.metrics.filter((m) => m !== metric && !isMetricStub(m) && m.slug === metric.slug);
|
|
350
352
|
const nodeOwner = data.nodes.some((n) => n.id === bare) || rivals.some((m) => metricNodeId(data, m) === bare);
|
|
351
353
|
if (rivals.length === 0 && !nodeOwner)
|
|
@@ -503,13 +505,13 @@ function namedBy(data, q) {
|
|
|
503
505
|
}
|
|
504
506
|
/**
|
|
505
507
|
* Exact matches only, never a substring guess. Five tiers, first hit wins:
|
|
506
|
-
* 1. the formula id or graph node id as typed ("
|
|
508
|
+
* 1. the formula id or graph node id as typed ("product_activation_rate",
|
|
507
509
|
* "nps"): unique keys, so never ambiguous;
|
|
508
510
|
* 2. a name: the full name, the name without its abbreviation, the
|
|
509
511
|
* abbreviation, or the label of the metric's graph node;
|
|
510
512
|
* 3. an alias;
|
|
511
513
|
* 4. the slug;
|
|
512
|
-
* 5. the formula id with spaces for underscores ("
|
|
514
|
+
* 5. the formula id with spaces for underscores ("Product Activation Rate").
|
|
513
515
|
* Non-stub metrics are tried through every tier first, then as a name
|
|
514
516
|
* qualified by department; a cross-reference stub answers only when no full
|
|
515
517
|
* record matches. Two full records matching in the same tier are ambiguous.
|
|
@@ -536,7 +538,7 @@ export function resolveMetric(data, name) {
|
|
|
536
538
|
if (hits.length > 1)
|
|
537
539
|
return { kind: "ambiguous", candidates: hits };
|
|
538
540
|
}
|
|
539
|
-
// A name qualified by its department: "
|
|
541
|
+
// A name qualified by its department: "Product Activation Rate", "Activation Rate (Product)"
|
|
540
542
|
for (const dept of new Set(full.map((m) => m.department))) {
|
|
541
543
|
const d = dept.toLowerCase();
|
|
542
544
|
const rest = q.startsWith(d + " ") ? q.slice(d.length + 1) : q.endsWith(` (${d})`) ? q.slice(0, -(d.length + 3)) : null;
|