query-context
Run a read-only Cypher query against the project's context graph. Needs the fabric:query.read grant.
curl -sS -X POST https://fabric.wexa.ai/mcp/$FABRIC_PROJECT_ID \
-H "authorization: Bearer $FABRIC_API_KEY" \
-H "Mcp-Session-Id: $MCP_SESSION_ID" \
-H "content-type: application/json" -H "accept: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query-context","arguments":{"query":"MATCH (n) WHERE n.project_id = $project_id RETURN n LIMIT 1"}}}'{
"content": [
{
"type": "text",
"text": "{\"lifecycle_id\":\"qlc_000167\",\"result\":{\"columns\":[\"n\"],\"row_count\":1,\"rows\":\"\\u2026\",\"stats\":{\"elapsed_ms\":240},\"truncated\":false}}"
}
],
"isError": false
}Parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
query | string | Required | Read-only Cypher; must filter on project_id = $project_id. Write clauses are rejected. |
parameters | object | Optional | Values the query refers to as $name; see passing values. Never project_id or project_ids. |
limit | integer | Optional | Sets the LIMIT added when the query has none and caps any LIMIT it has. 1 or more; above 1000 is reduced to 1000. Without a LIMIT the server adds LIMIT 100. |
timeout_ms | integer | Optional | Query timeout in ms. A call has about 25 seconds in total, so values above about 25000 have no effect. |
explain | boolean | Optional | Return the query plan instead of rows. |
explain_denials | boolean | Optional | When your access profile hid rows or masked fields, also return a governance block naming them and the clearance each needed (never values). |
Errors
UpstreamError(504): the call ran past about 25 seconds. See rows and time.
Show full response
A real response, captured from a live call to query-context:
rows came back populated and is shown as … here, because the real value runs to
thousands of characters. Read it from your own call rather than from this page.
{
"content": [
{
"type": "text",
"text": "{\"lifecycle_id\":\"qlc_000167\",\"result\":{\"columns\":[\"n\"],\"row_count\":1,\"rows\":\"\\u2026\",\"stats\":{\"elapsed_ms\":240},\"truncated\":false}}"
}
],
"isError": false
}More details
Full description from the tool, as the gateway advertises it:
Run a read-only Cypher query against this project's Wexa context graph. Queries must filter on project_id = $project_id (bound server-side). Write clauses are rejected. For CODE TEXT search (symbols, strings, file contents), use search-code then fetch-code — do NOT invent Cypher for that. query-context is for structural graph queries (callers, imports, ontology). For other large string properties (diffs, notes), prefer CALL db.index.fulltext.queryNodes('idx_codetext_fulltext', '\<terms\>') YIELD node, score over CONTAINS/regex.
Which projects a query reads
| Filter | Reads |
|---|---|
n.project_id = $project_id | This project, plus the projects that org-shared connectors and data sources shared with this project write into |
n.project_id IN $project_ids | Every project you can access |
The server binds both from your credential. On servers built before 10 October 2026, = $project_id also
matched every other project you can access; to be sure of one project there, check project_id in the rows
you get back. The map form (n {project_id: $project_id}) reads only this project. Do not put project_id
or project_ids in parameters: a project_id other than your own is refused.
Passing values: parameters
rows = fabric.query_context(
query="""
MATCH (t:Ticket)
WHERE t.project_id = $project_id
AND t.status IN $statuses
AND t.due >= $from AND t.due < $to
RETURN t.id AS id, t.due AS due
ORDER BY due
LIMIT 200
""",
parameters={"statuses": ["open", "blocked"], "from": "2026-10-15", "to": "2026-10-29"},
)Refer to each value as $name instead of pasting it into the query text. Range comparisons (>=, <, …)
work on any property. Values are stored exactly as save-context received them: a number stays a number, and a
date sent as text stays text. Text compares character by character, so a range filter on a date stored as
text only works when the date is ISO 8601 (2026-10-15); 10/15/2026 sorts wrongly across months and years.
Rows and time
- Rows: without a
LIMITthe server addsLIMIT 100, and aLIMITabove 1000 is reduced to 1000. Thelimitargument sets theLIMITadded when the query has none and caps anyLIMITit has (1 or more; above 1000 it is reduced to 1000). - Time: a call has about 25 seconds in total; past that it answers
504(UpstreamErrorin both SDKs).timeout_mscan only shorten that, so values above about 25000 have no effect.
The captured call returned one row, so the example shows rows as "\u2026", the value the capture holds in place of the row itself.
Marked consequential: it is audited in full, and a policy rule may hold it for a person to approve. See Policy and approvals.
Every tool call works the same way: How every tool call works.