---
summary: "Point an MCP client at /mcp with a ScaiGrid bearer token \u2014 list tools,\
  \ call one, see it metered. Five minutes end-to-end."
title: Quickstart
path: quickstart
status: published
---

In five minutes you'll have an MCP client connected to ScaiGrid, listing the tool catalog filtered for your permissions, and running a chat completion through it.

You need:

- A ScaiGrid API key (`sgk_...`) for a tenant where you have at least `models:use` and `models:list`.
- Python 3.10+ or Node 18+, depending on which sample you run.
- The MCP SDK for your language: `pip install mcp` or `npm install @modelcontextprotocol/sdk`.

```bash
export SCAIGRID_HOST="https://scaigrid.scailabs.ai"
export SCAIGRID_API_KEY="sgk_..."
```

## 1. Confirm the transport is up

The MCP transport is mounted at `/mcp` on the ScaiGrid host. It does not respond to plain GET — it's a streamable-HTTP transport — but you can verify the host is reachable and your token is accepted by hitting the admin tool-list endpoint:

```bash
curl "$SCAIGRID_HOST/v1/modules/scaimcp/tools" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY"
```

A 200 with a JSON list of tool names means you're good. A 401 means your token is wrong or expired; a 403 means your role doesn't have `scaimcp:access`.

## 2. Connect and list tools

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

async def main():
    url = f"{os.environ['SCAIGRID_HOST']}/mcp"
    headers = {"Authorization": f"Bearer {os.environ['SCAIGRID_API_KEY']}"}
    async with streamablehttp_client(url, headers=headers) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = await session.list_tools()
            for t in tools.tools:
                print(f"{t.name}: {t.description}")

asyncio.run(main())
```

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

const transport = new StreamableHTTPClientTransport(
  new URL(`${process.env.SCAIGRID_HOST}/mcp`),
  { requestInit: { headers: {
      Authorization: `Bearer ${process.env.SCAIGRID_API_KEY}` } } }
);
const client = new Client({ name: "qs", version: "0.1" }, { capabilities: {} });
await client.connect(transport);

const { tools } = await client.listTools();
for (const t of tools) console.log(`${t.name}: ${t.description}`);
```

```bash
# Inspector via npx — quick interactive UI
npx @modelcontextprotocol/inspector \
  --url "$SCAIGRID_HOST/mcp" \
  --header "Authorization: Bearer $SCAIGRID_API_KEY"
```

The list you see is **filtered for your identity** — modules disabled for your tenant are hidden, tools you can't invoke are hidden. A super-admin sees everything; a `tenant_user` typically sees inference and session tools only.

## 3. Call a tool

Run a non-streaming chat completion through `inference_chat`:

```python
result = await session.call_tool("inference_chat", {
    "model": "scailabs/poolnoodle-omni",
    "messages": [
        {"role": "user", "content": "Say hello in one short sentence."}
    ],
    "max_tokens": 60,
})
print(result.content[0].text)
```

```javascript
const result = await client.callTool({
  name: "inference_chat",
  arguments: {
    model: "scailabs/poolnoodle-omni",
    messages: [{ role: "user", content: "Say hello in one short sentence." }],
    max_tokens: 60,
  },
});
console.log(result.content[0].text);
```

The response is a single text content block containing a JSON-encoded result with `id`, `model`, `content`, `finish_reason`, and `usage`. The usage object is what gets recorded against your tenant budget.

## 4. List models, then call again

A typical agent loop: discover what's available, then invoke. `models_list` returns the frontend model catalog your tenant can use:

```python
models = await session.call_tool("models_list", {})
print(models.content[0].text)
```

Pick a slug from the response and re-run `inference_chat` with it.

## 5. Check the audit trail

Every MCP call goes through the same accounting and audit pipeline as a REST call. Confirm in two places:

```bash
# Token usage for the current period
curl "$SCAIGRID_HOST/v1/accounting/usage?period=day" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY"
```

```bash
# Audit events from your user
curl "$SCAIGRID_HOST/v1/audit/events?actor_user_id=$YOUR_USER_ID&limit=10" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY"
```

The chat tool call shows up as a normal inference event — there's no separate "MCP" audit category, because MCP is just another front door.

## What just happened

- Your client opened a streamable-HTTP MCP session against `/mcp` and authenticated with the bearer token.
- ScaiMCP resolved the token to a `CurrentUser` using the same code path as the REST API.
- `list_tools` ran the catalog through three filters: core tool permissions, enabled-module gating, and the cloud-MCP aggregator from ScaiLink.
- `call_tool("inference_chat", ...)` invoked the same `InferenceService.chat` that `/v1/inference/chat` uses — so the dispatcher, accounting, and audit log all behave identically.

## Next

- [Connect Claude Desktop](./tutorials/connect-claude-desktop) for the end-user setup.
- [Architecture](./concepts/architecture) for how the pieces fit.
- [Tool naming](./concepts/tool-naming) to read tool names correctly, especially the `remote.` prefix for ScaiLink-registered cloud servers.
