Setting the file. One moment.
Prepare · N8n:testmax N8n AI · n8n-io/n8n · Skills Docs
ContentsBack to the top of the page scripts/prepare.py
scripts/ prepare.py
Python · 109 lines · 4 KB
urllib.request
16
17 from n8n_api import TOKEN_FILE , N8nClient, env_files, is_full_token, load_token, save_token, setting
18
19
20 def mcp_handshake (base_url, token):
21 body = json.dumps({
22 'jsonrpc' : '2.0' , 'id' : 1 , 'method' : 'initialize' ,
23 'params' : { 'protocolVersion' : '2025-03-26' , 'capabilities' : {},
24 'clientInfo' : { 'name' : 'testmax-n8n-ai' , 'version' : '1' }},
25 }).encode()
26 req = urllib.request.Request(base_url + '/mcp-server/http' , data = body, method = 'POST' , headers = {
27 'content-type' : 'application/json' ,
28 'accept' : 'application/json, text/event-stream' ,
29 'authorization' : f 'Bearer { token } ' ,
30 })
31 try :
32 with urllib.request.urlopen(req, timeout = 20 ) as res:
33 return res.status == 200
34 except Exception :
35 return False
36
37
38 def docker_ready ():
39 if not shutil.which( 'docker' ):
40 return False
41 return subprocess.run([ 'docker' , 'info' ], capture_output = True ).returncode == 0
42
43
44 def main ():
45 ap = argparse.ArgumentParser()
46 ap.add_argument( '--rotate' , action = 'store_true' )
47 args = ap.parse_args()
48
49 status = {
50 'configFiles' : [ str (p) for p in env_files() if p.exists()],
51 'tokenFile' : str ( TOKEN_FILE ),
52 'builders' : setting( 'COMPARE_BUILDERS' , default = 'iai,claude,codex' ),
53 'keys' : {
54 'claude' : bool (setting( 'CLAUDE_API_KEY' , 'ANTHROPIC_API_KEY' )),
55 'codex' : bool (setting( 'CODEX_API_KEY' , 'OPENAI_API_KEY' )),
56 'langsmith' : bool (setting( 'LANGSMITH_API_KEY' )),
57 },
58 }
59 client = N8nClient()
60 status[ 'baseUrl' ] = client.base_url
61 try :
62 client.login()
63 status[ 'login' ] = True
64 except Exception as e:
65 status[ 'login' ] = False
66 status[ 'error' ] = str (e)
67 print (json.dumps(status, indent = 2 ))
68 sys.exit( 1 )
69
70 try :
71 settings = client.request( 'GET' , '/rest/assistant/settings' )
72 status[ 'instanceAiEnabled' ] = bool (settings.get( 'enabled' ))
73 except Exception :
74 status[ 'instanceAiEnabled' ] = False
75
76 client.request( 'PATCH' , '/rest/mcp/settings' , { 'mcpAccessEnabled' : True })
77 status[ 'mcpAccessEnabled' ] = True
78
79 token = None
80 if args.rotate:
81 if setting( 'N8N_MCP_TOKEN' ):
82 # The clients would keep the configured token, which the rotation makes invalid.
83 sys.exit( 'Remove N8N_MCP_TOKEN from the config before you rotate the MCP key, then run prepare.py --rotate again.' )
84 token = client.request( 'POST' , '/rest/mcp/api-key/rotate' )[ 'apiKey' ]
85 save_token(token)
86 else :
87 current = client.request( 'GET' , '/rest/mcp/api-key' )[ 'apiKey' ]
88 if is_full_token(current):
89 # A new key was created for this user, so the full value is visible once.
90 save_token(current)
91 token = load_token()
92 status[ 'mcpTokenSource' ] = 'N8N_MCP_TOKEN' if setting( 'N8N_MCP_TOKEN' ) else 'cache'
93
94 status[ 'mcpTokenAvailable' ] = bool (token)
95 status[ 'mcpTokenWorks' ] = bool (token) and mcp_handshake(client.base_url, token)
96 if not status[ 'mcpTokenWorks' ]:
97 status[ 'mcpTokenHelp' ] = (
98 'The MCP token is missing or invalid. Set N8N_MCP_TOKEN in .env.ai-surfaces, or run '
99 'prepare.py --rotate after the user agrees. Rotation invalidates the old key in every other MCP client.'
100 )
101
102 status[ 'docker' ] = docker_ready()
103 status[ 'claudeOnHost' ] = bool (shutil.which( 'claude' ))
104 status[ 'codexOnHost' ] = bool (shutil.which( 'codex' ))
105 print (json.dumps(status, indent = 2 ))
106
107
108 if __name__ == '__main__' :
109 main()