diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a9855a..362bb94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,18 @@ This ensures clients can always see what tools are available in a given version ### Added - `search` (hand-written) — resolve a company name, ticker fragment, or a person's name to a ticker or CIK in one call, returning separately-ranked `companies` and `insiders` lists. Free plan. Wraps `GET /v1/search` (backend insiderapi #314, live since 2026-09-25). Framed in `SERVER_INSTRUCTIONS` and its own description as the obvious first call when an agent has a name/fragment but no identifier yet. Kept hand-written (added to `HANDLED_BY_HANDWRITTEN` in `codegen/generate.mjs`) rather than left to codegen so the description could explicitly steer agents to call it before `get_company_overview`/`get_insider_profile`/etc., matching the pattern used for `research_company`. +## [1.15.1] — 2026-10-02 + +Tool count unchanged. No tools added, renamed, or removed. + +### Changed +- The `upgrade_required` payload no longer contains prices. `unlocks` is now a neutral description of what the plan includes (for example "The Business plan includes cluster-buy signals and sentiment scores, ..."), with no dollar amounts. +- `upgrade_url` is now optional: it is the backend's `upgradeUrl` when the error body has one, and is omitted when the backend sends none or null (previously it fell back to a hard-coded billing URL). +- `research_company` and `verify_setup` no longer embed a hard-coded billing link in their plan-required messages. + +### Removed +- `pricing_url` from the `upgrade_required` payload and its type. + ## [1.15.0] — 2026-09-24 **Tool count: 37** (+1: `list_schedule13_dg`). Free tools unchanged at 23; gated 13 → 14. diff --git a/README.md b/README.md index e96dd62..38a2c9f 100644 --- a/README.md +++ b/README.md @@ -311,9 +311,7 @@ A gated call never surfaces a bare HTTP error. The MCP returns a structured "required_plan": "business", "current_plan": "Free", "message": "This endpoint requires the Business plan or higher. Your current plan is Free.", - "unlocks": "Business ($149/mo) adds cluster-buy signals and sentiment scores, 13F institutional holdings and managers, Form 144 notices, bulk CSV export, and 250,000 requests/day.", - "upgrade_url": "https://www.form4api.com/dashboard/billing", - "pricing_url": "https://www.form4api.com/pricing" + "unlocks": "The Business plan includes cluster-buy signals and sentiment scores, 13F institutional holdings and managers, Form 144 notices, bulk CSV export, and 250,000 requests/day." } ``` @@ -322,8 +320,7 @@ parameter that stopped the call, which is usually what the model needs to sugges alternative. The same shape is returned when a Pro-only *parameter* is used on an otherwise free tool, so the model can simply retry without that filter. -Upgrade at [form4api.com/dashboard/billing](https://www.form4api.com/dashboard/billing), or -compare tiers at [form4api.com/pricing](https://www.form4api.com/pricing). +`upgrade_url` is included only when the API supplies one; it is omitted otherwise. --- diff --git a/package-lock.json b/package-lock.json index fe46a60..d56c9b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "form4api-mcp", - "version": "1.12.1", + "version": "1.15.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "form4api-mcp", - "version": "1.12.1", + "version": "1.15.1", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.12.0", diff --git a/package.json b/package.json index 4736bc1..332846e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "form4api-mcp", - "version": "1.15.0", + "version": "1.15.1", "mcpName": "io.github.theodor90/form4api-mcp", "description": "MCP server for Form4API — SEC Form 4 insider trading, Form 144 intent-to-sell, institutional 13F-HR, and congressional STOCK Act trading data, including the insider/Congress convergence signal. Lets Claude / Cursor / Windsurf / Zed query real-time US insider and congressional trading data via natural language.", "keywords": [ diff --git a/server.json b/server.json index d6f26bc..7956422 100644 --- a/server.json +++ b/server.json @@ -3,7 +3,7 @@ "name": "io.github.theodor90/form4api-mcp", "title": "Form4API — SEC Insider Trading", "description": "Real-time SEC Form 4, Form 144, 13F-HR & congressional STOCK Act insider data. 38 tools + 6 prompts.", - "version": "1.15.0", + "version": "1.15.1", "websiteUrl": "https://www.form4api.com", "repository": { "url": "https://github.com/theodor90/form4api-mcp", @@ -13,7 +13,7 @@ { "registryType": "npm", "identifier": "form4api-mcp", - "version": "1.15.0", + "version": "1.15.1", "transport": { "type": "stdio" }, "environmentVariables": [ { diff --git a/src/client.ts b/src/client.ts index a53f691..b025719 100644 --- a/src/client.ts +++ b/src/client.ts @@ -15,47 +15,40 @@ const { version: PKG_VERSION } = JSON.parse( export type RequiredPlan = 'starter' | 'pro' | 'business' | 'enterprise' | 'higher' // Structured payload the MCP returns when an upstream call needs a plan -// upgrade. The LLM router can read the JSON shape and surface the upgrade -// path to the user cleanly (e.g. "you need Business for sentiment scores, -// upgrade at form4api.com/dashboard/billing") instead of swallowing an -// opaque 402 text. PLAN_MCP_DEFENSE Decision 1 (2026-06-01). +// upgrade. The LLM router can read the JSON shape and explain to the user +// cleanly which plan a call needs, instead of swallowing an opaque 402 text. PLAN_MCP_DEFENSE Decision 1 (2026-06-01). // // `message` is the backend's own text, verbatim. That matters: the API // explains each gate specifically (which page depth was exceeded, which // filter param is Pro-only, that /v1/transactions/export exists for bulk // pulls), and that detail is far more useful to a calling agent than any -// sentence this client could synthesise. `unlocks` adds what the target plan -// buys, so the agent can relay a complete upgrade pitch in one turn instead -// of making the user go read the pricing page to find out. +// sentence this client could synthesise. `unlocks` states what the target plan +// includes. `upgrade_url` is present only when the backend supplies one. export interface UpgradeRequiredPayload { error: 'upgrade_required' required_plan: RequiredPlan current_plan?: string message: string unlocks?: string - upgrade_url: string - pricing_url: string + upgrade_url?: string } -// What each paid tier adds, phrased as the answer to "why would I pay for -// this". Kept in sync with form4api-web/app/lib/plans.ts, which is the -// single source of truth for plan contents. +// A neutral, factual description of what each plan includes. No prices: the +// site does not publish them. Kept in sync with form4api-web/app/lib/plans.ts, +// which is the single source of truth for plan contents. const PLAN_UNLOCKS: Record, string> = { starter: - 'Starter ($19/mo) adds a commercial-use license, 7,500 requests/day, and 100 pages of query depth.', + 'The Starter plan includes a commercial-use license, 7,500 requests/day, and 100 pages of query depth.', pro: - 'Pro ($49/mo) adds insider scorecards and career summaries, congressional trading data and convergence signals, ' + + 'The Pro plan includes insider scorecards and career summaries, congressional trading data and convergence signals, ' + 'return and trade-size filters, unlimited query depth, and 50,000 requests/day.', business: - 'Business ($149/mo) adds cluster-buy signals and sentiment scores, 13F institutional holdings and managers, ' + + 'The Business plan includes cluster-buy signals and sentiment scores, 13F institutional holdings and managers, ' + 'Form 144 notices, bulk CSV export, and 250,000 requests/day.', enterprise: - 'Enterprise ($499/mo) adds unlimited requests, unlimited webhooks, and Slack support with an SLA.', + 'The Enterprise plan includes unlimited requests, unlimited webhooks, and Slack support with an SLA.', } -const PRICING_URL = 'https://www.form4api.com/pricing' -const BILLING_URL = 'https://www.form4api.com/dashboard/billing' - export class Form4ApiError extends Error { constructor( message: string, @@ -148,8 +141,8 @@ export class Form4ApiClient { ...(currentPlan ? { current_plan: currentPlan } : {}), message, ...(requiredPlan !== 'higher' ? { unlocks: PLAN_UNLOCKS[requiredPlan] } : {}), - upgrade_url: upgradeUrl ?? BILLING_URL, - pricing_url: PRICING_URL, + // Only the backend's own link; omitted when it sends none/null. + ...(upgradeUrl ? { upgrade_url: upgradeUrl } : {}), } // The structured payload is JSON-encoded into the error message so // the MCP server's text-channel response carries the full shape. diff --git a/src/tools/research.ts b/src/tools/research.ts index d1da40d..fa6b8ee 100644 --- a/src/tools/research.ts +++ b/src/tools/research.ts @@ -79,13 +79,13 @@ export async function researchCompany( const signals = signalsResult.status === 'fulfilled' ? signalsResult.value : null const signalsUnavailable = signalsResult.status === 'rejected' - ? 'Cluster signals require Business plan. Upgrade at https://www.form4api.com/dashboard/billing' + ? 'Cluster signals require the Business plan.' : null const sentiment = sentimentResult.status === 'fulfilled' ? sentimentResult.value : null const sentimentUnavailable = sentimentResult.status === 'rejected' - ? 'Sentiment scores require Business plan. Upgrade at https://www.form4api.com/dashboard/billing' + ? 'Sentiment scores require the Business plan.' : null // Compute summary from real fetched data only diff --git a/src/tools/verify-setup.ts b/src/tools/verify-setup.ts index c9365f8..40aaa81 100644 --- a/src/tools/verify-setup.ts +++ b/src/tools/verify-setup.ts @@ -104,7 +104,7 @@ export async function verifySetup(_input: VerifySetupInput): Promise