
    ݗio@                       d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	m
Z
mZ ddlmZ ddlmZ  eej                   j#                  dd            Zej                   j#                  d	d
      Ze
 G d d             Ze
 G d d             Ze
 G d d             Z G d d      ZddZddZddZedk(  r e        yy)u  
openclaw_mcp.server — OpenClaw MCP (Model Context Protocol) gateway server.

OpenClaw is Genesis's unified MCP gateway. It sits between AI agents
(Claude, Gemini, etc.) and all the tools they can invoke. Every tool
call flows through OpenClaw, which:

1. ROUTES: Dispatches tool calls to the right MCP server
2. INTERCEPTS: Applies the interceptor middleware pipeline
3. LOGS: Writes every tool call to the audit log
4. THROTTLES: Rate-limits expensive tool calls per model/session
5. VALIDATES: Signs tool output before returning to agent
6. CACHES: Memoizes deterministic tool calls

Transport:
- Primary: stdio (for Claude Code sub-agents)
- Secondary: HTTP/SSE (for Gemini agents, web-based)
    )annotationsN)	dataclassfield)datetime)AnyOPENCLAW_PORT9000OPENCLAW_SECRET c                  b    e Zd ZU dZded<   ded<   ded<   ded<   ded<    ed	 
      Zded<   y)MCPToolCallzA single MCP tool invocation.strcall_id	tool_namezdict[str, Any]	arguments
session_idagent_idc                 F    t        j                         j                         S Nr   utcnow	isoformat     D/mnt/e/genesis-system/genesis-os/packages/openclaw-mcp/src/server.py<lambda>zMCPToolCall.<lambda>1   s    HOO4E4O4O4Q r   default_factory
created_atN)__name__
__module____qualname____doc____annotations__r   r   r   r   r   r   r   )   s/    'LNOM,QRJRr   r   c                  p    e Zd ZU dZded<   ded<   ded<   ded<   d	ed
<   dZded<    ed       Zded<   y)MCPToolResultz!Result of an MCP tool invocation.r   r   r   r   result
str | Noneerrorfloat
elapsed_msNsunaiva_signaturec                 F    t        j                         j                         S r   r   r   r   r   r   zMCPToolResult.<lambda>=   s    hoo6G6Q6Q6S r   r   completed_at)r    r!   r"   r#   r$   r,   r   r.   r   r   r   r&   r&   4   s;    +LNK$(z(.STL#Tr   r&   c                  d    e Zd ZU dZded<   ded<   ded<   ded<   dZded	<   dZded
<   dZded<   y)ToolRegistryEntryz5Registry entry mapping a tool name to its MCP server.r   r   
mcp_server	transportendpointFboolrequires_auth	cacheabler   intcache_ttl_secondsN)r    r!   r"   r#   r$   r5   r6   r8   r   r   r   r0   r0   @   s8    ?NONMM4Itsr   r0   c                      e Zd ZdZddZddZdddZddZddZddZ		 	 	 	 	 	 dd	Z
	 	 	 	 	 	 dd
Z	 	 	 	 	 	 ddZddZddZy)	MCPRouterz
    Routes tool calls to the appropriate MCP server.

    Loads tool registry from mcp-config.json at startup.
    Supports tool dispatch with interceptor chain integration.
    c                     i | _         d | _        y r   )	_registry_interceptor_chainselfs    r   __init__zMCPRouter.__init__X   s    79'+r   c                    || _         y)z$Attach an InterceptorChain instance.N)r=   )r?   chains     r   set_interceptor_chainzMCPRouter.set_interceptor_chain\   s
    "'r   Nc                <  K   |xs  t         j                  j                  dd      }t         j                  j	                  |      syt        |d      5 }t        j                  |      }ddd       d}j                  di       }|j                         D ]v  \  }}d}	d}
d	|v r|d	   }
d}	nd
|v r|d
   }
d}	t        |||	|
|j                  dd      |j                  dd      |j                  dd            }|| j                  |<   |dz  }x |S # 1 sw Y   xY ww)zf
        Load tool registry from mcp-config.json.
        Returns number of tools registered.
        MCP_CONFIG_PATHz%/mnt/e/genesis-system/mcp-config.jsonr   rN
mcpServersstdior   commandurlhttpr5   Fr6   r8   )r   r1   r2   r3   r5   r6   r8      )osenvirongetpathexistsopenjsonloaditemsr0   r<   )r?   config_pathrP   fconfigcountmcp_serversserver_nameserver_configr2   r3   entrys               r   load_registryzMCPRouter.load_registry`   s?    
  
bjjnn3

 ww~~d#$_ 	"YYq\F	" jjr2*5*;*;*= 	&KIH M)(3#	-'(/"	 &%&#!+//G'++K?"/"3"34G"KE +0DNN;'QJE/	2 ?	" 	"s   ADD)B'DDDc                6    || j                   |j                  <   y)zManually register a tool.N)r<   r   )r?   r]   s     r   register_toolzMCPRouter.register_tool   s    */u'r   c                    || j                   v r| j                   |   S d|v r|j                  d      d   n|}| j                   j                  |      S )zLook up a tool in the registry._r   )r<   splitrO   )r?   r   prefixs      r   	get_entryzMCPRouter.get_entry   sR     &>>),, -09,<%a()~~!!&))r   c                F  K   t        j                          }| j                  |j                        }|1t        |j                  |j                  dd|j                   d      S |}| j
                  a| j
                  j                  |       d{   }|<t        |j                  |j                  ddt        j                          |z
  dz        S |j                  j                  dd      }|=t        |j                  |j                  |dt        j                          |z
  dz        }n9| j                  ||       d{   }t        j                          |z
  dz  |_
        | j
                  $| j
                  j                  ||       d{   }|S 7 	7 X7 w)a  
        Route a tool call to the appropriate MCP server.
        1. Look up tool_name in registry
        2. Apply pre-interceptor chain
        3. Forward to MCP server (or mock in dev)
        4. Apply post-interceptor chain
        5. Return MCPToolResult
        NzUnknown tool:         r   r   r'   r)   r+   zBlocked by interceptor chaini  __cached_result)timere   r   r&   r   r=   run_prer   pop_execute_toolr+   run_post)r?   call
start_timer]   processed_callcachedr'   s          r   dispatchzMCPRouter.dispatch   s     YY[
 t~~.= ..&t~~&67  "".#'#:#:#B#B4#HHN%$ LL"nn8 $		j 8D@   ))--.?F".. IIK*4<F  --e^DDF!%z!9T AF "".22;;NFSSF; I, E
 Ts8   BF!FB2F!FAF!FF!F!F!c                  K   |j                   dk(  r| j                  ||       d{   S |j                   dk(  r| j                  ||       d{   S t        |j                  |j
                  dd|j                    d      S 7 ^7 7w)z
        Execute a tool call against the registered MCP server.
        In development: returns a mock result.
        In production: forwards via stdio subprocess or HTTP.
        rH   NrK   zUnsupported transport: rg   rh   )r2   _execute_stdio_execute_httpr&   r   r   )r?   r]   ro   s      r   rm   zMCPRouter._execute_tool   s      ??g%,,UD999__&++E4888 ..//@A 	 :8s!   %B
B(B
B6B
B
c                $  K   dd|j                   |j                  d|j                  d}	 t        |j                  t
              r|j                  j                         n|j                  g}t        j                  |t        j                  j                  t        j                  j                  t        j                  j                  d d{   }t        j                  |      dz   j                         }t        j                  |j                  |      d	       d{   \  }}|j                   d
k7  rEt#        |j                  |j                   dd|j                    d|j%                         dd  d      S t        j&                  |j%                               }	d|	v r7t#        |j                  |j                   d|	d   j)                  dd      d      S t#        |j                  |j                   |	j)                  d      dd      S 7 A7 # t        j*                  $ r' t#        |j                  |j                   ddd      cY S t,        $ r7}
t#        |j                  |j                   dt        |
      d      cY d}
~
S d}
~
ww xY ww)z
        Execute a tool call via stdio MCP server subprocess.
        Sends JSON-RPC 2.0 message to stdin, reads from stdout.
        2.0
tools/callnamer   jsonrpcmethodparamsid)stdinstdoutstderrN
      >@timeoutr   zProcess exited with code z: i  rg   rh   r)   messageUnknown errorr'   zTool call timed out (30s)g     L@)r   r   r   
isinstancer3   r   rc   asynciocreate_subprocess_exec
subprocessPIPErS   dumpsencodewait_forcommunicate
returncoder&   decodeloadsrO   TimeoutError	Exception)r?   r]   ro   rpc_requestargsprocessrequest_bytesr   r   responsees              r   ru   zMCPRouter._execute_stdio   sC     "!^^ ,,
<	-7-L5>>'')SXSaSaRbD#::((--))..))..	 G "ZZ4t;CCEM#*#3#3##M2$ NFF
 !!Q&$ LL"nn5g6H6H5IFMMO\`]`LaKbc"  zz&--/2H("$ LL"nn"7+//	?K"  !..||H- EB ## 	 ..1"   	 ..!f 	s   )JB#H HAH #H$AH >J?AH J2H JH H 7JJJ,JJJJJc           
       K   	 ddl }|j                  d      4 d{   }|j                  |j                  dd|j                  |j
                  d|j                  d	       d{   }|j                         }d
|v rHt        |j                  |j                  d|d
   j                  dd      d      cddd      d{    S t        |j                  |j                  |j                  d      dd      cddd      d{    S 7 7 7 M7 # 1 d{  7  sw Y   yxY w# t        $ r7}t        |j                  |j                  dt        |      d      cY d}~S d}~ww xY ww)z(Execute a tool call via HTTP MCP server.r   Nr   r   rx   ry   rz   r|   )rS   r)   r   r   rg   rh   r'   )httpxAsyncClientpostr3   r   r   r   rS   r&   rO   r   r   )r?   r]   ro   r   clientr   datar   s           r   rv   zMCPRouter._execute_http9  sS    &	(((6  &!'NN#(".+/>>"W"ll	 "- "   }}d?( $"&..#"7m//	?K#&  * % LL"nn88H-"+      :  	 ..!f 	s   E.D+ DD+ AD)D*AD8D+ DD+ 	E.
2D<D+ D	D+ E.D+ DD+ D+ D(DD($D+ 'E.(D+ +	E+4,E& E+!E.&E++E.c                ,    t        | j                        S )z"Return number of registered tools.)lenr<   r>   s    r   get_tool_countzMCPRouter.get_tool_counte  s    4>>""r   c                H    t        | j                  j                               S )z%Return list of registered tool names.)listr<   keysr>   s    r   
list_toolszMCPRouter.list_toolsi  s    DNN'')**r   returnNone)rB   r   r   r   r   )rV   r(   r   r7   )r]   r0   r   r   )r   r   r   zToolRegistryEntry | None)ro   r   r   r&   )r]   r0   ro   r   r   r&   )r   r7   )r   z	list[str])r    r!   r"   r#   r@   rC   r^   r`   re   rs   rm   ru   rv   r   r   r   r   r   r:   r:   P   s    ,(,\0*6p&.9	*N&N.9N	N`*&*.9*	*X#+r   r:   c           
       K   t        j                         }t        j                  |      t        j                         j	                  fdt
        j                         d{    	 |j                          d{   }|sy	 t        j                  |j                               }|j                  dd      }|j                  d      }|j                  di       }|dk(  rt        t        |xs! t        j                          j"                  dd	       |j                  d
d      |j                  di       |j                  dd      |j                  dd            }| j%                  |       d{   }d||j&                  %ddt        j(                  |j*                        dgind|j&                  rd|j&                  dndd}	nT|dk(  r,| j-                         D 
cg c]
  }
|
d|
 d }}
d|d|id}	n#|dk(  rd|ddddiid d!d"d#d}	nd|d$d%| dd&}	|	j                  d'      |	j/                  d'd       |	j                  d(      d'|	v r|	j/                  d(d       t
        j0                  j3                  t        j(                  |	      d)z          t
        j0                  j5                          U7 [7 E# t        j                  $ r Y rw xY w7 lc c}
w w)*z
    Run OpenClaw as a stdio MCP server.

    Reads JSON-RPC 2.0 messages from stdin, processes them via router,
    writes responses to stdout. This is the mode used by Claude Code.
    c                      S r   r   )protocols   r   r   z"run_stdio_server.<locals>.<lambda>{  s    X r   NTr~   r   r   r   ry      r{   r   r   rH   r   unknown)r   r   r   r   r   rx   contenttext)typer   i )coder   )r}   r   r'   r)   z
tools/listz
MCP tool: )r{   descriptiontools)r}   r   r'   
initializez
2024-11-05listChangedopenclaw-mcp0.1.0)r{   version)protocolVersioncapabilities
serverInfoizMethod not found: )r}   r   r)   r)   r'   r   )r   StreamReaderStreamReaderProtocolget_event_loopconnect_read_pipesysr   readlinerS   r   r   JSONDecodeErrorrO   r   r   uuiduuid4hexrs   r)   r   r'   r   rl   r   writeflush)routerreaderliner   r~   msg_idr   ro   r'   r   r{   r   r   s               @r   run_stdio_serverr   r  s     !!#F++F3H

 
 
"
4
45Esyy
QQQ
__&&	jj/G Xr*T"Xr*\!F;djjl&6&6s&;< **VR0 **["5!::lG<J	:D "??400F ! \\) FMM9R ST/3FLll&V\\BX\H |# #--/ 
4&.ABE  $)7EBRSH|# '3%,}d.C$D+9g"NH !"(7I&5RSH << (LL$'<<!)g.ALL4(

H-45

C  R ' ## 		 1si   A"K(%K &K(>K?K(#K *C
K(4K 5A/K($K#3CK(K(KK(KK(#K(c                 v    	 ddl m}   | dd      }|j                  d      d	d       }|S # t        $ r Y yw xY w)
z7Create a minimal FastAPI health endpoint for HTTP mode.r   )FastAPIzOpenClaw MCPr   )titler   z/healthc                    K   ddddS w)Nokr   r   )statusservicer   r   r   r   r   healthz!create_health_app.<locals>.health  s     "~'RRs   	N)r   dict)fastapir   rO   ImportError)r   appr   s      r   create_health_appr     sL    
#NG<			S 
	S 
 s   ), 	88c                    t         j                  j                  dd      } | dk(  r)t               }t	        j
                  t        |             y| dk(  rJddl}t               }|r|j                  |dt               yt        d       t        j                  d	       yt        d
|  d       t        j                  d	       y)z(CLI entry point: `openclaw-mcp` command.OPENCLAW_MODErH   rK   r   Nz0.0.0.0)hostportz;[openclaw-mcp] FastAPI not installed. Cannot run HTTP mode.rL   z[openclaw-mcp] Unknown mode: z. Use 'stdio' or 'http'.)rM   rN   rO   r:   r   runr   uvicornr   PORTprintr   exit)moder   r   r   s       r   mainr     s    ::>>/73Dw$V,-	!KK)$K7OPHHQK-dV3KLMr   __main__)r   r:   r   r   )r   r   r   )r#   
__future__r   r   rS   rM   r   rj   r   dataclassesr   r   r   typingr   r7   rN   rO   r   r
   r   r&   r0   r:   r   r   r   r    r   r   r   <module>r      s   & #   	 
   (   2::>>/623**..!2B7 S S S U U U   [+ [+D	Lf&( zF r   