44// before a server can be connected. Approval is invalidated if the
55// server config (command + args) changes.
66
7- import { readFileSync , writeFileSync , existsSync } from "node:fs" ;
8- import { join } from "node:path" ;
7+ import { readFileSync , writeFileSync , existsSync , mkdirSync } from "node:fs" ;
8+ import { join , dirname } from "node:path" ;
99import { homedir } from "node:os" ;
1010
1111import type {
@@ -36,6 +36,11 @@ export function loadMCPApprovalStore(): MCPApprovalStore {
3636 */
3737function saveMCPApprovalStore ( store : MCPApprovalStore ) : void {
3838 try {
39+ // Ensure directory exists (first-run case)
40+ const dir = dirname ( APPROVAL_FILE ) ;
41+ if ( ! existsSync ( dir ) ) {
42+ mkdirSync ( dir , { recursive : true , mode : 0o700 } ) ;
43+ }
3944 writeFileSync ( APPROVAL_FILE , JSON . stringify ( store , null , 2 ) , {
4045 mode : 0o600 ,
4146 } ) ;
@@ -53,12 +58,27 @@ export function isMCPApproved(
5358 name : string ,
5459 config : MCPServerConfig ,
5560 store : MCPApprovalStore ,
61+ currentTools ?: string [ ] ,
5662) : boolean {
5763 const record = store [ name ] ;
5864 if ( ! record ) return false ;
5965
6066 const currentHash = computeMCPConfigHash ( name , config ) ;
61- return record . configHash === currentHash ;
67+ if ( record . configHash !== currentHash ) return false ;
68+
69+ // If tool list is provided, verify it matches approval-time tools
70+ if ( currentTools && Array . isArray ( record . approvedTools ) ) {
71+ const approved = [ ...record . approvedTools ] . sort ( ) ;
72+ const current = [ ...currentTools ] . sort ( ) ;
73+ if (
74+ approved . length !== current . length ||
75+ ! approved . every ( ( t , i ) => t === current [ i ] )
76+ ) {
77+ return false ; // Tool set changed — re-approval required
78+ }
79+ }
80+
81+ return true ;
6282}
6383
6484/**
0 commit comments