Skip to main content
Glama
RaviIITk
by RaviIITk

code-index

Structural, queryable understanding of a Python codebase for a coding agent — built as an AST-derived RDF graph, stored in an embedded triple store, and exposed as MCP tools.

Instead of an agent re-discovering "who calls this function" or "what does this class inherit from" via repeated grep/read cycles, code-index parses the codebase once into a graph of facts and answers those questions as direct lookups, in milliseconds.

Why

Grep and file reads are the default way an LLM coding agent explores a repository, but they're the wrong tool for structural questions:

  • "What calls this function, anywhere in the repo, directly or transitively?" — grep finds text matches, not resolved call edges.

  • "What does this class inherit from, or what inherits from it?" — requires actually resolving imports across files.

  • "Give me a cheap overview of this folder before I decide what to read in full." — grep/read either gives you everything or nothing.

code-index answers these by parsing structure once and storing it as facts, so the agent can query instead of re-deriving.

Related MCP server: CodeGB

Concept

  1. Parse — Python's stdlib ast walks each file and extracts structural facts: modules, classes, functions, signatures, line ranges, docstrings, decorators, variable reads/writes, and unresolved call/import/inherit targets (by name only, not yet a link to the actual defining symbol).

  2. Resolve — a real Pyright language server runs as a managed subprocess for the duration of a build. It supplies return-type inference (hover) and cross-file symbol resolution (definition), which get attached onto the AST-built nodes — ast owns identity and structure, Pyright only annotates it.

  3. Store — every fact becomes an RDF triple (subject-predicate-object) in an embedded pyoxigraph store, one named graph per source file. This makes incremental re-indexing a drop-and-replace of a single file's graph, and gives the whole thing a real SPARQL 1.1 query engine for free.

  4. Materialize closurescalls, imports, and inherits are transitive relationships. Rather than walking property paths at query time, the full transitive closure of each is precomputed at index time and written into a dedicated urn:code:graph:inferred graph. "Every caller of X, however deep" becomes a direct lookup, not a graph traversal.

  5. Serve — an MCP server exposes six tools over the store: a token-cheap structural browser (get_context), a full-detail single-node lookup (get_details), three curated graph queries (get_callers, get_dependencies, get_class_hierarchy), and a raw SPARQL escape hatch (query_sparql) for anything the curated tools don't cover.

Everything downstream of parsing is just facts and queries over those facts — no LLM involved in indexing itself, so results are exact, not guessed.

Example: what actually gets stored

For a class like this:

class FeatureScaler:
    """Scales numeric features into [0, 1]."""

    def transform(self, values: list[float]) -> list[float]:
        """Scale values into [0, 1]."""
        return [self._normalize(v) for v in values]

parsing produces triples roughly equivalent to:

urn:code:src/scaler.py::FeatureScaler            a               code:Class
urn:code:src/scaler.py::FeatureScaler            code:defines    urn:code:src/scaler.py::FeatureScaler.transform
urn:code:src/scaler.py::FeatureScaler.transform  a               code:Function
urn:code:src/scaler.py::FeatureScaler.transform  code:signature  "transform(self, values: list[float]) -> list[float]"
urn:code:src/scaler.py::FeatureScaler.transform  code:description "Scale values into [0, 1]."
urn:code:src/scaler.py::FeatureScaler.transform  code:calls      urn:code:src/scaler.py::FeatureScaler._normalize
urn:code:src/scaler.py::FeatureScaler.transform  code:startLine  "5"

get_context("src/scaler.py") returns the cheap structural summary (names, signatures, line ranges — no docstrings). get_details(node_id) returns everything for one specific node, including its docstring. get_callers and get_class_hierarchy are direct lookups against the precomputed transitive closure, not live traversals.

Install

Requires Python 3.13+, uv, and Node.js (used only to vendor pyright-langserver as an internal subprocess — never a user-facing server).

git clone https://github.com/RaviIITk/code-index.git
cd code-index
uv sync
npm install       # vendors pyright-langserver into node_modules/

Usage

CLI

# Build (or incrementally rebuild) the index for a repo
uv run code-index build /path/to/repo

# Check what's currently indexed
uv run code-index status /path/to/repo

# Run a raw SPARQL query against the index
uv run code-index query "SELECT ?fn WHERE { ?fn a <http://example.org/code-ontology#Function> }" /path/to/repo

The index itself is stored outside the repo, in ~/.cache/code-index/<repo-slug>-<hash>/, keyed by the repo's canonical path — nothing is written into the working tree.

As a Python library

Everything the CLI and MCP server do is just calls into the library — useful for scripting or embedding in something else:

from pathlib import Path

from code_index.cache.location import store_path
from code_index.incremental.build import run_incremental_build
from code_index.mcp_server import tools
from code_index.store.triple_store import TripleStore

repo = Path("/path/to/repo")
store = TripleStore(store_path(repo))

# Build (or incrementally refresh) the index
report = run_incremental_build(store, repo, pyright_bin="pyright-langserver")
print(report.added, report.changed, report.deleted, report.parse_failures)

# Query it with the same functions the MCP tools delegate to
print(tools.get_context(store, "src/scaler.py", depth=1))

fn_id = "urn:code:src/scaler.py::FeatureScaler.transform"
print(tools.get_details(store, fn_id))
print(tools.get_callers(store, fn_id))
print(tools.get_dependencies(store, "src/scaler.py"))
print(tools.get_class_hierarchy(store, "urn:code:src/scaler.py::FeatureScaler"))

# Or drop to raw SPARQL 1.1
print(tools.query_sparql(store, "SELECT ?fn WHERE { ?fn a <http://example.org/code-ontology#Function> }"))

node ids are the same urn:code:<file_path>::<qualified.name> IRI strings returned in every get_context/get_details result's "id" field, so you can chain a broad query into a specific one without hand-building IRIs.

As an MCP server

Register it with an MCP-speaking agent host by adding to that host's MCP config (e.g. .mcp.json):

{
  "mcpServers": {
    "code-index": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/code-index", "code-index-mcp", "/path/to/repo"]
    }
  }
}

This starts a resident server (one per repo, guarded by an exclusive lock) exposing six tools:

Tool

Signature

Purpose

get_context

(path: str, depth: int = 1) -> dict

Token-cheap structural summary rooted at a file or folder; increase depth to drill from folder → file → function/class.

get_details

(node: str) -> dict

Full structural facts for one node, plus its docstring if the source has one.

get_callers

(fn: str) -> list[str]

Every function that calls fn, directly or transitively.

get_dependencies

(file: str) -> list[str]

Every file that file transitively imports.

get_class_hierarchy

(cls: str) -> dict

Transitive ancestors and descendants of a class.

query_sparql

(query: str) -> list[dict]

Raw SPARQL 1.1 escape hatch; the tool description embeds an ontology cheat-sheet so the agent has schema context without an extra call.

A typical agent workflow: call get_context on the repo root to see the folder tree, drill into a file of interest, grab a function/class id from that summary, then call get_details or get_callers with that id for the full picture.

Note: the MCP server only ever serves whatever is already in the store when it starts — run code-index build (or wire a session-start hook to do so) before pointing an agent at a repo whose index may be stale.

Development

uv run pytest    # 44 tests
uv run ruff check .
uv run ruff format .

Scope

v1 targets small-to-medium Python repos (hundreds of files). Deliberately out of scope for now: other languages, LLM-generated summaries/purpose fields, graph-centrality ranking, and transparent multi-session server sharing (each repo currently gets one resident server; a second concurrent session refuses to start rather than silently sharing state).

License

Dual-licensed under either of

at your option.

Available Tools

6 tools
get_callersA

Every function that calls fn (a node id from get_context/get_details), directly or transitively.

ParametersJSON Schema
NameRequiredDescriptionDefault
fnYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description bears full burden. It discloses the key behavior (direct/transitive callers) but omits details like recursion depth limits, performance implications, or how transitively is defined. Adequate for simple use.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single sentence, no redundant words, front-loaded with the key action. Every part is essential and informative.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given low complexity (1 param, output schema present), the description covers the core functionality. It explains parameter origin and result scope. Could mention if output is function IDs or names, but output schema likely handles that.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the description adds meaning by specifying that `fn` is a node ID from get_context/get_details, giving context for parameter usage. No examples or validation constraints provided, but valuable addition beyond schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states the tool returns functions that call a given function `fn`, with direct or transitive calls. Differentiates from sibling tools by focusing on callers rather than dependencies or hierarchy. Could be slightly more explicit but is clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides context that `fn` comes from get_context/get_details, which guides where to obtain the input. However, it does not explicitly state when to use this tool over alternatives like get_dependencies, leaving agents to infer from purpose.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_class_hierarchyA

Transitive ancestors and descendants of a class (a node id from get_context/get_details).

ParametersJSON Schema
NameRequiredDescriptionDefault
clsYes

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full burden. It describes the core behavior (transitive ancestors/descendants) but lacks details on permissions, rate limits, or side effects, which are acceptable for a read-only query.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence with no redundant words. It could be slightly improved by clarifying the output structure, but it is appropriately concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity, the description covers the input and output goal but does not specify whether the output is a flat list or hierarchical, nor its exact structure, leaving some ambiguity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 0% description coverage, but the description adds crucial context: the 'cls' parameter is a node id from get_context/get_details, which compensates for the schema gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it returns 'transitive ancestors and descendants' of a class, which distinguishes it from sibling tools like get_callers or query_sparql.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It specifies the prerequisite (node id from get_context/get_details) and implies use for class hierarchy queries, but does not explicitly exclude alternatives or state when not to use.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_contextA

Token-cheap structural summary rooted at a file or folder path (workspace-relative). Structural facts only (names, signatures, line ranges) — no docstrings, use get_details for those. Increase depth to drill from folder -> file -> function/class detail.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes
depthNo

TDQS

A4.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It mentions 'token-cheap' (cost awareness) and the structural nature, but does not explicitly state read-only or side-effect-free behavior. Adequate but not fully transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the core purpose, then specific details and related tool. No extraneous words. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple tool with 2 params and no output schema, the description covers what it returns, how to use the parameters, and how it relates to a sibling tool. It is complete for the context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so description must add meaning. It explains 'path' as workspace-relative and 'depth' as drilling from folder to detail levels. This adds significant value beyond the schema's names and default.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns a 'structural summary' with 'names, signatures, line ranges' for a file or folder path. It distinguishes from get_details by excluding docstrings. This is specific and differentiates from siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly advises using get_details for docstrings, implying that get_context is for structural info only. It does not explicitly state when not to use it, but the guidance is clear and helpful.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_dependenciesA

Every file (workspace-relative path) that file transitively depends on via its import edges.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses the core behavior: transitive dependency resolution via import edges. However, it omits details like cycle handling or response shape, though the output schema exists to cover return values.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, clear sentence with no wasted words. It is front-loaded with the key action and resource.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given one parameter, no annotations, and an existing output schema, the description adequately explains the tool's function. It could mention what the returned list contains (file paths), but the output schema likely covers that.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The only parameter 'file' has no schema description (0% coverage), so the description compensates by specifying it expects a 'workspace-relative path', which adds meaningful context beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it returns every file a given file transitively depends on via imports. It distinguishes itself from siblings like get_callers and get_class_hierarchy by focusing on import dependencies, not callers or class hierarchy.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies use for dependency analysis but lacks explicit guidance on when to use this tool versus its siblings (e.g., get_callers, get_context). No mention of when not to use or alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_detailsB

Full structural facts for one node (a node id from get_context), plus its full stored docstring if the source actually has one.

ParametersJSON Schema
NameRequiredDescriptionDefault
nodeYes

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description bears full burden. It discloses that it returns structural facts and optionally a docstring, but does not detail permissions, errors, or side effects. Adequate but minimal.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Extremely concise: two sentences with no wasted words. Core purpose is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool is simple with one parameter and no output schema. Description covers core behavior but lacks details on output structure, errors, or edge cases. Adequate but could be more comprehensive.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

While the input schema only specifies type 'string' for 'node', the description adds useful context that the node id originates from get_context. This compensates for the 0% schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states that the tool returns 'Full structural facts for one node' and optionally includes its docstring. It ties the input to get_context, which helps differentiate from siblings like get_context and get_dependencies, but lacks explicit differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives. Only implies that the node id should come from get_context. No mention of when not to use or other considerations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

query_sparqlA

Prefix: code: http://example.org/code-ontology# Node IRIs: urn:code:<repo/relative/path.py> (module), urn:code:<path.py>::<Qualified.Name> (class/function).

Classes: code:Module, code:Class, code:Function Datatype properties: code:name, code:startLine, code:endLine, code:signature, code:returnType, code:description, code:contentHash Object properties: code:defines (parent->child), code:belongsTo (child->parent, inverse of defines), code:reads, code:writes, code:raises, code:decoratedBy, code:imports, code:calls, code:inherits (imports/calls/inherits objects may be a string Literal if unresolved, or a NamedNode if Pyright resolved the target — both may be present)

Named graphs: urn:code:graph:<path.py> holds one file's direct/structural triples; urn:code:graph:inferred holds the fully materialized transitive closure (R+, includes direct edges) of calls/imports/inherits across the whole repo.

Every query runs with use_default_graph_as_union=True, so a plain WHERE clause with no GRAPH block already searches across every named graph — add an explicit GRAPH urn:code:graph:inferred { ... } block only when you specifically want the transitive/materialized edges rather than a single file's direct ones.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses the default behavior (use_default_graph_as_union=True) and explains graph partitioning. However, it does not state whether queries are read-only or any side effects, which is a minor gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is relatively long but well-organized, starting with prefix/IRI definitions, then classes/properties, then named graphs, and usage guidance. It could be trimmed slightly but is effectively structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the presence of an output schema (context shows true), return values are likely covered elsewhere. The description thoroughly explains ontology and graph context needed for queries, but omits possible SPARQL construct restrictions or output structure, which is a minor gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema coverage, the description must compensate but only provides ontology context for writing queries, not syntax or constraints for the query parameter itself. The ontology info is valuable, but the parameter semantics remain vague.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool executes SPARQL queries against a code ontology, with specific details on prefixes, node IRIs, classes, and properties. It distinguishes itself from sibling tools like get_callers or get_class_hierarchy by being a generic query tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance on graph selection: plain WHERE searches all graphs, while an explicit GRAPH block targets inferred edges. It does not mention when to avoid this tool or directly compare to siblings, but the context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 6 tool updatesv0.1.0
    • First observedget_callers
    • First observedget_class_hierarchy
    • First observedget_context
    • First observedget_dependencies
    • First observedget_details
    • First observedquery_sparql

TDQS

A4/5.0
Disambiguation5/5

Each tool has a distinct purpose: callers, class hierarchy, structural context, dependencies, detailed node info, and SPARQL queries. There is no overlap or ambiguity between them.

Naming Consistency4/5

Most tools follow the 'get_' prefix pattern (get_callers, get_class_hierarchy, etc.), but query_sparql deviates slightly by using 'query' instead. The naming is still clear and predictable overall.

Tool Count5/5

With 6 tools, the set is well-scoped for a code indexing server. Each tool provides essential functionality without redundancy or excess.

Completeness4/5

The tools cover structural analysis (context, details) and relational queries (callers, hierarchy, dependencies). The SPARQL endpoint allows arbitrary queries, filling most gaps. Minor missing direct tool for listing all modules, but query_sparql can handle that.

Maintenance

ActivitySlowing
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    F
    maintenance
    Provides a local code knowledge graph for Java projects, enabling querying of classes, methods, fields, calls, inheritance, and imports via MCP tools like query, context, impact, and cypher.
    1
    -
  • A
    license
    A
    quality
    B
    maintenance
    Indexes a mono-repo into a knowledge graph and provides MCP tools to query code structure—packages, components, routes, HTTP calls—without file reads or grep round-trips.
    7
    22
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    An MCP server that indexes a local Python codebase into a SQLite graph for hybrid code search, file context, lazy explanations, dependency traces, and version-aware symbol history.
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/RaviIITk/code-index'

If you have feedback or need assistance with the MCP directory API, please join our Discord server