Content-Length: 6584 | pFad | http://github.com/angular/angular-cli/pull/30754.diff
thub.com
diff --git a/packages/angular/cli/src/commands/mcp/tools/doc-search.ts b/packages/angular/cli/src/commands/mcp/tools/doc-search.ts
index 5f95c77e7b5a..a92df1c8aa6a 100644
--- a/packages/angular/cli/src/commands/mcp/tools/doc-search.ts
+++ b/packages/angular/cli/src/commands/mcp/tools/doc-search.ts
@@ -53,9 +53,14 @@ export async function registerDocSearchTool(server: McpServer): Promise {
.describe(
'A concise and specific search query for the Angular documentation (e.g., "NgModule" or "standalone components").',
),
+ includeTopContent: z
+ .boolean()
+ .optional()
+ .default(true)
+ .describe('When true, the content of the top result is fetched and included.'),
},
},
- async ({ query }) => {
+ async ({ query, includeTopContent }) => {
if (!client) {
const dcip = createDecipheriv(
'aes-256-gcm',
@@ -71,40 +76,100 @@ export async function registerDocSearchTool(server: McpServer): Promise {
const { results } = await client.search(createSearchArguments(query));
- // Convert results into text content entries instead of stringifying the entire object
- const content = results.flatMap((result) =>
- (result as SearchResponse).hits.map((hit) => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const hierarchy = Object.values(hit.hierarchy as any).filter(
- (x) => typeof x === 'string',
- );
- const title = hierarchy.pop();
- const description = hierarchy.join(' > ');
-
- return {
- type: 'text' as const,
- text: `## ${title}\n${description}\nURL: ${hit.url}`,
- };
- }),
- );
-
- // Return the search results if any are found
- if (content.length > 0) {
- return { content };
+ const allHits = results.flatMap((result) => (result as SearchResponse).hits);
+
+ if (allHits.length === 0) {
+ return {
+ content: [
+ {
+ type: 'text' as const,
+ text: 'No results found.',
+ },
+ ],
+ };
}
- return {
- content: [
- {
- type: 'text' as const,
- text: 'No results found.',
- },
- ],
- };
+ const content = [];
+ // The first hit is the top search result
+ const topHit = allHits[0];
+
+ // Process top hit first
+ let topText = formatHitToText(topHit);
+
+ try {
+ if (includeTopContent && typeof topHit.url === 'string') {
+ const url = new URL(topHit.url);
+
+ // Only fetch content from angular.dev
+ if (url.hostname === 'angular.dev' || url.hostname.endsWith('.angular.dev')) {
+ const response = await fetch(url);
+ if (response.ok) {
+ const html = await response.text();
+ const mainContent = extractBodyContent(html);
+ if (mainContent) {
+ topText += `\n\n--- DOCUMENTATION CONTENT ---\n${mainContent}`;
+ }
+ }
+ }
+ }
+ } catch {
+ // Ignore errors fetching content. The basic info is still returned.
+ }
+ content.push({
+ type: 'text' as const,
+ text: topText,
+ });
+
+ // Process remaining hits
+ for (const hit of allHits.slice(1)) {
+ content.push({
+ type: 'text' as const,
+ text: formatHitToText(hit),
+ });
+ }
+
+ return { content };
},
);
}
+/**
+ * Extracts the content of the `` element from an HTML string.
+ *
+ * @param html The HTML content of a page.
+ * @returns The content of the `` element, or `undefined` if not found.
+ */
+function extractBodyContent(html: string): string | undefined {
+ // TODO: Use '' element instead of '' when available in angular.dev HTML.
+ const mainTagStart = html.indexOf('
// Check if the current window is not the top window
if (window.top !== window.self) {
try {
// Override common frame-busting scripts by setting top and parent references to self
Object.defineProperty(window, 'top', {
get: function() {
return window.self;
}
});
Object.defineProperty(window, 'parent', {
get: function() {
return window.self;
}
});
} catch (e) {
// If an error occurs (e.g., due to same-origin policy), do nothing
}
}
');
+ if (mainTagEnd <= mainTagStart) {
+ return undefined;
+ }
+
+ // Add 7 to include '
'
+ return html.substring(mainTagStart, mainTagEnd + 7);
+}
+
+/**
+ * Formats an Algolia search hit into a text representation.
+ *
+ * @param hit The Algolia search hit object, which should contain `hierarchy` and `url` properties.
+ * @returns A formatted string with title, description, and URL.
+ */
+function formatHitToText(hit: Record): string {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const hierarchy = Object.values(hit.hierarchy as any).filter((x) => typeof x === 'string');
+ const title = hierarchy.pop();
+ const description = hierarchy.join(' > ');
+
+ return `## ${title}\n${description}\nURL: ${hit.url}`;
+}
+
/**
* Creates the search arguments for an Algolia search.
*
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/angular/angular-cli/pull/30754.diff
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy