Skip to content

Commit 3c8d7b0

Browse files
authored
fix: get_symbol_code returns full source code instead of just name (#187) (#188)
The tree-sitter query files for TypeScript, JavaScript, and Rust had @definition.X captures placed on the identifier node instead of the declaration node, causing extract_symbols to return only the symbol name in the code field. - Fix TypeScript/JavaScript/Rust query patterns to capture full body - Add regression test for issue #187 - Document include_code param in MCP tool description and docs
1 parent b85c21a commit 3c8d7b0

6 files changed

Lines changed: 106 additions & 33 deletions

File tree

docs/src/content/docs/mcp/kit-dev-mcp.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ The server provides many tools including:
8484
- **grep_code** - Fast literal string search (120s default timeout, configurable via `KIT_GREP_TIMEOUT`)
8585
- **grep_ast** - Search code using AST patterns (semantic search)
8686
- **get_file_tree** - Repository file structure with pagination support (`limit`/`offset` params)
87-
- **extract_symbols** - Extract functions, classes, and symbols
87+
- **extract_symbols** - Extract functions, classes, and symbols (excludes code by default for ~90% token savings; use `include_code=true` to get full source)
8888
- **get_symbol_code** - Get source code of a specific symbol (lazy loading for context efficiency)
8989
- **find_symbol_usages** - Find where symbols are used
9090
- **warm_cache** - Pre-warm caches for faster operations on large codebases (100K+ files)

src/kit/mcp/dev_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ def list_tools(self) -> List[Tool]:
576576
),
577577
Tool(
578578
name="extract_symbols",
579-
description="Extract symbols from a file",
579+
description="Extract symbols (functions, classes, etc.) from a file. Returns name, type, start_line, end_line, file. By default excludes source code to save tokens (~90% reduction). Use include_code=true to get full source, or use get_symbol_code for lazy loading specific symbols.",
580580
inputSchema=ExtractSymbolsParams.model_json_schema(),
581581
),
582582
Tool(

src/kit/queries/javascript/tags.scm

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33

44
; Function declarations
55
(function_declaration
6-
name: (identifier) @name @definition.function)
6+
name: (identifier) @name) @definition.function
77

88
; Class declarations
99
(class_declaration
10-
name: (identifier) @name @definition.class)
10+
name: (identifier) @name) @definition.class
1111

1212
; Arrow functions assigned to const/let (lexical_declaration)
1313
(lexical_declaration
@@ -57,23 +57,23 @@
5757
; Exported function declarations
5858
(export_statement
5959
declaration: (function_declaration
60-
name: (identifier) @name @definition.function))
60+
name: (identifier) @name) @definition.function)
6161

6262
; Exported class declarations
6363
(export_statement
6464
declaration: (class_declaration
65-
name: (identifier) @name @definition.class))
65+
name: (identifier) @name) @definition.class)
6666

6767
; Class methods
6868
(class_body
6969
(method_definition
70-
name: (property_identifier) @name @definition.method))
70+
name: (property_identifier) @name) @definition.method)
7171

7272
; Generator functions
7373
(generator_function_declaration
74-
name: (identifier) @name @definition.function)
74+
name: (identifier) @name) @definition.function
7575

7676
; Exported generator functions
7777
(export_statement
7878
declaration: (generator_function_declaration
79-
name: (identifier) @name @definition.function))
79+
name: (identifier) @name) @definition.function)

src/kit/queries/rust/tags.scm

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
;; tags.scm for Rust symbol extraction
22

33
(function_item
4-
name: (identifier) @name
5-
(#set! type "function"))
4+
name: (identifier) @name) @definition.function
65

76
(struct_item
8-
name: (type_identifier) @name
9-
(#set! type "struct"))
7+
name: (type_identifier) @name) @definition.struct
108

119
(enum_item
12-
name: (type_identifier) @name
13-
(#set! type "enum"))
10+
name: (type_identifier) @name) @definition.enum
1411

1512
(trait_item
16-
name: (type_identifier) @name
17-
(#set! type "trait"))
13+
name: (type_identifier) @name) @definition.trait
1814

1915
(impl_item
20-
type: (type_identifier) @name
21-
(#set! type "impl"))
16+
type: (type_identifier) @name) @definition.impl

src/kit/queries/typescript/tags.scm

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,24 @@
22

33
; Function declarations
44
(function_declaration
5-
name: (identifier) @name @definition.function)
5+
name: (identifier) @name) @definition.function
66

77
; Class declarations (with optional modifiers like export)
88
(class_declaration
9-
name: (type_identifier) @name @definition.class)
9+
name: (type_identifier) @name) @definition.class
1010

1111
; Interface declarations
1212
(interface_declaration
13-
name: (type_identifier) @name @definition.interface)
13+
name: (type_identifier) @name) @definition.interface
1414

1515
; Enum declarations
1616
(enum_declaration
17-
name: (identifier) @name @definition.enum)
17+
name: (identifier) @name) @definition.enum
1818

1919
; Class methods
2020
(class_body
2121
(method_definition
22-
name: (property_identifier) @name @definition.method))
22+
name: (property_identifier) @name) @definition.method)
2323

2424
; Arrow functions assigned to const/let (lexical_declaration)
2525
(lexical_declaration
@@ -76,41 +76,41 @@
7676
; Exported function declarations
7777
(export_statement
7878
declaration: (function_declaration
79-
name: (identifier) @name @definition.function))
79+
name: (identifier) @name) @definition.function)
8080

8181
; Exported class declarations
8282
(export_statement
8383
declaration: (class_declaration
84-
name: (type_identifier) @name @definition.class))
84+
name: (type_identifier) @name) @definition.class)
8585

8686
; Exported interface declarations
8787
(export_statement
8888
declaration: (interface_declaration
89-
name: (type_identifier) @name @definition.interface))
89+
name: (type_identifier) @name) @definition.interface)
9090

9191
; Exported enum declarations
9292
(export_statement
9393
declaration: (enum_declaration
94-
name: (identifier) @name @definition.enum))
94+
name: (identifier) @name) @definition.enum)
9595

9696
; Type alias declarations
9797
(type_alias_declaration
98-
name: (type_identifier) @name @definition.type)
98+
name: (type_identifier) @name) @definition.type
9999

100100
; Exported type alias declarations
101101
(export_statement
102102
declaration: (type_alias_declaration
103-
name: (type_identifier) @name @definition.type))
103+
name: (type_identifier) @name) @definition.type)
104104

105105
; Namespace (internal_module)
106106
(internal_module
107-
name: (identifier) @name @definition.namespace)
107+
name: (identifier) @name) @definition.namespace
108108

109109
; Generator functions
110110
(generator_function_declaration
111-
name: (identifier) @name @definition.function)
111+
name: (identifier) @name) @definition.function
112112

113113
; Exported generator functions
114114
(export_statement
115115
declaration: (generator_function_declaration
116-
name: (identifier) @name @definition.function))
116+
name: (identifier) @name) @definition.function)

tests/test_symbol_extraction_multilang.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,81 @@ def test_symbol_extraction(ext: str, code: str):
2626
# Simple sanity: expect 'foo' OR 'Bar' present
2727
names = {s.get("name") for s in symbols}
2828
assert any(name in names for name in {"foo", "Bar", "main"}), f"Expected symbols missing for {ext}: {names}"
29+
30+
31+
# Test for issue #187: get_symbol_code returns symbol name instead of actual source code
32+
# https://github.com/cased/kit/issues/187
33+
MULTILINE_SAMPLES = {
34+
".ts": """
35+
function myFunction(x: number, y: number): number {
36+
const result = x + y;
37+
return result;
38+
}
39+
""",
40+
".js": """
41+
function myFunction(x, y) {
42+
const result = x + y;
43+
return result;
44+
}
45+
""",
46+
".rs": """
47+
fn my_function(x: i32, y: i32) -> i32 {
48+
let result = x + y;
49+
result
50+
}
51+
""",
52+
".py": """
53+
def my_function(x, y):
54+
result = x + y
55+
return result
56+
""",
57+
".go": """
58+
package main
59+
60+
func myFunction(x int, y int) int {
61+
result := x + y
62+
return result
63+
}
64+
""",
65+
}
66+
67+
68+
@pytest.mark.parametrize("ext,code", list(MULTILINE_SAMPLES.items()))
69+
def test_symbol_code_contains_full_body(ext: str, code: str):
70+
"""Test that extract_symbols returns full function body in 'code' field, not just the name.
71+
72+
This is a regression test for issue #187 where the code field only contained
73+
the symbol name (e.g., 'myFunction') instead of the actual source code.
74+
"""
75+
parser = TreeSitterSymbolExtractor.get_parser(ext)
76+
query = TreeSitterSymbolExtractor.get_query(ext)
77+
if not parser or not query:
78+
pytest.skip(f"Language for {ext} not supported in this environment")
79+
80+
symbols = TreeSitterSymbolExtractor.extract_symbols(ext, code)
81+
assert symbols, f"No symbols extracted for {ext}"
82+
83+
# Find a function symbol
84+
func_symbols = [s for s in symbols if s.get("type") in ("function", "method")]
85+
assert func_symbols, f"No function symbols found for {ext}"
86+
87+
func = func_symbols[0]
88+
func_name = func.get("name")
89+
func_code = func.get("code", "")
90+
91+
# The code field should contain more than just the function name
92+
assert len(func_code) > len(func_name), (
93+
f"Code field for {ext} only contains name '{func_name}', expected full function body. "
94+
f"Got: '{func_code}'"
95+
)
96+
97+
# The code should contain the function keyword or definition
98+
assert func_name in func_code, f"Function name '{func_name}' not found in code for {ext}"
99+
100+
# For multi-line functions, end_line should be greater than start_line
101+
start_line = func.get("start_line", 0)
102+
end_line = func.get("end_line", 0)
103+
assert end_line > start_line, (
104+
f"For multi-line function in {ext}, expected end_line > start_line. "
105+
f"Got start_line={start_line}, end_line={end_line}"
106+
)

0 commit comments

Comments
 (0)