Skip to main content
CodeAlive 3.0: From Context Engine to Code Research Agent
CodeAlive

Back to blog

Codebase analytics through MCP: what query_artifact_metadata can answer

CodeAlive exposes query_artifact_metadata for querying the metrics and structure of indexed code. An agent can find functions with high cognitive complexity and many incoming call relationships, or calculate average complexity by namespace.

These questions require calculated metrics and a call graph. CodeAlive makes them available through the same MCP server the agent uses to search and read source. Queries use ArtifactQuery v1, a small, read-only SQL-like language.

How a QA agent uses it

One of our customers built an internal AI tool for QA. It reads code through CodeAlive and writes test cases for a task. When interface or DTO names appear in the diff, the agent finds their declarations, reads their fields, and uses that information to prepare test cases.

Here is an example based on that workflow:

sql
SELECT identifier, path, namespace, containing_typeFROM symbolsWHERE name = 'IParcelRouteWindowService'  AND symbol_kind IN ('class', 'record', 'interface', 'struct', 'enum')LIMIT 50

A known name can also be found with grep. The customer uses a symbol query to get declarations with paths and identifiers ready for fetch_artifacts. If several modules declare the same name, path and namespace help select the right one.

For a DTO such as ParcelRouteWindowRequestDto, the sequence is the same: find the declaration, retrieve its source, and read its fields. The metadata query itself does not return the DTO's fields.

Alongside statement, the agent passes data_sources: repository or workspace names returned by get_data_sources. The following examples use those same arguments; substitute paths from your project.

Which functions to read before refactoring

In a large codebase, a list of the longest functions can become unhelpful quickly. For a first pass, use a narrower criterion: high complexity combined with many direct call relationships.

sql
SELECT identifier, path, name, complexity, incoming_calls_countFROM functionsWHERE path_under('src/')  AND has_body = true  AND is_test = false  AND complexity >= 15  AND incoming_calls_count >= 10ORDER BY complexity DESCLIMIT 20

The query selects up to twenty functions with implementations that are not marked as tests, ordered by complexity. The thresholds of 15 and 10 are illustrative.

This lets the agent start with functions that are difficult to read and have many direct callers. It opens their source with fetch_artifacts, retrieves callers with get_artifact_relationships, and checks which scenarios the refactoring needs to preserve.

Searching for function names alone cannot produce this list. First you would need to calculate complexity, resolve calls, and combine the results. The tool uses data already collected by the index.

CodeAlive's complexity is a cognitive-load score, rather than cyclomatic complexity. incoming_calls_count counts direct indexed relationships. It does not establish production execution frequency or the full effect of a change.

Where a module's dependencies concentrate

Suppose you need to change public methods in a payments module. Before editing, inspect how their incoming relationships are distributed across directories:

sql
SELECT directory,       COUNT(*) AS public_functions_with_counts,       SUM(incoming_calls_count) AS direct_relationshipsFROM functionsWHERE path_under('src/Payments/')  AND is_public = true  AND has_body = true  AND is_test = false  AND incoming_calls_count >= 0GROUP BY directoryORDER BY direct_relationships DESCLIMIT 30

The summary suggests where to start reading API consumers. For individual functions, the agent expands their relationships and checks whether the change reaches neighboring parts of the system. That evidence helps determine PR boundaries.

The relationship sum is not a count of unique consumers: one function can call several methods in the selected module. Separate directories do not establish independent changes either.

Another view of the code is complexity by namespace:

sql
SELECT namespace,       COUNT(*) AS measured_functions,       AVG(complexity) AS avg_complexityFROM functionsWHERE path_under('src/')  AND has_body = true  AND is_test = false  AND complexity >= 0GROUP BY namespaceORDER BY avg_complexity DESCLIMIT 20

Use this to choose a module for the next review pass, then query individual functions in that namespace. The count alongside the average shows the population size; source inspection determines which functions need changes.

The complexity >= 0 condition keeps functions with a calculated score, including zero. Without it, COUNT(*) and AVG(complexity) could describe different populations. Functions without a score can be checked separately with complexity IS NULL.

Other query capabilities

ArtifactQuery operates on files, symbols, functions, and containers. Depending on the entity, fields include file sizes and line counts, languages, symbol kinds and visibility, test and recursion flags, return types, reference counts, and descendant counts. Filtering, sorting, grouping, and the aggregates COUNT(*), AVG, MIN, MAX, and SUM are supported.

Fields differ in which operations they allow. get_artifact_query_schema returns the current schema and examples, so an agent can check field names and supported groupings.

Results describe the selected indexed scope: freshness, excluded paths, and language support matter. Every query requires LIMIT between 1 and 100. It caps output rows or groups; aggregates use all matching records in scope. A broad query can exceed its execution budget, requiring a narrower directory or conditions.

Content search and graph navigation use separate tools. See the CodeAlive MCP documentation for tool descriptions and connection instructions.

Context EngineeringMCPCode Quality

Give your agents the whole codebase

Index your first repo in minutes — or try the Playground without signing up.