@render-harness/cap-search-exa 0.1.1
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/README.md +18 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +65 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
- package/skills/web-search-exa.md +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
## @render-harness/cap-search-exa
|
|
2
|
+
|
|
3
|
+
Wires Exa web search into a Render harness entry. Drop into `render-harness.yaml`:
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
capabilities:
|
|
7
|
+
- pack: "@render-harness/cap-search-exa"
|
|
8
|
+
config:
|
|
9
|
+
defaultMaxResults: 10
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Set `EXA_API_KEY` in the entry's environment.
|
|
13
|
+
|
|
14
|
+
The pack contributes:
|
|
15
|
+
|
|
16
|
+
- An MCP server `cap-search-exa.exa` (HTTP transport) talking to Exa's hosted MCP.
|
|
17
|
+
- A skill (`web-search-exa`) describing when to use Exa.
|
|
18
|
+
- An `EXA_API_KEY` entry in the effective env schema.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as _render_harness_registry from '@render-harness/registry';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* cap-search-exa — wires up Exa's hosted MCP server (https://exa.ai)
|
|
5
|
+
* for any harness entry that wants high-recall web search.
|
|
6
|
+
*
|
|
7
|
+
* Usage in render-harness.yaml:
|
|
8
|
+
*
|
|
9
|
+
* capabilities:
|
|
10
|
+
* - pack: "@render-harness/cap-search-exa"
|
|
11
|
+
* version: "^0.1"
|
|
12
|
+
* config:
|
|
13
|
+
* defaultMaxResults: 10
|
|
14
|
+
*
|
|
15
|
+
* Surfaces:
|
|
16
|
+
* - One MCP server (HTTP transport) named "exa" — namespaced by the
|
|
17
|
+
* loader to "cap-search-exa.exa".
|
|
18
|
+
* - One skill (skills/web-search-exa.md) explaining when to reach
|
|
19
|
+
* for Exa vs other tools.
|
|
20
|
+
* - envSchema entry for EXA_API_KEY.
|
|
21
|
+
*
|
|
22
|
+
* Config keys:
|
|
23
|
+
* - `apiKeyEnv` (string, default "EXA_API_KEY") — env var name to
|
|
24
|
+
* read for the bearer token. Override if the entry already has
|
|
25
|
+
* another env var holding the key.
|
|
26
|
+
* - `baseUrl` (string, default "https://mcp.exa.ai/mcp") — override
|
|
27
|
+
* for self-hosted or staging Exa MCP endpoints.
|
|
28
|
+
* - `defaultMaxResults` (number) — passed through to Exa's tool args
|
|
29
|
+
* by way of an "Exa-Default-Max-Results" header. Optional.
|
|
30
|
+
*/
|
|
31
|
+
declare const pack: _render_harness_registry.CapabilityPack;
|
|
32
|
+
|
|
33
|
+
export { pack as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { dirname, join } from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { definePack } from '@render-harness/registry';
|
|
4
|
+
|
|
5
|
+
// src/index.ts
|
|
6
|
+
var HERE = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
var SKILLS_DIR = join(HERE, "..", "skills");
|
|
8
|
+
function readConfig(ctx) {
|
|
9
|
+
const cfg = ctx.config;
|
|
10
|
+
return {
|
|
11
|
+
apiKeyEnv: cfg.apiKeyEnv ?? "EXA_API_KEY",
|
|
12
|
+
baseUrl: cfg.baseUrl ?? "https://mcp.exa.ai/mcp",
|
|
13
|
+
...cfg.defaultMaxResults !== void 0 ? { defaultMaxResults: cfg.defaultMaxResults } : {}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
var pack = definePack({
|
|
17
|
+
name: "cap-search-exa",
|
|
18
|
+
version: "0.1.0",
|
|
19
|
+
envSchema: [
|
|
20
|
+
{
|
|
21
|
+
name: "EXA_API_KEY",
|
|
22
|
+
required: true,
|
|
23
|
+
secret: true,
|
|
24
|
+
description: "API key for Exa search (https://exa.ai). Used as a bearer token to the Exa MCP."
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
mcpServers(ctx) {
|
|
28
|
+
const cfg = readConfig(ctx);
|
|
29
|
+
const apiKey = ctx.env(cfg.apiKeyEnv);
|
|
30
|
+
if (!apiKey) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`cap-search-exa: env var ${cfg.apiKeyEnv} is not set. Set it before building or starting the agent.`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const headers = {
|
|
36
|
+
Authorization: `Bearer ${apiKey}`
|
|
37
|
+
};
|
|
38
|
+
if (cfg.defaultMaxResults !== void 0) {
|
|
39
|
+
headers["Exa-Default-Max-Results"] = String(cfg.defaultMaxResults);
|
|
40
|
+
}
|
|
41
|
+
return [
|
|
42
|
+
{
|
|
43
|
+
name: "exa",
|
|
44
|
+
transport: "http",
|
|
45
|
+
url: cfg.baseUrl,
|
|
46
|
+
headers
|
|
47
|
+
}
|
|
48
|
+
];
|
|
49
|
+
},
|
|
50
|
+
skills(_ctx) {
|
|
51
|
+
return [
|
|
52
|
+
{
|
|
53
|
+
name: "web-search-exa",
|
|
54
|
+
description: "Use Exa for high-recall, neural web search.",
|
|
55
|
+
whenToUse: "When you need fresh web context, comparison shopping, or to find specific facts not in your training data. Prefer this over fetch_url unless you already have a specific URL.",
|
|
56
|
+
contentPath: join(SKILLS_DIR, "web-search-exa.md")
|
|
57
|
+
}
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
var src_default = pack;
|
|
62
|
+
|
|
63
|
+
export { src_default as default };
|
|
64
|
+
//# sourceMappingURL=index.js.map
|
|
65
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAkCA,IAAM,IAAA,GAAO,OAAA,CAAQ,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AAEnD,IAAM,UAAA,GAAa,IAAA,CAAK,IAAA,EAAM,IAAA,EAAM,QAAQ,CAAA;AAQ5C,SAAS,WAAW,GAAA,EAAkF;AACpG,EAAA,MAAM,MAAM,GAAA,CAAI,MAAA;AAChB,EAAA,OAAO;AAAA,IACL,SAAA,EAAW,IAAI,SAAA,IAAa,aAAA;AAAA,IAC5B,OAAA,EAAS,IAAI,OAAA,IAAW,wBAAA;AAAA,IACxB,GAAI,IAAI,iBAAA,KAAsB,MAAA,GAAY,EAAE,iBAAA,EAAmB,GAAA,CAAI,iBAAA,EAAkB,GAAI;AAAC,GAC5F;AACF;AAEA,IAAM,OAAO,UAAA,CAAW;AAAA,EACtB,IAAA,EAAM,gBAAA;AAAA,EACN,OAAA,EAAS,OAAA;AAAA,EACT,SAAA,EAAW;AAAA,IACT;AAAA,MACE,IAAA,EAAM,aAAA;AAAA,MACN,QAAA,EAAU,IAAA;AAAA,MACV,MAAA,EAAQ,IAAA;AAAA,MACR,WAAA,EAAa;AAAA;AACf,GACF;AAAA,EACA,WAAW,GAAA,EAAqC;AAC9C,IAAA,MAAM,GAAA,GAAM,WAAW,GAAG,CAAA;AAC1B,IAAA,MAAM,MAAA,GAAS,GAAA,CAAI,GAAA,CAAI,GAAA,CAAI,SAAS,CAAA;AACpC,IAAA,IAAI,CAAC,MAAA,EAAQ;AAGX,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,wBAAA,EAA2B,IAAI,SAAS,CAAA,0DAAA;AAAA,OAC1C;AAAA,IACF;AACA,IAAA,MAAM,OAAA,GAAkC;AAAA,MACtC,aAAA,EAAe,UAAU,MAAM,CAAA;AAAA,KACjC;AACA,IAAA,IAAI,GAAA,CAAI,sBAAsB,MAAA,EAAW;AACvC,MAAA,OAAA,CAAQ,yBAAyB,CAAA,GAAI,MAAA,CAAO,GAAA,CAAI,iBAAiB,CAAA;AAAA,IACnE;AACA,IAAA,OAAO;AAAA,MACL;AAAA,QACE,IAAA,EAAM,KAAA;AAAA,QACN,SAAA,EAAW,MAAA;AAAA,QACX,KAAK,GAAA,CAAI,OAAA;AAAA,QACT;AAAA;AACF,KACF;AAAA,EACF,CAAA;AAAA,EACA,OAAO,IAAA,EAAoC;AACzC,IAAA,OAAO;AAAA,MACL;AAAA,QACE,IAAA,EAAM,gBAAA;AAAA,QACN,WAAA,EAAa,6CAAA;AAAA,QACb,SAAA,EACE,+KAAA;AAAA,QACF,WAAA,EAAa,IAAA,CAAK,UAAA,EAAY,mBAAmB;AAAA;AACnD,KACF;AAAA,EACF;AACF,CAAC,CAAA;AAED,IAAO,WAAA,GAAQ","file":"index.js","sourcesContent":["/**\n * cap-search-exa — wires up Exa's hosted MCP server (https://exa.ai)\n * for any harness entry that wants high-recall web search.\n *\n * Usage in render-harness.yaml:\n *\n * capabilities:\n * - pack: \"@render-harness/cap-search-exa\"\n * version: \"^0.1\"\n * config:\n * defaultMaxResults: 10\n *\n * Surfaces:\n * - One MCP server (HTTP transport) named \"exa\" — namespaced by the\n * loader to \"cap-search-exa.exa\".\n * - One skill (skills/web-search-exa.md) explaining when to reach\n * for Exa vs other tools.\n * - envSchema entry for EXA_API_KEY.\n *\n * Config keys:\n * - `apiKeyEnv` (string, default \"EXA_API_KEY\") — env var name to\n * read for the bearer token. Override if the entry already has\n * another env var holding the key.\n * - `baseUrl` (string, default \"https://mcp.exa.ai/mcp\") — override\n * for self-hosted or staging Exa MCP endpoints.\n * - `defaultMaxResults` (number) — passed through to Exa's tool args\n * by way of an \"Exa-Default-Max-Results\" header. Optional.\n */\n\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { definePack, type PackContext } from \"@render-harness/registry\";\nimport type { McpServerConfig, SkillMetadata } from \"@render-harness/core\";\n\nconst HERE = dirname(fileURLToPath(import.meta.url));\n// dist/ is one level under the package root, so skills/ is one above.\nconst SKILLS_DIR = join(HERE, \"..\", \"skills\");\n\ninterface ExaConfig {\n apiKeyEnv?: string;\n baseUrl?: string;\n defaultMaxResults?: number;\n}\n\nfunction readConfig(ctx: PackContext): Required<Pick<ExaConfig, \"apiKeyEnv\" | \"baseUrl\">> & ExaConfig {\n const cfg = ctx.config as ExaConfig;\n return {\n apiKeyEnv: cfg.apiKeyEnv ?? \"EXA_API_KEY\",\n baseUrl: cfg.baseUrl ?? \"https://mcp.exa.ai/mcp\",\n ...(cfg.defaultMaxResults !== undefined ? { defaultMaxResults: cfg.defaultMaxResults } : {}),\n };\n}\n\nconst pack = definePack({\n name: \"cap-search-exa\",\n version: \"0.1.0\",\n envSchema: [\n {\n name: \"EXA_API_KEY\",\n required: true,\n secret: true,\n description: \"API key for Exa search (https://exa.ai). Used as a bearer token to the Exa MCP.\",\n },\n ],\n mcpServers(ctx: PackContext): McpServerConfig[] {\n const cfg = readConfig(ctx);\n const apiKey = ctx.env(cfg.apiKeyEnv);\n if (!apiKey) {\n // Fail loud at build time / boot time so users notice immediately\n // rather than getting a broken agent in production.\n throw new Error(\n `cap-search-exa: env var ${cfg.apiKeyEnv} is not set. Set it before building or starting the agent.`,\n );\n }\n const headers: Record<string, string> = {\n Authorization: `Bearer ${apiKey}`,\n };\n if (cfg.defaultMaxResults !== undefined) {\n headers[\"Exa-Default-Max-Results\"] = String(cfg.defaultMaxResults);\n }\n return [\n {\n name: \"exa\",\n transport: \"http\",\n url: cfg.baseUrl,\n headers,\n },\n ];\n },\n skills(_ctx: PackContext): SkillMetadata[] {\n return [\n {\n name: \"web-search-exa\",\n description: \"Use Exa for high-recall, neural web search.\",\n whenToUse:\n \"When you need fresh web context, comparison shopping, or to find specific facts not in your training data. Prefer this over fetch_url unless you already have a specific URL.\",\n contentPath: join(SKILLS_DIR, \"web-search-exa.md\"),\n },\n ];\n },\n});\n\nexport default pack;\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@render-harness/cap-search-exa",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Exa web-search capability pack for the Render agent harness.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"skills"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"render-harness-cap",
|
|
21
|
+
"render-harness",
|
|
22
|
+
"exa",
|
|
23
|
+
"search",
|
|
24
|
+
"mcp"
|
|
25
|
+
],
|
|
26
|
+
"renderHarness": {
|
|
27
|
+
"gallery": {
|
|
28
|
+
"label": "Exa web search",
|
|
29
|
+
"envHint": "EXA_API_KEY"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsup",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"test": "vitest run --passWithNoTests"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@render-harness/registry": "workspace:*"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@render-harness/core": "workspace:*",
|
|
42
|
+
"@types/node": "^25.6.2",
|
|
43
|
+
"tsup": "^8.5.1",
|
|
44
|
+
"typescript": "^6.0.3",
|
|
45
|
+
"vitest": "^4.1.5"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web-search-exa
|
|
3
|
+
description: Use Exa for high-recall, neural web search.
|
|
4
|
+
when_to_use: When you need fresh web context, comparison shopping, or to find specific facts not in your training data. Prefer this over fetch_url unless you already have a specific URL.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Web search with Exa
|
|
8
|
+
|
|
9
|
+
Exa is a neural search engine optimized for high-recall retrieval over the open web. The `cap-search-exa.exa` MCP exposes Exa's tool set; the most useful tools are:
|
|
10
|
+
|
|
11
|
+
- `web_search_exa` — returns ranked results for a free-form query, with title, URL, and a relevance-scored snippet.
|
|
12
|
+
- `crawl_url` — fetches the body of a single page.
|
|
13
|
+
|
|
14
|
+
## When to use Exa
|
|
15
|
+
|
|
16
|
+
- The user asks about something time-sensitive (current pricing, recent releases, announcements).
|
|
17
|
+
- You need a citation or a quote from a specific source.
|
|
18
|
+
- You're trying to find a project, library, or company by description.
|
|
19
|
+
|
|
20
|
+
## When not to use Exa
|
|
21
|
+
|
|
22
|
+
- The user is asking about their own Render account — use the Render MCP instead.
|
|
23
|
+
- You already have the exact URL — use `crawl_url` directly.
|
|
24
|
+
- The question is conceptual and doesn't need a fresh source.
|
|
25
|
+
|
|
26
|
+
## Tips
|
|
27
|
+
|
|
28
|
+
- Keep queries short and specific; Exa is neural, so concept-style queries work well.
|
|
29
|
+
- If you want results from a specific time window, set the `startPublishedDate` argument.
|
|
30
|
+
- Always cite the URL you used in your final answer.
|