Skip to content

Add MCP tool to create a database table - #8633

Merged
andypalmi merged 6 commits into
mainfrom
feat/mcp-create-database-table
Oct 6, 2026
Merged

andypalmi merged 6 commits into
mainfrom
feat/mcp-create-database-table

Conversation

@andypalmi

@andypalmi andypalmi commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Adds the platform_create_database_table action tool from #7709, on its own. The two other tools in that issue (platform_create_library_entry, platform_update_git_token) are not included here.

What it does

  1. New write tool platform_create_database_table backing POST /api/v1/teams/:teamId/databases/:databaseId/tables (team:database:create), added to the existing tables.js alongside the read tools rather than a separate file.
  2. annotations: readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false. Not idempotent because the route returns 409 if a table of that name already exists.

Input schema

  1. name and at least one column are both required (.min(1)). The route handler only replies when both name and columns are present, so requiring them keeps a bare call from producing no response.
  2. type documents the supported set (bigint, bigserial, boolean, date, timestamptz, real, double precision, text); the database driver rejects anything else.
  3. schema is optional and defaults to public. A schema that does not exist yet is created. It needs the create table API change from Add an optional schema field to the create table API #8763.

Behaviour notes

  1. Error responses pass through unchanged. The route's own feature-gate 404 already carries a descriptive error string ("FlowFuse Tables is not enabled for this team"), and a name clash returns 409 table_exists.

Tests

  1. Posts name and columns to the correct endpoint and returns the response unmodified.
  2. Passes through a 409 error response unchanged.

@andypalmi
andypalmi requested a review from cstns September 22, 2026 15:59
@andypalmi andypalmi self-assigned this Sep 22, 2026
@codecov

codecov Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.36%. Comparing base (ab65a17) to head (3a6e940).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #8633   +/-   ##
=======================================
  Coverage   78.35%   78.36%           
=======================================
  Files         474      474           
  Lines       25639    25648    +9     
  Branches     6827     6830    +3     
=======================================
+ Hits        20089    20098    +9     
  Misses       5550     5550           
Flag Coverage Δ
backend 78.36% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@andypalmi

Copy link
Copy Markdown
Contributor Author

Checked the tool against POST /api/v1/teams/:teamId/databases/:databaseId/tables and both table drivers.

The tool is correct. It targets the right route with team:database:create, sends name and columns as the route expects, and passes error responses through unchanged. Two choices are deliberate hardening against route quirks: it requires name and at least one column (.min(1)), which avoids the endpoint's no-reply hang on a partial body, and it documents the supported column types, since anything else is rejected by the driver.

While verifying the route I found some pre-existing backend bugs. They live in the route and drivers, not in this tool, so they do not block the tool, but they affect what a caller can actually do. Filed for follow-up:

  1. FF Tables: create-table endpoint hangs on a partial body and accepts invalid input #8640 create-table hangs on a partial body and accepts an empty columns array.
  2. FF Tables: unsupported column type returns a 500 on table creation #8634 an unsupported column type returns a 500 instead of a 400.
  3. FF Tables: column default values are broken for real/double and boolean, and ignored for date/bigserial #8635 column defaults are wrong for real/double and boolean, and dropped for date/bigserial.
  4. FF Tables: generated and maxLength column fields are accepted but silently ignored #8636 generated and maxLength are accepted but silently ignored.
  5. FF Tables: duplicate-table check is name-only and matches on exactly one row #8637 the duplicate-table check is name-only and matches on exactly one row.
  6. FF Tables: successful table creation returns an empty body and the response schema is unused #8638 a successful create returns an empty body and the declared response schema is unused.
  7. FF Tables: deleting a database logs a created audit event instead of deleted #8639 deleting a database logs a created audit event instead of deleted.

@andypalmi

andypalmi commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor Author

On schema-qualified table creation (e.g. myschema.mytable): the tool does not support it because the platform API does not support it yet. The create route accepts only { name, columns } and the driver runs an unqualified CREATE TABLE, so a new table always lands in the database's default schema. The UI reflects the same limit: its create form states the table is created in the default schema, and its name validation rejects dotted names. Read, query and delete already take a schema; only create does not.

If we want schema-qualified creation, there are two paths:

  1. Extend the API (route and driver) to accept a schema and create the table qualified, then add the field to this tool.
  2. Leave creation to the tables-query node, which runs arbitrary SQL including CREATE TABLE myschema.mytable (...) and other schema changes. This is the documented path for any write or schema change, per the flowfuse-tables-platform-tools and flowfuse-tables-query skills: the platform tools are read-only inspection, the node is the way to write or alter schema.

@Steve-Mcl

@andypalmi

Copy link
Copy Markdown
Contributor Author

Before this one merges, I'm going to change the description to say all tables are created in the public schema and a custom schema cannot be used from this tool. Instead, the agent would need to use the @flowfuse/nr-tables-nodes package with the tables-query node to create the table in a separate schema.

Unless we decide to hold this off, do the following first:

  • update UI to allow for setting the schema, with public as default
  • update API schema to add a "schema" field as optional that defaults to "public"
  • change the backend to allow accepting the schema in a table creation, defaulting to public

Let me know @cstns and @Steve-Mcl , I feel this can be a pretty small change that leads to a good improvement but I would like your thoughts first.

@andypalmi

Copy link
Copy Markdown
Contributor Author

Following the discussion, we're adding schema support to table creation before this merges:

  1. Accept a schema when creating a table in the Postgres drivers #8757 Postgres drivers: create the table in a given schema, creating the schema if missing, default public.
  2. Add an optional schema field to the create table API #8758 API: optional schema field on the create table route, default public, backwards compatible.
  3. Let users pick the schema when creating a table #8759 UI: schema field in the Create Table drawer, pre-filled with public.

Once those are merged, I'll update platform_create_database_table here with an optional schema input that defaults to public. This PR stays open until then.

The tool takes an optional schema, defaulting to public, validated with
the same rules as the create table API.
@andypalmi

Copy link
Copy Markdown
Contributor Author

Updated the tool to take an optional schema, defaulting to public. A schema that does not exist yet is created.

This depends on the create table API change in #8763, part of the stack #8762 to #8766, so this PR should merge after that stack lands.

The input schema now carries the supported types and the schema default,
so the tool description no longer repeats them.
@andypalmi
andypalmi marked this pull request as ready for review October 5, 2026 15:56
andypalmi and others added 2 commits October 5, 2026 17:56
The create table route replies to a successful create with an empty body, which failed to parse as the tool result. The tool now returns the table name and schema instead.
@andypalmi

Copy link
Copy Markdown
Contributor Author

Tested against a local FlowFuse through MCP

platform_create_database_table

Case Params Verdict Payload
New table in a new schema name: mcp_schema_check_4, schema: mcp_test, bigserial + nullable text Pass { table: { name: "mcp_schema_check_4", schema: "mcp_test" } }
No schema name: mcp_no_schema, no schema Pass { table: { name: "mcp_no_schema", schema: "public" } }, table found in public
Same name, same schema name: mcp_schema_check_4, schema: mcp_test Pass 409 "Table already exists"
Reserved prefix schema: pg_reports Pass Rejected by the input schema
Reserved name schema: information_schema Pass Rejected by the input schema
Starts with a digit schema: 1reports Pass Rejected by the input schema
Hyphen schema: my-schema Pass Rejected by the input schema
64 characters schema: "a" x 64 Pass Rejected by the input schema
Unsupported type type: varchar Pass Rejected, must be one of the 8 supported types
No columns columns: [] Pass Rejected, fewer than 1 item
Empty name name: "" Pass Rejected, shorter than 1 character

Latest commit

  1. The create table route replies to a successful create with 201 and an empty body, so the tool now returns { table: { name, schema } } on success, with schema defaulting to public. Error responses pass through unchanged.

@cstns can you take one last look and approve it if everything checks out for you? The backend for this landed already in main (#8762 and #8763) so we are ready to merge it.

@andypalmi
andypalmi requested a review from cstns October 5, 2026 16:09
@andypalmi
andypalmi requested a review from Steve-Mcl October 5, 2026 16:12
@Steve-Mcl

Copy link
Copy Markdown
Contributor

1 thing I would probably think on is the case sensitive regex and the fact there is a bug in the createTable method.

  1. currently the regex doesnt prevent PG_FOO or Information_Schema - should we? no real harm but future who knows!?!?
  2. createTable uses column.default where it should use col.default
    column += `DEFAULT ${parseFloat(column.default)}`
    } else if (col.type === 'boolean') {
    column += `DEFAULT ${column.default === 'true'}`
    - we might want to pick that up here or in a separate issue?

@andypalmi

Copy link
Copy Markdown
Contributor Author

Thanks @Steve-Mcl, good eyes! Tried both locally:

  1. Regex: I think we're fine here. Schema names get quoted, so PG_Check or Information_Schema just end up as normal schemas, and Postgres only reserves lowercase pg_ anyway. Created tables in both and they worked fine.
  2. column.default: yep, that's a real one, and it's broken from the UI too. A double precision default blows up with "cannot use column reference in DEFAULT expression", and a boolean default of true quietly becomes false. It's been there since the drivers were written, so I'd keep it out of this PR and tackle it separately in FF Tables: column defaults on real, double precision and boolean columns are built from the wrong variable #8793.

@andypalmi

Copy link
Copy Markdown
Contributor Author

@Steve-Mcl fix for the defaults is up in #8794

@andypalmi
andypalmi merged commit 3e4795d into main Oct 6, 2026
36 of 38 checks passed
@andypalmi
andypalmi deleted the feat/mcp-create-database-table branch October 6, 2026 10:02

This branch was successfully deployed

1 active deployment
staging — 3a6e940b Deployed Oct 6, 2026 by andypalmi via Remove application #12179
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants