Skip to content

MCP Server

Beacon exposes a Model Context Protocol (MCP) server that lets AI assistants (Claude, Cursor, Windsurf, custom agents) query your data sources, search your catalog, and access documentation — all through a standardized protocol.

The MCP server is project-centric: each API key is scoped to one or more projects, and all tools automatically resolve which data sources, schemas, and documentation to use based on the active project.

Project resolution is stateless — it is computed per call, with no session memory. If the key is restricted to a single project, that project is used automatically; if it can reach several, pass project_id on every call (the error lists the candidates when you don’t).

What you can do through MCP:

  • Ask natural language questions and get SQL + results back
  • Pull the exact grounding context Beacon uses for SQL generation — schemas with real sample values, verified join paths, human-verified examples — and write your own SQL (get_query_contextdry_runquery)
  • Validate SQL through every safety gate without executing it
  • Execute direct SQL queries against any data source in your project
  • Search tables, columns, and documentation by keyword, fused with embedding-based semantic matching when a local embedder is enabled
  • Retrieve AI-generated documentation for your project, data sources, or individual tables
  • Record feedback on answers — a correct verdict becomes a verified example that grounds future SQL generation

Go to API Keys in the React UI (/api-keys) and create a new key:

  • Choose a scope: Read, Execute, or Admin — the MCP endpoint requires Execute or Admin, because its tools can run SQL; Read keys are limited to the REST read API
  • Optionally restrict to specific projects
  • Copy the key — it’s shown only once (the key is SHA256-hashed before storage and never persisted in plaintext)

The key format is: sk-sem_...

Add Beacon to your MCP client configuration. The exact format depends on your client.

Claude Desktop (claude_desktop_config.json):

{
"mcpServers": {
"beacon": {
"url": "https://your-beacon-host/beacon/mcp",
"headers": {
"Authorization": "Bearer sk-sem_YOUR_API_KEY"
}
}
}
}

Cursor (.cursor/mcp.json):

{
"mcpServers": {
"beacon": {
"url": "https://your-beacon-host/beacon/mcp",
"headers": {
"Authorization": "Bearer sk-sem_YOUR_API_KEY"
}
}
}
}

Windsurf (.windsurf/mcp.json):

{
"mcpServers": {
"beacon": {
"serverUrl": "https://your-beacon-host/beacon/mcp",
"headers": {
"Authorization": "Bearer sk-sem_YOUR_API_KEY"
}
}
}
}

Once connected, your AI assistant can use the tools described below. Try asking:

“What tables are available in my project?”

“How many orders were placed last week?”

“Show me the schema for the customers table”

PropertyValue
Endpoint/beacon/mcp
TransportStreamable HTTP + JSON-RPC 2.0 (via ModelContextProtocol.AspNetCore)
AuthenticationRequired — Authorization: Bearer sk-sem_... header with Execute or Admin scope

The server is mounted with app.MapMcp("/beacon/mcp").RequireAuthorization(...) and enforces the Execute scope for API-key callers (§1.4). Clients exchange JSON-RPC messages with the single /beacon/mcp endpoint over the Streamable HTTP transport; the server streams responses back on the same connection.

Protocol niceties the server publishes:

  • The initialize response carries server instructions describing the recommended tool workflow, and every tool is published with a human-readable title and annotations (readOnlyHint, idempotentHint, destructiveHint, openWorldHint) so clients can reason about safety before calling.
  • query and ask responses include machine-readable structuredContent (columns, rows, row count, truncation flag — plus the generated SQL and signal_id for ask) alongside the markdown text, so agents don’t have to parse tables back out of prose.
  • Truncated results say so explicitly — row-capped query results, paged search results, and concise documentation exports all end with a note stating that more data exists and how to get it (raise max_rows, repeat with the next offset, or pass response_format: "detailed").

Remote MCP clients with a “connect by URL” flow — claude.ai (Settings → Connectors → Add custom connector), ChatGPT (developer mode connectors), VS Code (MCP: Add Server → HTTP) — need just one URL:

https://your-beacon-host/beacon/mcp

Authentication is a bearer token in the Authorization header — a Beacon API key with the Execute or Admin scope (Read keys cannot reach the SQL-executing tools). Paste the key wherever the client asks for a token/header; clients that probe the URL first will find the discovery documents below and learn the auth requirements automatically.

Beacon publishes anonymous, read-only discovery metadata so remote clients can bootstrap a connection without documentation:

EndpointWhat it serves
/.well-known/oauth-protected-resourceRFC 9728 protected-resource metadata: the resource identifier, scopes_supported (Execute, Admin), bearer-header auth. When SSO is enabled (Beacon:Authentication:Oidc), authorization_servers lists the configured OIDC authority; API-key-only deployments omit it
/.well-known/oauth-protected-resource/beacon/mcpThe same document at the RFC 9728 path-inserted variant clients derive from the /beacon/mcp resource path
/.well-known/mcp/server-card.jsonA server card (per the draft SEP-2127 proposal): server name and version, transport (streamable-http), endpoint URL, auth summary, and the full tool list with titles

Unauthenticated requests to /beacon/mcp answer 401 with a WWW-Authenticate: Bearer resource_metadata="…" header pointing at the metadata document, so OAuth-capable clients discover the auth requirements from the challenge itself (RFC 9728 §5.1).

Behind a reverse proxy, set Beacon:PublicBaseUrl (e.g. https://beacon.example.com) so the discovery documents advertise the public origin; when unset, the URLs are derived from the incoming request.

A registry manifest for the official MCP registry ships in deploy/registry/server.json with a publishing runbook alongside it — publishing is a deliberate out-of-band step (DNS verification of the namespace domain).

The MCP server exposes 8 tools that AI clients can call.

MCP Playground

Get an overview of the project: data sources, schemas, tables, quality scores, and documentation status. This is the recommended starting point for understanding what data is available.

ParameterTypeRequiredDescription
project_idintegerNoSpecify project if your API key has access to multiple projects

Example response (markdown):

# Project: E-Commerce Analytics
**Data Sources:** 2
**Documentation:** Generated
**Repositories:** 1
## Data Sources
### production-db (ID: 4)
- **Type:** PostgreSQL
- **Tables:** 45
- **Quality:** 87%
- **Code References:** 124
- **Schemas:** public (40 tables), audit (5 tables)
### analytics-api (ID: 7)
- **Type:** Api
- **Endpoints:** 12
- **Code References:** 9
- **Tags:** users (7 endpoints), orders (5 endpoints)

Ask a natural language question about your data. Beacon auto-detects the right data source(s), generates SQL, executes it, and returns results.

ParameterTypeRequiredDefaultDescription
questionstringYesNatural language question (e.g., “How many orders were placed last week?”)
project_idintegerNoSpecify project if needed
executebooleanNotrueSet to false to get the generated SQL without executing it

How it works:

  1. Routing phase — The LLM determines which data source(s) to query (skipped for single-source projects)
  2. SQL generation — Generates SQL using your actual schema as context
  3. Execution — Runs the query with safety guardrails (read-only, row limits, PII detection)

Cross-source queries: If your question spans multiple data sources, Beacon queries each source separately and joins results in an in-memory SQLite database.

Execute a direct SQL query against a specific data source. Use this when you already know the exact query you want to run.

ParameterTypeRequiredDefaultDescription
datasource_namestringNo*Name of the data source
datasource_idintegerNo*ID of the data source (alternative to name)
sqlstringNoSQL query (SELECT only) for database sources
api_querystringNoJSON query definition for REST API sources
max_rowsintegerNo100Maximum rows to return (max: 1000)
project_idintegerNoSpecify project if needed

*Either datasource_name or datasource_id is required.

For REST API data sources, pass a JSON query definition:

{
"method": "GET",
"path": "/api/users",
"parameters": { "limit": 10 },
"resultMapping": { ... }
}

Get the grounding context Beacon assembles internally for the ask tool, scoped to your question — so you (or your agent) can write well-grounded SQL instead of delegating generation to Beacon’s LLM. The context contains M-Schema table renderings with real sample values, join paths (verified foreign keys and inferred relationships kept apart), coverage notes when the table neighbourhood was capped, human-verified golden query examples, learned patterns from usage, and matching business-glossary terms.

ParameterTypeRequiredDefaultDescription
questionstringYesThe question you plan to answer with SQL — the context is retrieved and ranked against it
datasource_namestringNo*Name of the data source to ground against
datasource_idintegerNo*ID of the data source (alternative to name)
project_idintegerNoSpecify project if needed
max_charsintegerNo12000Maximum characters of context returned (min 1000, max 30000)

*If the project has exactly one data source it is auto-selected; with several, pass datasource_name or datasource_id (the error lists the candidates as id: name pairs).

The response opens with a header naming the data source and SQL dialect, followed by the grounding context. Sections marked (authoritative) are human-verified. When the context exceeds max_chars it is cut at the last complete section and ends with an explicit truncation note. structuredContent carries data_source_id, data_source, dialect, and truncated.

Intended workflow: get_query_context → write your own SQL → dry_run to validate → query to execute. Use this instead of ask when your agent wants full control over the SQL it runs.

Validate a SQL query through all of Beacon’s safety gates without executing it. Use before query to catch problems for free.

ParameterTypeRequiredDescription
datasource_namestringNo*Name of the data source to validate against
datasource_idintegerNo*ID of the data source (alternative to name)
sqlstringYesThe SQL query to validate (SELECT only)
project_idintegerNoSpecify project if needed

*Either datasource_name or datasource_id is required.

The four gates, in order:

  1. guardrail — the regex read-only backstop plus PII detection (reports the columns that would be masked)
  2. ast — dialect-aware AST parse rejecting DML/DDL, stacked queries, and comment-hidden writes (skipped only when read-only enforcement is disabled)
  3. schema — schema-catalog column check that catches hallucinated tables/columns without a database round-trip
  4. provider_dry_run — the database’s own validation (EXPLAIN / sp_describe_first_result_set); runs only when every earlier gate passed. Engines with no dry-run strategy (SQLite and friends) report the gate as skipped rather than silently passing, and the verdict is invalid with an advisory issue — a validation that never ran is never counted as a pass

Gate issues are collected rather than first-failure-wins, so one call reports everything to fix. The response gives a per-gate verdict and, when valid, the exact SQL that would execute with the row limit applied. structuredContent carries the machine-readable verdict: { valid, issues: [{gate, error}], executable_sql, pii_columns }. An INVALID verdict is still a successful tool call — isError is reserved for resolution failures.

Retrieve AI-generated documentation at three levels of detail.

ParameterTypeRequiredDescription
project_idintegerNoSpecify project if needed
datasource_namestringNoGet docs for a specific data source
table_namestringNoGet detailed docs for a specific table or API endpoint
schema_namestringNoSchema name or API tag (optional qualifier for table_name)
response_formatstringNoconcise (summary sections) or detailed (everything). Project level defaults to concise; data-source and table level default to detailed

Three levels:

  1. Project level (no parameters) — Full generated project documentation. Defaults to concise: the export is cut at ~8,000 characters on a line boundary with an explicit truncation note; pass response_format: "detailed" for the full document
  2. Data source level (datasource_name only) — Tables, schemas, code references, quality scores. Defaults to detailed; concise omits the LLM schema context section
  3. Table level (table_name) — Columns with types, relationships, quality rules; detailed (the default) adds code references and lineage, concise omits them

Search tables, columns, and documentation across all data sources in the project. Keyword matching is fused with embedding-based semantic matching (reciprocal rank fusion) when a local embedder is configured; without one, search falls back to keyword-only rather than failing.

ParameterTypeRequiredDefaultDescription
querystringYesSearch keyword (e.g., “customer”, “order_date”, “revenue”)
project_idintegerNoSpecify project if needed
max_resultsintegerNo20Maximum results to return (max: 50)
offsetintegerNo0Result offset for paging — use with max_results to page through large result sets

Each result line includes the item type ([TABLE], [COLUMN], [DOC]), the data source and schema-qualified table (plus the column name for column hits), and the description, ordered by relevance. When more matches exist beyond the current page, the response ends with an explicit note giving the offset to request next.

Record whether a previous ask answer was correct. A correct verdict is saved as a verified example (golden pair) that grounds future SQL generation for the same data source.

ParameterTypeRequiredDescription
signal_idintegerYesThe signal_id from the ask response you are rating
verdictstringYescorrect or incorrect
corrected_sqlstringNoThe corrected SQL, if you fixed it
notestringNoA short note

Every ask response that ran a data query ends with a _signal_id: N_ marker — pass that value back here. Conceptual answers from the knowledge base don’t record a query signal and carry no marker.

The MCP server enforces several safety measures:

FeatureDescriptionDefault
Read-only enforcementOnly SELECT queries are allowedEnabled
Row limitsMaximum rows returned per query100 (single), 500 (cross-source), max 1000
PII detectionAutomatically detects and flags sensitive data patternsEnabled
Query timeoutQueries are cancelled after 30 secondsAlways on
Audit loggingEvery tool call is recorded by McpAuditService with user, timing, and parametersAlways on
Usage signalsask, query, and dry_run calls are recorded by McpSignalService to feed the learning loopAlways on

McpAuditService fires on every tool invocation, including the failure path — it is never short-circuited. McpSignalService records the three SQL-carrying tools (ask, query, dry_run); the catalog tools (get_context, search, get_documentation, get_query_context) are audit-only by design.

Natural-language questions through the ask tool don’t go through a naive prompt-to-SQL pipe. Every generated query passes a layered accuracy stack:

  1. Grounded context — before generation, Beacon assembles an M-Schema rendering (column name, type, nullability, description) including real sample values so filters match actual data formats ('shipped' vs 'SHIPPED'), plus curated join paths, matching business-glossary terms, human-verified query examples, and lessons mined from past usage. See the Knowledge Base guide for what goes in and how you curate it.
  2. AST read-only validation — generated SQL is parsed into an abstract syntax tree with a dialect-aware parser (PostgreSQL, SQL Server, MySQL, BigQuery, Snowflake, Databricks). DML/DDL statements, stacked queries, and comment-hidden writes are rejected before anything reaches your database — defense-in-depth beyond the regex guardrail.
  3. Database read-only backstop — on PostgreSQL, MCP-executed SQL additionally runs inside a READ ONLY transaction, so a write that somehow slipped past both parsers is still refused by the database itself. Other engines rely on the parser gates and connector-level enforcement.
  4. Dry-run repair loop — if execution fails, the SQL is retried with the database error and a refreshed schema context; truncated or degenerate repairs are rejected rather than executed.
  5. Row limits & PII masking — results are capped and sensitive values (emails, phone numbers, SSNs, credit cards, tokens, and custom regex patterns) are masked (a***z) before leaving the server.

The MCP server is self-improving. McpSignalService records a usage signal for every SQL-carrying call — the question, the generated SQL, the tables and columns referenced, the routing decision, and the outcome. Recurring background jobs turn those signals into knowledge that grounds future generation:

MCP Learning

JobScheduleWhat it does
mcp-learning-aggregateEvery 6 hoursMines ask / query signals into learned patterns, retires stale ones, and runs replay verification
mcp-learning-cleanupDaily 03:00Drops signals past the retention window
mcp-embedding-reindexEvery 12 hoursRe-embeds schema metadata and exemplars
mcp-docchunk-reindexEvery 12 hoursRe-chunks and re-embeds project documentation

Two things are worth knowing about how patterns get promoted:

  • A candidate pattern is not trusted on a confidence score alone. Before promotion, Beacon replays the project’s golden cases with and without the candidate injected, and keeps it only if it flips at least one case from failing to passing.
  • dry_run signals are recorded for analytics but excluded from mining — their “question” is SQL, not natural language, and gate rejections would skew the failure statistics.

Review the queue on the MCP Learning page (/mcp-learning), where pending patterns and proposed documentation patches can be approved or rejected. The full mechanics — lesson extraction, decay, retention, and every setting — are in the Knowledge Base guide.

Administrators can customize the MCP server behavior at MCP Settings in the React UI (/mcp-settings), organised into four tabs:

Pre-prompt

  • Ask tool system prompt — the LLM prompt used for SQL generation in the ask tool
  • Global instruction — prepended to the user context of every LLM-aware tool

Tool descriptions

  • Override the description returned by tools/list for get_context, ask, query, get_documentation, and search. Leave a field blank to keep the built-in description. Overrides are applied to the live tool list, so connected clients see your wording. dry_run, get_query_context, and feedback currently use their built-in descriptions only.

Guardrails

  • Max row limit — the ceiling on rows returned (default: 1000)
  • Read-only enforcement — toggle the SELECT-only restriction
  • PII detection — enable/disable, plus custom PII regex patterns
  • Learning — master switch, auto-approve threshold, injection budget, and signal retention window

Context preview

  • Render the grounding context for a project exactly as the tools would assemble it — useful for spotting missing documentation before an agent hits it.

Changes take effect without a restart. These settings are part of Admin Settings; the retrieval, exemplar, and eval knobs that aren’t on this page are settable through PUT /beacon/api/mcp/settings and documented in the Knowledge Base guide.

You don’t need to wire up an external MCP client to try the server. The React UI ships an MCP Playground (/mcp-playground) where you select a project and ask questions interactively — the same tools, routing, guardrails, and audit trail as a real MCP session. Use it to validate documentation quality and tune the system prompt before pointing Claude or Cursor at your data.

The companion MCP Learning page (/mcp-learning) shows the learning loop at work: usage signals, success rate, learned schema patterns awaiting approval, and proposed documentation patches you can apply or reject.

If your MCP client doesn’t support Streamable HTTP configuration natively, you can drive the protocol manually by POSTing JSON-RPC messages to the single /beacon/mcp endpoint. Every request carries the Authorization: Bearer header.

Terminal window
curl -X POST "https://your-host/beacon/mcp" \
-H "Authorization: Bearer sk-sem_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"clientInfo": { "name": "my-client", "version": "1.0" }
}
}'
Terminal window
curl -X POST "https://your-host/beacon/mcp" \
-H "Authorization: Bearer sk-sem_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "id": 2, "method": "tools/list"}'
Terminal window
curl -X POST "https://your-host/beacon/mcp" \
-H "Authorization: Bearer sk-sem_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_context",
"arguments": {}
}
}'

The server streams the JSON-RPC response back on the same connection.

“No project found” error — Your API key must be associated with at least one project. Check API key settings.

“Multiple projects available” error — Your API key has access to multiple projects. Pass project_id in your tool calls, or restrict the API key to a single project.

“Query validation failed” error — The query contains write operations (INSERT, UPDATE, DELETE) which are blocked by read-only enforcement. Only SELECT queries are allowed.

Connection drops or timeouts — Streaming connections may be interrupted by proxies or load balancers. Reconnect by re-initializing against /beacon/mcp — a fresh session will be created.

Authentication fails — Verify your API key starts with sk-sem_, hasn’t expired, and hasn’t been revoked. Check the Authorization: Bearer header format.

Answers miss obvious joins or misread a business term — the model can only use what it was given. Check the Knowledge Base guide: register the join path, add the glossary term, or promote a correct answer to a golden example.