@llmindset/hf-mcp 0.2.53 → 0.2.54
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 +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/space/commands/discover.js +1 -1
- package/dist/space/commands/discover.js.map +1 -1
- package/dist/space/commands/invoke.d.ts.map +1 -1
- package/dist/space/commands/invoke.js +14 -110
- package/dist/space/commands/invoke.js.map +1 -1
- package/dist/space/dynamic-space-tool.d.ts.map +1 -1
- package/dist/space/dynamic-space-tool.js +16 -32
- package/dist/space/dynamic-space-tool.js.map +1 -1
- package/dist/space/types.d.ts +2 -1
- package/dist/space/types.d.ts.map +1 -1
- package/dist/space/types.js +9 -5
- package/dist/space/types.js.map +1 -1
- package/dist/space/utils/gradio-caller.d.ts +14 -0
- package/dist/space/utils/gradio-caller.d.ts.map +1 -0
- package/dist/space/utils/gradio-caller.js +138 -0
- package/dist/space/utils/gradio-caller.js.map +1 -0
- package/dist/space/utils/gradio-schema.d.ts +13 -0
- package/dist/space/utils/gradio-schema.d.ts.map +1 -0
- package/dist/space/utils/gradio-schema.js +44 -0
- package/dist/space/utils/gradio-schema.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/space/commands/discover.ts +1 -1
- package/src/space/commands/invoke.ts +13 -147
- package/src/space/dynamic-space-tool.ts +16 -31
- package/src/space/types.ts +18 -12
- package/src/space/utils/gradio-caller.ts +204 -0
- package/src/space/utils/gradio-schema.ts +74 -0
- package/test/gradio-caller.test.ts +77 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
|
|
3
|
+
export type ParsedSchemaFormat = 'array' | 'object';
|
|
4
|
+
|
|
5
|
+
export interface ParsedGradioSchema {
|
|
6
|
+
format: ParsedSchemaFormat;
|
|
7
|
+
tools: Array<{ name: string; description?: string; inputSchema: unknown }>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Parse a Gradio MCP schema that may be returned as either:
|
|
12
|
+
* - Array format: [{ name, description?, inputSchema }, ...]
|
|
13
|
+
* - Object format: { toolName: { properties, required, description?, ... }, ... }
|
|
14
|
+
*
|
|
15
|
+
* Returns a normalized list of tools and the detected format.
|
|
16
|
+
* Throws if the schema is invalid or empty.
|
|
17
|
+
*/
|
|
18
|
+
export function parseGradioSchemaResponse(schemaResponse: unknown): ParsedGradioSchema {
|
|
19
|
+
// Array format
|
|
20
|
+
if (Array.isArray(schemaResponse)) {
|
|
21
|
+
const tools = (schemaResponse as Array<unknown>).filter((tool): tool is { name: string; description?: string; inputSchema: unknown } => {
|
|
22
|
+
return (
|
|
23
|
+
typeof tool === 'object' &&
|
|
24
|
+
tool !== null &&
|
|
25
|
+
'name' in tool &&
|
|
26
|
+
typeof (tool as { name?: unknown }).name === 'string' &&
|
|
27
|
+
'inputSchema' in tool
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (tools.length === 0) {
|
|
32
|
+
throw new Error('Invalid schema: no tools found in array schema');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
format: 'array',
|
|
37
|
+
tools,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Object format
|
|
42
|
+
if (typeof schemaResponse === 'object' && schemaResponse !== null) {
|
|
43
|
+
const entries = Object.entries(schemaResponse as Record<string, unknown>);
|
|
44
|
+
const tools: ParsedGradioSchema['tools'] = entries.map(([name, toolSchema]) => ({
|
|
45
|
+
name,
|
|
46
|
+
description: typeof (toolSchema as { description?: unknown }).description === 'string' ? (toolSchema as { description?: string }).description : undefined,
|
|
47
|
+
inputSchema: toolSchema,
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
if (tools.length === 0) {
|
|
51
|
+
throw new Error('Invalid schema: no tools found in object schema');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
format: 'object',
|
|
56
|
+
tools,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
throw new Error('Invalid schema format: expected array or object');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Convert ParsedGradioSchema.tools into SDK Tool[] shape, filtering out <lambda tools.
|
|
65
|
+
*/
|
|
66
|
+
export function normalizeParsedTools(parsed: ParsedGradioSchema): Tool[] {
|
|
67
|
+
return parsed.tools
|
|
68
|
+
.filter((t) => !t.name.toLowerCase().includes('<lambda'))
|
|
69
|
+
.map((parsedTool) => ({
|
|
70
|
+
name: parsedTool.name,
|
|
71
|
+
description: parsedTool.description || `${parsedTool.name} tool`,
|
|
72
|
+
inputSchema: parsedTool.inputSchema as Tool['inputSchema'],
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { rewriteReplicaUrlsInResult, extractReplicaId } from '../src/space/utils/gradio-caller.js';
|
|
3
|
+
import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
+
|
|
5
|
+
const baseResult: typeof CallToolResultSchema._type = {
|
|
6
|
+
content: [],
|
|
7
|
+
isError: false,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
describe('extractReplicaId', () => {
|
|
11
|
+
it('extracts the suffix after hyphen', () => {
|
|
12
|
+
expect(extractReplicaId('oyerizs4-dspr4')).toBe('dspr4');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('returns null when no hyphen exists', () => {
|
|
16
|
+
expect(extractReplicaId('singlepart')).toBeNull();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('returns null for empty input', () => {
|
|
20
|
+
expect(extractReplicaId('')).toBeNull();
|
|
21
|
+
expect(extractReplicaId(undefined)).toBeNull();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('rewriteReplicaUrlsInResult', () => {
|
|
26
|
+
const matchUrl = 'https://mcp-tools-qwen-image-fast.hf.space/gradio_api';
|
|
27
|
+
const rewrittenUrl = 'https://mcp-tools-qwen-image-fast.hf.space/--replicas/dspr4/gradio_api';
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
delete process.env.NO_REPLICA_REWRITE;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
delete process.env.NO_REPLICA_REWRITE;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('rewrites URLs in text content when header is present', () => {
|
|
38
|
+
const result: typeof CallToolResultSchema._type = {
|
|
39
|
+
...baseResult,
|
|
40
|
+
content: [
|
|
41
|
+
{ type: 'text', text: `prefix ${matchUrl} suffix` },
|
|
42
|
+
`plain ${matchUrl}`,
|
|
43
|
+
{ type: 'image', data: 'noop' },
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const rewritten = rewriteReplicaUrlsInResult(result, 'oyerizs4-dspr4');
|
|
48
|
+
|
|
49
|
+
expect((rewritten.content[0] as { text: string }).text).toContain(rewrittenUrl);
|
|
50
|
+
expect((rewritten.content[1] as { text: string }).text).toContain(rewrittenUrl);
|
|
51
|
+
// Non-text blocks untouched
|
|
52
|
+
expect(rewritten.content[2]).toEqual(result.content[2]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('does nothing when header is missing', () => {
|
|
56
|
+
const result: typeof CallToolResultSchema._type = {
|
|
57
|
+
...baseResult,
|
|
58
|
+
content: [{ type: 'text', text: `prefix ${matchUrl} suffix` }],
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const rewritten = rewriteReplicaUrlsInResult(result, undefined);
|
|
62
|
+
|
|
63
|
+
expect(rewritten).toEqual(result);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('respects NO_REPLICA_REWRITE env', () => {
|
|
67
|
+
process.env.NO_REPLICA_REWRITE = '1';
|
|
68
|
+
const result: typeof CallToolResultSchema._type = {
|
|
69
|
+
...baseResult,
|
|
70
|
+
content: [{ type: 'text', text: matchUrl }],
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const rewritten = rewriteReplicaUrlsInResult(result, 'oyerizs4-dspr4');
|
|
74
|
+
|
|
75
|
+
expect(rewritten).toEqual(result);
|
|
76
|
+
});
|
|
77
|
+
});
|