2020import convex .core .cvm .AccountStatus ;
2121import convex .core .cvm .Address ;
2222import convex .core .cvm .Peer ;
23- import convex .core .cvm .Symbols ;
2423import convex .core .cvm .transactions .ATransaction ;
2524import convex .core .cvm .transactions .Invoke ;
2625import convex .core .data .ACell ;
@@ -105,6 +104,15 @@ public class McpAPI extends ABaseAPI {
105104 public static final StringShort ARG_BYTES = Strings .intern ("bytes" );
106105 public static final StringShort ARG_ACCOUNT_KEY = Strings .intern ("accountKey" );
107106 public static final StringShort ARG_FAUCET = Strings .intern ("faucet" );
107+ public static final StringShort ARG_HASH = Strings .intern ("hash" );
108+ public static final StringShort ARG_CAD3 = Strings .intern ("cad3" );
109+ public static final StringShort ARG_GET_PATH = Strings .intern ("getPath" );
110+
111+ public static final StringShort KEY_NETWORK_ID = Strings .intern ("networkId" );
112+ public static final StringShort KEY_PEER_KEY = Strings .intern ("peerKey" );
113+ public static final StringShort KEY_VALUE = Strings .intern ("value" );
114+ public static final StringShort KEY_ERROR_CODE = Strings .intern ("errorCode" );
115+ public static final StringShort KEY_INFO = Strings .intern ("info" );
108116
109117 private static final AHashMap <AString , ACell > BASE_RESPONSE = Maps .of ("jsonrpc" , "2.0" );
110118 private static final AMap <AString , ACell > EMPTY_MAP = Maps .empty ();
@@ -114,14 +122,14 @@ public class McpAPI extends ABaseAPI {
114122 public McpAPI (RESTServer restServer ) {
115123 super (restServer );
116124 AMap <AString , ACell > info = Maps .of (
117- Strings . create ( "name" ), Strings . create ( "convex-mcp" ) ,
118- Strings . create ( "title" ), Strings . create ( "Convex MCP" ) ,
119- Strings . create ( "version" ), Strings . create ( Utils .getVersion () )
125+ "name" , "convex-mcp" ,
126+ "title" , "Convex MCP" ,
127+ "version" , Utils .getVersion ()
120128 );
121129 Peer peer = server .getPeer ();
122130 if (peer != null ) {
123- info = info .assoc (Strings . create ( "networkId" ) , Strings .create (peer .getNetworkID ().toHexString ()));
124- info = info .assoc (Strings . create ( "peerKey" ) , Strings .create (peer .getPeerKey ().toHexString ()));
131+ info = info .assoc (KEY_NETWORK_ID , Strings .create (peer .getNetworkID ().toHexString ()));
132+ info = info .assoc (KEY_PEER_KEY , Strings .create (peer .getPeerKey ().toHexString ()));
125133 }
126134 serverInfo = info ;
127135 registerTools ();
@@ -239,11 +247,11 @@ private AMap<AString, ACell> createResponse(AMap<?, ?> request) {
239247 }
240248
241249 private AMap <AString , ACell > buildInitializeResult () {
242- AMap <AString , ACell > capabilities = Maps .of (Strings . create ( "tools" ) , EMPTY_MAP );
250+ AMap <AString , ACell > capabilities = Maps .of ("tools" , EMPTY_MAP );
243251 AMap <AString , ACell > result = Maps .of (
244- Strings . create ( "protocolVersion" ), Strings . create ( "2025-03-26" ) ,
245- Strings . create ( "serverInfo" ) , serverInfo ,
246- Strings . create ( "capabilities" ) , capabilities
252+ "protocolVersion" , "2025-03-26" ,
253+ "serverInfo" , serverInfo ,
254+ "capabilities" , capabilities
247255 );
248256 return result ;
249257 }
@@ -257,7 +265,7 @@ private AVector<AMap<AString, ACell>> listToolsVector() {
257265 }
258266
259267 private AMap <AString , ACell > listTools () {
260- return Maps .of (Strings . create ( "tools" ) , listToolsVector ());
268+ return Maps .of ("tools" , listToolsVector ());
261269 }
262270
263271 /* JSON-RPC protocol result */
@@ -269,7 +277,7 @@ private AMap<AString, ACell> protocolResult(AMap<AString, ACell> result) {
269277 private AMap <AString , ACell > protocolError (int code , String message ) {
270278 AMap <AString , ACell > error = Maps .of (
271279 FIELD_CODE , CVMLong .create (code ),
272- FIELD_MESSAGE , Strings . create ( message )
280+ FIELD_MESSAGE , message
273281 );
274282 return BASE_RESPONSE .assoc (FIELD_ERROR , error );
275283 }
@@ -308,15 +316,15 @@ private AMap<AString, ACell> toolResult(Result result) {
308316 AMap <AString , ACell > structured = EMPTY_MAP ;
309317 ACell value = result .getValue ();
310318 if (value != null ) {
311- structured = structured .assoc (Strings . create ( "value" ) , value );
319+ structured = structured .assoc (KEY_VALUE , value );
312320 }
313321 ACell errorCode = result .getErrorCode ();
314322 if (errorCode != null ) {
315- structured = structured .assoc (Strings . create ( "errorCode" ) , errorCode );
323+ structured = structured .assoc (KEY_ERROR_CODE , errorCode );
316324 }
317325 ACell info = result .getInfo ();
318326 if (info != null ) {
319- structured = structured .assoc (Strings . create ( "info" ) , info );
327+ structured = structured .assoc (KEY_INFO , info );
320328 }
321329 return protocolResult (buildMcpResult (structured , result .isError ()));
322330 }
@@ -330,15 +338,15 @@ private AMap<AString, ACell> toolSuccess(ACell structuredResult) {
330338 /* Create an error result for a tool call (but protocol valid) */
331339 private AMap <AString , ACell > toolError (String message ) {
332340 AMap <AString , ACell > payload = Maps .of (
333- Strings . create ( "message" ), Strings . create ( message )
341+ "message" , message
334342 );
335343 return protocolResult (buildMcpResult (payload , true ));
336344 }
337345
338346 private AMap <AString , ACell > buildMcpResult (AMap <AString , ACell > structured , boolean isError ) {
339347 AString jsonText = JSON .print (structured );
340348 AMap <AString , ACell > textContent = Maps .of (
341- FIELD_TYPE , Strings . create ( "text" ) ,
349+ FIELD_TYPE , "text" ,
342350 FIELD_TEXT , jsonText
343351 );
344352 AVector <AMap <AString , ACell >> content = Vectors .of (textContent );
@@ -496,14 +504,10 @@ public AMap<AString, ACell> handle(AMap<AString, ACell> arguments) {
496504 }
497505 sequence = seqLong .longValue ();
498506 } else try {
499-
500- sequence = restServer .getConvex ().getSequence (address ) + 1 ;
501- } catch (ResultException e ) {
502- return toolError ("Failed to get sequence number " +e .getMessage ());
503- } catch (InterruptedException e ) {
504- Thread .currentThread ().interrupt ();
505- return toolError ("Tool call interrupted" );
506- }
507+ sequence = server .getPeer ().getConsensusState ().getAccount (address ).getSequence () + 1 ;
508+ } catch (NullPointerException e ) {
509+ return toolError ("Failed to get sequence number, account does not exist: " +address );
510+ }
507511
508512 try {
509513 ATransaction transaction = Invoke .create (address , sequence , code );
@@ -512,11 +516,11 @@ public AMap<AString, ACell> handle(AMap<AString, ACell> arguments) {
512516 String hashHex = SignedData .getMessageForRef (ref ).toHexString ();
513517 String dataHex = Format .encodeMultiCell (transaction , true ).toHexString ();
514518 AMap <AString , ACell > structured = Maps .of (
515- Strings . create ( "source" ) , sourceCell ,
516- Strings . create ( "address" ), Strings . create ( address . toString ()) ,
517- Strings . create ( "hash" ), Strings . create ( hashHex ) ,
518- Strings . create ( "data" ), Strings . create ( dataHex ) ,
519- Strings . create ( "sequence" ) , CVMLong .create (sequence )
519+ "source" , sourceCell ,
520+ "address" , address ,
521+ "hash" , hashHex ,
522+ "data" , dataHex ,
523+ "sequence" , CVMLong .create (sequence )
520524 );
521525 return toolSuccess (structured );
522526 } catch (Exception e ) {
@@ -550,8 +554,8 @@ public AMap<AString, ACell> handle(AMap<AString, ACell> arguments) {
550554 }
551555 String hashHex = hash .toHexString ();
552556 AMap <AString , ACell > result = Maps .of (
553- Strings . create ( "algorithm" ), Strings . create ( algorithm ) ,
554- Strings . create ( "hash" ), Strings . create ( hashHex )
557+ "algorithm" , algorithm ,
558+ "hash" , hashHex
555559 );
556560 return toolSuccess (result );
557561 }
@@ -586,9 +590,9 @@ public AMap<AString, ACell> handle(AMap<AString, ACell> arguments) {
586590 ASignature signature = keyPair .sign (valueBlob );
587591 AccountKey accountKey = keyPair .getAccountKey ();
588592 AMap <AString , ACell > result = Maps .of (
589- Strings . create ( "value" ) , valueCell ,
590- Strings . create ( "signature" ), Strings . create ( signature .toHexString () ),
591- Strings . create ( "accountKey" ), Strings . create ( accountKey .toHexString () )
593+ "value" , valueCell ,
594+ "signature" , signature .toHexString (),
595+ "accountKey" , accountKey .toHexString ()
592596 );
593597 return toolSuccess (result );
594598 } catch (Exception e ) {
@@ -604,7 +608,7 @@ private class SubmitTool extends McpTool {
604608
605609 @ Override
606610 public AMap <AString , ACell > handle (AMap <AString , ACell > arguments ) {
607- AString hashCell = RT .ensureString (arguments .get (Strings . create ( "hash" ) ));
611+ AString hashCell = RT .ensureString (arguments .get (ARG_HASH ));
608612 if (hashCell == null ) {
609613 return protocolError (-32602 , "Submit requires 'hash' string" );
610614 }
@@ -614,15 +618,15 @@ public AMap<AString, ACell> handle(AMap<AString, ACell> arguments) {
614618 }
615619 try {
616620 ATransaction transaction = decodeTransaction (hashBlob );
617- AString accountKeyCell = RT .ensureString (arguments .get (Strings . create ( "accountKey" ) ));
621+ AString accountKeyCell = RT .ensureString (arguments .get (ARG_ACCOUNT_KEY ));
618622 if (accountKeyCell == null ) {
619623 return protocolError (-32602 , "Submit requires 'accountKey' string" );
620624 }
621625 AccountKey accountKey = AccountKey .parse (accountKeyCell .toString ());
622626 if (accountKey == null ) {
623627 return toolError ("Invalid account key" );
624628 }
625- AString signatureCell = RT .ensureString (arguments .get (Strings . create ( "signature" ) ));
629+ AString signatureCell = RT .ensureString (arguments .get (ARG_SIGNATURE ));
626630 if (signatureCell == null ) {
627631 return protocolError (-32602 , "Submit requires 'sig' string with Ed25519 signature" );
628632 }
@@ -649,7 +653,7 @@ private class PeerStatusTool extends McpTool {
649653 public AMap <AString , ACell > handle (AMap <AString , ACell > arguments ) {
650654 try {
651655 AMap <?, ?> status = server .getStatusMap ();
652- AMap <AString , ACell > payload = Maps .of (Strings . create ( "status" ) , (ACell ) status );
656+ AMap <AString , ACell > payload = Maps .of ("status" , (ACell ) status );
653657 return toolSuccess (payload );
654658 } catch (Exception e ) {
655659 return toolError ("Failed to load peer status: " + e .getMessage ());
@@ -664,16 +668,16 @@ private class EncodeTool extends McpTool {
664668
665669 @ Override
666670 public AMap <AString , ACell > handle (AMap <AString , ACell > arguments ) {
667- AString cvxCell = RT .ensureString (arguments .get (Strings . create ( "cvx" ) ));
671+ AString cvxCell = RT .ensureString (arguments .get (ARG_CVX ));
668672 if (cvxCell == null ) {
669673 return protocolError (-32602 , "Encode requires 'cvx' string" );
670674 }
671675 try {
672676 ACell value = Reader .read (cvxCell .toString ());
673677 Blob encoded = Format .encodeMultiCell (value , true );
674678 AMap <AString , ACell > result = Maps .of (
675- Strings . create ( "cad3" ), Strings . create ( encoded .toCVMHexString () ),
676- Strings . create ( "hash" ), Strings . create ( Ref .get (value ).getEncoding ().toCVMHexString () )
679+ "cad3" , encoded .toCVMHexString (),
680+ "hash" , Ref .get (value ).getEncoding ().toCVMHexString ()
677681 );
678682 return toolSuccess (result );
679683 } catch (Exception e ) {
@@ -689,7 +693,7 @@ private class DecodeTool extends McpTool {
689693
690694 @ Override
691695 public AMap <AString , ACell > handle (AMap <AString , ACell > arguments ) {
692- AString cad3Cell = RT .ensureString (arguments .get (Strings . create ( "cad3" ) ));
696+ AString cad3Cell = RT .ensureString (arguments .get (ARG_CAD3 ));
693697 if (cad3Cell == null ) {
694698 return protocolError (-32602 , "Decode requires 'cad3' string" );
695699 }
@@ -701,7 +705,7 @@ public AMap<AString, ACell> handle(AMap<AString, ACell> arguments) {
701705 ACell decoded = Format .decodeMultiCell (cad3Blob );
702706 AString cvx = RT .print (decoded );
703707 AMap <AString , ACell > result = Maps .of (
704- Strings . create ( "cvx" ) , cvx == null ? Strings . create ( "" ) : cvx
708+ "cvx" , cvx == null ? "" : cvx
705709 );
706710 return toolSuccess (result );
707711 } catch (Exception e ) {
@@ -739,8 +743,8 @@ public AMap<AString, ACell> handle(AMap<AString, ACell> arguments) {
739743 AccountKey publicKey = keyPair .getAccountKey ();
740744
741745 AMap <AString , ACell > result = Maps .of (
742- Strings . create ( "seed" ), Strings . create ( seedBlob .toString () ),
743- Strings . create ( "publicKey" ), Strings . create ( publicKey .toString () )
746+ "seed" , seedBlob .toString (),
747+ "publicKey" , publicKey .toString ()
744748 );
745749 return toolSuccess (result );
746750 } catch (Exception e ) {
@@ -792,7 +796,7 @@ public AMap<AString, ACell> handle(AMap<AString, ACell> arguments) {
792796 boolean isValid = Providers .verify (signature , messageBlob , publicKey );
793797
794798 AMap <AString , ACell > result = Maps .of (
795- Strings . create ( "value" ) , isValid ? CVMBool .TRUE : CVMBool .FALSE
799+ "value" , isValid ? CVMBool .TRUE : CVMBool .FALSE
796800 );
797801 return toolSuccess (result );
798802 } catch (Exception e ) {
@@ -864,7 +868,7 @@ public AMap<AString, ACell> handle(AMap<AString, ACell> arguments) {
864868 }
865869
866870 AMap <AString , ACell > result = Maps .of (
867- Strings . create ( "address" ) , CVMLong .create (address .longValue ())
871+ "address" , CVMLong .create (address .longValue ())
868872 );
869873 return toolSuccess (result );
870874 } catch (ResultException e ) {
@@ -975,7 +979,7 @@ public AMap<AString, ACell> handle(AMap<AString, ACell> arguments) {
975979 value = env .get (symbol );
976980
977981 // Apply path if provided
978- AString pathCell = RT .ensureString (arguments .get (Strings . create ( "getPath" ) ));
982+ AString pathCell = RT .ensureString (arguments .get (ARG_GET_PATH ));
979983 if (pathCell != null && value != null ) {
980984 String pathStr = pathCell .toString ();
981985 try {
@@ -1012,9 +1016,9 @@ public AMap<AString, ACell> handle(AMap<AString, ACell> arguments) {
10121016
10131017 // Build result
10141018 AMap <AString , ACell > resultMap = Maps .of (
1015- Strings . create ( "exists" ) , exists ? CVMBool .TRUE : CVMBool .FALSE ,
1016- Strings . create ( "value" ) , value != null ? value : RT .cvm (null ),
1017- Strings . create ( "meta" ) , meta != null ? meta : RT .cvm (null )
1019+ "exists" , exists ? CVMBool .TRUE : CVMBool .FALSE ,
1020+ "value" , value != null ? value : RT .cvm (null ),
1021+ "meta" , meta != null ? meta : RT .cvm (null )
10181022 );
10191023 return toolSuccess (resultMap );
10201024 } catch (Exception e ) {
0 commit comments