@@ -46,6 +46,76 @@ def _check_mcp_available() -> bool:
4646 return _mcp_available
4747
4848
49+ # Tool registry for lazy loading - minimal metadata for discovery
50+ # Full schemas are loaded on-demand via tenets_get_tool_schema()
51+ TOOL_REGISTRY : dict [str , dict [str , Any ]] = {
52+ # Analysis tools
53+ "tenets_examine" : {
54+ "category" : "analysis" ,
55+ "description" : "Analyze codebase structure and complexity metrics" ,
56+ "keywords" : ["structure" , "complexity" , "examine" , "analyze" , "metrics" , "overview" ],
57+ },
58+ "tenets_chronicle" : {
59+ "category" : "analysis" ,
60+ "description" : "Analyze git history and development patterns" ,
61+ "keywords" : ["git" , "history" , "commits" , "changes" , "chronicle" , "blame" ],
62+ },
63+ "tenets_momentum" : {
64+ "category" : "analysis" ,
65+ "description" : "Track development velocity and team momentum" ,
66+ "keywords" : ["velocity" , "momentum" , "sprint" , "team" , "activity" , "progress" ],
67+ },
68+ # Session tools
69+ "tenets_session_create" : {
70+ "category" : "session" ,
71+ "description" : "Create a development session for stateful context" ,
72+ "keywords" : ["session" , "create" , "new" , "workflow" ],
73+ },
74+ "tenets_session_list" : {
75+ "category" : "session" ,
76+ "description" : "List all development sessions" ,
77+ "keywords" : ["session" , "list" , "show" , "all" ],
78+ },
79+ "tenets_session_pin_file" : {
80+ "category" : "session" ,
81+ "description" : "Pin a file to a session for guaranteed inclusion" ,
82+ "keywords" : ["pin" , "file" , "session" , "include" ],
83+ },
84+ "tenets_session_pin_folder" : {
85+ "category" : "session" ,
86+ "description" : "Pin all files in a folder to a session" ,
87+ "keywords" : ["pin" , "folder" , "directory" , "session" ],
88+ },
89+ # Tenet tools
90+ "tenets_tenet_add" : {
91+ "category" : "tenet" ,
92+ "description" : "Add a guiding principle for consistent AI interactions" ,
93+ "keywords" : ["tenet" , "add" , "principle" , "rule" , "guideline" ],
94+ },
95+ "tenets_tenet_list" : {
96+ "category" : "tenet" ,
97+ "description" : "List all tenets with optional filtering" ,
98+ "keywords" : ["tenet" , "list" , "show" , "principles" ],
99+ },
100+ "tenets_tenet_instill" : {
101+ "category" : "tenet" ,
102+ "description" : "Instill pending tenets, marking them active" ,
103+ "keywords" : ["tenet" , "instill" , "activate" , "apply" ],
104+ },
105+ "tenets_set_system_instruction" : {
106+ "category" : "tenet" ,
107+ "description" : "Set a system instruction for AI interactions" ,
108+ "keywords" : ["system" , "instruction" , "prompt" , "set" ],
109+ },
110+ }
111+
112+ # Tools always available (not lazy loaded) - core discovery tools
113+ ALWAYS_AVAILABLE_TOOLS = {
114+ "tenets_distill" , "tenets_rank_files" ,
115+ "tenets_search_tools" , "tenets_get_tool_schema"
116+ }
117+
118+
49119class TenetsMCP :
50120 """Tenets MCP Server.
51121
@@ -199,6 +269,185 @@ def _register_tools(self) -> None:
199269 """
200270 mcp = self ._mcp
201271
272+ # === Meta-Tools (Tool Discovery / Lazy Loading) ===
273+
274+ @mcp .tool ()
275+ async def tenets_search_tools (
276+ query : str ,
277+ category : Optional [Literal ["analysis" , "session" , "tenet" ]] = None ,
278+ ) -> list [dict [str , Any ]]:
279+ """Search available tenets tools by keyword or category.
280+
281+ USE THIS TOOL when: you want to discover what tools are available beyond distill/rank,
282+ need specialized functionality, or want to explore session/tenet management.
283+
284+ Args:
285+ - query: Search term (matches name, description, or keywords).
286+ - category: Optional filter: "analysis" (examine, chronicle, momentum),
287+ "session" (session management), "tenet" (guiding principles).
288+
289+ Returns: List of matching tools [{name, category, description}].
290+ Call tenets_get_tool_schema() with tool name to get full input parameters.
291+ """
292+ query_lower = query .lower ()
293+ results = []
294+
295+ for name , info in TOOL_REGISTRY .items ():
296+ if category and info ["category" ] != category :
297+ continue
298+
299+ if (
300+ query_lower in name .lower ()
301+ or query_lower in info ["description" ].lower ()
302+ or any (query_lower in kw for kw in info ["keywords" ])
303+ ):
304+ results .append ({
305+ "name" : name ,
306+ "category" : info ["category" ],
307+ "description" : info ["description" ],
308+ })
309+
310+ return results
311+
312+ @mcp .tool ()
313+ async def tenets_get_tool_schema (tool_name : str ) -> dict [str , Any ]:
314+ """Get complete input schema for a specific tool.
315+
316+ USE THIS TOOL when: you found a tool via tenets_search_tools and need its parameters.
317+
318+ Args:
319+ - tool_name: Exact tool name (e.g., "tenets_examine", "tenets_chronicle").
320+
321+ Returns: Full tool schema with parameters, types, descriptions.
322+ If tool not found, returns {error, available} with list of valid names.
323+ """
324+ # Core tools schemas
325+ core_schemas = {
326+ "tenets_distill" : {
327+ "name" : "tenets_distill" ,
328+ "description" : "Find and retrieve relevant code using semantic ranking" ,
329+ "parameters" : {
330+ "prompt" : {"type" : "string" , "required" : True , "description" : "Task description" },
331+ "path" : {"type" : "string" , "default" : "." , "description" : "Directory to search" },
332+ "mode" : {"type" : "string" , "enum" : ["fast" , "balanced" , "thorough" ], "default" : "balanced" },
333+ "max_tokens" : {"type" : "integer" , "default" : 100000 },
334+ "format" : {"type" : "string" , "enum" : ["markdown" , "xml" , "json" , "html" ], "default" : "markdown" },
335+ },
336+ },
337+ "tenets_rank_files" : {
338+ "name" : "tenets_rank_files" ,
339+ "description" : "Identify relevant files without fetching content" ,
340+ "parameters" : {
341+ "prompt" : {"type" : "string" , "required" : True },
342+ "path" : {"type" : "string" , "default" : "." },
343+ "mode" : {"type" : "string" , "enum" : ["fast" , "balanced" , "thorough" , "ml" ], "default" : "balanced" },
344+ "top_n" : {"type" : "integer" , "default" : 20 },
345+ "explain" : {"type" : "boolean" , "default" : False },
346+ },
347+ },
348+ }
349+
350+ # Discoverable tool schemas
351+ tool_schemas = {
352+ "tenets_examine" : {
353+ "name" : "tenets_examine" ,
354+ "description" : "Analyze codebase structure and quality metrics" ,
355+ "parameters" : {
356+ "path" : {"type" : "string" , "default" : "." },
357+ "include_complexity" : {"type" : "boolean" , "default" : True },
358+ "include_hotspots" : {"type" : "boolean" , "default" : True },
359+ },
360+ },
361+ "tenets_chronicle" : {
362+ "name" : "tenets_chronicle" ,
363+ "description" : "Analyze git history and development patterns" ,
364+ "parameters" : {
365+ "path" : {"type" : "string" , "default" : "." },
366+ "since" : {"type" : "string" , "default" : "1 week" },
367+ "author" : {"type" : "string" , "optional" : True },
368+ },
369+ },
370+ "tenets_momentum" : {
371+ "name" : "tenets_momentum" ,
372+ "description" : "Track development velocity and team momentum" ,
373+ "parameters" : {
374+ "path" : {"type" : "string" , "default" : "." },
375+ "since" : {"type" : "string" , "default" : "last-month" },
376+ "team" : {"type" : "boolean" , "default" : False },
377+ },
378+ },
379+ "tenets_session_create" : {
380+ "name" : "tenets_session_create" ,
381+ "description" : "Create a new development session" ,
382+ "parameters" : {
383+ "name" : {"type" : "string" , "required" : True },
384+ "description" : {"type" : "string" , "optional" : True },
385+ },
386+ },
387+ "tenets_session_list" : {
388+ "name" : "tenets_session_list" ,
389+ "description" : "List all development sessions" ,
390+ "parameters" : {},
391+ },
392+ "tenets_session_pin_file" : {
393+ "name" : "tenets_session_pin_file" ,
394+ "description" : "Pin a file to a session" ,
395+ "parameters" : {
396+ "session" : {"type" : "string" , "required" : True },
397+ "file_path" : {"type" : "string" , "required" : True },
398+ },
399+ },
400+ "tenets_session_pin_folder" : {
401+ "name" : "tenets_session_pin_folder" ,
402+ "description" : "Pin all files in a folder to a session" ,
403+ "parameters" : {
404+ "session" : {"type" : "string" , "required" : True },
405+ "folder_path" : {"type" : "string" , "required" : True },
406+ "patterns" : {"type" : "array" , "items" : {"type" : "string" }, "optional" : True },
407+ },
408+ },
409+ "tenets_tenet_add" : {
410+ "name" : "tenets_tenet_add" ,
411+ "description" : "Add a guiding principle" ,
412+ "parameters" : {
413+ "content" : {"type" : "string" , "required" : True },
414+ "priority" : {"type" : "string" , "enum" : ["low" , "medium" , "high" , "critical" ], "default" : "medium" },
415+ "category" : {"type" : "string" , "optional" : True },
416+ "session" : {"type" : "string" , "optional" : True },
417+ },
418+ },
419+ "tenets_tenet_list" : {
420+ "name" : "tenets_tenet_list" ,
421+ "description" : "List all tenets" ,
422+ "parameters" : {
423+ "session" : {"type" : "string" , "optional" : True },
424+ "pending_only" : {"type" : "boolean" , "default" : False },
425+ },
426+ },
427+ "tenets_tenet_instill" : {
428+ "name" : "tenets_tenet_instill" ,
429+ "description" : "Instill pending tenets" ,
430+ "parameters" : {
431+ "session" : {"type" : "string" , "optional" : True },
432+ "force" : {"type" : "boolean" , "default" : False },
433+ },
434+ },
435+ "tenets_set_system_instruction" : {
436+ "name" : "tenets_set_system_instruction" ,
437+ "description" : "Set a system instruction for AI interactions" ,
438+ "parameters" : {
439+ "instruction" : {"type" : "string" , "required" : True },
440+ "position" : {"type" : "string" , "enum" : ["top" , "after_header" , "before_content" ], "default" : "top" },
441+ },
442+ },
443+ }
444+
445+ all_schemas = {** core_schemas , ** tool_schemas }
446+ if tool_name in all_schemas :
447+ return all_schemas [tool_name ]
448+
449+ return {"error" : f"Tool '{ tool_name } ' not found" , "available" : list (all_schemas .keys ())}
450+
202451 # === Context Tools ===
203452
204453 @mcp .tool ()
0 commit comments