@qase/mcp-server 1.1.8 → 2.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.
- package/CHANGELOG.md +25 -0
- package/README.md +75 -542
- package/build/auth/client-context.d.ts +12 -0
- package/build/auth/client-context.d.ts.map +1 -0
- package/build/auth/client-context.js +12 -0
- package/build/auth/client-context.js.map +1 -0
- package/build/auth/jwks-verifier.d.ts +15 -0
- package/build/auth/jwks-verifier.d.ts.map +1 -0
- package/build/auth/jwks-verifier.js +31 -0
- package/build/auth/jwks-verifier.js.map +1 -0
- package/build/auth/mcp-guard.d.ts +15 -0
- package/build/auth/mcp-guard.d.ts.map +1 -0
- package/build/auth/mcp-guard.js +44 -0
- package/build/auth/mcp-guard.js.map +1 -0
- package/build/auth/oauth-config.d.ts +40 -0
- package/build/auth/oauth-config.d.ts.map +1 -0
- package/build/auth/oauth-config.js +34 -0
- package/build/auth/oauth-config.js.map +1 -0
- package/build/auth/proxy-provider.d.ts +31 -0
- package/build/auth/proxy-provider.d.ts.map +1 -0
- package/build/auth/proxy-provider.js +87 -0
- package/build/auth/proxy-provider.js.map +1 -0
- package/build/auth/token-type.d.ts +7 -0
- package/build/auth/token-type.d.ts.map +1 -0
- package/build/auth/token-type.js +10 -0
- package/build/auth/token-type.js.map +1 -0
- package/build/cache/circuit-breaker.d.ts +33 -0
- package/build/cache/circuit-breaker.d.ts.map +1 -0
- package/build/cache/circuit-breaker.js +68 -0
- package/build/cache/circuit-breaker.js.map +1 -0
- package/build/cache/index.d.ts +24 -0
- package/build/cache/index.d.ts.map +1 -0
- package/build/cache/index.js +86 -0
- package/build/cache/index.js.map +1 -0
- package/build/cache/keys.d.ts +25 -0
- package/build/cache/keys.d.ts.map +1 -0
- package/build/cache/keys.js +36 -0
- package/build/cache/keys.js.map +1 -0
- package/build/cache/layered.d.ts +31 -0
- package/build/cache/layered.d.ts.map +1 -0
- package/build/cache/layered.js +88 -0
- package/build/cache/layered.js.map +1 -0
- package/build/cache/memory.d.ts +32 -0
- package/build/cache/memory.d.ts.map +1 -0
- package/build/cache/memory.js +108 -0
- package/build/cache/memory.js.map +1 -0
- package/build/cache/metrics.d.ts +31 -0
- package/build/cache/metrics.d.ts.map +1 -0
- package/build/cache/metrics.js +95 -0
- package/build/cache/metrics.js.map +1 -0
- package/build/cache/prom-format.d.ts +7 -0
- package/build/cache/prom-format.d.ts.map +1 -0
- package/build/cache/prom-format.js +16 -0
- package/build/cache/prom-format.js.map +1 -0
- package/build/cache/redis-bus.d.ts +29 -0
- package/build/cache/redis-bus.d.ts.map +1 -0
- package/build/cache/redis-bus.js +50 -0
- package/build/cache/redis-bus.js.map +1 -0
- package/build/cache/redis.d.ts +32 -0
- package/build/cache/redis.d.ts.map +1 -0
- package/build/cache/redis.js +89 -0
- package/build/cache/redis.js.map +1 -0
- package/build/cache/types.d.ts +38 -0
- package/build/cache/types.d.ts.map +1 -0
- package/build/cache/types.js +2 -0
- package/build/cache/types.js.map +1 -0
- package/build/client/index.d.ts +10 -3
- package/build/client/index.d.ts.map +1 -1
- package/build/client/index.js +75 -50
- package/build/client/index.js.map +1 -1
- package/build/http/agent.d.ts +16 -0
- package/build/http/agent.d.ts.map +1 -0
- package/build/http/agent.js +16 -0
- package/build/http/agent.js.map +1 -0
- package/build/http/dedupe.d.ts +14 -0
- package/build/http/dedupe.d.ts.map +1 -0
- package/build/http/dedupe.js +69 -0
- package/build/http/dedupe.js.map +1 -0
- package/build/http/index.d.ts +6 -0
- package/build/http/index.d.ts.map +1 -0
- package/build/http/index.js +4 -0
- package/build/http/index.js.map +1 -0
- package/build/http/retry.d.ts +18 -0
- package/build/http/retry.d.ts.map +1 -0
- package/build/http/retry.js +52 -0
- package/build/http/retry.js.map +1 -0
- package/build/index.d.ts +1 -18
- package/build/index.d.ts.map +1 -1
- package/build/index.js +95 -63
- package/build/index.js.map +1 -1
- package/build/operations-v2/composites/ci-report.d.ts +2 -0
- package/build/operations-v2/composites/ci-report.d.ts.map +1 -0
- package/build/operations-v2/composites/ci-report.js +99 -0
- package/build/operations-v2/composites/ci-report.js.map +1 -0
- package/build/operations-v2/composites/regression-run.d.ts +2 -0
- package/build/operations-v2/composites/regression-run.d.ts.map +1 -0
- package/build/operations-v2/composites/regression-run.js +81 -0
- package/build/operations-v2/composites/regression-run.js.map +1 -0
- package/build/operations-v2/composites/triage-defect.d.ts +2 -0
- package/build/operations-v2/composites/triage-defect.d.ts.map +1 -0
- package/build/operations-v2/composites/triage-defect.js +67 -0
- package/build/operations-v2/composites/triage-defect.js.map +1 -0
- package/build/operations-v2/escape/api.d.ts +2 -0
- package/build/operations-v2/escape/api.d.ts.map +1 -0
- package/build/operations-v2/escape/api.js +34 -0
- package/build/operations-v2/escape/api.js.map +1 -0
- package/build/operations-v2/index.d.ts +19 -0
- package/build/operations-v2/index.d.ts.map +1 -0
- package/build/operations-v2/index.js +25 -0
- package/build/operations-v2/index.js.map +1 -0
- package/build/operations-v2/meta/discover.d.ts +9 -0
- package/build/operations-v2/meta/discover.d.ts.map +1 -0
- package/build/operations-v2/meta/discover.js +76 -0
- package/build/operations-v2/meta/discover.js.map +1 -0
- package/build/{operations/search.d.ts → operations-v2/qql/index.d.ts} +1 -1
- package/build/operations-v2/qql/index.d.ts.map +1 -0
- package/build/{operations/search.js → operations-v2/qql/index.js} +34 -9
- package/build/operations-v2/qql/index.js.map +1 -0
- package/build/operations-v2/read/get.d.ts +2 -0
- package/build/operations-v2/read/get.d.ts.map +1 -0
- package/build/operations-v2/read/get.js +91 -0
- package/build/operations-v2/read/get.js.map +1 -0
- package/build/operations-v2/read/project-context.d.ts +2 -0
- package/build/operations-v2/read/project-context.d.ts.map +1 -0
- package/build/operations-v2/read/project-context.js +102 -0
- package/build/operations-v2/read/project-context.js.map +1 -0
- package/build/operations-v2/write/attachments.d.ts +2 -0
- package/build/operations-v2/write/attachments.d.ts.map +1 -0
- package/build/operations-v2/write/attachments.js +58 -0
- package/build/operations-v2/write/attachments.js.map +1 -0
- package/build/operations-v2/write/cases.d.ts +2 -0
- package/build/operations-v2/write/cases.d.ts.map +1 -0
- package/build/operations-v2/write/cases.js +121 -0
- package/build/operations-v2/write/cases.js.map +1 -0
- package/build/operations-v2/write/defects.d.ts +2 -0
- package/build/operations-v2/write/defects.d.ts.map +1 -0
- package/build/operations-v2/write/defects.js +81 -0
- package/build/operations-v2/write/defects.js.map +1 -0
- package/build/operations-v2/write/environments.d.ts +2 -0
- package/build/operations-v2/write/environments.d.ts.map +1 -0
- package/build/operations-v2/write/environments.js +56 -0
- package/build/operations-v2/write/environments.js.map +1 -0
- package/build/operations-v2/write/milestones.d.ts +2 -0
- package/build/operations-v2/write/milestones.d.ts.map +1 -0
- package/build/operations-v2/write/milestones.js +56 -0
- package/build/operations-v2/write/milestones.js.map +1 -0
- package/build/operations-v2/write/plans.d.ts +2 -0
- package/build/operations-v2/write/plans.d.ts.map +1 -0
- package/build/operations-v2/write/plans.js +55 -0
- package/build/operations-v2/write/plans.js.map +1 -0
- package/build/operations-v2/write/results.d.ts +2 -0
- package/build/operations-v2/write/results.d.ts.map +1 -0
- package/build/operations-v2/write/results.js +72 -0
- package/build/operations-v2/write/results.js.map +1 -0
- package/build/operations-v2/write/runs.d.ts +2 -0
- package/build/operations-v2/write/runs.d.ts.map +1 -0
- package/build/operations-v2/write/runs.js +84 -0
- package/build/operations-v2/write/runs.js.map +1 -0
- package/build/operations-v2/write/shared-steps.d.ts +2 -0
- package/build/operations-v2/write/shared-steps.d.ts.map +1 -0
- package/build/operations-v2/write/shared-steps.js +60 -0
- package/build/operations-v2/write/shared-steps.js.map +1 -0
- package/build/operations-v2/write/suites.d.ts +2 -0
- package/build/operations-v2/write/suites.d.ts.map +1 -0
- package/build/operations-v2/write/suites.js +63 -0
- package/build/operations-v2/write/suites.js.map +1 -0
- package/build/prompts/index.d.ts +31 -0
- package/build/prompts/index.d.ts.map +1 -0
- package/build/prompts/index.js +229 -0
- package/build/prompts/index.js.map +1 -0
- package/build/transports/sse.d.ts.map +1 -1
- package/build/transports/sse.js +6 -0
- package/build/transports/sse.js.map +1 -1
- package/build/transports/streamableHttp.d.ts +6 -1
- package/build/transports/streamableHttp.d.ts.map +1 -1
- package/build/transports/streamableHttp.js +128 -16
- package/build/transports/streamableHttp.js.map +1 -1
- package/build/utils/auth-context.d.ts +8 -0
- package/build/utils/auth-context.d.ts.map +1 -1
- package/build/utils/auth-context.js +18 -0
- package/build/utils/auth-context.js.map +1 -1
- package/build/utils/case-enums.d.ts +5 -3
- package/build/utils/case-enums.d.ts.map +1 -1
- package/build/utils/case-enums.js +77 -59
- package/build/utils/case-enums.js.map +1 -1
- package/build/utils/output-schemas.d.ts +16 -0
- package/build/utils/output-schemas.d.ts.map +1 -0
- package/build/utils/output-schemas.js +82 -0
- package/build/utils/output-schemas.js.map +1 -0
- package/build/utils/registry.d.ts +42 -9
- package/build/utils/registry.d.ts.map +1 -1
- package/build/utils/registry.js +64 -11
- package/build/utils/registry.js.map +1 -1
- package/build/utils/response-shape.d.ts +23 -0
- package/build/utils/response-shape.d.ts.map +1 -0
- package/build/utils/response-shape.js +73 -0
- package/build/utils/response-shape.js.map +1 -0
- package/build/utils/rich-response.d.ts +58 -0
- package/build/utils/rich-response.d.ts.map +1 -0
- package/build/utils/rich-response.js +93 -0
- package/build/utils/rich-response.js.map +1 -0
- package/build/utils/server-context.d.ts +65 -0
- package/build/utils/server-context.d.ts.map +1 -0
- package/build/utils/server-context.js +67 -0
- package/build/utils/server-context.js.map +1 -0
- package/build/version.d.ts +1 -1
- package/build/version.js +1 -1
- package/docs/connect.md +81 -0
- package/docs/migration.md +351 -0
- package/docs/self-run.md +270 -0
- package/docs/tools.md +109 -0
- package/docs/troubleshooting.md +144 -0
- package/package.json +11 -2
- package/server.json +8 -2
- package/build/operations/attachments.d.ts +0 -8
- package/build/operations/attachments.d.ts.map +0 -1
- package/build/operations/attachments.js +0 -146
- package/build/operations/attachments.js.map +0 -1
- package/build/operations/authors.d.ts +0 -8
- package/build/operations/authors.d.ts.map +0 -1
- package/build/operations/authors.js +0 -70
- package/build/operations/authors.js.map +0 -1
- package/build/operations/cases.d.ts +0 -8
- package/build/operations/cases.d.ts.map +0 -1
- package/build/operations/cases.js +0 -370
- package/build/operations/cases.js.map +0 -1
- package/build/operations/configurations.d.ts +0 -10
- package/build/operations/configurations.d.ts.map +0 -1
- package/build/operations/configurations.js +0 -125
- package/build/operations/configurations.js.map +0 -1
- package/build/operations/custom-fields.d.ts +0 -8
- package/build/operations/custom-fields.d.ts.map +0 -1
- package/build/operations/custom-fields.js +0 -172
- package/build/operations/custom-fields.js.map +0 -1
- package/build/operations/defects.d.ts +0 -8
- package/build/operations/defects.d.ts.map +0 -1
- package/build/operations/defects.js +0 -256
- package/build/operations/defects.js.map +0 -1
- package/build/operations/environments.d.ts +0 -8
- package/build/operations/environments.d.ts.map +0 -1
- package/build/operations/environments.js +0 -154
- package/build/operations/environments.js.map +0 -1
- package/build/operations/milestones.d.ts +0 -8
- package/build/operations/milestones.d.ts.map +0 -1
- package/build/operations/milestones.js +0 -155
- package/build/operations/milestones.js.map +0 -1
- package/build/operations/plans.d.ts +0 -8
- package/build/operations/plans.d.ts.map +0 -1
- package/build/operations/plans.js +0 -158
- package/build/operations/plans.js.map +0 -1
- package/build/operations/projects.d.ts +0 -8
- package/build/operations/projects.d.ts.map +0 -1
- package/build/operations/projects.js +0 -192
- package/build/operations/projects.js.map +0 -1
- package/build/operations/results.d.ts +0 -8
- package/build/operations/results.d.ts.map +0 -1
- package/build/operations/results.js +0 -214
- package/build/operations/results.js.map +0 -1
- package/build/operations/runs.d.ts +0 -8
- package/build/operations/runs.d.ts.map +0 -1
- package/build/operations/runs.js +0 -224
- package/build/operations/runs.js.map +0 -1
- package/build/operations/search.d.ts.map +0 -1
- package/build/operations/search.js.map +0 -1
- package/build/operations/shared-parameters.d.ts +0 -10
- package/build/operations/shared-parameters.d.ts.map +0 -1
- package/build/operations/shared-parameters.js +0 -93
- package/build/operations/shared-parameters.js.map +0 -1
- package/build/operations/shared-steps.d.ts +0 -8
- package/build/operations/shared-steps.d.ts.map +0 -1
- package/build/operations/shared-steps.js +0 -160
- package/build/operations/shared-steps.js.map +0 -1
- package/build/operations/suites.d.ts +0 -8
- package/build/operations/suites.d.ts.map +0 -1
- package/build/operations/suites.js +0 -169
- package/build/operations/suites.js.map +0 -1
- package/build/operations/system-fields.d.ts +0 -10
- package/build/operations/system-fields.d.ts.map +0 -1
- package/build/operations/system-fields.js +0 -47
- package/build/operations/system-fields.js.map +0 -1
- package/build/operations/users.d.ts +0 -10
- package/build/operations/users.d.ts.map +0 -1
- package/build/operations/users.js +0 -78
- package/build/operations/users.js.map +0 -1
|
@@ -1,18 +1,45 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
3
|
+
import { mcpAuthRouter } from '@modelcontextprotocol/sdk/server/auth/router.js';
|
|
3
4
|
import { randomUUID } from 'crypto';
|
|
4
5
|
import { requestTokenStorage } from '../utils/auth-context.js';
|
|
6
|
+
import { getMetrics } from '../cache/index.js';
|
|
7
|
+
import { getOAuthConfig } from '../auth/oauth-config.js';
|
|
8
|
+
import { createJwksVerifier } from '../auth/jwks-verifier.js';
|
|
9
|
+
import { createProxyProvider } from '../auth/proxy-provider.js';
|
|
10
|
+
import { createMcpGuard } from '../auth/mcp-guard.js';
|
|
11
|
+
import { authorizeRedirectUriStorage } from '../auth/client-context.js';
|
|
5
12
|
// Helper to check if a request is an initialize request
|
|
6
13
|
function isInitializeRequest(body) {
|
|
7
14
|
return (typeof body === 'object' && body !== null && 'method' in body && body.method === 'initialize');
|
|
8
15
|
}
|
|
9
|
-
export function setupStreamableHttpTransport(createServer, config) {
|
|
16
|
+
export function setupStreamableHttpTransport(createServer, config, oauthDeps) {
|
|
10
17
|
const app = express();
|
|
18
|
+
// Behind a reverse proxy (k8s ingress, load balancer, ngrok) the client's
|
|
19
|
+
// X-Forwarded-For header is set. The SDK's OAuth router uses express-rate-limit,
|
|
20
|
+
// which throws on rate-limited endpoints (/register, /authorize, /token) unless
|
|
21
|
+
// Express `trust proxy` is configured — otherwise those endpoints return 500.
|
|
22
|
+
// Default to trusting 1 proxy hop; override with TRUST_PROXY (a hop count, or
|
|
23
|
+
// 'false' to disable when running with no proxy in front).
|
|
24
|
+
const trustProxyEnv = process.env.TRUST_PROXY;
|
|
25
|
+
app.set('trust proxy', trustProxyEnv === undefined
|
|
26
|
+
? 1
|
|
27
|
+
: /^\d+$/.test(trustProxyEnv)
|
|
28
|
+
? Number(trustProxyEnv)
|
|
29
|
+
: trustProxyEnv === 'false'
|
|
30
|
+
? false
|
|
31
|
+
: trustProxyEnv === 'true'
|
|
32
|
+
? true
|
|
33
|
+
: trustProxyEnv);
|
|
11
34
|
// CORS middleware for inspector
|
|
12
35
|
app.use((req, res, next) => {
|
|
13
36
|
res.header('Access-Control-Allow-Origin', process.env.CORS_ORIGIN || '*');
|
|
14
37
|
res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
|
|
15
38
|
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, mcp-session-id');
|
|
39
|
+
// Expose auth challenge + session id so browser-based MCP clients (Inspector,
|
|
40
|
+
// Claude.ai web) can read them from cross-origin responses. Without this the
|
|
41
|
+
// 401 WWW-Authenticate challenge is invisible to client JS and OAuth never starts.
|
|
42
|
+
res.header('Access-Control-Expose-Headers', 'WWW-Authenticate, mcp-session-id');
|
|
16
43
|
if (req.method === 'OPTIONS') {
|
|
17
44
|
res.sendStatus(200);
|
|
18
45
|
return;
|
|
@@ -22,10 +49,76 @@ export function setupStreamableHttpTransport(createServer, config) {
|
|
|
22
49
|
next();
|
|
23
50
|
});
|
|
24
51
|
app.use(express.json());
|
|
52
|
+
// OAuth: mount proxy auth router + protected-resource metadata, and guard /mcp.
|
|
53
|
+
const oauthConfig = oauthDeps?.config ?? getOAuthConfig();
|
|
54
|
+
let mcpGuard = null;
|
|
55
|
+
if (oauthConfig.enabled) {
|
|
56
|
+
const verifier = oauthDeps?.verifier ?? createJwksVerifier(oauthConfig);
|
|
57
|
+
const provider = createProxyProvider(oauthConfig, verifier);
|
|
58
|
+
// Seed per-request redirect_uri so the proxy's getClient can echo it during /authorize.
|
|
59
|
+
app.use((req, _res, next) => {
|
|
60
|
+
const redirectUri = typeof req.query.redirect_uri === 'string' ? req.query.redirect_uri : undefined;
|
|
61
|
+
authorizeRedirectUriStorage.run(redirectUri, () => next());
|
|
62
|
+
});
|
|
63
|
+
// RFC 9728 protected-resource metadata — served at TWO paths by OUR OWN handlers:
|
|
64
|
+
//
|
|
65
|
+
// - `/.well-known/oauth-protected-resource/mcp` (resource="https://mcp.qase.io/mcp")
|
|
66
|
+
// for clients that use the full endpoint URL as the resource identifier (VS Code).
|
|
67
|
+
// - `/.well-known/oauth-protected-resource` (root, resource=origin) for clients that
|
|
68
|
+
// discover at the root (Claude, Cursor, Codex). Backward compatible.
|
|
69
|
+
//
|
|
70
|
+
// Why our own handlers instead of the SDK's mcpAuthMetadataRouter: the SDK derives
|
|
71
|
+
// authorization_servers from `new URL(issuer).href`, which appends a trailing slash
|
|
72
|
+
// ("https://auth.qase.io/"). The AS's own metadata advertises the issuer WITHOUT the
|
|
73
|
+
// slash ("https://auth.qase.io"), and RFC 8414 §3.3 requires the issuer a client used
|
|
74
|
+
// to discover the AS to match the metadata's `issuer` EXACTLY. That slash mismatch
|
|
75
|
+
// makes strict clients (VS Code) reject the AS metadata and refuse automatic client
|
|
76
|
+
// registration (DCR). So authorization_servers must be exactly oauthConfig.issuer,
|
|
77
|
+
// un-normalized. These routes are registered BEFORE mcpAuthRouter so they take
|
|
78
|
+
// precedence over the SDK's own (slash-normalized) PRM mount; the SDK still serves the
|
|
79
|
+
// AS metadata document + the proxy authorize/token/register endpoints.
|
|
80
|
+
const authorizationServers = [oauthConfig.issuer];
|
|
81
|
+
const rootResourceMetadata = {
|
|
82
|
+
resource: new URL(oauthConfig.publicUrl).href,
|
|
83
|
+
authorization_servers: authorizationServers,
|
|
84
|
+
};
|
|
85
|
+
const mcpResourceMetadata = {
|
|
86
|
+
resource: new URL(oauthConfig.resourceUrl).href,
|
|
87
|
+
authorization_servers: authorizationServers,
|
|
88
|
+
};
|
|
89
|
+
app.get('/.well-known/oauth-protected-resource', (_req, res) => {
|
|
90
|
+
res.json(rootResourceMetadata);
|
|
91
|
+
});
|
|
92
|
+
app.get('/.well-known/oauth-protected-resource/mcp', (_req, res) => {
|
|
93
|
+
res.json(mcpResourceMetadata);
|
|
94
|
+
});
|
|
95
|
+
app.use(mcpAuthRouter({
|
|
96
|
+
provider,
|
|
97
|
+
issuerUrl: new URL(oauthConfig.issuer),
|
|
98
|
+
// baseUrl = the public ORIGIN: the OAuth proxy endpoints (/authorize, /token,
|
|
99
|
+
// /register) and AS metadata mount here. MUST stay the origin — decoupled from
|
|
100
|
+
// resourceUrl — or the resource's `/mcp` path would leak into these paths.
|
|
101
|
+
baseUrl: new URL(oauthConfig.publicUrl),
|
|
102
|
+
// Still passed so the SDK generates its AS-metadata document; its own PRM mount is
|
|
103
|
+
// shadowed by the explicit routes above.
|
|
104
|
+
resourceServerUrl: new URL(oauthConfig.resourceUrl),
|
|
105
|
+
}));
|
|
106
|
+
mcpGuard = createMcpGuard(verifier, oauthConfig);
|
|
107
|
+
console.error('[StreamableHTTP] OAuth proxy enabled');
|
|
108
|
+
}
|
|
25
109
|
const endpoint = config.endpoint || '/mcp';
|
|
26
110
|
const host = config.host || '0.0.0.0';
|
|
27
|
-
|
|
28
|
-
|
|
111
|
+
const guards = mcpGuard ? [mcpGuard] : [];
|
|
112
|
+
// Session management - store transport and last-seen timestamp per session.
|
|
113
|
+
// Sessions are in-memory per pod; one idle longer than this window is evicted
|
|
114
|
+
// (the client transparently re-initializes on the resulting 404). Default 24h so
|
|
115
|
+
// intermittently-used hosted connectors aren't dropped on idle; tune via
|
|
116
|
+
// QASE_MCP_SESSION_TTL_MINUTES. Note: longer TTL keeps more sessions in memory.
|
|
117
|
+
const ttlMinutesRaw = process.env.QASE_MCP_SESSION_TTL_MINUTES;
|
|
118
|
+
const ttlMinutes = ttlMinutesRaw && /^\d+$/.test(ttlMinutesRaw) && Number(ttlMinutesRaw) > 0
|
|
119
|
+
? Number(ttlMinutesRaw)
|
|
120
|
+
: 1440; // 24 hours
|
|
121
|
+
const SESSION_TTL_MS = ttlMinutes * 60 * 1000;
|
|
29
122
|
const sessions = new Map();
|
|
30
123
|
const sessionLastSeen = new Map();
|
|
31
124
|
// Periodically evict stale sessions that never sent a DELETE
|
|
@@ -48,13 +141,21 @@ export function setupStreamableHttpTransport(createServer, config) {
|
|
|
48
141
|
app.get('/health', (_req, res) => {
|
|
49
142
|
res.json({ status: 'ok', transport: 'streamable-http' });
|
|
50
143
|
});
|
|
144
|
+
// Prometheus metrics endpoint
|
|
145
|
+
app.get('/metrics', (_req, res) => {
|
|
146
|
+
res.set('Content-Type', 'text/plain; version=0.0.4');
|
|
147
|
+
res.send(getMetrics().renderPrometheus());
|
|
148
|
+
});
|
|
51
149
|
// MCP endpoint - DELETE for session cleanup
|
|
52
|
-
app.delete(endpoint, async (req, res) => {
|
|
150
|
+
app.delete(endpoint, ...guards, async (req, res) => {
|
|
53
151
|
const sessionId = req.headers['mcp-session-id'];
|
|
54
|
-
if (!sessionId
|
|
55
|
-
res.status(400).json({
|
|
56
|
-
|
|
57
|
-
|
|
152
|
+
if (!sessionId) {
|
|
153
|
+
res.status(400).json({ error: 'Missing mcp-session-id header' });
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (!sessions.has(sessionId)) {
|
|
157
|
+
// Unknown session → 404 so the client re-initializes (see POST handler).
|
|
158
|
+
res.status(404).json({ error: 'Session not found. Reinitialize the session.' });
|
|
58
159
|
return;
|
|
59
160
|
}
|
|
60
161
|
const transport = sessions.get(sessionId);
|
|
@@ -75,12 +176,15 @@ export function setupStreamableHttpTransport(createServer, config) {
|
|
|
75
176
|
}
|
|
76
177
|
});
|
|
77
178
|
// MCP endpoint - GET for SSE streams (if needed)
|
|
78
|
-
app.get(endpoint, async (req, res) => {
|
|
179
|
+
app.get(endpoint, ...guards, async (req, res) => {
|
|
79
180
|
const sessionId = req.headers['mcp-session-id'];
|
|
80
|
-
if (!sessionId
|
|
81
|
-
res.status(400).json({
|
|
82
|
-
|
|
83
|
-
|
|
181
|
+
if (!sessionId) {
|
|
182
|
+
res.status(400).json({ error: 'Missing mcp-session-id header' });
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (!sessions.has(sessionId)) {
|
|
186
|
+
// Unknown session → 404 so the client re-initializes (see POST handler).
|
|
187
|
+
res.status(404).json({ error: 'Session not found. Reinitialize the session.' });
|
|
84
188
|
return;
|
|
85
189
|
}
|
|
86
190
|
const transport = sessions.get(sessionId);
|
|
@@ -97,7 +201,7 @@ export function setupStreamableHttpTransport(createServer, config) {
|
|
|
97
201
|
}
|
|
98
202
|
});
|
|
99
203
|
// MCP endpoint - POST for messages
|
|
100
|
-
app.post(endpoint, async (req, res) => {
|
|
204
|
+
app.post(endpoint, ...guards, async (req, res) => {
|
|
101
205
|
console.error(`[StreamableHTTP] POST ${endpoint} received`, {
|
|
102
206
|
sessionId: req.headers['mcp-session-id'],
|
|
103
207
|
body: req.body,
|
|
@@ -123,10 +227,18 @@ export function setupStreamableHttpTransport(createServer, config) {
|
|
|
123
227
|
await createServer().connect(transport);
|
|
124
228
|
console.error(`[StreamableHTTP] New session created: ${newSessionId}`);
|
|
125
229
|
}
|
|
230
|
+
else if (sessionId) {
|
|
231
|
+
// Session ID provided but unknown (expired, evicted, server restarted, or the
|
|
232
|
+
// request landed on a different instance). Per the MCP streamable-http spec,
|
|
233
|
+
// respond 404 so the client transparently starts a new session (re-initialize)
|
|
234
|
+
// instead of getting stuck on a 400. Auth (JWT) is client-side, so no re-login.
|
|
235
|
+
res.status(404).json({ error: 'Session not found. Reinitialize the session.' });
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
126
238
|
else {
|
|
127
|
-
//
|
|
239
|
+
// No session ID and not an initialize request.
|
|
128
240
|
res.status(400).json({
|
|
129
|
-
error: '
|
|
241
|
+
error: 'Bad Request: no session ID provided and not an initialize request',
|
|
130
242
|
});
|
|
131
243
|
return;
|
|
132
244
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streamableHttp.js","sourceRoot":"","sources":["../../src/transports/streamableHttp.ts"],"names":[],"mappings":"AAAA,OAAO,OAAoB,MAAM,SAAS,CAAC;AAE3C,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"streamableHttp.js","sourceRoot":"","sources":["../../src/transports/streamableHttp.ts"],"names":[],"mappings":"AAAA,OAAO,OAAoB,MAAM,SAAS,CAAC;AAE3C,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,aAAa,EAAE,MAAM,iDAAiD,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAoB,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAqB,MAAM,0BAA0B,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AASxE,wDAAwD;AACxD,SAAS,mBAAmB,CAAC,IAAa;IACxC,OAAO,CACL,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,QAAQ,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,YAAY,CAC9F,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC1C,YAA0B,EAC1B,MAA4B,EAC5B,SAA2D;IAE3D,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IAEtB,0EAA0E;IAC1E,iFAAiF;IACjF,gFAAgF;IAChF,8EAA8E;IAC9E,8EAA8E;IAC9E,2DAA2D;IAC3D,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IAC9C,GAAG,CAAC,GAAG,CACL,aAAa,EACb,aAAa,KAAK,SAAS;QACzB,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC;YAC3B,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC;YACvB,CAAC,CAAC,aAAa,KAAK,OAAO;gBACzB,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,aAAa,KAAK,MAAM;oBACxB,CAAC,CAAC,IAAI;oBACN,CAAC,CAAC,aAAa,CACxB,CAAC;IAEF,gCAAgC;IAChC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,GAAG,CAAC,MAAM,CAAC,6BAA6B,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,CAAC;QAC1E,GAAG,CAAC,MAAM,CAAC,8BAA8B,EAAE,4BAA4B,CAAC,CAAC;QACzE,GAAG,CAAC,MAAM,CAAC,8BAA8B,EAAE,6CAA6C,CAAC,CAAC;QAC1F,8EAA8E;QAC9E,6EAA6E;QAC7E,mFAAmF;QACnF,GAAG,CAAC,MAAM,CAAC,+BAA+B,EAAE,kCAAkC,CAAC,CAAC;QAEhF,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;YACpB,OAAO;QACT,CAAC;QAED,2EAA2E;QAC3E,OAAO,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QAClF,IAAI,EAAE,CAAC;IACT,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAExB,gFAAgF;IAChF,MAAM,WAAW,GAAG,SAAS,EAAE,MAAM,IAAI,cAAc,EAAE,CAAC;IAC1D,IAAI,QAAQ,GAA0B,IAAI,CAAC;IAE3C,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,SAAS,EAAE,QAAQ,IAAI,kBAAkB,CAAC,WAAW,CAAC,CAAC;QACxE,MAAM,QAAQ,GAAG,mBAAmB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QAE5D,wFAAwF;QACxF,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;YAC1B,MAAM,WAAW,GACf,OAAO,GAAG,CAAC,KAAK,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC;YAClF,2BAA2B,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;QAEH,kFAAkF;QAClF,EAAE;QACF,sFAAsF;QACtF,sFAAsF;QACtF,sFAAsF;QACtF,wEAAwE;QACxE,EAAE;QACF,mFAAmF;QACnF,oFAAoF;QACpF,qFAAqF;QACrF,sFAAsF;QACtF,mFAAmF;QACnF,oFAAoF;QACpF,mFAAmF;QACnF,+EAA+E;QAC/E,uFAAuF;QACvF,uEAAuE;QACvE,MAAM,oBAAoB,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAClD,MAAM,oBAAoB,GAAG;YAC3B,QAAQ,EAAE,IAAI,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,IAAI;YAC7C,qBAAqB,EAAE,oBAAoB;SAC5C,CAAC;QACF,MAAM,mBAAmB,GAAG;YAC1B,QAAQ,EAAE,IAAI,GAAG,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,IAAI;YAC/C,qBAAqB,EAAE,oBAAoB;SAC5C,CAAC;QACF,GAAG,CAAC,GAAG,CAAC,uCAAuC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YAC7D,GAAG,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,GAAG,CAAC,2CAA2C,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YACjE,GAAG,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,GAAG,CACL,aAAa,CAAC;YACZ,QAAQ;YACR,SAAS,EAAE,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC;YACtC,8EAA8E;YAC9E,+EAA+E;YAC/E,2EAA2E;YAC3E,OAAO,EAAE,IAAI,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC;YACvC,mFAAmF;YACnF,yCAAyC;YACzC,iBAAiB,EAAE,IAAI,GAAG,CAAC,WAAW,CAAC,WAAW,CAAC;SACpD,CAAC,CACH,CAAC;QAEF,QAAQ,GAAG,cAAc,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QACjD,OAAO,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC;IAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,SAAS,CAAC;IACtC,MAAM,MAAM,GAAqB,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE5D,4EAA4E;IAC5E,8EAA8E;IAC9E,iFAAiF;IACjF,yEAAyE;IACzE,gFAAgF;IAChF,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC;IAC/D,MAAM,UAAU,GACd,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC;QACvE,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC;QACvB,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW;IACvB,MAAM,cAAc,GAAG,UAAU,GAAG,EAAE,GAAG,IAAI,CAAC;IAC9C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyC,CAAC;IAClE,MAAM,eAAe,GAAG,IAAI,GAAG,EAAkB,CAAC;IAElD,6DAA6D;IAC7D,MAAM,eAAe,GAAG,WAAW,CACjC,GAAG,EAAE;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,eAAe,EAAE,CAAC;YACvC,IAAI,GAAG,GAAG,EAAE,GAAG,cAAc,EAAE,CAAC;gBAC9B,MAAM,cAAc,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACxC,IAAI,cAAc,EAAE,CAAC;oBACnB,cAAc,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;oBACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACtB,CAAC;gBACD,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC3B,OAAO,CAAC,KAAK,CAAC,2CAA2C,EAAE,EAAE,CAAC,CAAC;YACjE,CAAC;QACH,CAAC;IACH,CAAC,EACD,CAAC,GAAG,EAAE,GAAG,IAAI,CACd,CAAC,CAAC,wBAAwB;IAC3B,eAAe,CAAC,KAAK,EAAE,CAAC,CAAC,gDAAgD;IAEzE,wBAAwB;IACxB,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QAC/B,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,8BAA8B;IAC9B,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QAChC,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,2BAA2B,CAAC,CAAC;QACrD,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,4CAA4C;IAC5C,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAiB,EAAE;QAChE,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAuB,CAAC;QAEtE,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC,CAAC;YACjE,OAAO;QACT,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7B,yEAAyE;YACzE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,8CAA8C,EAAE,CAAC,CAAC;YAChF,OAAO;QACT,CAAC;QAED,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAE,CAAC;QAE3C,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;YACxB,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC3B,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAClC,OAAO,CAAC,KAAK,CAAC,oCAAoC,SAAS,EAAE,CAAC,CAAC;YAC/D,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;QACrD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,yCAAyC,EAAE,KAAK,CAAC,CAAC;YAChE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACnB,KAAK,EAAE,uBAAuB;iBAC/B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,iDAAiD;IACjD,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAiB,EAAE;QAC7D,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAuB,CAAC;QAEtE,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC,CAAC;YACjE,OAAO;QACT,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7B,yEAAyE;YACzE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,8CAA8C,EAAE,CAAC,CAAC;YAChF,OAAO;QACT,CAAC;QAED,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAE,CAAC;QAE3C,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8CAA8C,EAAE,KAAK,CAAC,CAAC;YACrE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACnB,KAAK,EAAE,uBAAuB;iBAC/B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,mCAAmC;IACnC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAiB,EAAE;QAC9D,OAAO,CAAC,KAAK,CAAC,yBAAyB,QAAQ,WAAW,EAAE;YAC1D,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC;YACxC,IAAI,EAAE,GAAG,CAAC,IAAI;SACf,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAuB,CAAC;QACtE,IAAI,SAAwC,CAAC;QAE7C,IAAI,SAAS,IAAI,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACzC,6DAA6D;YAC7D,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAE,CAAC;YACrC,eAAe,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7C,CAAC;aAAM,IAAI,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,qDAAqD;YACrD,MAAM,YAAY,GAAG,UAAU,EAAE,CAAC;YAClC,SAAS,GAAG,IAAI,6BAA6B,CAAC;gBAC5C,kBAAkB,EAAE,GAAG,EAAE,CAAC,YAAY;gBACtC,kBAAkB,EAAE,IAAI;aACzB,CAAC,CAAC;YAEH,yCAAyC;YACzC,QAAQ,CAAC,GAAG,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;YACtC,eAAe,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;YAE9C,yFAAyF;YACzF,MAAM,YAAY,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAExC,OAAO,CAAC,KAAK,CAAC,yCAAyC,YAAY,EAAE,CAAC,CAAC;QACzE,CAAC;aAAM,IAAI,SAAS,EAAE,CAAC;YACrB,8EAA8E;YAC9E,6EAA6E;YAC7E,+EAA+E;YAC/E,gFAAgF;YAChF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,8CAA8C,EAAE,CAAC,CAAC;YAChF,OAAO;QACT,CAAC;aAAM,CAAC;YACN,+CAA+C;YAC/C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,KAAK,EAAE,mEAAmE;aAC3E,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,uEAAuE;QACvE,MAAM,UAAU,GAAI,GAAG,CAAC,OAAO,CAAC,eAAe,CAAY,IAAI,EAAE,CAAC;QAClE,MAAM,YAAY,GAAG,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAExF,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACnE,CAAC;QAED,wFAAwF;QACxF,IAAI,CAAC;YACH,MAAM,mBAAmB,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,EAAE,CAC/C,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAC5C,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,0CAA0C,EAAE,KAAK,CAAC,CAAC;YACjE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACnB,KAAK,EAAE,uBAAuB;iBAC/B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,oDAAoD;IACpD,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;QACpD,OAAO,CAAC,KAAK,CAAC,+CAA+C,IAAI,IAAI,MAAM,CAAC,IAAI,GAAG,QAAQ,EAAE,CAAC,CAAC;QAC/F,OAAO,CAAC,KAAK,CAAC,yCAAyC,IAAI,IAAI,MAAM,CAAC,IAAI,SAAS,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;IAEH,uBAAuB;IACvB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE;QACtC,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,4BAA4B;IAC5B,UAAU,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE;QACrC,OAAO,CAAC,KAAK,CACX,+CAA+C,MAAM,CAAC,aAAa,IAAI,MAAM,CAAC,UAAU,EAAE,CAC3F,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,2DAA2D;IAC1D,GAAW,CAAC,WAAW,GAAG,UAAU,CAAC;IAEtC,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -5,4 +5,12 @@ import { AsyncLocalStorage } from 'async_hooks';
|
|
|
5
5
|
* Empty string means no user token — fall back to shared QASE_API_TOKEN.
|
|
6
6
|
*/
|
|
7
7
|
export declare const requestTokenStorage: AsyncLocalStorage<string>;
|
|
8
|
+
/**
|
|
9
|
+
* Read the effective token for the current async context: request-scoped
|
|
10
|
+
* token first, then QASE_API_TOKEN env var.
|
|
11
|
+
*
|
|
12
|
+
* Throws if neither is available so callers never silently fall back to a
|
|
13
|
+
* global cache shard.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getEffectiveToken(): string;
|
|
8
16
|
//# sourceMappingURL=auth-context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-context.d.ts","sourceRoot":"","sources":["../../src/utils/auth-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,2BAAkC,CAAC"}
|
|
1
|
+
{"version":3,"file":"auth-context.d.ts","sourceRoot":"","sources":["../../src/utils/auth-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,2BAAkC,CAAC;AAEnE;;;;;;GAMG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAW1C"}
|
|
@@ -5,4 +5,22 @@ import { AsyncLocalStorage } from 'async_hooks';
|
|
|
5
5
|
* Empty string means no user token — fall back to shared QASE_API_TOKEN.
|
|
6
6
|
*/
|
|
7
7
|
export const requestTokenStorage = new AsyncLocalStorage();
|
|
8
|
+
/**
|
|
9
|
+
* Read the effective token for the current async context: request-scoped
|
|
10
|
+
* token first, then QASE_API_TOKEN env var.
|
|
11
|
+
*
|
|
12
|
+
* Throws if neither is available so callers never silently fall back to a
|
|
13
|
+
* global cache shard.
|
|
14
|
+
*/
|
|
15
|
+
export function getEffectiveToken() {
|
|
16
|
+
const requestToken = requestTokenStorage.getStore();
|
|
17
|
+
if (requestToken)
|
|
18
|
+
return requestToken;
|
|
19
|
+
const envToken = process.env.QASE_API_TOKEN;
|
|
20
|
+
if (!envToken) {
|
|
21
|
+
throw new Error('QASE_API_TOKEN environment variable is required or a per-request Bearer token must be provided. ' +
|
|
22
|
+
'Get your API token from: https://app.qase.io/user/api/token');
|
|
23
|
+
}
|
|
24
|
+
return envToken;
|
|
25
|
+
}
|
|
8
26
|
//# sourceMappingURL=auth-context.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-context.js","sourceRoot":"","sources":["../../src/utils/auth-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,iBAAiB,EAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"auth-context.js","sourceRoot":"","sources":["../../src/utils/auth-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,iBAAiB,EAAU,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,YAAY,GAAG,mBAAmB,CAAC,QAAQ,EAAE,CAAC;IACpD,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC5C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,kGAAkG;YAChG,6DAA6D,CAChE,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
type SystemFieldMap = Record<string,
|
|
1
|
+
type SystemFieldMap = Record<string, Record<string, number>>;
|
|
2
2
|
export declare function normalizeCaseEnums<T extends Record<string, unknown>>(caseData: T): Promise<T>;
|
|
3
|
-
|
|
4
|
-
export declare function __setCaseEnumCacheForTest(snapshot: SystemFieldMap): void
|
|
3
|
+
/** @internal — used by tests to pre-populate the tenant-scoped shard. */
|
|
4
|
+
export declare function __setCaseEnumCacheForTest(snapshot: SystemFieldMap): Promise<void>;
|
|
5
|
+
/** @internal — used by tests to clear the tenant-scoped shard. */
|
|
6
|
+
export declare function resetCaseEnumCacheForTest(): Promise<void>;
|
|
5
7
|
export {};
|
|
6
8
|
//# sourceMappingURL=case-enums.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"case-enums.d.ts","sourceRoot":"","sources":["../../src/utils/case-enums.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"case-enums.d.ts","sourceRoot":"","sources":["../../src/utils/case-enums.ts"],"names":[],"mappings":"AAkCA,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AA+E7D,wBAAsB,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxE,QAAQ,EAAE,CAAC,GACV,OAAO,CAAC,CAAC,CAAC,CAaZ;AAED,yEAAyE;AACzE,wBAAsB,yBAAyB,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAGvF;AAED,kEAAkE;AAClE,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC,CAG/D"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { getApiClient } from '../client/index.js';
|
|
2
|
+
import { getCache, buildCacheKey, hashToken } from '../cache/index.js';
|
|
3
|
+
import { getEffectiveToken } from './auth-context.js';
|
|
2
4
|
const caseEnumFields = [
|
|
3
5
|
'priority',
|
|
4
6
|
'type',
|
|
@@ -8,85 +10,101 @@ const caseEnumFields = [
|
|
|
8
10
|
'layer',
|
|
9
11
|
'automation',
|
|
10
12
|
];
|
|
11
|
-
|
|
13
|
+
// System fields are workspace-level config (priority, severity, status options,
|
|
14
|
+
// etc.) that change on the order of hours to days when an admin reconfigures
|
|
15
|
+
// the workspace. 5 minutes balances freshness against API call frequency; do
|
|
16
|
+
// not decrease below 1 minute without measuring.
|
|
17
|
+
const SYSTEM_FIELDS_TTL_MS = 5 * 60 * 1000;
|
|
12
18
|
function normalizeEnumValue(raw, lookup) {
|
|
13
|
-
if (raw === undefined || raw === null)
|
|
19
|
+
if (raw === undefined || raw === null)
|
|
14
20
|
return undefined;
|
|
15
|
-
|
|
16
|
-
if (typeof raw === 'number') {
|
|
21
|
+
if (typeof raw === 'number')
|
|
17
22
|
return raw;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const lower = trimmed.toLowerCase();
|
|
29
|
-
if (lookup?.has(lower)) {
|
|
30
|
-
return lookup.get(lower);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return undefined;
|
|
23
|
+
if (typeof raw !== 'string')
|
|
24
|
+
return undefined;
|
|
25
|
+
const trimmed = raw.trim();
|
|
26
|
+
if (trimmed === '')
|
|
27
|
+
return undefined;
|
|
28
|
+
const parsed = Number(trimmed);
|
|
29
|
+
if (Number.isInteger(parsed))
|
|
30
|
+
return parsed;
|
|
31
|
+
const lower = trimmed.toLowerCase();
|
|
32
|
+
return lookup?.[lower];
|
|
34
33
|
}
|
|
35
|
-
function
|
|
36
|
-
if (!entry.options || entry.options.length === 0)
|
|
34
|
+
function buildFieldLookup(entry) {
|
|
35
|
+
if (!entry.options || entry.options.length === 0)
|
|
37
36
|
return undefined;
|
|
37
|
+
const out = {};
|
|
38
|
+
for (const opt of entry.options) {
|
|
39
|
+
out[opt.slug.toLowerCase()] = opt.id;
|
|
40
|
+
if (opt.title)
|
|
41
|
+
out[opt.title.toLowerCase()] = opt.id;
|
|
42
|
+
out[opt.id.toString()] = opt.id;
|
|
38
43
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
function systemFieldsKey() {
|
|
47
|
+
const token = getEffectiveToken();
|
|
48
|
+
const host = process.env.QASE_API_DOMAIN || 'api.qase.io';
|
|
49
|
+
return buildCacheKey({
|
|
50
|
+
host,
|
|
51
|
+
tenantId: hashToken(token),
|
|
52
|
+
resource: 'system_fields',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async function fetchSystemFieldMaps() {
|
|
56
|
+
const client = getApiClient();
|
|
57
|
+
const response = await client.systemFields.getSystemFields();
|
|
58
|
+
const map = {};
|
|
59
|
+
for (const entry of response.data.result) {
|
|
60
|
+
const slug = entry.slug.toLowerCase();
|
|
61
|
+
const lookup = buildFieldLookup(entry);
|
|
62
|
+
if (lookup)
|
|
63
|
+
map[slug] = lookup;
|
|
46
64
|
}
|
|
47
65
|
return map;
|
|
48
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Resolve system-field metadata from cache, fetching from the API on miss.
|
|
69
|
+
*
|
|
70
|
+
* Note on concurrency: this function does not coalesce concurrent cache
|
|
71
|
+
* misses. If two requests race past a cold cache simultaneously, both will
|
|
72
|
+
* hit the API. This is deliberate — the axios-level in-flight deduplication
|
|
73
|
+
* added in Task 12 of the Phase 1 plan coalesces identical GETs at the HTTP
|
|
74
|
+
* layer, which is a more general fix that also covers other metadata
|
|
75
|
+
* fetches, so there is no need to reimplement it here.
|
|
76
|
+
*/
|
|
49
77
|
async function getSystemFieldMaps() {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
for (const entry of response.data.result) {
|
|
59
|
-
const normalizedSlug = entry.slug.toLowerCase();
|
|
60
|
-
const fieldMap = createFieldMap(entry);
|
|
61
|
-
if (fieldMap) {
|
|
62
|
-
map[normalizedSlug] = fieldMap;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return map;
|
|
66
|
-
})
|
|
67
|
-
.catch((error) => {
|
|
68
|
-
systemFieldCache = null;
|
|
69
|
-
throw error;
|
|
70
|
-
});
|
|
71
|
-
return systemFieldCache;
|
|
78
|
+
const cache = await getCache();
|
|
79
|
+
const key = systemFieldsKey();
|
|
80
|
+
const cached = await cache.get(key);
|
|
81
|
+
if (cached)
|
|
82
|
+
return cached;
|
|
83
|
+
const fresh = await fetchSystemFieldMaps();
|
|
84
|
+
await cache.set(key, fresh, SYSTEM_FIELDS_TTL_MS);
|
|
85
|
+
return fresh;
|
|
72
86
|
}
|
|
73
87
|
export async function normalizeCaseEnums(caseData) {
|
|
74
88
|
const normalized = { ...caseData };
|
|
75
89
|
const systemFields = await getSystemFieldMaps();
|
|
76
90
|
for (const field of caseEnumFields) {
|
|
77
|
-
const
|
|
91
|
+
const raw = normalized[field];
|
|
78
92
|
const lookup = systemFields[field];
|
|
79
|
-
const
|
|
80
|
-
if (
|
|
81
|
-
normalized[field] =
|
|
93
|
+
const mapped = normalizeEnumValue(raw, lookup);
|
|
94
|
+
if (mapped !== undefined) {
|
|
95
|
+
normalized[field] = mapped;
|
|
82
96
|
}
|
|
83
97
|
}
|
|
84
98
|
return normalized;
|
|
85
99
|
}
|
|
86
|
-
|
|
87
|
-
|
|
100
|
+
/** @internal — used by tests to pre-populate the tenant-scoped shard. */
|
|
101
|
+
export async function __setCaseEnumCacheForTest(snapshot) {
|
|
102
|
+
const cache = await getCache();
|
|
103
|
+
await cache.set(systemFieldsKey(), snapshot, SYSTEM_FIELDS_TTL_MS);
|
|
88
104
|
}
|
|
89
|
-
|
|
90
|
-
|
|
105
|
+
/** @internal — used by tests to clear the tenant-scoped shard. */
|
|
106
|
+
export async function resetCaseEnumCacheForTest() {
|
|
107
|
+
const cache = await getCache();
|
|
108
|
+
await cache.delete(systemFieldsKey());
|
|
91
109
|
}
|
|
92
110
|
//# sourceMappingURL=case-enums.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"case-enums.js","sourceRoot":"","sources":["../../src/utils/case-enums.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"case-enums.js","sourceRoot":"","sources":["../../src/utils/case-enums.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output Schemas for MCP Tools
|
|
3
|
+
*
|
|
4
|
+
* JSON Schema definitions describing tool output structure.
|
|
5
|
+
* Enables MCP clients to process results programmatically in a sandbox
|
|
6
|
+
* instead of passing raw JSON to the LLM (reduces token usage ~37%).
|
|
7
|
+
*/
|
|
8
|
+
import type { OutputSchema } from './registry.js';
|
|
9
|
+
export declare const CiReportOutput: OutputSchema;
|
|
10
|
+
export declare const RegressionRunOutput: OutputSchema;
|
|
11
|
+
export declare const TriageDefectOutput: OutputSchema;
|
|
12
|
+
export declare const QqlSearchOutput: OutputSchema;
|
|
13
|
+
export declare const ProjectContextOutput: OutputSchema;
|
|
14
|
+
export declare const DeleteOutput: OutputSchema;
|
|
15
|
+
export declare const DiscoverToolsOutput: OutputSchema;
|
|
16
|
+
//# sourceMappingURL=output-schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-schemas.d.ts","sourceRoot":"","sources":["../../src/utils/output-schemas.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,eAAO,MAAM,cAAc,EAAE,YAQ5B,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,YAQjC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,YAQhC,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,YAO7B,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,YAWlC,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,YAO1B,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,YAkBjC,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output Schemas for MCP Tools
|
|
3
|
+
*
|
|
4
|
+
* JSON Schema definitions describing tool output structure.
|
|
5
|
+
* Enables MCP clients to process results programmatically in a sandbox
|
|
6
|
+
* instead of passing raw JSON to the LLM (reduces token usage ~37%).
|
|
7
|
+
*/
|
|
8
|
+
export const CiReportOutput = {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
run_id: { type: 'integer', description: 'Created run ID' },
|
|
12
|
+
run_status: { type: 'string', enum: ['active', 'complete', 'complete_failed'] },
|
|
13
|
+
results_recorded: { type: 'integer', description: 'Number of results recorded' },
|
|
14
|
+
},
|
|
15
|
+
required: ['run_id', 'run_status', 'results_recorded'],
|
|
16
|
+
};
|
|
17
|
+
export const RegressionRunOutput = {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
run_id: { type: 'integer', description: 'Created run ID' },
|
|
21
|
+
cases_added: { type: 'integer', description: 'Number of cases added to the run' },
|
|
22
|
+
run: { type: 'object', description: 'Full run entity' },
|
|
23
|
+
},
|
|
24
|
+
required: ['run_id', 'cases_added'],
|
|
25
|
+
};
|
|
26
|
+
export const TriageDefectOutput = {
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
defect_id: { type: 'integer', description: 'Created defect ID' },
|
|
30
|
+
defect: { type: 'object', description: 'Full defect entity' },
|
|
31
|
+
linked_results: { type: 'integer', description: 'Number of linked result hashes' },
|
|
32
|
+
},
|
|
33
|
+
required: ['defect_id', 'linked_results'],
|
|
34
|
+
};
|
|
35
|
+
export const QqlSearchOutput = {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {
|
|
38
|
+
total: { type: 'integer', description: 'Total matching entities' },
|
|
39
|
+
entities: { type: 'array', description: 'Matching entities', items: { type: 'object' } },
|
|
40
|
+
},
|
|
41
|
+
required: ['total', 'entities'],
|
|
42
|
+
};
|
|
43
|
+
export const ProjectContextOutput = {
|
|
44
|
+
type: 'object',
|
|
45
|
+
properties: {
|
|
46
|
+
project: { type: 'object', description: 'Project details' },
|
|
47
|
+
suites: { type: 'object', description: 'Suites list with entities array' },
|
|
48
|
+
milestones: { type: 'object', description: 'Milestones list' },
|
|
49
|
+
environments: { type: 'object', description: 'Environments list' },
|
|
50
|
+
custom_fields: { type: 'object', description: 'Custom fields list' },
|
|
51
|
+
users: { type: 'object', description: 'Team members list' },
|
|
52
|
+
},
|
|
53
|
+
required: ['project', 'suites', 'milestones', 'environments'],
|
|
54
|
+
};
|
|
55
|
+
export const DeleteOutput = {
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
success: { type: 'boolean' },
|
|
59
|
+
id: { type: 'integer', description: 'Deleted entity ID' },
|
|
60
|
+
},
|
|
61
|
+
required: ['success'],
|
|
62
|
+
};
|
|
63
|
+
export const DiscoverToolsOutput = {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
found: { type: 'integer', description: 'Number of matching tools' },
|
|
67
|
+
activated: { type: 'integer', description: 'Number of newly activated tools' },
|
|
68
|
+
tools: {
|
|
69
|
+
type: 'array',
|
|
70
|
+
items: {
|
|
71
|
+
type: 'object',
|
|
72
|
+
properties: {
|
|
73
|
+
name: { type: 'string' },
|
|
74
|
+
description: { type: 'string' },
|
|
75
|
+
destructive: { type: 'boolean' },
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
required: ['found', 'activated', 'tools'],
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=output-schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-schemas.js","sourceRoot":"","sources":["../../src/utils/output-schemas.ts"],"names":[],"mappings":"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"}
|