@llmindset/hf-mcp 0.2.41 → 0.2.43
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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/space/commands/invoke.d.ts +6 -0
- package/dist/space/commands/invoke.d.ts.map +1 -0
- package/dist/space/commands/invoke.js +236 -0
- package/dist/space/commands/invoke.js.map +1 -0
- package/dist/space/commands/view-parameters.d.ts +3 -0
- package/dist/space/commands/view-parameters.d.ts.map +1 -0
- package/dist/space/commands/view-parameters.js +144 -0
- package/dist/space/commands/view-parameters.js.map +1 -0
- package/dist/space/space-tool.d.ts +35 -0
- package/dist/space/space-tool.d.ts.map +1 -0
- package/dist/space/space-tool.js +198 -0
- package/dist/space/space-tool.js.map +1 -0
- package/dist/space/types.d.ts +74 -0
- package/dist/space/types.d.ts.map +1 -0
- package/dist/space/types.js +24 -0
- package/dist/space/types.js.map +1 -0
- package/dist/space/utils/parameter-formatter.d.ts +5 -0
- package/dist/space/utils/parameter-formatter.d.ts.map +1 -0
- package/dist/space/utils/parameter-formatter.js +132 -0
- package/dist/space/utils/parameter-formatter.js.map +1 -0
- package/dist/space/utils/result-formatter.d.ts +4 -0
- package/dist/space/utils/result-formatter.d.ts.map +1 -0
- package/dist/space/utils/result-formatter.js +146 -0
- package/dist/space/utils/result-formatter.js.map +1 -0
- package/dist/space/utils/schema-validator.d.ts +9 -0
- package/dist/space/utils/schema-validator.d.ts.map +1 -0
- package/dist/space/utils/schema-validator.js +145 -0
- package/dist/space/utils/schema-validator.js.map +1 -0
- package/dist/space-search.d.ts.map +1 -1
- package/dist/space-search.js +5 -4
- package/dist/space-search.js.map +1 -1
- package/dist/tool-ids.d.ts +4 -2
- package/dist/tool-ids.d.ts.map +1 -1
- package/dist/tool-ids.js +4 -1
- package/dist/tool-ids.js.map +1 -1
- package/package.json +2 -1
- package/src/index.ts +1 -0
- package/src/space/commands/invoke.ts +335 -0
- package/src/space/commands/view-parameters.ts +204 -0
- package/src/space/space-tool.ts +252 -0
- package/src/space/types.ts +127 -0
- package/src/space/utils/parameter-formatter.ts +200 -0
- package/src/space/utils/result-formatter.ts +226 -0
- package/src/space/utils/schema-validator.ts +232 -0
- package/src/space-search.ts +5 -4
- package/src/tool-ids.ts +4 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import type { SchemaComplexityResult, ParameterInfo } from '../types.js';
|
|
2
|
+
import { FILE_INPUT_HELP_MESSAGE } from '../types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Formats parameter schema for display to users
|
|
6
|
+
* Shows parameters in a clear, organized manner with:
|
|
7
|
+
* - Required parameters first
|
|
8
|
+
* - Alphabetical sorting within groups
|
|
9
|
+
* - Clear type information
|
|
10
|
+
* - Default values
|
|
11
|
+
* - Enum options
|
|
12
|
+
*/
|
|
13
|
+
export function formatParameters(schemaResult: SchemaComplexityResult, spaceName: string): string {
|
|
14
|
+
const { toolName, toolDescription, parameters } = schemaResult;
|
|
15
|
+
|
|
16
|
+
let output = `# Parameters for: ${toolName}\n\n`;
|
|
17
|
+
|
|
18
|
+
if (toolDescription) {
|
|
19
|
+
output += `**Description:** ${toolDescription}\n\n`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Sort parameters: required first, then alphabetical
|
|
23
|
+
const sortedParams = [...parameters].sort((a, b) => {
|
|
24
|
+
if (a.required !== b.required) {
|
|
25
|
+
return a.required ? -1 : 1;
|
|
26
|
+
}
|
|
27
|
+
return a.name.localeCompare(b.name);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
output += `## Parameters:\n\n`;
|
|
31
|
+
|
|
32
|
+
for (const param of sortedParams) {
|
|
33
|
+
output += formatParameter(param);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Add usage example
|
|
37
|
+
output += '\n## Usage Example:\n\n';
|
|
38
|
+
output += formatUsageExample(spaceName, parameters);
|
|
39
|
+
|
|
40
|
+
return output;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Formats a single parameter for display
|
|
45
|
+
*/
|
|
46
|
+
function formatParameter(param: ParameterInfo): string {
|
|
47
|
+
const badge = param.required ? '[REQUIRED]' : '[OPTIONAL]';
|
|
48
|
+
let output = `### ${param.name} ${badge}\n`;
|
|
49
|
+
|
|
50
|
+
// Type
|
|
51
|
+
output += `- **Type:** ${param.type}\n`;
|
|
52
|
+
|
|
53
|
+
// Description
|
|
54
|
+
if (param.description) {
|
|
55
|
+
output += `- **Description:** ${param.description}\n`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Default value
|
|
59
|
+
if (param.default !== undefined) {
|
|
60
|
+
const defaultStr = formatValue(param.default);
|
|
61
|
+
output += `- **Default:** ${defaultStr}\n`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Enum values
|
|
65
|
+
if (param.enum && param.enum.length > 0) {
|
|
66
|
+
const enumStr = param.enum.map((v) => formatValue(v)).join(', ');
|
|
67
|
+
output += `- **Allowed values:** ${enumStr}\n`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// File input help
|
|
71
|
+
if (param.isFileData) {
|
|
72
|
+
output += `- **Note:** ${FILE_INPUT_HELP_MESSAGE}\n`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
output += '\n';
|
|
76
|
+
return output;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Formats a value for display
|
|
81
|
+
*/
|
|
82
|
+
function formatValue(value: unknown): string {
|
|
83
|
+
if (value === null) return 'null';
|
|
84
|
+
if (value === undefined) return 'undefined';
|
|
85
|
+
if (typeof value === 'string') return `"${value}"`;
|
|
86
|
+
if (typeof value === 'object') {
|
|
87
|
+
try {
|
|
88
|
+
return JSON.stringify(value);
|
|
89
|
+
} catch {
|
|
90
|
+
return '[object]';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
94
|
+
return String(value);
|
|
95
|
+
}
|
|
96
|
+
return JSON.stringify(value);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Formats a usage example
|
|
101
|
+
*/
|
|
102
|
+
function formatUsageExample(spaceName: string, parameters: ParameterInfo[]): string {
|
|
103
|
+
// Create example with required parameters and some optional ones
|
|
104
|
+
const exampleParams: Record<string, string> = {};
|
|
105
|
+
|
|
106
|
+
// Add required parameters
|
|
107
|
+
for (const param of parameters.filter((p) => p.required)) {
|
|
108
|
+
exampleParams[param.name] = getExampleValue(param);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Add one or two optional parameters if they exist
|
|
112
|
+
const optionalParams = parameters.filter((p) => !p.required);
|
|
113
|
+
for (let i = 0; i < Math.min(2, optionalParams.length); i++) {
|
|
114
|
+
const param = optionalParams[i];
|
|
115
|
+
if (param) {
|
|
116
|
+
exampleParams[param.name] = getExampleValue(param);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const paramsJson = JSON.stringify(exampleParams, null, 2)
|
|
121
|
+
.split('\n')
|
|
122
|
+
.map((line) => ` ${line}`)
|
|
123
|
+
.join('\n')
|
|
124
|
+
.trim();
|
|
125
|
+
|
|
126
|
+
return `\`\`\`json
|
|
127
|
+
{
|
|
128
|
+
"operation": "invoke",
|
|
129
|
+
"space_name": "${spaceName}",
|
|
130
|
+
"parameters": "${paramsJson.replace(/"/g, '\\"')}"
|
|
131
|
+
}
|
|
132
|
+
\`\`\``;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Gets an example value for a parameter
|
|
137
|
+
*/
|
|
138
|
+
function getExampleValue(param: ParameterInfo): string {
|
|
139
|
+
// Use default if available
|
|
140
|
+
if (param.default !== undefined) {
|
|
141
|
+
return formatValue(param.default);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Use first enum value if available
|
|
145
|
+
if (param.enum && param.enum.length > 0) {
|
|
146
|
+
return formatValue(param.enum[0]);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// File data
|
|
150
|
+
if (param.isFileData) {
|
|
151
|
+
return '"https://example.com/file.jpg"';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Generate example based on type
|
|
155
|
+
const baseType = param.type.split(' ')[0]?.split('<')[0];
|
|
156
|
+
|
|
157
|
+
switch (baseType) {
|
|
158
|
+
case 'string':
|
|
159
|
+
return '"example value"';
|
|
160
|
+
case 'number':
|
|
161
|
+
case 'integer':
|
|
162
|
+
return '42';
|
|
163
|
+
case 'boolean':
|
|
164
|
+
return 'true';
|
|
165
|
+
case 'array':
|
|
166
|
+
return '["item1", "item2"]';
|
|
167
|
+
case 'object':
|
|
168
|
+
return '{"key": "value"}';
|
|
169
|
+
default:
|
|
170
|
+
return '"value"';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Formats a complex schema error message
|
|
176
|
+
*/
|
|
177
|
+
export function formatComplexSchemaError(spaceName: string, reason: string): string {
|
|
178
|
+
return `Error: Schema too complex for space '${spaceName}'.
|
|
179
|
+
|
|
180
|
+
${reason}
|
|
181
|
+
|
|
182
|
+
Supported types: strings, numbers, booleans, arrays of primitives, enums, shallow objects, and file URLs.
|
|
183
|
+
|
|
184
|
+
For this space, use the dedicated gr_* prefixed tools instead.`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Formats validation errors
|
|
189
|
+
*/
|
|
190
|
+
export function formatValidationError(errors: string[], spaceName: string): string {
|
|
191
|
+
let output = `Error: Invalid parameters for space '${spaceName}'.\n\n`;
|
|
192
|
+
|
|
193
|
+
for (const error of errors) {
|
|
194
|
+
output += `- ${error}\n`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
output += `\nUse the view_parameters operation to see all required parameters and their types.`;
|
|
198
|
+
|
|
199
|
+
return output;
|
|
200
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import type { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Formats tool result for user-friendly display
|
|
5
|
+
* Handles different content types:
|
|
6
|
+
* - Text content (primary)
|
|
7
|
+
* - Image content (with descriptions)
|
|
8
|
+
* - Resource content (with URIs)
|
|
9
|
+
* - Error results
|
|
10
|
+
*/
|
|
11
|
+
export function formatToolResult(result: typeof CallToolResultSchema._type): string {
|
|
12
|
+
// Handle error results
|
|
13
|
+
if (result.isError) {
|
|
14
|
+
return formatErrorResult(result);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Handle successful results with content
|
|
18
|
+
if (Array.isArray(result.content) && result.content.length > 0) {
|
|
19
|
+
return formatContentArray(result.content);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Fallback for empty results
|
|
23
|
+
return 'Tool executed successfully (no content returned).';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Formats error results
|
|
28
|
+
*/
|
|
29
|
+
function formatErrorResult(result: typeof CallToolResultSchema._type): string {
|
|
30
|
+
if (!Array.isArray(result.content) || result.content.length === 0) {
|
|
31
|
+
return 'Error: Tool execution failed (no error details provided).';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const errorMessages: string[] = [];
|
|
35
|
+
|
|
36
|
+
for (const item of result.content) {
|
|
37
|
+
if (typeof item === 'string') {
|
|
38
|
+
errorMessages.push(item);
|
|
39
|
+
} else if (item && typeof item === 'object') {
|
|
40
|
+
const obj = item as Record<string, unknown>;
|
|
41
|
+
if (typeof obj.text === 'string') {
|
|
42
|
+
errorMessages.push(obj.text);
|
|
43
|
+
} else if (typeof obj.message === 'string') {
|
|
44
|
+
errorMessages.push(obj.message);
|
|
45
|
+
} else if (typeof obj.error === 'string') {
|
|
46
|
+
errorMessages.push(obj.error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (errorMessages.length > 0) {
|
|
52
|
+
return `Error: ${errorMessages.join('\n')}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return 'Error: Tool execution failed.';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Formats an array of content items
|
|
60
|
+
*/
|
|
61
|
+
function formatContentArray(content: unknown[]): string {
|
|
62
|
+
const formattedItems: string[] = [];
|
|
63
|
+
|
|
64
|
+
for (const item of content) {
|
|
65
|
+
const formatted = formatContentItem(item);
|
|
66
|
+
if (formatted) {
|
|
67
|
+
formattedItems.push(formatted);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (formattedItems.length === 0) {
|
|
72
|
+
return 'Tool executed successfully (no displayable content).';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return formattedItems.join('\n\n');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Formats a single content item
|
|
80
|
+
*/
|
|
81
|
+
function formatContentItem(item: unknown): string | null {
|
|
82
|
+
if (!item) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Handle string content
|
|
87
|
+
if (typeof item === 'string') {
|
|
88
|
+
return item;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Handle non-object content
|
|
92
|
+
if (typeof item !== 'object') {
|
|
93
|
+
if (typeof item === 'number' || typeof item === 'boolean') {
|
|
94
|
+
return String(item);
|
|
95
|
+
}
|
|
96
|
+
return JSON.stringify(item);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const obj = item as Record<string, unknown>;
|
|
100
|
+
const type = typeof obj.type === 'string' ? obj.type.toLowerCase() : undefined;
|
|
101
|
+
|
|
102
|
+
switch (type) {
|
|
103
|
+
case 'text':
|
|
104
|
+
return formatTextContent(obj);
|
|
105
|
+
|
|
106
|
+
case 'image':
|
|
107
|
+
return formatImageContent(obj);
|
|
108
|
+
|
|
109
|
+
case 'resource':
|
|
110
|
+
return formatResourceContent(obj);
|
|
111
|
+
|
|
112
|
+
case 'embedded_resource':
|
|
113
|
+
return formatEmbeddedResourceContent(obj);
|
|
114
|
+
|
|
115
|
+
default:
|
|
116
|
+
// Try to extract text from unknown types
|
|
117
|
+
if (typeof obj.text === 'string') {
|
|
118
|
+
return obj.text;
|
|
119
|
+
}
|
|
120
|
+
// Fallback to JSON representation
|
|
121
|
+
try {
|
|
122
|
+
return JSON.stringify(item, null, 2);
|
|
123
|
+
} catch {
|
|
124
|
+
return '[complex object]';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Formats text content
|
|
131
|
+
*/
|
|
132
|
+
function formatTextContent(obj: Record<string, unknown>): string | null {
|
|
133
|
+
if (typeof obj.text === 'string') {
|
|
134
|
+
return obj.text;
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Formats image content
|
|
141
|
+
*/
|
|
142
|
+
function formatImageContent(obj: Record<string, unknown>): string {
|
|
143
|
+
const parts: string[] = ['[Image Content]'];
|
|
144
|
+
|
|
145
|
+
// Add MIME type if available
|
|
146
|
+
if (typeof obj.mimeType === 'string') {
|
|
147
|
+
parts.push(`Type: ${obj.mimeType}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Add URL if available
|
|
151
|
+
if (typeof obj.url === 'string') {
|
|
152
|
+
parts.push(`URL: ${obj.url}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Add data indicator if present
|
|
156
|
+
if (typeof obj.data === 'string') {
|
|
157
|
+
const dataLength = obj.data.length;
|
|
158
|
+
parts.push(`Data: ${dataLength} characters (base64)`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return parts.join('\n');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Formats resource content
|
|
166
|
+
*/
|
|
167
|
+
function formatResourceContent(obj: Record<string, unknown>): string {
|
|
168
|
+
const parts: string[] = ['[Resource]'];
|
|
169
|
+
|
|
170
|
+
// Extract resource details
|
|
171
|
+
const resource = obj.resource as Record<string, unknown> | undefined;
|
|
172
|
+
if (resource) {
|
|
173
|
+
if (typeof resource.uri === 'string') {
|
|
174
|
+
parts.push(`URI: ${resource.uri}`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (typeof resource.name === 'string') {
|
|
178
|
+
parts.push(`Name: ${resource.name}`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (typeof resource.mimeType === 'string') {
|
|
182
|
+
parts.push(`Type: ${resource.mimeType}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (typeof resource.description === 'string') {
|
|
186
|
+
parts.push(`Description: ${resource.description}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return parts.join('\n');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Formats embedded resource content
|
|
195
|
+
*/
|
|
196
|
+
function formatEmbeddedResourceContent(obj: Record<string, unknown>): string {
|
|
197
|
+
const parts: string[] = ['[Embedded Resource]'];
|
|
198
|
+
|
|
199
|
+
if (typeof obj.uri === 'string') {
|
|
200
|
+
parts.push(`URI: ${obj.uri}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (typeof obj.mimeType === 'string') {
|
|
204
|
+
parts.push(`Type: ${obj.mimeType}`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Add blob indicator if present
|
|
208
|
+
if (typeof obj.blob === 'string') {
|
|
209
|
+
const blobLength = obj.blob.length;
|
|
210
|
+
parts.push(`Data: ${blobLength} characters`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return parts.join('\n');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Formats a list of warnings
|
|
218
|
+
*/
|
|
219
|
+
export function formatWarnings(warnings: string[]): string {
|
|
220
|
+
if (warnings.length === 0) {
|
|
221
|
+
return '';
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const header = warnings.length === 1 ? 'Warning:' : 'Warnings:';
|
|
225
|
+
return `${header}\n${warnings.map((w) => `- ${w}`).join('\n')}\n\n`;
|
|
226
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import type {
|
|
3
|
+
JsonSchema,
|
|
4
|
+
JsonSchemaProperty,
|
|
5
|
+
SchemaComplexityResult,
|
|
6
|
+
ParameterInfo,
|
|
7
|
+
} from '../types.js';
|
|
8
|
+
import { isFileDataProperty } from '../types.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Analyzes a tool schema to determine if it's simple enough for the dynamic space tool
|
|
12
|
+
*
|
|
13
|
+
* Supported types:
|
|
14
|
+
* - string, number, boolean
|
|
15
|
+
* - enum (with predefined values)
|
|
16
|
+
* - array of primitives
|
|
17
|
+
* - shallow objects (one level deep with primitive properties)
|
|
18
|
+
* - FileData (as string URLs)
|
|
19
|
+
*
|
|
20
|
+
* Rejected types:
|
|
21
|
+
* - Deeply nested objects (2+ levels)
|
|
22
|
+
* - Arrays of objects
|
|
23
|
+
* - Union types
|
|
24
|
+
* - Recursive schemas
|
|
25
|
+
*/
|
|
26
|
+
export function analyzeSchemaComplexity(tool: Tool): SchemaComplexityResult {
|
|
27
|
+
const result: SchemaComplexityResult = {
|
|
28
|
+
isSimple: true,
|
|
29
|
+
parameters: [],
|
|
30
|
+
toolName: tool.name,
|
|
31
|
+
toolDescription: tool.description,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const inputSchema = tool.inputSchema as JsonSchema;
|
|
35
|
+
if (!inputSchema || !inputSchema.properties) {
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const properties = inputSchema.properties;
|
|
40
|
+
const required = inputSchema.required || [];
|
|
41
|
+
|
|
42
|
+
for (const [paramName, prop] of Object.entries(properties)) {
|
|
43
|
+
const paramInfo = analyzeProperty(paramName, prop, required.includes(paramName));
|
|
44
|
+
|
|
45
|
+
// Check if this parameter is too complex
|
|
46
|
+
if (paramInfo.complexType) {
|
|
47
|
+
result.isSimple = false;
|
|
48
|
+
result.reason = `Parameter "${paramName}" has complex type: ${paramInfo.complexType}`;
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
result.parameters.push(paramInfo);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Analyzes a single property to extract parameter information
|
|
60
|
+
*/
|
|
61
|
+
function analyzeProperty(name: string, prop: JsonSchemaProperty, isRequired: boolean): ParameterInfo {
|
|
62
|
+
const paramInfo: ParameterInfo = {
|
|
63
|
+
name,
|
|
64
|
+
type: prop.type || 'unknown',
|
|
65
|
+
description: prop.description,
|
|
66
|
+
required: isRequired,
|
|
67
|
+
default: prop.default,
|
|
68
|
+
enum: prop.enum,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Check for FileData types - treat as string URLs
|
|
72
|
+
if (isFileDataProperty(prop)) {
|
|
73
|
+
paramInfo.type = 'string (file URL)';
|
|
74
|
+
paramInfo.isFileData = true;
|
|
75
|
+
return paramInfo;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check for enum types
|
|
79
|
+
if (prop.enum && Array.isArray(prop.enum) && prop.enum.length > 0) {
|
|
80
|
+
paramInfo.type = `enum (${prop.enum.length} values)`;
|
|
81
|
+
return paramInfo;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check for complex types
|
|
85
|
+
if (prop.type === 'object') {
|
|
86
|
+
// Check if it's a shallow object
|
|
87
|
+
if (prop.properties) {
|
|
88
|
+
const nestedProps = Object.values(prop.properties);
|
|
89
|
+
const hasComplexNested = nestedProps.some(
|
|
90
|
+
(nested) => nested.type === 'object' || nested.type === 'array'
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
if (hasComplexNested) {
|
|
94
|
+
paramInfo.complexType = 'deeply nested object (2+ levels)';
|
|
95
|
+
} else {
|
|
96
|
+
paramInfo.type = 'object (shallow)';
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
// Object without properties definition is too vague
|
|
100
|
+
paramInfo.complexType = 'object without defined properties';
|
|
101
|
+
}
|
|
102
|
+
return paramInfo;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Check for array types
|
|
106
|
+
if (prop.type === 'array') {
|
|
107
|
+
if (prop.items) {
|
|
108
|
+
const itemType = prop.items.type;
|
|
109
|
+
if (itemType === 'object') {
|
|
110
|
+
paramInfo.complexType = 'array of objects';
|
|
111
|
+
} else if (itemType === 'array') {
|
|
112
|
+
paramInfo.complexType = 'nested arrays';
|
|
113
|
+
} else {
|
|
114
|
+
paramInfo.type = `array<${itemType || 'unknown'}>`;
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
paramInfo.type = 'array<any>';
|
|
118
|
+
}
|
|
119
|
+
return paramInfo;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Simple types are fine
|
|
123
|
+
if (['string', 'number', 'integer', 'boolean'].includes(prop.type || '')) {
|
|
124
|
+
return paramInfo;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Unknown or unsupported type
|
|
128
|
+
if (!prop.type) {
|
|
129
|
+
paramInfo.complexType = 'unknown type';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return paramInfo;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Validates parameter values against the schema
|
|
137
|
+
*/
|
|
138
|
+
export function validateParameters(
|
|
139
|
+
parameters: Record<string, unknown>,
|
|
140
|
+
schemaResult: SchemaComplexityResult
|
|
141
|
+
): { valid: boolean; errors: string[] } {
|
|
142
|
+
const errors: string[] = [];
|
|
143
|
+
|
|
144
|
+
// Check for missing required parameters
|
|
145
|
+
const requiredParams = schemaResult.parameters.filter((p) => p.required);
|
|
146
|
+
for (const param of requiredParams) {
|
|
147
|
+
if (!(param.name in parameters) || parameters[param.name] === undefined) {
|
|
148
|
+
errors.push(`Missing required parameter: "${param.name}"`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Check types (basic validation)
|
|
153
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
154
|
+
const paramInfo = schemaResult.parameters.find((p) => p.name === key);
|
|
155
|
+
|
|
156
|
+
if (!paramInfo) {
|
|
157
|
+
// Unknown parameter - warning but not error (permissive inputs)
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Type checking
|
|
162
|
+
if (value !== null && value !== undefined) {
|
|
163
|
+
if (!validateType(value, paramInfo)) {
|
|
164
|
+
errors.push(
|
|
165
|
+
`Parameter "${key}" should be type ${paramInfo.type}, got ${typeof value}`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
valid: errors.length === 0,
|
|
173
|
+
errors,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Validates a value against a parameter type
|
|
179
|
+
*/
|
|
180
|
+
function validateType(value: unknown, paramInfo: ParameterInfo): boolean {
|
|
181
|
+
// FileData as string URL
|
|
182
|
+
if (paramInfo.isFileData) {
|
|
183
|
+
return typeof value === 'string';
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Enum validation
|
|
187
|
+
if (paramInfo.enum && Array.isArray(paramInfo.enum)) {
|
|
188
|
+
return paramInfo.enum.includes(value);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Basic type validation
|
|
192
|
+
const baseType = paramInfo.type.split(' ')[0]?.split('<')[0]; // Extract base type
|
|
193
|
+
|
|
194
|
+
switch (baseType) {
|
|
195
|
+
case 'string':
|
|
196
|
+
return typeof value === 'string';
|
|
197
|
+
case 'number':
|
|
198
|
+
case 'integer':
|
|
199
|
+
return typeof value === 'number';
|
|
200
|
+
case 'boolean':
|
|
201
|
+
return typeof value === 'boolean';
|
|
202
|
+
case 'array':
|
|
203
|
+
return Array.isArray(value);
|
|
204
|
+
case 'object':
|
|
205
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
206
|
+
case 'enum':
|
|
207
|
+
// Already checked above
|
|
208
|
+
return true;
|
|
209
|
+
default:
|
|
210
|
+
// Unknown type, allow it (permissive)
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Applies default values to parameters
|
|
217
|
+
*/
|
|
218
|
+
export function applyDefaults(
|
|
219
|
+
parameters: Record<string, unknown>,
|
|
220
|
+
schemaResult: SchemaComplexityResult
|
|
221
|
+
): Record<string, unknown> {
|
|
222
|
+
const result = { ...parameters };
|
|
223
|
+
|
|
224
|
+
for (const param of schemaResult.parameters) {
|
|
225
|
+
// Only apply defaults for optional parameters that are missing
|
|
226
|
+
if (!param.required && !(param.name in result) && param.default !== undefined) {
|
|
227
|
+
result[param.name] = param.default;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return result;
|
|
232
|
+
}
|
package/src/space-search.ts
CHANGED
|
@@ -34,7 +34,8 @@ const RESULTS_TO_RETURN = 10;
|
|
|
34
34
|
export const SEMANTIC_SEARCH_TOOL_CONFIG = {
|
|
35
35
|
name: 'space_search',
|
|
36
36
|
description:
|
|
37
|
-
'Find Hugging Face Spaces using semantic search.
|
|
37
|
+
'Find Hugging Face Spaces using semantic search. IMPORTANT Only MCP Servers can be used with the dynamic_space tool' +
|
|
38
|
+
'Include links to the Space when presenting the results.',
|
|
38
39
|
schema: z.object({
|
|
39
40
|
query: z.string().min(1, 'Query is required').max(100, 'Query too long').describe('Semantic Search Query'),
|
|
40
41
|
limit: z.number().optional().default(RESULTS_TO_RETURN).describe('Number of results to return'),
|
|
@@ -115,7 +116,7 @@ export class SpaceSearchTool extends HfApiCall<SpaceSearchParams, SpaceSearchRes
|
|
|
115
116
|
return {
|
|
116
117
|
formatted: `No matching Hugging Face Spaces found referencing ${filter}.`,
|
|
117
118
|
totalResults: 0,
|
|
118
|
-
resultsShared: 0
|
|
119
|
+
resultsShared: 0,
|
|
119
120
|
};
|
|
120
121
|
}
|
|
121
122
|
|
|
@@ -150,7 +151,7 @@ export const formatSearchResults = (
|
|
|
150
151
|
return {
|
|
151
152
|
formatted: `No matching Hugging Face Spaces found for the query '${query}'. Try a different query.`,
|
|
152
153
|
totalResults: 0,
|
|
153
|
-
resultsShared: 0
|
|
154
|
+
resultsShared: 0,
|
|
154
155
|
};
|
|
155
156
|
}
|
|
156
157
|
|
|
@@ -185,6 +186,6 @@ export const formatSearchResults = (
|
|
|
185
186
|
return {
|
|
186
187
|
formatted: markdown,
|
|
187
188
|
totalResults: totalCount,
|
|
188
|
-
resultsShared: results.length
|
|
189
|
+
resultsShared: results.length,
|
|
189
190
|
};
|
|
190
191
|
};
|