Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,13 +208,30 @@ curl -X POST http://localhost:8080/query \

# Check status
curl http://localhost:8080/status

# Plain-text / CSV / TSV output for terminals and unix pipes
curl -X POST "http://localhost:8080/query?format=text" -d "SELECT name, size FROM funcs LIMIT 5"
curl -X POST "http://localhost:8080/query?format=csv" -d "SELECT name, size FROM funcs LIMIT 5"
```

**Response Format:**
**Response Format:** JSON by default — a script envelope (single statement = array of one):
```json
{"success": true, "columns": ["name", "size"], "rows": [["main", "500"]], "row_count": 1}
{"success": true, "statement_count": 1,
"results": [{"statement_index": 0, "success": true,
"columns": ["name", "size"], "rows": [["_main", "2851"]],
"row_count": 1, "elapsed_ms": 8, "error": null}],
"row_count_total": 1, "elapsed_ms_total": 8, "first_error_index": null}
```

**Output formats** (query-string `format=`, default `json`):

| `format` | Content-Type | Use |
|----------|--------------|-----|
| `json` (default) | `application/json` | Programmatic use — **agents should consume this**, not a reformatted view |
| `text` | `text/plain` | Ready-to-read ASCII table for terminals |
| `csv` | `text/csv` | Spreadsheets / RFC-4180 CSV |
| `tsv` | `text/tab-separated-values` | Unix pipes (`cut`/`awk`/`sort`) |

### MCP Server (Model Context Protocol)

For integration with Claude Desktop, Cursor, and other MCP-compatible AI tools:
Expand Down
24 changes: 21 additions & 3 deletions src/common/bnsql_http_routes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,9 @@ Response Format (canonical script envelope, single = array of one):
Splitter failure (e.g. unterminated quote):
{"success": false, "statement_count": 0, "results": [],
"parse_error": "<message>", ...}
Options (query string): continue_on_error=1, include_sql=1
Options (query string): continue_on_error=1, include_sql=1,
format=json|text|csv|tsv (default json; text/csv/tsv
are for terminal/pipe use, agents should consume json)

Authentication (if enabled):
Header: Authorization: Bearer <token>
Expand Down Expand Up @@ -322,6 +324,13 @@ inline void setup_http_routes(
if (incl_it != req.params.end() && incl_it->second == "1") {
opts.include_sql = true;
}
// Optional output format (default json). text/csv/tsv are for direct
// terminal/curl use; json stays the canonical machine format.
std::string format = "json";
auto fmt_it = req.params.find("format");
if (fmt_it != req.params.end() && !fmt_it->second.empty()) {
format = fmt_it->second;
}

auto script = xsql::run_script(sql, opts,
[&](const std::string& stmt, xsql::ScriptStatementResult& out) {
Expand All @@ -338,8 +347,17 @@ inline void setup_http_routes(
query_mutex->unlock();
queue_count->fetch_sub(1);

res.set_content(xsql::script_result_to_json(script, opts.include_sql),
"application/json");
if (format == "text") {
res.set_content(xsql::script_result_to_text(script), "text/plain");
} else if (format == "csv") {
res.set_content(xsql::script_result_to_csv(script), "text/csv");
} else if (format == "tsv") {
res.set_content(xsql::script_result_to_tsv(script),
"text/tab-separated-values");
} else {
res.set_content(xsql::script_result_to_json(script, opts.include_sql),
"application/json");
}
});

// GET /status - Health check with runtime settings
Expand Down
Loading