{"slug": "arc-14-catch-up-agentic-solana", "title": "Arc 14 Catch-up: Agentic Solana", "summary": "An Arc 14 project built an AI agent on Solana with a wallet, tools, MCP server, policy engine, and autonomous workflow, enforcing a strict boundary where the model decides what to do but code decides what is allowed. The agent's private key remains in the application, never exposed to the model, and a hard cap on transfers prevents unauthorized spending.", "body_md": "An AI agent with access to a wallet sounds powerful.\n\nIt also sounds like it could be risky.\n\nA language model can misunderstand a request, choose the wrong tool, repeat an action, or confidently pursue a goal that should never have been allowed in the first place. Giving it unrestricted signing authority would turn those mistakes into transactions.\n\nArc 14 was about building something more useful than that.\n\nAcross Days 92–98, we gave an agent access to Solana data, then gradually introduced tools, a wallet, an MCP server, a policy engine and an autonomous workflow.\n\nAt every stage, the same boundary held:\n\nThe model could decide what it wanted to do.\n\nCode decided what it was allowed to do.\n\nThe first challenge kept the stakes low.\n\nWe built a small agent loop using Claude and a set of Solana devnet tools. The agent could answer natural-language questions such as:\n\nThe model never connected to Solana directly.\n\nIt did not construct arbitrary RPC requests or receive unrestricted network access. The application defined a small set of tools, described what each one did, and decided how to execute them.\n\nClaude could inspect those descriptions and choose which tool to call, but the surrounding application remained in control.\n\nThe result still felt conversational.\n\nWe asked a question in plain English.\n\nThe agent selected a balance tool.\n\nThe application called the RPC endpoint.\n\nThe result went back to the model.\n\nThe model explained it to us.\n\nBehind that interaction was a simple loop:\n\nThe model supplied the judgement.\n\nThe application supplied the capabilities.\n\nEach tool had four important parts:\n\nThose details mattered.\n\nA vague description could lead the model to choose the wrong tool.\n\nA loose schema could allow malformed or ambiguous requests.\n\nAn implementation that trusted every argument could turn a model mistake into a system problem.\n\nEven the read-only tools needed boundaries.\n\nA balance tool should accept a valid Solana address.\n\nAn account-inspection tool should return useful structured metadata.\n\nErrors should come back in a form the model could reason about without hiding the original detail from the developer.\n\nThe prompt described how we wanted the agent to behave.\n\nThe tool definitions established what it could actually ask the application to do.\n\nDay 93 raised the stakes.\n\nThe agent received its own devnet wallet and two new abilities:\n\nThat turned it from a read-only assistant into a system capable of changing on-chain state.\n\nThe wallet belonged to the application, not to the model.\n\nIts private key remained inside the process that built and signed transactions. It was never inserted into the prompt, returned in a tool result or exposed through the model context.\n\nThe model did not need the key.\n\nIt only needed a tool that accepted a proposed recipient and amount.\n\nThe application could then decide whether to build and sign the transaction.\n\nThis separation matters because model context is not a safe place for secrets.\n\nPrompts, tool calls, logs and responses may be stored, inspected or passed between components. A private key placed there could be leaked accidentally or repeated in generated output.\n\nKeeping the key inside the signing layer meant the model could request an action without possessing the credential required to perform it.\n\nThe agent could ask to send 0.05 SOL to an address.\n\nOnly the application could turn that request into a signed transaction.\n\nWe did not rely on the prompt to limit how much SOL the agent could send.\n\nThe transfer tool enforced a hard cap.\n\nIf the proposed amount exceeded that limit, the application rejected the request before building the transaction.\n\nWe could have told the model never to send more than 0.1 SOL. It would probably follow that instruction most of the time.\n\nBut a prompt can be misunderstood, overridden by conflicting instructions or applied inconsistently.\n\nA code-level check behaves differently.\n\nIf the amount is too high, the transfer does not happen.\n\nIt does not matter how persuasive the request is.\n\nIt does not matter how confident the model sounds.\n\nIt does not matter whether the proposed action appears to support the wider goal.\n\nThe signing layer refuses.\n\nThat is where rules involving money, permissions and irreversible actions belong.\n\nA per-transfer limit protects against one large transaction.\n\nIt does not stop an agent from making many smaller ones.\n\nAn agent limited to 0.1 SOL per transfer could still attempt ten transfers and spend 1 SOL overall.\n\nThat showed why validation inside the transfer tool was only the beginning.\n\nThe wider system also needed to decide:\n\nThose decisions belonged in a dedicated policy layer rather than being scattered through prompts and tool implementations.\n\nDay 94 moved the Solana tools into a Model Context Protocol server.\n\nUntil then, the tools had lived inside one application.\n\nThat worked, but it tied the capabilities to a particular agent implementation.\n\nAn MCP server made them reusable.\n\nIt exposed tools for tasks such as:\n\nAny compatible AI client could discover and use those tools through the same protocol.\n\nThe server described what was available, accepted structured requests and returned structured results.\n\nThis was similar to the role the IDL played in Arc 13.\n\nThe IDL made a Solana program discoverable to software.\n\nMCP made agent-facing capabilities discoverable to AI clients.\n\nThe useful part was not simply that another client could call the tools.\n\nThe wallet key and safety rules stayed on the server.\n\nA new client did not receive direct access to either.\n\nIt received the same controlled interface as every other client.\n\nThat meant we could change the model, desktop client or agent framework without rebuilding the Solana integration or moving authority into the new client.\n\nMCP made tools easier to discover and reuse.\n\nIt did not make every client trustworthy.\n\nA compatible client could request a tool call, but the server still had to validate it.\n\nThe client might be misconfigured.\n\nThe model might choose the wrong tool.\n\nA user might request an action outside the intended scope.\n\nA request might contain an invalid address or an excessive amount.\n\nThe server remained responsible for:\n\nTool results were untrusted too.\n\nOn-chain text and metadata can be written by other people. A memo, token name or decoded account field returned to the model could contain instructions intended to influence its behaviour.\n\nThe application could not treat that content as trusted guidance merely because it came back from a tool.\n\nEven if hostile data changed the model’s reasoning, the same policy rules still controlled what could be signed. A poisoned response could affect what the model proposed, but it could not expand the allowlist, raise the spending cap or reset the session budget.\n\nThat carried forward the security lesson from Arc 12.\n\nUntrusted input is not limited to transaction accounts. It also includes the data an agent reads and brings into its context.\n\nDay 95 turned the simple transfer cap into a proper policy layer.\n\nA proposed transfer was denied unless it passed every rule.\n\nThe policy checked:\n\nThe application tracked cumulative spend outside the model context.\n\nThe agent did not get to remember, reset or reinterpret its own remaining budget. The policy engine calculated that from application-owned session state.\n\nThis was a deny-by-default design.\n\nThe system did not search for a reason to reject the transfer.\n\nIt required the transfer to satisfy every condition for approval.\n\nIf any check failed, the transaction was not signed.\n\nA missing allowlist entry resulted in denial.\n\nA malformed address resulted in denial.\n\nAn amount above the cap resulted in denial.\n\nAn exhausted session budget resulted in denial.\n\nUnexpected input did not fall through into execution.\n\nIt stopped.\n\nThe model’s job was to reason about the goal.\n\nThe policy engine’s job was to decide whether a proposed action fitted the rules.\n\nThe agent might correctly calculate that a wallet was short by 0.4 SOL.\n\nThat did not mean it had permission to transfer 0.4 SOL.\n\nThe amount might exceed the per-transfer cap.\n\nThe destination might not be allowlisted.\n\nThe remaining session budget might be only 0.2 SOL.\n\nThe correct outcome could therefore be a refusal even when the model’s reasoning was sound.\n\nThat was an important property of the system.\n\nIts safety did not depend on the model making the right security decision every time.\n\nThe model could be mistaken, overconfident, manipulated by the user or influenced by hostile tool output.\n\nThe policy should produce the same result for the same proposed action and the same session state.\n\nModel behaviour was variable.\n\nPolicy behaviour was meant to be predictable.\n\nA rejected action was not necessarily an error.\n\nSometimes it was the system working exactly as designed.\n\nIf the agent proposed sending funds to an address outside the allowlist, the policy engine denied the request and returned the reason.\n\nThe agent could then decide what to do next.\n\nIt might tell the user that the action was not permitted.\n\nIt might ask for a different recipient.\n\nIt might propose a smaller transfer.\n\nIt might conclude that the goal could not be completed under the current limits.\n\nThis was better than treating every denial as an exception that crashed the workflow.\n\nThe policy result became information the agent could reason over.\n\nApproved actions could proceed to signing.\n\nDenied actions came back with a structured explanation.\n\nThe agent could adapt to that decision, but it could not override it.\n\nDay 96 combined the agent loop, wallet tools and policy engine in a goal-driven workflow.\n\nThe example goal was to make sure a savings wallet held at least a specified amount of SOL.\n\nThe agent needed to:\n\nThis was more than one tool call followed by an answer.\n\nThe agent had to inspect the current state, decide whether action was needed, act within its limits and then check the result.\n\nThat final verification mattered.\n\nA transaction signature shows that a transaction was submitted.\n\nIt does not by itself prove that the intended goal was reached.\n\nThe agent read the on-chain state again and compared it with the target.\n\nThat closed the loop:\n\nObserve.\n\nReason.\n\nAct.\n\nVerify.\n\nWe tested the workflow under several conditions.\n\nIn the normal case, the target wallet was below its minimum balance, the funding wallet had enough SOL, and the required transfer passed policy.\n\nThe agent calculated the shortfall, requested the transfer and verified the new balance.\n\nIn a constrained case, the goal was achievable only within the transfer cap and remaining session budget.\n\nThe agent had to work within those limits rather than simply choosing the most direct action.\n\nIn an impossible case, the policy or available funds made the target unreachable.\n\nThe recipient might not be allowlisted.\n\nThe shortfall might exceed the session budget.\n\nThe funding wallet might not contain enough SOL.\n\nThe agent could reason correctly and still be unable to complete the goal.\n\nThat was not a failure of the system.\n\nThe correct behaviour was to stop, explain the constraint and preserve the policy boundary.\n\n“Unable to proceed” is sometimes the right result.\n\nAn agent loop needs a stopping condition.\n\nWithout one, the model could continue calling tools, retrying failed actions or revisiting the same reasoning indefinitely.\n\nArc 14 included explicit turn limits so the application could stop the workflow after a fixed number of steps.\n\nThat protected against accidental loops and limited the amount of time, tokens and network activity one request could consume.\n\nA turn limit is a simple control, but it addresses a real feature of agent systems.\n\nThe model decided what to do next.\n\nThe application decided how long it was allowed to keep deciding.\n\nOther systems might also use time limits, tool-call limits or approval gates for particular actions.\n\nThe exact control can vary.\n\nThe important part is that the agent cannot grant itself unlimited time or unlimited attempts.\n\nAutonomous behaviour is hard to trust when all we can see is the final answer.\n\nThe workflow therefore wrote structured logs for each important step.\n\nThose logs could record:\n\nThis created a trace of how the system moved from the original goal to the result.\n\nThe logs were useful for debugging, but their value went further.\n\nThey showed whether the agent had interpreted the goal correctly.\n\nThey revealed whether the policy engine had applied the expected rule.\n\nThey made repeated actions visible.\n\nThey provided evidence that a successful workflow had verified its work.\n\nThey also made denials easier to explain.\n\nWithout logs, we might know only that the transfer did not happen.\n\nWith logs, we could see that the recipient was not on the allowlist or that the remaining budget was too low.\n\nAutonomous systems need this kind of evidence.\n\nA final response is not enough.\n\nDay 97 moved from building the system to explaining it.\n\nWe wrote a public technical walkthrough covering:\n\nThe most important distinction was between variable and invariant behaviour.\n\nThe model’s reasoning was variable.\n\nIt might choose different tools, phrase its explanation differently or take another path towards the same goal.\n\nThe policy layer was invariant.\n\nThe same proposed action and session state should produce the same approval decision regardless of how the model explained its intention.\n\nThat made the architecture easier to understand.\n\nThe agent was not safe because the model had been instructed to behave.\n\nIt was safe because the model could reach the signing capability only through code that enforced fixed rules.\n\nThe documentation needed to show exactly where that boundary lived.\n\nA public walkthrough of an agent system can easily become a long explanation of prompting.\n\nPrompts mattered, but they were only one part of this arc.\n\nThe stronger material was in the tool contracts and policy rules.\n\nWhich fields did a transfer request require?\n\nWhich validation happened before policy?\n\nWhich policy result came back after denial?\n\nWhere did the private key live?\n\nWhere was cumulative session spend stored?\n\nWhich component built the transaction?\n\nHow did the workflow confirm success?\n\nHow were tool calls and policy decisions logged?\n\nHow was untrusted tool output kept from bypassing the signing rules?\n\nThose details explained what the system could actually guarantee.\n\nA prompt described intended behaviour.\n\nA tool contract defined the action the model could request.\n\nThe policy engine defined which requests could proceed.\n\nThe signer controlled what reached the network.\n\nThe logs showed what occurred.\n\nThat was the full safety story.\n\nDay 98 turned the workflow into a short recorded demonstration.\n\nThe demo began with a natural-language goal.\n\nThe agent inspected balances, selected tools, proposed an action and passed it through policy.\n\nAn approved transaction appeared on devnet and could be verified on-chain.\n\nBut the demo also needed to show a denial.\n\nThat was just as important as the successful transfer.\n\nA system that can move funds is easy to demonstrate.\n\nA system that can refuse an unsafe or disallowed request is more convincing.\n\nThe denial showed that the policy engine remained in control even when the model proposed the action.\n\nThe most useful demo therefore included:\n\nThis made the boundaries visible rather than asking the audience to trust that they existed somewhere in the code.\n\nArc 14 was not about handing an AI model a wallet and hoping for the best.\n\nIt was about building an agent inside a system that retained control.\n\nBy the end of the arc, we had learned how to:\n\nThe central lesson was that autonomy and authority are not the same thing.\n\nThe model can decide which tool might help.\n\nIt can interpret a goal.\n\nIt can calculate a shortfall.\n\nIt can propose a transfer.\n\nIt can explain a denial.\n\nBut it does not hold the private key.\n\nIt does not decide which recipients are permitted.\n\nIt does not set its own spending cap.\n\nIt does not remember or reset its own session budget.\n\nIt does not override the policy engine.\n\nThe model reasons.\n\nThe code holds the authority.\n\n**Day 92:** Build a read-only Solana agent that answers questions through defined tools\n\n**Day 93:** Give the agent a devnet wallet while keeping its key and spending limits in code\n\n**Day 94:** Package the Solana tools as a reusable MCP server\n\n**Day 95:** Build a deny-by-default policy engine with allowlists and budgets\n\n**Day 96:** Run and verify an autonomous wallet-management workflow\n\n**Day 97:** Document the agent architecture, tool contracts, policy rules and run logs\n\n**Day 98:** Record a public demo showing both a successful action and a policy denial", "url": "https://wpnews.pro/news/arc-14-catch-up-agentic-solana", "canonical_source": "https://dev.to/100daysofsolana/arc-14-catch-up-agentic-solana-4ap2", "published_at": "2026-07-29 09:59:11+00:00", "updated_at": "2026-07-29 10:37:36.189952+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-agents", "ai-safety"], "entities": ["Arc 14", "Solana", "Claude"], "alternates": {"html": "https://wpnews.pro/news/arc-14-catch-up-agentic-solana", "markdown": "https://wpnews.pro/news/arc-14-catch-up-agentic-solana.md", "text": "https://wpnews.pro/news/arc-14-catch-up-agentic-solana.txt", "jsonld": "https://wpnews.pro/news/arc-14-catch-up-agentic-solana.jsonld"}}