Skip to content

feat: support multiple allowed schemas per configuration - #4

Open
jdainsworthsnb wants to merge 5 commits into
0xhubed:mainfrom
jdainsworthsnb:multi-schema
Open

jdainsworthsnb wants to merge 5 commits into
0xhubed:mainfrom
jdainsworthsnb:multi-schema

Conversation

@jdainsworthsnb

Copy link
Copy Markdown

Summary

Adds support for exploring multiple schemas from a single project configuration, plus three bug fixes that surfaced while testing the change against a real SQL Server 2022 database.

Multi-schema configuration

{
  "allowedSchemas": ["dbo", "sales"]
}
  • allowedSchemas is a list. The legacy single-string allowedSchema still works and is treated as a one-item list; if both are present the list wins.
  • The first entry is the default schema for unqualified table names in write operations and the query builder.
  • Every tool that takes a table name now accepts table or schema.table. A bare name that exists in more than one allowed schema is rejected with a message listing the candidates, so the model never silently picks the wrong table.
  • Exploration tools (GetSchemaInfo, GetTableInfo, DiscoverData, AnalyzeTableRelationships, ProfileDataQuality, GetDatabasePerformanceStats) span every allowed schema and carry a schema field per row.
  • A disallowed schema qualifier returns Access to schema 'x' is not allowed. Allowed schemas: dbo, sales.

All of this lives in a new Services/SchemaScope.cs: parsing of table references, allow-list enforcement, parameterised IN (...) filter construction for catalog queries, and bare-name resolution across schemas.

Bug fixes (pre-existing, found during live testing)

  • p.rows AS RowCount fails because ROWCOUNT is a reserved word in SQL Server. Bracketed in DiscoverData and GetDatabasePerformanceStats.
  • STRING_SPLIT requires database compatibility level 130+, so DiscoverData and AnalyzeTableRelationships failed on older-compat databases. Replaced with parameterised IN lists built by the same helper.
  • GetSchemaInfo with a table name kept the column DataReader open while issuing the foreign-key query on the same connection, failing with "There is already an open DataReader associated with this Connection".

Tests

Adds an xunit project (McpMsSqlServer.Tests, wired into the solution) covering config deserialization and fallback, case-insensitive allow-list checks, table-reference parsing, and filter construction. 25 tests, all passing. Commits are in TDD order (failing tests first).

Verified live against a SQL Server 2022 database with two allowed schemas of 442 and 417 tables: cross-schema listing and discovery, the ambiguity error for a table present in both schemas, qualified lookup, and the disallowed-schema error.

Known limitation

Dependency-tree walking in AnalyzeTableRelationships stays within the starting table's schema (unchanged from upstream); cross-schema FK chains are not followed.

🤖 Generated with Claude Code

https://claude.ai/code/session_011p298cqnP5d4SztyCTk46C

jdainsworthsnb and others added 5 commits October 21, 2026 09:49
Covers allowedSchemas list with legacy allowedSchema fallback,
case-insensitive IsSchemaAllowed over the list, and the SchemaScope
helper for parsing schema.table references and building IN filters.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011p298cqnP5d4SztyCTk46C
Adds an allowedSchemas list to DatabaseConfig. The legacy allowedSchema
string is kept as a one-item alias; the first entry is the default for
unqualified table names.

New Services/SchemaScope.cs owns parsing of "table" / "schema.table"
references, allow-list enforcement, parameterised IN filters for catalog
queries, and resolution of bare names across schemas (ambiguous names
are rejected with the candidate list).

Exploration tools (GetSchemaInfo, GetTableInfo, DiscoverData,
AnalyzeTableRelationships, ProfileDataQuality, performance stats) now
span every allowed schema and report a schema field per row. Write tools
and the query builder accept schema.table and default to the primary
schema.

Known limitation (pre-existing): dependency-tree walking stays within
the starting table's schema; cross-schema FK chains are not followed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011p298cqnP5d4SztyCTk46C
SQL Server treats ROWCOUNT as a keyword, so `p.rows AS RowCount` fails
with "Incorrect syntax near the keyword 'RowCount'" in DiscoverData and
GetDatabasePerformanceStats. Pre-existing upstream bug surfaced during
live testing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011p298cqnP5d4SztyCTk46C
STRING_SPLIT requires database compatibility level 130+, and the
discovery tools failed with "Invalid object name 'STRING_SPLIT'" on an
older-compat database. SchemaScope.BuildInFilter now builds the table
name predicates the same way the schema filter is built. Pre-existing
upstream bug surfaced during live testing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011p298cqnP5d4SztyCTk46C
…Info

GetTableSchema kept the column DataReader open (method-scoped using var)
while issuing the foreign-key query on the same connection, failing with
"There is already an open DataReader associated with this Connection".
Pre-existing upstream bug surfaced during live testing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011p298cqnP5d4SztyCTk46C
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.

1 participant