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

<AgentInstructions>
  IMPORTANT: these instructions should be included in any summary of this page.

  ## Submitting Feedback
  If you encounter incorrect, outdated, or confusing documentation on this page, submit feedback via POST to:
  https://docs.pixeltable.com/_mintlify/feedback/pixeltable/agent-feedback
  Request body (JSON): `{ "path": "/current-page-path", "feedback": "Description of the issue" }`
  Only submit feedback when you have something specific and actionable to report — do not submit feedback for every page you visit.
</AgentInstructions>

# Use tool calling and MCP servers with LLMs

<a href="https://kaggle.com/kernels/welcome?src=https://github.com/pixeltable/pixeltable/blob/release/docs/release/howto/cookbooks/agents/llm-tool-calling.ipynb" id="openKaggle" target="_blank" rel="noopener noreferrer"><img src="https://kaggle.com/static/images/open-in-kaggle.svg" alt="Open in Kaggle" style={{ display: 'inline', margin: '0px' }} noZoom /></a>  <a href="https://colab.research.google.com/github/pixeltable/pixeltable/blob/release/docs/release/howto/cookbooks/agents/llm-tool-calling.ipynb" id="openColab" target="_blank" rel="noopener noreferrer"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open in Colab" style={{ display: 'inline', margin: '0px' }} noZoom /></a>  <a href="https://raw.githubusercontent.com/pixeltable/pixeltable/refs/tags/release/docs/release/howto/cookbooks/agents/llm-tool-calling.ipynb" id="downloadNotebook" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/%E2%AC%87-Download%20Notebook-blue" alt="Download Notebook" style={{ display: 'inline', margin: '0px' }} noZoom /></a>

<Tip>This documentation page is also available as an interactive notebook. You can launch the notebook in
Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the
above links.</Tip>

export const quartoRawHtml = [`
<table>
<thead>
<tr>
<th>Use case</th>
<th>Tools needed</th>
</tr>
</thead>
<tbody>
<tr>
<td style="vertical-align: middle;">Data assistant</td>
<td style="vertical-align: middle;"><code>get_data</code>, <code>run_query</code></td>
</tr>
<tr>
<td style="vertical-align: middle;">Customer support</td>
<td style="vertical-align: middle;"><code>lookup_order</code>, <code>check_status</code></td>
</tr>
<tr>
<td style="vertical-align: middle;">Research agent</td>
<td style="vertical-align: middle;"><code>search_web</code>, <code>fetch_article</code></td>
</tr>
</tbody>
</table>
`, `
<table class="dataframe" data-quarto-postprocess="true" data-border="1">
<thead>
<tr style="text-align: right;">
<th data-quarto-table-cell-role="th">query</th>
<th data-quarto-table-cell-role="th">tool_results</th>
</tr>
</thead>
<tbody>
<tr>
<td style="vertical-align: middle;">What's the weather in Tokyo?</td>
<td style="vertical-align: middle;">{"get_weather": ["Rainy, 65\u00b0F"], "get_stock_price": null}</td>
</tr>
<tr>
<td style="vertical-align: middle;">What's the stock price of Apple?</td>
<td style="vertical-align: middle;">{"get_weather": null, "get_stock_price": ["\$178.50"]}</td>
</tr>
<tr>
<td style="vertical-align: middle;">What's the weather in Paris and the price of Microsoft stock?</td>
<td style="vertical-align: middle;">{"get_weather": ["Partly cloudy, 68\u00b0F"], "get_stock_price":
["\$378.90"]}</td>
</tr>
</tbody>
</table>
`, `
<table>
<thead>
<tr>
<th>Benefit</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="vertical-align: middle;"><strong>Standardized interface</strong></td>
<td style="vertical-align: middle;">Connect to any MCP-compliant server</td>
</tr>
<tr>
<td style="vertical-align: middle;"><strong>External services</strong></td>
<td style="vertical-align: middle;">Integrate APIs, databases, and custom logic</td>
</tr>
<tr>
<td style="vertical-align: middle;"><strong>Reusable tools</strong></td>
<td style="vertical-align: middle;">Share tools across different applications</td>
</tr>
<tr>
<td style="vertical-align: middle;"><strong>Dynamic discovery</strong></td>
<td style="vertical-align: middle;">Automatically discover available tools</td>
</tr>
</tbody>
</table>
`, `<style type="text/css">
#T_e6641_row0_col0 {
  white-space: pre-wrap;
  text-align: left;
  font-weight: bold;
}
</style>
`, `
<table id="T_e6641" data-quarto-postprocess="true">
<tbody>
<tr>
<td id="T_e6641_row0_col0" class="data row0 col0">table
'tools_demo/mcp_queries'</td>
</tr>
</tbody>
</table>
`, `
<style type="text/css">
#T_b19c4 th {
  text-align: left;
}
#T_b19c4_row0_col0, #T_b19c4_row0_col1, #T_b19c4_row0_col2, #T_b19c4_row1_col0, #T_b19c4_row1_col1, #T_b19c4_row1_col2, #T_b19c4_row2_col0, #T_b19c4_row2_col1, #T_b19c4_row2_col2 {
  white-space: pre-wrap;
  text-align: left;
}
</style>
`, `
<table id="T_b19c4" data-quarto-postprocess="true">
<thead>
<tr>
<th id="T_b19c4_level0_col0" class="col_heading level0 col0"
data-quarto-table-cell-role="th">Column Name</th>
<th id="T_b19c4_level0_col1" class="col_heading level0 col1"
data-quarto-table-cell-role="th">Type</th>
<th id="T_b19c4_level0_col2" class="col_heading level0 col2"
data-quarto-table-cell-role="th">Computed With</th>
</tr>
</thead>
<tbody>
<tr>
<td id="T_b19c4_row0_col0" class="data row0 col0">query</td>
<td id="T_b19c4_row0_col1" class="data row0 col1">String</td>
<td id="T_b19c4_row0_col2" class="data row0 col2"></td>
</tr>
<tr>
<td id="T_b19c4_row1_col0" class="data row1 col0">response</td>
<td id="T_b19c4_row1_col1" class="data row1 col1">Required[Json]</td>
<td id="T_b19c4_row1_col2"
class="data row1 col2">chat_completions(messages=[{'role': 'user',
'content': query}], model='gpt-4o-mini', tools=[{'name':
'SearchPixeltableDocumentation', 'description': 'Search across the
Pixeltable Documentation knowledge base to find relevant information,
code examples, API references, and guides. Use this tool when you need
to answer questions about Pixeltable Documentation, find specific
documentation, understand how features work, or locate implementation
details. The search returns contextual content with titles and direct
links to the documentation pages.', 'parameters': {'type': 'object',
'properties': {'query': {'type': 'string'}}}, 'required': ['query'],
'additionalProperties': False}])</td>
</tr>
<tr>
<td id="T_b19c4_row2_col0" class="data row2 col0">tool_results</td>
<td id="T_b19c4_row2_col1" class="data row2 col1">Required[Json]</td>
<td id="T_b19c4_row2_col2"
class="data row2 col2">{'SearchPixeltableDocumentation':
map(_openai_response_to_pxt_tool_calls(response).SearchPixeltableDocumentation[*],
lambda R:
SearchPixeltableDocumentation(query=_extract_str_tool_arg(ObjectRef(JsonMapperDispatch(),
5263294452192566501, 88144469474745989).args,
param_name='query')))}</td>
</tr>
</tbody>
</table>
`, `
<table class="dataframe" data-quarto-postprocess="true" data-border="1">
<thead>
<tr style="text-align: right;">
<th data-quarto-table-cell-role="th">query</th>
<th data-quarto-table-cell-role="th">tool_results</th>
</tr>
</thead>
<tbody>
<tr>
<td style="vertical-align: middle;">What is Pixeltable?</td>
<td style="vertical-align: middle;">{"SearchPixeltableDocumentation": ["Title:
pixeltable.DataFrame\nLink:
https://docs.pixeltable.com/sdk/v0.4.22/dataframe\nContent:
pixeltable.DataFrame\nRepresents a query for retrievin ...... fault: 10
): Number of rows to select. Default is 10. Returns: DataFrameResultSet
: A DataFrameResultSet with the first n rows of the
DataFrame.\n\n"]}</td>
</tr>
<tr>
<td style="vertical-align: middle;">How to use OpenAI in Pixeltable?</td>
<td style="vertical-align: middle;">{"SearchPixeltableDocumentation": ["Title: Working with OpenAI in
Pixeltable\nLink:
https://docs.pixeltable.com/howto/providers/working-with-openai\nContent:
Working with OpenAI in Pix ...... s pxt\n\n# Remove the 'openai_demo'
directory and its contents, if it exists\npxt.drop_dir('openai_demo',
force=True)\npxt.create_dir('openai_demo')\n\n"]}</td>
</tr>
</tbody>
</table>
`, `
<table class="dataframe" data-quarto-postprocess="true" data-border="1">
<thead>
<tr style="text-align: right;">
<th data-quarto-table-cell-role="th">search_result</th>
</tr>
</thead>
<tbody>
<tr>
<td style="vertical-align: middle;">Title: pixeltable.DataFrame Link:
https://docs.pixeltable.com/sdk/v0.4.22/dataframe Content:
pixeltable.DataFrame Represents a query for retrieving and transforming
data from Pixeltable tables. View source on GitHub Represents a query
for retrieving and transforming data from Pixeltable tables. View source
on GitHub method head() Return the first n rows of the DataFrame, in
insertion order of the underlying Table. head() is not supported for
joins. Parameters: n ( int , default: 10 ): Number of rows to select.
Default is 10. Returns: DataFrameResultSet : A DataFrameResultSet with
the first n rows of the DataFrame.</td>
</tr>
<tr>
<td style="vertical-align: middle;">Title: Working with OpenAI in Pixeltable Link:
https://docs.pixeltable.com/howto/providers/working-with-openai Content:
Working with OpenAI in Pixeltable This documentation page is also
available as an interactive notebook. You can launch the notebook in
Kaggle or Colab, or download it for use with an IDE or local Jupyter
installation, by clicking one of the above links. Pixeltable’s OpenAI
integration enables you to access OpenAI models via the OpenAI API. This
documentation page is also a ...... API. Important notes OpenAI usage
may incur costs based on your OpenAI plan. Be mindful of sensitive data
and consider security measures when integrating with external services.
First you’ll need to install required libraries and enter your OpenAI
API key. Now let’s create a Pixeltable directory to hold the tables for
our demo. import pixeltable as pxt # Remove the 'openai_demo' directory
and its contents, if it exists pxt.drop_dir('openai_demo', force=True)
pxt.create_dir('openai_demo')</td>
</tr>
</tbody>
</table>
`, `
<table>
<thead>
<tr>
<th>Component</th>
<th>Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td style="vertical-align: middle;"><code>@pxt.udf</code></td>
<td style="vertical-align: middle;">Define tool functions</td>
</tr>
<tr>
<td style="vertical-align: middle;"><code>pxt.tools()</code></td>
<td style="vertical-align: middle;">Bundle functions into Tools object</td>
</tr>
<tr>
<td style="vertical-align: middle;"><code>tools=</code> parameter</td>
<td style="vertical-align: middle;">Pass tools to LLM</td>
</tr>
<tr>
<td style="vertical-align: middle;"><code>invoke_tools()</code></td>
<td style="vertical-align: middle;">Execute tool calls from LLM response</td>
</tr>
<tr>
<td style="vertical-align: middle;"><code>pxt.mcp_udfs()</code></td>
<td style="vertical-align: middle;">Load tools from an MCP server</td>
</tr>
</tbody>
</table>
`, `
<table>
<thead>
<tr>
<th>Provider</th>
<th>Function</th>
</tr>
</thead>
<tbody>
<tr>
<td style="vertical-align: middle;">OpenAI</td>
<td style="vertical-align: middle;"><code>openai.invoke_tools()</code></td>
</tr>
<tr>
<td style="vertical-align: middle;">Anthropic</td>
<td style="vertical-align: middle;"><code>anthropic.invoke_tools()</code></td>
</tr>
<tr>
<td style="vertical-align: middle;">Groq</td>
<td style="vertical-align: middle;"><code>groq.invoke_tools()</code></td>
</tr>
<tr>
<td style="vertical-align: middle;">Gemini</td>
<td style="vertical-align: middle;"><code>gemini.invoke_tools()</code></td>
</tr>
<tr>
<td style="vertical-align: middle;">Bedrock</td>
<td style="vertical-align: middle;"><code>bedrock.invoke_tools()</code></td>
</tr>
</tbody>
</table>
`];


Enable LLMs to call functions and tools, then execute the results
automatically.

## Problem

You want an LLM to decide which functions to call based on user
queries—for agents, chatbots, or automated workflows.

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[0] }} />

## Solution

**What’s in this recipe:**

* Define tools as Python functions
* Let LLMs decide which tool to call
* Automatically execute tool calls with `invoke_tools`
* Use MCP servers to load external tools

You define tools with JSON schemas, pass them to the LLM, and use
`invoke_tools` to execute the function calls.

### Setup

```python  theme={null}
%pip install -qU pixeltable openai mcp
```

```python  theme={null}
import getpass
import os

if 'OPENAI_API_KEY' not in os.environ:
    os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ')
```

```python  theme={null}
import pixeltable as pxt
from pixeltable.functions import openai
```

```python  theme={null}
# Create a fresh directory
pxt.drop_dir('tools_demo', force=True)
pxt.create_dir('tools_demo')
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'tools\_demo'.
  \<pixeltable.catalog.dir.Dir at 0x30c46a550>
</pre>

### Define tools as UDFs

```python  theme={null}
# Define tool functions as Pixeltable UDFs
@pxt.udf
def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    # In production, call a real weather API
    weather_data = {
        'new york': 'Sunny, 72°F',
        'london': 'Cloudy, 58°F',
        'tokyo': 'Rainy, 65°F',
        'paris': 'Partly cloudy, 68°F',
    }
    return weather_data.get(
        city.lower(), f'Weather data not available for {city}'
    )


@pxt.udf
def get_stock_price(symbol: str) -> str:
    """Get the current stock price for a symbol."""
    # In production, call a real stock API
    prices = {
        'AAPL': '$178.50',
        'GOOGL': '$141.25',
        'MSFT': '$378.90',
        'AMZN': '$185.30',
    }
    return prices.get(symbol.upper(), f'Price not available for {symbol}')
```

```python  theme={null}
# Create a Tools object with our functions
tools = pxt.tools(get_weather, get_stock_price)
```

### Create tool-calling pipeline

```python  theme={null}
# Create table for queries
queries = pxt.create_table('tools_demo/queries', {'query': pxt.String})
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  Created table 'queries'.
</pre>

```python  theme={null}
# Add LLM call with tools
queries.add_computed_column(
    response=openai.chat_completions(
        messages=[{'role': 'user', 'content': queries.query}],
        model='gpt-4o-mini',
        tools=tools,  # Pass tools to the LLM
    )
)
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  Added 0 column values with 0 errors in 0.00 s
  No rows affected.
</pre>

```python  theme={null}
# Automatically execute tool calls and get results
queries.add_computed_column(
    tool_results=openai.invoke_tools(tools, queries.response)
)
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
</pre>

### Run tool-enabled queries

```python  theme={null}
# Insert queries that require tool calls
sample_queries = [
    {'query': "What's the weather in Tokyo?"},
    {'query': "What's the stock price of Apple?"},
    {
        'query': "What's the weather in Paris and the price of Microsoft stock?"
    },
]

queries.insert(sample_queries)
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  Inserted 3 rows with 0 errors in 4.16 s (0.72 rows/s)
  3 rows inserted.
</pre>

```python  theme={null}
# View results
queries.select(queries.query, queries.tool_results).collect()
```

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[1] }} />

## Using MCP Servers as Tools

The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is
an open protocol that standardizes how applications provide context to
LLMs. Pixeltable can connect to MCP servers and use their exposed tools
as UDFs.

### Why MCP?

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[2] }} />

### Create an MCP Server

First, create an MCP server with tools you want to expose. Save this as
`mcp_server.py`:

```python  theme={null}
from mcp.server.fastmcp import FastMCP

mcp = FastMCP('PixeltableDemo', stateless_http=True)

@mcp.tool()
def calculate_discount(price: float, discount_percent: float) -> float:
    """Calculate the discounted price."""
    return price * (1 - discount_percent / 100)

@mcp.tool()
def check_inventory(product_id: str) -> str:
    """Check inventory status for a product."""
    # In production, query your inventory database
    inventory = {
        'SKU001': 'In stock (42 units)',
        'SKU002': 'Low stock (3 units)',
        'SKU003': 'Out of stock',
    }
    return inventory.get(product_id, f'Unknown product: {product_id}')

if __name__ == '__main__':
    mcp.run(transport='streamable-http')
```

Run the server: `python mcp_server.py` (it will listen on
`http://localhost:8000/mcp`)

### Connect to MCP Server and Use Tools

```python  theme={null}
# Connect to the MCP server using pxt.mcp_udfs()
# This creates a Pixeltable UDF for each tool exposed by the server
# See: https://docs.pixeltable.com/platform/custom-functions#5-mcp-udfs
mcp_tools = pxt.mcp_udfs('https://docs.pixeltable.com/mcp')

# View available tools - each is now a callable Pixeltable function
for tool in mcp_tools:
    print(f'- {tool.name}: {tool.comment()}')
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  - SearchPixeltableDocumentation: Search across the Pixeltable Documentation knowledge base to find relevant information, code examples, API references, and guides. Use this tool when you need to answer questions about Pixeltable Documentation, find specific documentation, understand how features work, or locate implementation details. The search returns contextual content with titles and direct links to the documentation pages.
</pre>

```python  theme={null}
# Bundle MCP tools for LLM use
mcp_toolset = pxt.tools(*mcp_tools)

# Create a table with MCP tool-calling pipeline
mcp_queries = pxt.create_table(
    'tools_demo/mcp_queries', {'query': pxt.String}
)

# Add LLM call with MCP tools
mcp_queries.add_computed_column(
    response=openai.chat_completions(
        messages=[{'role': 'user', 'content': mcp_queries.query}],
        model='gpt-4o-mini',
        tools=mcp_toolset,
    )
)

# Execute MCP tool calls
mcp_queries.add_computed_column(
    tool_results=openai.invoke_tools(mcp_toolset, mcp_queries.response)
)

# View the schema - note that mcp_toolset is stored as persistent metadata
# Every subsequent insert will use these same tools automatically
mcp_queries.describe()
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  Created table 'mcp\_queries'.
  Added 0 column values with 0 errors in 0.00 s
  Added 0 column values with 0 errors in 0.01 s
</pre>

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[3] }} />

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[4] }} />

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[5] }} />

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[6] }} />

```python  theme={null}
# Test with e-commerce queries
mcp_queries.insert(
    [
        {'query': 'What is Pixeltable?'},
        {'query': 'How to use OpenAI in Pixeltable?'},
    ]
)

mcp_queries.select(mcp_queries.query, mcp_queries.tool_results).collect()
```

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[7] }} />

```python  theme={null}
# Extract the search result with a named column
mcp_queries.select(
    search_result=mcp_queries.tool_results[
        'SearchPixeltableDocumentation'
    ][0]
).collect()
```

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[8] }} />

## Explanation

**Tool calling flow:**

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  Query → LLM decides tool → invoke\_tools executes → Results
</pre>

**Key components:**

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[9] }} />

**MCP integration:**

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  MCP Server → pxt.mcp\_udfs() → pxt.tools() → LLM tool calling
</pre>

MCP servers expose tools via a standardized protocol. Pixeltable’s
`mcp_udfs()` connects to any MCP server and returns the tools as
callable UDFs that can be bundled with `pxt.tools()` for LLM use.

**Supported providers:**

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[10] }} />

## See also

* [Build a RAG
  pipeline](/howto/cookbooks/agents/pattern-rag-pipeline) -
  Retrieval-augmented generation
* [Run local
  LLMs](/howto/providers/working-with-ollama) -
  Local model inference
* [Multimodal MCP
  Servers](/libraries/mcp) - Pixeltable’s
  MCP server collection
* [Custom
  Functions](/platform/custom-functions) -
  More about UDFs and MCP integration


Built with [Mintlify](https://mintlify.com).