Skip to content

MCP Server

KUKAN provides a Model Context Protocol (MCP) compliant server endpoint. This enables Claude, ChatGPT, and other MCP-compatible AI agents to search and browse the data catalog.

POST /api/mcp

Operates in stateless mode using the Streamable HTTP transport.

Add it with the CLI:

ターミナルウィンドウ
claude mcp add --transport http kukan https://your-kukan-site/api/mcp

To write it in a project’s .mcp.json, set type to http:

{
"mcpServers": {
"kukan": {
"type": "http",
"url": "https://your-kukan-site/api/mcp"
}
}
}

To include private datasets of your organizations, pass an API token as a header (--header "Authorization: Bearer kukan_..." on the CLI, or "headers": { "Authorization": "Bearer kukan_..." } in .mcp.json).

Add https://your-kukan-site/api/mcp as a custom connector under Connectors in the settings (see the Claude Help Center for which plans allow it).

  • The connection comes from Anthropic’s cloud, not from your computer. A site the internet cannot reach (a closed network, behind a VPN) cannot be connected. Use a client that connects from your computer, such as Claude Code, instead
  • A custom connector cannot carry an API token header, so it reaches public datasets only
ターミナルウィンドウ
curl -X POST https://your-kukan-site/api/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": { "name": "test", "version": "0.1" }
}
}'

The instructions of the initialize result tell the client how the tools go together. MCP clients add them to what the model is told.

  • Language to search in: search in the catalog’s language and answer in the user’s. The catalog’s language is judged from the characters of the public datasets’ titles (the 50 most recently updated). Only Japanese and Korean are named. A catalog of kanji alone (which cannot tell Japanese from Chinese), of a language in Latin script, or of mixed languages gets no language named — only “in the language of the titles the searches return”
  • Choosing the words: give a few words a dataset’s title or a document would use, not the whole question; if nothing fits, drop a word or try another
  • Which tools go together: where the answer is in documents’ text, search_passages then get_document_section; where it is a figure in a table, search_datasets then get_resource_schema and query_resource
  • Citing: cite the resource, the page or rows, and the URL each tool gives. Judging what the text means is left to the caller

All tools are read-only. Access to private datasets is controlled based on the authenticated user’s permissions.

Tool Description
search_datasets Search datasets by keyword or natural language (hybrid search). Also says which file matched
get_dataset Get detailed dataset information including resource list
get_resource Get resource (file) metadata, URL, and its AI-written description
get_resource_schema Get a resource’s column schema (names, types, row count, stats) and whether OData serves it. Tables only
query_resource Run a read-only SQL query (SELECT/WITH only) over a resource’s data. Tables only
get_document_outline Get a document’s outline: its headings with their pages, or a workbook’s sheets and rows. PDF/Word/PowerPoint/Excel/Ichitaro/Markdown
get_document_section Read a document’s text by page, slide, sheet or heading, with where it is from. PDF/Word/PowerPoint/Excel/Ichitaro/Markdown
search_passages Find the passages of documents’ text that match a query, with where each is from and its page. PDF/Word/PowerPoint/Excel/Ichitaro/Markdown
list_organizations List organizations
list_groups List groups (categories)
list_tags List tags

Search the data catalog. Besides keywords, natural-language queries work too — semantically related datasets are included in the results (hybrid search). By keyword, a dataset matches if it holds every word of the query, so a few words its title or description would use find more than the whole question. Where no dataset holds every word, the datasets holding fewer of them are returned, the reply says so first, and each names the words it lacks under Lacks:.

When a dataset holds several files, the result says which of them matched, under Matched files:, each with what the match is on account of — in its name or description, in its contents, in its AI-written description, or by meaning 0.42 (the number is the similarity). A match by meaning means the query’s words appear nowhere in that file, so do not go looking for them there. The first five are named and the rest are counted. Each result also carries its organization, description (first 200 characters), and formats (read from the database).

Parameter Type Required Description
q string Yes Search keywords or a natural-language query
organization string No Filter by organization name (slug)
tags string[] No Filter by tag names
semantic boolean No false limits results to keyword matches only (default: true)
offset number No Number of results to skip (default: 0)
limit number No Maximum results (default: 10, max: 50)

Get detailed information about a dataset. Resources are listed in the order of the public page, with a [section name] heading line where a section begins. / nesting is indented to the same depth as on the public page, and the resources under a heading one level deeper.

Parameter Type Required Description
nameOrId string Yes Dataset name (slug) or UUID

Get resource metadata. Returns the download URL for uploaded files. A resource inside a section carries a Section: line. A document whose text has been extracted (PDF, Word, PowerPoint, Excel, Ichitaro, the Office formats before 2007 included) carries a Document text: line saying it can be read with get_document_outline and get_document_section (or, for a document of scans, that it has none to read).

Where the site has AI-written descriptions enabled, one is appended, together with how much of the file it was written from (read the column layout and the first 100 of 1200 rows, for example) and which version it describes. If the file has been replaced and the description has not caught up, that reads describes v1, and the file is now v2, so an old description is never mistaken for a current one. Where none was written, the reason is given instead.

Parameter Type Required Description
id string Yes Resource UUID

Get a resource’s column schema (names, types, nullability, row count, and min/max for numeric columns). When the resource has a settled primary key it is reported on a Primary key: line. Each column carries its role (name, category, free text, identifier, number or date, number written as text), with the most frequent values of a name or category column and the values that do not read as numbers in a column of numbers written as text. Only tables — CSV/TSV, and JSON/GeoJSON whose records read as one — with a generated preview are queryable. When the resource is queryable, the output names the query table data and includes an example query, so the agent can write SQL after inspecting the schema; when it is not (the preview is gone or does not reflect the current content), the columns are still listed for reference but the query guidance is withheld.

Whether the table is served over OData comes back too. When it is, the output carries the feed URL (ending in /Rows), what identifies its rows — the primary key, or the RowId column KUKAN adds and why it had to — and, for a table whose rows are large enough that a page may fail to read, a caution saying so. When it is not, it carries the reason: headings that are not OData identifiers, two columns of one name, a private or draft dataset. Queryable over SQL and readable over OData are not the same thing, so read this line before choosing how to fetch the data.

Parameter Type Required Description
id string Yes Resource UUID

Run a read-only SQL query (SELECT / WITH only) over a resource’s data and return the result as a Markdown table. The data is exposed as a table named data. Use get_resource_schema first to learn the column names and types.

Parameter Type Required Description
id string Yes Resource UUID
sql string Yes A single SELECT/WITH statement over the data table

Runs in a throwaway in-memory DuckDB instance with external access disabled and row/byte/time limits. Double-quote column names that contain non-ASCII characters. See the REST /resources/{id}/query endpoint for limits and error details.

Get a document’s outline, to choose what to read before calling get_document_section.

  • Headings: the PDF’s bookmarks (or where it has none, its heading tags), the Word file’s heading styles or the slides’ titles, indented by level, each with the pages (slides) it spans — pages 15–18 1-1-3 Staff
  • Workbooks: the sheets in place of headings, with their names and rows
  • Pages and unreadable pages: the range of pages (slides, sheets), the pages with no text to read (scans), and those with some characters that cannot be read (a PDF font that does not map them), which are left out of their text
  • The response opens with the same source as get_document_section

A Word file from before 2007 (DOC) and an Ichitaro file mark no headings a reader can find, so their text is one block. An Ichitaro file of several sheets is a block a sheet, selected by the sheet’s number.

A document that is all scans, with no text, still has its pages, headings and unreadable pages listed. A long outline is cut at about 12,000 characters (a heading at 120, the list of unreadable pages at a quarter of it), with a count of what was left out; give from and to to see part of it.

Parameter Type Required Description
id string Yes Resource UUID
from number No First page, slide or sheet (1-based)
to number No Last page, slide or sheet

Read the text KUKAN extracted from a document (PDF, Word, PowerPoint, Excel, Ichitaro, the Office formats before 2007 included) by where it is in the file: a range of pages — a PDF’s pages, a presentation’s slides, a workbook’s sheets — a heading, or both. No AI model is called; what comes back is the file’s own text.

The response opens with the source to cite: the resource and dataset names, the resource page and file URLs (for a PDF, add #page=N to open it at a page), the format and the version. Each block of text is labelled with its page (and printed page number, where it differs), slide, or sheet and rows, and the headings it is under — --- page 12 (iv) · Chapter 2 > Population. A page with no text to read (a scan) says so rather than being left out, and one with characters that cannot be read says how many were left out of its text. If the file has been replaced and its text not yet extracted again, the response gives the version the text is from (Text from: version 1) and the file’s current version (Current file version: 2) in place of Version:; cite the version the text is from.

A heading matches when it contains the text given, ignoring case, width and spaces; it also matches a sheet’s name. A Word file has no pages until it is laid out, so it is selected by heading only. When Joining headings with >, as in Chapter 1 > Overview, picks a heading under another. When nothing matches, the response gives the document’s page range and points to get_document_outline.

Parameter Type Required Description
id string Yes Resource UUID
from number No First page, slide or sheet (1-based)
to number No Last page, slide or sheet (inclusive)
heading string No Part of a heading the text is under, or of a sheet’s name
max_chars number No Characters of text to return (default: 8,000, max: 32,000)
cursor string No The cursor a previous response ended with; from, to and heading come from it

A longer section is cut — at a line break where it can be — and the response ends with a cursor to read on. A cursor stops working once the document is extracted again.

The outline and the text are also available over REST as GET /api/v1/resources/{id}/document-outline and document-section, with the same parameters and a JSON response.

Return the passages of documents’ text across the catalog (PDF, Word, PowerPoint, Excel, Ichitaro) that match a query, best first. Datasets and resources are found first, by the same search as search_datasets, and then the matching passages inside their documents. Where no dataset holds every word, the documents are found by fewer of them, as search_datasets does, and the reply says so first. No generative AI is called (the query is embedded, to find documents by meaning as well; a site without an embedding model finds them by words alone). Tables such as CSV are not searched (use get_resource_schema and query_resource).

Each passage opens with the same source as get_document_section (a PDF’s file URL comes with #page=N for the passage’s page), then where it is (page, slide or sheet, and the headings above it), the words that matched, and its text. Each comes with a cursor: give it to get_document_section to read around the passage from its page. A document without pages (Word) is read from where the passage is.

Passages inside a document are matched by words, not meaning. If nothing fits, search again in the words a document would use (an administration’s terms, for instance). Where more passages matched than were returned, the response ends by saying so.

Parameter Type Required Description
q string Yes Words to search for
organization string No Filter by organization name (slug)
tags string[] No Filter by tag names
k number No Passages to return (default: 5, max: 20)
per_resource number No Passages from one resource at most (default: 2)
max_chars number No Characters of text to return, all passages together (default: 8,000, max: 32,000)

The same search is available over REST as GET /api/v1/passages, with the same parameters, and semantic=false to match by words alone.

Parameter Type Required Description
q string No Search by name
offset number No Number of results to skip (default: 0)
limit number No Maximum results (default: 20, max: 100)
Parameter Type Required Description
q string No Search by name
offset number No Number of results to skip (default: 0)
limit number No Maximum results (default: 20, max: 100)
Parameter Type Required Description
q string No Search by name
offset number No Number of results to skip (default: 0)
limit number No Maximum results (default: 50, max: 200)

The MCP endpoint integrates with KUKAN’s authentication system.

  • Unauthenticated: Access to public datasets only
  • Authenticated: Access to private datasets within your organizations
  • System admin: Access to all datasets

To authenticate, send an API token in the Authorization: Bearer kukan_... header (see Authentication).

  • Protocol: MCP (Model Context Protocol). The version is negotiated with the client at initialization (2024-11-05 and later)
  • Transport: Streamable HTTP
  • Mode: Stateless (no session management)
  • Response format: JSON-RPC 2.0