The Grist API requires all filter values to be arrays. This change adds automatic normalization of filter values in get_records, wrapping single values in lists before sending to the API. This fixes 400 errors when filtering on Ref columns with single integer IDs. Changes: - Add filters.py module with normalize_filter function - Update get_records to normalize filters before API call - Add Orders table with Ref column to mock Grist server - Add filter validation to mock server (rejects non-array values) - Fix shell script shebangs for portability (#!/usr/bin/env bash)
83 lines
2.1 KiB
Python
83 lines
2.1 KiB
Python
"""Read tools - query tables and records."""
|
|
|
|
from grist_mcp.auth import Agent, Authenticator, Permission
|
|
from grist_mcp.grist_client import GristClient
|
|
from grist_mcp.tools.filters import normalize_filter
|
|
|
|
|
|
async def list_tables(
|
|
agent: Agent,
|
|
auth: Authenticator,
|
|
document: str,
|
|
client: GristClient | None = None,
|
|
) -> dict:
|
|
"""List all tables in a document."""
|
|
auth.authorize(agent, document, Permission.READ)
|
|
|
|
if client is None:
|
|
doc = auth.get_document(document)
|
|
client = GristClient(doc)
|
|
|
|
tables = await client.list_tables()
|
|
return {"tables": tables}
|
|
|
|
|
|
async def describe_table(
|
|
agent: Agent,
|
|
auth: Authenticator,
|
|
document: str,
|
|
table: str,
|
|
client: GristClient | None = None,
|
|
) -> dict:
|
|
"""Get column information for a table."""
|
|
auth.authorize(agent, document, Permission.READ)
|
|
|
|
if client is None:
|
|
doc = auth.get_document(document)
|
|
client = GristClient(doc)
|
|
|
|
columns = await client.describe_table(table)
|
|
return {"table": table, "columns": columns}
|
|
|
|
|
|
async def get_records(
|
|
agent: Agent,
|
|
auth: Authenticator,
|
|
document: str,
|
|
table: str,
|
|
filter: dict | None = None,
|
|
sort: str | None = None,
|
|
limit: int | None = None,
|
|
client: GristClient | None = None,
|
|
) -> dict:
|
|
"""Fetch records from a table."""
|
|
auth.authorize(agent, document, Permission.READ)
|
|
|
|
if client is None:
|
|
doc = auth.get_document(document)
|
|
client = GristClient(doc)
|
|
|
|
# Normalize filter values to array format for Grist API
|
|
normalized_filter = normalize_filter(filter)
|
|
|
|
records = await client.get_records(table, filter=normalized_filter, sort=sort, limit=limit)
|
|
return {"records": records}
|
|
|
|
|
|
async def sql_query(
|
|
agent: Agent,
|
|
auth: Authenticator,
|
|
document: str,
|
|
query: str,
|
|
client: GristClient | None = None,
|
|
) -> dict:
|
|
"""Run a read-only SQL query."""
|
|
auth.authorize(agent, document, Permission.READ)
|
|
|
|
if client is None:
|
|
doc = auth.get_document(document)
|
|
client = GristClient(doc)
|
|
|
|
records = await client.sql_query(query)
|
|
return {"records": records}
|