# Extend MCP MCP Server

> **Extend MCP MCP Server** is a hosted, multitenant Model Context Protocol (MCP) server run by **MewCP** (https://mewcp.com), giving AI agents managed access to Extend MCP.
>
> MewCP takes care of all MCP infrastructure for you — credential storage, OAuth flows,
> token refresh, and production-grade auto-scaling — so your AI agents can connect to
> third-party services and run freely without you managing any MCP server yourself.
>
> Server page:  https://mewcp.com/mcp/extend-mcp
> MewCP docs:   https://docs.mewcp.com
> Full catalog: https://mewcp.com/llms.txt

---

## About

Extend transforms PDFs, images, Office files, and spreadsheets into structured data by extracting fields, tables, and line items; classifying document types; splitting document bundles; and filling PDF forms. Build reusable processors and workflows, evaluate extraction accuracy, and run document-processing jobs at scale.

---

## Details

- Server ID: `extend-mcp`
- Version: 0.10.0
- Tools: 86
- Authentication: OAuth, managed by MewCP
- Transport: http

---

## Access

The gateway URL below serves your **entire MewCP toolset**, not this server on its
own. Pasting a config snippet is not enough — Extend MCP has to be in the toolset
first. In order:

1. Add Extend MCP to your toolset at https://mewcp.com/mcp/extend-mcp
2. Connect your Extend MCP account (OAuth); MewCP stores the credential and attaches it to each call
3. Copy your MewCP API key from the dashboard (Developer)
4. Configure your client with the snippet for it below

Once connected, an agent does **not** see this server's tools as top-level tools.
It sees four meta-tools and reaches everything through them:

- `search(query)` — find tools by keyword across the toolset
- `get_schema(tools)` — full description and arguments for the tools you picked
- `list_accounts(provider)` — only when one app has several connected accounts
- `call_tool(server_maskedId, tool_name, args)` — execute

So the list below is what `search` can return for this server, not a set of
callable tool names on their own.

---

## Tools (86)

Descriptions are truncated to 160 characters; call `get_schema`
for the full text and the argument schema. Where a tool is annotated, its type
is shown — treat `destructive` as irreversible.

- `extract_data` _(write)_ — Extract specific structured values (totals, line items, dates, names, tables) from a document (extract group) using a saved extractor, an inline JSON Schema,…
- `create_extractor` _(write)_ — Create a saved, reusable extractor (extract group). Three starting points, mutually exclusive: config (inline schema — call get_documentation with…
- `update_extractor` _(destructive)_ — Update an extractor's mutable draft (extract group): rename it and/or replace the draft config (hand-editing the schema? call get_documentation with…
- `get_extractor` _(read)_ — Get an extractor with its draft config, or one specific version's config via version (extract group). Use list_extractors to discover IDs.
- `list_extractors` _(read)_ — List the workspace's extractors, newest first (extract group). Fetch a specific one's config with get_extractor. hasMore: true means more items exist — prefer…
- `list_extractor_versions` _(read)_ — List an extractor's published (immutable) versions (extract group). Fetch one version's config with get_extractor. hasMore: true means more items exist —…
- `publish_extractor_version` _(write)_ — Publish the extractor's current draft config as a new immutable semver version ("1.2"-style) that runs and evaluations can pin (extract group). releaseType…
- `get_extract_run` _(read)_ — Get the state and output of an extract run (extract group) — resume a status: "running" run or inspect a failed one. wait: true blocks until terminal or…
- `list_extract_runs` _(read)_ — List recent extract runs, newest first (extract group). Filter rather than paginate: status, extractorId, batchId, fileNameContains. hasMore: true means more…
- `cancel_extract_run` _(destructive)_ — Cancel a queued or in-flight extract run (extract group). Best-effort: a run already finishing may still complete.
- `delete_extract_run` _(destructive)_ — Permanently delete an extract run and its stored outputs (extract group). Cannot be undone.
- `run_extract_batch` _(write)_ — Submit up to 1,000 documents as one batch of extract runs (extract group) against a saved processor. Returns a batchId immediately; runs execute async — poll…
- `get_extract_batch` _(read)_ — Get the aggregate status of an extract batch (extract group) submitted by run_extract_batch. wait: true polls until terminal. Statuses: PENDING, PROCESSING,…
- `classify_document` _(write)_ — Categorize a document into one of a set of types, e.g. MSA vs SOW vs NDA (classify group), using a saved classifier or an inline list of classifications.…
- `create_classifier` _(write)_ — Create a saved, reusable classifier (classify group). Start from config (inline classifications list — call get_documentation with…
- `update_classifier` _(destructive)_ — Update a classifier's mutable draft (classify group): rename it and/or replace the draft config (hand-editing it? call get_documentation with…
- `get_classifier` _(read)_ — Get a classifier with its draft config, or one specific version's config via version (classify group). Use list_classifiers to discover IDs.
- `list_classifiers` _(read)_ — List the workspace's classifiers, newest first (classify group). Fetch a specific one's config with get_classifier. hasMore: true means more items exist —…
- `list_classifier_versions` _(read)_ — List a classifier's published (immutable) versions (classify group). Fetch one version's config with get_classifier. hasMore: true means more items exist —…
- `publish_classifier_version` _(write)_ — Publish the classifier's current draft config as a new immutable semver version ("1.2"-style) that runs and evaluations can pin (classify group). releaseType…
- `get_classify_run` _(read)_ — Get the state and output of a classify run (classify group) — resume a status: "running" run or inspect a failed one. wait: true blocks until terminal or…
- `list_classify_runs` _(read)_ — List recent classify runs, newest first (classify group). Filter rather than paginate: status, classifierId, batchId, fileNameContains. hasMore: true means…
- `cancel_classify_run` _(destructive)_ — Cancel a queued or in-flight classify run (classify group). Best-effort: a run already finishing may still complete.
- `delete_classify_run` _(destructive)_ — Permanently delete a classify run and its stored outputs (classify group). Cannot be undone.
- `run_classify_batch` _(write)_ — Submit up to 1,000 documents as one batch of classify runs (classify group) against a saved processor. Returns a batchId immediately; runs execute async — poll…
- `get_classify_batch` _(read)_ — Get the aggregate status of a classify batch (classify group) submitted by run_classify_batch. wait: true polls until terminal. Statuses: PENDING, PROCESSING,…
- `split_document` _(write)_ — Divide a combined file, e.g. a scanned bundle of invoices or a merged PDF of statements, into typed segments with page ranges (split group), using a saved…
- `create_splitter` _(write)_ — Create a saved, reusable splitter (split group). Start from config (inline split classifications — call get_documentation with…
- `update_splitter` _(destructive)_ — Update a splitter's mutable draft (split group): rename it and/or replace the draft config (hand-editing it? call get_documentation with…
- `get_splitter` _(read)_ — Get a splitter with its draft config, or one specific version's config via version (split group). Use list_splitters to discover IDs.
- `list_splitters` _(read)_ — List the workspace's splitters, newest first (split group). Fetch a specific one's config with get_splitter. hasMore: true means more items exist — prefer…
- `list_splitter_versions` _(read)_ — List a splitter's published (immutable) versions (split group). Fetch one version's config with get_splitter. hasMore: true means more items exist — prefer…
- `publish_splitter_version` _(write)_ — Publish the splitter's current draft config as a new immutable semver version ("1.2"-style) that runs and evaluations can pin (split group). releaseType picks…
- `get_split_run` _(read)_ — Get the state and output of a split run (split group) — resume a status: "running" run or inspect a failed one. wait: true blocks until terminal or wait-budget…
- `list_split_runs` _(read)_ — List recent split runs, newest first (split group). Filter rather than paginate: status, splitterId, batchId, fileNameContains. hasMore: true means more items…
- `cancel_split_run` _(destructive)_ — Cancel a queued or in-flight split run (split group). Best-effort: a run already finishing may still complete.
- `delete_split_run` _(destructive)_ — Permanently delete a split run and its stored outputs (split group). Cannot be undone.
- `run_split_batch` _(write)_ — Submit up to 1,000 documents as one batch of split runs (split group) against a saved processor. Returns a batchId immediately; runs execute async — poll…
- `get_split_batch` _(read)_ — Get the aggregate status of a split batch (split group) submitted by run_split_batch. wait: true polls until terminal. Statuses: PENDING, PROCESSING,…
- `parse_document` _(write)_ — Convert a document (PDF, image, Office doc, spreadsheet) into clean structured text — markdown by default (parse group). Right when the user wants the content…
- `get_parse_run` _(read)_ — Get the state and output of a parse run (parse group) — resume a status: "running" run or inspect a failed one. wait: true blocks until terminal or wait-budget…
- `list_parse_runs` _(read)_ — List recent parse runs, newest first (parse group). Filter rather than paginate: status, batchId, fileNameContains. Results come back in the upstream default…
- `delete_parse_run` _(destructive)_ — Permanently delete a parse run and its stored outputs (parse group). Cannot be undone.
- `run_parse_batch` _(write)_ — Submit up to 1,000 documents as one batch of parse runs (parse group); no saved processor needed — an optional inline config applies to every run. Returns a…
- `get_parse_batch` _(read)_ — Get the aggregate status of a parse batch (parse group) submitted by run_parse_batch. wait: true polls until terminal. Statuses: PENDING, PROCESSING,…
- `run_workflow` _(write)_ — Start a run of a deployed multi-step workflow on a document (workflows group; not runnable until a version is deployed, or pass version "draft"). Workflow runs…
- `create_workflow` _(write)_ — Create a workflow — a multi-step document pipeline (workflows group): parse → extract/classify/split → validations → human review. name alone creates an empty…
- `update_workflow` _(destructive)_ — Update a workflow's mutable draft (workflows group): rename it and/or replace the entire draft step graph (hand-editing steps? call get_documentation with…
- `get_workflow` _(read)_ — Get a workflow with its draft step graph, or one specific deployed version's steps via version (workflows group). Use list_workflows to discover IDs.
- `list_workflows` _(read)_ — List the workspace's workflows, newest first (workflows group). Fetch a specific one's step graph with get_workflow. hasMore: true means more items exist —…
- `list_workflow_versions` _(read)_ — List a workflow's deployed (immutable) versions — integer deploy numbers with optional names (workflows group). Fetch one version's steps with get_workflow.…
- `deploy_workflow_version` _(write)_ — Deploy the workflow's current draft as a new immutable version (workflows group). Unlike extractor/classifier/splitter publishing there is NO releaseType —…
- `get_workflow_run` _(read)_ — Get the state and output of a workflow run (workflows group) — resume a status: "running" run or inspect a failed one. wait: true blocks until terminal or…
- `list_workflow_runs` _(read)_ — List recent workflow runs, newest first (workflows group). Filter rather than paginate: status, workflowId, batchId, fileNameContains. Workflow batches have no…
- `cancel_workflow_run` _(destructive)_ — Cancel a queued or in-flight workflow run (workflows group). Best-effort: a run already finishing may still complete.
- `delete_workflow_run` _(destructive)_ — Permanently delete a workflow run and its stored outputs (workflows group). Cannot be undone.
- `run_workflow_batch` _(write)_ — Submit up to 1,000 documents as one batch of workflow runs (workflows group). Returns a batchId immediately; workflow batches have NO batch-get endpoint —…
- `edit_pdf` _(write)_ — Fill form fields in a PDF and return the edited file (edit group). Values come from instructions (free-form prose, e.g. "name is Acme Corp; date is…
- `detect_form_fields` _(write)_ — Detect the fillable fields in a PDF form and return an edit schema (edit group): each property is a detected field with its type…
- `get_edit_run` _(read)_ — Get the state and output of an edit run (edit group) — resume a status: "running" run or inspect a failed one. wait: true blocks until terminal or wait-budget…
- `get_form_detection_run` _(read)_ — Get the state and output of a form detection run (edit group) — resume a status: "running" run or inspect a failed one. wait: true blocks until terminal or…
- `delete_edit_run` _(destructive)_ — Permanently delete an edit run and its stored outputs (edit group). Cannot be undone.
- `upload_file` _(write)_ — Upload a file from the user's computer (files group). Returns a dashboard upload link; uploaded file IDs only become available after the user completes the…
- `get_file_upload` _(read)_ — Get the status and uploaded file ids of an upload link created by upload_file (files group). Default is one instant status check; wait: true blocks until the…
- `get_file` _(read)_ — Get a file's metadata and a fresh presigned download URL — expires in ~15 minutes, re-call to refresh (files group). Set contents to also return parsed text;…
- `list_files` _(read)_ — List files in the workspace, newest first (files group). Filter with nameContains rather than paginating broadly. Use get_file for a download URL or parsed…
- `delete_file` _(destructive)_ — Permanently delete an uploaded file from Extend (files group). Runs that already processed the file keep their outputs, but the file's bytes and parsed…
- `create_webhook_endpoint` _(write)_ — Register an HTTPS URL to receive Extend events (webhooks group). CRITICAL: the response's signingSecret is shown ONLY on this call — store it immediately; it…
- `list_webhook_endpoints` _(read)_ — List the workspace's webhook endpoints with their global event lists (webhooks group). Signing secrets are never returned here — they are shown once at…
- `update_webhook_endpoint` _(destructive)_ — Update a webhook endpoint's URL, name, status (enable/disable deliveries), global event list, or advanced options (webhooks group). The signing secret never…
- `delete_webhook_endpoint` _(destructive)_ — Permanently delete a webhook endpoint (webhooks group). This ALSO deletes every subscription attached to it, and deliveries stop immediately. This cannot be…
- `create_webhook_subscription` _(write)_ — Subscribe an existing webhook endpoint to events from ONE specific extractor, classifier, splitter, or workflow (webhooks group) — deliveries for other…
- `list_webhook_subscriptions` _(read)_ — List resource-scoped webhook subscriptions, filterable by endpoint or by the watched resource (webhooks group). hasMore: true means more items exist — prefer…
- `update_webhook_subscription` _(destructive)_ — Replace a webhook subscription's event list (webhooks group). The endpoint and watched resource cannot change — delete and recreate the subscription for that.
- `delete_webhook_subscription` _(destructive)_ — Permanently delete a webhook subscription (webhooks group). The endpoint and its global events are unaffected. This cannot be undone.
- `create_evaluation_set` _(write)_ — Create an evaluation set — a named collection of ground-truth examples scoped to ONE extractor, classifier, or splitter via entityId (evaluations group). The…
- `list_evaluation_sets` _(read)_ — List evaluation sets, optionally scoped to one extractor/classifier/splitter via entityId (evaluations group). There is no get-by-id tool — this list returns…
- `add_evaluation_items` _(write)_ — Bulk-add ground-truth examples (1-100 per call) to an evaluation set (evaluations group). Each item pairs an already-uploaded file with the output the resource…
- `list_evaluation_items` _(read)_ — List an evaluation set's items — file pairings only; expected outputs are not included in summaries (evaluations group). Item IDs feed update_evaluation_item /…
- `update_evaluation_item` _(destructive)_ — Replace one evaluation item's expected output (evaluations group). The file pairing cannot change — delete and re-add for that.
- `delete_evaluation_item` _(destructive)_ — Permanently remove one ground-truth item from an evaluation set (evaluations group). Past run metrics are unaffected. This cannot be undone.
- `run_evaluation` _(write)_ — Run an evaluation set against a version of its resource and score the results against the ground truth (evaluations group). Async: returns immediately with a…
- `get_evaluation_run` _(read)_ — Get an evaluation run's status and accuracy metrics — this is the ONLY tool that returns them, not a get-batch tool (evaluations group). An evaluation is an…
- `get_me` _(read)_ — Return the connected organization, the credential in use, and the granted workspace + environment ("TEST" or "PRODUCTION") targets — exactly the values the…
- `get_documentation` _(read)_ — Fetch an Extend docs page as markdown, live from docs.extend.ai. Pass a URL from a tool description, an error's docUrl, or a search_documentation result.…
- `search_documentation` _(read)_ — Search the Extend documentation. Returns matching pages with URLs — results are pointers, not content: fetch the most relevant with get_documentation. An empty…

---

## Connect

Gateway URL: https://gateway.mewcp.com/personal/mcp

Every request carries one header:

    Authorization: Bearer <API_KEY>

Replace `API_KEY` with your own key from the dashboard (Developer).

## Apps

### Claude Desktop

Mac & Windows app

- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "mewcp": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote@latest",
        "https://gateway.mewcp.com/personal/mcp",
        "--header",
        "Authorization:${AUTH_HEADER}"
      ],
      "env": {
        "AUTH_HEADER": "Bearer API_KEY"
      }
    }
  }
}
```

Already have an "mcpServers" section in your config? Just add the server entry inside it.

1. Open Claude Desktop → Settings → Developer → "Edit Config"
2. Paste the snippet inside the outer { } of the config file (merge with your existing "mcpServers" section if you have one)
3. Save the file, then quit Claude Desktop completely and reopen it
4. Start a new conversation — your tool will be available

### VS Code

Copilot / Cline

- Command Palette → "MCP: Open User Configuration" (opens mcp.json). For one project only, use .vscode/mcp.json instead.

```json
{
  "mcp.servers": {
    "mewcp": {
      "type": "http",
      "url": "https://gateway.mewcp.com/personal/mcp",
      "headers": {
        "Authorization": "Bearer API_KEY"
      }
    }
  }
}
```

Already have a "servers" section in your mcp.json? Just add the server entry inside it.

1. Open VS Code → Command Palette (Cmd+Shift+P / Ctrl+Shift+P)
2. Run "MCP: Open User Configuration" to open your mcp.json
3. Paste the snippet and save
4. Start the server when prompted (or from the MCP servers view) and use it in Copilot Chat

### Cursor

AI-first editor

- macOS: `~/.cursor/mcp.json`
- Windows: `%USERPROFILE%\.cursor\mcp.json`

```json
{
  "mcpServers": {
    "mewcp": {
      "url": "https://gateway.mewcp.com/personal/mcp",
      "headers": {
        "Authorization": "Bearer API_KEY"
      }
    }
  }
}
```

Already have an "mcpServers" section in your mcp.json? Just add the server entry inside it.

1. Open Cursor → Settings → Cursor Settings → MCP
2. Click "Add new global MCP server"
3. Paste the snippet and save
4. Restart Cursor

### Codex

OpenAI's CLI agent

- Add the snippet to your Codex MCP config or your standard MCP config file for the CLI tool you use.

```json
{
  "mcpServers": {
    "mewcp": {
      "type": "http",
      "url": "https://gateway.mewcp.com/personal/mcp",
      "headers": {
        "Authorization": "Bearer API_KEY"
      }
    }
  }
}
```

Codex generally reads a standard MCP server block, so you can add this alongside your other configured servers.

1. Open your Codex MCP config or project-level config file
2. Paste the MewCP server block inside the config JSON/TOML structure your tool expects
3. Save the file and restart Codex
4. Verify the tool is available inside a fresh session

### Claude Code

Anthropic's CLI agent

- ~/.claude.json (user scope) or .mcp.json in your project root — create it if it doesn't exist. Or skip the file and use the CLI command below.

```json
{
  "mcpServers": {
    "mewcp": {
      "type": "http",
      "url": "https://gateway.mewcp.com/personal/mcp",
      "headers": {
        "Authorization": "Bearer API_KEY"
      }
    }
  }
}
```

Already have an "mcpServers" section in your config? Just add the server entry inside it.

1. Open ~/.claude.json (or .mcp.json in your project root) in a text editor
2. Paste the snippet inside the outer { } (merge with your existing "mcpServers" section if you have one)
3. Save the file and start (or restart) Claude Code
4. Or skip the file entirely and run the CLI command below instead

### OpenCode

Open-source terminal agent

- ~/.config/opencode/opencode.json (global) or opencode.json in your project root — create it if it doesn't exist.

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "mewcp": {
      "type": "remote",
      "url": "https://gateway.mewcp.com/personal/mcp",
      "enabled": true,
      "headers": {
        "Authorization": "Bearer API_KEY"
      }
    }
  }
}
```

Already have an "mcp" section in your opencode.json? Just add the server entry inside it.

1. Open ~/.config/opencode/opencode.json (or opencode.json in your project root) in a text editor
2. Paste the snippet inside the outer { } (merge with your existing "mcp" section if you have one)
3. Save the file and start (or restart) OpenCode

### OpenClaw

Self-hosted agent gateway

- ~/.openclaw/openclaw.json — create it if it doesn't exist.

```json
{
  "mcp": {
    "servers": {
      "mewcp": {
        "transport": "streamable-http",
        "url": "https://gateway.mewcp.com/personal/mcp",
        "enabled": true,
        "headers": {
          "Authorization": "Bearer API_KEY"
        }
      }
    }
  }
}
```

Already have an "mcp" section in your openclaw.json? Just add the server entry inside "servers".

1. Open ~/.openclaw/openclaw.json in a text editor
2. Paste the snippet inside the outer { } (merge with your existing "mcp" section if you have one)
3. Save the file and restart OpenClaw

### Antigravity

Google's agentic IDE

- ~/.gemini/config/mcp_config.json (global) or .agents/mcp_config.json (workspace-local) — create it if it doesn't exist.

```json
{
  "mcpServers": {
    "mewcp": {
      "serverUrl": "https://gateway.mewcp.com/personal/mcp",
      "headers": {
        "Authorization": "Bearer API_KEY"
      }
    }
  }
}
```

Already have an "mcpServers" section in your config? Just add the server entry inside it. Remote servers must use the "serverUrl" field — the legacy "url"/"httpUrl" fields aren't supported.

1. In the editor's agent side panel, click "…" → "MCP Servers" → "Manage MCP Servers" → "View raw config" (Antigravity CLI: type /mcp instead to open the Interactive MCP Manager)
2. Paste the snippet inside the outer { } (merge with your existing "mcpServers" section if you have one)
3. Save the file — the server connects automatically

### Hermes

Nous Research's CLI agent

- config.yaml in your Hermes config directory (~/.hermes) — add this under a top-level "mcp_servers:" key.

```yaml
mcp_servers:
  mewcp:
    url: "https://gateway.mewcp.com/personal/mcp"
    headers:
      Authorization: "Bearer API_KEY"
```

Already have an "mcp_servers" section in your config.yaml? Just add the server entry inside it.

1. Open config.yaml in your Hermes config directory
2. Paste the snippet under the top-level "mcp_servers:" key (merge with existing entries if you have any)
3. Save the file, then run /reload-mcp in Hermes (or start a fresh session)
4. Ask Hermes "Tell me which MCP-backed tools are available right now" to confirm it connected

### DeepSeek Harness

DeepSeek's agent harness

- cordis.yml in your DSH project — or the patch file you mount plugins from.

```yaml
- id: mcp-mewcp
  name: '@deepseek-ai/dsh-mcp-client'
  config:
    serverName: mewcp
    transport: streamable-http
    url: https://gateway.mewcp.com/personal/mcp
    headers:
      Authorization: "Bearer API_KEY"
```

One plugin instance = one MCP server. Add this entry to your plugin list; don't nest it inside another entry.

1. Open cordis.yml (or your patch file) in your DSH project
2. Paste the entry into your plugin list, keeping the leading dash and indentation
3. Restart DSH (or let HMR reload) — tools register as mcp__mewcp__<tool_name>
4. Verify with: dsh web --dump-config | grep -A3 mcp

## SDKs

### Python

fastmcp client

```python
import asyncio
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport

SERVER_URL = "https://gateway.mewcp.com/personal/mcp"
API_KEY = "API_KEY"

transport = StreamableHttpTransport(
    url=SERVER_URL,
    headers={
        "Authorization": f"Bearer {API_KEY}",
    }
)

async def main():
    client = Client(transport)
    async with client:
        tools = await client.list_tools()
        print(tools)

asyncio.run(main())
```

1. Install fastmcp: pip install fastmcp
2. Copy the snippet into your project
3. Replace API_KEY with your key from the dashboard
4. Run your script

### TypeScript

MCP SDK

```typescript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const SERVER_URL = "https://gateway.mewcp.com/personal/mcp";
const API_KEY = "API_KEY";

const transport = new StreamableHTTPClientTransport(new URL(SERVER_URL), {
  requestInit: {
    headers: {
      Authorization: `Bearer ${API_KEY}`,
    },
  },
});

const client = new Client({ name: "mewcp-client", version: "1.0.0" });
await client.connect(transport);

const tools = await client.listTools();
console.log(tools.tools.map((t) => t.name));
```

1. Install: npm install @modelcontextprotocol/sdk
2. Copy the snippet into your project
3. Replace API_KEY with your key from the dashboard
4. Run with Node 18+ as an ES module (e.g. npx tsx script.ts)