Skip to content

Commit 9309187

Browse files
committed
Implemented tsvValidate
1 parent 701f49b commit 9309187

15 files changed

+520
-627
lines changed

package-lock.json

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/server.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ import {
1616
ValidateHedStringArgs
1717
} from "./tools/validateHedString.js";
1818
import {
19-
validateHedSidecar,
20-
handleValidateHedSidecar,
21-
ValidateHedSidecarArgs
22-
} from "./tools/validateHedSidecar.js";
19+
parseHedSidecar,
20+
handleParseHedSidecar,
21+
ParseHedSidecarArgs
22+
} from "./tools/parseHedSidecar.js";
2323
import {
2424
validateHedTsv,
2525
handleValidateHedTsv,
@@ -64,7 +64,7 @@ class HEDMCPServer {
6464
// List available tools
6565
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
6666
return {
67-
tools: [validateHedString, validateHedSidecar, validateHedTsv],
67+
tools: [validateHedString, parseHedSidecar, validateHedTsv],
6868
};
6969
});
7070

@@ -84,8 +84,8 @@ class HEDMCPServer {
8484
],
8585
};
8686

87-
case "validateHedSidecar":
88-
const sidecarResult = await handleValidateHedSidecar(args as ValidateHedSidecarArgs);
87+
case "parseHedSidecar":
88+
const sidecarResult = await handleParseHedSidecar(args as ParseHedSidecarArgs);
8989
return {
9090
content: [
9191
{

src/tools/parseHedSidecar.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { Tool } from "@modelcontextprotocol/sdk/types.js";
2+
import { z } from "zod";
3+
import * as path from "path";
4+
import { FormattedIssue, ParseHedSidecarResult } from "../types/index.js";
5+
import { formatIssue, formatIssues, separateIssuesBySeverity } from "../utils/issueFormatter.js";
6+
import { readFileFromPath } from "../utils/fileReader.js";
7+
import { schemaCache } from '../utils/schemaCache.js';
8+
import { mcpToZod } from '../utils/mcpToZod.js';
9+
10+
// Import HED validation functions
11+
import { buildSchemasFromVersion, BidsSidecar } from "hed-validator";
12+
13+
// Define the MCP inputSchema first
14+
const parseHedSidecarInputSchema = {
15+
type: "object" as const,
16+
properties: {
17+
filePath: {
18+
type: "string" as const,
19+
description: "The absolute path to the sidecar file to parse"
20+
},
21+
hedVersion: {
22+
type: "string" as const,
23+
description: "The HED schema version to use (e.g., 8.4.0 or lang_1.1.0, score_2.1.0)"
24+
},
25+
checkForWarnings: {
26+
type: "boolean" as const,
27+
description: "Whether to check for warnings in addition to errors",
28+
default: false
29+
},
30+
fileData: {
31+
type: "string" as const,
32+
description: "Optional JSON string containing the sidecar data to use instead of reading from filePath"
33+
}
34+
},
35+
required: ["filePath", "hedVersion"]
36+
};
37+
38+
// Generate Zod schema from MCP schema
39+
const ParseHedSidecarSchema = mcpToZod(parseHedSidecarInputSchema);
40+
41+
export type ParseHedSidecarArgs = z.infer<typeof ParseHedSidecarSchema>;
42+
43+
/**
44+
* Tool definition for parsing HED sidecar files
45+
*/
46+
export const parseHedSidecar: Tool = {
47+
name: "parseHedSidecar",
48+
description: "Parses a HED sidecar file using the specified HED schema version",
49+
inputSchema: parseHedSidecarInputSchema
50+
};
51+
52+
/**
53+
* Parse a HED sidecar file using the specified HED schema version.
54+
*/
55+
export async function handleParseHedSidecar(args: ParseHedSidecarArgs): Promise<ParseHedSidecarResult> {
56+
const { filePath, hedVersion, checkForWarnings = false, fileData } = args;
57+
58+
try {
59+
// Use schema cache to get or create the HED schemas
60+
const hedSchemas = await schemaCache.getOrCreateSchema(hedVersion);
61+
62+
// Get the file data if not provided
63+
let data = fileData;
64+
if (!data) {
65+
data = await readFileFromPath(filePath);
66+
}
67+
68+
// Parse JSON data (fileData is always a string now)
69+
const jsonData = JSON.parse(data);
70+
71+
const fileName = path.basename(filePath) || "sidecar.json"; // Properly extract filename using path.basename
72+
const sidecar = new BidsSidecar(fileName, { path: filePath, name: fileName }, jsonData);
73+
const validationIssues = sidecar.validate(hedSchemas);
74+
75+
// Format all validation issues
76+
const allFormattedIssues = formatIssues(validationIssues);
77+
78+
// Separate issues by severity
79+
const { errors: formattedErrors, others: formattedWarnings } = separateIssuesBySeverity(allFormattedIssues);
80+
81+
// Only include warnings if checkForWarnings is true
82+
const finalWarnings = checkForWarnings ? formattedWarnings : [];
83+
84+
return {
85+
parsedHedSidecar: JSON.stringify(jsonData),
86+
errors: formattedErrors,
87+
warnings: finalWarnings
88+
};
89+
90+
} catch (error) {
91+
return {parsedHedSidecar: "", errors: [formatIssue(error)], warnings: []};
92+
}
93+
}

src/tools/validateHedSidecar.ts

Lines changed: 0 additions & 143 deletions
This file was deleted.

src/tools/validateHedString.ts

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Tool } from "@modelcontextprotocol/sdk/types.js";
22
import { z } from "zod";
33
import { FormattedIssue, HedValidationResult } from "../types/index.js";
4-
import { formatIssues, separateIssuesBySeverity } from "../utils/issueFormatter.js";
4+
import { formatIssue, formatIssues, separateIssuesBySeverity } from "../utils/issueFormatter.js";
55
import { schemaCache } from '../utils/schemaCache.js';
66
import { createDefinitionManager } from '../utils/definitionProcessor.js';
77
import { mcpToZod } from '../utils/mcpToZod.js';
@@ -67,11 +67,7 @@ export async function handleValidateHedString(args: ValidateHedStringArgs): Prom
6767

6868
// If there are errors in definition processing, return them immediately
6969
if (definitionResult.errors.length > 0) {
70-
return {
71-
isValid: false,
72-
errors: definitionResult.errors,
73-
warnings: checkForWarnings ? definitionResult.warnings : []
74-
};
70+
return {errors: definitionResult.errors, warnings: checkForWarnings ? definitionResult.warnings : []};
7571
}
7672

7773
const defManager = definitionResult.definitionManager;
@@ -87,30 +83,13 @@ export async function handleValidateHedString(args: ValidateHedStringArgs): Prom
8783

8884
// For HED string validation, errors and warnings come pre-separated from parseStandaloneString
8985
// But we could also use separateIssuesBySeverity if we had mixed issues
90-
const isValid = allErrors.length === 0;
9186

9287
// Combine definition warnings with HED string warnings (only if checkForWarnings is true)
9388
const finalWarnings = checkForWarnings ? [...definitionWarnings, ...allWarnings] : [];
9489

95-
return {
96-
isValid: isValid,
97-
errors: allErrors,
98-
warnings: finalWarnings
99-
};
90+
return { errors: allErrors, warnings: finalWarnings};
10091

10192
} catch (error) {
102-
return {
103-
isValid: false,
104-
errors: [{
105-
code: "INTERNAL_ERROR",
106-
detailedCode: "unExpectedErrorDuringValidation",
107-
severity: "error",
108-
message: `Validation failed: ${error && typeof error === "object" && "message" in error ? error.message : error instanceof Error ? error.message : 'Unknown error'}`,
109-
line: "",
110-
column: "",
111-
location: ""
112-
} as FormattedIssue],
113-
warnings: []
114-
};
93+
return { errors: [formatIssue(error)], warnings: []};
11594
}
11695
}

0 commit comments

Comments
 (0)