Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ npx -y firecrawl-cli@latest init -y --browser
Use the Alexandria beta with your existing Firecrawl login or API key:

```bash
npx firecrawl-cli@alexandria list # visible providers
npx firecrawl-cli@alexandria alexandria list # introduction and live categories
npx firecrawl-cli@alexandria list --providers # flat provider list
npx firecrawl-cli@alexandria list finance # providers in a category
npx firecrawl-cli@alexandria list benzinga # provider's tools
npx firecrawl-cli@alexandria list-tools benzinga # same browsing interface
Expand All @@ -32,11 +33,11 @@ npx firecrawl-cli@alexandria list benzinga calendar --group
npx firecrawl-cli@alexandria list benzinga <capability> --json
```

Available in `1.23.4-alexandria-beta.8` onward. `list` and `list-tools` are interchangeable, including under `firecrawl alexandria`. Provider IDs work directly; you do not need their category. Lists stay compact until you select a capability, which reveals its inputs, response, examples, and price. Browsable results include next commands; selecting a capability displays the final contract. Generated commands use `firecrawl`; when using `npx`, replace that prefix with `npx firecrawl-cli@alexandria`.
The category overview is available in `1.23.4-alexandria-beta.9` onward. `list` and `list-tools` are interchangeable, including under `firecrawl alexandria`. The root explains how to find and call tools and lists live category descriptions. Choose a category to see its providers, or jump directly to a provider. Selecting a capability reveals its inputs, response, examples, and price. Browsable results expose next commands in JSON; the text guide explains how to select each category. Selecting a capability displays the final contract. Generated commands use `firecrawl`; when using `npx`, replace that prefix with `npx firecrawl-cli@alexandria`.

Discovery is free and never executes the listed tools. Results come from the live Find Tools catalog through Scrape and respect your access. `--limit` controls page size (default 20, maximum 100); follow the returned `More` command for the next page. `--json` includes the API envelope, next commands, and receipts for every discovery request.
Discovery is free and never executes the listed tools. The root reads `GET /exchange/discover` on the configured Firecrawl API using your existing credentials; provider and tool lookups use the Find Tools meta tool through Scrape. Category membership and descriptions stay on the server. The root shows all returned categories; `--limit` controls provider/tool page size (default 20, maximum 100). Follow `More` to continue a page. Root `--json` exposes categories at `data.items`; provider/tool JSON keeps the Scrape envelope at `data.alexandria[0].data`. Both include request IDs and navigation where available.

Provider IDs take precedence over category IDs; use `--category` to select a category explicitly. Exact capability IDs take precedence over group IDs; use `--group` to select a group explicitly. Use `firecrawl search --sources alexandria` to find tools by task, or `firecrawl find-tools` for URL lookup and raw catalog selectors.
Provider IDs take precedence over category IDs; use `--category` to select a category explicitly. Display names such as `retail`, `developer`, and `public-records` also resolve to their catalog category IDs. Exact capability IDs take precedence over group IDs; use `--group` to select a group explicitly. Use `firecrawl search --sources alexandria` to find tools by task, or `firecrawl find-tools` for URL lookup and raw catalog selectors.

### Setup Skills, Workflows, and MCP

Expand Down
8 changes: 4 additions & 4 deletions beta-skills/firecrawl-alexandria/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,20 @@ A complete contract returned by search needs no additional discovery call. Check

## Browse the catalog progressively

With beta `1.23.4-alexandria-beta.8` or newer, use `list` when the user wants to browse providers, categories, or a known provider's tools:
With beta `1.23.4-alexandria-beta.9` or newer, use `list` when the user wants to browse categories, providers, or a known provider's tools:

```sh
npx firecrawl-cli@alexandria list
npx firecrawl-cli@alexandria alexandria list
npx firecrawl-cli@alexandria list finance
npx firecrawl-cli@alexandria list benzinga
npx firecrawl-cli@alexandria list benzinga --groups
npx firecrawl-cli@alexandria list benzinga calendar --group
npx firecrawl-cli@alexandria list benzinga <returned-capability-id> --json
```

The root lists visible providers. A category filters providers; a provider lists compact tools directly. Selecting a capability expands only that contract, including price, inputs, response, and examples. Categories are optional: use returned provider and capability IDs directly. `list-tools` is an alias for `list`; both also work under `alexandria`. `--category` and `--group` resolve ambiguous IDs explicitly.
The root shows an introduction, discovery/execution commands, and live categories with descriptions. A category lists its providers; a provider lists compact tools directly. Selecting a capability expands only that contract, including price, inputs, response, and examples. Categories are optional: use returned provider and capability IDs directly. Use `list --providers` only when a flat provider inventory is needed. `list-tools` is an alias for `list`; both also work under `alexandria`. `--category` and `--group` resolve ambiguous IDs explicitly. Category display names such as `retail`, `developer`, and `public-records` are accepted alongside the returned canonical IDs.

`list` only calls the free Find Tools meta tool through Scrape. `--json` preserves the API envelope and adds `discoveryRequests` receipts and `nextCommand` navigation. Page size defaults to 20 (`--limit 1–100`); follow the returned `More`/`nextCommand` only when needed. Generated commands start with `firecrawl`; replace that prefix with `npx firecrawl-cli@alexandria` to stay on this beta. Raw `--request` next requests preserve selectors and pagination and must not be mixed with a path or filters.
The root reads the free public `GET /exchange/discover` route on the configured Firecrawl API with existing credentials. Category rows are at `data.items` in JSON. Provider and tool lookups use the free Find Tools meta tool through Scrape, with rows at `data.alexandria[0].data.items`. Both include `discoveryRequests` and `nextCommand` navigation; no listed tool is executed. The root shows all categories. Provider/tool page size defaults to 20 (`--limit 1–100`); follow `More`/`nextCommand` only when needed. Generated commands start with `firecrawl`; replace that prefix with `npx firecrawl-cli@alexandria` to stay on this beta. Raw `--request` next requests preserve selectors and pagination and must not be mixed with a path or filters.

## Explicit requests for tools: search then the Find Tools meta tool

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "firecrawl-cli",
"version": "1.23.4-alexandria-beta.8",
"version": "1.23.4-alexandria-beta.9",
"publishConfig": {
"tag": "alexandria"
},
Expand Down
88 changes: 86 additions & 2 deletions src/__tests__/alexandria-beta.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,23 +91,93 @@ function catalogue(level: string, items: any[], next?: unknown) {
};
}

it('starts with a live category guide and preserves category-discovery access errors', async () => {
response = {
cohorts: [
{
cohort: 'finance',
about: 'Live market data description.',
providers: 2,
},
{
cohort: 'new-category',
about: 'A category added by the server.',
providers: 0,
},
],
};
const guide = await cli(['alexandria', 'list']);
expect(guide.code).toBe(0);
expect(guide.stdout).toContain('Firecrawl Alexandria');
expect(guide.stdout).toContain(
'Finance (finance): Live market data description.'
);
expect(guide.stdout).toContain('New Category (new-category)');
expect(guide.stdout).not.toContain('Providers (');
expect(requests[0]).toMatchObject({
url: '/exchange/discover',
headers: { authorization: 'Bearer fc-test' },
});
expect(requests[0].body).toBeUndefined();
const json = await cli(['list-tools', '--json']);
expect(json.code).toBe(0);
expect(JSON.parse(json.stdout).data).toMatchObject({
level: 'categories',
total: 2,
items: [
{
id: 'finance',
nextCommand: 'firecrawl alexandria list finance --category',
},
{ id: 'new-category' },
],
});
status = 403;
response = {
success: false,
error: 'Access required',
code: 'ACCESS_REQUIRED',
requiresAction: { type: 'request_access' },
};
const denied = await cli(['list', '--json']);
expect(denied.code).toBe(1);
expect(JSON.parse(denied.stdout)).toMatchObject({
...response,
discoveryRequests: [{ requestId: expect.any(String) }],
});
expect(
requests.every((request) => request.url === '/exchange/discover')
).toBe(true);
});

it('browses live provider IDs directly or through a category without expanding contracts', async () => {
responseFor = (body) => {
const options = body.alexandria[0].options;
if (options.providers?.[0] === 'finance')
return catalogue(options.level, []);
if (
['retail', 'Retail', 'Public records'].includes(options.categories?.[0])
)
return catalogue(options.level, []);
return catalogue(options.level, [
{ id: 'benzinga', provider: 'benzinga', name: 'Benzinga' },
]);
};
for (const args of [[], ['finance'], ['benzinga'], ['finance', 'benzinga']]) {
for (const args of [
['--providers'],
['finance'],
['benzinga'],
['finance', 'benzinga'],
]) {
expect((await cli(['list', ...args])).code).toBe(0);
}
expect((await cli(['alexandria', 'list', 'benzinga', '--groups'])).code).toBe(
0
);
expect((await cli(['list-tools', 'benzinga'])).code).toBe(0);
expect((await cli(['alexandria', 'list-tools', 'benzinga'])).code).toBe(0);
expect((await cli(['list', 'Retail', '--category'])).code).toBe(0);
expect((await cli(['list', 'Public records', '--category'])).code).toBe(0);
expect(requests.map((request) => request.body.alexandria[0].options)).toEqual(
[
{ level: 'providers', limit: 20 },
Expand All @@ -125,6 +195,10 @@ it('browses live provider IDs directly or through a category without expanding c
{ providers: ['benzinga'], level: 'groups', limit: 20 },
{ providers: ['benzinga'], level: 'tools', limit: 20 },
{ providers: ['benzinga'], level: 'tools', limit: 20 },
{ categories: ['Retail'], level: 'providers', limit: 20 },
{ categories: ['shopping'], level: 'providers', limit: 20 },
{ categories: ['Public records'], level: 'providers', limit: 20 },
{ categories: ['government'], level: 'providers', limit: 20 },
]
);
expect(
Expand All @@ -135,6 +209,16 @@ it('browses live provider IDs directly or through a category without expanding c
body.alexandria[0].capability === 'find-tools'
)
).toBe(true);
responseFor = () =>
catalogue('providers', [
{ id: 'future-retailer', provider: 'future-retailer' },
]);
const canonical = await cli(['list', 'retail', '--category']);
expect(canonical.code).toBe(0);
expect(canonical.stdout).toContain('future-retailer');
expect(requests.at(-1)?.body.alexandria[0].options.categories).toEqual([
'retail',
]);
});

it('expands only a selected tool and falls back to a compact group listing', async () => {
Expand Down Expand Up @@ -254,7 +338,7 @@ it('refuses execution through list and propagates discovery access errors', asyn
creditsCost: 0,
},
};
const result = await cli(['list', '--json']);
const result = await cli(['list', '--providers', '--json']);
expect(result.code).toBe(1);
expect(JSON.parse(result.stdout).data.alexandria[0].error).toEqual(
response.data.alexandria[0].error
Expand Down
Loading
Loading