About indykite-ciq-read
An agent skill for creating read-only IndyKite ContX IQ policies and Knowledge Queries over the IndyKite Graph. It guides schema setup, execution, and result checks for teams retrieving graph data without enabling writes.
Key features
- 01Define Cypher patterns and subject filters
- 02Whitelist readable graph variables
- 03Create read-only Knowledge Queries
- 04Execute queries with parameters and paging
- 05Check returned rows and troubleshoot
Use cases
- 01Retrieve a person's related graph records
- 02Prepare queries for the IndyKite MCP server
- 03Debug empty or forbidden query results
MCP support
The skill explains that a Knowledge Query created with it can be run through the `indykite-mcp-server` `ciq_execute` tool. It also covers execution through the ContX IQ HTTP endpoint.
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ indykite/ skills --skill indykite-ciq-readAfter installing, type/indykite-ciq-readin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
assets/knowledge-query-read-cars.jsonpolicy-read-cars.jsonreferences/execution-reference.mdknowledge-query-reference.mdpolicy-reference.mdscripts/execute.shDetails
From the project READMEIndyKite ContX IQ - read-only policy + Knowledge Query
ContX IQ (CIQ) is IndyKite's context-aware data layer over the IKG — IndyKite's knowledge graph, a property-graph database queried with Cypher (the Neo4j / openCypher graph query language). A CIQ policy declares what graph elements may be touched and under what conditions; a CIQ Knowledge Query declares what to do with them; an execution call runs the Knowledge Query at runtime with concrete parameter values.
This skill covers the read-only path:
- A policy whose
condition.cyphermatches nodes and relationships and whoseallowed_readswhitelists the variables the Knowledge Query may return. - A Knowledge Query that lists those variables in
nodes,relationships, and/oraggregate_values. - An
executecall that supplies values for the policy's partial filters ($variable) and returns the rows.
allowed_upserts and allowed_deletes are intentionally out of scope here - leave them out entirely for read-only use.
When to use
Activate this skill when the user:
- wants to read data from the IKG and shape the result through a CIQ policy + Knowledge Query;
- is building the
(workflow, agent_list)query theindykite-agent-gatewayskill consumes from ContX IQ; - is preparing a Knowledge Query that the
indykite-mcp-serverskill will run viaciq_execute; - is debugging a read CIQ that returns nothing or
403s for a subject that should have access.
Do not activate this skill when the user:
- needs to create, update, or delete nodes or relationships through CIQ - that uses
allowed_upserts/allowed_deletesandupsert_*/delete_*Knowledge Query fields, which this skill leaves out for clarity; - is calling AuthZEN for a yes/no authorization decision (use
authzen_evaluateand the MCP skill); - is calling the External Data Resolver to fetch data from an external API at query time (different feature).
Prerequisites
- An IndyKite project, an AppAgent, and AppAgent credentials (the token that goes into
X-IK-ClientKeyat execution time). - A Service Account token with Config API access, and the project's GID in
PROJECT_GID(both used to create the policy and Knowledge Query). - The IKG already populated with the nodes and relationships the policy will match. CIQ only filters and projects; it does not seed data.
- A subject type to authenticate against -
Person,User,_Application, etc. CIQ policies are restricted to a single subject type, so if you need two subjects, plan for two policies.
If any of these are missing, stop and tell the user - fixing them first is much cheaper than debugging an opaque CIQ rejection.
Steps
1. Pick the subject and the Cypher pattern
Subject type - pick one. The schema is identical across both choices; only subject.type, the filter, and the execute-time auth differ:
| Subject | Use when | Auth at execute time | Filter convention |
|---|---|---|---|
_Application | System-side / ETL / catalog work; no user in the loop. | X-IK-ClientKey only. | subject.external_id = $_appId (reserved). |
Person / User | The authenticated user is performing the operation themselves. | X-IK-ClientKey + Authorization: Bearer <token>. | subject.external_id = $token.sub. |
A policy is restricted to a single subject type - if both should be allowed, write two policies. The subject's variable in cypher is conventionally named subject. The runnable example below uses Person; an _Application variant - for example, a service reading the catalog - differs only in subject.type, the filter, and the execute headers.
Cypher pattern - the MATCH / OPTIONAL MATCH clauses naming every node and relationship the query will touch. Each one must have a variable name so the policy and Knowledge Query can reference it. If the exact node types, relationship types, or property spellings in the project's IKG are unknown, read them from the Data Schema API first (indykite-data-schema) - a typoed name silently matches nothing.
Working example used throughout this skill:
A
Person(subject)OWNSCars. Given a person'sexternal_id, return the cars they own.
MATCH (subject:Person)-[r:OWNS]->(car:Car)
Variables: subject, r, car.
2. Author the read-only CIQ policy
Build the policy JSON. For a read-only policy you need three things and only three things:
meta.policy_version- currently1.0-ciq.subject.type- the chosen subject type.condition.cypherand (optionally)condition.filter- the pattern and any filters. Use$varnameto mark partial filters that will be supplied at execution time. A third, optionalcondition.skip_resource_labelexists for a condition that stays slow after it is anchored and bounded - see the policy reference before using it.allowed_reads- list every variable the Knowledge Query will be allowed to return. Use<var>.*to allow all properties of a node/relationship, or<var>.property.<name>for a single property.
Skip allowed_upserts and allowed_deletes entirely - omitting them is the supported way to forbid writes.
A complete read-only policy for the running example: see assets/policy-read-cars.json.
Create it through the Config API:
# set the current project_id, and stringify only the `policy` field, before POSTing
jq --arg pid "$PROJECT_GID" '.project_id = $pid | .policy |= tojson' assets/policy-read-cars.json \
| curl -X POST "$API_URL/configs/v1/authorization-policies" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $SERVICE_ACCOUNT_TOKEN" \
-d @-
A 201 Created returns the policy's id (GID). Export it as POLICY_ID - the Knowledge Query create injects it into policy_id.
For the full schema (every operator, every attribute pattern, what token_filter is for) see references/policy-reference.md.
3. Create the Knowledge Query
A read-only Knowledge Query references the policy and lists what to return. The four read-relevant fields are:
nodes- node variables (or<var>.property.<name>) to include in the response.relationships- relationship variables to include.aggregate_values- variables produced by aggregate functions incypher(e.g.COLLECT(...) AS xs→"xs").batch_read- set totrueonly when you expect a result set big enough to risk the default timeout; raises the timeout to 5 minutes.
A complete Knowledge Query for the running example: see assets/knowledge-query-read-cars.json.
Create it through the Config API (with POLICY_ID set to the policy's GID from the previous step):
# set the current project_id and policy_id, and stringify only the `query` field, before POSTing
jq --arg pid "$PROJECT_GID" --arg polid "$POLICY_ID" '.project_id = $pid | .policy_id = $polid | .query |= tojson' assets/knowledge-query-read-cars.json \
| curl -X POST "$API_URL/configs/v1/knowledge-queries" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $SERVICE_ACCOUNT_TOKEN" \
-d @-
A 201 Created returns the Knowledge Query's id (GID). This is what execute (and the MCP ciq_execute tool) will reference.
Schema details for every Knowledge Query field, including upsert_* and delete_* (omitted for the read case): references/knowledge-query-reference.md.
4. Execute the query
The execute endpoint runs the Knowledge Query at runtime with concrete parameter values:
POST <API_URL>/contx-iq/v1/execute
Authentication:
- Always:
X-IK-ClientKey: <AppAgent-credentials-token>. - If
subject.typeis not_Application: alsoAuthorization: Bearer <user-access-token>. The token'ssubis the subject identifier. To see the exacttype/ida token resolves to, callGET /contx-iq/v1/whoami(indykite-ciq-whoami). - If
subject.typeis_Application: the reserved$_appIdparameter is auto-filled from the application'sexternal_id; do not pass it ininput_params.
Request:
{
"id": "<knowledge_query_gid_or_name>",
"input_params": {
"person_external_id": "alice"
}
}
A runnable shell helper: scripts/execute.sh.
Large result sets page with two optional body fields: page_size (rows per page, default 100) and page_token, a 1-based page number (1 or omitted is the first page, 2 the next). The response carries no token, so increment the number yourself and stop at the first page shorter than page_size. The helper sends the first page; add the two fields to the raw body for the others. The MCP ciq_execute tool takes the same two optional arguments (indykite-mcp-server).
The full execute reference (auth combinations, response shape, paging, error semantics) lives in references/execution-reference.md.
5. Read the response and verify
The response shape is:
{
"data": [
{ "nodes": { "<var>.<attr>": "<value>", ... } },
{ "relationships": { ... } }
]
}
One row per match, with nodes keyed <var>.<attr> and relationships keyed similarly. If the policy or Knowledge Query whitelists a variable but the IKG has nothing matching, the data array is simply empty.
If the response is not what you expected, walk through this check list before changing the policy:
- Variable in the response? It must appear in both the policy's
allowed_reads.nodes/relationships/aggregate_valuesand the Knowledge Query'snodes/relationships/aggregate_values. The intersection is what gets returned. - Filter actually firing? A typo in
attribute(e.g.subject.external_idvs.person.external_id) silently matches nothing. Re-read the attribute naming conventions. - Subject set up correctly? For non-
_Applicationsubjects, the Bearer token'ssubis the subject identifier; without a token the subject is not bound and many policies match nothing. - Data actually in the IKG? Run a probe query against the same shape but with
IS NOT NULLfilters to confirm the data exists.
Outcome
When this skill has been applied successfully:
- A read-only CIQ policy exists in the project; it has a single
subject.type, acypherpattern with named variables, optional partial filters, and anallowed_readswhitelist - but noallowed_upsertsorallowed_deletes. - A Knowledge Query references that policy and lists exactly the variables it should return in
nodes/relationships/aggregate_values. POST /contx-iq/v1/execute(or the MCPciq_executetool) returns the expected rows for validinput_paramsand an emptydataarray for valid-but-non-matching ones.- The same Knowledge Query can be invoked from the
indykite-mcp-serverskill viaciq_executewithout further changes.
Files in this skill
references/policy-reference.md- read-only policy schema, operators, attribute naming, partial filters,token_filterand step-up advice.references/knowledge-query-reference.md- Knowledge Query schema, including the read-only fields used here and a one-line description of every other field for context.references/execution-reference.md-POST /contx-iq/v1/executerequest and response shape, auth combinations, common error codes.assets/policy-read-cars.json- runnable read-only policy for thePerson -[:OWNS]-> Carexample.assets/knowledge-query-read-cars.json- matching Knowledge Query.scripts/execute.sh- Bash helper that posts to/contx-iq/v1/executewith the right headers.
Agent-specific notes
This skill uses generic markdown instructions and works across all agents listed in the README. The agent needs to be able to issue HTTP requests (curl, an HTTP client, or the IndyKite Terraform provider - see References). No Claude Code hooks, Cursor @-mentions, or Copilot workspace context are required.
References
- ContX IQ guide (developer hub)
- Config API documentation
- Cypher query language manual (Neo4j; openCypher) - the graph query language used in CIQ policy and Knowledge Query conditions over the IndyKite Knowledge Graph.
- IndyKite Terraform provider -
indykite_authorization_policyandindykite_knowledge_query - Credentials guide
- External Data Resolver guide (out of scope here, but useful for follow-on work)