*MCP › Connect*

# Build your own client

> Use the official MCP SDKs or the MCP connectors of the Anthropic and OpenAI APIs.

## The official SDKs

With this server, the TypeScript SDK's `Client.callTool` and the Python SDK's `call_tool` return successes and errors without raising `-32602`. Filmo checked it with `@modelcontextprotocol/sdk` 1.26.0 and 1.32.1, `@modelcontextprotocol/client` 2.3.1 and `mcp` 2.3.0 and 1.27.1. In the 2.x TypeScript client `Client` and `StreamableHTTPClientTransport` keep their names and arguments; only the package and the import change, so take the import from that package's documentation. The example below is the 1.x one. How to read an error is in [Errors](https://docs.filmo.io/mcp/guides/errors).

*Call a tool with a key*

**TypeScript**

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

const transport = new StreamableHTTPClientTransport(new URL("https://app.filmo.io/api/v1/mcp"), {
  requestInit: { headers: { Authorization: `Bearer ${process.env.FILMO_API_KEY}` } },
});
const client = new Client({ name: "my-agent", version: "1.0.0" });
await client.connect(transport);

const result = await client.callTool({ name: "filmo_orders_list", arguments: { limit: 20 } });
if (result.isError) {
  // A tool error: the problem is JSON in the first text block, and there is no structuredContent.
  const problem = JSON.parse((result.content as { text: string }[])[0].text);
  console.error(problem.code, problem.detail);
} else {
  console.log(result.structuredContent); // { data, meta }
}
```

**Python**

```python
import asyncio, json, os
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

async def main():
    headers = {"Authorization": f"Bearer {os.environ['FILMO_API_KEY']}"}
    async with streamablehttp_client("https://app.filmo.io/api/v1/mcp", headers=headers) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool("filmo_orders_list", arguments={"limit": 20})
            if result.isError:
                # A tool error: the problem is JSON in the first text block.
                problem = json.loads(result.content[0].text)
                print(problem["code"], problem["detail"])
            else:
                print(result.structuredContent)  # {"data": ..., "meta": ...}

asyncio.run(main())
```

The 2.x packages of both SDKs (`@modelcontextprotocol/client`, `mcp` 2.x with `result.is_error` and `httpx2`) have a different API; the server's contract is the same.

## The MCP connectors of the LLM APIs

Both APIs accept a remote MCP server. The lists below come from the catalogue: the read tools of the recommended preset, and the tools that need human confirmation.

**Anthropic**

```json
{
  "mcp_servers": [
    {
      "type": "url",
      "url": "https://app.filmo.io/api/v1/mcp",
      "name": "filmo",
      "authorization_token": "<FILMO_API_KEY>"
    }
  ],
  "tools": [
    {
      "type": "mcp_toolset",
      "mcp_server_name": "filmo",
      "default_config": {
        "enabled": false
      },
      "configs": {
        "filmo_items_list": {
          "enabled": true
        },
        "filmo_items_get": {
          "enabled": true
        },
        "filmo_availability_check": {
          "enabled": true
        },
        "filmo_serialized_items_list": {
          "enabled": true
        },
        "filmo_serialized_items_get": {
          "enabled": true
        },
        "filmo_spaces_list": {
          "enabled": true
        },
        "filmo_spaces_get": {
          "enabled": true
        },
        "filmo_vehicles_list": {
          "enabled": true
        },
        "filmo_vehicles_get": {
          "enabled": true
        },
        "filmo_staff_list": {
          "enabled": true
        },
        "filmo_staff_get": {
          "enabled": true
        },
        "filmo_staff_availability_check": {
          "enabled": true
        },
        "filmo_vehicles_availability_check": {
          "enabled": true
        },
        "filmo_kits_list": {
          "enabled": true
        },
        "filmo_kits_get": {
          "enabled": true
        },
        "filmo_planner_services_list": {
          "enabled": true
        },
        "filmo_schedule_get": {
          "enabled": true
        },
        "filmo_unavailabilities_list": {
          "enabled": true
        },
        "filmo_planner_resources_list": {
          "enabled": true
        },
        "filmo_account_get": {
          "enabled": true
        }
      }
    }
  ]
}
```

**OpenAI**

```json
{
  "type": "mcp",
  "server_label": "filmo",
  "server_url": "https://app.filmo.io/api/v1/mcp",
  "authorization": "<FILMO_API_KEY>",
  "allowed_tools": [
    "filmo_items_list",
    "filmo_items_get",
    "filmo_availability_check",
    "filmo_serialized_items_list",
    "filmo_serialized_items_get",
    "filmo_spaces_list",
    "filmo_spaces_get",
    "filmo_vehicles_list",
    "filmo_vehicles_get",
    "filmo_staff_list",
    "filmo_staff_get",
    "filmo_staff_availability_check",
    "filmo_vehicles_availability_check",
    "filmo_kits_list",
    "filmo_kits_get",
    "filmo_planner_services_list",
    "filmo_schedule_get",
    "filmo_unavailabilities_list",
    "filmo_planner_resources_list",
    "filmo_account_get"
  ],
  "require_approval": {
    "never": {
      "tool_names": [
        "filmo_items_list",
        "filmo_items_get",
        "filmo_availability_check",
        "filmo_serialized_items_list",
        "filmo_serialized_items_get",
        "filmo_spaces_list",
        "filmo_spaces_get",
        "filmo_vehicles_list",
        "filmo_vehicles_get",
        "filmo_staff_list",
        "filmo_staff_get",
        "filmo_staff_availability_check",
        "filmo_vehicles_availability_check",
        "filmo_kits_list",
        "filmo_kits_get",
        "filmo_planner_services_list",
        "filmo_schedule_get",
        "filmo_unavailabilities_list",
        "filmo_planner_resources_list",
        "filmo_account_get"
      ]
    }
  }
}
```

Anthropic needs the `anthropic-beta: mcp-client-2025-11-20` header, and `authorization_token` is the Bearer token. OpenAI documents no header field of its own, only `authorization`. With OpenAI, `require_approval` with `never` for the read tools alone leaves everything else asking for approval.

**Tools that need human confirmation (for your approval list)**

```json
[
  "filmo_contacts_delete",
  "filmo_contacts_update",
  "filmo_contacts_address_delete",
  "filmo_contacts_related_remove",
  "filmo_items_delete",
  "filmo_spaces_delete",
  "filmo_spaces_amenities_replace",
  "filmo_vehicles_delete",
  "filmo_staff_delete",
  "filmo_tags_delete",
  "filmo_tags_assignment_remove",
  "filmo_planner_assignment_remove",
  "filmo_unavailabilities_delete",
  "filmo_tasks_delete",
  "filmo_purchase_orders_receive",
  "filmo_repair_tickets_update",
  "filmo_treasury_payments_create",
  "filmo_orders_cancel",
  "filmo_items_accessories_update",
  "filmo_items_alternatives_update",
  "filmo_orders_line_items_return",
  "filmo_ai_ask",
  "filmo_workflows_run",
  "filmo_orders_unreserve",
  "filmo_projects_orders_unlink",
  "filmo_invoices_validate",
  "filmo_invoices_void",
  "filmo_credit_notes_validate",
  "filmo_credit_notes_void",
  "filmo_contracts_validate",
  "filmo_contracts_sign",
  "filmo_contracts_void",
  "filmo_contracts_cancel",
  "filmo_supplier_invoices_validate",
  "filmo_supplier_invoices_void"
]
```

> **Warning:** Do not paste the key into code or a file of the repository: read it from a secret store or the environment.

---

Canonical URL: https://docs.filmo.io/mcp/connect/build-your-own  
Last updated: 2026-10-25