# Agents and LLMs

Give your agent the model as a tool, over MCP from any framework or over REST with one function-calling definition, and narrate the bundle with your own LLM.

> Informational documentation, based on the paper Data Language Models: A New Foundation Model Class for Tabular Data (arXiv:2605.06290, https://arxiv.org/abs/2605.06290). Reference material, not a hosted service; hosts, keys, and ids are placeholders.

An agent that meets raw tables mid-task gets the bundle back: sector, column profile, cross-table map, filled values, and predictions, each with a confidence. It reasons over that structured JSON, not over rows. Two ways to give it the tool.

- [Over MCP](/model/mcp): One remote server, every platform and API operation as a tool. Any framework that consumes MCP servers connects to the same URL.
- Over REST: One function-calling tool that posts to the API, or an endpoint wrapped from its OpenAPI description.

## The pattern

A data agent holds the the model tool; the orchestrator routes data tasks to it; the bundle flows on to reasoning and action agents.

```text
 orchestrator ──► data agent ──► the model (MCP tool, or POST /v2/serve/:id or /v2/run)
                       ▲                │
                       └── the bundle ◄─┘
                           sector · profile · map · imputation · predictions
```

The lifecycle: a **quick run** to understand a new source (nothing persists), **create an endpoint** when the same understanding should persist and carry a held-out score (a vertical product typically creates one per customer at onboarding), then **refresh and upgrade** to keep it current. Branch on confidence at every step: act above your threshold, queue the rest for review, and mask columns flagged `pii` before they leave the agent.

## Over MCP

Point your framework's MCP client at `https://mcp.example.com/mcp` with a dedicated key and the tools appear: `run`, `create`, `serve`, `refresh`, `upgrade`, data, reports, jobs, and usage. Snippets for CrewAI, LangChain and LangGraph, LlamaIndex, Bedrock AgentCore, Snowflake, Databricks, Replit, and n8n are on the [MCP server](/model/mcp#agent-frameworks) page, and the tool descriptions tell the model what each one returns and when to call it.

## Over REST

Give your LLM one tool. The definition below is what the model needs to call a quick run; for a live endpoint, fix the URL to `/v2/serve/{endpoint_id}` and keep `tables` only.

```json
{
  "name": "model_run",
  "description": "Sector, roles, PII, matches, imputation, prediction on raw tables.",
  "parameters": {
    "type": "object",
    "properties": {
      "tables": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "id": { "type": "string" },
            "columns": { "type": "array", "items": { "type": "string" } },
            "rows": { "type": "array", "items": { "type": "array" } }
          },
          "required": ["id", "columns", "rows"]
        }
      },
      "target": {
        "type": "object",
        "properties": { "mode": { "enum": ["auto", "none"] },
                        "column": { "type": "string" } }
      }
    },
    "required": ["tables"]
  }
}
```

The tool body is a `POST /v2/run` with the arguments as the request body and `Authorization: Bearer $API_KEY`. Return the bundle, or a trimmed view of it, as the tool result.

An endpoint also publishes its own OpenAPI description at `https://api.example.com/v2/openapi/{endpoint_id}` (reference). LangChain, LlamaIndex, CrewAI, and AutoGen wrap a tool from that spec directly, and observability layers such as LangSmith trace it like any other tool call.

## Narration

Let your own LLM explain a result. Pass the bundle with the endpoint's `system_prompts` (active fragments, in order) as the system prompt; read them from the endpoint object, which needs the `read` scope. See [System prompts](/model/endpoints#system-prompts).

Python:

```python
import os, json, requests

API_KEY = os.environ["API_KEY"]
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
EP_ID = "{endpoint_id}"
SERVE_URL = f"https://api.example.com/v2/serve/{EP_ID}"
# llm.chat(): your own model call

bundle = requests.post(SERVE_URL, headers=HEADERS, json={"tables": tables}).json()
ep_url = f"https://api.example.com/v2/endpoints/{EP_ID}"
ep = requests.get(ep_url, headers=HEADERS).json()
fragments = sorted(ep["system_prompts"], key=lambda f: f["order"])
system = "\n\n".join(f["body"] for f in fragments if f["active"])
user = f"Question: {q}\n\nModel bundle:\n{json.dumps(bundle)}"
answer = llm.chat(system=system, user=user)
```
JavaScript:

```javascript
const API_KEY = process.env.API_KEY;
const HEADERS = { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' };
const EP_ID = '{endpoint_id}';
const SERVE_URL = `https://api.example.com/v2/serve/${EP_ID}`;
// llm.chat(): your own model call

const res = await fetch(SERVE_URL, { method: 'POST', headers: HEADERS,
  body: JSON.stringify({ tables }) });
const bundle = await res.json();
const epUrl = `https://api.example.com/v2/endpoints/${EP_ID}`;
const ep = await (await fetch(epUrl, { headers: HEADERS })).json();
const system = ep.system_prompts.filter(f => f.active)
  .sort((a, b) => a.order - b.order).map(f => f.body).join('\n\n');
const answer = await llm.chat({ system,
  user: `Question: ${q}\n\nModel bundle:\n${JSON.stringify(bundle)}` });
```

Over MCP the client's own model narrates; `get_endpoint` returns the same system-prompt fragments.

## Keys for agents

One dedicated, named key per agent. Give it `run` for quick runs, or `serve` only for production, so a leaked key cannot create, change, or delete anything. See [Authentication](/model/authentication).

## Next

- [MCP server](/model/mcp): Add the server to your client or framework.
- [Endpoints](/model/endpoints): State, refresh, upgrade, and what the URL guarantees.
- [Outputs](/model/outputs): Every field in the bundle, with its confidence.
