Skip to content
ANcpLuaPublic

About

qyl telemetry MCP server and client

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

qyl.mcp

qyl provides MCP tools for investigating traces, logs, metrics and sessions in a qyl Collector. It measures and correlates; the agent reasons and acts.

Component Source role
server/ Telemetry MCP server and bundled MCP App viewers
workbench/ Local client for inspecting and invoking other MCP servers
dashboard/ Browser UI built alongside the Workbench

Use the hosted server

Endpoint: https://mcp.qyl.at/mcp, deployed from main on Railway. An unauthenticated request is answered 401 with resource_metadata; the metadata names that resource, issuer https://qyl-eu.eu.auth0.com/ and scopes qyl:read qyl:write qyl:delete. Only the write tools need qyl:write and qyl:delete; they ask for them through a step-up challenge.

Connect

Configure that endpoint in the intended client and authorize an account with access to the intended Collector project. Client registration and user/project authorization must both be configured by the operator.

Client Connection setting Registration path
ChatGPT web In Plugins, choose Add custom MCP server, enter https://mcp.qyl.at/mcp, select OAuth and request qyl:read qyl:write qyl:delete; create and install the personal plugin. Choose CIMD, DCR or a provisioned OAuth client as offered. For CIMD, use the metadata document and callback shown by the connection page; stable metadata is https://chatgpt.com/oauth/client.json where issuer-bound callbacks are supported.
claude.ai In Customize → Connectors, add a custom connector for https://mcp.qyl.at/mcp, select sign-in and complete OAuth. Organization users first need their owner to add the connector under organization settings. Select Claude's published identity (CIMD), automatic registration (DCR), or a provisioned client. Public CIMD: https://claude.ai/oauth/mcp-oauth-client-metadata.
Claude Code Run claude mcp add --transport http qyl https://mcp.qyl.at/mcp, then open /mcp to authenticate. Automatic CIMD/DCR discovery; if a provisioned client is required, use the CLI's --client-id and --client-secret prompt, with its actual callback registered.
Codex CLI Add the TOML below to the intended Codex configuration, then run codex mcp login qyl --scopes "qyl:read qyl:write qyl:delete". Automatic CIMD/DCR selection; for a targeted registration check, the CLI accepts --oauth-client-registration cimd or dcr on login. A configured client ID takes precedence.
MCP Inspector Save the JSON below as inspector-modern.json; run bunx @modelcontextprotocol/inspector@2.9.0 --config inspector-modern.json, complete OAuth with qyl:read qyl:write qyl:delete, then invoke tools/list. Use the provisioned client or registration flow offered by Inspector and the issuer. Registration alone does not grant resource access.

Codex configuration:

[mcp_servers.qyl]
url = "https://mcp.qyl.at/mcp"

Inspector configuration (repeat with "legacy" for the legacy check):

{"mcpServers":{"qyl":{"type":"streamable-http","url":"https://mcp.qyl.at/mcp","protocolEra":"modern"}}}

To check the modern era against production, connect Inspector 2.9.0 with OAuth and protocolEra: "modern", call tools/list and confirm the request carries MCP-Protocol-Version: 2026-07-28. A legacy-only connection or an initialize response does not show it.

Run the server yourself

Run the published package with Bun:

bunx qyl-mcp-server --stdio
bunx qyl-mcp-server

npx and pnpm dlx also work when Bun 1.4 or newer is installed.

From a checkout, build the server and start stdio:

bun install --frozen-lockfile
bun run build
bun server/dist/main.js --stdio

Configure QYL_COLLECTOR_URL and, when needed, QYL_API_KEY. For local HTTP, run bun server/dist/main.js; the source defaults to loopback port 3001. QYL_API_KEY is an outgoing Collector credential, not an incoming MCP login.

./release.sh <version> publishes qyl-mcp-server from the developer's machine (see SKILL.md). The executable uses Bun; the exported library subpaths support Node 24. C# and TypeScript telemetry definitions come from the qyl convention registry, while product API contracts come from qyl-api-schema. The official MCP SDK, OpenTelemetry SDKs and exporters remain in use. See release notes; the npm registry's latest tag identifies the published version.

Tools and viewers

The generated manifest is the tool contract. It has 32 tools: 26 read tools annotated readOnlyHint: true and the six write tools; fetch_telemetry is app-only through _meta.ui.visibility: ["app"]. bun run smoke checks that tools/list returns exactly these 32 tools on 2026-07-28 and over the 2025-era handshake.

Start with sessions and traces, drill into one trace and its error logs, then discover metric instruments and series before querying. get_trace returns the complete span tree by default. Use errors_only to select error spans, max_spans to cap the result after filtering, and include_attributes=false to omit attribute collections. Original trace totals are retained; the summary states how many matching spans were returned. display_traces supplies the waterfall and search_logs supplies filtered detail. ci_log accepts telemetry from any CI following its emitter convention; service_prefix selects services in both run lists and phase breakdowns and defaults to qyl-ci. The shared skill contains the full workflow; the server also serves it through the MCP skills extension (skills/list, skills/get, skill://qyl/qyl-investigate/SKILL.md), which the OpenAI plugin portal imports when it scans the server, and the build copies it into server/dist/skills.

Write tools

Six tools change something, and only records inside the caller's Collector project: annotations (plain-text notes of at most 2000 characters on a trace, span or session), saved queries (named filters for traces, logs or metrics) and the status of recorded failures. No tool deletes or edits traces, logs or metrics; retention alone removes telemetry.

Tool Changes Annotations Scope
create_annotation adds a note readOnlyHint: false, destructiveHint: false, idempotentHint: true qyl:write
update_annotation replaces a note's text readOnlyHint: false, destructiveHint: true, idempotentHint: true qyl:write
create_saved_query adds a named filter readOnlyHint: false, destructiveHint: false, idempotentHint: true qyl:write
update_saved_query renames it or replaces its description or filter readOnlyHint: false, destructiveHint: true, idempotentHint: true qyl:write
update_failure_status sets a failure's status (open, resolved or ignored), assignee or ignore-until time readOnlyHint: false, destructiveHint: true, idempotentHint: true qyl:write
delete removes one annotation or saved query (kind is annotation or saved_query) readOnlyHint: false, destructiveHint: true, idempotentHint: true qyl:delete

openWorldHint is false on all of them. list_saved_queries and run_saved_query read saved queries and keep qyl:read; get_trace and list_sessions return the annotations on what they read, so there is no annotation listing tool. Updates and deletes send the object's etag as If-Match: a stale etag is refused with precondition_failed, a duplicate saved-query name with conflict. Alert rules are outside the current write surface. On the hosted server a write call without its scope gets a 403 insufficient_scope challenge naming the scopes to request, in both protocol eras, before any Collector request.

The two viewers declare versioned resource URIs, a configured hosted origin and empty external connection/resource CSP allowlists. Local tests cover restoring the original query when refresh input is empty.

MCP Events

The source supports opt-in trace.error subscriptions on authenticated Auth0 deployments configured with MCP_EVENTS_STORE. The optional service_name filter narrows delivery. The implementation verifies callbacks, signs deliveries, checks ongoing authorization, expires subscriptions and cancels delivery on unsubscribe or confirmed revocation. Subscription state is bounded and excludes OAuth tokens and copied telemetry.

The workbench

bun run build
bun run start:workbench

The default URL is http://127.0.0.1:18888; QYL_MCP_WORKBENCH_PORT changes the port. Workbench can connect to HTTP and stdio servers, negotiate the protocol automatically, inspect schemas, invoke tools and retain execution evidence. It uses a loopback browser session and checks host/origin access. Tool annotations guide confirmation; they do not grant permission.

Review an external stdio command before launching it: it executes locally. Use environment-variable references for credentials instead of embedding secrets in URLs or saved headers. Workbench state defaults to ~/.qyl/mcp-workbench.json, overridden by QYL_MCP_STATE_PATH. Its execution records are distinct from the server's minimized incoming-call records.

Incoming-call records and telemetry

The server's strict native record schema contains its own ID, tool name, status, timestamps, duration and error type. It contains no arguments, _meta, result body or error-message field. It defaults to retaining 1,000 records at ~/.qyl/mcp-native-executions.json; QYL_MCP_NATIVE_STATE_PATH overrides the path. Hosted HTTP with MCP_PUBLIC_URL disables that local store. Known version-1/2 data is projected into the minimized schema; unreadable archives require operator review.

OTLP exports include operation timing/status and trace correlation. Native incoming calls do not supply argument/result bodies to that telemetry. Workbench has a separate optional content-capture setting. Local OTLP tests check redaction and matching trace/span IDs; they do not establish a production retention policy.

Configuration

Variable Purpose
QYL_COLLECTOR_URL Collector base URL; local default http://127.0.0.1:5100.
QYL_API_KEY Outgoing Collector credential; the write tools need a key with the Collector's write and delete roles.
MCP_ALLOWED_HOSTS Comma-separated additional request hostnames, added to the public URL hostname for hosted HTTP.
MCP_ALLOWED_ORIGIN_HOSTS Comma-separated additional browser-origin hostnames, added to the public URL hostname for hosted HTTP.
QYL_PROJECT Server-configured Collector project scope; its default is stated in the server README.
MCP_COLLECTOR_PROJECTS Operator-controlled verified-subject-to-project mapping.
QYL_MCP_TELEMETRY=0 Disable MCP self-telemetry.
QYL_MCP_CAPTURE_CONTENT=1 Optional Workbench capture; does not add incoming-call payloads to native records.
QYL_OTLP_ENDPOINT Optional telemetry base; signal-specific OTel endpoints take precedence.
QYL_MCP_STATE_PATH Workbench state location.
QYL_MCP_NATIVE_STATE_PATH Native incoming-call record location.
QYL_MCP_WORKBENCH_PORT Workbench port, default 18888.

Deploying your own

These are operator setup instructions. The threat model describes the authorization boundary.

.railway/railway.ts declares build bun run --cwd server build, start bun server/dist/main.js, healthcheck /healthz and an Events volume at /data. Railway deploys mcp.qyl.at from every push to main.

For a hosted bind, configure MCP_PUBLIC_URL, MCP_BIND_HOST, allowed hosts and origins, and the intended Collector URL/credential. The public URL sets the resource audience and canonical /mcp connector URL. For Claude, qyl sets ui.domain to the first 32 hex characters of SHA-256 over that exact connector URL plus .claudemcpcontent.com. Claude requires this format when the optional field is supplied. ChatGPT receives qyl's configured HTTPS origin in ui.domain and its openai/widgetDomain alias. OpenAI documents an origin-valued field and requires a dedicated, unique origin for UI plugin submission; choosing the MCP server's origin is qyl's configuration choice.

The SDK's per-request client name and User-Agent are both checked. A recognized Claude or ChatGPT name wins conflicting hints. A missing or unrecognized name can use a recognized UA, including Claude-User, openai-mcp/1.0.0 or ChatGPT-User. If neither hint is recognized, qyl omits both domain fields. A host that supports MCP Apps can then use its default sandbox; omission alone does not disable the viewer or reject the MCP client. Local viewers also omit the domain. The hints and their precedence are qyl's compatibility policy, not a documented client format.

Keep Collector credentials on the server. With project mapping enabled, only the verified subject selects the project; client hints, tool arguments and metadata do not select credentials.

The default Auth0 mode accepts only the qyl production Auth0 issuer https://qyl-eu.eu.auth0.com/; arbitrary issuer substitution is not an available configuration. Non-loopback serving requires a public URL and hosted authorization. For Events, configure persistent MCP_EVENTS_STORE and the dedicated Auth0 authorization-check credentials listed in the server README.

Authentication

The hosted profile is CIMD or DCR + OAuth 2.1 Authorization Code with PKCE S256 + issuer identification. ChatGPT's client metadata advertises none and private_key_jwt and uses the latter; Claude's CIMD is a public client using none.

Operator setup:

  1. Register the resource audience <public-url>/mcp, RS256 access tokens and the permissions qyl:read, qyl:write and qyl:delete. Configure the provider capabilities required by startup.
  2. Provision the intended client using its current CIMD document or DCR, preserving its actual callback and authentication method.
  3. If DCR is needed, Enable Dynamic Client Registration and set dynamic_client_registration_security_mode to strict. The DCR endpoint is open to registration when enabled; keep third-party API default permissions empty.
  4. qyl:read exposes traces, logs, metrics, sessions, and CI evidence; grant it only to intended clients and users. Verify a real authenticated read and project isolation after deployment; discovery alone is insufficient.
  5. qyl:write lets a client create and update annotations and saved queries and set failure status, and qyl:delete lets it delete annotations and saved queries, in the caller's project only; neither reaches telemetry. Grant them only to users who should change those records; a token without them keeps the read tools. The Collector key the server uses needs the Collector's write and delete roles as well.

server/verify-deployment-guidance.mjs holds this section to these requirements after every server build; provider configuration is an owner action.

The source validates signatures, issuer, resource audience, expiry and scopes; malformed credentials, invalid tokens and insufficient scopes have separate error paths. A write tool called without qyl:write or qyl:delete is answered 403 with error="insufficient_scope" and the scopes to request, so the client can re-authorize for them. Both /.well-known/oauth-protected-resource/mcp and /.well-known/oauth-protected-resource describe the same /mcp resource. Auth0 is the OIDC issuer; qyl.mcp does not expose its own OIDC issuer discovery. For workspace domain claiming, the owner must verify any openid/email requirements and the provider's verified-email response separately; these do not replace qyl:read.

Optional authorization extensions

The source selects extensions explicitly through:

MCP_AUTH_EXTENSIONS=enterprise-managed-authorization@1.0.0,oauth-client-credentials@1.0.0
Integration Source-declared stability Additional provider requirement
enterprise-managed-authorization@1.0.0 Stable JWT bearer grant and ID-JAG grant profile.
oauth-client-credentials@1.0.0 Draft; explicitly opt in Client credentials and private_key_jwt.

These are the installed integration definitions, not proof of a provider grant. Unknown versions and conflicting provider requirements are rejected. They do not issue tokens or replace the core verifier.

Cloudflare Access Managed OAuth

The alternative source configuration uses MCP_AUTH_PROVIDER=cloudflare-access, MCP_ACCESS_TEAM_DOMAIN and MCP_ACCESS_AUD, plus MCP_PUBLIC_URL. The origin checks Cf-Access-Jwt-Assertion: issuer, application audience, expiry and subject, with RS256 signature validation. Configure and verify the Access application before selecting this mode. It does not establish that the public endpoint uses Access, and Auth0-backed Events cannot be enabled in this mode. Access grants the read tools only: its assertion carries no qyl scopes, so the write tools answer insufficient_scope.

Directory preparation

submission/qyl is the plugin bundle for both directories: the Anthropic files in .claude-plugin/ and .mcp.json, the OpenAI root plugin.json and mcp.json. python3 submission/build-openai-package.py builds the OpenAI ZIP into submission/packages/ (ignored by Git), and the connector listing draft holds the Anthropic connector fields. The owner handles identity, public pages, reviewer accounts, recording, attestations, uploads, submission and publication. GET /.well-known/openai-apps-challenge returns the trimmed value of the hosted OPENAI_APPS_CHALLENGE variable as plain text, and 404 while it is blank; on mcp.qyl.at it carries the OpenAI portal's domain token.

The rules in AGENTS.md follow the directories' requirements: OpenAI MCP server, authentication, Events, MCP UI, plugin guidelines; Anthropic connector checklist, plugin checklist, directory policy.

Verification and limits

mise run check
bun run smoke:otlp
bun run smoke:projects

Build the collector once in Release configuration; these checks reuse its output. Set QYL_COLLECTOR_DLL to use a different completed build. Local checks do not cover hosted connections, production Events, the deployed commit or a fresh npm consumer.

Contracts

The generated contracts come from @ancplua/qyl-api-schema; exact dependency versions are recorded in server/package.json and bun.lock. SDK major version and wire revision are independent. The server answers MCP 2026-07-28 and negotiates down for 2025-era clients that send initialize. Server HTTP/stdio use SDK factories and negotiation defaults; Workbench uses automatic negotiation.

About

qyl telemetry MCP server and client

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages