Skip to content

Commit 3b57f4f

Browse files
mogerynickscamara
andauthored
feat(search): includeDomains/excldueDomains (#222)
* feat(search): includeDomains/excldueDomains * Nick: * Update publish.yml --------- Co-authored-by: Nicolas <20311743+nickscamara@users.noreply.github.com>
1 parent badde9b commit 3b57f4f

5 files changed

Lines changed: 117 additions & 42 deletions

File tree

.github/workflows/publish.yml

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,36 @@ on:
77

88
jobs:
99
publish:
10+
name: Publish
1011
runs-on: ubuntu-latest
1112
permissions:
1213
id-token: write # Required for OIDC authentication with MCP registry
1314
contents: read
1415

1516
steps:
16-
- uses: actions/checkout@v3
17+
- uses: actions/checkout@v5
1718

18-
- name: Use Node.js
19-
uses: actions/setup-node@v3
19+
- name: Install pnpm
20+
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4
2021
with:
21-
node-version: '20.x'
22-
cache: 'pnpm'
23-
registry-url: 'https://registry.npmjs.org'
22+
version: 10
2423

25-
- name: Install dependencies
26-
run: pnpm install --frozen-lockfile
24+
- name: Set up Node.js
25+
uses: actions/setup-node@v4
26+
with:
27+
node-version: "20"
28+
cache: "pnpm"
2729

28-
- name: Build
29-
run: pnpm run build
30+
- name: Authenticate
31+
run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
32+
env:
33+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
3034

3135
- name: Publish to NPM
32-
run: pnpm publish --access public
33-
env:
34-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
36+
run: |
37+
pnpm install
38+
pnpm run build
39+
pnpm publish --access public --no-git-checks
3540
3641
- name: Install MCP Publisher
3742
run: |

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
},
2929
"license": "MIT",
3030
"dependencies": {
31-
"@mendable/firecrawl-js": "4.17.0",
31+
"@mendable/firecrawl-js": "4.21.0",
3232
"dotenv": "^17.2.2",
3333
"firecrawl-fastmcp": "^1.0.4",
3434
"typescript": "^5.9.2",

pnpm-lock.yaml

Lines changed: 15 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.ts

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,35 @@ function removeEmptyTopLevel<T extends Record<string, any>>(
5353
return out;
5454
}
5555

56+
const searchDomainSchema = z
57+
.string()
58+
.trim()
59+
.toLowerCase()
60+
.regex(
61+
/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/,
62+
'Domain must be a valid hostname without protocol or path'
63+
);
64+
65+
function buildSearchQueryWithDomains(
66+
query: string,
67+
includeDomains?: string[],
68+
excludeDomains?: string[]
69+
): string {
70+
if (includeDomains?.length) {
71+
return `${query} (${includeDomains
72+
.map((domain) => `site:${domain}`)
73+
.join(' OR ')})`;
74+
}
75+
76+
if (excludeDomains?.length) {
77+
return `${query} ${excludeDomains
78+
.map((domain) => `-site:${domain}`)
79+
.join(' ')}`;
80+
}
81+
82+
return query;
83+
}
84+
5685
class ConsoleLogger implements Logger {
5786
private shouldLog =
5887
process.env.CLOUD_SERVICE === 'true' ||
@@ -576,6 +605,7 @@ The query also supports search operators, that you can use if needed to refine t
576605
**Common mistakes:** Using crawl or map for open-ended questions (use search instead).
577606
**Prompt Example:** "Find the latest research papers on AI published in 2023."
578607
**Sources:** web, images, news, default to web unless needed images or news.
608+
**Domain filters:** Use includeDomains to restrict results to specific domains, or excludeDomains to remove domains. Do not use both in the same request. Domains must be hostnames only, without protocol or path.
579609
**Scrape Options:** Only use scrapeOptions when you think it is absolutely necessary. When you do so default to a lower limit to avoid timeouts, 5 or lower.
580610
**Optimal Workflow:** Search first using firecrawl_search without formats, then after fetching the results, use the scrape tool to get the content of the relevantpage(s) that you want to scrape
581611
@@ -586,6 +616,7 @@ The query also supports search operators, that you can use if needed to refine t
586616
"arguments": {
587617
"query": "top AI companies",
588618
"limit": 5,
619+
"includeDomains": ["example.com"],
589620
"sources": [
590621
{ "type": "web" }
591622
]
@@ -615,18 +646,28 @@ The query also supports search operators, that you can use if needed to refine t
615646
\`\`\`
616647
**Returns:** Array of search results (with optional scraped content).
617648
`,
618-
parameters: z.object({
619-
query: z.string().min(1),
620-
limit: z.number().optional(),
621-
tbs: z.string().optional(),
622-
filter: z.string().optional(),
623-
location: z.string().optional(),
624-
sources: z
625-
.array(z.object({ type: z.enum(['web', 'images', 'news']) }))
626-
.optional(),
627-
scrapeOptions: scrapeParamsSchema.omit({ url: true }).partial().optional(),
628-
enterprise: z.array(z.enum(['default', 'anon', 'zdr'])).optional(),
629-
}),
649+
parameters: z
650+
.object({
651+
query: z.string().min(1),
652+
limit: z.number().optional(),
653+
tbs: z.string().optional(),
654+
filter: z.string().optional(),
655+
location: z.string().optional(),
656+
includeDomains: z.array(searchDomainSchema).optional(),
657+
excludeDomains: z.array(searchDomainSchema).optional(),
658+
sources: z
659+
.array(z.object({ type: z.enum(['web', 'images', 'news']) }))
660+
.optional(),
661+
scrapeOptions: scrapeParamsSchema
662+
.omit({ url: true })
663+
.partial()
664+
.optional(),
665+
enterprise: z.array(z.enum(['default', 'anon', 'zdr'])).optional(),
666+
})
667+
.refine(
668+
(args) => !(args.includeDomains?.length && args.excludeDomains?.length),
669+
'includeDomains and excludeDomains cannot both be specified'
670+
),
630671
execute: async (
631672
args: unknown,
632673
{ session, log }: { session?: SessionData; log: Logger }
@@ -635,15 +676,25 @@ The query also supports search operators, that you can use if needed to refine t
635676
const { query, ...opts } = args as Record<string, unknown>;
636677

637678
const searchOpts = { ...opts } as Record<string, unknown>;
679+
const includeDomains = searchOpts.includeDomains as string[] | undefined;
680+
const excludeDomains = searchOpts.excludeDomains as string[] | undefined;
681+
delete searchOpts.includeDomains;
682+
delete searchOpts.excludeDomains;
683+
638684
if (searchOpts.scrapeOptions) {
639685
searchOpts.scrapeOptions = transformScrapeParams(
640686
searchOpts.scrapeOptions as Record<string, unknown>
641687
);
642688
}
643689

644690
const cleaned = removeEmptyTopLevel(searchOpts);
645-
log.info('Searching', { query: String(query) });
646-
const res = await client.search(query as string, {
691+
const searchQuery = buildSearchQueryWithDomains(
692+
query as string,
693+
includeDomains,
694+
excludeDomains
695+
);
696+
log.info('Searching', { query: searchQuery });
697+
const res = await client.search(searchQuery, {
647698
...(cleaned as any),
648699
origin: ORIGIN,
649700
});

src/legacy/index.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,7 @@ Search the web and optionally extract content from search results. This is the m
499499
**Common mistakes:** Using crawl or map for open-ended questions (use search instead).
500500
**Prompt Example:** "Find the latest research papers on AI published in 2023."
501501
**Sources:** web, images, news, default to web unless needed images or news.
502+
**Domain filters:** Use includeDomains to restrict results to specific domains, or excludeDomains to remove domains. Do not use both in the same request. Domains must be hostnames only, without protocol or path.
502503
**Usage Example:**
503504
\`\`\`json
504505
{
@@ -508,6 +509,7 @@ Search the web and optionally extract content from search results. This is the m
508509
"limit": 5,
509510
"lang": "en",
510511
"country": "us",
512+
"excludeDomains": ["example.com"],
511513
"sources": [
512514
"web",
513515
"images",
@@ -545,6 +547,22 @@ Search the web and optionally extract content from search results. This is the m
545547
type: 'string',
546548
description: 'Location parameter for search results',
547549
},
550+
includeDomains: {
551+
type: 'array',
552+
description:
553+
'Domains to include in search results. Cannot be used with excludeDomains. Domains must be hostnames only, without protocol or path.',
554+
items: {
555+
type: 'string',
556+
},
557+
},
558+
excludeDomains: {
559+
type: 'array',
560+
description:
561+
'Domains to exclude from search results. Cannot be used with includeDomains. Domains must be hostnames only, without protocol or path.',
562+
items: {
563+
type: 'string',
564+
},
565+
},
548566
sources: {
549567
type: 'array',
550568
description:

0 commit comments

Comments
 (0)