prospectapis-mcp 0.2.3 → 0.2.5
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/CHANGELOG.md +26 -0
- package/README.md +15 -5
- package/package.json +1 -1
- package/src/playbooks.js +113 -0
- package/src/server.js +52 -2
- package/src/tools.js +44 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,32 @@ All notable changes to `prospectapis-mcp` are documented here. The format follow
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.2.5] - 2026-10-20
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Four playbooks, served as MCP resources (resources/list, resources/read) and as one prompt each (prompts/list, prompts/get), so a client can offer them as slash commands: newly funded prospects, pre-call brief, funding alerts to a webhook, and an investor's recent rounds. A prompt hands its playbook over as an embedded resource, so the recipe matches what resources/read serves, and a client that omits the optional `arguments` still gets it. Tests hold every name a playbook uses to the tool catalog.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- The README's first sentence now names the product URL (https://prospectapis.com) and who it is for, because directory cards and search snippets copy that sentence alone and were linking GitHub only. A test pins both.
|
|
18
|
+
- Added CODE_OF_CONDUCT.md (Contributor Covenant 2.1), which open-source programs ask for.
|
|
19
|
+
|
|
20
|
+
## [0.2.4] - unreleased
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Every tool has a human title ("Search Funding Rounds", "Delete Watchlist"), sent as both the tool's `title` and `annotations.title`; before, the title was the raw tool name.
|
|
25
|
+
- `watchlist_delete` now says `destructiveHint: true` (it removes the watchlist and its delivery history), so a client asks before running it; it said `false`, like every other tool. The other writes stay non-destructive: they only add a research job, a watchlist, a test event or a report.
|
|
26
|
+
- `idempotentHint` is set where it is true: the free reads and `watchlist_delete`. `funding_search` and `funding_get` stay `false`, because every call that returns records is billed again. `openWorldHint` stays `true` on every tool.
|
|
27
|
+
- One factory, `annotationsFor()` in `src/tools.js`, builds these annotations for the npm server and the hosted MCP door alike. `test/annotations.test.js` checks every served tool (a title that is not the raw name and has no underscore, every `*_delete` tool destructive and not read-only) and plants each regression to prove the check fails on it.
|
|
28
|
+
|
|
29
|
+
- README opening rewritten for the listings that copy it word for word (Glama, mcpservers.org, the MCP Registry, npm): one paragraph on what the server does and costs, then the install lines and the website, plus a three-question FAQ. No tool or behaviour change; 14 tools.
|
|
30
|
+
- `package-lock.json` carries the package version again (it still said 0.2.2).
|
|
31
|
+
- `test/readme-opening.test.js` checks the opening against the package name, its Website line against `server.json` `websiteUrl`, every tool count the README states against `src/tools.js`, the stated Node floor against `engines`, and one version across `package.json`, `server.json` and `package-lock.json`.
|
|
32
|
+
- The README no longer says every funding record carries its announcement URL: some records come back without one, so the opening and the FAQ now say records carry the public announcement URL when there is one, and `test/readme-opening.test.js` fails on any sentence that promises a URL on every record.
|
|
33
|
+
- `test/package-scripts.test.js` checks that every file an npm script runs exists in the checkout.
|
|
34
|
+
|
|
9
35
|
## [0.2.3] - 2026-10-11
|
|
10
36
|
|
|
11
37
|
### Added
|
package/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# prospectapis-mcp
|
|
2
2
|
|
|
3
|
-
MCP server for the ProspectAPIs funding
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
`
|
|
3
|
+
prospectapis-mcp is the MCP server for ProspectAPIs (https://prospectapis.com), for sales and prospecting teams and the AI agents they run in Claude Code, Claude Desktop, Cursor and other MCP clients. It gives your agent fourteen tools over the ProspectAPIs API: search funding rounds by date, round, amount, company, investor and country, get new matching rounds pushed to your webhook, and order a cited research brief on any company or person. Funding records carry the public announcement URL when there is one. Records cost $0.02 each after 100 free a month, a completed brief costs $0.15, and there are no seats, no subscription and no card to sign up. It needs Node.js 22 or later.
|
|
4
|
+
|
|
5
|
+
Install (Claude Code): `claude mcp add prospectapis -e PROSPECTAPIS_API_KEY=pa_live_... -- npx -y prospectapis-mcp`
|
|
6
|
+
|
|
7
|
+
Other clients: run `npx -y prospectapis-mcp` with `PROSPECTAPIS_API_KEY` from https://prospectapis.com
|
|
8
|
+
|
|
9
|
+
Website: https://prospectapis.com/mcp
|
|
8
10
|
|
|
9
11
|
| Tool | Endpoint | Cost |
|
|
10
12
|
|---|---|---|
|
|
@@ -50,6 +52,14 @@ Client config (for example `claude_desktop_config.json`):
|
|
|
50
52
|
|
|
51
53
|
Claude Code: `claude mcp add prospectapis -e PROSPECTAPIS_API_KEY=pa_live_... -- npx -y prospectapis-mcp`.
|
|
52
54
|
|
|
55
|
+
## FAQ
|
|
56
|
+
|
|
57
|
+
**Where does the funding data come from?** Records carry the public announcement URL when there is one.
|
|
58
|
+
|
|
59
|
+
**What is free?** 100 funding records a month, any search that matches nothing, watchlist management, and the balance, pricing and version tools.
|
|
60
|
+
|
|
61
|
+
**Which clients work?** Claude Code, Claude Desktop, Cursor and any client that reads an mcpServers config, through `npx -y prospectapis-mcp`.
|
|
62
|
+
|
|
53
63
|
## Develop
|
|
54
64
|
|
|
55
65
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prospectapis-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"mcpName": "io.github.ProspectAPIs/prospectapis-mcp",
|
|
5
5
|
"description": "MCP server for ProspectAPIs: search funding rounds, run paid prospect research briefs, manage funding watchlists, read your balance and the live price list.",
|
|
6
6
|
"type": "module",
|
package/src/playbooks.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Playbooks: short recipes an agent runs with the tools in src/tools.js, served over the MCP RESOURCE primitive
|
|
2
|
+
// (resources/list, resources/read) and offered as one PROMPT each (prompts/list, prompts/get), so a client can show
|
|
3
|
+
// them as slash commands.
|
|
4
|
+
//
|
|
5
|
+
// WHY RESOURCES AND PROMPTS, NOT TOOLS. Every tool in src/tools.js is one REST endpoint (test/api-parity.test.js
|
|
6
|
+
// holds the catalog to the API). A recipe wraps no endpoint; it is read-only context an agent fetches when it needs
|
|
7
|
+
// it, which is what resources are for, and a prompt is how a client offers that recipe to a user.
|
|
8
|
+
//
|
|
9
|
+
// A DESCRIPTION IS LISTED, A BODY IS FETCHED. Clients show the title and description beside every tool, so those
|
|
10
|
+
// state what the recipe does in plain product terms and never instruct the model. The body is returned only for a
|
|
11
|
+
// URI or prompt the caller asked for by name, so it names tools freely.
|
|
12
|
+
//
|
|
13
|
+
// Every figure in a body (prices, page size, alert limit, poll interval) is the one the tool descriptions in
|
|
14
|
+
// src/tools.js state, checked by review, not by a test. test/playbooks.test.js fails if a body names a tool,
|
|
15
|
+
// parameter, value or response field the catalog does not have.
|
|
16
|
+
|
|
17
|
+
export const PLAYBOOK_MIME = "text/markdown";
|
|
18
|
+
|
|
19
|
+
const NEWLY_FUNDED = `# Newly funded prospects
|
|
20
|
+
|
|
21
|
+
Build a short list of companies that just raised, worth reaching out to while the round is fresh.
|
|
22
|
+
|
|
23
|
+
1. \`account_balance\` first. Each funding record returned costs $0.02 after 100 free a month, so know what is left
|
|
24
|
+
before you pull pages.
|
|
25
|
+
2. \`funding_search\` with a tight window and the filters that describe the buyer: \`since\` (for example the last 30
|
|
26
|
+
days), \`round\` (such as \`seed,series_a\`), \`country\`, and \`min_amount_usd\` or \`max_amount_usd\`. Start with
|
|
27
|
+
\`limit\` 10 to 25. A search that matches nothing costs nothing, so narrow before you widen.
|
|
28
|
+
3. Read the results. Each record has the company, its domain, the round, the amount, the investors and, when there is
|
|
29
|
+
one, the URL of the public announcement. Drop companies without a domain or outside the buyer profile.
|
|
30
|
+
4. Page with \`cursor\` only if the first page is all good fits; pass \`next_cursor\` back exactly as returned. A null
|
|
31
|
+
\`next_cursor\` means there are no more matches.
|
|
32
|
+
5. For the two or three best fits, run the pre-call brief playbook (\`research_submit\` with \`domain\`).
|
|
33
|
+
6. Present the list as company, domain, round, amount, date, lead investors and the announcement URL where there is
|
|
34
|
+
one, so every line can be checked.
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
const PRE_CALL_BRIEF = `# Pre-call brief
|
|
38
|
+
|
|
39
|
+
A cited brief on one company or person before a call or a first email.
|
|
40
|
+
|
|
41
|
+
1. \`research_submit\` with exactly ONE identifier: \`domain\` is best; otherwise \`website_url\`, \`company_name\`, a work
|
|
42
|
+
\`email\`, or \`person_name\` together with \`company_name\`. Set \`purpose\` to \`pre_call\` for objections and discovery
|
|
43
|
+
questions, or \`outreach\` for sourced hooks with draft first lines. Add \`context\` (your own product, up to 1,000
|
|
44
|
+
characters) to get a \`fit\` section.
|
|
45
|
+
2. It returns \`research_id\` at once. The brief takes 20 to 90 seconds: call \`research_get\` with that id every 10 to 15
|
|
46
|
+
seconds until \`status\` is \`completed\` or \`failed\`. Polling is free.
|
|
47
|
+
3. A brief is charged only when it completes, at the \`research_price_usd\` that \`account_balance\` reports, and refunded
|
|
48
|
+
in full if it fails. Free records do not apply to research.
|
|
49
|
+
4. Every field in \`result\` is \`{value, sources, confidence}\`. Quote the sources with the claims you use, and say when
|
|
50
|
+
a field came back empty rather than filling the gap.
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const FUNDING_ALERTS = `# Funding alerts to a webhook
|
|
54
|
+
|
|
55
|
+
Get every new funding round that matches a profile pushed to your own endpoint, instead of searching again.
|
|
56
|
+
|
|
57
|
+
1. Decide the filters with \`funding_search\` first (\`round\`, \`country\`, \`min_amount_usd\`, \`max_amount_usd\`,
|
|
58
|
+
\`investor\`, \`company\` or \`domain\`), and check the results look right. Date windows and paging do not apply to an
|
|
59
|
+
alert.
|
|
60
|
+
2. \`watchlist_create\` with a \`name\`, those \`filters\` and an https \`webhook_url\` on a public host. Save the \`secret\`
|
|
61
|
+
it returns: it is shown once, and every delivery is signed with it in the Prospect-Signature header
|
|
62
|
+
(t=..,v1=HMAC-SHA256). Creating is free; each delivered event costs one record, charged only when your webhook
|
|
63
|
+
answers 2xx. An account can have 20 active alerts.
|
|
64
|
+
3. \`watchlist_test\` sends a signed sample event now, so the receiver can be built and checked before a real round
|
|
65
|
+
arrives. Free, 5 a minute.
|
|
66
|
+
4. Later, \`watchlist_deliveries\` shows the last 50 deliveries and their outcome (delivered, retry, failed,
|
|
67
|
+
no_credit), and \`watchlist_get\` shows whether the alert is active and why it was paused.
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
const INVESTOR_ROUNDS = `# An investor's recent rounds
|
|
71
|
+
|
|
72
|
+
See which companies an investor has backed lately, to find warm introductions or a portfolio to sell into.
|
|
73
|
+
|
|
74
|
+
1. \`funding_search\` with \`investor\` set to the investor's exact name (case-insensitive) and a \`since\` date, newest
|
|
75
|
+
first. Add \`round\` to keep to one stage.
|
|
76
|
+
2. Each record lists every investor named and the lead investors, so you can tell a lead from a participation.
|
|
77
|
+
3. Group the results by round and by country, and give the announcement URL with each line that has one.
|
|
78
|
+
4. To follow this investor from now on, run the funding alerts playbook with \`investor\` as the filter.
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
export const PLAYBOOKS = Object.freeze([
|
|
82
|
+
{
|
|
83
|
+
name: "newly-funded-prospects",
|
|
84
|
+
uri: "prospectapis://playbooks/newly-funded-prospects",
|
|
85
|
+
title: "Newly funded prospects",
|
|
86
|
+
description: "Build a short list of companies that raised recently, filtered by round, country and amount, with the announcement for each.",
|
|
87
|
+
text: NEWLY_FUNDED,
|
|
88
|
+
prompt: { arg: "profile", describe: "Who you sell to, for example 'seed and Series A fintech in the US'.", ask: (v) => `Run this playbook for: ${v}` },
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "pre-call-brief",
|
|
92
|
+
uri: "prospectapis://playbooks/pre-call-brief",
|
|
93
|
+
title: "Pre-call brief",
|
|
94
|
+
description: "A cited research brief on one company or person before a call or a first email.",
|
|
95
|
+
text: PRE_CALL_BRIEF,
|
|
96
|
+
prompt: { arg: "prospect", describe: "The company domain, company name, or person and company.", ask: (v) => `Run this playbook for: ${v}` },
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "funding-alerts",
|
|
100
|
+
uri: "prospectapis://playbooks/funding-alerts",
|
|
101
|
+
title: "Funding alerts to a webhook",
|
|
102
|
+
description: "Push every new funding round that matches a profile to your own https endpoint, signed, instead of searching again.",
|
|
103
|
+
text: FUNDING_ALERTS,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: "investor-rounds",
|
|
107
|
+
uri: "prospectapis://playbooks/investor-rounds",
|
|
108
|
+
title: "An investor's recent rounds",
|
|
109
|
+
description: "The companies one investor has backed lately, split into lead and participation, with sources.",
|
|
110
|
+
text: INVESTOR_ROUNDS,
|
|
111
|
+
prompt: { arg: "investor", describe: "The investor's name, for example 'Example Ventures'.", ask: (v) => `Run this playbook for: ${v}` },
|
|
112
|
+
},
|
|
113
|
+
]);
|
package/src/server.js
CHANGED
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
-
import { TOOLS, buildUrl, buildPath, buildBody } from "./tools.js";
|
|
8
|
+
import { TOOLS, buildUrl, buildPath, buildBody, annotationsFor } from "./tools.js";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { GetPromptRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
11
|
+
import { PLAYBOOKS, PLAYBOOK_MIME } from "./playbooks.js";
|
|
9
12
|
|
|
10
13
|
export const { version: VERSION } = createRequire(import.meta.url)("../package.json");
|
|
11
14
|
|
|
@@ -91,13 +94,60 @@ export function createServer({
|
|
|
91
94
|
}
|
|
92
95
|
|
|
93
96
|
const server = new McpServer({ name: "prospectapis", version: VERSION }, { instructions: INSTRUCTIONS });
|
|
97
|
+
|
|
98
|
+
// Playbooks (src/playbooks.js): each one a resource (resources/list, resources/read) and a prompt (prompts/list,
|
|
99
|
+
// prompts/get). The prompt hands the playbook over as an embedded resource, so the recipe a user picks is exactly
|
|
100
|
+
// the one resources/read serves. Registering either declares that capability, which a server with none does not
|
|
101
|
+
// advertise.
|
|
102
|
+
for (const p of PLAYBOOKS) {
|
|
103
|
+
server.registerResource(
|
|
104
|
+
p.name,
|
|
105
|
+
p.uri,
|
|
106
|
+
{ title: p.title, description: p.description, mimeType: PLAYBOOK_MIME },
|
|
107
|
+
async (uri) => ({ contents: [{ uri: uri.href, mimeType: PLAYBOOK_MIME, text: p.text }] }),
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
// `arguments` is optional on prompts/get (MCP spec), but the SDK validates a prompt that declares arguments against
|
|
111
|
+
// a required object, so a client that leaves it out would get -32602 for an argument it was told is optional. The
|
|
112
|
+
// SDK installs its prompts/get handler through the low-level Server the first time a prompt is registered; wrapping
|
|
113
|
+
// setRequestHandler before that defaults a missing `arguments` to {} and leaves every other request untouched.
|
|
114
|
+
{
|
|
115
|
+
const low = server.server;
|
|
116
|
+
const setRequestHandler = low.setRequestHandler.bind(low);
|
|
117
|
+
low.setRequestHandler = (schema, handler) =>
|
|
118
|
+
setRequestHandler(
|
|
119
|
+
schema,
|
|
120
|
+
schema === GetPromptRequestSchema
|
|
121
|
+
? (request, extra) => handler({ ...request, params: { ...request.params, arguments: request.params?.arguments ?? {} } }, extra)
|
|
122
|
+
: handler,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
for (const p of PLAYBOOKS) {
|
|
126
|
+
const argsSchema = p.prompt ? { [p.prompt.arg]: z.string().max(200).optional().describe(p.prompt.describe) } : undefined;
|
|
127
|
+
server.registerPrompt(
|
|
128
|
+
p.name,
|
|
129
|
+
{ title: p.title, description: p.description, ...(argsSchema ? { argsSchema } : {}) },
|
|
130
|
+
async (args = {}) => {
|
|
131
|
+
const v = p.prompt ? String(args[p.prompt.arg] ?? "").trim() : "";
|
|
132
|
+
return {
|
|
133
|
+
description: p.description,
|
|
134
|
+
messages: [
|
|
135
|
+
{ role: "user", content: { type: "resource", resource: { uri: p.uri, mimeType: PLAYBOOK_MIME, text: p.text } } },
|
|
136
|
+
{ role: "user", content: { type: "text", text: p.prompt && v ? p.prompt.ask(v) : "Run this playbook." } },
|
|
137
|
+
],
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
94
143
|
for (const tool of TOOLS) {
|
|
95
144
|
server.registerTool(
|
|
96
145
|
tool.name,
|
|
97
146
|
{
|
|
147
|
+
title: tool.title,
|
|
98
148
|
description: tool.description,
|
|
99
149
|
inputSchema: tool.shape,
|
|
100
|
-
annotations:
|
|
150
|
+
annotations: annotationsFor(tool),
|
|
101
151
|
},
|
|
102
152
|
(args) => callEndpoint(tool, args),
|
|
103
153
|
);
|
package/src/tools.js
CHANGED
|
@@ -10,7 +10,13 @@
|
|
|
10
10
|
// - `write: true` marks a tool that changes state or spends credits
|
|
11
11
|
// (research_submit starts a paid job); the server turns `write` into MCP's
|
|
12
12
|
// readOnlyHint, so every other tool is read-only;
|
|
13
|
-
// - `auth: false` marks the tools the API serves without a key (pricing_get, api_version)
|
|
13
|
+
// - `auth: false` marks the tools the API serves without a key (pricing_get, api_version);
|
|
14
|
+
// - `title` is the human name a client shows instead of the raw tool name;
|
|
15
|
+
// - `idempotent: true` marks a tool whose repeat call with the same arguments changes nothing more: the free
|
|
16
|
+
// reads, and watchlist_delete (a second delete finds nothing to delete). funding_search and funding_get are
|
|
17
|
+
// read-only but NOT idempotent, because every call that returns records is billed again.
|
|
18
|
+
// annotationsFor() below turns these fields into MCP tool annotations; the npm server and the hosted door
|
|
19
|
+
// (apps/api/src/lib/mcp-door.js, through createServer) both register every tool with it, so they cannot drift.
|
|
14
20
|
|
|
15
21
|
import { z } from "zod";
|
|
16
22
|
|
|
@@ -38,6 +44,7 @@ export const TOOLS = [
|
|
|
38
44
|
name: "funding_search",
|
|
39
45
|
method: "GET",
|
|
40
46
|
path: "/v1/funding",
|
|
47
|
+
title: "Search Funding Rounds",
|
|
41
48
|
write: false,
|
|
42
49
|
format: { min_amount_usd: formatAmount, max_amount_usd: formatAmount },
|
|
43
50
|
description:
|
|
@@ -55,7 +62,7 @@ export const TOOLS = [
|
|
|
55
62
|
company: z.string().min(1).max(100).optional().describe("Company name contains this text (case-insensitive)."),
|
|
56
63
|
domain: z.string().min(3).max(253).optional().describe("Exact company domain, for example `example.com`."),
|
|
57
64
|
investor: z.string().min(1).max(100).optional().describe("Exact investor name, case-insensitive."),
|
|
58
|
-
country: z.string().min(1).max(60).optional().describe("Headquarters country,
|
|
65
|
+
country: z.string().min(1).max(60).optional().describe("Headquarters country as a name or ISO code, such as US, United States or DE. Other text, such as a city, matches the stated location."),
|
|
59
66
|
limit: z.number().int().min(1).max(100).optional().describe("Records per page, 1 to 100 (default 25). Each record returned is billed."),
|
|
60
67
|
cursor: z.string().max(200).optional().describe("Opaque cursor from the previous page's `next_cursor`."),
|
|
61
68
|
},
|
|
@@ -64,6 +71,7 @@ export const TOOLS = [
|
|
|
64
71
|
name: "funding_get",
|
|
65
72
|
method: "GET",
|
|
66
73
|
path: "/v1/funding/{id}",
|
|
74
|
+
title: "Get Funding Round",
|
|
67
75
|
write: false,
|
|
68
76
|
description: "Read one funding record by id. Costs $0.02 when found (free records are used first); a missing id is free.",
|
|
69
77
|
shape: {
|
|
@@ -74,6 +82,7 @@ export const TOOLS = [
|
|
|
74
82
|
name: "research_submit",
|
|
75
83
|
method: "POST",
|
|
76
84
|
path: "/v1/research",
|
|
85
|
+
title: "Start Prospect Research",
|
|
77
86
|
write: true,
|
|
78
87
|
body: true,
|
|
79
88
|
description:
|
|
@@ -103,6 +112,8 @@ export const TOOLS = [
|
|
|
103
112
|
name: "research_get",
|
|
104
113
|
method: "GET",
|
|
105
114
|
path: "/v1/research/{id}",
|
|
115
|
+
title: "Get Research Brief",
|
|
116
|
+
idempotent: true,
|
|
106
117
|
write: false,
|
|
107
118
|
description:
|
|
108
119
|
"Read a research job by the `research_id` research_submit returned: `status` (queued, running, completed, failed), `input`, " +
|
|
@@ -118,6 +129,7 @@ export const TOOLS = [
|
|
|
118
129
|
name: "watchlist_create",
|
|
119
130
|
method: "POST",
|
|
120
131
|
path: "/v1/watchlists",
|
|
132
|
+
title: "Create Funding Watchlist",
|
|
121
133
|
body: true,
|
|
122
134
|
write: true,
|
|
123
135
|
description:
|
|
@@ -145,6 +157,8 @@ export const TOOLS = [
|
|
|
145
157
|
name: "watchlist_list",
|
|
146
158
|
method: "GET",
|
|
147
159
|
path: "/v1/watchlists",
|
|
160
|
+
title: "List Watchlists",
|
|
161
|
+
idempotent: true,
|
|
148
162
|
write: false,
|
|
149
163
|
description: "Your funding-signal alerts (without their secrets). Free.",
|
|
150
164
|
shape: {},
|
|
@@ -153,6 +167,8 @@ export const TOOLS = [
|
|
|
153
167
|
name: "watchlist_get",
|
|
154
168
|
method: "GET",
|
|
155
169
|
path: "/v1/watchlists/{id}",
|
|
170
|
+
title: "Get Watchlist",
|
|
171
|
+
idempotent: true,
|
|
156
172
|
write: false,
|
|
157
173
|
description: "One funding-signal alert: its name, filters, webhook URL, whether it is active and why it was paused (never its secret). Free.",
|
|
158
174
|
shape: { id: z.string().uuid().describe("The watchlist id.") },
|
|
@@ -161,6 +177,8 @@ export const TOOLS = [
|
|
|
161
177
|
name: "watchlist_deliveries",
|
|
162
178
|
method: "GET",
|
|
163
179
|
path: "/v1/watchlists/{id}/deliveries",
|
|
180
|
+
title: "List Watchlist Deliveries",
|
|
181
|
+
idempotent: true,
|
|
164
182
|
write: false,
|
|
165
183
|
description: "The last 50 deliveries of one alert and their outcome (delivered, retry, failed, no_credit). Free.",
|
|
166
184
|
shape: { id: z.string().uuid().describe("The watchlist id.") },
|
|
@@ -169,6 +187,7 @@ export const TOOLS = [
|
|
|
169
187
|
name: "watchlist_test",
|
|
170
188
|
method: "POST",
|
|
171
189
|
path: "/v1/watchlists/{id}/test",
|
|
190
|
+
title: "Send Watchlist Test Event",
|
|
172
191
|
body: true,
|
|
173
192
|
write: true,
|
|
174
193
|
description: "Send a signed sample event to the alert's webhook now, to build and verify the receiver. Free; 5 a minute.",
|
|
@@ -178,6 +197,8 @@ export const TOOLS = [
|
|
|
178
197
|
name: "watchlist_delete",
|
|
179
198
|
method: "DELETE",
|
|
180
199
|
path: "/v1/watchlists/{id}",
|
|
200
|
+
title: "Delete Watchlist",
|
|
201
|
+
idempotent: true,
|
|
181
202
|
write: true,
|
|
182
203
|
description: "Delete a funding-signal alert and its delivery history. Free.",
|
|
183
204
|
shape: { id: z.string().uuid().describe("The watchlist id.") },
|
|
@@ -186,6 +207,8 @@ export const TOOLS = [
|
|
|
186
207
|
name: "account_balance",
|
|
187
208
|
method: "GET",
|
|
188
209
|
path: "/v1/account",
|
|
210
|
+
title: "Get Account Balance",
|
|
211
|
+
idempotent: true,
|
|
189
212
|
write: false,
|
|
190
213
|
description: "Your credit balance and the free records left this month. Free. Check it before a large paged search.",
|
|
191
214
|
shape: {},
|
|
@@ -194,6 +217,7 @@ export const TOOLS = [
|
|
|
194
217
|
name: "feedback_send",
|
|
195
218
|
method: "POST",
|
|
196
219
|
path: "/v1/feedback",
|
|
220
|
+
title: "Send Feedback",
|
|
197
221
|
body: true,
|
|
198
222
|
write: true,
|
|
199
223
|
description:
|
|
@@ -212,6 +236,8 @@ export const TOOLS = [
|
|
|
212
236
|
name: "pricing_get",
|
|
213
237
|
method: "GET",
|
|
214
238
|
path: "/pricing",
|
|
239
|
+
title: "Get Pricing",
|
|
240
|
+
idempotent: true,
|
|
215
241
|
write: false,
|
|
216
242
|
auth: false,
|
|
217
243
|
description:
|
|
@@ -223,6 +249,8 @@ export const TOOLS = [
|
|
|
223
249
|
name: "api_version",
|
|
224
250
|
method: "GET",
|
|
225
251
|
path: "/v1/version",
|
|
252
|
+
title: "Get API Version",
|
|
253
|
+
idempotent: true,
|
|
226
254
|
write: false,
|
|
227
255
|
auth: false,
|
|
228
256
|
description: "The API's version and deployed build. Free, needs no key.",
|
|
@@ -270,3 +298,17 @@ export function buildBody(tool, args) {
|
|
|
270
298
|
}
|
|
271
299
|
return JSON.stringify(out);
|
|
272
300
|
}
|
|
301
|
+
|
|
302
|
+
// The one place a catalog entry becomes MCP tool annotations. A tool is destructive when it is a write whose
|
|
303
|
+
// method is DELETE (watchlist_delete removes the watchlist and its delivery history); every other write only adds
|
|
304
|
+
// (a research job, a watchlist, a test event, a report), so it is a write that is not destructive.
|
|
305
|
+
export function annotationsFor(tool) {
|
|
306
|
+
const write = !!tool.write;
|
|
307
|
+
return {
|
|
308
|
+
title: tool.title,
|
|
309
|
+
readOnlyHint: !write,
|
|
310
|
+
destructiveHint: write && tool.method === "DELETE",
|
|
311
|
+
idempotentHint: !!tool.idempotent,
|
|
312
|
+
openWorldHint: true,
|
|
313
|
+
};
|
|
314
|
+
}
|