You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(mcp-server): implement read-only SQL Server tools (M6)
Build out CloudEngAgent.Mcp.Server as a working MCP server exposing
four read-only SQL Server introspection tools over MCP/HTTP at /mcp:
- list_databases -> sys.databases (system DBs filtered)
- list_tables -> INFORMATION_SCHEMA.TABLES
- describe_table -> INFORMATION_SCHEMA.COLUMNS
- sample_rows -> SELECT TOP (n) * FROM <quoted>; (n capped at 100)
Safety model:
- All values flow through SqlParameter; identifiers are allow-listed
(^[A-Za-z_][A-Za-z0-9_]{0,127}\$) and verified against
INFORMATION_SCHEMA before being bracket-quoted.
- Errors return McpException with friendly messages; SqlException
details are logged but never leaked through MCP.
- Connection strings bind from Mcp:SqlServer:ConnectionStrings; the
server still boots with no DBs configured (tools then return
InvalidParams 'no connection configured').
Tests (CloudEngAgent.Mcp.Server.Tests):
- SqlIdentifier unit tests (allow-list / injection rejection / quoting).
- Testcontainers MsSql integration tests for all four tools, skipped
automatically when 'docker info' fails (Windows-without-Docker safe).
- WebApplicationFactory health-endpoint test that boots the server
with no SQL config to confirm the DI graph stays optional.
README: new 'MCP Server (M6)' section + roadmap mark.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy file name to clipboardExpand all lines: README.md
+62-1Lines changed: 62 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -251,6 +251,67 @@ Selection is driven by `WorkflowEngine:Mode`:
251
251
252
252
Multi-agent orchestration via `Microsoft.Agents.AI.Workflows` (orchestrator → {explorer, analyst, …}) is the M5.2 follow-up; the M5.1 slice runs the workflow's entry persona as a single agent.
253
253
254
+
## MCP Server (M6)
255
+
256
+
`CloudEngAgent.Mcp.Server`is an ASP.NET Core 10 host that exposes a small set of **read-only** SQL Server introspection tools over the [Model Context Protocol](https://modelcontextprotocol.io). It is the server side of the MCP integration; the API project consumes it through the `IMcpToolRegistry` HTTP client (M7).
257
+
258
+
### Run locally
259
+
260
+
```powershell
261
+
dotnet run --project src\CloudEngAgent.Mcp.Server
262
+
```
263
+
264
+
The server listens on a Kestrel-assigned port and exposes:
| ANY | `/mcp` | MCP endpoint (`Streamable HTTP` transport from `ModelContextProtocol.AspNetCore`). |
270
+
271
+
### Configuration
272
+
273
+
Connection strings live under `Mcp:SqlServer:ConnectionStrings`. Each entry maps a logical database name (the value MCP callers pass as the `database` argument) to a SQL Server ADO.NET connection string. The first non-empty entry is used when callers omit `database`.
If no connection strings are configured the server still boots (and `/healthz` returns OK), but invoking any tool returns a structured `InvalidParams` error so callers get a clear "no connection configured" message.
| `sample_rows` | `schema`, `table`, `top` (1–100, default 10), `database?`| Up to N rows as `{column → value}` dictionaries. `SELECT TOP (n) * FROM ...`. |
306
+
307
+
### Safety model
308
+
309
+
- All tool **values** are sent as `SqlParameter` instances — never concatenated into SQL.
310
+
- All tool **identifiers** (schema/table) must pass a strict allow-list (`^[A-Za-z_][A-Za-z0-9_]{0,127}$`) **and** be present in `INFORMATION_SCHEMA` before being bracket-quoted and interpolated. Anything else is rejected up-front with `InvalidParams`.
311
+
- `sample_rows`enforces a hard cap of 100 rows regardless of the requested `top`.
312
+
- `SqlException`details are logged in full but only the SQL `Number` + the first line of the message are returned to the client; stack traces never leak through MCP.
313
+
- All tools are **read-only** — there is no DDL/DML surface.
314
+
254
315
## API surface (v1)
255
316
256
317
| Method | Route | Description |
@@ -339,7 +400,7 @@ See the in-session plan for the full P0/P1/P2 backlog. Milestones:
339
400
- **M4 (YAML personas + hot reload)**: ✅ Complete — see the "Personas (M4)" section above.
340
401
- **M5.1 (single-agent real LLM execution)**: ✅ Complete — see the "Workflow engine (M5.1)" section above.
341
402
- **M5.2** (multi-agent graph orchestration via `Microsoft.Agents.AI.Workflows`)
342
-
- **M6** (MCP SQL server)
403
+
- **M6 (MCP SQL server)**: ✅ Complete — see the "MCP Server (M6)" section above.
343
404
- **M7** (MCP client wiring)
344
405
345
406
Forward-looking features (P3) include resumption, multi-tenancy, persona overlays, a workflow DSL, human-in-the-loop tool approval, cost/token telemetry, and saved investigations.
/// <see cref="McpException"/> with <see cref="McpErrorCode.InvalidParams"/>
55
+
/// when the identifier fails <see cref="IsValid"/>.
56
+
/// </summary>
57
+
publicstaticstringQuote(string?identifier)
58
+
{
59
+
if(!IsValid(identifier))
60
+
{
61
+
thrownewMcpException(
62
+
$"Invalid SQL identifier '{identifier}'. Only ASCII letters, digits, and underscores are allowed (must start with a letter or underscore, max {MaxLength} chars).",
63
+
McpErrorCode.InvalidParams);
64
+
}
65
+
66
+
// Identifier is already constrained to [A-Za-z0-9_], so no embedded
67
+
// ']' is possible — bracket quoting is sufficient. Defense-in-depth:
68
+
// double any ']' anyway in case the validation surface ever changes.
0 commit comments