Skip to content

Commit 1e68676

Browse files
Update tool name validation to support dots, slashes, and 128-char names
Co-authored-by: me <me@kentcdodds.com>
1 parent d4c7e26 commit 1e68676

File tree

2 files changed

+45
-37
lines changed

2 files changed

+45
-37
lines changed

src/shared/toolNameValidation.test.ts

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,20 @@ describe('validateToolName', () => {
3131
expect(result.warnings).toHaveLength(0);
3232
});
3333

34-
test('should reject names with dots', () => {
34+
test('should accept names with dots', () => {
3535
const result = validateToolName('admin.tools.list');
36-
expect(result.isValid).toBe(false);
37-
expect(result.warnings).toContain('Tool name contains invalid characters: "."');
36+
expect(result.isValid).toBe(true);
37+
expect(result.warnings).toHaveLength(0);
3838
});
3939

40-
test('should reject names with forward slashes', () => {
40+
test('should accept names with forward slashes', () => {
4141
const result = validateToolName('user/profile/update');
42-
expect(result.isValid).toBe(false);
43-
expect(result.warnings).toContain('Tool name contains invalid characters: "/"');
42+
expect(result.isValid).toBe(true);
43+
expect(result.warnings).toHaveLength(0);
4444
});
4545

4646
test('should accept mixed character names', () => {
47-
const result = validateToolName('DATA_EXPORT_v2-1');
47+
const result = validateToolName('DATA_EXPORT_v2.1');
4848
expect(result.isValid).toBe(true);
4949
expect(result.warnings).toHaveLength(0);
5050
});
@@ -55,8 +55,8 @@ describe('validateToolName', () => {
5555
expect(result.warnings).toHaveLength(0);
5656
});
5757

58-
test('should accept 64 character names', () => {
59-
const name = 'a'.repeat(64);
58+
test('should accept 128 character names', () => {
59+
const name = 'a'.repeat(128);
6060
const result = validateToolName(name);
6161
expect(result.isValid).toBe(true);
6262
expect(result.warnings).toHaveLength(0);
@@ -70,11 +70,11 @@ describe('validateToolName', () => {
7070
expect(result.warnings).toContain('Tool name cannot be empty');
7171
});
7272

73-
test('should reject names longer than 64 characters', () => {
74-
const name = 'a'.repeat(65);
73+
test('should reject names longer than 128 characters', () => {
74+
const name = 'a'.repeat(129);
7575
const result = validateToolName(name);
7676
expect(result.isValid).toBe(false);
77-
expect(result.warnings).toContain('Tool name exceeds maximum length of 64 characters (current: 65)');
77+
expect(result.warnings).toContain('Tool name exceeds maximum length of 128 characters (current: 129)');
7878
});
7979

8080
test('should reject names with spaces', () => {
@@ -92,7 +92,7 @@ describe('validateToolName', () => {
9292
test('should reject names with other special characters', () => {
9393
const result = validateToolName('user@domain.com');
9494
expect(result.isValid).toBe(false);
95-
expect(result.warnings).toContain('Tool name contains invalid characters: "@", "."');
95+
expect(result.warnings).toContain('Tool name contains invalid characters: "@"');
9696
});
9797

9898
test('should reject names with multiple invalid characters', () => {
@@ -133,22 +133,22 @@ describe('validateToolName', () => {
133133
expect(result.warnings).toContain('Tool name starts or ends with a dash, which may cause parsing issues in some contexts');
134134
});
135135

136-
test('should reject names starting with dot', () => {
136+
test('should warn about names starting with dot', () => {
137137
const result = validateToolName('.get.user');
138-
expect(result.isValid).toBe(false);
139-
expect(result.warnings).toContain('Tool name contains invalid characters: "."');
138+
expect(result.isValid).toBe(true);
139+
expect(result.warnings).toContain('Tool name starts or ends with a dot, which may cause parsing issues in some contexts');
140140
});
141141

142-
test('should reject names ending with dot', () => {
142+
test('should warn about names ending with dot', () => {
143143
const result = validateToolName('get.user.');
144-
expect(result.isValid).toBe(false);
145-
expect(result.warnings).toContain('Tool name contains invalid characters: "."');
144+
expect(result.isValid).toBe(true);
145+
expect(result.warnings).toContain('Tool name starts or ends with a dot, which may cause parsing issues in some contexts');
146146
});
147147

148-
test('should reject names with both leading and trailing dots', () => {
148+
test('should warn about names with both leading and trailing dots', () => {
149149
const result = validateToolName('.get.user.');
150-
expect(result.isValid).toBe(false);
151-
expect(result.warnings).toContain('Tool name contains invalid characters: "."');
150+
expect(result.isValid).toBe(true);
151+
expect(result.warnings).toContain('Tool name starts or ends with a dot, which may cause parsing issues in some contexts');
152152
});
153153
});
154154
});
@@ -202,18 +202,18 @@ describe('validateAndWarnToolName', () => {
202202
});
203203

204204
test('should return false for names exceeding length limit', () => {
205-
const longName = 'a'.repeat(65);
205+
const longName = 'a'.repeat(129);
206206
const result = validateAndWarnToolName(longName);
207207
expect(result).toBe(false);
208208
expect(warnSpy).toHaveBeenCalled(); // Now issues warnings instead of errors
209209
});
210210
});
211211

212212
describe('edge cases and robustness', () => {
213-
test('should reject names with only dots', () => {
213+
test('should warn about names with only dots', () => {
214214
const result = validateToolName('...');
215-
expect(result.isValid).toBe(false);
216-
expect(result.warnings).toContain('Tool name contains invalid characters: "."');
215+
expect(result.isValid).toBe(true);
216+
expect(result.warnings).toContain('Tool name starts or ends with a dot, which may cause parsing issues in some contexts');
217217
});
218218

219219
test('should handle names with only dashes', () => {
@@ -222,10 +222,10 @@ describe('edge cases and robustness', () => {
222222
expect(result.warnings).toContain('Tool name starts or ends with a dash, which may cause parsing issues in some contexts');
223223
});
224224

225-
test('should reject names with only forward slashes', () => {
225+
test('should warn about names with only forward slashes', () => {
226226
const result = validateToolName('///');
227-
expect(result.isValid).toBe(false);
228-
expect(result.warnings).toContain('Tool name contains invalid characters: "/"');
227+
expect(result.isValid).toBe(true);
228+
expect(result.warnings).toContain('Tool name starts or ends with a slash, which may cause parsing issues in some contexts');
229229
});
230230

231231
test('should handle names with mixed valid and invalid characters', () => {

src/shared/toolNameValidation.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
*/
1010

1111
/**
12-
* Regular expression for valid tool names according to the MCP SEP specification
12+
* Regular expression for valid tool names according to SEP-986 specification
1313
*/
14-
const TOOL_NAME_REGEX = /^[A-Za-z0-9_-]{1,64}$/;
14+
const TOOL_NAME_REGEX = /^[A-Za-z0-9._/-]{1,128}$/;
1515

1616
/**
1717
* Validates a tool name according to the SEP specification
@@ -32,10 +32,10 @@ export function validateToolName(name: string): {
3232
};
3333
}
3434

35-
if (name.length > 64) {
35+
if (name.length > 128) {
3636
return {
3737
isValid: false,
38-
warnings: [`Tool name exceeds maximum length of 64 characters (current: ${name.length})`]
38+
warnings: [`Tool name exceeds maximum length of 128 characters (current: ${name.length})`]
3939
};
4040
}
4141

@@ -48,21 +48,29 @@ export function validateToolName(name: string): {
4848
warnings.push("Tool name contains commas, which may cause parsing issues");
4949
}
5050

51-
// Check for potentially confusing patterns (only hyphens, not dots/slashes)
51+
// Check for potentially confusing patterns (leading/trailing dashes, dots, slashes)
5252
if (name.startsWith('-') || name.endsWith('-')) {
5353
warnings.push("Tool name starts or ends with a dash, which may cause parsing issues in some contexts");
5454
}
5555

56-
// Check for invalid characters (including dots and slashes)
56+
if (name.startsWith('.') || name.endsWith('.')) {
57+
warnings.push("Tool name starts or ends with a dot, which may cause parsing issues in some contexts");
58+
}
59+
60+
if (name.startsWith('/') || name.endsWith('/')) {
61+
warnings.push("Tool name starts or ends with a slash, which may cause parsing issues in some contexts");
62+
}
63+
64+
// Check for invalid characters
5765
if (!TOOL_NAME_REGEX.test(name)) {
5866
const invalidChars = name
5967
.split('')
60-
.filter(char => !/[A-Za-z0-9_-]/.test(char))
68+
.filter(char => !/[A-Za-z0-9._/-]/.test(char))
6169
.filter((char, index, arr) => arr.indexOf(char) === index); // Remove duplicates
6270

6371
warnings.push(
6472
`Tool name contains invalid characters: ${invalidChars.map(c => `"${c}"`).join(', ')}`,
65-
"Allowed characters are: A-Z, a-z, 0-9, underscore (_), and dash (-)"
73+
"Allowed characters are: A-Z, a-z, 0-9, underscore (_), dash (-), dot (.), and forward slash (/)"
6674
);
6775

6876
return {

0 commit comments

Comments
 (0)