7
7
ListToolsRequestSchema ,
8
8
ListToolsResult ,
9
9
} from "@modelcontextprotocol/sdk/types.js" ;
10
- import { mcpError } from "./util.js" ;
11
- import { ServerFeature } from "./types.js" ;
10
+ import { checkFeatureActive , mcpError } from "./util.js" ;
11
+ import { SERVER_FEATURES , ServerFeature } from "./types.js" ;
12
12
import { availableTools } from "./tools/index.js" ;
13
13
import { ServerTool } from "./tool.js" ;
14
14
import { configstore } from "../configstore.js" ;
@@ -30,6 +30,7 @@ export class FirebaseMcpServer {
30
30
projectRoot ?: string ;
31
31
server : Server ;
32
32
activeFeatures ?: ServerFeature [ ] ;
33
+ detectedFeatures ?: ServerFeature [ ] ;
33
34
fixedRoot ?: boolean ;
34
35
35
36
constructor ( options : { activeFeatures ?: ServerFeature [ ] ; projectRoot ?: string } ) {
@@ -44,30 +45,34 @@ export class FirebaseMcpServer {
44
45
process . env . PROJECT_ROOT ??
45
46
process . cwd ( ) ;
46
47
if ( options . projectRoot ) this . fixedRoot = true ;
48
+ this . detectActiveFeatures ( ) ;
49
+ }
50
+
51
+ async detectActiveFeatures ( ) : Promise < ServerFeature [ ] > {
52
+ if ( this . detectedFeatures ?. length ) return this . detectedFeatures ; // memoized
53
+ const options = await this . resolveOptions ( ) ;
54
+ const projectId = await this . getProjectId ( ) ;
55
+ const detected = await Promise . all (
56
+ SERVER_FEATURES . map ( async ( f ) => {
57
+ if ( await checkFeatureActive ( f , projectId , options ) ) return f ;
58
+ return null ;
59
+ } ) ,
60
+ ) ;
61
+ this . detectedFeatures = detected . filter ( ( f ) => ! ! f ) as ServerFeature [ ] ;
62
+ return this . detectedFeatures ;
47
63
}
48
64
49
65
get availableTools ( ) : ServerTool [ ] {
50
- return availableTools ( ! ! this . fixedRoot , this . activeFeatures ) ;
66
+ return availableTools (
67
+ ! ! this . fixedRoot ,
68
+ this . activeFeatures ?. length ? this . activeFeatures : this . detectedFeatures ,
69
+ ) ;
51
70
}
52
71
53
72
getTool ( name : string ) : ServerTool | null {
54
73
return this . availableTools . find ( ( t ) => t . mcp . name === name ) || null ;
55
74
}
56
75
57
- async mcpListTools ( ) : Promise < ListToolsResult > {
58
- const hasActiveProject = ! ! ( await this . getProjectId ( ) ) ;
59
- await trackGA4 ( "mcp_list_tools" , { } ) ;
60
- return {
61
- tools : this . availableTools . map ( ( t ) => t . mcp ) ,
62
- _meta : {
63
- projectRoot : this . projectRoot ,
64
- projectDetected : hasActiveProject ,
65
- authenticated : await this . getAuthenticated ( ) ,
66
- activeFeatures : this . activeFeatures ,
67
- } ,
68
- } ;
69
- }
70
-
71
76
setProjectRoot ( newRoot : string | null ) : void {
72
77
if ( newRoot === null ) {
73
78
configstore . delete ( PROJECT_ROOT_KEY ) ;
@@ -78,6 +83,7 @@ export class FirebaseMcpServer {
78
83
79
84
configstore . set ( PROJECT_ROOT_KEY , newRoot ) ;
80
85
this . projectRoot = newRoot ;
86
+ this . detectedFeatures = undefined ; // reset detected features
81
87
void this . server . sendToolListChanged ( ) ;
82
88
}
83
89
@@ -100,6 +106,22 @@ export class FirebaseMcpServer {
100
106
}
101
107
}
102
108
109
+ async mcpListTools ( ) : Promise < ListToolsResult > {
110
+ if ( ! this . activeFeatures ) await this . detectActiveFeatures ( ) ;
111
+ const hasActiveProject = ! ! ( await this . getProjectId ( ) ) ;
112
+ await trackGA4 ( "mcp_list_tools" , { } ) ;
113
+ return {
114
+ tools : this . availableTools . map ( ( t ) => t . mcp ) ,
115
+ _meta : {
116
+ projectRoot : this . projectRoot ,
117
+ projectDetected : hasActiveProject ,
118
+ authenticated : await this . getAuthenticated ( ) ,
119
+ activeFeatures : this . activeFeatures ,
120
+ detectedFeatures : this . detectedFeatures ,
121
+ } ,
122
+ } ;
123
+ }
124
+
103
125
async mcpCallTool ( request : CallToolRequest ) : Promise < CallToolResult > {
104
126
const toolName = request . params . name ;
105
127
const toolArgs = request . params . arguments ;
0 commit comments