This is a submission for the Sanity Challenge, Path One: Ship an Agent That Queries Real Content.
What I Built
I built Unscript, a terminal-based writing transformation agent that uses structured knowledge from Sanity to make writing feel more natural, clear, and human without treating every piece of text the same way.
The main idea started with a simple problem:
Most writing tools can change text. The harder part is deciding how the text should be changed and what should be preserved.
Instead of putting all of the rules inside the prompt, I wanted the agent to have an actual knowledge layer that it could query.
That became Unscript.
Unscript combines:
- A terminal-first user experience
- A structured Sanity Knowledge Base
- Sanity Context MCP
- Gemini 3.1 Flash-Lite
- Deterministic validation
- Source-aware retrieval
- Configurable writing levels and content types
The resulting flow is:
User
↓
Unscript CLI
↓
Agent
↓
Sanity Context MCP
↓
Structured Knowledge
↓
Gemini 3.1 Flash-Lite
↓
Transformation
↓
Validation
↓
Polished CLI Output
The important part is that the model does not have to invent the writing rules on its own.
The rules live in structured content.
Why I Built It This Way
I did not want to build another text box with a prompt behind it.
I wanted the knowledge itself to be part of the product.
For example, Unscript needs to understand that transforming an article is different from transforming a short message. It also needs to know what tone is being requested, which writing patterns should be avoided, which parts of the original text must remain intact, and what level of transformation the user selected.
That information is modeled separately in Sanity.
The Knowledge Base contains structured categories for:
- Writing Patterns
- Content Types
- Humanization Levels
- Tone Rules
- Transformation Rules
- Preservation Rules
- Sources
- User Decisions
This gives the agent something much more useful than a large collection of unstructured instructions.
It can retrieve the relevant pieces of knowledge for the specific transformation it is performing.
The Agent Workflow
When a user runs the humanization flow, Unscript does not immediately send the text to Gemini.
It first builds the context required for the transformation.
The agent goes through several stages:
1. Analyze the input
↓
2. Classify the content
↓
3. Determine the requested transformation level
↓
4. Retrieve relevant knowledge from Sanity
↓
5. Transform the text
↓
6. Validate the result
↓
7. Display the result and knowledge used
This separation was important to me because it makes the system easier to reason about.
The model handles the language transformation, while Sanity provides the structured rules and knowledge that guide that transformation.
The main flow includes:
Unscript
├── Inspect Knowledge
├── Inspect Environment
├── Help
└── Humanize Text
Inspect Knowledge
The first part of the demo shows the knowledge available to the agent.
Unscript connects to the Sanity Context MCP endpoint and retrieves structured knowledge from the Sanity Knowledge Base.
The output shows information such as:
- Knowledge rules
- Writing patterns
- Sources
- Preservation rules
- Provenance
The demo then switches to Sanity Studio to show where that structured content actually lives.
This was important to demonstrate because the knowledge isn't mocked inside the CLI.
It exists as real Sanity content.
Inspect Environment
The environment inspection checks the pieces required by the application, including the configured Sanity Context MCP connection and Gemini integration.
Help
The help screen explains the available Unscript commands and how to move through the terminal interface.
Humanize Text
The main demo then enters the actual transformation workflow.
I select:
Content Type → Article
Tone → Friendly
Transformation → Custom
Unscript asks for the text, retrieves the relevant knowledge through Sanity Context MCP, sends the transformation task to Gemini 3.1 Flash-Lite, validates the result, and displays the final output.
The result also exposes the knowledge that influenced the transformation instead of simply showing a new paragraph and hiding everything that happened behind it.
For the demonstrated run, the retrieved context included:
- 9 rules
- 7 writing patterns
- 3 sources
- 4 preservation rules
along with the transformed result and provenance information.
How I Used Sanity
Sanity is the knowledge layer of Unscript.
I created a Sanity project called:
Unscript Knowledge
The structured content is stored in the production dataset.
The project contains schemas for:
writingPattern
contentType
humanizationLevel
toneRule
transformationRule
preservationRule
source
userDecision
These schemas let the knowledge remain structured instead of becoming one large prompt.
For example, a transformation rule can exist as its own piece of knowledge, while a preservation rule can independently describe something that must not be changed.
Sources are also represented separately so retrieved knowledge can retain provenance.
Sanity Context MCP
The most important part of the Sanity integration is the Sanity Context MCP endpoint.
Unscript connects to:
https://api.sanity.io/v1/context/organizations/o8io0s3zz/mcp/unscript-knowledge
The agent establishes an MCP session and performs the required MCP initialization flow.
The runtime sequence includes:
initialize
↓
notifications/initialized
↓
initial_context
↓
tools/list
↓
groq_query
The groq_query capability is then used to retrieve the relevant structured knowledge.
This means the agent isn't simply querying a local JSON file or a hard-coded array.
The knowledge comes through Sanity's Context MCP layer.
Why Structured Content Matters Here
This is probably the most important part of the project.
A normal keyword search might find a phrase such as "make writing more conversational."
But Unscript needs more context than that.
It needs to know:
- What type of content is being transformed?
- What tone was selected?
- What transformation level was requested?
- Which patterns should be changed?
- Which patterns should be preserved?
- Which rules apply to this particular type of writing?
- Where did the rule come from?
That is where structured content becomes useful.
Instead of treating knowledge as a flat document, Unscript can retrieve different pieces of related knowledge and use them together.
For example:
Article
+
Friendly Tone
+
Selected Humanization Level
+
Transformation Rules
+
Preservation Rules
+
Writing Patterns
+
Source Information
becomes the context used for the transformation.
That is the part of the project that would be difficult to reproduce with a simple keyword search.
Knowledge Base
The initial Knowledge Base is populated with structured content across the different schemas.
The current knowledge set contains:
Sources 3
Content Types 8
Humanization Levels 5
Tone Rules 8
Transformation Rules 14
Preservation Rules 10
Writing Patterns 10
User Decisions 0
That gives the agent a deliberately separated knowledge model rather than one giant prompt.
The sources include established writing guidance such as:
- U.S. Digital.gov / GSA plain-language guidance
- Microsoft Writing Style Guide
- Google writing guidance
The goal was not to copy these sources into one enormous instruction block.
Instead, relevant ideas were modeled into structured knowledge that the agent can retrieve and apply.
Gemini 3.1 Flash-Lite
Gemini 3.1 Flash-Lite handles the actual language transformation.
Sanity provides the context.
Gemini performs the transformation.
Unscript then validates the result before presenting it.
Conceptually:
Sanity
=
What should guide the transformation?
Gemini
=
How should the text be transformed?
Unscript
=
Did the result follow the required constraints?
Keeping those responsibilities separate made the architecture much easier to control.
Validation
The transformation does not end when Gemini returns text.
Unscript performs deterministic validation after the model response.
This gives the application a second layer between the model and the final terminal output.
The validation stage checks the result against the requirements established by the agent and the retrieved knowledge.
This was especially important for preservation rules.
A writing transformation should not accidentally remove information simply because the model thinks the sentence sounds better.
The agent therefore treats transformation and preservation as separate concerns.
Provenance
Another part I wanted to expose was where the knowledge came from.
The retrieved context is not presented as mysterious model knowledge.
The result can show the sources and knowledge used by the agent.
This creates a clearer chain:
Source
↓
Structured Sanity Knowledge
↓
Sanity Context MCP
↓
Agent Retrieval
↓
Gemini Transformation
↓
Validation
↓
Final Result
That makes the application easier to inspect and debug.
It also makes the Sanity integration meaningful rather than decorative.
The Terminal Experience
I intentionally built Unscript as a CLI.
The reason is simple: I wanted the tool to feel like something a developer could actually use from a terminal rather than another generic AI webpage.
The interface includes:
- Interactive command navigation
- Human-readable status information
- Environment inspection
- Knowledge inspection
- Interactive transformation flow
- Structured result output
- Retrieved knowledge information
- Validation information
- Clean handling of terminal input
I also spent time fixing edge cases around terminal input.
For example, partial-line Ctrl+D handling and the interaction between editing, backspacing, and the prompt needed to be handled properly.
Those details are easy to ignore when building a demo, but they matter when the application is actually being used from a terminal.
Technical Architecture
The complete architecture looks like this:
┌──────────────────────────────┐
│ User │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ Unscript CLI │
│ │
│ commands / prompts / UI │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ Unscript Agent │
│ │
│ analyze │
│ classify │
│ select level │
│ retrieve knowledge │
│ transform │
│ validate │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ Sanity Context MCP │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ Unscript Knowledge │
│ │
│ writing patterns │
│ content types │
│ humanization levels │
│ tone rules │
│ transformation rules │
│ preservation rules │
│ sources │
│ user decisions │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ Gemini 3.1 Flash-Lite │
│ │
│ language transformation │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ Validation │
│ │
│ deterministic checks │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ Final CLI Result │
│ │
│ transformed text │
│ retrieved knowledge │
│ provenance │
│ validation │
└──────────────────────────────┘
Implementation
Unscript is built as a TypeScript CLI.
The project was developed around a few principles:
1. Knowledge should live outside the model
Writing rules are structured in Sanity rather than being permanently embedded in the application prompt.
2. Retrieval should be inspectable
The agent should be able to show what it retrieved and where that knowledge came from.
3. Transformation should be separate from validation
The model generates the transformation, but deterministic application logic is responsible for checking the result.
4. The CLI should be usable on its own
The terminal experience is part of the product rather than just a debugging interface.
5. Real services should be used in the demo
The Sanity project, Sanity Context MCP endpoint, and Gemini integration are real parts of the runtime.
Testing
I did not want the project to only work for the exact demo recording.
The project went through separate checks during development, including:
- TypeScript compilation
- ESLint
- Prettier
- Production build
- CLI runtime checks
- Sanity schema validation
- MCP initialization
- Knowledge retrieval
- Gemini integration
- Transformation flow
- Deterministic validation
- Terminal input edge cases
The final project reached a clean test/build state with the complete end-to-end flow working against the real Sanity Context MCP integration.
What I Learned
The biggest thing I learned from building Unscript is that structured content changes how an agent can be designed.
It is tempting to put everything into one prompt:
Here are some rules.
Now transform this text.
That can work for a small prototype.
But once the rules become more complex, the prompt becomes the application.
That was exactly what I wanted to avoid.
With Sanity, the knowledge becomes something that can be modeled, inspected, changed, queried, and reused.
The agent can then treat that knowledge as an external system rather than a giant hard-coded instruction.
That separation makes the whole application easier to understand.
Why Sanity Is Actually Part of the Product
I did not use Sanity simply because the challenge required a Sanity project.
If Sanity were removed from Unscript, the architecture would lose an important part of its purpose.
The application depends on structured knowledge for:
- Content classification
- Humanization levels
- Tone selection
- Transformation rules
- Preservation rules
- Writing patterns
- Source information
- Knowledge retrieval
The model is therefore not the entire product.
The knowledge layer is part of the product.
That is the main reason I chose Path One.
Source Code
https://github.com/maisamabbas0323/unscript.git
Sanity Project
Project ID: b209xsoi
Dataset: production
Sanity Context MCP
https://api.sanity.io/v1/context/organizations/o8io0s3zz/mcp/unscript-knowledge
Demo Video
https://youtube.com/watch?v=ZopcANma17Q\&si=jhlLzNV6l999JFDY)
Sanity Project Details
Project: Unscript Knowledge
Project ID: b209xsoi
Dataset: production
Organization: o8io0s3zz
The Sanity Studio contains the schemas and structured documents used by the agent's Knowledge Base.
Final Thoughts
Unscript started with a simple question:
What if a writing agent did not have to keep all of its knowledge inside the model?
The result became a terminal application where Sanity acts as the structured knowledge layer, Sanity Context MCP connects that knowledge to the agent, Gemini 3.1 Flash-Lite performs the language transformation, and Unscript validates and presents the result.
The interesting part for me is not simply that the application can rewrite text.
It is that the reasoning context behind the transformation can be structured, queried, inspected, and connected back to its sources.
That is what I wanted to explore with the Sanity Challenge.
Built With
- Sanity
- Sanity Content Lake
- Sanity Context MCP
- Sanity Studio
- TypeScript
- Gemini 3.1 Flash-Lite
- MCP
- GROQ
- Node.js
- Terminal CLI
Challenge
Sanity Challenge — Path One: Ship an Agent That Queries Real Content
Top comments (0)