DEV Community

Maisam Abbas
Maisam Abbas

Posted on

Unscript — A Sanity-Powered Writing Agent That Actually Knows Why It Changes Your Text

Sanity Challenge Path One Submission

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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?
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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                   │
└──────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Sanity Context MCP

https://api.sanity.io/v1/context/organizations/o8io0s3zz/mcp/unscript-knowledge
Enter fullscreen mode Exit fullscreen mode

Demo Video

https://youtube.com/watch?v=ZopcANma17Q\&si=jhlLzNV6l999JFDY)


Sanity Project Details

Project: Unscript Knowledge
Project ID: b209xsoi
Dataset: production
Organization: o8io0s3zz
Enter fullscreen mode Exit fullscreen mode

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)