> ## Documentation Index
> Fetch the complete documentation index at: https://docs.semicola.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Runnable examples

> Copy-and-run starters that call Semicola over MCP and REST with curl, TypeScript and Python.

Each example makes read-only calls, so it is safe to run against any account. They use an
[API key](/guides/api-keys) from an environment variable; an OAuth access token works the same way in
the `Authorization` header. For browser sign-in instead, use [Connect in five minutes](/v3/quickstart).

```bash theme={null}
export SEMICOLA_API_KEY='sck_…'   # from Settings → API keys; never commit it
```

<Note>
  There is no Semicola client SDK package yet. These starters use the official MCP SDKs and plain
  HTTP.
</Note>

## curl: MCP

The MCP endpoint serves stateless Streamable HTTP requests, so without a session each call is one
`POST` with a JSON-RPC body. (SDK clients that `initialize` get a session instead; see
[MCP client setup](/v3/client-setup#connection-lifetime).)

```bash theme={null}
curl -s https://api.semicola.com/mcp/v3 \
  -H "Authorization: Bearer $SEMICOLA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_status","arguments":{}}}'
```

The answer's `result.structuredContent` names the active account, your role, readiness and next
actions. List the tools your account can call:

```bash theme={null}
curl -s https://api.semicola.com/mcp/v3 \
  -H "Authorization: Bearer $SEMICOLA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
```

Search for existing records before creating anything:

```bash theme={null}
curl -s https://api.semicola.com/mcp/v3 \
  -H "Authorization: Bearer $SEMICOLA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"search","arguments":{"kind":"advertiser"}}}'
```

<Warning>
  Calls from a browser carry an `Origin` header and are refused unless that origin is registered
  under **Settings → API keys → Browser origins**. Server-side calls like these send no origin.
</Warning>

## curl: REST

REST answers in a `{ "data": …, "error": … }` envelope. An API key is bound to one account, so
`X-Account-Id` is optional; add `X-Advertiser-Id` to narrow to one advertiser.

```bash theme={null}
curl -s https://api.semicola.com/api/v2/buyer/advertisers \
  -H "Authorization: Bearer $SEMICOLA_API_KEY"
```

```bash theme={null}
curl -s https://api.semicola.com/health
```

Every REST endpoint is in the **Buyer API reference** and **Storefront API reference** groups.

## TypeScript

Node.js 20 or newer, with the official MCP SDK. This starter connects, reads the tool list and makes
one `get_status` call, all within 15 seconds.

```json package.json theme={null}
{
  "name": "semicola-typescript-starter",
  "private": true,
  "type": "module",
  "scripts": { "start": "tsx src/main.ts" },
  "dependencies": { "@modelcontextprotocol/sdk": "^1.29.0" },
  "devDependencies": { "tsx": "^4.20.6", "typescript": "^5.9.0", "@types/node": "^22.0.0" }
}
```

```ts src/main.ts theme={null}
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

export const SEMICOLA_MCP_URL = process.env.SEMICOLA_MCP_URL ?? 'https://api.semicola.com/mcp/v3';
export const TIMEOUT_MS = 15_000;

/** Connects with an API key or OAuth access token and makes one read-only call. */
export async function connectAndVerify(token: string, endpoint = SEMICOLA_MCP_URL) {
  const signal = AbortSignal.timeout(TIMEOUT_MS);
  const client = new Client({ name: 'semicola-typescript-starter', version: '1.0.0' });
  const transport = new StreamableHTTPClientTransport(new URL(endpoint), {
    requestInit: { headers: { Authorization: `Bearer ${token}` } },
  });
  try {
    await client.connect(transport, { signal, timeout: TIMEOUT_MS });
    const { tools } = await client.listTools({}, { signal, timeout: TIMEOUT_MS });
    const result = await client.callTool({ name: 'get_status', arguments: {} }, undefined, {
      signal,
      timeout: TIMEOUT_MS,
    });
    if (result.isError || result.structuredContent === undefined) {
      throw new Error('get_status failed: check the token and the account it belongs to.');
    }
    return { toolCount: tools.length, status: result.structuredContent };
  } finally {
    await client.close();
  }
}

const token = process.env.SEMICOLA_API_KEY;
if (!token) throw new Error('Set SEMICOLA_API_KEY before running.');
console.log(JSON.stringify(await connectAndVerify(token), null, 2));
```

```bash theme={null}
npm install
npm start
```

When the call works, follow [Build an agent](/v3/build-an-agent) to add tool discovery, confirmation
gates, idempotency keys and retries. To run against a local stack, set
`SEMICOLA_MCP_URL=http://localhost:4000/mcp/v3`.

## Python

Python 3.10 or newer, with the official MCP SDK.

```text requirements.txt theme={null}
mcp>=1.26,<2
anyio>=4.5,<5
```

```python main.py theme={null}
"""Verify a Semicola account through the v3 MCP endpoint."""

import asyncio
import json
import os

import anyio
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

SEMICOLA_MCP_URL = os.environ.get("SEMICOLA_MCP_URL", "https://api.semicola.com/mcp/v3")
TIMEOUT_SECONDS = 15


async def connect_and_verify(token: str, endpoint: str = SEMICOLA_MCP_URL):
    headers = {"Authorization": f"Bearer {token}"}
    async with streamablehttp_client(endpoint, headers=headers) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool("get_status", {})
            if result.isError or result.structuredContent is None:
                raise RuntimeError("get_status failed: check the token and its account.")
            return result.structuredContent


async def main() -> None:
    token = os.environ.get("SEMICOLA_API_KEY")
    if not token:
        raise RuntimeError("Set SEMICOLA_API_KEY before running.")
    with anyio.fail_after(TIMEOUT_SECONDS):
        status = await connect_and_verify(token)
    print(json.dumps(status, indent=2))


if __name__ == "__main__":
    asyncio.run(main())
```

```bash theme={null}
pip install -r requirements.txt
python main.py
```

## Next steps

* [Bring data in](/guides/bring-data-in): send events, creative, inventory and material.
* [Tool catalog](/v3/tool-catalog): every tool's input and output.
* [Errors](/v3/errors) and [Limits](/v3/limits): failure shapes and bounds.
