@@ -3,9 +3,11 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
33import {
44 CallToolRequest ,
55 CallToolRequestSchema ,
6- CallToolResult ,
7- ListToolsRequestSchema ,
86 ListToolsResult ,
7+ LoggingLevel ,
8+ SetLevelRequestSchema ,
9+ ListToolsRequestSchema ,
10+ CallToolResult ,
911} from "@modelcontextprotocol/sdk/types.js" ;
1012import { checkFeatureActive , mcpError } from "./util.js" ;
1113import { ClientConfig , SERVER_FEATURES , ServerFeature } from "./types.js" ;
@@ -30,6 +32,17 @@ const SERVER_VERSION = "0.1.0";
3032
3133const cmd = new Command ( "experimental:mcp" ) . before ( requireAuth ) ;
3234
35+ const orderedLogLevels = [
36+ "debug" ,
37+ "info" ,
38+ "notice" ,
39+ "warning" ,
40+ "error" ,
41+ "critical" ,
42+ "alert" ,
43+ "emergency" ,
44+ ] as const ;
45+
3346export class FirebaseMcpServer {
3447 private _ready : boolean = false ;
3548 private _readyPromises : { resolve : ( ) => void ; reject : ( err : unknown ) => void } [ ] = [ ] ;
@@ -41,11 +54,22 @@ export class FirebaseMcpServer {
4154 clientInfo ?: { name ?: string ; version ?: string } ;
4255 emulatorHubClient ?: EmulatorHubClient ;
4356
57+ // logging spec:
58+ // https://modelcontextprotocol.io/specification/2025-03-26/server/utilities/logging
59+ currentLogLevel ?: LoggingLevel ;
60+ // the api of logging from a consumers perspective looks like `server.logger.warn("my warning")`.
61+ public readonly logger = Object . fromEntries (
62+ orderedLogLevels . map ( ( logLevel ) => [
63+ logLevel ,
64+ ( message : unknown ) => this . log ( logLevel , message ) ,
65+ ] ) ,
66+ ) as Record < LoggingLevel , ( message : unknown ) => Promise < void > > ;
67+
4468 constructor ( options : { activeFeatures ?: ServerFeature [ ] ; projectRoot ?: string } ) {
4569 this . activeFeatures = options . activeFeatures ;
4670 this . startupRoot = options . projectRoot || process . env . PROJECT_ROOT ;
4771 this . server = new Server ( { name : "firebase" , version : SERVER_VERSION } ) ;
48- this . server . registerCapabilities ( { tools : { listChanged : true } } ) ;
72+ this . server . registerCapabilities ( { tools : { listChanged : true } , logging : { } } ) ;
4973 this . server . setRequestHandler ( ListToolsRequestSchema , this . mcpListTools . bind ( this ) ) ;
5074 this . server . setRequestHandler ( CallToolRequestSchema , this . mcpCallTool . bind ( this ) ) ;
5175 this . server . oninitialized = async ( ) => {
@@ -64,6 +88,12 @@ export class FirebaseMcpServer {
6488 this . _readyPromises . pop ( ) ?. resolve ( ) ;
6589 }
6690 } ;
91+
92+ this . server . setRequestHandler ( SetLevelRequestSchema , async ( { params } ) => {
93+ this . currentLogLevel = params . level ;
94+ return { } ;
95+ } ) ;
96+
6797 this . detectProjectRoot ( ) ;
6898 this . detectActiveFeatures ( ) ;
6999 }
@@ -275,4 +305,24 @@ export class FirebaseMcpServer {
275305 const transport = new StdioServerTransport ( ) ;
276306 await this . server . connect ( transport ) ;
277307 }
308+
309+ private async log ( level : LoggingLevel , message : unknown ) {
310+ let data = message ;
311+
312+ // mcp protocol only takes jsons or it errors; for convienence, format
313+ // a a string into a json.
314+ if ( typeof message === "string" ) {
315+ data = { message } ;
316+ }
317+
318+ if ( ! this . currentLogLevel ) {
319+ return ;
320+ }
321+
322+ if ( orderedLogLevels . indexOf ( this . currentLogLevel ) < orderedLogLevels . indexOf ( level ) ) {
323+ return ;
324+ }
325+
326+ await this . server . sendLoggingMessage ( { level, data } ) ;
327+ }
278328}
0 commit comments