Skip to content

Commit 738b631

Browse files
committed
make use of product documentation listing endpoint. fix mcp-ui build.
1 parent 2b6f765 commit 738b631

File tree

6 files changed

+73
-91
lines changed

6 files changed

+73
-91
lines changed

packages/app/src/server/gradio-endpoint-connector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ function createToolHandler(
594594

595595
const title = `${connection.name || 'MCP UI tool'}`;
596596
const uriSafeName = (connection.name || 'audio').replace(/[^a-z0-9-_]+/gi, '-');
597-
const uiUri = `ui://huggingface-mcp/${uriSafeName}/${Date.now().toString()}`;
597+
const uiUri: `ui://${string}` = `ui://huggingface-mcp/${uriSafeName}/${Date.now().toString()}`;
598598

599599
const uiResource = createAudioPlayerUIResource(uiUri, {
600600
title,

packages/app/src/server/types/mcp-ui-server-shim.ts

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

packages/app/src/server/utils/ui/audio-player.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ export function buildAudioPlayerHTML({
161161
/**
162162
* Convenience helper to wrap the HTML as a UIResource for MCP-UI transports.
163163
*/
164-
export function createAudioPlayerUIResource(uri: string, options: AudioPlayerOptions): UIResource {
164+
export function createAudioPlayerUIResource(uri: `ui://${string}`, options: AudioPlayerOptions): UIResource {
165165
return createUIResource({
166166
uri,
167167
encoding: 'text',

packages/mcp/src/docs-search/docs-semantic-search.ts

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,34 @@ import type { ToolResult } from '../types/tool-result.js';
1010
export const DOCS_SEMANTIC_SEARCH_CONFIG = {
1111
name: 'hf_doc_search',
1212
description:
13-
'Search documentation about all of Hugging Face products and libraries (Transformers, Datasets, Diffusers, Gradio, Hub, and more). Use this for the most up-to-date information ' +
14-
'Returns excerpts grouped by Product and Document.',
13+
'Search and Discover Hugging Face Product and Library documentation. Use an empty query to discover structure and navigation hints and tips. ' +
14+
'You MUST consult this tool for the most up-to-date information when using Hugging Face libraries. Combine with the Product filter to focus results.',
1515
schema: z.object({
1616
query: z
1717
.string()
18-
.min(3, 'Supply at least one search term')
1918
.max(200, 'Query too long')
20-
.describe('Semantic search query'),
21-
product: z
22-
.string()
23-
.optional()
19+
.superRefine((value, ctx) => {
20+
const trimmed = value.trim();
21+
if (trimmed.length === 0) {
22+
return;
23+
}
24+
if (trimmed.length < 3) {
25+
ctx.addIssue({
26+
code: z.ZodIssueCode.too_small,
27+
type: 'string',
28+
minimum: 3,
29+
inclusive: true,
30+
message: 'Supply at least one search term',
31+
});
32+
}
33+
})
2434
.describe(
25-
'Filter by Product (e.g., "hub", "dataset-viewer", "transformers"). Supply when known for focused results'
35+
'Start with an empty query for structure, endpoint discovery and navigation tips. Use semantic queries for targetted searches.'
2636
),
37+
product: z.string().optional().describe('Filter by Product. Supply when known for focused results'),
2738
}),
2839
annotations: {
29-
title: 'Hugging Face Documentation Library Search',
40+
title: 'Hugging Face Documentation Search',
3041
destructiveHint: false,
3142
readOnlyHint: true,
3243
openWorldHint: true,
@@ -49,9 +60,16 @@ interface DocSearchApiParams {
4960
product?: string;
5061
}
5162

63+
interface DocsIndexEntry {
64+
id: string;
65+
url: string;
66+
category: string;
67+
}
68+
5269
// Token budget defaults
5370
const DEFAULT_TOKEN_BUDGET = 12500;
5471
const TRUNCATE_EXCERPT_LENGTH = 400; // chars for truncated excerpts
72+
const DOCS_INDEX_URL = 'https://huggingface.co/api/docs';
5573

5674
/**
5775
* Use the Hugging Face Semantic Document Search API
@@ -75,13 +93,13 @@ export class DocSearchTool extends HfApiCall<DocSearchApiParams, DocSearchResult
7593
*/
7694
async search(params: DocSearchParams): Promise<ToolResult> {
7795
try {
78-
if (!params.query) return {
79-
formatted: 'No query provided',
80-
totalResults: 0,
81-
resultsShared: 0
82-
};
96+
const query = params.query?.trim() ?? '';
97+
if (query.length === 0) {
98+
const docsIndex = await this.fetchFromApi<DocsIndexEntry[]>(DOCS_INDEX_URL);
99+
return formatDocsIndex(docsIndex);
100+
}
83101

84-
const apiParams: DocSearchApiParams = { q: params.query.toLowerCase() };
102+
const apiParams: DocSearchApiParams = { q: query.toLowerCase() };
85103
if (params.product) {
86104
apiParams.product = params.product;
87105
}
@@ -91,14 +109,14 @@ export class DocSearchTool extends HfApiCall<DocSearchApiParams, DocSearchResult
91109
if (results.length === 0) {
92110
return {
93111
formatted: params.product
94-
? `No documentation found for query '${params.query}' in product '${params.product}'`
95-
: `No documentation found for query '${params.query}'`,
112+
? `No documentation found for query '${query}' in product '${params.product}'`
113+
: `No documentation found for query '${query}'`,
96114
totalResults: 0,
97-
resultsShared: 0
115+
resultsShared: 0,
98116
};
99117
}
100118

101-
return formatSearchResults(params.query, results, params.product, this.tokenBudget);
119+
return formatSearchResults(query, results, params.product, this.tokenBudget);
102120
} catch (error) {
103121
if (error instanceof Error) {
104122
throw new Error(`Failed to search documentation: ${error.message}`);
@@ -138,6 +156,39 @@ function groupResults(results: DocSearchResult[]): Map<string, Map<string, DocSe
138156
return grouped;
139157
}
140158

159+
/**
160+
* Format the documentation root index response for empty queries
161+
*/
162+
function formatDocsIndex(docsIndex: DocsIndexEntry[]): ToolResult {
163+
if (!docsIndex.length) {
164+
return {
165+
formatted:
166+
'No documentation categories are currently available. Try running the search again with specific terms.',
167+
totalResults: 0,
168+
resultsShared: 0,
169+
};
170+
}
171+
172+
const header = '### Hugging Face Documentation Products\n';
173+
const tableHeader = '| Product | Category | Documentation |\n| --- | --- | --- |\n';
174+
const rows = docsIndex
175+
.map((entry) => {
176+
const docsUrl = `https://huggingface.co${entry.url}`;
177+
return `| \`${entry.id}\` | ${escapeMarkdown(entry.category)} | ${docsUrl} |`;
178+
})
179+
.join('\n');
180+
181+
const llmsNote =
182+
'\n\nEach documentation root exposes an `llms.txt` endpoint (e.g. add `/llms.txt` to the documentation URL). ' +
183+
'Use semantic search when you have a specific question for faster, more targeted results.';
184+
185+
return {
186+
formatted: `${header}${tableHeader}${rows}${llmsNote}`,
187+
totalResults: docsIndex.length,
188+
resultsShared: docsIndex.length,
189+
};
190+
}
191+
141192
/**
142193
* Group page results by section (heading2)
143194
*/
@@ -323,6 +374,6 @@ function formatSearchResults(
323374
return {
324375
formatted: lines.join('\n'),
325376
totalResults: results.length,
326-
resultsShared: results.length
377+
resultsShared: results.length,
327378
};
328379
}

packages/mcp/src/types/mcp-ui-server-shim.ts

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

packages/mcp/src/use-space.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { z } from 'zod';
22
import { HfApiCall } from './hf-api-call.js';
33
import { spaceInfo, type SpaceEntry } from '@huggingface/hub';
44
import type { ToolResult } from './types/tool-result.js';
5-
import './types/mcp-ui-server-shim.js';
65
import { createUIResource } from '@mcp-ui/server';
76
// Define the return type that matches MCP server expectations
87
interface UseSpaceResult {

0 commit comments

Comments
 (0)