@dawmatt/api-grade-mcp 0.1.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 +128 -0
- package/dist/auth/entra.d.ts +8 -0
- package/dist/auth/entra.d.ts.map +1 -0
- package/dist/auth/entra.js +59 -0
- package/dist/auth/entra.js.map +1 -0
- package/dist/auth/github.d.ts +10 -0
- package/dist/auth/github.d.ts.map +1 -0
- package/dist/auth/github.js +42 -0
- package/dist/auth/github.js.map +1 -0
- package/dist/config/resolve-ruleset.d.ts +3 -0
- package/dist/config/resolve-ruleset.d.ts.map +1 -0
- package/dist/config/resolve-ruleset.js +31 -0
- package/dist/config/resolve-ruleset.js.map +1 -0
- package/dist/config/ruleset-config.d.ts +13 -0
- package/dist/config/ruleset-config.d.ts.map +1 -0
- package/dist/config/ruleset-config.js +49 -0
- package/dist/config/ruleset-config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +33 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/assert-grade.d.ts +4 -0
- package/dist/tools/assert-grade.d.ts.map +1 -0
- package/dist/tools/assert-grade.js +132 -0
- package/dist/tools/assert-grade.js.map +1 -0
- package/dist/tools/configure-ruleset.d.ts +4 -0
- package/dist/tools/configure-ruleset.d.ts.map +1 -0
- package/dist/tools/configure-ruleset.js +94 -0
- package/dist/tools/configure-ruleset.js.map +1 -0
- package/dist/tools/get-ruleset-config.d.ts +4 -0
- package/dist/tools/get-ruleset-config.d.ts.map +1 -0
- package/dist/tools/get-ruleset-config.js +53 -0
- package/dist/tools/get-ruleset-config.js.map +1 -0
- package/dist/tools/grade-detailed.d.ts +4 -0
- package/dist/tools/grade-detailed.d.ts.map +1 -0
- package/dist/tools/grade-detailed.js +143 -0
- package/dist/tools/grade-detailed.js.map +1 -0
- package/dist/tools/grade.d.ts +4 -0
- package/dist/tools/grade.d.ts.map +1 -0
- package/dist/tools/grade.js +134 -0
- package/dist/tools/grade.js.map +1 -0
- package/dist/tools/non-breaking.d.ts +4 -0
- package/dist/tools/non-breaking.d.ts.map +1 -0
- package/dist/tools/non-breaking.js +138 -0
- package/dist/tools/non-breaking.js.map +1 -0
- package/dist/tools/quick-fixes-only.d.ts +4 -0
- package/dist/tools/quick-fixes-only.d.ts.map +1 -0
- package/dist/tools/quick-fixes-only.js +138 -0
- package/dist/tools/quick-fixes-only.js.map +1 -0
- package/dist/tools/set-ruleset-config.d.ts +4 -0
- package/dist/tools/set-ruleset-config.d.ts.map +1 -0
- package/dist/tools/set-ruleset-config.js +94 -0
- package/dist/tools/set-ruleset-config.js.map +1 -0
- package/dist/types.d.ts +27 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/classify.d.ts +14 -0
- package/dist/utils/classify.d.ts.map +1 -0
- package/dist/utils/classify.js +123 -0
- package/dist/utils/classify.js.map +1 -0
- package/dist/utils/errors.d.ts +33 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +56 -0
- package/dist/utils/errors.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { mcpError, ERROR_CODES } from '../utils/errors.js';
|
|
3
|
+
import { saveWorkspaceConfig, saveGlobalConfig, getWorkspaceConfigPath, getGlobalConfigPath, ConfigWriteError, } from '../config/ruleset-config.js';
|
|
4
|
+
export function registerConfigureRulesetTool(server, sessionState) {
|
|
5
|
+
server.tool('configure-ruleset', 'Set the default ruleset used by this MCP server when no rulesetPath is supplied on a grading request. Supports three scopes: session (in-memory, resets on server restart), workspace (persisted to .api-grade/config.json in the workspace root), and global (persisted to ~/.api-grade/config.json). Optionally configure authentication for rulesets hosted in secured locations.', {
|
|
6
|
+
scope: z
|
|
7
|
+
.enum(['session', 'workspace', 'global'])
|
|
8
|
+
.describe("Where to store this default: 'session' is in-memory for this server process only; 'workspace' persists to .api-grade/config.json in the current workspace root; 'global' persists to ~/.api-grade/config.json."),
|
|
9
|
+
rulesetPath: z
|
|
10
|
+
.string()
|
|
11
|
+
.nullish()
|
|
12
|
+
.describe('Absolute or relative file path, or HTTPS URL, to a Spectral-compatible ruleset file. To clear the default at this scope, omit this field or pass null.'),
|
|
13
|
+
auth: z
|
|
14
|
+
.object({
|
|
15
|
+
type: z.enum(['github-pat', 'entra-id']).describe("'github-pat' uses a Bearer token for GitHub Enterprise URLs. 'entra-id' uses Microsoft Entra ID OAuth 2.0 device-code flow."),
|
|
16
|
+
githubToken: z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("GitHub Personal Access Token. Only used when type is 'github-pat'. If omitted, falls back to GITHUB_TOKEN environment variable."),
|
|
20
|
+
tenantId: z
|
|
21
|
+
.string()
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("Microsoft Entra ID tenant ID. Required when type is 'entra-id'."),
|
|
24
|
+
clientId: z
|
|
25
|
+
.string()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("Microsoft Entra ID application (client) ID. Required when type is 'entra-id'."),
|
|
28
|
+
})
|
|
29
|
+
.optional(),
|
|
30
|
+
}, async ({ scope, rulesetPath, auth }) => {
|
|
31
|
+
const input = { scope, rulesetPath, auth };
|
|
32
|
+
if (auth?.type === 'entra-id' && (!auth.tenantId || !auth.clientId)) {
|
|
33
|
+
return mcpError(ERROR_CODES.INVALID_AUTH_CONFIG, "auth.type 'entra-id' requires tenantId and clientId fields.", input);
|
|
34
|
+
}
|
|
35
|
+
const resolvedPath = rulesetPath ?? null;
|
|
36
|
+
const resolvedAuth = auth
|
|
37
|
+
? {
|
|
38
|
+
type: auth.type,
|
|
39
|
+
...(auth.type === 'github-pat' && auth.githubToken ? { githubToken: auth.githubToken } : {}),
|
|
40
|
+
...(auth.type === 'entra-id' ? { tenantId: auth.tenantId, clientId: auth.clientId } : {}),
|
|
41
|
+
}
|
|
42
|
+
: null;
|
|
43
|
+
const config = { rulesetPath: resolvedPath, auth: resolvedAuth };
|
|
44
|
+
if (scope === 'session') {
|
|
45
|
+
sessionState.defaultRuleset = resolvedPath !== null ? config : null;
|
|
46
|
+
if (resolvedPath !== null) {
|
|
47
|
+
sessionState.sessionRulesetOverride = null;
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
content: [
|
|
51
|
+
{
|
|
52
|
+
type: 'text',
|
|
53
|
+
text: JSON.stringify({
|
|
54
|
+
scope,
|
|
55
|
+
rulesetPath: resolvedPath,
|
|
56
|
+
auth: resolvedAuth ? { type: resolvedAuth.type } : null,
|
|
57
|
+
message: resolvedPath !== null
|
|
58
|
+
? 'Session default ruleset configured. This setting applies for the duration of this server process.'
|
|
59
|
+
: 'Session default ruleset cleared.',
|
|
60
|
+
}),
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const configFile = scope === 'workspace' ? getWorkspaceConfigPath() : getGlobalConfigPath();
|
|
66
|
+
const saveFn = scope === 'workspace' ? saveWorkspaceConfig : saveGlobalConfig;
|
|
67
|
+
try {
|
|
68
|
+
await saveFn(config);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
if (err instanceof ConfigWriteError) {
|
|
72
|
+
return mcpError(ERROR_CODES.CONFIG_WRITE_ERROR, err.message, input);
|
|
73
|
+
}
|
|
74
|
+
return mcpError(ERROR_CODES.CONFIG_WRITE_ERROR, `Could not write ${scope} config: ${err instanceof Error ? err.message : String(err)}`, input);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: 'text',
|
|
80
|
+
text: JSON.stringify({
|
|
81
|
+
scope,
|
|
82
|
+
rulesetPath: resolvedPath,
|
|
83
|
+
auth: resolvedAuth ? { type: resolvedAuth.type } : null,
|
|
84
|
+
configFile,
|
|
85
|
+
message: resolvedPath !== null
|
|
86
|
+
? `${scope.charAt(0).toUpperCase() + scope.slice(1)} default ruleset configured. This setting will apply to all grading requests in this ${scope} unless overridden by a higher-precedence default or a per-request rulesetPath.`
|
|
87
|
+
: `${scope.charAt(0).toUpperCase() + scope.slice(1)} default ruleset cleared.`,
|
|
88
|
+
}),
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=configure-ruleset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configure-ruleset.js","sourceRoot":"","sources":["../../src/tools/configure-ruleset.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { SessionState } from '../types.js';
|
|
3
|
+
export declare function registerGetRulesetConfigTool(server: McpServer, sessionState: SessionState): void;
|
|
4
|
+
//# sourceMappingURL=get-ruleset-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-ruleset-config.d.ts","sourceRoot":"","sources":["../../src/tools/get-ruleset-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,aAAa,CAAC;AAQ5D,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,GAAG,IAAI,CA0DhG"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { loadWorkspaceConfig, loadGlobalConfig, getWorkspaceConfigPath, getGlobalConfigPath } from '../config/ruleset-config.js';
|
|
2
|
+
import { resolveRuleset } from '../config/resolve-ruleset.js';
|
|
3
|
+
function sanitizeAuth(auth, hasToken) {
|
|
4
|
+
if (!auth)
|
|
5
|
+
return null;
|
|
6
|
+
const tokenSource = hasToken ? 'config-file' : process.env.GITHUB_TOKEN ? 'env-var' : 'none';
|
|
7
|
+
return { type: auth.type, tokenSource };
|
|
8
|
+
}
|
|
9
|
+
export function registerGetRulesetConfigTool(server, sessionState) {
|
|
10
|
+
server.tool('get-ruleset-config', 'Return the active Spectral ruleset used by this MCP server when no rulesetPath is supplied on a grading request. Supports three scopes: session (in-memory, resets on server restart), workspace (persisted in workspace), and global (persisted to home). Return configuration at every scope, indicate which scope is currently in effect (the effective ruleset), and show the full resolution chain. Use this to diagnose why a particular ruleset is being applied or to confirm a set-ruleset-config call took effect.', {}, async () => {
|
|
11
|
+
const workspaceConfig = await loadWorkspaceConfig();
|
|
12
|
+
const globalConfig = await loadGlobalConfig();
|
|
13
|
+
const resolved = resolveRuleset(null, sessionState, workspaceConfig, globalConfig);
|
|
14
|
+
const sessionInfo = sessionState.defaultRuleset?.rulesetPath != null
|
|
15
|
+
? {
|
|
16
|
+
rulesetPath: sessionState.defaultRuleset.rulesetPath,
|
|
17
|
+
auth: sanitizeAuth(sessionState.defaultRuleset.auth, !!(sessionState.defaultRuleset.auth?.githubToken)),
|
|
18
|
+
}
|
|
19
|
+
: null;
|
|
20
|
+
const workspaceInfo = workspaceConfig?.rulesetPath != null
|
|
21
|
+
? {
|
|
22
|
+
rulesetPath: workspaceConfig.rulesetPath,
|
|
23
|
+
auth: sanitizeAuth(workspaceConfig.auth, false),
|
|
24
|
+
configFile: getWorkspaceConfigPath(),
|
|
25
|
+
}
|
|
26
|
+
: null;
|
|
27
|
+
const globalInfo = globalConfig?.rulesetPath != null
|
|
28
|
+
? {
|
|
29
|
+
rulesetPath: globalConfig.rulesetPath,
|
|
30
|
+
auth: sanitizeAuth(globalConfig.auth, false),
|
|
31
|
+
configFile: getGlobalConfigPath(),
|
|
32
|
+
}
|
|
33
|
+
: null;
|
|
34
|
+
const effectiveAuth = resolved.auth
|
|
35
|
+
? sanitizeAuth(resolved.auth, !!(resolved.auth.githubToken))
|
|
36
|
+
: null;
|
|
37
|
+
const response = {
|
|
38
|
+
effective: {
|
|
39
|
+
scope: resolved.scope,
|
|
40
|
+
rulesetPath: resolved.rulesetPath,
|
|
41
|
+
...(effectiveAuth ? { auth: effectiveAuth } : {}),
|
|
42
|
+
},
|
|
43
|
+
session: sessionInfo,
|
|
44
|
+
workspace: workspaceInfo,
|
|
45
|
+
global: globalInfo,
|
|
46
|
+
builtIn: 'default',
|
|
47
|
+
precedenceOrder: ['session', 'workspace', 'global', 'built-in'],
|
|
48
|
+
note: 'Per-request rulesetPath (if supplied on a grading call) always takes precedence over all configured defaults.',
|
|
49
|
+
};
|
|
50
|
+
return { content: [{ type: 'text', text: JSON.stringify(response) }] };
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=get-ruleset-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-ruleset-config.js","sourceRoot":"","sources":["../../src/tools/get-ruleset-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { SessionState } from '../types.js';
|
|
3
|
+
export declare function registerGradeDetailedTool(server: McpServer, sessionState: SessionState): void;
|
|
4
|
+
//# sourceMappingURL=grade-detailed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grade-detailed.d.ts","sourceRoot":"","sources":["../../src/tools/grade-detailed.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAMzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,GAAG,IAAI,CAqJ7F"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { statSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { GradeEngine } from '@dawmatt/api-grade-core';
|
|
6
|
+
import { mcpError, buildAuthFailureResponse, ERROR_CODES } from '../utils/errors.js';
|
|
7
|
+
import { loadWorkspaceConfig, loadGlobalConfig } from '../config/ruleset-config.js';
|
|
8
|
+
import { resolveRuleset } from '../config/resolve-ruleset.js';
|
|
9
|
+
import { fetchRulesetContent, RulesetAuthError, INITIAL_FETCH_TIMEOUT_MS, RETRY_FETCH_TIMEOUT_MS } from '../auth/github.js';
|
|
10
|
+
import { EntraAuthRequired, acquireEntraToken } from '../auth/entra.js';
|
|
11
|
+
const LARGE_SPEC_THRESHOLD_BYTES = 500_000;
|
|
12
|
+
const MAX_DIAGNOSTICS = 100;
|
|
13
|
+
export function registerGradeDetailedTool(server, sessionState) {
|
|
14
|
+
server.tool('grade-api-detailed', 'Grade an API specification and return the full result including all individual violations, per-category breakdowns, and prioritised recommendations. Use this when you need to analyse specific violations or present detailed findings to the user. Supports OpenAPI (2.x, 3.x) and AsyncAPI (2.x, 3.x).', {
|
|
15
|
+
specPath: z
|
|
16
|
+
.string()
|
|
17
|
+
.describe('Absolute or relative path to the OpenAPI or AsyncAPI specification file (YAML or JSON)'),
|
|
18
|
+
rulesetPath: z
|
|
19
|
+
.string()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe('Optional path to a custom Spectral-compatible ruleset file'),
|
|
22
|
+
recoveryOption: z
|
|
23
|
+
.enum(['retry', 'use-builtin-once', 'use-builtin-session', 'cancel'])
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('Recovery action when the configured default ruleset is inaccessible. Only supply in response to a RULESET_AUTH_FAILED response.'),
|
|
26
|
+
}, async ({ specPath, rulesetPath, recoveryOption }) => {
|
|
27
|
+
if (recoveryOption === 'cancel') {
|
|
28
|
+
return mcpError(ERROR_CODES.REQUEST_CANCELLED, 'Grading request cancelled by user.', { specPath });
|
|
29
|
+
}
|
|
30
|
+
if (recoveryOption === 'use-builtin-session') {
|
|
31
|
+
sessionState.sessionRulesetOverride = 'builtin';
|
|
32
|
+
}
|
|
33
|
+
const workspaceConfig = await loadWorkspaceConfig();
|
|
34
|
+
const globalConfig = await loadGlobalConfig();
|
|
35
|
+
const resolved = resolveRuleset(rulesetPath, sessionState, workspaceConfig, globalConfig);
|
|
36
|
+
let effectiveRulesetPath = resolved.rulesetPath ?? undefined;
|
|
37
|
+
let tempRulesetFile;
|
|
38
|
+
if (resolved.rulesetPath?.startsWith('http')) {
|
|
39
|
+
if (recoveryOption === 'use-builtin-once') {
|
|
40
|
+
effectiveRulesetPath = undefined;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
const timeoutMs = recoveryOption === 'retry' ? RETRY_FETCH_TIMEOUT_MS : INITIAL_FETCH_TIMEOUT_MS;
|
|
44
|
+
try {
|
|
45
|
+
let content;
|
|
46
|
+
if (resolved.auth?.type === 'github-pat') {
|
|
47
|
+
const token = resolved.auth.githubToken ?? process.env.GITHUB_TOKEN ?? '';
|
|
48
|
+
content = await fetchRulesetContent(resolved.rulesetPath, token || undefined, timeoutMs);
|
|
49
|
+
}
|
|
50
|
+
else if (resolved.auth?.type === 'entra-id' && resolved.auth.tenantId && resolved.auth.clientId) {
|
|
51
|
+
const token = await acquireEntraToken(resolved.auth.tenantId, resolved.auth.clientId);
|
|
52
|
+
content = await fetchRulesetContent(resolved.rulesetPath, token, timeoutMs);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
content = await fetchRulesetContent(resolved.rulesetPath, undefined, timeoutMs);
|
|
56
|
+
}
|
|
57
|
+
tempRulesetFile = join(tmpdir(), `api-grade-ruleset-${Date.now()}.yaml`);
|
|
58
|
+
writeFileSync(tempRulesetFile, content);
|
|
59
|
+
effectiveRulesetPath = tempRulesetFile;
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
if (err instanceof EntraAuthRequired) {
|
|
63
|
+
return {
|
|
64
|
+
content: [{
|
|
65
|
+
type: 'text',
|
|
66
|
+
text: JSON.stringify({
|
|
67
|
+
error: ERROR_CODES.ENTRA_AUTH_REQUIRED,
|
|
68
|
+
deviceCodeUrl: err.verificationUri,
|
|
69
|
+
userCode: err.userCode,
|
|
70
|
+
expiresIn: err.expiresIn,
|
|
71
|
+
message: `Complete Entra ID sign-in: Visit ${err.verificationUri} and enter code ${err.userCode}`,
|
|
72
|
+
}),
|
|
73
|
+
}],
|
|
74
|
+
isError: true,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const reason = err instanceof RulesetAuthError ? err.reason : 'network-unreachable';
|
|
78
|
+
return buildAuthFailureResponse(reason, resolved.rulesetPath, resolved.scope, `Could not fetch ruleset from '${resolved.rulesetPath}' (${resolved.scope} default): ${reason.replace('-', ' ')}.`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (effectiveRulesetPath) {
|
|
83
|
+
try {
|
|
84
|
+
statSync(effectiveRulesetPath);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return mcpError(ERROR_CODES.RULESET_NOT_FOUND, `The ruleset file '${effectiveRulesetPath}' does not exist. Check the path and try again.`, { rulesetPath: effectiveRulesetPath });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
let largeSpecWarning;
|
|
91
|
+
try {
|
|
92
|
+
const stat = statSync(specPath);
|
|
93
|
+
if (stat.size > LARGE_SPEC_THRESHOLD_BYTES) {
|
|
94
|
+
largeSpecWarning = `Specification exceeds 500KB (${stat.size} bytes); diagnostic results may be truncated`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
if (tempRulesetFile)
|
|
99
|
+
try {
|
|
100
|
+
unlinkSync(tempRulesetFile);
|
|
101
|
+
}
|
|
102
|
+
catch { /* ignore */ }
|
|
103
|
+
return mcpError(ERROR_CODES.SPEC_NOT_FOUND, `The specification file '${specPath}' does not exist. Check the path and try again.`, { specPath });
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const engine = new GradeEngine();
|
|
107
|
+
const result = await engine.grade({ specPath, rulesetPath: effectiveRulesetPath });
|
|
108
|
+
let truncated = false;
|
|
109
|
+
let diagnostics = result.diagnostics;
|
|
110
|
+
if (diagnostics.length > MAX_DIAGNOSTICS) {
|
|
111
|
+
diagnostics = diagnostics.slice(0, MAX_DIAGNOSTICS);
|
|
112
|
+
truncated = true;
|
|
113
|
+
}
|
|
114
|
+
const response = {
|
|
115
|
+
specPath: result.specPath,
|
|
116
|
+
format: result.format,
|
|
117
|
+
letterGrade: result.letterGrade,
|
|
118
|
+
gradeLabel: result.gradeLabel,
|
|
119
|
+
numericScore: result.numericScore,
|
|
120
|
+
summary: result.summary,
|
|
121
|
+
diagnostics,
|
|
122
|
+
rulesetSource: result.rulesetSource,
|
|
123
|
+
truncated,
|
|
124
|
+
};
|
|
125
|
+
if (largeSpecWarning) {
|
|
126
|
+
response.largeSpecWarning = largeSpecWarning;
|
|
127
|
+
}
|
|
128
|
+
return { content: [{ type: 'text', text: JSON.stringify(response) }] };
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
132
|
+
return mcpError(ERROR_CODES.GRADE_ENGINE_ERROR, `GradeEngine error: ${message}`, { specPath });
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
if (tempRulesetFile)
|
|
136
|
+
try {
|
|
137
|
+
unlinkSync(tempRulesetFile);
|
|
138
|
+
}
|
|
139
|
+
catch { /* ignore */ }
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=grade-detailed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grade-detailed.js","sourceRoot":"","sources":["../../src/tools/grade-detailed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grade.d.ts","sourceRoot":"","sources":["../../src/tools/grade.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAMzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,GAAG,IAAI,CA8IrF"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { statSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { GradeEngine } from '@dawmatt/api-grade-core';
|
|
6
|
+
import { mcpError, buildAuthFailureResponse, ERROR_CODES } from '../utils/errors.js';
|
|
7
|
+
import { loadWorkspaceConfig, loadGlobalConfig } from '../config/ruleset-config.js';
|
|
8
|
+
import { resolveRuleset } from '../config/resolve-ruleset.js';
|
|
9
|
+
import { fetchRulesetContent, RulesetAuthError, INITIAL_FETCH_TIMEOUT_MS, RETRY_FETCH_TIMEOUT_MS } from '../auth/github.js';
|
|
10
|
+
import { EntraAuthRequired, acquireEntraToken } from '../auth/entra.js';
|
|
11
|
+
const LARGE_SPEC_THRESHOLD_BYTES = 500_000;
|
|
12
|
+
export function registerGradeTool(server, sessionState) {
|
|
13
|
+
server.tool('grade-api', 'Grade an API specification file and return quality score, letter grade, and diagnostic summary. Use this for a token-efficient overview without the full violations list. Supports OpenAPI (2.x, 3.x) and AsyncAPI (2.x, 3.x) specifications in YAML or JSON.', {
|
|
14
|
+
specPath: z
|
|
15
|
+
.string()
|
|
16
|
+
.describe('Absolute or relative path to the OpenAPI or AsyncAPI specification file (YAML or JSON)'),
|
|
17
|
+
rulesetPath: z
|
|
18
|
+
.string()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe('Optional path to a custom Spectral-compatible ruleset file. If omitted, the default api-grade ruleset is used.'),
|
|
21
|
+
recoveryOption: z
|
|
22
|
+
.enum(['retry', 'use-builtin-once', 'use-builtin-session', 'cancel'])
|
|
23
|
+
.optional()
|
|
24
|
+
.describe('Recovery action when the configured default ruleset is inaccessible. Only supply in response to a RULESET_AUTH_FAILED response.'),
|
|
25
|
+
}, async ({ specPath, rulesetPath, recoveryOption }) => {
|
|
26
|
+
if (recoveryOption === 'cancel') {
|
|
27
|
+
return mcpError(ERROR_CODES.REQUEST_CANCELLED, 'Grading request cancelled by user.', { specPath });
|
|
28
|
+
}
|
|
29
|
+
if (recoveryOption === 'use-builtin-session') {
|
|
30
|
+
sessionState.sessionRulesetOverride = 'builtin';
|
|
31
|
+
}
|
|
32
|
+
const workspaceConfig = await loadWorkspaceConfig();
|
|
33
|
+
const globalConfig = await loadGlobalConfig();
|
|
34
|
+
const resolved = resolveRuleset(rulesetPath, sessionState, workspaceConfig, globalConfig);
|
|
35
|
+
let effectiveRulesetPath = resolved.rulesetPath ?? undefined;
|
|
36
|
+
let tempRulesetFile;
|
|
37
|
+
if (resolved.rulesetPath?.startsWith('http')) {
|
|
38
|
+
if (recoveryOption === 'use-builtin-once') {
|
|
39
|
+
effectiveRulesetPath = undefined;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const timeoutMs = recoveryOption === 'retry' ? RETRY_FETCH_TIMEOUT_MS : INITIAL_FETCH_TIMEOUT_MS;
|
|
43
|
+
try {
|
|
44
|
+
let content;
|
|
45
|
+
if (resolved.auth?.type === 'github-pat') {
|
|
46
|
+
const token = resolved.auth.githubToken ?? process.env.GITHUB_TOKEN ?? '';
|
|
47
|
+
content = await fetchRulesetContent(resolved.rulesetPath, token || undefined, timeoutMs);
|
|
48
|
+
}
|
|
49
|
+
else if (resolved.auth?.type === 'entra-id' && resolved.auth.tenantId && resolved.auth.clientId) {
|
|
50
|
+
const token = await acquireEntraToken(resolved.auth.tenantId, resolved.auth.clientId);
|
|
51
|
+
content = await fetchRulesetContent(resolved.rulesetPath, token, timeoutMs);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
content = await fetchRulesetContent(resolved.rulesetPath, undefined, timeoutMs);
|
|
55
|
+
}
|
|
56
|
+
tempRulesetFile = join(tmpdir(), `api-grade-ruleset-${Date.now()}.yaml`);
|
|
57
|
+
writeFileSync(tempRulesetFile, content);
|
|
58
|
+
effectiveRulesetPath = tempRulesetFile;
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
if (err instanceof EntraAuthRequired) {
|
|
62
|
+
return {
|
|
63
|
+
content: [{
|
|
64
|
+
type: 'text',
|
|
65
|
+
text: JSON.stringify({
|
|
66
|
+
error: ERROR_CODES.ENTRA_AUTH_REQUIRED,
|
|
67
|
+
deviceCodeUrl: err.verificationUri,
|
|
68
|
+
userCode: err.userCode,
|
|
69
|
+
expiresIn: err.expiresIn,
|
|
70
|
+
message: `Complete Entra ID sign-in: Visit ${err.verificationUri} and enter code ${err.userCode}`,
|
|
71
|
+
}),
|
|
72
|
+
}],
|
|
73
|
+
isError: true,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const reason = err instanceof RulesetAuthError ? err.reason : 'network-unreachable';
|
|
77
|
+
return buildAuthFailureResponse(reason, resolved.rulesetPath, resolved.scope, `Could not fetch ruleset from '${resolved.rulesetPath}' (${resolved.scope} default): ${reason.replace('-', ' ')}.`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else if (effectiveRulesetPath) {
|
|
82
|
+
try {
|
|
83
|
+
statSync(effectiveRulesetPath);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return mcpError(ERROR_CODES.RULESET_NOT_FOUND, `The ruleset file '${effectiveRulesetPath}' does not exist. Check the path and try again.`, { rulesetPath: effectiveRulesetPath });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
let largeSpecWarning;
|
|
90
|
+
try {
|
|
91
|
+
const stat = statSync(specPath);
|
|
92
|
+
if (stat.size > LARGE_SPEC_THRESHOLD_BYTES) {
|
|
93
|
+
largeSpecWarning = `Specification exceeds 500KB (${stat.size} bytes); diagnostic results may be truncated`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
if (tempRulesetFile)
|
|
98
|
+
try {
|
|
99
|
+
unlinkSync(tempRulesetFile);
|
|
100
|
+
}
|
|
101
|
+
catch { /* ignore */ }
|
|
102
|
+
return mcpError(ERROR_CODES.SPEC_NOT_FOUND, `The specification file '${specPath}' does not exist. Check the path and try again.`, { specPath });
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const engine = new GradeEngine();
|
|
106
|
+
const result = await engine.grade({ specPath, rulesetPath: effectiveRulesetPath });
|
|
107
|
+
const response = {
|
|
108
|
+
specPath: result.specPath,
|
|
109
|
+
format: result.format,
|
|
110
|
+
letterGrade: result.letterGrade,
|
|
111
|
+
gradeLabel: result.gradeLabel,
|
|
112
|
+
numericScore: result.numericScore,
|
|
113
|
+
summary: result.summary,
|
|
114
|
+
rulesetSource: result.rulesetSource,
|
|
115
|
+
};
|
|
116
|
+
if (largeSpecWarning) {
|
|
117
|
+
response.largeSpecWarning = largeSpecWarning;
|
|
118
|
+
}
|
|
119
|
+
return { content: [{ type: 'text', text: JSON.stringify(response) }] };
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
123
|
+
return mcpError(ERROR_CODES.GRADE_ENGINE_ERROR, `GradeEngine error: ${message}`, { specPath });
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
if (tempRulesetFile)
|
|
127
|
+
try {
|
|
128
|
+
unlinkSync(tempRulesetFile);
|
|
129
|
+
}
|
|
130
|
+
catch { /* ignore */ }
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=grade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grade.js","sourceRoot":"","sources":["../../src/tools/grade.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"non-breaking.d.ts","sourceRoot":"","sources":["../../src/tools/non-breaking.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,GAAG,IAAI,CAgJ3F"}
|