Houston We Have a Podcast is the official weekly podcast of the
NASA Johnson Space Center in Houston, Texas. Launched in July 2017
and hosted primarily by Public Affairs Officer Gary Jordan, the show
brings listeners into the world of human spaceflight, covering everything
from Low Earth orbit to missions heading to the Moon and Mars.
From Earth orbit to the Moon and Mars, explore the world of human
spaceflight with NASA each week on the official podcast of the Johnson Space
Center in Houston, Texas. Listen to in-depth conversations with the
astronauts, scientists and engineers who make it possible.
Cosmic Perspectives with Neil deGrasse Tyson
On episode 437, NASA astronaut Chris Williams and astrophysicist Neil
deGrasse Tyson explore human spaceflight, astrophysics, and the wonder of
the universe. This episode was recorded September 1, 2026.
Home > Ajabbi Research > Library > Subscriptions > UX Collective
Home > Handbook >
Last Updated
26/10/2026
Visualizing the systems behind our designs
By: Justin Farrugia
UX Collective (Medium): 25/02/2020
Justin Farrugia: Editor of Casumo Design and Soundbite.
...
Tangible systems thinking that goes beyond buttons.
A button on the left which is what we see, and a complex casual loop diagram on the right which is what’s actually happening
Systems thinking is often heralded in different circles as a competency that’s worth investing in. Having a wider lens and being able to model the systems that influence inputs, outputs and the patterns of behavior for people using the products we build is how we can deliver real value.
Most product designers tend to default to design systems as a proxy for systems thinking. They’re not entirely wrong. The ecology around building design systems does require maintainers to think systematically. They need to deeply understand the core needs of the product team, to deliver reusable component libraries coupled with accompanying guidelines, processes, and documentation.
However, this doesn’t mean that building a design system is the only natural outcome of thinking in systems. Far from it.
Beyond buttons
As of writing, few people in history have had the surreal experience of being able to witness the Earth from space in person. Astronauts often recall the overwhelming experience they felt when looking at the fragile blue ball they called home. Their front row seats enabled them to see the world through a different lens, one where all life was interconnected and every system they’ve come to know was working in concert.
Astronomy author Frank White called this experience, ‘The Overview effect’.
Whilst not nearly as life-changing of an experience, zooming out from our narrowly focused product domains and realizing every design decision’s ripple effect in the ecosystem we operate in, can have a similar effect. Designing software often requires us to situate ourselves at both a macro and micro level. Yet, sometimes there’s a tendency to over-index on the latter at the expense of a broader set of goals.
As a result, we risk becoming distracted with the details when we can’t afford to do so. The tools, products and service experiences we’re shaping today are responsible for far more than just the daily metric movements relative to the bottom line. They inhabit already complex systems such as political landscapes or socioeconomic hierarchies and sometimes unconsciously introduce new dynamics that have outsized impacts.
"We are surrounded by systems that we’ve directly or indirectly shaped or been a product of."
That being said we’ve done very little to represent any of them. By doing so we can peel back the layers and uncover patterns, boundaries, behaviors and even points of leverage where we can infer our influence. The good news is that we have an underdeveloped muscle which we ought to flex more often that can help us out.
Tools of the trade
We often picture systems in a linear fashion where A leads to B. Needless to say this is a reductive way of describing relationships and causality, as it overlooks the fact that each node may disrupt or restore the balance in uniquely different ways. In other words, systems are messy.
Linear Systems Thinking versus Loop Based Systems Thinking
Instead of A leading to B, it’s more likely you’ll find something like, A can cause B and C given a set of conditions, both of which reinforce D which restarts the cycle. Now trying to articulate all that through text isn’t exactly the best way to explain what’s actually going on. That’s where system diagramming can come in handy. Below are a few examples.
Behavior Over Time Graph
You’ve probably already seen this type of diagram being plotted somewhere on an office whiteboard. This graph helps us see patterns and interrelationships that emerge over a specific period of time. We can use it to observe how and why something is happening.
Let’s use something we’re all too familiar with as an example — product debt. Two things that we can use as variables in our graph are the cost of building features which keeps rising exponentially as the product matures, and customer value delivered which is inversely proportional due to fragmentation and debt that the team is spending time wrestling with.
A behaviour over time graph that indicates when a product team should address its mounting debt.
Despite ancillary efforts to revive that delivery curve from declining further, it most likely wouldn’t be enough to curb the overhead. The intersection point between the two curves signals the necessity for a cadence of refactoring or even a commitment towards building a design system.
Quadrant Matrix
This is another common diagram, that also has likely donned a few whiteboards. It is mainly used to represent the strength of relationships between several different items. You’ve likely seen it being used to chart the impact versus effort of doing something in a bid to inform the decision-making process.
A quadrant of usability issues with repetition versus impact
Let’s say you’ve finished up a moderated usability testing session and you’ve come away with a few insights. To start applying what you’ve learned you’ll need to rank your findings against a few different dimensions namely repetition and impact. Using a quadrant matrix will help you synthesize the raw data you’ve collected and visually make sense of what issues were the most severe and iron them out through iteration.
Iceberg Model
The iceberg model is a tool that can be used to show the four levels of abstraction within a specific setting that can allow us to uncover the root cause of why something is actually happening the way it is. It helps us go beyond the observable events that anyone can notice, and help us realize the patterns that might have contributed to those events, the structures that have influenced those patterns and the underlying mental models that keep those structures in place.
Often times when we try to address a problem, we do it on an event level which in turn leads to suboptimization. It’s not enough to swap things around, change information flow or incentives. You’ll need to expand the perception and shift mental models to have leverage and upend the system.
Let’s take a real-world example of a product team that’s trying to meet a deadline. An event would be that the team doesn’t ship in time. The natural tendency is to be reactive and start to find people to blame. Instead of playing the blame game start looking at the trends that might have led to this.
The Iceberg Model representing the Events, Patterns, Structures and Mental Models behind a product team missing a deadline.
A pattern might be looking at recurring evidence like the team’s collective track record and finding that three of the last five things they’ve worked on have had overrun dates. The structures that might be causing these patterns could be bandwidth issues with engineers and designers needing to context switch due to commitments outside the team.
What’s keeping this system in place is the mental model each team member holds, which might normalize the balancing act of being first responders instead of establishing a cadence coupled with some boundaries.
Reinforcing Feedback Loops
However intentional it might have been, you’ve probably designed a reinforcing feedback loop. As the name suggests, these kinds of loops have self-reinforcing behavior where the actions of one variable reinforce another. I’d be remiss to not mention the Hooked model as an example of a reinforcing loop that optimizes user stickiness and engagement.
The Hooked Model by Nir Eyal.
Unsurprisingly, reinforcing feedback loops are used and abused. Most consumer products with some kind of social component and a healthy network effect have reinforcing feedback loops baked into the fabric of the core experience. Products like TikTok have predictive models, computer vision and matrix factorization algorithms each built-in to perfect the content recommendation loop and have our attention.
TikTok’s reinforcing feedback loop.
Even though it has a cold start problem, after only a few seconds of watching and a series of swipes TikTok has collected enough data points and spun that loop so many times, that the content suddenly becomes unmissable. The diagram above illustrates this example.
Balancing Feedback Loops
Instead of creating a snowball effect, balancing feedback loops counter an input with an opposite output. These loops can correct an oversupply or undersupply, indicate that a plateau has been reached or restore the lost stability back to the system. Like reinforcing feedback loops however, the outcome of balancing feedback loops might not always lead to positive outcomes depending on prior inputs.
Some teams seem to jump the gun when it comes to bridging the gap between their customer success representatives and the customers. Access to an agent is either obscured by a messy navigation system or else it’s available at all times even when unnecessary. Both are examples of a balancing feedback loop.
The ramifications of introducing a chat bubble representing as a balancing feedback loop.
The former being the increase in customer frustration in trying to flag an issue by jumping through a few hoops which leads to an inevitable decrease in experiential and customer satisfaction measures. The latter shows a complete disregard for the time spent in answering repeatable queries which increases given there is no FAQ-like system in place which in turn leads to customer success agents availability decreasing, diminishing the likelihood of being a first responder for high-value accounts.
External Factors, Gaps, and Delays in Causal Loops
In the ever-increasing complexity of the systems we build towards, loops rarely ever have equally defined units of input or output. There are usually things that happen in between or outside of the model that have an effect. What better way to represent concepts like gaps and delays than an unpredictable complex landscape like healthcare, specifically the use of software in aiding care decisions.
Electronic Health Records or EHRs are real-time robust systems used in hospitals that contain the medical history of a patient. Their goal is to ultimately chip away at the mundanity of paperwork and enable a meaningful patient to physician interaction. If you zoom out far enough, the most basic loop you can make out is the one where the effects of introducing an EHR into a previously paper-run practice become visible.
Diagram of a reinforcing feedback loop that shows the effects of introducing an Electronic Health Record or EHR.
As can be seen in the diagram above, the impact of introducing an EHR into the mix has saved staff time which in turn has increased the patient treatment quality. As a result, the better the treatment quality, the more time will be saved in preventing adverse drug events or readmissions.
Diagram representing a delay within a causal loop.
Now that’s not going to happen overnight, the staff on hand needs to be gradually onboarded onto the new system and get familiar. That’s why this feedback loop will likely have delays which we can represent by marking the arc with a double line.
Diagram representing an external factor, in this case an update to the software.
Our feedback loop is still looking a bit too convenient. Let’s introduce another component, such as an update to the EHR software. That in itself might eat up some of that time the staff is saving since there might need to be retraining. That’s called an external factor. Gaps can also be another feature of causal feedback loops. A gap can exist between the actual quality and the actions we’re taking to improve that quality.
Representing Gaps in EHR causal loops
In our example for patient treatment quality to improve, the gap in between, as shown in the diagram, is the secure migration of the patient records from paper to digital records. This, in turn, enables more confidence to adopt and inevitably invest in the software beyond just procurement, but also designing training programs for practitioners and defining performance targets such as decreasing error rates or patient treatment time.
Cause and Effect Diagram, Onion Model Diagram and a Hierarchy
These are just a select few of the tools available in your systems utility belt. Needless to say, there’s an abundance of other ways you can visualize systems. If you’re trying to bring clarity to a post mortem, you can use a cause and effect diagram to isolate what went wrong during a project. A stock and flow diagram can help you simulate the impact of your design decisions, whereas an onion model or hierarchy can help us see top-down or bottom-up causality, and chart the flow of influence.
...
Each of these visual aids helps us see things we couldn’t have possibly seen through a block of text, especially when co-created with people outside our discipline. They enable us to align viewpoints, understand root causality, synthesize, generate options and even simulate design decisions within our product experiences and organizations. Quite frankly, being able to produce these artifacts is a superpower.
That superpower, however, shouldn’t go unchecked. Systems can also be consciously designed to manipulate, cheat and sometimes even harm people. This underscores our responsibility to make we have a full understanding of the chain reaction our design decisions cause beyond canvas and code.
Just like the astronauts whose new perspective of our planet engendered a new sense of responsibility for the environment, we can use whatever palette of system diagrams to shape, tug or prune systems in small but impactful increments and become catalysts for sustainable and ethical change.
Home > Ajabbi Research > Library > Subscriptions > steve Blank
Home > Handbook >
Last Updated
25/10/2026
Elon Musk Just Gave Europe a Reason to Replace American Technology
By: Steve Blank
Steve Blank: 24/10/2026
Steve Blank: Steve Blank is an American entrepreneur, educator, author and speaker. He created the customer development method that launched the lean startup movement. His work has influenced modern entrepreneurship through the creation of tools and processes for new ventures which differ from those used in large companies..
...
I’ve been in London for the last month and have heard policymakers react to the wake up call Elon Musk just delivered.
The decision of the U.S. to shut off intelligence and throttle the military version of Starlink called Starshield to Ukraine may have been a blip in the news in the US, but it has sent tremors across Europe.
For years doomsayers in the UK and Europe were worried about the U.S. having a kill switch in weapon systems and critical commercial technology that could disconnect them at will. Most people, including me, thought that it was a paranoid fantasy.
Until it happened. Seemingly on a whim of a single individual, (according to the Wall Street Journal) Elon Musk personally ordered the restrictions, and the effect was to tilt Russia’s war against Ukraine in favor of the Russians. This wasn’t an articulated U.S. foreign policy nor was it an announced act of the U.S. government. From news accounts it appears to have been a personal decision of an American CEO acting unilaterally.
Blowback
For whatever reason, whether it was to satisfy a request from the White House or some personal animosity to the Ukrainians, from Europe’s perspective, that’s almost beside the point. An American company operating critical military infrastructure was able to degrade the capability of an American partner in the middle of a war. Without warning.
The message heard loud and clear here in Europe was that the U.S. is an unreliable security and technology partner willing to throw any partner under the bus, at any time, for any reason.
The conversations I’ve heard have included, “what happens if other U.S. CEOs – Microsoft, OpenAI or Anthropic, Oracle, Palantir, or Google – decide to restrict/limit or deny access to their products? What capabilities would Europe lose if enterprise software and/or AI across Europe goes dark?” This was a laughable scenario until this week. To a lot of European decision makers, it is no longer outside the realm of possibility.
The lesson that was brought home was that no nation, Europe or elsewhere, can continue to outsource its national sovereignty and security by depending solely on US data systems or intelligence.
Unintended Consequences
Besides the political ramifications, the business ones are going to come home to roost. If every U.S. vendor is now a kill-switch risk, the allied market for that whole ecosystem shrinks. As it sinks into the European countries that the U.S, can simply shut off any technology, over time they will find and build alternatives. In the years ahead, this may cost American companies tens if not hundreds of billions of lost revenue.
Europe has been debating digital sovereignty for years. Up to this year, the idea of cloning the capabilities of the best U.S. products seemed like an insurmountable problem. And some, like low-cost access to space, still are. But today AI collapses the cost of creating sovereign software at exactly the time that geopolitical risk is increasing customers’ willingness to switch. AI makes recreating the features, functions and compatibility of Microsoft Office, Oracle, Salesforce, Palantir, Anduril, et al. within reach. AI puts it well within the capabilities of most nations.
The U.S. spent decades establishing itself as the world’s indispensable technology supplier. A substantial part of that advantage depended on customers believing American technology would remain reliable and available.
Going forward a U.S. founder selling into European defense or government now needs an answer to “who holds the switch” before the first meeting; and European founders just got sovereignty as a sellable feature.
What was done in a fit of pique will cost the U.S. both leadership and jobs.
Home > Ajabbi Research > Library > Subscriptions > Quanta Magazine
Home > Handbook >
Last Updated
24/10/2026
The Riemann Hypothesis, Explained
By: Alex Kontorovich
Quanta Magazine: 05/01/2021
Alex Kontorovich:A professor of mathematics at Rutgers University.
...
The Riemann Hypothesis is the most notorious unsolved problem in all of mathematics. Ever since it was first proposed by Bernhard Riemann in 1859, the conjecture has maintained the status of the "Holy Grail" of mathematics. The person who solves it will win a $1 million prize from the Clay Institute of Mathematics. So, what is the Riemann Hypothesis? Why is it so important? What can it tell us about the chaotic universe of prime numbers? And why is its proof so elusive? Alex Kontorovich, professor of mathematics at Rutgers University, breaks it all down in this comprehensive explainer.
Yesterday (Wednesday NZ time), I attended the Google Startups Product Roadmap Seminar: October 2026 via
Google Meet.
"Join Google Cloud and Wiz specialists to explore the
latest in startup security and Gemini, with a deep dive into our
newest models. This session is exclusively tailored for our
strategic Google Cloud startup customers." - Google
These great presentations got me thinking, and this morning I solved
this problem on the big list.
"How to connect any LLM model, starting with Gemini, to a
Pipi workspace so that an Ajabbi customer can use AI tools
without the AI changing the actual workspace code. Everything must run
in CFML on Ortus BoxLang, starting from Pipi 10."
Update 23/10/2026
The AGNTCon + MCPCon North America is on today (5 am to 2 pm NZ time), and I'm watching it live. The unforgettable Demetrios Brinkmann from AAIF is the host. Fantastic coverage of many of the issues in great depth, connected with solving this
problem;
By: Mike Peters and Google Search AI Mode (Gemini)
On a Sandy Beach: 22/10/2026
Mike Peters: Mike invented and designed Pipi and founded
Ajabbi.
Google Search AI Mode (Gemini): GSAM is great.
...
Today, using Google Search AI Mode, I asked some
very dumb questions about using connectors. I did this to find a way
to safely connect any workspace generated by Pipi running in CFML on
BoxLang to Gemini. Originally, MCP was the top
contender.
Here is the raw output, with Grammarly post-editing; I'll rework
the code manually. This is a good place to start.
Boxlang AI Module is free and open-source.
Remember, "Use this code with caution." 😎😎😎😎
Mike: What is a connector for Google Gemini?
Gemini:A Google Gemini connector is a secure
bridge that links Google Gemini with external apps, databases, and
third-party software.
Depending on the context, a "connector" refers to either data-ingestion
tools built into enterprise versions of Gemini or third-party integrations
that let other software platforms use Gemini’s AI model
capabilities.
1. Gemini Enterprise Connectors (Data Ingestion)
In Gemini Enterprise (Business, Standard, and Plus editions), IT
administrators manage connectors to securely bring scattered organisational
data into Gemini. This allows users to search, summarise, and analyse their
company's internal information using natural language.
Google Connectors: Link seamlessly to core Google Workspace apps
such as Gmail, Google Drive, Google Calendar, Chat, and Google
Sites.
Third-Party Connectors: Provide native access to external business
ecosystems. Gemini Enterprise supports over 100 integrations, including
Microsoft 365 (SharePoint, OneDrive),
Atlassian (Jira, Confluence), Salesforce,
ServiceNow, Slack, and Zendesk.
Custom Connectors: If a specific proprietary database or software
isn't in the standard catalogue, companies can build custom connectors
using the Google Cloud Discovery Engine API to fetch, transform,
and sync their data.
2. Outbound Connectors (AI for External Apps)
Conversely, many software developers use "Gemini Connectors" to inject
Gemini's AI capabilities into other software. In this scenario, the
connector acts as an API plugin. Examples include:
Automation Platforms: Tools
like Camunda and Microsoft Power Automate utilise a custom Gemini connector so business processes can
automatically generate text, analyse sentiment, or extract keywords from
files.
App Development Modules: Low-code platforms like Mendix feature
pre-packaged Gemini modules that let developers easily build AI chatbots,
translate languages, or parse large blocks of text directly inside their
applications.
Gemini: Are you looking to connect your enterprise data (like Jira
or SharePoint) to Gemini, or are you trying to build Gemini's AI features
into an external app you are developing? Let me know so I can provide the
exact steps or documentation links!
Mike: I want to find a way to connect an enterprise SaaS I built
to integrate with Gemini, so that Gemini can edit the content stored in
the SaaS, but not the SaaS itself.
Gemini: To connect your enterprise SaaS so Gemini can
manipulate content without threatening your core application codebase or
infrastructure, you should implement Function Calling via the Gemini
Enterprise Agent Platform (formerly Vertex AI) or the native Gemini
developer API.
With Function Calling, Gemini doesn't execute code inside your application and has no direct access to
your codebase. Instead, it acts as a decision-maker: you provide a strict
blueprint of the content-editing APIs it can use, and Gemini replies with
a request containing the specific content edits to make.
How the Security Boundary Works
Because Gemini lives on Google's servers, it can only interact with your
SaaS through the specific API endpoints you choose to expose.
Safe (Content Editing): You expose a
PATCH /api/v1/documents/{id} or
PUT /api/v1/content endpoint.
Gemini can request changes to text or data fields.
Isolated (SaaS Integrity): You never give Gemini endpoints that
can rewrite your code, alter your deployment configurations, drop
database tables, or access server SSH keys. Gemini literally cannot
touch the SaaS itself because no such API actions are made available to
it.
Architecture: The 4-Step Tool Loop
When a user gives Gemini an instruction (e.g., "Fix the typos in the
project brief stored in the SaaS"), the following cycle occurs:
┌──────┐ 1. User Prompt + Function Schema
┌────────┐
You Define the Blueprint: When you start a session using the
Gemini API or the standard Interactions API, you pass a list of
available tools in a JSON schema.
Gemini Decides, It Does Not Execute: Gemini parses the user's
request, notices that it needs to edit content, and spits back a
structured JSON command (a
functionCall) specifying which tool
to use and what arguments to supply (e.g., document_id: "123", new_text:
"...").
Your SaaS Validates and Executes: Your application receives
Gemini's JSON payload. Your server checks user permissions, sanitises
the inputs, executes the actual database write, and generates a success
confirmation.
Closing the Loop: Your backend returns the execution results
(functionResponse) back to Gemini, which then formats a friendly final
confirmation message for the end user.
Code Example: Exposing Content-Only Tools
When configuring the model using a modern environment like
Google AI Studio or your backend application SDK, you restrict
Gemini's access directly in the tools array.
javascript
import { GoogleGenAI } from '@google/genai'; const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY
}); // 1. Define only content-centric functions. // Do NOT include system management or code modification tools here. const updateSaasContentTool = { functionDeclarations: [{ name: 'updateSaasContent', description: 'Updates the text body or content of a
specific resource inside the SaaS database.', parameters: { type: 'OBJECT', properties: { contentId: { type: 'STRING', description:
'The unique ID of the document or record.' }, updatedText: { type: 'STRING', description:
'The newly edited or corrected text content.' } }, required: ['contentId', 'updatedText'] } }] }; // 2. Pass the content tool into the Gemini model instance const chat = ai.chats.create({ model: 'gemini-2.5-flash', // Or your preferred Gemini model config: { tools: [updateSaasContentTool], systemInstruction: "You are an assistant that edits
customer data records within our SaaS system. You only have access to
modify data fields via the provided tools." } });
Best Practices for Absolute Isolation
Least Privilege API Keys: Ensure the API key or OAuth token your
application uses to execute the function calls only has read/write
permissions for user content data—never admin or infrastructure
roles.
Strict Schema Validation: Always treat Gemini’s JSON output as
untrusted user input. Validate the fields on your server side before
writing to your database.
The Model Context Protocol (MCP): If you want an open-source,
standardised framework for securely exposing your SaaS data tools to
Gemini, consider building an MCP Server. Gemini natively supports
connecting to external data ecosystems via MCP server setups.
Gemini: What language or framework is your SaaS backend built in
(e.g., Node.js,
Python,
Go)? I can provide the exact code
block for handling the functionCall feedback loop on your server.
Mike: The SaaS backend is built in
CFML.
Gemini:Implementing Function Calling in a CFML backend (such as Lucee or Adobe
ColdFusion) requires managing the request and response cycle via standard
HTTP calls, as there isn't a native Google-maintained SDK for CFML.
Because Gemini only sends back JSON instructions, your CFML application
retains absolute control. Gemini decides what to edit, but your CFML
components (.cfc) validate the parameters, check user sessions, and
execute the actual SQL or ORM operations to modify the content
database.
The CFML Integration Workflow
The integration is split into two phases inside your CFML
application:
The Request: Sending the user prompt alongside your defined CFML
tool schema to Gemini.
The Execution: Parse Gemini's response, run your internal
CFML logic, and send the results back to complete the loop.
Phase 1: Sending the Tool Definition to Gemini
You will send a POST request to the
Gemini API
(/v1beta/models/gemini-2.5-flash:generateContent). Include your system boundaries and the explicit content-editing
functions in the tools array.
cfml
<!--- Define the tools Gemini is allowed to use ---> <cfset toolsPayload = [ { "functionDeclarations": [ { "name":
"editSaaSContent", "description":
"Edits the text body of a specific record inside the SaaS database. Use
this when the user asks to modify, format, or fix text content.", "parameters":
{
"type": "OBJECT",
"properties": {
"recordID": { "type": "STRING", "description": "The unique
UUID or ID of the content record." },
"newContent": { "type": "STRING", "description": "The
newly edited text content." }
},
"required": ["recordID", "newContent"] } } ] } ]> <!--- Prepare the request body ---> <cfset requestBody = { "contents": [ { "role": "user", "parts": [{ "text": "Fix
the grammatical errors in document ID 98765" }] } ], "systemInstruction": { "parts": [{ "text": "You are a content
assistant. You can only modify data records using the provided tools.
You cannot touch SaaS infrastructure or codebase." }] }, "tools": toolsPayload }> <!--- Make the API call to Gemini ---> <cfhttp url="https://googleapis.com" method="POST"
result="geminiResponse"> <cfhttpparam type="header" name="Content-Type"
value="application/json"> <cfhttpparam type="body"
value="#serializeJSON(requestBody)#"> </cfhttp>
Phase 2: Processing Gemini’s Decision in CFML
Gemini will not return a text answer. Instead, it will return a
functionCall object. Your CFML code must intercept this, verify it, run your
safe database update, and send the result back to Gemini.
Here is how you handle that response securely:
cfml
<cfif geminiResponse.statusCode CONTAINS "200"> <cfset responseData =
deserializeJSON(geminiResponse.fileContent)> <!--- Check if Gemini wants to call your
content-editing tool ---> <cfif structKeyExists(responseData,
"candidates") AND
arrayLen(responseData.candidates) GT 0 AND
structKeyExists(responseData.candidates[1].content.parts[1],
"functionCall")> <cfset functionCall =
responseData.candidates[1].content.parts[1].functionCall> <!--- Ensure Gemini is calling the
expected tool ---> <cfif functionCall.name EQ
"editSaaSContent"> <cfset args =
functionCall.args> <!--- SECURITY: Safely
validate incoming parameters before touching the database ---> <cfif isValid("guid",
args.recordID) OR isValid("integer", args.recordID)> <!---
Execute your native CFML database update --->
<cftry>
<query execute="UPDATE saas_content_table SET body_text = :newText
WHERE id = :id"
params="{ newText: {value=args.newContent,
cfsqltype='cf_sql_clob'}, id: {value=args.recordID,
cfsqltype='cf_sql_varchar'} }">
<cfset executionStatus = "Success: Content has been updated inside
the SaaS database.">
<cfcatch type="any">
<cfset executionStatus = "Error: Failed to write to
database. #cfcatch.message#">
</cfcatch>
</cftry> <cfelse> <cfset
executionStatus = "Error: Invalid record ID format provided by
AI."> </cfif> <!--- Final Step: Send the
execution result back to Gemini so it can reply to the user ---> <cfset followUpBody = { "contents":
[
<!--- You must include the original conversation history here,
followed by: --->
{
"role": "function",
"parts": [{
"functionResponse": {
"name": "editSaaSContent",
"response": { "output":
executionStatus }
}
}]
} ] }> <!--- Post this back to
Gemini to get the final conversational confirmation message ---> </cfif> </cfif> </cfif>
Keeping the Boundary Enforced in CFML
Session Validation: Do not rely solely on Gemini knowing who the
user is. Before running the
<cfquery> update, verify that
the logged-in user session (session.userID) actually has permission to modify
args.recordID.
Input Sanitisation: Use
cfqueryparam (or query params
inside script syntax as shown above) without exception. This completely
stops Gemini from performing SQL Injection if it goes rogue or gets
manipulated by a malicious user prompt (Prompt Injection).
Isolation of Admin CFCs: Keep this integration code inside a
separate, non-administrative Component (e.g.,
aiService.cfc). Never pass any
methods that handle server maps, application reloads (url.init), or file system manipulation (<cffile>) into the tools definition payload.
Gemini: Are you using a specific CFML framework like
ColdBox, Framework One (FW/1), or a legacy/custom legacy
codebase? I can help tailor the controller architecture or routing
settings to match your current setup.
Mike: Custom legacy CFML codebase running on
Boxlang.
Gemini: Running on BoxLang, you have a massive architectural
advantage. Because BoxLang includes a native, unified ecosystem called
BoxLang AI (bx-ai), you do not need to
mess with raw JSON schemas or manual HTTP requests like traditional CFML
setups require.
BoxLang AI acts as a multi-tenant orchestration layer. It allows you to
wrap your legacy .cfm/.cfc functions
as native AI Tools that Gemini can call.
The Security Sandbox Strategy
To ensure Gemini can edit data fields but cannot touch files, code, or the
application framework, wrap your legacy database modification logic in a
dedicated BoxLang AI tool execution loop.
Expose only raw data components: Only register data-centric
components with the BoxLang AI agent.
Context isolation: BoxLang AI isolates system contexts, so
Gemini sees only the specific functions you register via
bx-ai. It has zero visibility into
the underlying BoxLang JVM runtime, application maps, or file
headers.
Step-by-Step Implementation with BoxLang AI
1. Configure the Gemini Provider
In your configuration file (or programmatically at app startup), specify
Google Gemini as your LLM driver.
2. Create the Legacy CFML Tool Component (ContentEditor.cfc)
Write a clean, isolated component that handles the actual content-editing
task. BoxLang uses metadata annotations to communicate tool definitions to
Gemini.
cfml
component hint="Exposes database editing boundaries to Gemini via
BoxLang AI" { /** * Updates the text body of a specific SaaS
record. * @recordID The unique primary key or UUID of the
content row. * @newText The finalized text generated by Gemini
to inject into the row. */ public string function modifyRecordText( required string
recordID, required string newText ) { // 1. SECURITY CRITICAL: Session/Role
authorization check if ( !structKeyExists( session, "userID" )
) { return "Error: Unauthorized
execution block."; } // 2. Perform safe, isolated SQL write try { queryExecute( "UPDATE
saas_user_content SET body_content = :newText WHERE id = :id AND user_id
= :uid", {
newText: { value=arguments.newText, cfsqltype="cf_sql_clob" },
id: { value=arguments.recordID, cfsqltype="cf_sql_varchar" },
uid: { value=session.userID, cfsqltype="cf_sql_varchar" } } ); return "Success: Database
updated successfully."; } catch ( any e ) { return "Database insertion
failure: " & e.message; } } }
3. Execute via the BoxLang AI Agent
In your controller or execution script, register the ContentEditor.cfc as
an active tool inside the BoxLang AI interface. BoxLang automatically
converts your component methods into the function schemas Gemini
expects.
cfml
// 1. Instantiate the BoxLang AI service manager aiService = new bx.ai.models.AIService( application.aiConfig ); // 2. Create an agent and bind our safe legacy tool component agent = aiService.newAgent( systemInstruction = "You are a content assistant. You
format, correct, and edit user content strings. You have NO permission
to manage software infrastructure or access code files." ); // BoxLang registers the component methods natively as tools Gemini can
read agent.addTool( new path.to.components.ContentEditor() ); // 3. Process incoming prompt userPrompt = "Please clean up the grammar inside item ID:
ABC-12345"; aiResponse = agent.chat( userPrompt ); // BoxLang automatically intercepts Gemini's tool requests, matches it
to your // component method, executes the SQL, and handles the chat loop. writeOutput( aiResponse.getText() );
Why BoxLang Safeguards Your Legacy Codebase
Automatic Reflection Parsing: BoxLang reads the doc comments
(@recordID, @newText) of your legacy
components natively. It writes the precise JSON schemas Gemini needs
without exposing your component's internal SQL or actual source
path.
Prompt Injection Defence: Because you execute database updates using standard BoxLang
queryExecute bindings, any attempt by a user to manipulate Gemini into
writing malicious SQL code is stopped via parameter isolation at the JVM
database driver level.
Process Isolation: The model never touches file interpreters or
runtime evaluations (evaluate()). It
is entirely a "data-in, data-out" loop passing string arguments back into
your custom CFML method.
Gemini: Are you using CommandBox to manage this runtime
environment, or are you utilizing the new BoxLang Spring Boot Starter /
MiniServer configurations? Let me know if you want help mapping
environment secrets or handling cross-origin requests securely.
Mike: Is there a need to create a MCP server in this
situation?
Gemini: No, you do not need to create an MCP (Model Context
Protocol) server for this specific situation.
Since you are running your legacy CFML codebase on BoxLang, you can use
the built-in bx-ai module to handle Gemini’s function calling out of the
box. Creating an independent MCP server would add unnecessary
architectural overhead.
The distinction between the two approaches explains why the native BoxLang
AI method is more effective for your current setup:
1. Traditional Native BoxLang AI
(bx-ai) Approach
In the solution detailed above, you use BoxLang's native AI
orchestration.
How it works: Your SaaS application initiates the API call to
Gemini, provides the component tools directly, intercepts the response,
and runs the local CFML code.
Why it's sufficient: The AI loop is fully contained inside your
existing application runtime. There is no middleman.
2. The MCP (Model Context Protocol) Alternative
The Model Context Protocol (introduced by Anthropic) is designed to solve
the "N×M integration problem"—enabling any AI model to talk to any software platform via a universal,
standardised JSON-RPC protocol without custom API adapters.
If you were to build an MCP server for your SaaS, the architecture would
look like this:
Gemini AI Client ⇄ Your Custom BoxLang MCP Server (JSON-RPC) ⇄ Your SaaS
Database
Head-to-Head Comparison
Feature
Native BoxLang AI (bx-ai)
Building a Custom MCP Server
Architectural Overhead
Very Low. Built directly into your existing
app.
Medium. Requires setting up a persistent HTTP/SSE or
STDIO listener.
Security Handling
Easier. Inherits your user's live session
(session.userID) naturally during the web request.
Harder. You must implement a separate token-auth
protocol so the MCP server knows which tenant's data is being
edited.
When to use it
When you control the app interface and want Gemini
to act as an integrated feature inside your SaaS.
When you want external AI clients (like Claude
Desktop, Cursor, or ChatGPT) to connect directly into your SaaS
database.
The Only Exception: BoxLang's Built-In
bx-mcp
The creators of BoxLang (Ortus Solutions) shipped a native module called
bx-mcp. However, this module is
specifically used to expose BoxLang runtime diagnostics and server
monitoring to AI coding assistants (like Cursor or Claude Desktop) so they
can help you debug your server. It is not meant for business-logic data
editing inside a SaaS platform.
Final Recommendation
Stick to the native BoxLang AI tool registration (agent.addTool()) approach. It keeps your security boundaries tied straight to your
legacy CFML session state, eliminates the need to route traffic through
external protocol translators, and requires significantly less code to
maintain.
Gemini: Is this Gemini integration meant to be a user-facing
feature inside your SaaS dashboard, or is it an internal automation tool
for your development team? Knowing the end-user environment helps
determine the best way to handle user authentication.
Mike:
It will be a user-facing feature inside the SaaS dashboard. Drafting
emails, writing reports, etc.
Gemini: Since this is a user-facing feature inside your SaaS
dashboard, security boundaries shift from protecting infrastructure to
protecting tenant data isolation. You must guarantee that User A cannot
trick Gemini into modifying or reading User B's content (a vulnerability
known as Indirect Prompt Injection).
Using the native BoxLang AI Agent framework, your legacy CFML codebase
can safely contain this interaction within the user's active
session.
The 3-Tier Security Architecture
Because Gemini operates statelessly, it doesn't know who is logged into
your application. Your CFML controller acts as the gatekeeper by wrapping
the execution cycle:
[ User Dashboard ]
│ 1. Submits Prompt
▼
[ BoxLang Controller ] ──( 2. Restricts Tool to Session User ID )──► [
Gemini API ]
│
│
│ 4. Executes SQL restricted by User ID
│ 3. Returns Function Call
▼
▼
[ SaaS Content DB ]
◄──────────────────────────────────────────────────────┘
Production-Ready BoxLang Implementation
Here is how you handle the user's dashboard request safely. This ensures
Gemini can draft emails and write reports only within the strict scope of
that specific logged-in user.
1. The Secure Legacy Component (ContentManager.cfc)
To lock Gemini down, the component's internal SQL must strictly filter on
the user's session identifier.
cfml
component hint="Handles user-facing AI content operations securely."
{ /** * Creates a brand new document draft (like an email
or report) for the logged-in user. * @title The descriptive title of the document,
email, or report. * @body The AI-generated text body content. */ public struct function createDraftDocument( required
string title, required string body ) { // 1. HARD SECURITY WALL: Verify user is
actively authenticated if ( !structKeyExists( session, "userID" )
|| !len( session.userID ) ) { return { "success": false,
"message": "Authentication required." }; } try { // 2. TENANT ISOLATION: The
session.userID is hardcoded into the write operation var newID = createUUID(); queryExecute( "INSERT INTO
saas_documents (id, user_id, title, body_content, status,
created_at) VALUES
(:id, :uid, :title, :body, 'draft', NOW())", {
id: { value=newID, cfsqltype="cf_sql_varchar" },
uid: { value=session.userID, cfsqltype="cf_sql_varchar"
},
title: { value=arguments.title, cfsqltype="cf_sql_varchar" },
body: { value=arguments.body, cfsqltype="cf_sql_clob" } } ); return { "success":
true, "message":
"Draft securely created.", "documentID":
newID }; } catch ( any e ) { return { "success": false,
"message": "Failed to save draft system-side." }; } } }
2. The Dashboard Endpoint (/api/generate.cfm)
When the user clicks "Draft Email" or "Write Report" on the UI, your
legacy backend endpoint intercepts the action, provisions the BoxLang AI
agent, and executes the sequence.
cfml
<cfsetting showdebugoutput="false"> <cfcontent type="application/json"> <!--- Basic session protection checklist ---> <cfif !structKeyExists( session, "userID" )> <cfset writeOutput( serializeJSON({ "error":
"Unauthorized session context." }) )> <cfabort> </cfif> <!--- Read incoming AJAX request payload from dashboard front-end
---> <cfset requestBody = toString(
getPageContext().getRequest().getInputStream() )> <cfset payload = isJSON( requestBody ) ? deserializeJSON(
requestBody ) : {}> <cfset userPrompt = structKeyExists( payload, "prompt" ) ?
payload.prompt : ""> <cfif !len( trim( userPrompt ) )> <cfset writeOutput( serializeJSON({ "error": "Prompt
cannot be empty." }) )> <cfabort> </cfif> <!--- Orchestrate the BoxLang AI Agent ---> <cfscript> // Initialize BoxLang AI configuration aiConfig = { "provider": "google", "model": "gemini-2.5-flash", "apiKey":
server.system.environment.GEMINI_API_KEY }; aiService = new bx.ai.models.AIService( aiConfig ); // Inject persona boundaries preventing infrastructure
manipulation agent = aiService.newAgent( systemInstruction = "You are an embedded
text generation feature inside our SaaS dashboard. Your role is strictly
to draft emails, generate reports, or format user notes. Use the
provided creation tool when the text is ready." ); // Register our secure, session-aware component contentTool = new path.to.components.ContentManager(); agent.addTool( contentTool ); try { // Execute the processing loop // BoxLang handles: Prompt -> Gemini
-> Tool Call execution -> Final message aiResponse = agent.chat( userPrompt ); // Return clear status back to your
Javascript dashboard UI writeOutput( serializeJSON({ "status": "success", "message":
aiResponse.getText() })); } catch ( any e ) { writeOutput( serializeJSON({ "status": "error", "message": "An error occurred
processing the AI generation request." })); } </cfscript>
Core Rules for User-Facing UI Security
Never Pass Document IDs blindly from Gemini: If you allow Gemini
to edit existing reports, always add a server-side cross-check inside
your CFC method to ensure that the document's user_id matches the
current session.userID before
running an UPDATE query.
Sanitise Output Rendering: When displaying Gemini's output
inside your web application's email template editor or dashboard UI,
ensure your frontend escapes the returned string or uses text-only
content inputs. This blocks potential
HTML/XSS injection vulnerabilities
if Gemini accidentally reads a malicious script from external text
input.
Keep the UI Asynchronous: Generating comprehensive reports can
take a few seconds. Ensure your dashboard interface triggers a loading
spinner or streaming layout while waiting for this endpoint to
reply.
Gemini: How do you intend to present the output to your users? Will they see
the final report saved quietly in the background to their file list, or
should Gemini stream the text dynamically onto their screen inside a
text editor layout?
Mike: Both options are required.
Gemini: To support both workflows seamlessly, structure your
BoxLang backend to handle them dynamically.
When streaming content directly into a UI editor, you want Gemini to
respond with raw text as fast as possible. When saving files silently in
the background, you want Gemini to process the text and automatically
invoke your secure ContentManager.cfc tool to write to the database.
Here is how you can implement both approaches within your legacy BoxLang
infrastructure.
Architecture Strategy
You can use a simple URL parameter or request payload field (e.g.,
mode=stream or
mode=background) to route the
request:
Stream Mode: Bypasses BoxLang's automated tool system entirely.
It hooks into BoxLang AI's underlying streaming mechanism to push
real-time text chunks straight to the client browser via Server-Sent
Events (SSE) or a flushing response buffer.
Background Mode: Utilises the Function Calling setup we built
previously. It tells the agent to orchestrate the generation, save it
quietly via your secure ContentManager.cfc, and return a simple
"Success" payload.
The Dual-Mode Endpoint (/api/generate.cfm)
This unified legacy endpoint routes incoming dashboard requests based on
the user's intent.
cfml
<cfsetting showdebugoutput="false"> <!--- Session and validation guardrails ---> <cfif !structKeyExists( session, "userID" )> <cfcontent type="application/json"> <cfset writeOutput( serializeJSON({ "error":
"Unauthorized" }) )> <cfabort> </cfif> <cfparam name="url.mode" default="background"> <!--- Options:
'stream' or 'background' ---> <cfset userPrompt = form.prompt ?: ""> <cfscript> // Initialize standard BoxLang AI Configuration aiConfig = { "provider": "google", "model": "gemini-2.5-flash", "apiKey":
server.system.environment.GEMINI_API_KEY }; aiService = new bx.ai.models.AIService( aiConfig ); //
---------------------------------------------------- // OPTION 1: STREAM MODE (Direct to Text Editor UI) //
---------------------------------------------------- if ( url.mode EQ "stream" ) { // Change content type to allow dynamic
browser streaming cfcontent( type="text/event-stream" ); agent = aiService.newAgent( systemInstruction = "You are
an AI text editor assistant. Generate the requested text directly. Do
NOT attempt to run tools or database commands." ); // Request a stream from BoxLang AI // BoxLang stream chunks are iterated
natively agent.chatStream( userPrompt, function(
chunk ) { // Write each text fragment
directly to the dashboard UI writeOutput( "data: " &
chunk.getText() & "#chr(10)##chr(10)#" ); cfflush(); // Force the
server buffer to send the text chunk immediately }); cfabort(); } //
---------------------------------------------------- // OPTION 2: BACKGROUND MODE (Save Silently to DB) //
---------------------------------------------------- if ( url.mode EQ "background" ) { cfcontent( type="application/json" ); agent = aiService.newAgent( systemInstruction = "You are
a background generator. Take the prompt, create the required content,
and immediately use the createDraftDocument tool to save it." ); // Bind the secure tool component we
created earlier contentTool = new
path.to.components.ContentManager(); agent.addTool( contentTool ); try { // Gemini creates content,
triggers tool, BoxLang updates database aiResponse = agent.chat(
userPrompt ); writeOutput(
serializeJSON({ "status":
"success", "message":
"Your request has been generated and filed to your dashboard document
list." })); } catch ( any e ) { writeOutput( serializeJSON({
"status": "error", "message": "Failed background execution loop."
})); } cfabort(); } </cfscript>
Frontend JavaScript Connections
Handling Option 1 (Streaming into a UI Rich Text Editor)
To consume the streaming API on your client dashboard web page, use
JavaScript's EventSource api or a standard fetch body reader:
javascript
async function startStreamingEditor(promptText) { const editorElement =
document.getElementById('my-rich-text-editor'); editorElement.innerHTML = "Generating draft..."; const response = await
fetch('/api/generate.cfm?mode=stream', { method: 'POST', headers: { 'Content-Type':
'application/x-www-form-urlencoded' }, body: new URLSearchParams({ prompt:
promptText }) }); const reader = response.body.getReader(); const decoder = new TextDecoder(); editorElement.innerHTML = ""; // Clear loader while (true) { const { value, done } = await
reader.read(); if (done) break; let chunk = decoder.decode(value); // Clean out SSE data prefix formatting if
necessary let processedText =
chunk.replace(/^data:\s*/gm, ''); // Append raw content directly onto the
editor screen in real-time
editorElement.append(processedText); } }
Handling Option 2 (Background Generation Banner)
For background workflows, a standard asynchronous fetch request alerts
the user when the document safely lands in their dashboard
portfolio.
javascript
function generateBackgroundReport(promptText) { showGlobalToast("Generating your report in the
background..."); fetch('/api/generate.cfm?mode=background', { method: 'POST', headers: { 'Content-Type':
'application/x-www-form-urlencoded' }, body: new URLSearchParams({ prompt:
promptText }) }) .then(res => res.json()) .then(data => { if(data.status === "success") { showGlobalToast("Finished!
Your document is ready in your list."); refreshDocumentTableGrid();
// Reloads legacy dashboard table data } }); }
The industry standard for embedding Gemini into a custom web UI is a
Hybrid API + Frontend Component architecture. Your BoxLang backend acts as
the secure proxy to the Gemini API, and your frontend uses JavaScript to
render a tailored, styled chat or text interface.
Recommended Frontend Integration Paths
Depending on how integrated you want the feature to feel, you have three
primary architectural choices:
1. Pre-built Custom Web Components (Easiest & Cleanest)
Instead of building a text box and message bubbles from scratch, you can
use open-source, framework-agnostic UI component libraries designed
exactly for this.
The Tools: Use libraries like Vercel AI SDK UI components or
Deep Chat Core.
How it works: You drop a custom HTML tag (e.g.,
<deep-chat>) into your legacy
.cfm dashboard page. You point its url property to your
/api/generate.cfm endpoint. The
component handles layout, streaming chunks, loading skeletons, and
markdown formatting natively.
html
<!-- Example using an open-source web component in a standard .cfm
page --> <script type="module" src="https://unpkg.com"></script> <div class="dashboard-panel"> <h3>SaaS AI Copywriter</h3> <!-- The component automatically handles user input
and streams your BoxLang response --> <deep-chat request='{"url":
"/api/generate.cfm?mode=stream", "method": "POST"}' stream="true" initialMessages='[{"text": "Hi! Let me help
you draft your next email or report.", "role": "ai"}]' style='{"borderRadius": "8px", "border":
"1px solid #ccc", "height": "500px"}'> </deep-chat> </div>
2. Native Dynamic Text Injection (For In-Line Editors)
If your goal is to let users click a button (e.g., "Write a summary of
this report"), you shouldn't use a chat window at all. Use inline
JavaScript to inject text directly into your existing editor layout.
The Tools: Standard JavaScript fetch() API combined with your
existing text editors (like TinyMCE, CKEditor, or Quill).
How it works: When the user clicks the "AI Assist" button, your
JavaScript calls your BoxLang stream endpoint and updates the text
editor's content block-by-block using the Reader API shown in the
previous response. This creates a seamless "AI is writing inside my
document" effect.
3. Custom Chat Assistant Widget (For Floating Help Panels)
If you want an expandable, floating AI helper in the bottom corner of
your SaaS dashboard, you can build a small HTML/CSS chat container.
How it works: You manage a message array in JavaScript. When the
user types a prompt and hits send, your code appends a user-message
bubble to the DOM, fires the AJAX request to BoxLang, and streams the
response directly into a newly appended ai-message bubble.
Why This Architecture Keeps Your SaaS Secure
By routing everything through your BoxLang backend and rendering natively
on the client side:
API Key Masking: Your Google Gemini API key stays safe on your
server inside BoxLang (server.system.environment.GEMINI_API_KEY). It never appears in the browser's developer inspector.
Seamless Styling: The AI feature matches your SaaS design
system, fonts, and colours perfectly.
State Control: You maintain full control over the data loop, so
you can save copies of the generated text to your database before it
even touches the user's screen.
Gemini:Which style fits your vision best? A floating conversational chat
assistant, a standalone copywriting page, or contextual buttons embedded
right inside your text input fields? I can give you the exact HTML/CSS
boilerplates to start wiring it up.
... to be continued ... 😎😎😎😎
Live from AGNTCon + MCPCon NA, OpenAI, Anthropic, DeepMind, Entire, Block
& More
Join the Agentic AI Foundation live on YouTube for AGNTCon + MCPCon North
America on October 22, 2026, from 9:00 AM to 6:00 PM California time (PDT /
UTC−7).
Tune in from anywhere for a day focused on the ideas, people, and open
technologies shaping agentic AI and the Model Context Protocol. Drop in for
a keynote or stay with us for the full broadcast.
What to expect
3,500+ attendees from across the agentic AI ecosystem
1,000+ organizations represented
150+ talks across 11 tracks
7 hands-on workshops at no added cost for in-person attendees