qyl-mcp-server 7.1.0 → 7.2.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 +118 -15
- package/dist/auth-extensions.d.ts +20 -0
- package/dist/auth-extensions.js +81 -0
- package/dist/auth-extensions.js.map +1 -0
- package/dist/auth.d.ts +4 -0
- package/dist/auth.js +5 -0
- package/dist/auth.js.map +1 -0
- package/dist/authorization.d.ts +14 -0
- package/dist/authorization.js +82 -0
- package/dist/authorization.js.map +1 -0
- package/dist/ci.d.ts +5 -5
- package/dist/ci.js +25 -19
- package/dist/ci.js.map +1 -1
- package/dist/collector-access.d.ts +17 -0
- package/dist/collector-access.js +61 -0
- package/dist/collector-access.js.map +1 -0
- package/dist/collector.d.ts +4 -1
- package/dist/collector.js +6 -2
- package/dist/collector.js.map +1 -1
- package/dist/config.d.ts +4 -3
- package/dist/config.js +5 -5
- package/dist/config.js.map +1 -1
- package/dist/contract-operations.d.ts +1 -1
- package/dist/contract-operations.js +1 -1
- package/dist/contract-operations.js.map +1 -1
- package/dist/contract-validation.d.ts +1 -1
- package/dist/contract-validation.js +45 -1
- package/dist/contract-validation.js.map +1 -1
- package/dist/events-authorization.d.ts +9 -0
- package/dist/events-authorization.js +160 -0
- package/dist/events-authorization.js.map +1 -0
- package/dist/events.d.ts +235 -0
- package/dist/events.js +647 -0
- package/dist/events.js.map +1 -0
- package/dist/main.d.ts +14 -23
- package/dist/main.js +99 -36
- package/dist/main.js.map +1 -1
- package/dist/mcp-app.html +1 -1
- package/dist/mcp-dashboard.html +1 -1
- package/dist/mcp-home.html +6 -6
- package/dist/metrics-tools.js +16 -13
- package/dist/metrics-tools.js.map +1 -1
- package/dist/native-execution.d.ts +8 -52
- package/dist/native-execution.js +51 -222
- package/dist/native-execution.js.map +1 -1
- package/dist/oauth-metadata.d.ts +8 -0
- package/dist/oauth-metadata.js +89 -0
- package/dist/oauth-metadata.js.map +1 -0
- package/dist/oauth.d.ts +7 -17
- package/dist/oauth.js +16 -68
- package/dist/oauth.js.map +1 -1
- package/dist/request-scope.d.ts +4 -12
- package/dist/request-scope.js +9 -22
- package/dist/request-scope.js.map +1 -1
- package/dist/server.d.ts +7 -1
- package/dist/server.js +83 -33
- package/dist/server.js.map +1 -1
- package/dist/summaries.d.ts +1 -1
- package/dist/summaries.js +2 -2
- package/dist/summaries.js.map +1 -1
- package/dist/telemetry-redaction.js +13 -0
- package/dist/telemetry-redaction.js.map +1 -1
- package/dist/tools.d.ts +10 -0
- package/dist/tools.js +77 -18
- package/dist/tools.js.map +1 -1
- package/dist/trace-query.d.ts +5 -0
- package/dist/trace-query.js +8 -0
- package/dist/trace-query.js.map +1 -0
- package/dist/ui-domain.d.ts +4 -0
- package/dist/ui-domain.js +28 -0
- package/dist/ui-domain.js.map +1 -0
- package/dist/webhook.d.ts +45 -0
- package/dist/webhook.js +198 -0
- package/dist/webhook.js.map +1 -0
- package/package.json +27 -22
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ npx qyl-mcp-server --stdio # stdio MCP server
|
|
|
9
9
|
npx qyl-mcp-server # Streamable HTTP on 127.0.0.1:3001
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Use a modern or 2025-era MCP client. Point it at a collector:
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
export QYL_COLLECTOR_URL=http://127.0.0.1:5100
|
|
@@ -24,17 +24,53 @@ It is an OAuth 2.1 resource server, so an unauthenticated request answers `401`
|
|
|
24
24
|
with an RFC 9728 protected-resource document that a stock MCP client follows on
|
|
25
25
|
its own.
|
|
26
26
|
|
|
27
|
-
Both
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
Both use MCP TypeScript SDK v2: Streamable HTTP through `createMcpHandler`
|
|
28
|
+
and stdio through `serveStdio`, with their documented compatibility defaults.
|
|
29
|
+
One factory serves revision `2026-07-28` and supported 2025-era requests.
|
|
30
|
+
Events require `2026-07-28`.
|
|
30
31
|
The HTTP server serves a product page at `/`; `/mcp` is the only protocol
|
|
31
32
|
endpoint.
|
|
32
33
|
|
|
34
|
+
## Hosted authentication
|
|
35
|
+
|
|
36
|
+
The hosted Auth0 profile requires CIMD and DCR discovery, Authorization Code
|
|
37
|
+
with PKCE S256, both `private_key_jwt` and `none` for ChatGPT CIMD compatibility,
|
|
38
|
+
`none` for Claude CIMD, and issuer identification. Auth0 owns registration and token
|
|
39
|
+
issuance; this server validates resource-bound RFC 9068 access tokens on every
|
|
40
|
+
MCP request. It rejects malformed/query credentials, invalid tokens and
|
|
41
|
+
insufficient scopes with distinct 400/401/403 responses.
|
|
42
|
+
|
|
43
|
+
Optional resource-server integrations are selected explicitly:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
MCP_AUTH_EXTENSIONS=enterprise-managed-authorization@1.0.0,oauth-client-credentials@1.0.0
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Enterprise-managed authorization is stable upstream; client credentials is a
|
|
50
|
+
draft extension. Both preserve the core token verifier and require matching
|
|
51
|
+
Auth0 capabilities and separately provisioned client grants. Integration versions
|
|
52
|
+
are independent of the MCP wire version. Embedders can use the exported
|
|
53
|
+
`qyl-mcp-server/auth` composition API for additional declarative modules and
|
|
54
|
+
explicit scope hierarchies.
|
|
55
|
+
|
|
56
|
+
See the [deployment and authentication guide](https://github.com/ANcpLua/qyl.mcp#authentication)
|
|
57
|
+
for Auth0 configuration, extension responsibilities, and verification. Auth0
|
|
58
|
+
requires Enterprise for `private_key_jwt`. Neither a provider capability flag nor
|
|
59
|
+
an enabled module proves a client has been provisioned.
|
|
60
|
+
|
|
33
61
|
## Tools
|
|
34
62
|
|
|
35
63
|
**Telemetry** — `list_traces`, `get_trace`, `list_sessions`, `search_logs`,
|
|
36
64
|
`ci_log`, `display_traces`, `display_mcp_dashboard`.
|
|
37
65
|
|
|
66
|
+
`get_trace` returns every span by default. `errors_only` selects error spans;
|
|
67
|
+
`max_spans` (1–1000) caps the result after filtering in Collector order.
|
|
68
|
+
`include_attributes=false` omits span, resource, event, link and instrumentation
|
|
69
|
+
scope attribute collections, including any returned root-span copy. Trace
|
|
70
|
+
totals remain the Collector's totals; the text reports matching and returned
|
|
71
|
+
counts. `ci_log.service_prefix` is case-sensitive, defaults to `qyl-ci`, and
|
|
72
|
+
filters both recent runs and individual phase spans.
|
|
73
|
+
|
|
38
74
|
**Metrics** — `list_metrics`, `get_metric_series`, `query_metric`. Read in that
|
|
39
75
|
order: the catalog gives you an exact instrument name and how many attribute
|
|
40
76
|
streams it has, series discovery tells you which attribute keys are worth
|
|
@@ -58,18 +94,20 @@ in-flight collector fetch instead of waiting out its timeout. Progress: a
|
|
|
58
94
|
request that carries a `progressToken` gets `notifications/progress` per
|
|
59
95
|
completed step, one per collector round trip plus a final "Result ready"
|
|
60
96
|
(`ci_log` with a `run_id` reports the flatten into legs as a step of its own).
|
|
61
|
-
Logging: the server declares
|
|
62
|
-
`notifications/message
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
the deprecation window beside stderr and OpenTelemetry.
|
|
97
|
+
Logging: the server declares no `logging` capability and sends no
|
|
98
|
+
`notifications/message`, including when a client supplies a log level.
|
|
99
|
+
Operational error diagnostics go to stderr; native tool timing/status telemetry
|
|
100
|
+
uses OpenTelemetry. Requested progress and cancellation remain available.
|
|
101
|
+
[Source and test commands/output, 2026-10-08](https://github.com/ANcpLua/qyl.mcp/blob/main/docs/evidence/2026-10-08-followup-01.md).
|
|
102
|
+
|
|
68
103
|
|
|
69
104
|
Every inbound `tools/call` on a local server — `--stdio`, or HTTP without
|
|
70
|
-
`MCP_PUBLIC_URL` —
|
|
71
|
-
|
|
72
|
-
`~/.qyl/mcp-native-executions.json`.
|
|
105
|
+
`MCP_PUBLIC_URL` — records only its tool name, timing, status and error type,
|
|
106
|
+
plus a server-generated execution ID, atomically in
|
|
107
|
+
`~/.qyl/mcp-native-executions.json`. Arguments, request metadata, result bodies
|
|
108
|
+
and error messages are not retained. Known version-1/2 files are reduced to
|
|
109
|
+
this format on load; unreadable files still use the existing archive recovery
|
|
110
|
+
path, and pre-existing archives are not purged. A public deployment (`MCP_PUBLIC_URL` set)
|
|
73
111
|
records nothing to disk; that evidence file is a local developer artifact, not a
|
|
74
112
|
multi-tenant audit log.
|
|
75
113
|
|
|
@@ -80,11 +118,15 @@ multi-tenant audit log.
|
|
|
80
118
|
| `QYL_COLLECTOR_URL` | Collector read API base; default `http://127.0.0.1:5100`. Also the OTLP base when set. |
|
|
81
119
|
| `QYL_API_KEY` | Collector read and OTLP credential. Outgoing only. |
|
|
82
120
|
| `QYL_PROJECT` | Server-owned collector project scope; defaults to `default`. Never accepted as tool input. |
|
|
121
|
+
| `MCP_COLLECTOR_PROJECTS` | Optional secret JSON mapping validated Auth0 subjects to project-bound Collector keys; see account isolation below. |
|
|
83
122
|
| `QYL_OTLP_ENDPOINT` | Optional OTLP base for self-telemetry. |
|
|
84
123
|
| `QYL_DEMO=1` | Explicit, visibly labelled demo telemetry. A collector failure never silently substitutes demo data. |
|
|
85
124
|
| `QYL_MCP_TELEMETRY=0` | Disable MCP spans, metrics, and operation logs. |
|
|
86
|
-
| `QYL_MCP_CAPTURE_CONTENT=1` |
|
|
125
|
+
| `QYL_MCP_CAPTURE_CONTENT=1` | Workbench content capture; native incoming tool-call telemetry never supplies request or response bodies. |
|
|
87
126
|
| `QYL_MCP_NATIVE_STATE_PATH` | Override the native execution-evidence path. |
|
|
127
|
+
| `MCP_EVENTS_STORE` | Persistent Events subscription file; requires Auth0 and the access-check credentials below. |
|
|
128
|
+
| `MCP_EVENTS_AUTH0_CLIENT_ID` / `MCP_EVENTS_AUTH0_CLIENT_SECRET` | Dedicated Management API application with `read:users`, `read:clients`, `read:client_grants`, `read:grants`; resolves CIMD identities and keeps access checks current without storing subscriber tokens. |
|
|
129
|
+
| `MCP_EVENTS_POLL_MS` | Collector polling interval, at least 5000 ms; default 30000 ms. |
|
|
88
130
|
| `PORT` | HTTP listener port; default `3001`. |
|
|
89
131
|
|
|
90
132
|
Secrets are redacted before results reach the model, structured content, or
|
|
@@ -94,8 +136,69 @@ durable evidence.
|
|
|
94
136
|
The HTTP entry is a web-standard fetch handler served by its default export,
|
|
95
137
|
so serving it requires Bun.
|
|
96
138
|
|
|
139
|
+
### Account isolation for hosted Auth0
|
|
140
|
+
|
|
141
|
+
To give a reviewer access to sample data, provision a separate Collector
|
|
142
|
+
project/key through the Collector's `QYL_OTLP_PROJECT_KEYS` configuration and
|
|
143
|
+
assign that key to the reviewer's validated Auth0 subject. Configure the MCP
|
|
144
|
+
service's secret `MCP_COLLECTOR_PROJECTS` with this shape (placeholder values):
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
[
|
|
148
|
+
{ "project": "default", "apiKey": "OWNER_COLLECTOR_KEY", "subjects": ["auth0|owner-example"] },
|
|
149
|
+
{ "project": "review", "apiKey": "REVIEW_COLLECTOR_KEY", "subjects": ["auth0|reviewer-example"] }
|
|
150
|
+
]
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Include every account that should retain access, including the owner. An
|
|
154
|
+
enabled map rejects unmapped callers and never falls back to `QYL_API_KEY`.
|
|
155
|
+
Missing, malformed, duplicate or conflicting entries fail validation. The
|
|
156
|
+
feature requires the hosted Auth0 configuration; local stdio and Cloudflare
|
|
157
|
+
Access deployments cannot enable this map. When the variable is absent, the
|
|
158
|
+
existing deployment-wide credential behavior is retained.
|
|
159
|
+
|
|
160
|
+
Only the verified SDK `AuthInfo.extra.subject` selects the project. Tool inputs
|
|
161
|
+
and request metadata cannot override it. All telemetry tools, viewer refreshes
|
|
162
|
+
and Events queries use that account's project credential. Removing the account
|
|
163
|
+
assignment revokes its Events subscription on the next access check. Project
|
|
164
|
+
keys are redacted from text and structured results, including keys nested in
|
|
165
|
+
this JSON configuration. Keep the map in host secrets and out of the plugin ZIP.
|
|
166
|
+
|
|
167
|
+
`bun run smoke:projects` from the repository root checks all eleven tools,
|
|
168
|
+
viewer query paths, both protocol eras and Events against a real local
|
|
169
|
+
Collector with two temporary projects. Set `QYL_COLLECTOR_PROJECT` to its
|
|
170
|
+
`.csproj` when using a different checkout. The script uses local test identities
|
|
171
|
+
and an in-process signed webhook receiver; the hosted reviewer OAuth login and
|
|
172
|
+
real ChatGPT demonstration are separate checks before upload.
|
|
173
|
+
|
|
97
174
|
## Release notes
|
|
98
175
|
|
|
176
|
+
### 7.2.0
|
|
177
|
+
|
|
178
|
+
- Removed deprecated MCP logging capability and per-call log notifications;
|
|
179
|
+
retained requested progress, cancellation and native operation telemetry.
|
|
180
|
+
[Dated verification, 2026-10-08](https://github.com/ANcpLua/qyl.mcp/blob/main/docs/evidence/2026-10-08-followup-01.md).
|
|
181
|
+
|
|
182
|
+
- Optional Auth0-account-to-Collector project isolation, also applied to Events.
|
|
183
|
+
- Events share polling for accounts assigned to the same project; independent
|
|
184
|
+
projects are polled concurrently. Invalid stdio project configuration fails
|
|
185
|
+
before accepting a connection.
|
|
186
|
+
- Workbench telemetry recognizes the SDK's per-request server factory so native
|
|
187
|
+
tool spans are not duplicated by the surrounding protocol journal, while
|
|
188
|
+
failed server construction still produces a correlated failure span.
|
|
189
|
+
|
|
190
|
+
### 7.1.1
|
|
191
|
+
|
|
192
|
+
- SDK v2.3.1 serving defaults accept modern and 2025-era tool clients over HTTP
|
|
193
|
+
and stdio. The workbench negotiates automatically. The strict policy described
|
|
194
|
+
under 6.0.0 below has been removed.
|
|
195
|
+
- Events recheck account status, explicit client access, user permissions and
|
|
196
|
+
consent through Auth0. Unsubscribe aborts outstanding deliveries; restart
|
|
197
|
+
cleanup removes expired subscriptions and obsolete signing keys.
|
|
198
|
+
- Public viewers declare their deployment origin and keep the requested trace
|
|
199
|
+
limit on refresh. Release verification exercises a fresh npm consumer with
|
|
200
|
+
modern and 2025-era discovery and a real demo-tool call.
|
|
201
|
+
|
|
99
202
|
### 7.1.0
|
|
100
203
|
|
|
101
204
|
- The hosted server can sit behind Cloudflare Access Managed OAuth:
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { OAuthMetadata } from "@modelcontextprotocol/server";
|
|
2
|
+
/** Resource-server integrations, versioned separately from the MCP wire revision.
|
|
3
|
+
* Extensions declare provider requirements; they cannot replace token verification,
|
|
4
|
+
* mint tokens, rewrite discovery, or grant scopes to a caller.
|
|
5
|
+
*/
|
|
6
|
+
export interface AuthorizationExtension {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly version: string;
|
|
9
|
+
readonly stability: "stable" | "draft";
|
|
10
|
+
readonly specification: string;
|
|
11
|
+
readonly requirements: Readonly<Record<string, true | readonly string[]>>;
|
|
12
|
+
}
|
|
13
|
+
export declare const enterpriseManagedAuthorization: AuthorizationExtension;
|
|
14
|
+
export declare const clientCredentialsAuthorization: AuthorizationExtension;
|
|
15
|
+
export declare const authorizationExtensions: readonly AuthorizationExtension[];
|
|
16
|
+
/** Explicit version pins are mandatory, including for draft integrations. */
|
|
17
|
+
export declare function resolveAuthorizationExtensions(selections: readonly string[], available?: readonly AuthorizationExtension[]): readonly AuthorizationExtension[];
|
|
18
|
+
export declare function readAuthorizationExtensions(environment: Readonly<Record<string, string | undefined>>): readonly AuthorizationExtension[];
|
|
19
|
+
/** Union requirements deterministically; a composition can only add requirements. */
|
|
20
|
+
export declare function validateAuthorizationExtensions(metadata: OAuthMetadata, extensions: readonly AuthorizationExtension[]): void;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export const enterpriseManagedAuthorization = {
|
|
2
|
+
id: "enterprise-managed-authorization",
|
|
3
|
+
version: "1.0.0",
|
|
4
|
+
stability: "stable",
|
|
5
|
+
specification: "https://github.com/modelcontextprotocol/ext-auth/blob/main/specification/stable/enterprise-managed-authorization.mdx",
|
|
6
|
+
requirements: {
|
|
7
|
+
grant_types_supported: ["urn:ietf:params:oauth:grant-type:jwt-bearer"],
|
|
8
|
+
authorization_grant_profiles_supported: ["urn:ietf:params:oauth:grant-profile:id-jag"],
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
export const clientCredentialsAuthorization = {
|
|
12
|
+
id: "oauth-client-credentials",
|
|
13
|
+
version: "1.0.0",
|
|
14
|
+
stability: "draft",
|
|
15
|
+
specification: "https://github.com/modelcontextprotocol/ext-auth/blob/main/specification/draft/oauth-client-credentials.mdx",
|
|
16
|
+
requirements: {
|
|
17
|
+
grant_types_supported: ["client_credentials"],
|
|
18
|
+
token_endpoint_auth_methods_supported: ["private_key_jwt"],
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
export const authorizationExtensions = [
|
|
22
|
+
enterpriseManagedAuthorization,
|
|
23
|
+
clientCredentialsAuthorization,
|
|
24
|
+
];
|
|
25
|
+
/** Explicit version pins are mandatory, including for draft integrations. */
|
|
26
|
+
export function resolveAuthorizationExtensions(selections, available = authorizationExtensions) {
|
|
27
|
+
const resolved = selections.map((selection) => {
|
|
28
|
+
const matches = available.filter((entry) => `${entry.id}@${entry.version}` === selection);
|
|
29
|
+
if (matches.length > 1)
|
|
30
|
+
throw new Error(`Ambiguous authorization extension registration: ${selection}`);
|
|
31
|
+
const extension = matches[0];
|
|
32
|
+
if (!extension)
|
|
33
|
+
throw new Error(`Unknown authorization extension/version: ${selection}`);
|
|
34
|
+
return extension;
|
|
35
|
+
});
|
|
36
|
+
assertUniqueExtensions(resolved);
|
|
37
|
+
return resolved;
|
|
38
|
+
}
|
|
39
|
+
export function readAuthorizationExtensions(environment) {
|
|
40
|
+
const configured = environment.MCP_AUTH_EXTENSIONS?.trim();
|
|
41
|
+
if (!configured)
|
|
42
|
+
return [];
|
|
43
|
+
return resolveAuthorizationExtensions(configured.split(",").map((value) => value.trim()));
|
|
44
|
+
}
|
|
45
|
+
function assertUniqueExtensions(extensions) {
|
|
46
|
+
const ids = new Set();
|
|
47
|
+
for (const extension of extensions) {
|
|
48
|
+
if (!/^[a-z][a-z0-9-]+$/u.test(extension.id)
|
|
49
|
+
|| !/^\d+\.\d+\.\d+$/u.test(extension.version)) {
|
|
50
|
+
throw new Error("Authorization extensions require an ID and an exact integration version");
|
|
51
|
+
}
|
|
52
|
+
if (ids.has(extension.id))
|
|
53
|
+
throw new Error(`Duplicate authorization extension: ${extension.id}`);
|
|
54
|
+
ids.add(extension.id);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Union requirements deterministically; a composition can only add requirements. */
|
|
58
|
+
export function validateAuthorizationExtensions(metadata, extensions) {
|
|
59
|
+
assertUniqueExtensions(extensions);
|
|
60
|
+
const requirements = new Map();
|
|
61
|
+
for (const extension of extensions) {
|
|
62
|
+
for (const [field, expected] of Object.entries(extension.requirements)) {
|
|
63
|
+
const existing = requirements.get(field);
|
|
64
|
+
if (existing !== undefined && (existing === true) !== (expected === true)) {
|
|
65
|
+
throw new Error(`Conflicting authorization extension requirements for ${field}`);
|
|
66
|
+
}
|
|
67
|
+
if (expected === true)
|
|
68
|
+
requirements.set(field, true);
|
|
69
|
+
else
|
|
70
|
+
requirements.set(field, new Set([...(existing instanceof Set ? existing : []), ...expected]));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (const [field, expected] of [...requirements].sort(([a], [b]) => a.localeCompare(b))) {
|
|
74
|
+
const actual = metadata[field];
|
|
75
|
+
if (expected === true ? actual !== true
|
|
76
|
+
: !Array.isArray(actual) || ![...expected].every((value) => actual.includes(value))) {
|
|
77
|
+
throw new Error(`Authorization Server does not satisfy enabled extension requirement: ${field}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=auth-extensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-extensions.js","sourceRoot":"","sources":["../src/auth-extensions.ts"],"names":[],"mappings":"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"}
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Public composition surface for hosted resource authorization. */
|
|
2
|
+
export { createResourceAuthorization, type ResourceAuthorizationOptions } from "./authorization.js";
|
|
3
|
+
export { authorizationExtensions, clientCredentialsAuthorization, enterpriseManagedAuthorization, readAuthorizationExtensions, resolveAuthorizationExtensions, validateAuthorizationExtensions, type AuthorizationExtension, } from "./auth-extensions.js";
|
|
4
|
+
export { createJwtTokenVerifier, loadHostedOAuth, type HostedOAuth } from "./oauth.js";
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Public composition surface for hosted resource authorization. */
|
|
2
|
+
export { createResourceAuthorization } from "./authorization.js";
|
|
3
|
+
export { authorizationExtensions, clientCredentialsAuthorization, enterpriseManagedAuthorization, readAuthorizationExtensions, resolveAuthorizationExtensions, validateAuthorizationExtensions, } from "./auth-extensions.js";
|
|
4
|
+
export { createJwtTokenVerifier, loadHostedOAuth } from "./oauth.js";
|
|
5
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,OAAO,EAAE,2BAA2B,EAAqC,MAAM,oBAAoB,CAAC;AACpG,OAAO,EACL,uBAAuB,EACvB,8BAA8B,EAC9B,8BAA8B,EAC9B,2BAA2B,EAC3B,8BAA8B,EAC9B,+BAA+B,GAEhC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAoB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type AuthInfo, type OAuthTokenVerifier } from "@modelcontextprotocol/server";
|
|
2
|
+
export interface ResourceAuthorizationOptions {
|
|
3
|
+
readonly verifier: OAuthTokenVerifier;
|
|
4
|
+
readonly resourceMetadataUrl: string;
|
|
5
|
+
readonly requiredScopes: readonly string[];
|
|
6
|
+
/** Explicit implications only; a wildcard-looking string grants nothing by itself. */
|
|
7
|
+
readonly scopeImplications?: Readonly<Record<string, readonly string[]>>;
|
|
8
|
+
}
|
|
9
|
+
/** A hierarchy is an explicit, transitive relation. Cycles terminate and never
|
|
10
|
+
* grant a scope without a verified scope as their starting point.
|
|
11
|
+
*/
|
|
12
|
+
export declare function scopeClosure(granted: readonly string[], implications: ReadonlyMap<string, readonly string[]>): ReadonlySet<string>;
|
|
13
|
+
/** All grants converge on this gate. Extensions never see an unverified token. */
|
|
14
|
+
export declare function createResourceAuthorization(options: ResourceAuthorizationOptions): (request: Request) => Promise<AuthInfo | Response>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { requireBearerAuth, } from "@modelcontextprotocol/server";
|
|
2
|
+
function validateScopes(scopes) {
|
|
3
|
+
for (const scope of scopes) {
|
|
4
|
+
// RFC 6749 scope-token excludes quotes, backslashes and whitespace.
|
|
5
|
+
if (!/^[\x21\x23-\x5B\x5D-\x7E]+$/u.test(scope) || scope === "offline_access") {
|
|
6
|
+
throw new Error("Resource scopes must be valid OAuth scope tokens and cannot include offline_access");
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/** A hierarchy is an explicit, transitive relation. Cycles terminate and never
|
|
11
|
+
* grant a scope without a verified scope as their starting point.
|
|
12
|
+
*/
|
|
13
|
+
export function scopeClosure(granted, implications) {
|
|
14
|
+
const result = new Set(granted);
|
|
15
|
+
const pending = [...granted];
|
|
16
|
+
for (let i = 0; i < pending.length; i++) {
|
|
17
|
+
for (const implied of implications.get(pending[i]) ?? []) {
|
|
18
|
+
if (!result.has(implied)) {
|
|
19
|
+
result.add(implied);
|
|
20
|
+
pending.push(implied);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
/** All grants converge on this gate. Extensions never see an unverified token. */
|
|
27
|
+
export function createResourceAuthorization(options) {
|
|
28
|
+
const requiredScopes = [...new Set(options.requiredScopes)].sort();
|
|
29
|
+
validateScopes(requiredScopes);
|
|
30
|
+
const implications = new Map(Object.entries(options.scopeImplications ?? {})
|
|
31
|
+
.map(([scope, implied]) => {
|
|
32
|
+
validateScopes([scope, ...implied]);
|
|
33
|
+
return [scope, [...implied]];
|
|
34
|
+
}));
|
|
35
|
+
const metadataUrl = new URL(options.resourceMetadataUrl);
|
|
36
|
+
if (metadataUrl.protocol !== "https:" || metadataUrl.username || metadataUrl.password
|
|
37
|
+
|| metadataUrl.search || metadataUrl.hash || /["\\]/u.test(options.resourceMetadataUrl)
|
|
38
|
+
|| Array.from(options.resourceMetadataUrl).some((character) => character.charCodeAt(0) <= 0x20)) {
|
|
39
|
+
throw new Error("Resource metadata must use a safe HTTPS URL");
|
|
40
|
+
}
|
|
41
|
+
const coreGate = requireBearerAuth({
|
|
42
|
+
verifier: options.verifier,
|
|
43
|
+
resourceMetadataUrl: metadataUrl.href,
|
|
44
|
+
});
|
|
45
|
+
const challenge = (status, error) => {
|
|
46
|
+
const parameters = [
|
|
47
|
+
...(error === undefined ? [] : [`error="${error}"`]),
|
|
48
|
+
...(requiredScopes.length === 0 ? [] : [`scope="${requiredScopes.join(" ")}"`]),
|
|
49
|
+
`resource_metadata="${metadataUrl.href}"`,
|
|
50
|
+
];
|
|
51
|
+
return Response.json({ error: error ?? "unauthorized" }, {
|
|
52
|
+
status,
|
|
53
|
+
headers: { "WWW-Authenticate": `Bearer ${parameters.join(", ")}`, "Cache-Control": "no-store" },
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
return async (request) => {
|
|
57
|
+
const authorization = request.headers.get("authorization");
|
|
58
|
+
const bearer = authorization === null ? null : /^Bearer +([A-Za-z0-9._~+/-]+=*)$/iu.exec(authorization);
|
|
59
|
+
if (new URL(request.url).searchParams.has("access_token")
|
|
60
|
+
|| (authorization !== null && bearer === null)) {
|
|
61
|
+
return challenge(400, "invalid_request");
|
|
62
|
+
}
|
|
63
|
+
// RFC 6750 permits multiple spaces. The SDK splits on one space; normalize
|
|
64
|
+
// the validated header on a bodyless request so the MCP body remains unread.
|
|
65
|
+
const auth = await coreGate(bearer === null ? request : new Request(request.url, {
|
|
66
|
+
headers: { authorization: `Bearer ${bearer[1]}` },
|
|
67
|
+
}));
|
|
68
|
+
if (auth instanceof Response) {
|
|
69
|
+
// Keep authentication failures distinct from authorization failures and
|
|
70
|
+
// include the complete operation scope set on every challenge.
|
|
71
|
+
if (auth.status === 401)
|
|
72
|
+
return challenge(401, authorization === null ? undefined : "invalid_token");
|
|
73
|
+
return auth;
|
|
74
|
+
}
|
|
75
|
+
const effectiveScopes = scopeClosure(auth.scopes, implications);
|
|
76
|
+
if (!requiredScopes.every((scope) => effectiveScopes.has(scope))) {
|
|
77
|
+
return challenge(403, "insufficient_scope");
|
|
78
|
+
}
|
|
79
|
+
return auth;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=authorization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorization.js","sourceRoot":"","sources":["../src/authorization.ts"],"names":[],"mappings":"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"}
|
package/dist/ci.d.ts
CHANGED
|
@@ -15,11 +15,11 @@ import type { McpServer } from "@modelcontextprotocol/server";
|
|
|
15
15
|
import type { Mode, QylSession, QylTrace } from "./wire.js";
|
|
16
16
|
/** Resource service-name prefix that marks telemetry as CI-emitted. */
|
|
17
17
|
export declare const CI_SERVICE_PREFIX = "qyl-ci";
|
|
18
|
-
/** CI sessions
|
|
19
|
-
export declare function filterCiSessions(sessions: QylSession[]): QylSession[];
|
|
18
|
+
/** CI sessions carry at least one service matching the requested prefix. */
|
|
19
|
+
export declare function filterCiSessions(sessions: QylSession[], servicePrefix?: string): QylSession[];
|
|
20
20
|
/** Flatten a run's traces into per-leg phases, failures first. */
|
|
21
|
-
export declare function collectCiPhases(traces: QylTrace[]): CiPhase[];
|
|
22
|
-
export declare function summarizeCiRuns(runs: CiRunSummary[], mode: Mode): string;
|
|
23
|
-
export declare function summarizeCiRun(runId: string, phases: CiPhase[], mode: Mode): string;
|
|
21
|
+
export declare function collectCiPhases(traces: QylTrace[], servicePrefix?: string): CiPhase[];
|
|
22
|
+
export declare function summarizeCiRuns(runs: CiRunSummary[], mode: Mode, servicePrefix?: string): string;
|
|
23
|
+
export declare function summarizeCiRun(runId: string, phases: CiPhase[], mode: Mode, servicePrefix?: string): string;
|
|
24
24
|
/** Register the CI dogfooding block. */
|
|
25
25
|
export declare function registerCiTools(server: McpServer): void;
|
package/dist/ci.js
CHANGED
|
@@ -2,12 +2,12 @@ import { CiLogInputSchema, CiLogOutputSchema, compactOutputSchema, } from "./con
|
|
|
2
2
|
import { fetchSessions, fetchSessionTraces } from "./data.js";
|
|
3
3
|
import { runTool } from "./request-scope.js";
|
|
4
4
|
import { telemetryToolResult } from "./telemetry-redaction.js";
|
|
5
|
-
import { READ_ONLY_TELEMETRY_TOOL_ANNOTATIONS } from "./tools.js";
|
|
5
|
+
import { READ_ONLY_TELEMETRY_TOOL_ANNOTATIONS, TELEMETRY_TOOL_AUTH_META } from "./tools.js";
|
|
6
6
|
/** Resource service-name prefix that marks telemetry as CI-emitted. */
|
|
7
7
|
export const CI_SERVICE_PREFIX = "qyl-ci";
|
|
8
|
-
/** CI sessions
|
|
9
|
-
export function filterCiSessions(sessions) {
|
|
10
|
-
return sessions.filter((session) => session.services.some((service) => service.startsWith(
|
|
8
|
+
/** CI sessions carry at least one service matching the requested prefix. */
|
|
9
|
+
export function filterCiSessions(sessions, servicePrefix = CI_SERVICE_PREFIX) {
|
|
10
|
+
return sessions.filter((session) => session.services.some((service) => service.startsWith(servicePrefix)));
|
|
11
11
|
}
|
|
12
12
|
function spanLeg(span) {
|
|
13
13
|
const attribute = span.attributes?.find((entry) => entry.key === "ci.leg");
|
|
@@ -16,8 +16,8 @@ function spanLeg(span) {
|
|
|
16
16
|
: span.resource.service_name;
|
|
17
17
|
}
|
|
18
18
|
/** Flatten a run's traces into per-leg phases, failures first. */
|
|
19
|
-
export function collectCiPhases(traces) {
|
|
20
|
-
const phases = traces.flatMap((trace) => trace.spans.map((span) => ({
|
|
19
|
+
export function collectCiPhases(traces, servicePrefix = CI_SERVICE_PREFIX) {
|
|
20
|
+
const phases = traces.flatMap((trace) => trace.spans.filter((span) => span.resource.service_name.startsWith(servicePrefix)).map((span) => ({
|
|
21
21
|
leg: spanLeg(span),
|
|
22
22
|
phase: span.name,
|
|
23
23
|
status: span.status.code === 2 ? "error" : span.status.code === 1 ? "ok" : "unset",
|
|
@@ -32,10 +32,10 @@ export function collectCiPhases(traces) {
|
|
|
32
32
|
? 1
|
|
33
33
|
: 0);
|
|
34
34
|
}
|
|
35
|
-
export function summarizeCiRuns(runs, mode) {
|
|
35
|
+
export function summarizeCiRuns(runs, mode, servicePrefix = CI_SERVICE_PREFIX) {
|
|
36
36
|
if (runs.length === 0) {
|
|
37
37
|
return `No CI runs found (${mode} mode). CI telemetry appears once runs emit ` +
|
|
38
|
-
`spans with a '${
|
|
38
|
+
`spans with a '${servicePrefix}*' service.name.`;
|
|
39
39
|
}
|
|
40
40
|
const lines = runs.map((run) => {
|
|
41
41
|
const failure = run.error_count > 0 ? `${run.error_count} error(s)` : "clean";
|
|
@@ -44,9 +44,9 @@ export function summarizeCiRuns(runs, mode) {
|
|
|
44
44
|
});
|
|
45
45
|
return `${runs.length} CI run(s) (${mode} mode), pass a run_id for the per-leg breakdown:\n${lines.join("\n")}`;
|
|
46
46
|
}
|
|
47
|
-
export function summarizeCiRun(runId, phases, mode) {
|
|
47
|
+
export function summarizeCiRun(runId, phases, mode, servicePrefix = CI_SERVICE_PREFIX) {
|
|
48
48
|
if (phases.length === 0) {
|
|
49
|
-
return `CI run ${runId} has no phase spans (${mode} mode).`;
|
|
49
|
+
return `CI run ${runId} has no phase spans matching '${servicePrefix}*' (${mode} mode).`;
|
|
50
50
|
}
|
|
51
51
|
const legs = new Map();
|
|
52
52
|
for (const phase of phases) {
|
|
@@ -71,29 +71,35 @@ export function summarizeCiRun(runId, phases, mode) {
|
|
|
71
71
|
export function registerCiTools(server) {
|
|
72
72
|
server.registerTool("ci_log", {
|
|
73
73
|
title: "CI Log",
|
|
74
|
-
description: "Read
|
|
75
|
-
"
|
|
76
|
-
`'${CI_SERVICE_PREFIX}')
|
|
77
|
-
"
|
|
74
|
+
description: "Read CI telemetry from qyl when the user wants recent runs or a per-leg " +
|
|
75
|
+
"phase breakdown with failures first. Any CI can emit this convention: " +
|
|
76
|
+
`resource service.name starts with service_prefix (default '${CI_SERVICE_PREFIX}'), session.id identifies ` +
|
|
77
|
+
"the run, and one span per phase carries a ci.leg attribute; failed phases " +
|
|
78
|
+
"set span status to error. Without run_id, filters the 50 most recent " +
|
|
79
|
+
"sessions and returns up to limit matching runs (default 10). With run_id, " +
|
|
80
|
+
"reads up to 100 traces for that session and returns only phases matching " +
|
|
81
|
+
"the same case-sensitive service prefix; phase output can be large.",
|
|
78
82
|
inputSchema: CiLogInputSchema,
|
|
79
83
|
outputSchema: compactOutputSchema(CiLogOutputSchema),
|
|
80
84
|
annotations: READ_ONLY_TELEMETRY_TOOL_ANNOTATIONS,
|
|
85
|
+
_meta: TELEMETRY_TOOL_AUTH_META,
|
|
81
86
|
}, (args, ctx) =>
|
|
82
87
|
// A run breakdown is two units a client can watch: the collector round
|
|
83
88
|
// trip for the run's traces, then the flatten into per-leg phases. The
|
|
84
89
|
// run list is one round trip.
|
|
85
|
-
runTool(ctx,
|
|
90
|
+
runTool(ctx, args.run_id ? 2 : 1, async (scope) => {
|
|
91
|
+
const servicePrefix = args.service_prefix ?? CI_SERVICE_PREFIX;
|
|
86
92
|
if (args.run_id) {
|
|
87
93
|
const { traces, mode } = await fetchSessionTraces(args.run_id, 100, scope.collector);
|
|
88
94
|
await scope.step(`Fetched ${traces.length} trace(s) of CI run ${args.run_id}`);
|
|
89
|
-
const phases = collectCiPhases(traces);
|
|
95
|
+
const phases = collectCiPhases(traces, servicePrefix);
|
|
90
96
|
const legs = new Set(phases.map((phase) => phase.leg)).size;
|
|
91
97
|
await scope.step(`Collected ${phases.length} phase(s) across ${legs} leg(s)`);
|
|
92
98
|
const output = { run_id: args.run_id, phases, mode };
|
|
93
|
-
return telemetryToolResult(summarizeCiRun(args.run_id, phases, mode), output);
|
|
99
|
+
return telemetryToolResult(summarizeCiRun(args.run_id, phases, mode, servicePrefix), output);
|
|
94
100
|
}
|
|
95
101
|
const { sessions, mode } = await fetchSessions(50, undefined, scope.collector);
|
|
96
|
-
const runs = filterCiSessions(sessions)
|
|
102
|
+
const runs = filterCiSessions(sessions, servicePrefix)
|
|
97
103
|
.slice(0, args.limit ?? 10)
|
|
98
104
|
.map((session) => ({
|
|
99
105
|
run_id: session["session_id"],
|
|
@@ -105,7 +111,7 @@ export function registerCiTools(server) {
|
|
|
105
111
|
}));
|
|
106
112
|
await scope.step(`Found ${runs.length} CI run(s) among ${sessions.length} session(s)`);
|
|
107
113
|
const output = { runs, mode };
|
|
108
|
-
return telemetryToolResult(summarizeCiRuns(runs, mode), output);
|
|
114
|
+
return telemetryToolResult(summarizeCiRuns(runs, mode, servicePrefix), output);
|
|
109
115
|
}));
|
|
110
116
|
}
|
|
111
117
|
//# sourceMappingURL=ci.js.map
|
package/dist/ci.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ci.js","sourceRoot":"","sources":["../src/ci.ts"],"names":[],"mappings":"AAmBA,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,oCAAoC,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"ci.js","sourceRoot":"","sources":["../src/ci.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** A project-bound Collector credential, supplied only by host configuration. */
|
|
2
|
+
export interface CollectorAccess {
|
|
3
|
+
readonly project: string;
|
|
4
|
+
readonly apiKey: string;
|
|
5
|
+
}
|
|
6
|
+
/** Safe to return to a caller; never includes subjects, projects or credentials. */
|
|
7
|
+
export declare class CollectorAccessError extends Error {
|
|
8
|
+
constructor();
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Opt-in subject-to-project access. Absence retains the single-project setup;
|
|
12
|
+
* present-but-invalid configuration and unassigned callers never fall back.
|
|
13
|
+
* Values stay in the host's secret configuration, not in tool arguments.
|
|
14
|
+
*/
|
|
15
|
+
export declare function readCollectorProjects(environment?: Readonly<Record<string, string | undefined>>): ReadonlyMap<string, CollectorAccess> | undefined;
|
|
16
|
+
/** Pass only the subject from verified SDK AuthInfo, never request metadata. */
|
|
17
|
+
export declare function collectorAccessForSubject(subject: unknown, environment?: Readonly<Record<string, string | undefined>>): CollectorAccess | undefined;
|