Skip to main content
Glama
okeefeco

PyEye Server

by okeefeco

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
PYEYE_POOL_TTLNoConnection time-to-live in seconds3600
PYEYE_CACHE_TTLNoCache time-to-live in seconds300
PYEYE_MAX_WORKERSNoMaximum concurrent analysis workers4
PYEYE_MAX_PROJECTSNoMaximum number of projects in memory10
PYEYE_MAX_FILE_SIZENoMaximum file size to analyze (bytes)1048576
PYEYE_ANALYSIS_TIMEOUTNoAnalysis timeout in seconds30.0
PYEYE_WATCHER_DEBOUNCENoFile watcher debounce delay in seconds0.5
PYEYE_POOL_MAX_CONNECTIONSNoMaximum pooled project connections10
PYEYE_ENABLE_MEMORY_PROFILINGNoEnable memory profilingfalse
PYEYE_ENABLE_CONNECTION_POOLINGNoEnable connection pooling for multiple projectstrue
PYEYE_ENABLE_PERFORMANCE_METRICSNoEnable performance metricsfalse

Capabilities

Features and capabilities supported by this server

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
configure_packagesA

Python: Configure additional packages, namespaces, and standalone scripts for analysis.

Args: packages: List of package paths to include namespaces: Namespace packages with their repo paths standalone_dirs: Directories containing standalone Python scripts recursive: Scan standalone directories recursively file_pattern: Glob pattern for standalone files exclude_patterns: Patterns to exclude from standalone scanning save: Save configuration to .pyeye.json

resolveA

Python: Resolve any identifier form to a canonical Handle.

Accepts bare names, FQN dotted paths, re-exported public paths, file:line coordinates, or file paths. Returns the definition-site canonical handle along with kind and scope ("project" or "external").

Args: identifier: The identifier to resolve. Forms supported: - Bare name: "Config" - FQN: "a.b.c.Config" - Re-exported: "package.Config" (collapses to definition site) - File:line: "src/foo.py:42" - File only: "src/foo.py" project_path: Project root path (default: current directory)

Returns: ResolveResult dict — one of: - Success: {"found": True, "handle": str, "kind": str, "scope": "project"|"external"} - Ambiguous: {"found": True, "ambiguous": True, "candidates": [...]} - Not found: {"found": False, "reason": str}

resolve_atA

Python: Resolve a (file, line, column) position to a canonical Handle.

Used when you have coordinates (from a stack trace, error report, or pasted excerpt) rather than a name. Returns the same shape as resolve().

Args: file: Absolute or project-relative path to the source file. line: 1-indexed line number. column: 0-indexed column number. Pass 0 for the start of the line — this is valid; do not coerce to a default. project_path: Project root path (default: current directory)

Returns: ResolveResult dict (see resolve() for shape).

inspectA

Python: Inspect a canonical handle and return a structural Node.

The "what is this?" operation. Returns the symbol's kind, location, signature, docstring, and kind-dependent fields. Cheap by default — no source content, no exhaustive enumerations. Edge counts and highlights come in later phases.

Args: handle: Canonical Python dotted-name string (from resolve/resolve_at). project_path: Project root path (default: current directory)

Returns: Node dict with universal fields (handle, kind, scope, location, docstring, edge_counts={}) plus kind-dependent fields: - class: signature (constructor), superclasses (list of Handle strings) - function/method: signature, parameters, return_type?, is_async, is_classmethod, is_staticmethod - module: is_package, package? - attribute/property/variable: type?, default? (simple literals only)

expandA

Python: Expand one outbound edge from a canonical handle (single hop).

The traversal primitive that walks ONE edge from a source handle and returns adjacent symbols as lightweight Stubs. Use resolve()/inspect() first to obtain a canonical handle, then call expand() to traverse.

Supported edges (the complete static/outbound set):

  • members — class/module → direct members (attributes, methods, nested classes). stubs: [] means the class/module was found but has no members; that is NOT the same as unsupported. Static-surface ceiling: members are read from source; runtime-injected members (metaclass / setattr / __getattr__ / type() / __init_subclass__) are NOT captured — e.g. a Django Model shows none of its metaclass-injected _meta / objects / DoesNotExist.

  • callees — function/method → forward static call targets. Includes project symbols and stdlib/external symbols reachable via Jedi's goto. Dynamic calls (un-inferable parameters, getattr, lambdas, etc.) are counted in unresolved_call_sites rather than invented.

  • imported_by — module → the project modules that import it (module Stubs), computed by static AST import-graph reversal (no reverse symbol search). Covers importers anywhere in the project including tests and standalone scripts. Non-module handles return the unsupported branch with reason: "not_yet_implemented" (symbol-level imported_by is not yet implemented). Ceiling: runtime-dynamic imports (importlib/__import__ with computed targets) are not detected.

  • subclasses — class → the project classes that directly subclass it (class Stubs), computed by an AST class-graph walk + forward goto (no reverse symbol search). Returns the DIRECT (depth-1) subclasses only (#422) — one hop, symmetric with superclasses; the full transitive closure is served by trace(follow=["subclasses"], max_depth=k, max_nodes=N), which carries the cap + truncated contract. A class result includes a static transitive_hint field pointing to that trace route. subclasses is an expand-only edge: inspect does NOT measure it (dropped in #392); a cheap direct count is gated on the Pyright reference backend / class-graph cache (#333/#397), because even the direct count is a reverse query needing the same project-wide scan as callers/references. stubs: [] means the class has no project subclasses (measured-none). A non-class handle also returns the supported branch with stubs: [] (and no transitive_hint) — only a class CAN be subclassed, so [] is true by definition, not an absence-vs-zero lie. Static-surface ceiling: the result is complete only over literal class B(A): subclassing; dynamically-created subclasses (type('B', (A,), {}), factory-built classes, __init_subclass__ registration) are NOT captured.

  • superclasses — class → its base classes (class Stubs), resolved by Jedi from the class definition (no reverse search). A non-class handle returns stubs: [] ([] true by definition, as with subclasses).

  • imports — module → the symbols/modules it imports (Stubs), computed by static AST + forward goto. stubs: [] is measured-none. A non-module handle returns the unsupported branch with reason: "not_yet_implemented" (mirrors imported_by).

  • enclosing_scope — symbol → its immediate lexical enclosing scope (the inverse of members), resolved by Jedi parent(): a method → its class, a nested def/class → its enclosing def/class, a top-level def/class/variable → its module. At most ONE Stub. A module returns stubs: [] (a module has no enclosing lexical scope — packages are not lexical scopes); [] is therefore measured-empty, never unsupported.

Unsupported edges return the unsupported branch (never raise):

  • Inbound/reference edges (callers, references, overrides, …) require the Pyright reference backend (#333) and return unsupported: true, reason: "deferred_reference_backend".

  • Wrong-kind handles (e.g. imported_by on a non-module) return the unsupported branch with reason: "not_yet_implemented".

  • Unrecognised edge names return reason: "unknown_edge".

Response shape — discriminated union:

Supported branch ("unsupported" key absent): ::

{ "source": str,                 # canonical source handle
  "edge":   str,
  "stubs":  [Stub, ...],         # [] == measured-empty (NOT unsupported)
  "unresolved_call_sites": int   # callees ONLY; absent for members }

Unsupported branch ("stubs" key absent): ::

{ "source": str,
  "edge":   str,
  "unsupported": True,
  "reason":  str,               # deferred_reference_backend |
                                # not_yet_implemented | unknown_edge
  "detail":  str }              # human-readable explanation

Each Stub carries: handle, kind, scope, line_start, line_end, and signature when Jedi yields one (always for class/function/method; also any name whose inferred type is callable).

Relationship to deprecated tools: members supersedes the deprecated find_subclasses/find_symbol pattern for enumerating class members. callees supersedes manual get_call_hierarchy usage for forward edges. Both deprecated tools remain registered until Phase B migration.

Args: handle: Canonical Python dotted-name string (from resolve/inspect). edge: The outbound edge to expand (e.g. "members", "callees"). project_path: Project root path (default: current directory).

Returns: ExpandResult dict — supported branch or unsupported branch (see above). Never raises; unresolvable source handles yield graceful supported-empty results consistent with inspect()'s minimal-node contract.

traceA

Python: Bounded multi-hop BFS traversal — returns a typed Subgraph.

The composition primitive: it walks the follow edges outward from start across multiple hops, deduping by canonical handle, and returns a Subgraph of the reachable structure. Use resolve()/inspect() to obtain canonical handles first, then trace() to see structure across hops (call chains, reverse-import closures, member trees).

Composes the same edge registry as expand; the implemented edges (members, callees, imported_by, subclasses, superclasses, imports, enclosing_scope) are traversed. Any other edge named in follow (deferred reference edges, unknown names) is reported in unsupported_edges rather than silently dropped — a silent drop would falsely read as "no such neighbours".

Response shape — Subgraph::

{ "nodes": { handle: Stub, ... },        # deduped by canonical handle
  "edges": [ {"from": h, "to": h, "kind": edge}, ... ],
  "truncated": bool,                     # a cap cut off reachable nodes
  "truncation_reasons": ["max_depth"?, "max_nodes"?],  # which cap(s) fired
  "unsupported_edges": [ {"edge", "reason", "detail"}, ... ] }

Edges are NOT deduped across kinds; edges to already-visited handles are recorded (so cycles stay visible) but never re-expanded, guaranteeing termination on cyclic graphs. truncated is true ONLY when max_depth or max_nodes cut off reachable handles before natural termination — not merely because a cap was set.

Args: start: One canonical handle, or a list of them, as BFS roots. follow: Edge names to traverse at every hop (e.g. ["members"], ["callees"], ["imported_by"]). project_path: Project root path (default: current directory). max_depth: Maximum hop distance from a root before a node becomes a non-expanded frontier leaf (default 3). max_nodes: Maximum number of distinct nodes in the subgraph; reaching it sets truncated (default 50). stop_when: Optional StopPredicate (exclude_external / module_pattern / exclude_tests); a matching adjacent is a pruned boundary. Roots are never pruned. exclude_external stops at stdlib/site-packages nodes — keeps a trace inside the project (the common callees case).

Returns: A Subgraph dict (plain, JSON-serialisable). Never raises; an unresolvable root simply contributes no node.

outlineA

Python: Structural skeleton of a module or class — names, kinds, signatures, line spans.

Returns a nested OutlineTree — the members hierarchy of handle as a tree of lightweight structural nodes (Stub). Each node carries handle, kind, scope, line_start, line_end, and signature when Jedi yields one. No source content anywhere in the tree.

Use resolve() or inspect() first to obtain a canonical handle, then outline() to see the complete structural skeleton in one call — the single-call answer to "show me the structure of this scope."

Static-surface ceiling. The tree walks the members edge, so it is complete over what is statically defined in source but not over runtime. Runtime-injected members (metaclass / setattr / __getattr__ / type() / __init_subclass__) are NOT captured — e.g. outline of a Django Model omits its metaclass-injected _meta / objects / DoesNotExist. An absent member is "not in source," not "not at runtime."

Absence contracts — an agent MUST read these before consuming the tree.

Contract 1 — children absent ⇔ not expanded.

children present (including children: []) means measured: the complete set of direct members of this node. children: [] is a genuine leaf — a container with no members, or a non-container (function/method/ variable). children absent means a cap fired and this node was not walked — treat it as "unknown," never as empty.

Contract 2 — truncated absent-not-false.

truncated: true is present only on a node that a cap cut off; it always co-occurs with truncation_reason and an absent children. Fully-walked nodes omit truncated entirely — truncated: false never appears.

Truncation reasons (one string per node — not a list):

  • "max_depth" — at the depth frontier; resolve_members was peeked once and found members (a genuine empty container at the frontier gets children: [] instead).

  • "max_nodes" — total-node budget exhausted; no peek performed.

  • "external" — external-scope container at depth ≥ 1; no deeper walk into third-party code.

When both max_nodes AND a depth/external cap could apply to the same node, truncation_reason is "max_nodes" (the harder global bound).

Args: handle: Canonical Python dotted-name string (from resolve/inspect). project_path: Project root path (default: current directory). max_depth: Maximum depth from the root (root is depth 0). None means unbounded within scope; the external cap and max_nodes still apply. At the frontier, resolve_members is peeked once to distinguish a genuine empty container from a cut-off one. max_nodes: Total-node budget for the tree (root counts as 1, default 200). Containers that exceed the budget are marked truncated: "max_nodes" without peeking.

Returns: OutlineTree dict — {"node": Stub, "children": [OutlineTree, ...]}. Never raises; an unresolvable handle yields a minimal single-node tree with children: []. Children within each parent are in source order (sorted by (line_start, handle)); BFS inclusion order bounds the budget gracefully (all of depth 1 before any of depth 2, etc.).

find_referencesA

Python: Find ALL usages of a symbol. Understands inheritance - grep misses subclass refs.

For general use, prefer lookup() which accepts any identifier form. This tool provides fields filtering, include_subclasses, and symbol_name for full reference lists.

Two calling conventions (coordinates take precedence if both provided):

  1. Coordinates: file + line + column (precise, unambiguous)

  2. Symbol name: symbol_name only (convenient; fails if name is ambiguous)

If symbol_name matches multiple symbols, returns error with a "matches" list so you can pick the right one and retry with coordinates.

Args: file: Path to the file (required with line and column) line: Line number (1-indexed, required with file and column) column: Column number (0-indexed, required with file and line) symbol_name: Symbol name (alternative to file+line+column) project_path: Root path of the project include_definitions: Include definitions in results include_subclasses: Also find references to all subclasses (polymorphic search) fields: Fields to include per reference. Valid: name, type, line, column, description, full_name, file, is_definition. Default: all fields.

get_call_hierarchyA

Python: Trace function callers and callees through the codebase.

Deprecated: Replaced by inspect(handle).edge_counts.callers for the count and future expand(handle, edge="callers") for the list in the redesigned API. See docs/superpowers/specs/2026-05-02-progressive-disclosure-api-design.md for the migration plan. This method will be removed once the legacy MCP tools are deprecated (Phase B of the migration).

For general use, prefer lookup() which accepts any identifier form. This tool provides full call graph traversal beyond the default limit.

Args: function_name: Name of the function file: Optional file to search in (searches whole project if not specified) project_path: Root path of the project

analyze_dependenciesA

Python: Map module dependencies and detect circular imports. Semantic analysis grep can't do.

Deprecated: Replaced by future trace(handle, follow=["imports"]) in the redesigned API. See docs/superpowers/specs/2026-05-02-progressive-disclosure-api-design.md for the migration plan. This method will be removed once the legacy MCP tools are deprecated (Phase B of the migration).

For general use, prefer lookup() which accepts any identifier form. This tool provides circular dependency detection and scope filtering for targeted queries.

Args: module_path: Import path of the module (e.g., "pyeye.mcp") project_path: Root path of the project scope: Search scope - "main", "all", "namespace:name", or list

find_modelsA

Find all Pydantic models in the project.

Args: scope: Search scope (default "main"): - "main": Only the main project (default for plugins) - "all": Include configured namespaces - "namespace:name": Specific namespace

get_model_schemaA

Get the schema for a specific Pydantic model.

Args: model_name: Name of the model to get schema for scope: Search scope (default "main"): - "main": Only the main project (default for plugins) - "all": Include configured namespaces - "namespace:name": Specific namespace

find_validatorsA

Find all Pydantic validators in the project.

Args: scope: Search scope (default "main"): - "main": Only the main project (default for plugins) - "all": Include configured namespaces - "namespace:name": Specific namespace

find_field_validatorsA

Find all field-specific validators.

Args: scope: Search scope (default "main"): - "main": Only the main project (default for plugins) - "all": Include configured namespaces - "namespace:name": Specific namespace

find_model_configA

Find all model configurations.

Args: scope: Search scope (default "main"): - "main": Only the main project (default for plugins) - "all": Include configured namespaces - "namespace:name": Specific namespace

trace_model_inheritanceB

Trace the inheritance hierarchy of a Pydantic model.

Args: model_name: Name of the model to trace scope: Search scope (default "main"): - "main": Only the main project (default for plugins) - "all": Include configured namespaces - "namespace:name": Specific namespace

find_computed_fieldsA

Find all computed fields (properties, computed_field decorator).

Args: scope: Search scope (default "main"): - "main": Only the main project (default for plugins) - "all": Include configured namespaces - "namespace:name": Specific namespace

find_routesA

Find all Flask routes in the project.

Args: scope: Search scope (default "main"): - "main": Only the main project (default for plugins) - "all": Include configured namespaces - "namespace:name": Specific namespace

find_blueprintsA

Find all Flask blueprints in the project.

Args: scope: Search scope (default "main"): - "main": Only the main project (default for plugins) - "all": Include configured namespaces - "namespace:name": Specific namespace

find_viewsA

Find all Flask view functions and classes.

Args: scope: Search scope (default "main"): - "main": Only the main project (default for plugins) - "all": Include configured namespaces - "namespace:name": Specific namespace

find_templatesA

Find all Flask templates and render_template calls.

Args: scope: Search scope (default "main"): - "main": Only the main project (default for plugins) - "all": Include configured namespaces - "namespace:name": Specific namespace

find_extensionsA

Find Flask extensions in use.

Args: scope: Search scope (default "main"): - "main": Only the main project (default for plugins) - "all": Include configured namespaces - "namespace:name": Specific namespace

find_configA

Find Flask configuration files and app.config usage.

Args: scope: Search scope (default "main"): - "main": Only the main project (default for plugins) - "all": Include configured namespaces - "namespace:name": Specific namespace

find_error_handlersA

Find error handler functions.

Args: scope: Search scope (default "main"): - "main": Only the main project (default for plugins) - "all": Include configured namespaces - "namespace:name": Specific namespace

find_cli_commandsB

Find Flask CLI commands.

Args: scope: Search scope (default "main"): - "main": Only the main project (default for plugins) - "all": Include configured namespaces - "namespace:name": Specific namespace

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription
get_find_references_workflowGet the Find All References workflow. This workflow shows how to find ALL usages of a class/function, including both package code and standalone scripts/notebooks. Returns: Markdown workflow documentation
get_refactoring_workflowGet the Safe Refactoring workflow. This workflow guides through safe refactoring by analyzing subclasses, references, and dependencies before making changes. Returns: Markdown workflow documentation
get_code_understanding_workflowGet the Code Understanding workflow. This workflow helps understand unfamiliar code by systematically exploring structure, relationships, and usage patterns. Returns: Markdown workflow documentation
get_dependency_analysis_workflowGet the Dependency Analysis workflow. This workflow helps analyze module dependencies, import relationships, and architectural patterns in Python projects. Returns: Markdown workflow documentation
get_code_review_standards_workflowGet the Python Code Review Standards workflow. This workflow provides industry best practices for Python code review including PEP standards, modern Python features, type safety, testing, and anti-pattern detection with MCP-enhanced semantic analysis. Returns: Markdown workflow documentation
get_code_review_security_workflowGet the Python Security Code Review workflow. This workflow provides OWASP security guidelines for Python code review including input validation, injection prevention, authentication patterns, and data flow analysis using MCP tools. Returns: Markdown workflow documentation
get_code_review_pr_workflowGet the Complete Pull Request Review workflow. This workflow provides a comprehensive PR review process combining automated checks, semantic analysis, code standards, security review, and manual review best practices. Returns: Markdown workflow documentation

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/okeefeco/pyeye-mcp'

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