qyl-mcp-server 2.1.0 → 4.0.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.
Files changed (47) hide show
  1. package/README.md +44 -21
  2. package/dist/collector.d.ts +15 -2
  3. package/dist/collector.js +67 -3
  4. package/dist/collector.js.map +1 -1
  5. package/dist/config.d.ts +0 -2
  6. package/dist/config.js +0 -2
  7. package/dist/config.js.map +1 -1
  8. package/dist/contract-operations.d.ts +37 -0
  9. package/dist/contract-operations.js +124 -0
  10. package/dist/contract-operations.js.map +1 -0
  11. package/dist/contract-validation.d.ts +7 -20
  12. package/dist/contract-validation.js +11 -22
  13. package/dist/contract-validation.js.map +1 -1
  14. package/dist/demo.d.ts +18 -0
  15. package/dist/demo.js +99 -0
  16. package/dist/demo.js.map +1 -1
  17. package/dist/mcp-app.html +5 -5
  18. package/dist/mcp-dashboard.html +4 -4
  19. package/dist/mcp-home.html +6 -7
  20. package/dist/metrics-tools.d.ts +23 -0
  21. package/dist/metrics-tools.js +96 -0
  22. package/dist/metrics-tools.js.map +1 -0
  23. package/dist/metrics.d.ts +45 -0
  24. package/dist/metrics.js +274 -0
  25. package/dist/metrics.js.map +1 -0
  26. package/dist/oauth.d.ts +0 -1
  27. package/dist/oauth.js +1 -2
  28. package/dist/oauth.js.map +1 -1
  29. package/dist/server.js +3 -4
  30. package/dist/server.js.map +1 -1
  31. package/dist/summaries.d.ts +13 -0
  32. package/dist/summaries.js +67 -0
  33. package/dist/summaries.js.map +1 -1
  34. package/package.json +4 -4
  35. package/dist/observe-graph.html +0 -214
  36. package/dist/workflow-data.d.ts +0 -13
  37. package/dist/workflow-data.js +0 -189
  38. package/dist/workflow-data.js.map +0 -1
  39. package/dist/workflow-layout.d.ts +0 -24
  40. package/dist/workflow-layout.js +0 -165
  41. package/dist/workflow-layout.js.map +0 -1
  42. package/dist/workflow-reducer.d.ts +0 -37
  43. package/dist/workflow-reducer.js +0 -114
  44. package/dist/workflow-reducer.js.map +0 -1
  45. package/dist/workflow-tools.d.ts +0 -10
  46. package/dist/workflow-tools.js +0 -170
  47. package/dist/workflow-tools.js.map +0 -1
@@ -6,14 +6,14 @@
6
6
  <meta name="color-scheme" content="dark">
7
7
  <meta name="description" content="Bring qyl traces, logs, and MCP telemetry into your coding agent.">
8
8
  <title>qyl · MCP telemetry for coding agents</title>
9
- <script type="module" crossorigin>(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e={name:`qyl-mcp-server`,version:`2.1.0`,type:`module`,description:`qyl telemetry MCP Apps server: trace explorer and MCP dashboard for a live collector or explicit demo mode`,repository:{type:`git`,url:`https://github.com/ANcpLua/qyl.mcp`,directory:`server`},license:`MIT`,main:`dist/server.js`,types:`dist/server.d.ts`,exports:{".":{types:`./dist/server.d.ts`,default:`./dist/server.js`},"./contract-validation":{types:`./dist/contract-validation.d.ts`,default:`./dist/contract-validation.js`},"./secret-redactor":{types:`./dist/secret-redactor.d.ts`,default:`./dist/secret-redactor.js`},"./atomic-json-store":{types:`./dist/atomic-json-store.d.ts`,default:`./dist/atomic-json-store.js`},"./execution-evidence":{types:`./dist/execution-evidence.d.ts`,default:`./dist/execution-evidence.js`},"./execution-result":{types:`./dist/execution-result.d.ts`,default:`./dist/execution-result.js`},"./mcp-semconv":{types:`./dist/mcp-semconv.d.ts`,default:`./dist/mcp-semconv.js`},"./observability-suppression":{types:`./dist/observability-suppression.d.ts`,default:`./dist/observability-suppression.js`},"./telemetry":{types:`./dist/telemetry.d.ts`,default:`./dist/telemetry.js`}},bin:{"qyl-mcp-server":`dist/main.js`},files:[`dist`],scripts:{clean:`node -e "require('node:fs').rmSync('dist', { recursive: true, force: true }); require('node:fs').rmSync('dist-test', { recursive: true, force: true })"`,build:`bun run clean && tsc --noEmit && vite build --mode mcp-home && vite build --mode mcp-app && vite build --mode mcp-dashboard && vite build --mode observe-graph && tsc -p tsconfig.server.json && node -e "require('node:fs').chmodSync('dist/main.js', 0o755)"`,postbuild:`bun run verify:deployment-guidance`,"verify:deployment-guidance":`node verify-deployment-guidance.mjs`,"verify:shapes":`node verify-generated-shapes.mjs`,"verify:readme":`node verify-package-readme.mjs`,"snapshot:tools":`bun run build && tsc -p tsconfig.test.json && node update-tool-manifest-snapshot.mjs`,test:`bun run verify:shapes && bun run verify:readme && bun run build && tsc -p tsconfig.test.json && node -e "const fs=require('node:fs'); for (const name of ['mcp-app.html','mcp-dashboard.html','observe-graph.html']) fs.copyFileSync('dist/'+name,'dist-test/'+name)" && node --test dist-test/*.test.js`,smoke:`node smoke-test.mjs`,prepack:`bun run build`},dependencies:{"@ancplua/qyl-api-schema":`7.4.0`,"@modelcontextprotocol/core":`2.0.0`,"@modelcontextprotocol/server":`2.0.0`,"@opentelemetry/api":`^1.9.1`,"@opentelemetry/api-logs":`^0.221.0`,"@opentelemetry/core":`^2.10.0`,"@opentelemetry/exporter-logs-otlp-proto":`^0.221.0`,"@opentelemetry/exporter-metrics-otlp-proto":`^0.221.0`,"@opentelemetry/exporter-trace-otlp-proto":`^0.221.0`,"@opentelemetry/resources":`^2.10.0`,"@opentelemetry/sdk-logs":`^0.221.0`,"@opentelemetry/sdk-metrics":`^2.10.0`,"@opentelemetry/sdk-trace-base":`^2.10.0`,"@opentelemetry/sdk-trace-node":`^2.10.0`,"@opentelemetry/semantic-conventions":`^1.43.0`,jose:`^6.2.3`,zod:`^4.5.0`},devDependencies:{"@modelcontextprotocol/client":`2.0.0`,"@types/node":`26.4.0`,typescript:`7.0.2`,vite:`8.2.2`,"vite-plugin-singlefile":`2.3.3`},engines:{node:`24.x`}},t=`https://mcp.qyl.at/mcp`,n={codex:{title:`terminal`,value:`codex mcp add qyl --url ${t}`},claude:{title:`terminal`,value:`claude mcp add --transport http qyl ${t}`},cursor:{title:`mcp.json`,value:`{
9
+ <script type="module" crossorigin>(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e={name:`qyl-mcp-server`,version:`4.0.0`,type:`module`,description:`qyl telemetry MCP Apps server: trace explorer and MCP dashboard for a live collector or explicit demo mode`,repository:{type:`git`,url:`https://github.com/ANcpLua/qyl.mcp`,directory:`server`},license:`MIT`,main:`dist/server.js`,types:`dist/server.d.ts`,exports:{".":{types:`./dist/server.d.ts`,default:`./dist/server.js`},"./contract-validation":{types:`./dist/contract-validation.d.ts`,default:`./dist/contract-validation.js`},"./secret-redactor":{types:`./dist/secret-redactor.d.ts`,default:`./dist/secret-redactor.js`},"./atomic-json-store":{types:`./dist/atomic-json-store.d.ts`,default:`./dist/atomic-json-store.js`},"./execution-evidence":{types:`./dist/execution-evidence.d.ts`,default:`./dist/execution-evidence.js`},"./execution-result":{types:`./dist/execution-result.d.ts`,default:`./dist/execution-result.js`},"./mcp-semconv":{types:`./dist/mcp-semconv.d.ts`,default:`./dist/mcp-semconv.js`},"./observability-suppression":{types:`./dist/observability-suppression.d.ts`,default:`./dist/observability-suppression.js`},"./telemetry":{types:`./dist/telemetry.d.ts`,default:`./dist/telemetry.js`}},bin:{"qyl-mcp-server":`dist/main.js`},files:[`dist`],scripts:{clean:`node -e "require('node:fs').rmSync('dist', { recursive: true, force: true }); require('node:fs').rmSync('dist-test', { recursive: true, force: true })"`,build:`bun run clean && tsc --noEmit && vite build --mode mcp-home && vite build --mode mcp-app && vite build --mode mcp-dashboard && tsc -p tsconfig.server.json && node -e "require('node:fs').chmodSync('dist/main.js', 0o755)"`,postbuild:`bun run verify:deployment-guidance`,"verify:deployment-guidance":`node verify-deployment-guidance.mjs`,"verify:shapes":`node verify-generated-shapes.mjs`,"verify:readme":`node verify-package-readme.mjs`,"snapshot:tools":`bun run build && tsc -p tsconfig.test.json && node update-tool-manifest-snapshot.mjs`,test:`bun run verify:shapes && bun run verify:readme && bun run build && tsc -p tsconfig.test.json && node -e "const fs=require('node:fs'); for (const name of ['mcp-app.html','mcp-dashboard.html']) fs.copyFileSync('dist/'+name,'dist-test/'+name)" && node --test dist-test/*.test.js`,smoke:`node smoke-test.mjs`,prepack:`bun run build`},dependencies:{"@ancplua/qyl-api-schema":`9.0.0`,"@modelcontextprotocol/core":`2.0.0`,"@modelcontextprotocol/server":`2.0.0`,"@opentelemetry/api":`^1.9.1`,"@opentelemetry/api-logs":`^0.221.0`,"@opentelemetry/core":`^2.10.0`,"@opentelemetry/exporter-logs-otlp-proto":`^0.221.0`,"@opentelemetry/exporter-metrics-otlp-proto":`^0.221.0`,"@opentelemetry/exporter-trace-otlp-proto":`^0.221.0`,"@opentelemetry/resources":`^2.10.0`,"@opentelemetry/sdk-logs":`^0.221.0`,"@opentelemetry/sdk-metrics":`^2.10.0`,"@opentelemetry/sdk-trace-base":`^2.10.0`,"@opentelemetry/sdk-trace-node":`^2.10.0`,"@opentelemetry/semantic-conventions":`^1.43.0`,jose:`^6.2.3`,zod:`^4.5.0`},devDependencies:{"@modelcontextprotocol/client":`2.0.0`,"@types/node":`26.4.0`,typescript:`7.0.2`,vite:`8.2.2`,"vite-plugin-singlefile":`2.3.3`},engines:{node:`24.x`}},t=`https://mcp.qyl.at/mcp`,n={codex:{title:`terminal`,value:`codex mcp add qyl --url ${t}`},claude:{title:`terminal`,value:`claude mcp add --transport http qyl ${t}`},cursor:{title:`mcp.json`,value:`{
10
10
  "mcpServers": {
11
11
  "qyl": {
12
12
  "url": "${t}"
13
13
  }
14
14
  }
15
15
  }`}},r=document.getElementById(`install-command`),i=document.getElementById(`terminal-title`),a=document.getElementById(`copy-status`),o=document.getElementById(`copy-install`),s=`codex`,c;function l(){let e=n[s];i.textContent=e.title,r.textContent=e.value}function u(e){a.textContent=e,a.classList.add(`visible`),c!==void 0&&clearTimeout(c),c=setTimeout(()=>a.classList.remove(`visible`),1800)}async function d(e,t){try{await navigator.clipboard.writeText(e),u(`${t} copied`)}catch{u(`Select the text and copy it manually`)}}for(let e of document.querySelectorAll(`[data-client]`))e.addEventListener(`click`,()=>{s=e.dataset.client;for(let t of document.querySelectorAll(`[data-client]`))t.setAttribute(`aria-selected`,String(t===e));l()});document.getElementById(`copy-endpoint`).addEventListener(`click`,()=>{d(t,`Endpoint`)}),o.addEventListener(`click`,()=>{d(n[s].value,`Configuration`)}),document.getElementById(`release-version`).textContent=`v${e.version}`,l();</script>
16
- <style rel="stylesheet" crossorigin>:root{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--ink:#ecf2f8;--muted:#8c9aae;--faint:#596679;--base:#080b10;--panel:#0d1219;--panel-raised:#111923;--line:#263141;--line-strong:#3a495e;--cyan:#4dd8d0;--cyan-strong:#78eee7;--cyan-wash:#4dd8d01a;--green:#63dda1;--amber:#f1bd68;--red:#ff7a85;--mono:"SFMono-Regular", Consolas, "Liberation Mono", monospace;--sans:Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-family:var(--sans);font-synthesis:none;text-rendering:optimizelegibility}*{box-sizing:border-box}html{scroll-behavior:smooth}body{background:var(--base);min-width:320px;color:var(--ink);margin:0}body:before{z-index:-1;content:"";pointer-events:none;background:linear-gradient(#ffffff05 1px,#0000 1px) 0 0/48px 48px,linear-gradient(90deg,#ffffff05 1px,#0000 1px) 0 0/48px 48px,radial-gradient(circle at 72% 2%,#4dd8d01a,#0000 31rem);position:fixed;inset:0}a{color:inherit;text-decoration:none}button{font:inherit}.site-header,main,footer{width:min(1180px,100% - 48px);margin-inline:auto}.site-header{border-bottom:1px solid var(--line);grid-template-columns:1fr auto 1fr;align-items:center;height:72px;display:grid}.brand{font:750 1.08rem/1 var(--mono);letter-spacing:-.07em}.brand span{color:var(--cyan)}.site-header nav{color:var(--muted);gap:2rem;font-size:.76rem;display:flex}.site-header nav a{transition:color .12s}.site-header nav a:hover{color:var(--ink)}.release-badge{background:var(--cyan-wash);color:var(--cyan-strong);font:700 .62rem/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;border:1px solid #4dd8d059;border-radius:999px;justify-self:end;padding:.26rem .58rem}.hero{grid-template-columns:minmax(0,.92fr) minmax(460px,1.08fr);align-items:center;gap:clamp(3rem,7vw,7rem);min-height:680px;padding:7.25rem 0 7.75rem;display:grid}.eyebrow,.section-index{color:var(--cyan);font:700 .66rem/1.4 var(--mono);letter-spacing:.12em;text-transform:uppercase}.eyebrow{align-items:center;gap:.65rem;margin:0 0 1.55rem;display:flex}.eyebrow span{background:var(--green);border-radius:50%;width:7px;height:7px;box-shadow:0 0 0 5px #63dda11a}h1,h2,h3,p{margin-top:0}h1{letter-spacing:-.075em;margin-bottom:1.6rem;font-size:clamp(3.2rem,6vw,6.15rem);line-height:.91}h1 em{color:var(--cyan);font-style:normal}.hero-lede{max-width:590px;color:var(--muted);margin-bottom:2rem;font-size:1.04rem;line-height:1.7}.endpoint{border:1px solid var(--line-strong);background:#0d1219e0;border-radius:6px;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;width:min(100%,560px);height:54px;display:grid;overflow:hidden}.endpoint-protocol{border-right:1px solid var(--line);height:100%;color:var(--faint);font:.68rem var(--mono);place-items:center;padding:0 .9rem;display:grid}.endpoint code{color:#b9d8ff;min-width:0;font:.76rem var(--mono);text-overflow:ellipsis;white-space:nowrap;padding:0 .85rem;overflow:hidden}.endpoint button,.terminal-bar button{color:var(--cyan);cursor:pointer;font:700 .66rem var(--mono);letter-spacing:.04em;background:0 0;border:0}.endpoint button{border-left:1px solid var(--line);height:100%;padding:0 1rem}.endpoint button:hover,.terminal-bar button:hover{color:var(--cyan-strong);background:var(--cyan-wash)}.protocol-facts{color:var(--muted);font:.64rem var(--mono);flex-wrap:wrap;gap:1.2rem;margin-top:1.15rem;display:flex}.protocol-facts span{align-items:center;gap:.42rem;display:inline-flex}.fact-dot{background:var(--faint);border-radius:50%;width:5px;height:5px}.fact-live{background:var(--green)}.trace-window{border:1px solid var(--line-strong);background:#090e14f0;border-radius:9px;overflow:hidden;transform:rotate(.45deg);box-shadow:0 38px 90px #00000059}.trace-titlebar{border-bottom:1px solid var(--line);background:var(--panel);height:48px;font:.65rem var(--mono);justify-content:space-between;align-items:center;padding:0 .9rem;display:flex}.trace-titlebar>div,.traffic-lights{align-items:center;display:flex}.traffic-lights{gap:5px;margin-right:.75rem}.traffic-lights i,.terminal-bar i{background:var(--line-strong);border-radius:50%;width:7px;height:7px}.traffic-lights i:first-child,.terminal-bar i:first-child{background:var(--red)}.traffic-lights i:nth-child(2),.terminal-bar i:nth-child(2){background:var(--amber)}.traffic-lights i:last-child,.terminal-bar i:last-child{background:var(--green)}.trace-name{color:var(--muted)}.trace-duration{color:var(--cyan)}.trace-ruler{border-bottom:1px solid var(--line);height:32px;color:var(--faint);font:.54rem var(--mono);grid-template-columns:repeat(5,1fr);align-items:end;padding:0 12px 7px 174px;display:grid}.trace-ruler span:not(:first-child){text-align:right}.trace-rows{padding:.55rem 0;position:relative}.trace-rows:after{content:"";pointer-events:none;background:repeating-linear-gradient(90deg,#0000 0 calc(25% - 1px),#ffffff0b 25%);position:absolute;inset:0 12px 0 174px}.trace-row{z-index:1;border-bottom:1px solid #2631418c;grid-template-columns:174px minmax(0,1fr);align-items:center;min-height:54px;display:grid;position:relative}.trace-row:last-child{border-bottom:0}.span-label{min-width:0;font-family:var(--mono);gap:.22rem;padding:0 .8rem;display:grid}.span-label b,.span-label small{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.span-label b{font-size:.61rem;font-weight:600}.span-label small{color:var(--faint);font-size:.54rem}.span-track{height:13px;margin-right:12px;position:relative}.span-track i{top:0;bottom:0;left:var(--start);width:var(--size);background:linear-gradient(90deg,#1f6e6b,#27827e);border:1px solid #329f9a;border-radius:2px;position:absolute;box-shadow:inset 0 0 0 1px #ffffff0a}.root-span .span-track i{background:linear-gradient(90deg,#31598e,#4777b8);border-color:#527bbb}.trace-footer{border-top:1px solid var(--line);background:var(--panel);height:47px;color:var(--faint);font:.57rem var(--mono);align-items:center;gap:1.1rem;padding:0 .9rem;display:flex}.trace-footer b{color:var(--ink)}.trace-footer .trace-ok b{color:var(--green)}.trace-id{margin-left:auto}.tool-surface,.protocol-section,.install-section{border-top:1px solid var(--line);padding:7rem 0}.tool-surface{grid-template-columns:.72fr 1.28fr;gap:clamp(3rem,8vw,8rem);display:grid}.section-index{margin-bottom:1.35rem}h2{letter-spacing:-.055em;margin-bottom:1.15rem;font-size:clamp(2rem,4vw,3.7rem);line-height:1.02}.section-intro>p:last-child,.protocol-heading p,.install-copy>p:last-of-type{color:var(--muted);line-height:1.65}.tool-list{counter-reset:tools;margin:0;padding:0;list-style:none}.tool-list li{border-bottom:1px solid var(--line);counter-increment:tools;grid-template-columns:28px 1.15fr 1.6fr auto;align-items:center;gap:1.2rem;min-height:68px;display:grid}.tool-list li:first-child{border-top:1px solid var(--line)}.tool-list li:before{content:"0" counter(tools);color:var(--faint);font:.55rem var(--mono)}.tool-list code{color:var(--ink);font:.7rem var(--mono)}.tool-list span{color:var(--muted);font-size:.75rem}.tool-list b{color:var(--cyan);font:600 .53rem var(--mono);letter-spacing:.05em;text-transform:uppercase}.protocol-heading{grid-template-columns:1fr .65fr;align-items:end;gap:4rem;display:grid}.protocol-heading p{max-width:430px;margin-bottom:1.2rem}.protocol-grid{border:1px solid var(--line);grid-template-columns:repeat(4,1fr);margin-top:3.2rem;display:grid}.protocol-grid article{border-right:1px solid var(--line);background:#0d12197a;min-height:230px;padding:1.35rem}.protocol-grid article:last-child{border-right:0}.protocol-grid article>span{color:var(--cyan);font:.6rem var(--mono)}.protocol-grid h3{letter-spacing:-.02em;margin:4rem 0 .7rem;font-size:.95rem}.protocol-grid p{color:var(--muted);margin-bottom:0;font-size:.73rem;line-height:1.6}.install-section{grid-template-columns:.78fr 1.22fr;align-items:center;gap:clamp(3rem,8vw,8rem);padding-bottom:8rem;display:grid}.client-tabs{flex-wrap:wrap;gap:.4rem;margin-top:2rem;display:flex}.client-tabs button{border:1px solid var(--line);color:var(--muted);cursor:pointer;background:0 0;border-radius:5px;padding:.52rem .72rem;font-size:.7rem}.client-tabs button:hover{border-color:var(--line-strong);color:var(--ink)}.client-tabs button[aria-selected=true]{background:var(--cyan-wash);color:var(--cyan-strong);border-color:#329f9a}.install-terminal{border:1px solid var(--line-strong);background:#090e14;border-radius:8px;overflow:hidden;box-shadow:0 28px 70px #00000045}.terminal-bar{border-bottom:1px solid var(--line);background:var(--panel);grid-template-columns:1fr auto 1fr;align-items:center;height:43px;padding:0 .8rem;display:grid}.terminal-bar>span{gap:5px;display:flex}.terminal-bar b{color:var(--faint);font:500 .59rem var(--mono)}.terminal-bar button{justify-self:end;height:100%;padding:0 .7rem}.install-terminal pre{color:#b9d8ff;min-height:150px;font:.74rem/1.75 var(--mono);white-space:pre-wrap;align-items:center;margin:0;padding:1.5rem;display:flex;overflow:auto}.install-terminal>p{border-top:1px solid var(--line);color:var(--faint);font:.58rem var(--mono);margin:0;padding:.75rem 1rem}.install-terminal>p span{color:var(--cyan);margin-right:.45rem}footer{border-top:1px solid var(--line);min-height:120px;color:var(--faint);grid-template-columns:1fr auto 1fr;align-items:center;font-size:.66rem;display:grid}footer p{margin:0}footer>div{font-family:var(--mono);justify-self:end;gap:1.2rem;display:flex}footer a:hover{color:var(--ink)}.copy-status{z-index:10;background:var(--panel-raised);color:var(--cyan-strong);opacity:0;pointer-events:none;font:.65rem var(--mono);border:1px solid #4dd8d066;border-radius:5px;padding:.65rem .85rem;transition:opacity .12s,transform .12s;position:fixed;bottom:1rem;right:1rem;transform:translateY(8px)}.copy-status.visible{opacity:1;transform:translateY(0)}@media (width<=900px){.site-header,main,footer{width:min(100% - 32px,720px)}.hero{grid-template-columns:1fr;gap:4rem;min-height:auto;padding:5.5rem 0 6rem}.trace-window{transform:none}.tool-surface,.install-section{grid-template-columns:1fr;gap:3rem}.protocol-grid{grid-template-columns:repeat(2,1fr)}.protocol-grid article:nth-child(2){border-right:0}.protocol-grid article:nth-child(-n+2){border-bottom:1px solid var(--line)}footer{grid-template-columns:1fr auto}footer p{display:none}}@media (width<=620px){.site-header{grid-template-columns:1fr auto}.site-header nav{display:none}h1{font-size:clamp(3rem,15vw,4.7rem)}.hero{padding-top:4.5rem}.trace-ruler{padding-left:124px}.trace-rows:after{left:124px}.trace-row{grid-template-columns:124px minmax(0,1fr)}.trace-footer{gap:.65rem}.trace-id{display:none}.tool-list li{grid-template-columns:1fr auto;padding:.85rem 0}.tool-list li:before{display:none}.tool-list span{grid-area:2/1/auto/-1}.protocol-heading{grid-template-columns:1fr;gap:0}.protocol-grid{grid-template-columns:1fr}.protocol-grid article{border-right:0;border-bottom:1px solid var(--line);min-height:185px}.protocol-grid article:last-child{border-bottom:0}.protocol-grid h3{margin-top:2.7rem}.endpoint-protocol{display:none}.endpoint{grid-template-columns:minmax(0,1fr) auto}}@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}*{transition:none!important}}
16
+ <style rel="stylesheet" crossorigin>:root{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--ink:#ecf2f8;--muted:#8c9aae;--faint:#596679;--base:#080b10;--panel:#0d1219;--panel-raised:#111923;--line:#263141;--line-strong:#3a495e;--cyan:#4dd8d0;--cyan-strong:#78eee7;--cyan-wash:#4dd8d01a;--green:#63dda1;--amber:#f1bd68;--red:#ff7a85;--mono:"SFMono-Regular", Consolas, "Liberation Mono", monospace;--sans:Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-family:var(--sans);font-synthesis:none;text-rendering:optimizelegibility}*{box-sizing:border-box}html{scroll-behavior:smooth}body{background:var(--base);min-width:320px;color:var(--ink);margin:0}body:before{z-index:-1;content:"";pointer-events:none;background:linear-gradient(#ffffff05 1px,#0000 1px) 0 0/48px 48px,linear-gradient(90deg,#ffffff05 1px,#0000 1px) 0 0/48px 48px,radial-gradient(circle at 72% 2%,#4dd8d01a,#0000 31rem);position:fixed;inset:0}a{color:inherit;text-decoration:none}button{font:inherit}.site-header,main,footer{width:min(1180px,100% - 48px);margin-inline:auto}.site-header{border-bottom:1px solid var(--line);grid-template-columns:1fr auto 1fr;align-items:center;height:72px;display:grid}.brand{font:750 1.08rem/1 var(--mono);letter-spacing:-.07em}.brand span{color:var(--cyan)}.site-header nav{color:var(--muted);gap:2rem;font-size:.76rem;display:flex}.site-header nav a{transition:color .12s}.site-header nav a:hover{color:var(--ink)}.hero{grid-template-columns:minmax(0,.92fr) minmax(460px,1.08fr);align-items:center;gap:clamp(3rem,7vw,7rem);min-height:680px;padding:7.25rem 0 7.75rem;display:grid}.eyebrow,.section-index{color:var(--cyan);font:700 .66rem/1.4 var(--mono);letter-spacing:.12em;text-transform:uppercase}.eyebrow{align-items:center;gap:.65rem;margin:0 0 1.55rem;display:flex}.eyebrow span{background:var(--green);border-radius:50%;width:7px;height:7px;box-shadow:0 0 0 5px #63dda11a}h1,h2,h3,p{margin-top:0}h1{letter-spacing:-.075em;margin-bottom:1.6rem;font-size:clamp(3.2rem,6vw,6.15rem);line-height:.91}h1 em{color:var(--cyan);font-style:normal}.hero-lede{max-width:590px;color:var(--muted);margin-bottom:2rem;font-size:1.04rem;line-height:1.7}.endpoint{border:1px solid var(--line-strong);background:#0d1219e0;border-radius:6px;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;width:min(100%,560px);height:54px;display:grid;overflow:hidden}.endpoint-protocol{border-right:1px solid var(--line);height:100%;color:var(--faint);font:.68rem var(--mono);place-items:center;padding:0 .9rem;display:grid}.endpoint code{color:#b9d8ff;min-width:0;font:.76rem var(--mono);text-overflow:ellipsis;white-space:nowrap;padding:0 .85rem;overflow:hidden}.endpoint button,.terminal-bar button{color:var(--cyan);cursor:pointer;font:700 .66rem var(--mono);letter-spacing:.04em;background:0 0;border:0}.endpoint button{border-left:1px solid var(--line);height:100%;padding:0 1rem}.endpoint button:hover,.terminal-bar button:hover{color:var(--cyan-strong);background:var(--cyan-wash)}.protocol-facts{color:var(--muted);font:.64rem var(--mono);flex-wrap:wrap;gap:1.2rem;margin-top:1.15rem;display:flex}.protocol-facts span{align-items:center;gap:.42rem;display:inline-flex}.fact-dot{background:var(--faint);border-radius:50%;width:5px;height:5px}.fact-live{background:var(--green)}.trace-window{border:1px solid var(--line-strong);background:#090e14f0;border-radius:9px;overflow:hidden;transform:rotate(.45deg);box-shadow:0 38px 90px #00000059}.trace-titlebar{border-bottom:1px solid var(--line);background:var(--panel);height:48px;font:.65rem var(--mono);justify-content:space-between;align-items:center;padding:0 .9rem;display:flex}.trace-titlebar>div,.traffic-lights{align-items:center;display:flex}.traffic-lights{gap:5px;margin-right:.75rem}.traffic-lights i,.terminal-bar i{background:var(--line-strong);border-radius:50%;width:7px;height:7px}.traffic-lights i:first-child,.terminal-bar i:first-child{background:var(--red)}.traffic-lights i:nth-child(2),.terminal-bar i:nth-child(2){background:var(--amber)}.traffic-lights i:last-child,.terminal-bar i:last-child{background:var(--green)}.trace-name{color:var(--muted)}.trace-duration{color:var(--cyan)}.trace-ruler{border-bottom:1px solid var(--line);height:32px;color:var(--faint);font:.54rem var(--mono);grid-template-columns:repeat(5,1fr);align-items:end;padding:0 12px 7px 174px;display:grid}.trace-ruler span:not(:first-child){text-align:right}.trace-rows{padding:.55rem 0;position:relative}.trace-rows:after{content:"";pointer-events:none;background:repeating-linear-gradient(90deg,#0000 0 calc(25% - 1px),#ffffff0b 25%);position:absolute;inset:0 12px 0 174px}.trace-row{z-index:1;border-bottom:1px solid #2631418c;grid-template-columns:174px minmax(0,1fr);align-items:center;min-height:54px;display:grid;position:relative}.trace-row:last-child{border-bottom:0}.span-label{min-width:0;font-family:var(--mono);gap:.22rem;padding:0 .8rem;display:grid}.span-label b,.span-label small{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.span-label b{font-size:.61rem;font-weight:600}.span-label small{color:var(--faint);font-size:.54rem}.span-track{height:13px;margin-right:12px;position:relative}.span-track i{top:0;bottom:0;left:var(--start);width:var(--size);background:linear-gradient(90deg,#1f6e6b,#27827e);border:1px solid #329f9a;border-radius:2px;position:absolute;box-shadow:inset 0 0 0 1px #ffffff0a}.root-span .span-track i{background:linear-gradient(90deg,#31598e,#4777b8);border-color:#527bbb}.trace-footer{border-top:1px solid var(--line);background:var(--panel);height:47px;color:var(--faint);font:.57rem var(--mono);align-items:center;gap:1.1rem;padding:0 .9rem;display:flex}.trace-footer b{color:var(--ink)}.trace-footer .trace-ok b{color:var(--green)}.trace-id{margin-left:auto}.tool-surface,.protocol-section,.install-section{border-top:1px solid var(--line);padding:7rem 0}.tool-surface{grid-template-columns:.72fr 1.28fr;gap:clamp(3rem,8vw,8rem);display:grid}.section-index{margin-bottom:1.35rem}h2{letter-spacing:-.055em;margin-bottom:1.15rem;font-size:clamp(2rem,4vw,3.7rem);line-height:1.02}.section-intro>p:last-child,.protocol-heading p,.install-copy>p:last-of-type{color:var(--muted);line-height:1.65}.tool-list{counter-reset:tools;margin:0;padding:0;list-style:none}.tool-list li{border-bottom:1px solid var(--line);counter-increment:tools;grid-template-columns:28px 1.15fr 1.6fr auto;align-items:center;gap:1.2rem;min-height:68px;display:grid}.tool-list li:first-child{border-top:1px solid var(--line)}.tool-list li:before{content:"0" counter(tools);color:var(--faint);font:.55rem var(--mono)}.tool-list code{color:var(--ink);font:.7rem var(--mono)}.tool-list span{color:var(--muted);font-size:.75rem}.tool-list b{color:var(--cyan);font:600 .53rem var(--mono);letter-spacing:.05em;text-transform:uppercase}.protocol-heading{grid-template-columns:1fr .65fr;align-items:end;gap:4rem;display:grid}.protocol-heading p{max-width:430px;margin-bottom:1.2rem}.protocol-grid{border:1px solid var(--line);grid-template-columns:repeat(4,1fr);margin-top:3.2rem;display:grid}.protocol-grid article{border-right:1px solid var(--line);background:#0d12197a;min-height:230px;padding:1.35rem}.protocol-grid article:last-child{border-right:0}.protocol-grid article>span{color:var(--cyan);font:.6rem var(--mono)}.protocol-grid h3{letter-spacing:-.02em;margin:4rem 0 .7rem;font-size:.95rem}.protocol-grid p{color:var(--muted);margin-bottom:0;font-size:.73rem;line-height:1.6}.install-section{grid-template-columns:.78fr 1.22fr;align-items:center;gap:clamp(3rem,8vw,8rem);padding-bottom:8rem;display:grid}.client-tabs{flex-wrap:wrap;gap:.4rem;margin-top:2rem;display:flex}.client-tabs button{border:1px solid var(--line);color:var(--muted);cursor:pointer;background:0 0;border-radius:5px;padding:.52rem .72rem;font-size:.7rem}.client-tabs button:hover{border-color:var(--line-strong);color:var(--ink)}.client-tabs button[aria-selected=true]{background:var(--cyan-wash);color:var(--cyan-strong);border-color:#329f9a}.install-terminal{border:1px solid var(--line-strong);background:#090e14;border-radius:8px;overflow:hidden;box-shadow:0 28px 70px #00000045}.terminal-bar{border-bottom:1px solid var(--line);background:var(--panel);grid-template-columns:1fr auto 1fr;align-items:center;height:43px;padding:0 .8rem;display:grid}.terminal-bar>span{gap:5px;display:flex}.terminal-bar b{color:var(--faint);font:500 .59rem var(--mono)}.terminal-bar button{justify-self:end;height:100%;padding:0 .7rem}.install-terminal pre{color:#b9d8ff;min-height:150px;font:.74rem/1.75 var(--mono);white-space:pre-wrap;align-items:center;margin:0;padding:1.5rem;display:flex;overflow:auto}.install-terminal>p{border-top:1px solid var(--line);color:var(--faint);font:.58rem var(--mono);margin:0;padding:.75rem 1rem}.install-terminal>p span{color:var(--cyan);margin-right:.45rem}footer{border-top:1px solid var(--line);min-height:120px;color:var(--faint);grid-template-columns:1fr auto 1fr;align-items:center;font-size:.66rem;display:grid}footer p{margin:0}footer>div{font-family:var(--mono);justify-self:end;gap:1.2rem;display:flex}footer a:hover{color:var(--ink)}.copy-status{z-index:10;background:var(--panel-raised);color:var(--cyan-strong);opacity:0;pointer-events:none;font:.65rem var(--mono);border:1px solid #4dd8d066;border-radius:5px;padding:.65rem .85rem;transition:opacity .12s,transform .12s;position:fixed;bottom:1rem;right:1rem;transform:translateY(8px)}.copy-status.visible{opacity:1;transform:translateY(0)}@media (width<=900px){.site-header,main,footer{width:min(100% - 32px,720px)}.hero{grid-template-columns:1fr;gap:4rem;min-height:auto;padding:5.5rem 0 6rem}.trace-window{transform:none}.tool-surface,.install-section{grid-template-columns:1fr;gap:3rem}.protocol-grid{grid-template-columns:repeat(2,1fr)}.protocol-grid article:nth-child(2){border-right:0}.protocol-grid article:nth-child(-n+2){border-bottom:1px solid var(--line)}footer{grid-template-columns:1fr auto}footer p{display:none}}@media (width<=620px){.site-header{grid-template-columns:1fr auto}.site-header nav{display:none}h1{font-size:clamp(3rem,15vw,4.7rem)}.hero{padding-top:4.5rem}.trace-ruler{padding-left:124px}.trace-rows:after{left:124px}.trace-row{grid-template-columns:124px minmax(0,1fr)}.trace-footer{gap:.65rem}.trace-id{display:none}.tool-list li{grid-template-columns:1fr auto;padding:.85rem 0}.tool-list li:before{display:none}.tool-list span{grid-area:2/1/auto/-1}.protocol-heading{grid-template-columns:1fr;gap:0}.protocol-grid{grid-template-columns:1fr}.protocol-grid article{border-right:0;border-bottom:1px solid var(--line);min-height:185px}.protocol-grid article:last-child{border-bottom:0}.protocol-grid h3{margin-top:2.7rem}.endpoint-protocol{display:none}.endpoint{grid-template-columns:minmax(0,1fr) auto}}@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}*{transition:none!important}}
17
17
  /*$vite$:1*/</style>
18
18
  </head>
19
19
  <body>
@@ -24,7 +24,6 @@
24
24
  <a href="#install">Install</a>
25
25
  <a href="https://github.com/ANcpLua/qyl.mcp" rel="noreferrer">GitHub</a>
26
26
  </nav>
27
- <span class="release-badge">Beta</span>
28
27
  </header>
29
28
 
30
29
  <main>
@@ -33,8 +32,8 @@
33
32
  <p class="eyebrow"><span aria-hidden="true"></span> Hosted MCP endpoint</p>
34
33
  <h1 id="hero-title">Your telemetry,<br><em>inside the agent.</em></h1>
35
34
  <p class="hero-lede">
36
- Give coding agents direct read access to qyl traces, logs, sessions, and CI
37
- evidence, plus explicitly granted workflow controls—without leaving the work.
35
+ Give coding agents direct read access to qyl traces, logs, sessions, metrics,
36
+ and CI evidence—without leaving the work.
38
37
  </p>
39
38
 
40
39
  <div class="endpoint" aria-label="MCP endpoint">
@@ -96,7 +95,7 @@
96
95
  <div class="section-intro">
97
96
  <p class="section-index">01 / TOOL SURFACE</p>
98
97
  <h2 id="tools-title">Ask from the place you’re already fixing.</h2>
99
- <p><code>qyl:read</code> covers evidence and views. Grant <code>qyl:control</code> separately to named clients for approval-gated workflow controls.</p>
98
+ <p><code>qyl:read</code> covers every tool: evidence and views, read-only throughout.</p>
100
99
  </div>
101
100
  <ol class="tool-list">
102
101
  <li><code>display_traces</code><span>Open a correlated waterfall and its logs.</span><b>MCP App</b></li>
@@ -104,7 +103,7 @@
104
103
  <li><code>get_trace</code><span>Inspect the complete span tree for one trace.</span><b>Read</b></li>
105
104
  <li><code>display_mcp_dashboard</code><span>See tool traffic, latency, and failures.</span><b>MCP App</b></li>
106
105
  <li><code>ci_log</code><span>Bring CI execution evidence into the investigation.</span><b>Read</b></li>
107
- <li><code>control_workflow_run</code><span>Steer, interrupt, or resume a run.</span><b>Control</b></li>
106
+ <li><code>query_metric</code><span>Answer a windowed question over an instrument.</span><b>Read</b></li>
108
107
  </ol>
109
108
  </section>
110
109
 
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The metrics read tools: catalog, series discovery, range query.
3
+ *
4
+ * Three tools rather than one because they answer three different questions an
5
+ * agent asks in order — "what is recorded here", "which streams exist under
6
+ * this name", "what did this one do over that window" — and folding them into a
7
+ * single tool with a mode argument would make every call carry the arguments of
8
+ * the other two.
9
+ *
10
+ * Output shapes are the operations' own 200 bodies from contract-validation.ts.
11
+ * Input shapes are derived from the same operations' published parameters (see
12
+ * contract-operations.ts): the metrics API landed in @ancplua/qyl-api-schema
13
+ * 8.0.0 with responses but without `Mcp.Tools.*Metric*` input models, and
14
+ * generating them from the OpenAPI document keeps gate G10a honest instead of
15
+ * exempting three hand-written objects from it.
16
+ */
17
+ import type { McpServer } from "@modelcontextprotocol/server";
18
+ /**
19
+ * The list operations page, and their pages are the tool's output. `has_more`
20
+ * travels with the items so a reader can tell a complete catalog from a first
21
+ * page; the cursor does not, because these tools take no cursor.
22
+ */
23
+ export declare function registerMetricsTools(server: McpServer): void;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The metrics read tools: catalog, series discovery, range query.
3
+ *
4
+ * Three tools rather than one because they answer three different questions an
5
+ * agent asks in order — "what is recorded here", "which streams exist under
6
+ * this name", "what did this one do over that window" — and folding them into a
7
+ * single tool with a mode argument would make every call carry the arguments of
8
+ * the other two.
9
+ *
10
+ * Output shapes are the operations' own 200 bodies from contract-validation.ts.
11
+ * Input shapes are derived from the same operations' published parameters (see
12
+ * contract-operations.ts): the metrics API landed in @ancplua/qyl-api-schema
13
+ * 8.0.0 with responses but without `Mcp.Tools.*Metric*` input models, and
14
+ * generating them from the OpenAPI document keeps gate G10a honest instead of
15
+ * exempting three hand-written objects from it.
16
+ */
17
+ import { operationInputSchema } from "./contract-operations.js";
18
+ import { MetricQueryResultSchema, MetricsListResponseSchema, MetricSeriesListResponseSchema, compactOutputSchema, } from "./contract-validation.js";
19
+ import { listMetricSeries, listMetrics, queryMetric, } from "./metrics.js";
20
+ import { summarizeMetricCatalog, summarizeMetricQuery, summarizeMetricSeries, } from "./summaries.js";
21
+ import { telemetryToolResult } from "./telemetry-redaction.js";
22
+ import { READ_ONLY_TELEMETRY_TOOL_ANNOTATIONS, toolError } from "./tools.js";
23
+ const METRICS_PATH = "/api/v1/metrics";
24
+ const SERIES_PATH = "/api/v1/metrics/{metric_name}/series";
25
+ const QUERY_PATH = "/api/v1/metrics/{metric_name}/query";
26
+ /**
27
+ * The list operations page, and their pages are the tool's output. `has_more`
28
+ * travels with the items so a reader can tell a complete catalog from a first
29
+ * page; the cursor does not, because these tools take no cursor.
30
+ */
31
+ export function registerMetricsTools(server) {
32
+ server.registerTool("list_metrics", {
33
+ title: "List Metrics",
34
+ description: "List the metric instruments recorded for this project: name, kind " +
35
+ "(gauge/sum/histogram), unit, how many attribute streams exist under each " +
36
+ "name, and when it was last written. Start here — query_metric needs an " +
37
+ "exact instrument name, and this is where the names come from.",
38
+ inputSchema: operationInputSchema(METRICS_PATH),
39
+ outputSchema: compactOutputSchema(MetricsListResponseSchema),
40
+ annotations: READ_ONLY_TELEMETRY_TOOL_ANNOTATIONS,
41
+ }, async (args) => {
42
+ try {
43
+ const { metrics, mode } = await listMetrics(args);
44
+ return telemetryToolResult(summarizeMetricCatalog(metrics, mode), {
45
+ items: metrics,
46
+ has_more: false,
47
+ });
48
+ }
49
+ catch (error) {
50
+ return toolError(error);
51
+ }
52
+ });
53
+ server.registerTool("get_metric_series", {
54
+ title: "Get Metric Series",
55
+ description: "List the distinct attribute streams recorded under one metric name, with " +
56
+ "each stream's attributes, service, and first/last seen. Use it to discover " +
57
+ "which attribute keys are worth passing to query_metric as group_by or attr " +
58
+ "filters, before running a range query that would otherwise collapse or " +
59
+ "explode the result.",
60
+ inputSchema: operationInputSchema(SERIES_PATH),
61
+ outputSchema: compactOutputSchema(MetricSeriesListResponseSchema),
62
+ annotations: READ_ONLY_TELEMETRY_TOOL_ANNOTATIONS,
63
+ }, async (args) => {
64
+ try {
65
+ const { series, mode } = await listMetricSeries(args);
66
+ return telemetryToolResult(summarizeMetricSeries(series, mode), {
67
+ items: series,
68
+ has_more: false,
69
+ });
70
+ }
71
+ catch (error) {
72
+ return toolError(error);
73
+ }
74
+ });
75
+ server.registerTool("query_metric", {
76
+ title: "Query Metric",
77
+ description: "Run a time-bucketed range query over one metric: a window (start_time, " +
78
+ "end_time), a bucket width (step_ms), a reducer (aggregation: avg, min, max, " +
79
+ "sum, count, last, p50, p90, p95, p99), optional group_by attribute keys, and " +
80
+ "optional attr/attr_prefix matchers written 'key=value'. Returns one stream " +
81
+ "per grouping with its buckets. One bucket spanning the whole window collapses " +
82
+ "the answer to a single number.",
83
+ inputSchema: operationInputSchema(QUERY_PATH),
84
+ outputSchema: compactOutputSchema(MetricQueryResultSchema),
85
+ annotations: READ_ONLY_TELEMETRY_TOOL_ANNOTATIONS,
86
+ }, async (args) => {
87
+ try {
88
+ const { result, mode } = await queryMetric(args);
89
+ return telemetryToolResult(summarizeMetricQuery(result, mode), result);
90
+ }
91
+ catch (error) {
92
+ return toolError(error);
93
+ }
94
+ });
95
+ }
96
+ //# sourceMappingURL=metrics-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics-tools.js","sourceRoot":"","sources":["../src/metrics-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Metric reading over the contract 8.0.0 metrics API, live and demo.
3
+ *
4
+ * Live mode forwards to the collector's three published operations and revalidates
5
+ * the answer against the operation's own 200 body. Demo mode holds stored points
6
+ * and runs the same bucketing and reducer the collector would, so a demo answer
7
+ * honors step_ms, aggregation, group_by and the attribute matchers rather than
8
+ * pretending they were applied — the invariant the rest of the demo dataset keeps.
9
+ */
10
+ import type { MetricAggregation, MetricDescriptor, MetricQueryResult, MetricSeries } from "@ancplua/qyl-api-schema/types";
11
+ import type { Mode } from "./wire.js";
12
+ /** Tool arguments, mirroring the published operation parameters that shape them. */
13
+ export interface ListMetricsArgs {
14
+ name_prefix?: string;
15
+ limit?: number;
16
+ }
17
+ export interface MetricSeriesArgs {
18
+ metric_name: string;
19
+ attr?: readonly string[];
20
+ attr_prefix?: readonly string[];
21
+ limit?: number;
22
+ }
23
+ export interface QueryMetricArgs {
24
+ metric_name: string;
25
+ start_time: string;
26
+ end_time: string;
27
+ step_ms?: number;
28
+ aggregation?: MetricAggregation;
29
+ group_by?: readonly string[];
30
+ attr?: readonly string[];
31
+ attr_prefix?: readonly string[];
32
+ series_limit?: number;
33
+ }
34
+ export declare function listMetrics(args: ListMetricsArgs): Promise<{
35
+ metrics: MetricDescriptor[];
36
+ mode: Mode;
37
+ }>;
38
+ export declare function listMetricSeries(args: MetricSeriesArgs): Promise<{
39
+ series: MetricSeries[];
40
+ mode: Mode;
41
+ }>;
42
+ export declare function queryMetric(args: QueryMetricArgs): Promise<{
43
+ result: MetricQueryResult;
44
+ mode: Mode;
45
+ }>;
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Metric reading over the contract 8.0.0 metrics API, live and demo.
3
+ *
4
+ * Live mode forwards to the collector's three published operations and revalidates
5
+ * the answer against the operation's own 200 body. Demo mode holds stored points
6
+ * and runs the same bucketing and reducer the collector would, so a demo answer
7
+ * honors step_ms, aggregation, group_by and the attribute matchers rather than
8
+ * pretending they were applied — the invariant the rest of the demo dataset keeps.
9
+ */
10
+ import { CollectorError, collectorGet, parseCollectorMetricDescriptor, parseCollectorMetricQueryResult, parseCollectorMetricSeries, parseCollectorPage, resolveMode, } from "./collector.js";
11
+ import { MetricsListResponseSchema, MetricSeriesListResponseSchema, } from "./contract-validation.js";
12
+ import { getDemoMetrics } from "./demo.js";
13
+ import { redactTelemetry } from "./telemetry-redaction.js";
14
+ const METRICS_PATH = "/api/v1/metrics";
15
+ /**
16
+ * `key=value` and `key=prefix` are the published spellings of both matcher
17
+ * lists. A matcher without `=` is the model's mistake, not the collector's, so
18
+ * it fails here with the offending text rather than reaching the wire as a
19
+ * silently ignored filter.
20
+ */
21
+ function parseMatchers(raw, parameter) {
22
+ return (raw ?? []).map((entry) => {
23
+ const separator = entry.indexOf("=");
24
+ if (separator <= 0) {
25
+ throw new CollectorError(`${parameter} entries are written 'key=value'; received ${JSON.stringify(entry)}`);
26
+ }
27
+ return { key: entry.slice(0, separator), value: entry.slice(separator + 1) };
28
+ });
29
+ }
30
+ function attributeText(attributes, key) {
31
+ const found = attributes.find((attribute) => attribute.key === key);
32
+ if (found === undefined)
33
+ return undefined;
34
+ const { value } = found;
35
+ if (typeof value === "string")
36
+ return value;
37
+ if (typeof value === "boolean")
38
+ return String(value);
39
+ if (value !== null && typeof value === "object" && "value" in value) {
40
+ return String(value.value);
41
+ }
42
+ return value === null ? undefined : String(value);
43
+ }
44
+ function matchesStream(stream, exact, prefixes) {
45
+ const read = (key) => key === "service.name" ? stream.serviceName : attributeText(stream.attributes, key);
46
+ return (exact.every((matcher) => read(matcher.key) === matcher.value) &&
47
+ prefixes.every((matcher) => read(matcher.key)?.startsWith(matcher.value) === true));
48
+ }
49
+ /** Stable across rebuilds because it is derived from identity, not from order. */
50
+ function demoSeriesId(stream) {
51
+ const identity = [
52
+ stream.name,
53
+ stream.serviceName,
54
+ ...stream.attributes.map((attribute) => `${attribute.key}=${attributeText([attribute], attribute.key) ?? ""}`),
55
+ ].join("|");
56
+ let hash = 0x811c9dc5;
57
+ for (let index = 0; index < identity.length; index += 1) {
58
+ hash ^= identity.charCodeAt(index);
59
+ hash = Math.imul(hash, 0x01000193) >>> 0;
60
+ }
61
+ return hash.toString(16).padStart(8, "0");
62
+ }
63
+ export async function listMetrics(args) {
64
+ const mode = await resolveMode();
65
+ const limit = args.limit ?? 200;
66
+ if (mode === "demo") {
67
+ const byName = new Map();
68
+ for (const stream of getDemoMetrics()) {
69
+ if (args.name_prefix !== undefined && !stream.name.startsWith(args.name_prefix))
70
+ continue;
71
+ const bucket = byName.get(stream.name);
72
+ if (bucket)
73
+ bucket.push(stream);
74
+ else
75
+ byName.set(stream.name, [stream]);
76
+ }
77
+ const metrics = [...byName.entries()]
78
+ .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
79
+ .slice(0, limit)
80
+ .map(([name, streams]) => {
81
+ const [first] = streams;
82
+ if (first === undefined)
83
+ throw new CollectorError(`demo metric ${name} has no streams`);
84
+ const lastSeen = Math.max(...streams.map((stream) => stream.points.at(-1)?.atMs ?? 0));
85
+ return {
86
+ name,
87
+ kind: first.kind,
88
+ temporality: first.temporality,
89
+ monotonic: first.monotonic,
90
+ unit: first.unit,
91
+ description: first.description,
92
+ series_count: streams.length,
93
+ last_seen: new Date(lastSeen).toISOString(),
94
+ };
95
+ });
96
+ return redactTelemetry({ metrics, mode });
97
+ }
98
+ const body = await collectorGet(METRICS_PATH, {
99
+ name_prefix: args.name_prefix,
100
+ limit,
101
+ });
102
+ return redactTelemetry({
103
+ metrics: parseCollectorPage(body, METRICS_PATH, MetricsListResponseSchema, parseCollectorMetricDescriptor).items,
104
+ mode,
105
+ });
106
+ }
107
+ export async function listMetricSeries(args) {
108
+ const mode = await resolveMode();
109
+ const limit = args.limit ?? 200;
110
+ const exact = parseMatchers(args.attr, "attr");
111
+ const prefixes = parseMatchers(args.attr_prefix, "attr_prefix");
112
+ if (mode === "demo") {
113
+ const streams = getDemoMetrics().filter((stream) => stream.name === args.metric_name && matchesStream(stream, exact, prefixes));
114
+ if (streams.length === 0 && !getDemoMetrics().some((s) => s.name === args.metric_name)) {
115
+ throw new CollectorError(`metric not found: ${args.metric_name}`);
116
+ }
117
+ const series = streams.slice(0, limit).map((stream) => ({
118
+ series_id: demoSeriesId(stream),
119
+ name: stream.name,
120
+ kind: stream.kind,
121
+ attributes: stream.attributes,
122
+ service_name: stream.serviceName,
123
+ unit: stream.unit,
124
+ first_seen: new Date(stream.points[0]?.atMs ?? 0).toISOString(),
125
+ last_seen: new Date(stream.points.at(-1)?.atMs ?? 0).toISOString(),
126
+ }));
127
+ return redactTelemetry({ series, mode });
128
+ }
129
+ const path = `${METRICS_PATH}/${encodeURIComponent(args.metric_name)}/series`;
130
+ try {
131
+ const body = await collectorGet(path, {
132
+ attr: args.attr,
133
+ attr_prefix: args.attr_prefix,
134
+ limit,
135
+ });
136
+ return redactTelemetry({
137
+ series: parseCollectorPage(body, path, MetricSeriesListResponseSchema, parseCollectorMetricSeries).items,
138
+ mode,
139
+ });
140
+ }
141
+ catch (error) {
142
+ if (error instanceof CollectorError && error.status === 404) {
143
+ throw new CollectorError(`metric not found: ${args.metric_name}`);
144
+ }
145
+ throw error;
146
+ }
147
+ }
148
+ /** The published reducers, over the points that landed in one bucket. */
149
+ function reduce(values, aggregation) {
150
+ if (values.length === 0)
151
+ return null;
152
+ const sorted = [...values].sort((left, right) => left - right);
153
+ const quantile = (fraction) => {
154
+ const index = Math.min(sorted.length - 1, Math.floor(fraction * sorted.length));
155
+ return sorted[index];
156
+ };
157
+ switch (aggregation) {
158
+ case "avg":
159
+ return values.reduce((total, value) => total + value, 0) / values.length;
160
+ case "min":
161
+ return sorted[0];
162
+ case "max":
163
+ return sorted[sorted.length - 1];
164
+ case "sum":
165
+ return values.reduce((total, value) => total + value, 0);
166
+ case "count":
167
+ return values.length;
168
+ case "last":
169
+ return values[values.length - 1];
170
+ case "p50":
171
+ return quantile(0.5);
172
+ case "p90":
173
+ return quantile(0.9);
174
+ case "p95":
175
+ return quantile(0.95);
176
+ case "p99":
177
+ return quantile(0.99);
178
+ }
179
+ }
180
+ function groupingKey(stream, groupBy) {
181
+ const attributes = [];
182
+ for (const key of groupBy) {
183
+ const value = key === "service.name"
184
+ ? stream.serviceName
185
+ : attributeText(stream.attributes, key);
186
+ if (value !== undefined)
187
+ attributes.push({ key, value });
188
+ }
189
+ return { key: attributes.map((a) => `${a.key}=${String(a.value)}`).join("|"), attributes };
190
+ }
191
+ export async function queryMetric(args) {
192
+ const mode = await resolveMode();
193
+ const stepMs = args.step_ms ?? 60_000;
194
+ const aggregation = args.aggregation ?? "avg";
195
+ const seriesLimit = args.series_limit ?? 50;
196
+ const exact = parseMatchers(args.attr, "attr");
197
+ const prefixes = parseMatchers(args.attr_prefix, "attr_prefix");
198
+ if (mode === "demo") {
199
+ const streams = getDemoMetrics().filter((stream) => stream.name === args.metric_name && matchesStream(stream, exact, prefixes));
200
+ if (!getDemoMetrics().some((stream) => stream.name === args.metric_name)) {
201
+ throw new CollectorError(`metric not found: ${args.metric_name}`);
202
+ }
203
+ const startMs = Date.parse(args.start_time);
204
+ const endMs = Date.parse(args.end_time);
205
+ if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs <= startMs) {
206
+ throw new CollectorError("end_time must be a valid timestamp after start_time");
207
+ }
208
+ const groups = new Map();
209
+ for (const stream of streams) {
210
+ const { key, attributes } = groupingKey(stream, args.group_by ?? []);
211
+ const existing = groups.get(key);
212
+ if (existing)
213
+ existing.streams.push(stream);
214
+ else
215
+ groups.set(key, { attributes, streams: [stream] });
216
+ }
217
+ const ordered = [...groups.values()];
218
+ const truncated = ordered.length > seriesLimit;
219
+ const bucketCount = Math.max(1, Math.ceil((endMs - startMs) / stepMs));
220
+ const series = ordered.slice(0, seriesLimit).map((group) => {
221
+ const buckets = Array.from({ length: bucketCount }, () => []);
222
+ for (const stream of group.streams) {
223
+ for (const point of stream.points) {
224
+ if (point.atMs < startMs || point.atMs >= endMs)
225
+ continue;
226
+ const index = Math.min(bucketCount - 1, Math.floor((point.atMs - startMs) / stepMs));
227
+ buckets[index].push(point.value);
228
+ }
229
+ }
230
+ return {
231
+ attributes: group.attributes,
232
+ buckets: buckets.map((values, index) => ({
233
+ bucket_start: new Date(startMs + index * stepMs).toISOString(),
234
+ value: reduce(values, aggregation),
235
+ point_count: values.length,
236
+ })),
237
+ };
238
+ });
239
+ const first = streams[0];
240
+ const result = {
241
+ name: args.metric_name,
242
+ kind: first?.kind ?? "gauge",
243
+ unit: first?.unit,
244
+ aggregation,
245
+ step_ms: stepMs,
246
+ start_time: new Date(startMs).toISOString(),
247
+ end_time: new Date(endMs).toISOString(),
248
+ series,
249
+ truncated,
250
+ };
251
+ return redactTelemetry({ result, mode });
252
+ }
253
+ const path = `${METRICS_PATH}/${encodeURIComponent(args.metric_name)}/query`;
254
+ try {
255
+ const body = await collectorGet(path, {
256
+ start_time: args.start_time,
257
+ end_time: args.end_time,
258
+ step_ms: stepMs,
259
+ aggregation,
260
+ group_by: args.group_by,
261
+ attr: args.attr,
262
+ attr_prefix: args.attr_prefix,
263
+ series_limit: seriesLimit,
264
+ });
265
+ return redactTelemetry({ result: parseCollectorMetricQueryResult(body, path), mode });
266
+ }
267
+ catch (error) {
268
+ if (error instanceof CollectorError && error.status === 404) {
269
+ throw new CollectorError(`metric not found: ${args.metric_name}`);
270
+ }
271
+ throw error;
272
+ }
273
+ }
274
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"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"}
package/dist/oauth.d.ts CHANGED
@@ -3,7 +3,6 @@ import { type OAuthMetadata, type OAuthTokenVerifier } from "@modelcontextprotoc
3
3
  export declare const QYL_MCP_ISSUER = "https://qyl-eu.eu.auth0.com/";
4
4
  export declare const QYL_MCP_RESOURCE = "https://mcp.qyl.at/mcp";
5
5
  export declare const QYL_MCP_SCOPE = "qyl:read";
6
- export declare const QYL_MCP_CONTROL_SCOPE = "qyl:control";
7
6
  export interface HostedOAuth {
8
7
  readonly requiredScopes: string[];
9
8
  readonly scopesSupported: string[];
package/dist/oauth.js CHANGED
@@ -5,7 +5,6 @@ const DISCOVERY_TIMEOUT_MS = 10_000;
5
5
  export const QYL_MCP_ISSUER = "https://qyl-eu.eu.auth0.com/";
6
6
  export const QYL_MCP_RESOURCE = "https://mcp.qyl.at/mcp";
7
7
  export const QYL_MCP_SCOPE = "qyl:read";
8
- export const QYL_MCP_CONTROL_SCOPE = "qyl:control";
9
8
  function readIssuer(environment) {
10
9
  const configured = environment.MCP_OAUTH_ISSUER;
11
10
  if (!configured) {
@@ -114,7 +113,7 @@ export async function loadHostedOAuth(resourceServerUrl, environment = process.e
114
113
  }
115
114
  return {
116
115
  requiredScopes: [QYL_MCP_SCOPE],
117
- scopesSupported: [QYL_MCP_SCOPE, QYL_MCP_CONTROL_SCOPE],
116
+ scopesSupported: [QYL_MCP_SCOPE],
118
117
  oauthMetadata,
119
118
  verifier: createJwtTokenVerifier({
120
119
  issuer: oauthMetadata.issuer,