@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.
Files changed (70) hide show
  1. package/README.md +128 -0
  2. package/dist/auth/entra.d.ts +8 -0
  3. package/dist/auth/entra.d.ts.map +1 -0
  4. package/dist/auth/entra.js +59 -0
  5. package/dist/auth/entra.js.map +1 -0
  6. package/dist/auth/github.d.ts +10 -0
  7. package/dist/auth/github.d.ts.map +1 -0
  8. package/dist/auth/github.js +42 -0
  9. package/dist/auth/github.js.map +1 -0
  10. package/dist/config/resolve-ruleset.d.ts +3 -0
  11. package/dist/config/resolve-ruleset.d.ts.map +1 -0
  12. package/dist/config/resolve-ruleset.js +31 -0
  13. package/dist/config/resolve-ruleset.js.map +1 -0
  14. package/dist/config/ruleset-config.d.ts +13 -0
  15. package/dist/config/ruleset-config.d.ts.map +1 -0
  16. package/dist/config/ruleset-config.js +49 -0
  17. package/dist/config/ruleset-config.js.map +1 -0
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +6 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/server.d.ts +3 -0
  23. package/dist/server.d.ts.map +1 -0
  24. package/dist/server.js +33 -0
  25. package/dist/server.js.map +1 -0
  26. package/dist/tools/assert-grade.d.ts +4 -0
  27. package/dist/tools/assert-grade.d.ts.map +1 -0
  28. package/dist/tools/assert-grade.js +132 -0
  29. package/dist/tools/assert-grade.js.map +1 -0
  30. package/dist/tools/configure-ruleset.d.ts +4 -0
  31. package/dist/tools/configure-ruleset.d.ts.map +1 -0
  32. package/dist/tools/configure-ruleset.js +94 -0
  33. package/dist/tools/configure-ruleset.js.map +1 -0
  34. package/dist/tools/get-ruleset-config.d.ts +4 -0
  35. package/dist/tools/get-ruleset-config.d.ts.map +1 -0
  36. package/dist/tools/get-ruleset-config.js +53 -0
  37. package/dist/tools/get-ruleset-config.js.map +1 -0
  38. package/dist/tools/grade-detailed.d.ts +4 -0
  39. package/dist/tools/grade-detailed.d.ts.map +1 -0
  40. package/dist/tools/grade-detailed.js +143 -0
  41. package/dist/tools/grade-detailed.js.map +1 -0
  42. package/dist/tools/grade.d.ts +4 -0
  43. package/dist/tools/grade.d.ts.map +1 -0
  44. package/dist/tools/grade.js +134 -0
  45. package/dist/tools/grade.js.map +1 -0
  46. package/dist/tools/non-breaking.d.ts +4 -0
  47. package/dist/tools/non-breaking.d.ts.map +1 -0
  48. package/dist/tools/non-breaking.js +138 -0
  49. package/dist/tools/non-breaking.js.map +1 -0
  50. package/dist/tools/quick-fixes-only.d.ts +4 -0
  51. package/dist/tools/quick-fixes-only.d.ts.map +1 -0
  52. package/dist/tools/quick-fixes-only.js +138 -0
  53. package/dist/tools/quick-fixes-only.js.map +1 -0
  54. package/dist/tools/set-ruleset-config.d.ts +4 -0
  55. package/dist/tools/set-ruleset-config.d.ts.map +1 -0
  56. package/dist/tools/set-ruleset-config.js +94 -0
  57. package/dist/tools/set-ruleset-config.js.map +1 -0
  58. package/dist/types.d.ts +27 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +2 -0
  61. package/dist/types.js.map +1 -0
  62. package/dist/utils/classify.d.ts +14 -0
  63. package/dist/utils/classify.d.ts.map +1 -0
  64. package/dist/utils/classify.js +123 -0
  65. package/dist/utils/classify.js.map +1 -0
  66. package/dist/utils/errors.d.ts +33 -0
  67. package/dist/utils/errors.d.ts.map +1 -0
  68. package/dist/utils/errors.js +56 -0
  69. package/dist/utils/errors.js.map +1 -0
  70. package/package.json +61 -0
@@ -0,0 +1,138 @@
1
+ import { statSync, readFileSync, 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
+ import { classifyViolation, buildNonBreakingViolation } from '../utils/classify.js';
12
+ const LARGE_SPEC_THRESHOLD_BYTES = 500_000;
13
+ export function registerNonBreakingTool(server, sessionState) {
14
+ server.tool('get-non-breaking-violations', 'Return a classified, AI-actionable list of non-breaking violations in an API specification. Non-breaking violations are those whose fixes do not alter the API interface contract (paths, methods, required parameters, schema types, or response structures). Use this tool to obtain issues the AI can safely resolve — the AI generates the corrected specification content; the MCP server does not modify files.', {
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
+ let specContent = '';
92
+ try {
93
+ const stat = statSync(specPath);
94
+ if (stat.size > LARGE_SPEC_THRESHOLD_BYTES) {
95
+ largeSpecWarning = `Specification exceeds 500KB (${stat.size} bytes); diagnostic results may be truncated`;
96
+ }
97
+ specContent = readFileSync(specPath, 'utf-8');
98
+ }
99
+ catch {
100
+ if (tempRulesetFile)
101
+ try {
102
+ unlinkSync(tempRulesetFile);
103
+ }
104
+ catch { /* ignore */ }
105
+ return mcpError(ERROR_CODES.SPEC_NOT_FOUND, `The specification file '${specPath}' does not exist. Check the path and try again.`, { specPath });
106
+ }
107
+ try {
108
+ const engine = new GradeEngine();
109
+ const result = await engine.grade({ specPath, rulesetPath: effectiveRulesetPath });
110
+ const nonBreakingViolations = result.diagnostics
111
+ .filter((d) => classifyViolation(d) === 'nonBreaking')
112
+ .map((d) => buildNonBreakingViolation(d, specContent));
113
+ const response = {
114
+ specPath: result.specPath,
115
+ format: result.format,
116
+ totalViolations: result.diagnostics.length,
117
+ nonBreakingCount: nonBreakingViolations.length,
118
+ nonBreakingViolations,
119
+ };
120
+ if (largeSpecWarning) {
121
+ response.largeSpecWarning = largeSpecWarning;
122
+ }
123
+ return { content: [{ type: 'text', text: JSON.stringify(response) }] };
124
+ }
125
+ catch (err) {
126
+ const message = err instanceof Error ? err.message : String(err);
127
+ return mcpError(ERROR_CODES.GRADE_ENGINE_ERROR, `GradeEngine error: ${message}`, { specPath });
128
+ }
129
+ finally {
130
+ if (tempRulesetFile)
131
+ try {
132
+ unlinkSync(tempRulesetFile);
133
+ }
134
+ catch { /* ignore */ }
135
+ }
136
+ });
137
+ }
138
+ //# sourceMappingURL=non-breaking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"non-breaking.js","sourceRoot":"","sources":["../../src/tools/non-breaking.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 registerQuickFixesOnlyTool(server: McpServer, sessionState: SessionState): void;
4
+ //# sourceMappingURL=quick-fixes-only.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quick-fixes-only.d.ts","sourceRoot":"","sources":["../../src/tools/quick-fixes-only.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,GAAG,IAAI,CAgJ9F"}
@@ -0,0 +1,138 @@
1
+ import { statSync, readFileSync, 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
+ import { classifyViolation, buildQuickFix } from '../utils/classify.js';
12
+ const LARGE_SPEC_THRESHOLD_BYTES = 500_000;
13
+ export function registerQuickFixesOnlyTool(server, sessionState) {
14
+ server.tool('grade-api-quick-fixes-only', 'Return a classified, AI-actionable list of quick fixes for an API specification. Quick fixes are improvements that can be made via non-breaking changes — those that do not alter the API interface contract (paths, methods, required parameters, schema types, or response structures). Use this tool (not grade-api-detailed) when the goal is for the AI to safely resolve violations; the AI generates the corrected specification content and the MCP server does not modify files.', {
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
+ let specContent = '';
92
+ try {
93
+ const stat = statSync(specPath);
94
+ if (stat.size > LARGE_SPEC_THRESHOLD_BYTES) {
95
+ largeSpecWarning = `Specification exceeds 500KB (${stat.size} bytes); diagnostic results may be truncated`;
96
+ }
97
+ specContent = readFileSync(specPath, 'utf-8');
98
+ }
99
+ catch {
100
+ if (tempRulesetFile)
101
+ try {
102
+ unlinkSync(tempRulesetFile);
103
+ }
104
+ catch { /* ignore */ }
105
+ return mcpError(ERROR_CODES.SPEC_NOT_FOUND, `The specification file '${specPath}' does not exist. Check the path and try again.`, { specPath });
106
+ }
107
+ try {
108
+ const engine = new GradeEngine();
109
+ const result = await engine.grade({ specPath, rulesetPath: effectiveRulesetPath });
110
+ const quickFixes = result.diagnostics
111
+ .filter((d) => classifyViolation(d) === 'nonBreaking')
112
+ .map((d) => buildQuickFix(d, specContent));
113
+ const response = {
114
+ specPath: result.specPath,
115
+ format: result.format,
116
+ totalViolations: result.diagnostics.length,
117
+ quickFixCount: quickFixes.length,
118
+ quickFixes,
119
+ };
120
+ if (largeSpecWarning) {
121
+ response.largeSpecWarning = largeSpecWarning;
122
+ }
123
+ return { content: [{ type: 'text', text: JSON.stringify(response) }] };
124
+ }
125
+ catch (err) {
126
+ const message = err instanceof Error ? err.message : String(err);
127
+ return mcpError(ERROR_CODES.GRADE_ENGINE_ERROR, `GradeEngine error: ${message}`, { specPath });
128
+ }
129
+ finally {
130
+ if (tempRulesetFile)
131
+ try {
132
+ unlinkSync(tempRulesetFile);
133
+ }
134
+ catch { /* ignore */ }
135
+ }
136
+ });
137
+ }
138
+ //# sourceMappingURL=quick-fixes-only.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quick-fixes-only.js","sourceRoot":"","sources":["../../src/tools/quick-fixes-only.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 registerSetRulesetConfigTool(server: McpServer, sessionState: SessionState): void;
4
+ //# sourceMappingURL=set-ruleset-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"set-ruleset-config.d.ts","sourceRoot":"","sources":["../../src/tools/set-ruleset-config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AASzE,OAAO,KAAK,EAAE,YAAY,EAA6B,MAAM,aAAa,CAAC;AAE3E,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,GAAG,IAAI,CAwHhG"}
@@ -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 registerSetRulesetConfigTool(server, sessionState) {
5
+ server.tool('set-ruleset-config', 'Set the default 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 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=set-ruleset-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"set-ruleset-config.js","sourceRoot":"","sources":["../../src/tools/set-ruleset-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ export interface AuthConfig {
2
+ type: 'github-pat' | 'entra-id';
3
+ githubToken?: string;
4
+ tenantId?: string;
5
+ clientId?: string;
6
+ }
7
+ export interface RulesetConfig {
8
+ rulesetPath: string | null;
9
+ auth: AuthConfig | null;
10
+ }
11
+ export type RulesetScope = 'per-request' | 'session' | 'workspace' | 'global' | 'built-in';
12
+ export interface RulesetResolution {
13
+ rulesetPath: string | null;
14
+ scope: RulesetScope;
15
+ auth: AuthConfig | null;
16
+ }
17
+ export interface SessionState {
18
+ defaultRuleset: RulesetConfig | null;
19
+ sessionRulesetOverride: 'builtin' | null;
20
+ }
21
+ export type RecoveryOptionId = 'retry' | 'use-builtin-once' | 'use-builtin-session' | 'cancel';
22
+ export interface RecoveryOption {
23
+ id: RecoveryOptionId;
24
+ label: string;
25
+ description: string;
26
+ }
27
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,YAAY,GAAG,UAAU,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE3F,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,YAAY,CAAC;IACpB,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC,sBAAsB,EAAE,SAAS,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,kBAAkB,GAAG,qBAAqB,GAAG,QAAQ,CAAC;AAE/F,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,gBAAgB,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,14 @@
1
+ import type { Diagnostic } from '@dawmatt/api-grade-core';
2
+ export type ViolationClass = 'nonBreaking' | 'breaking' | 'unknown';
3
+ export declare function classifyViolation(diagnostic: Diagnostic): ViolationClass;
4
+ export interface QuickFix {
5
+ ruleId: string;
6
+ message: string;
7
+ severity: string;
8
+ path: string[];
9
+ location: string;
10
+ currentValue: string | null;
11
+ expectedImprovement: string;
12
+ }
13
+ export declare function buildQuickFix(diagnostic: Diagnostic, specContent: string): QuickFix;
14
+ //# sourceMappingURL=classify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../../src/utils/classify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,UAAU,GAAG,SAAS,CAAC;AAgDpE,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,cAAc,CAWxE;AASD,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,wBAAgB,aAAa,CAC3B,UAAU,EAAE,UAAU,EACtB,WAAW,EAAE,MAAM,GAClB,QAAQ,CAsCV"}
@@ -0,0 +1,123 @@
1
+ const RULE_ID_NON_BREAKING_PREFIXES = [
2
+ 'operation-description',
3
+ 'operation-summary',
4
+ 'info-contact',
5
+ 'info-description',
6
+ 'info-license',
7
+ 'oas3-examples-',
8
+ 'tag-description',
9
+ ];
10
+ const NON_BREAKING_SEGMENTS = new Set([
11
+ 'description',
12
+ 'summary',
13
+ 'title',
14
+ 'contact',
15
+ 'license',
16
+ 'termsOfService',
17
+ 'externalDocs',
18
+ 'example',
19
+ 'examples',
20
+ 'tags',
21
+ 'info',
22
+ ]);
23
+ const BREAKING_SEGMENTS = new Set([
24
+ 'required',
25
+ 'type',
26
+ 'format',
27
+ ]);
28
+ function isNonBreakingPath(path) {
29
+ for (const segment of path) {
30
+ if (segment.startsWith('x-'))
31
+ return true;
32
+ if (NON_BREAKING_SEGMENTS.has(segment))
33
+ return true;
34
+ }
35
+ return false;
36
+ }
37
+ function isBreakingPath(path) {
38
+ for (const segment of path) {
39
+ if (BREAKING_SEGMENTS.has(segment))
40
+ return true;
41
+ if (segment === 'parameters')
42
+ return true;
43
+ }
44
+ return false;
45
+ }
46
+ export function classifyViolation(diagnostic) {
47
+ // Rule ID overrides take priority
48
+ for (const prefix of RULE_ID_NON_BREAKING_PREFIXES) {
49
+ if (diagnostic.ruleId.startsWith(prefix))
50
+ return 'nonBreaking';
51
+ }
52
+ const path = diagnostic.path ?? [];
53
+ if (isBreakingPath(path))
54
+ return 'breaking';
55
+ if (isNonBreakingPath(path))
56
+ return 'nonBreaking';
57
+ return 'unknown';
58
+ }
59
+ const SEVERITY_LABELS = {
60
+ 0: 'error',
61
+ 1: 'warn',
62
+ 2: 'info',
63
+ 3: 'hint',
64
+ };
65
+ export function buildQuickFix(diagnostic, specContent) {
66
+ const path = (diagnostic.path ?? []);
67
+ const location = path.join('.');
68
+ let currentValue = null;
69
+ try {
70
+ if (path.length > 0) {
71
+ const parsed = JSON.parse(specContent);
72
+ let node = parsed;
73
+ for (const segment of path) {
74
+ if (node === null || typeof node !== 'object') {
75
+ node = undefined;
76
+ break;
77
+ }
78
+ node = node[segment];
79
+ }
80
+ if (node !== undefined && node !== null) {
81
+ currentValue = typeof node === 'string' ? node : JSON.stringify(node);
82
+ }
83
+ }
84
+ }
85
+ catch {
86
+ // JSON parse failed (e.g. YAML spec) — leave currentValue as null
87
+ }
88
+ const lastSegment = path[path.length - 1] ?? 'field';
89
+ const expectedImprovement = deriveExpectedImprovement(diagnostic.ruleId, diagnostic.message, lastSegment, path);
90
+ const severityNum = typeof diagnostic.severity === 'number' ? diagnostic.severity : 1;
91
+ return {
92
+ ruleId: diagnostic.ruleId,
93
+ message: diagnostic.message,
94
+ severity: SEVERITY_LABELS[severityNum] ?? 'warn',
95
+ path,
96
+ location,
97
+ currentValue,
98
+ expectedImprovement,
99
+ };
100
+ }
101
+ function deriveExpectedImprovement(ruleId, message, lastSegment, path) {
102
+ if (ruleId.includes('description')) {
103
+ const entity = path.length > 1 ? path[path.length - 2] : 'item';
104
+ return `Add a \`description\` field that explains the purpose of this ${entity}`;
105
+ }
106
+ if (ruleId.includes('summary')) {
107
+ return `Add a \`summary\` field with a brief one-line description`;
108
+ }
109
+ if (ruleId.includes('contact')) {
110
+ return `Add a \`contact\` object to the info block with name, email, or url`;
111
+ }
112
+ if (ruleId.includes('license')) {
113
+ return `Add a \`license\` object to the info block with name and url`;
114
+ }
115
+ if (ruleId.includes('example')) {
116
+ return `Add an \`example\` or \`examples\` field illustrating expected values`;
117
+ }
118
+ if (ruleId.includes('tag-description')) {
119
+ return `Add a \`description\` field to this tag explaining its purpose`;
120
+ }
121
+ return `Fix: ${message}. Add or update \`${lastSegment}\` as required`;
122
+ }
123
+ //# sourceMappingURL=classify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/utils/classify.ts"],"names":[],"mappings":"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"}