@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,204 @@
|
|
|
1
|
+
import type { ToolResult } from '../../types/tool-result.js';
|
|
2
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import { analyzeSchemaComplexity } from '../utils/schema-validator.js';
|
|
4
|
+
import { formatParameters, formatComplexSchemaError } from '../utils/parameter-formatter.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Fetches space metadata and schema to discover parameters
|
|
8
|
+
*/
|
|
9
|
+
export async function viewParameters(spaceName: string, hfToken?: string): Promise<ToolResult> {
|
|
10
|
+
try {
|
|
11
|
+
// Step 1: Fetch space metadata to get subdomain
|
|
12
|
+
const metadata = await fetchSpaceMetadata(spaceName, hfToken);
|
|
13
|
+
|
|
14
|
+
// Step 2: Fetch schema from Gradio endpoint
|
|
15
|
+
const tools = await fetchGradioSchema(metadata.subdomain, metadata.private, hfToken);
|
|
16
|
+
|
|
17
|
+
// For simplicity, we'll work with the first tool
|
|
18
|
+
// (most Gradio spaces expose a single primary tool)
|
|
19
|
+
if (tools.length === 0) {
|
|
20
|
+
return {
|
|
21
|
+
formatted: `Error: No tools found for space '${spaceName}'.`,
|
|
22
|
+
totalResults: 0,
|
|
23
|
+
resultsShared: 0,
|
|
24
|
+
isError: true,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const tool = tools[0] as Tool;
|
|
29
|
+
|
|
30
|
+
// Step 3: Analyze schema complexity
|
|
31
|
+
const schemaResult = analyzeSchemaComplexity(tool);
|
|
32
|
+
|
|
33
|
+
if (!schemaResult.isSimple) {
|
|
34
|
+
return {
|
|
35
|
+
formatted: formatComplexSchemaError(spaceName, schemaResult.reason || 'Unknown reason'),
|
|
36
|
+
totalResults: 0,
|
|
37
|
+
resultsShared: 0,
|
|
38
|
+
isError: true,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Step 4: Format parameters for display
|
|
43
|
+
const formatted = formatParameters(schemaResult, spaceName);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
formatted,
|
|
47
|
+
totalResults: schemaResult.parameters.length,
|
|
48
|
+
resultsShared: schemaResult.parameters.length,
|
|
49
|
+
};
|
|
50
|
+
} catch (error) {
|
|
51
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
52
|
+
|
|
53
|
+
// Check if this is a 404 error (space not found)
|
|
54
|
+
const is404 = errorMessage.includes('404') || errorMessage.toLowerCase().includes('not found');
|
|
55
|
+
|
|
56
|
+
let formattedError = `Error fetching parameters for space '${spaceName}': ${errorMessage}`;
|
|
57
|
+
|
|
58
|
+
if (is404) {
|
|
59
|
+
formattedError += '\n\nNote: The space MUST be an MCP enabled space. Use the `space_search` tool to find MCP enabled spaces.';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
formatted: formattedError,
|
|
64
|
+
totalResults: 0,
|
|
65
|
+
resultsShared: 0,
|
|
66
|
+
isError: true,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Fetches space metadata from HuggingFace API
|
|
73
|
+
*/
|
|
74
|
+
async function fetchSpaceMetadata(
|
|
75
|
+
spaceName: string,
|
|
76
|
+
hfToken?: string
|
|
77
|
+
): Promise<{ subdomain: string; private: boolean }> {
|
|
78
|
+
const url = `https://huggingface.co/api/spaces/${spaceName}`;
|
|
79
|
+
const headers: Record<string, string> = {};
|
|
80
|
+
|
|
81
|
+
if (hfToken) {
|
|
82
|
+
headers['Authorization'] = `Bearer ${hfToken}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const controller = new AbortController();
|
|
86
|
+
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const response = await fetch(url, {
|
|
90
|
+
headers,
|
|
91
|
+
signal: controller.signal,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
clearTimeout(timeoutId);
|
|
95
|
+
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const info = (await response.json()) as {
|
|
101
|
+
subdomain?: string;
|
|
102
|
+
private?: boolean;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
if (!info.subdomain) {
|
|
106
|
+
throw new Error('Space does not have a subdomain');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
subdomain: info.subdomain,
|
|
111
|
+
private: info.private || false,
|
|
112
|
+
};
|
|
113
|
+
} finally {
|
|
114
|
+
clearTimeout(timeoutId);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Fetches schema from Gradio endpoint
|
|
120
|
+
*/
|
|
121
|
+
async function fetchGradioSchema(subdomain: string, isPrivate: boolean, hfToken?: string): Promise<Tool[]> {
|
|
122
|
+
const schemaUrl = `https://${subdomain}.hf.space/gradio_api/mcp/schema`;
|
|
123
|
+
|
|
124
|
+
const headers: Record<string, string> = {
|
|
125
|
+
'Content-Type': 'application/json',
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
if (isPrivate && hfToken) {
|
|
129
|
+
headers['X-HF-Authorization'] = `Bearer ${hfToken}`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const controller = new AbortController();
|
|
133
|
+
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const response = await fetch(schemaUrl, {
|
|
137
|
+
method: 'GET',
|
|
138
|
+
headers,
|
|
139
|
+
signal: controller.signal,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
clearTimeout(timeoutId);
|
|
143
|
+
|
|
144
|
+
if (!response.ok) {
|
|
145
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const schemaResponse = (await response.json()) as unknown;
|
|
149
|
+
|
|
150
|
+
// Parse schema response (handle both array and object formats)
|
|
151
|
+
return parseSchemaResponse(schemaResponse);
|
|
152
|
+
} finally {
|
|
153
|
+
clearTimeout(timeoutId);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Parses schema response and extracts tools
|
|
159
|
+
*/
|
|
160
|
+
function parseSchemaResponse(schemaResponse: unknown): Tool[] {
|
|
161
|
+
const tools: Tool[] = [];
|
|
162
|
+
|
|
163
|
+
if (Array.isArray(schemaResponse)) {
|
|
164
|
+
// Array format: [{ name: "toolName", description: "...", inputSchema: {...} }, ...]
|
|
165
|
+
for (const item of schemaResponse) {
|
|
166
|
+
if (
|
|
167
|
+
typeof item === 'object' &&
|
|
168
|
+
item !== null &&
|
|
169
|
+
'name' in item &&
|
|
170
|
+
'inputSchema' in item
|
|
171
|
+
) {
|
|
172
|
+
const itemRecord = item as Record<string, unknown>;
|
|
173
|
+
if (typeof itemRecord.name === 'string') {
|
|
174
|
+
const tool = itemRecord as { name: string; description?: string; inputSchema: unknown };
|
|
175
|
+
tools.push({
|
|
176
|
+
name: tool.name,
|
|
177
|
+
description: tool.description || `${tool.name} tool`,
|
|
178
|
+
inputSchema: {
|
|
179
|
+
type: 'object',
|
|
180
|
+
...(tool.inputSchema as Record<string, unknown>),
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
} else if (typeof schemaResponse === 'object' && schemaResponse !== null) {
|
|
187
|
+
// Object format: { "toolName": { properties: {...}, required: [...] }, ... }
|
|
188
|
+
for (const [name, toolSchema] of Object.entries(schemaResponse)) {
|
|
189
|
+
if (typeof toolSchema === 'object' && toolSchema !== null) {
|
|
190
|
+
const schema = toolSchema as { description?: string };
|
|
191
|
+
tools.push({
|
|
192
|
+
name,
|
|
193
|
+
description: schema.description || `${name} tool`,
|
|
194
|
+
inputSchema: {
|
|
195
|
+
type: 'object',
|
|
196
|
+
...(toolSchema as Record<string, unknown>),
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return tools.filter((tool) => !tool.name.toLowerCase().includes('<lambda'));
|
|
204
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import type { ToolResult } from '../types/tool-result.js';
|
|
2
|
+
import type { ServerNotification, ServerRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
4
|
+
import { spaceArgsSchema, OPERATION_NAMES, type OperationName, type SpaceArgs, type InvokeResult } from './types.js';
|
|
5
|
+
import { viewParameters } from './commands/view-parameters.js';
|
|
6
|
+
import { invokeSpace } from './commands/invoke.js';
|
|
7
|
+
|
|
8
|
+
// Re-export types (including InvokeResult for external use)
|
|
9
|
+
export * from './types.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Usage instructions when tool is called with no operation
|
|
13
|
+
*/
|
|
14
|
+
const USAGE_INSTRUCTIONS = `# Gradio Space Interaction
|
|
15
|
+
|
|
16
|
+
Dynamically interact with any Gradio MCP Space. View parameter schemas or invoke spaces with custom parameters.
|
|
17
|
+
|
|
18
|
+
## Supported Schema Types
|
|
19
|
+
|
|
20
|
+
✅ **Simple types** (supported):
|
|
21
|
+
- Strings, numbers, booleans
|
|
22
|
+
- Enums (predefined value sets)
|
|
23
|
+
- Arrays of primitives
|
|
24
|
+
- Shallow objects (one level deep)
|
|
25
|
+
- FileData (as URL strings)
|
|
26
|
+
|
|
27
|
+
❌ **Complex types** (not supported):
|
|
28
|
+
- Deeply nested objects (2+ levels)
|
|
29
|
+
- Arrays of objects
|
|
30
|
+
- Union types
|
|
31
|
+
- Recursive schemas
|
|
32
|
+
|
|
33
|
+
For spaces with complex schemas, direct the user to huggingface.co/settings/mcp to manage their settings.
|
|
34
|
+
|
|
35
|
+
## Available Operations
|
|
36
|
+
|
|
37
|
+
### view_parameters
|
|
38
|
+
Display the parameter schema for a space's first tool.
|
|
39
|
+
|
|
40
|
+
**Example:**
|
|
41
|
+
\`\`\`json
|
|
42
|
+
{
|
|
43
|
+
"operation": "view_parameters",
|
|
44
|
+
"space_name": "evalstate/FLUX1_schnell"
|
|
45
|
+
}
|
|
46
|
+
\`\`\`
|
|
47
|
+
|
|
48
|
+
### invoke
|
|
49
|
+
Execute a space's first tool with provided parameters.
|
|
50
|
+
|
|
51
|
+
**Example:**
|
|
52
|
+
\`\`\`json
|
|
53
|
+
{
|
|
54
|
+
"operation": "invoke",
|
|
55
|
+
"space_name": "evalstate/FLUX1_schnell",
|
|
56
|
+
"parameters": "{\\"prompt\\": \\"a cute cat\\", \\"num_steps\\": 4}"
|
|
57
|
+
}
|
|
58
|
+
\`\`\`
|
|
59
|
+
|
|
60
|
+
## Workflow
|
|
61
|
+
|
|
62
|
+
1. **Discover parameters** - Use \`view_parameters\` to see what a space accepts
|
|
63
|
+
2. **Invoke the space** - Use \`invoke\` with the required parameters
|
|
64
|
+
3. **Review results** - Get formatted output (text, images, resources)
|
|
65
|
+
|
|
66
|
+
## File Handling
|
|
67
|
+
|
|
68
|
+
For parameters that accept files (FileData types):
|
|
69
|
+
- Provide a publicly accessible URL (http:// or https://)
|
|
70
|
+
- Example: \`{"image": "https://example.com/photo.jpg"}\`
|
|
71
|
+
- To upload local files, use the dedicated gr_* prefixed tool for that space
|
|
72
|
+
|
|
73
|
+
## Tips
|
|
74
|
+
|
|
75
|
+
- The tool automatically applies default values for optional parameters
|
|
76
|
+
- Unknown parameters generate warnings but are still passed through (permissive inputs)
|
|
77
|
+
- Enum parameters show all allowed values in view_parameters
|
|
78
|
+
- Required parameters are clearly marked and validated
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Space tool configuration
|
|
83
|
+
*/
|
|
84
|
+
export const DYNAMIC_SPACE_TOOL_CONFIG = {
|
|
85
|
+
name: 'dynamic_space',
|
|
86
|
+
description:
|
|
87
|
+
'Dynamically interact with Gradio MCP Spaces . View parameter schemas or invoke spaces with custom parameters. ' +
|
|
88
|
+
'Supports simple parameter types (strings, numbers, booleans, arrays, enums, shallow objects). ' +
|
|
89
|
+
'Call with no operation for full usage instructions.',
|
|
90
|
+
schema: spaceArgsSchema,
|
|
91
|
+
annotations: {
|
|
92
|
+
title: 'Gradio Space Interaction',
|
|
93
|
+
readOnlyHint: false,
|
|
94
|
+
openWorldHint: true,
|
|
95
|
+
},
|
|
96
|
+
} as const;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Space tool implementation
|
|
100
|
+
*/
|
|
101
|
+
export class SpaceTool {
|
|
102
|
+
private hfToken?: string;
|
|
103
|
+
|
|
104
|
+
constructor(hfToken?: string) {
|
|
105
|
+
this.hfToken = hfToken;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Execute a space operation
|
|
110
|
+
* Returns InvokeResult (with raw MCP content) for invoke operation,
|
|
111
|
+
* or ToolResult (with formatted text) for other operations
|
|
112
|
+
*/
|
|
113
|
+
async execute(
|
|
114
|
+
params: SpaceArgs,
|
|
115
|
+
extra?: RequestHandlerExtra<ServerRequest, ServerNotification>
|
|
116
|
+
): Promise<InvokeResult | ToolResult> {
|
|
117
|
+
const requestedOperation = params.operation;
|
|
118
|
+
|
|
119
|
+
// If no operation provided, return usage instructions
|
|
120
|
+
if (!requestedOperation) {
|
|
121
|
+
return {
|
|
122
|
+
formatted: USAGE_INSTRUCTIONS,
|
|
123
|
+
totalResults: 1,
|
|
124
|
+
resultsShared: 1,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Validate operation
|
|
129
|
+
const normalizedOperation = requestedOperation.toLowerCase();
|
|
130
|
+
if (!isOperationName(normalizedOperation)) {
|
|
131
|
+
return {
|
|
132
|
+
formatted: `Unknown operation: "${requestedOperation}"
|
|
133
|
+
Available operations: ${OPERATION_NAMES.join(', ')}
|
|
134
|
+
|
|
135
|
+
Call this tool with no operation for full usage instructions.`,
|
|
136
|
+
totalResults: 0,
|
|
137
|
+
resultsShared: 0,
|
|
138
|
+
isError: true,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Execute operation
|
|
143
|
+
try {
|
|
144
|
+
switch (normalizedOperation) {
|
|
145
|
+
case 'view_parameters':
|
|
146
|
+
return await this.handleViewParameters(params);
|
|
147
|
+
|
|
148
|
+
case 'invoke':
|
|
149
|
+
return await this.handleInvoke(params, extra);
|
|
150
|
+
|
|
151
|
+
default:
|
|
152
|
+
return {
|
|
153
|
+
formatted: `Unknown operation: "${requestedOperation}"`,
|
|
154
|
+
totalResults: 0,
|
|
155
|
+
resultsShared: 0,
|
|
156
|
+
isError: true,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
} catch (error) {
|
|
160
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
161
|
+
return {
|
|
162
|
+
formatted: `Error executing ${requestedOperation}: ${errorMessage}`,
|
|
163
|
+
totalResults: 0,
|
|
164
|
+
resultsShared: 0,
|
|
165
|
+
isError: true,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Handle view_parameters operation
|
|
172
|
+
*/
|
|
173
|
+
private async handleViewParameters(params: SpaceArgs): Promise<ToolResult> {
|
|
174
|
+
if (!params.space_name) {
|
|
175
|
+
return {
|
|
176
|
+
formatted: `Error: Missing required parameter: "space_name"
|
|
177
|
+
|
|
178
|
+
Example:
|
|
179
|
+
\`\`\`json
|
|
180
|
+
{
|
|
181
|
+
"operation": "view_parameters",
|
|
182
|
+
"space_name": "username/space-name"
|
|
183
|
+
}
|
|
184
|
+
\`\`\``,
|
|
185
|
+
totalResults: 0,
|
|
186
|
+
resultsShared: 0,
|
|
187
|
+
isError: true,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return await viewParameters(params.space_name, this.hfToken);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Handle invoke operation
|
|
196
|
+
* Returns either InvokeResult (with raw MCP content) or ToolResult (error messages)
|
|
197
|
+
*/
|
|
198
|
+
private async handleInvoke(
|
|
199
|
+
params: SpaceArgs,
|
|
200
|
+
extra?: RequestHandlerExtra<ServerRequest, ServerNotification>
|
|
201
|
+
): Promise<InvokeResult | ToolResult> {
|
|
202
|
+
// Validate required parameters
|
|
203
|
+
if (!params.space_name) {
|
|
204
|
+
return {
|
|
205
|
+
formatted: `Error: Missing required parameter: "space_name"
|
|
206
|
+
|
|
207
|
+
Example:
|
|
208
|
+
\`\`\`json
|
|
209
|
+
{
|
|
210
|
+
"operation": "invoke",
|
|
211
|
+
"space_name": "username/space-name",
|
|
212
|
+
"parameters": "{\\"param1\\": \\"value1\\"}"
|
|
213
|
+
}
|
|
214
|
+
\`\`\``,
|
|
215
|
+
totalResults: 0,
|
|
216
|
+
resultsShared: 0,
|
|
217
|
+
isError: true,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (!params.parameters) {
|
|
222
|
+
return {
|
|
223
|
+
formatted: `Error: Missing required parameter: "parameters"
|
|
224
|
+
|
|
225
|
+
The "parameters" field must be a JSON object string containing the space parameters.
|
|
226
|
+
|
|
227
|
+
Example:
|
|
228
|
+
\`\`\`json
|
|
229
|
+
{
|
|
230
|
+
"operation": "invoke",
|
|
231
|
+
"space_name": "${params.space_name}",
|
|
232
|
+
"parameters": "{\\"param1\\": \\"value1\\", \\"param2\\": 42}"
|
|
233
|
+
}
|
|
234
|
+
\`\`\`
|
|
235
|
+
|
|
236
|
+
Use "view_parameters" to see what parameters this space accepts.`,
|
|
237
|
+
totalResults: 0,
|
|
238
|
+
resultsShared: 0,
|
|
239
|
+
isError: true,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return await invokeSpace(params.space_name, params.parameters, this.hfToken, extra);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Type guard for operation names
|
|
249
|
+
*/
|
|
250
|
+
function isOperationName(value: string): value is OperationName {
|
|
251
|
+
return (OPERATION_NAMES as readonly string[]).includes(value);
|
|
252
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Operations supported by the space tool
|
|
5
|
+
*/
|
|
6
|
+
export const OPERATION_NAMES = ['view_parameters', 'invoke'] as const;
|
|
7
|
+
export type OperationName = (typeof OPERATION_NAMES)[number];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Zod schema for operation arguments
|
|
11
|
+
*/
|
|
12
|
+
export const spaceArgsSchema = z.object({
|
|
13
|
+
operation: z
|
|
14
|
+
.enum(OPERATION_NAMES)
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('Operation to execute. Valid values: "view_parameters", "invoke"'),
|
|
17
|
+
space_name: z.string().optional().describe('The Hugging Face space ID (format: "username/space-name")'),
|
|
18
|
+
parameters: z.string().optional().describe('For invoke operation: JSON object string of parameters'),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export type SpaceArgs = z.infer<typeof spaceArgsSchema>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parameter information extracted from schema
|
|
25
|
+
*/
|
|
26
|
+
export interface ParameterInfo {
|
|
27
|
+
name: string;
|
|
28
|
+
type: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
required: boolean;
|
|
31
|
+
default?: unknown;
|
|
32
|
+
enum?: unknown[];
|
|
33
|
+
isFileData?: boolean;
|
|
34
|
+
complexType?: string; // Reason why the type is complex
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Result of schema complexity analysis
|
|
39
|
+
*/
|
|
40
|
+
export interface SchemaComplexityResult {
|
|
41
|
+
isSimple: boolean;
|
|
42
|
+
reason?: string; // Reason if not simple
|
|
43
|
+
parameters: ParameterInfo[];
|
|
44
|
+
toolName: string;
|
|
45
|
+
toolDescription?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Result of parameter processing
|
|
50
|
+
*/
|
|
51
|
+
export interface ProcessParametersResult {
|
|
52
|
+
valid: boolean;
|
|
53
|
+
parameters?: Record<string, unknown>;
|
|
54
|
+
error?: string;
|
|
55
|
+
warnings?: string[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* JSON Schema property definition
|
|
60
|
+
*/
|
|
61
|
+
export interface JsonSchemaProperty {
|
|
62
|
+
type?: string;
|
|
63
|
+
title?: string;
|
|
64
|
+
description?: string;
|
|
65
|
+
default?: unknown;
|
|
66
|
+
enum?: unknown[];
|
|
67
|
+
format?: string;
|
|
68
|
+
properties?: Record<string, JsonSchemaProperty>;
|
|
69
|
+
items?: JsonSchemaProperty;
|
|
70
|
+
required?: string[];
|
|
71
|
+
[key: string]: unknown;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* JSON Schema definition
|
|
76
|
+
*/
|
|
77
|
+
export interface JsonSchema {
|
|
78
|
+
type?: string;
|
|
79
|
+
properties?: Record<string, JsonSchemaProperty>;
|
|
80
|
+
required?: string[];
|
|
81
|
+
description?: string;
|
|
82
|
+
[key: string]: unknown;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* File input help message constant
|
|
87
|
+
*/
|
|
88
|
+
export const FILE_INPUT_HELP_MESSAGE =
|
|
89
|
+
'Provide a publicly accessible URL (http:// or https://) pointing to the file. ' +
|
|
90
|
+
'To upload local files, use the dedicated gr_* prefixed tool for this space, which supports file upload.';
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Check if a property is a FileData type
|
|
94
|
+
*/
|
|
95
|
+
export function isFileDataProperty(prop: JsonSchemaProperty): boolean {
|
|
96
|
+
return (
|
|
97
|
+
prop.title === 'ImageData' ||
|
|
98
|
+
prop.title === 'FileData' ||
|
|
99
|
+
(prop.format?.includes('http') && prop.format?.includes('file')) ||
|
|
100
|
+
false
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Check if a schema contains file data
|
|
106
|
+
*/
|
|
107
|
+
export function hasFileData(schema: JsonSchema): boolean {
|
|
108
|
+
if (!schema.properties) return false;
|
|
109
|
+
|
|
110
|
+
return Object.values(schema.properties).some((prop) => isFileDataProperty(prop));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Extended result type for invoke operation that includes raw MCP result
|
|
115
|
+
* This allows the space tool to return structured content blocks instead of formatted text
|
|
116
|
+
*/
|
|
117
|
+
export interface InvokeResult {
|
|
118
|
+
result: {
|
|
119
|
+
content: unknown[];
|
|
120
|
+
isError?: boolean;
|
|
121
|
+
[key: string]: unknown;
|
|
122
|
+
};
|
|
123
|
+
warnings: string[];
|
|
124
|
+
totalResults: number;
|
|
125
|
+
resultsShared: number;
|
|
126
|
+
isError?: boolean;
|
|
127
|
+
}
|