The Graffiticode MCP server connects AI assistants and applications to a growing catalog of domain-specific tools. Each tool is powered by a Graffiticode language — a specialized DSL optimized for a particular task domain. You interact with these languages entirely through natural language: describe what you want to create, and a language-specific AI generates the result.
You never need to learn or write DSL code. The MCP server is the interface.
The Graffiticode MCP server is a thin router. It exposes five language-agnostic tools that route your natural language requests to language-specific backends. Each backend has deep knowledge of its language's domain and translates your descriptions into working programs.
┌──────────────────────────────────────────────────────────┐
│ Your AI Assistant (Claude, ChatGPT, or any MCP client) │
│ "Create a concept web assessment about photosynthesis" │
└──────────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Graffiticode MCP Server (thin router) │
│ Tools: create_item, update_item, get_item, │
│ list_languages, get_language_info │
└──────────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Graffiticode API → Language-specific AI backends │
│ L0169 backend: concept web assessments │
│ L0166 backend: spreadsheets and tabular data │
│ L0159 backend: flashcards and matching games │
│ ...and more via list_languages │
└──────────────────────────────────────────────────────────┘
The workflow is always the same regardless of language:
- Discover — Call
list_languagesto see what's available. - Create — Call
create_itemwith a language ID and a natural language description. - Iterate — Call
update_itemwith a natural language description of what to change. - Retrieve — Call
get_itemto fetch an existing item by ID. - Learn more — Call
get_language_infofor details about a specific language.
The MCP server supports two transport modes: a hosted HTTP endpoint for cloud deployments and agent platforms, and a stdio transport for local CLI usage.
The hosted Graffiticode MCP server is available at:
https://mcp.graffiticode.org/mcp
Add via Settings → Connectors with the URL https://mcp.graffiticode.org/mcp. OAuth authentication is handled automatically.
{
"mcpServers": {
"graffiticode": {
"url": "https://mcp.graffiticode.org/mcp",
"headers": {
"Authorization": "Bearer <your-api-key>"
}
}
}
}Any MCP-compatible client can connect using Streamable HTTP transport at the /mcp endpoint. Authentication is via Bearer token in the Authorization header — either an OAuth access token or a Graffiticode API key.
For local development or CLI usage:
npm install graffiticode-mcp-server
GC_API_KEY_SECRET=your-api-key npx graffiticode-mcpOr clone and build from source:
git clone https://github.com/graffiticode/graffiticode-mcp-server.git
cd graffiticode-mcp-server
npm install && npm run build
GC_API_KEY_SECRET=your-api-key npm startThe server supports two authentication methods:
- OAuth 2.1 (recommended for interactive clients) — The server implements OAuth 2.1 with PKCE. Clients that support MCP OAuth discovery will be guided through the flow automatically.
- API Key (for programmatic access) — Pass your Graffiticode API key as a Bearer token:
Authorization: Bearer gc_xxxxx
Discover what Graffiticode languages are available.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
category |
string | No | Filter by category (e.g., "data", "general") |
search |
string | No | Search by keyword |
Example — discovering all languages:
list_languages()
Returns:
{
"languages": [
{
"id": "L0002",
"name": "L0002",
"description": "Simple programs with text rendering and theming",
"category": "general"
},
{
"id": "L0159",
"name": "L0159",
"description": "Flashcards, Match and Memory card games",
"category": "data"
},
{
"id": "L0166",
"name": "L0166",
"description": "Spreadsheets and tabular data with formulas",
"category": "data"
},
{
"id": "L0169",
"name": "L0169",
"description": "Interactive concept web assessment diagrams",
"category": "data"
}
]
}Example — searching for assessment-related languages:
list_languages(search: "assessment")
Get detailed information about a specific language, including its spec URL and React component for embedding.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
language |
string | Yes | Language ID (e.g., "L0169") |
Example:
get_language_info(language: "L0169")
Returns the language's description, category, specification URL, and instructions for rendering items using the language's React component.
Create a new item in any Graffiticode language. Describe what you want in natural language — the language-specific AI backend handles everything else.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
language |
string | Yes | Language ID (e.g., "L0169"). Use list_languages to discover options. |
description |
string | Yes | Natural language description of what to create. |
name |
string | No | A friendly name for the item. |
Returns: An object containing item_id (for subsequent calls), task_id, language, data (the compiled output), and react_usage (instructions for rendering).
What makes a good description? Be specific about what you want. Include domain-relevant details. Think about describing the end result you want to see, not implementation details. The language-specific AI understands domain terminology.
Modify an existing item by describing what you want to change. The language is auto-detected from the item — you don't need to specify it again.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
item_id |
string | Yes | The item ID from a previous create_item or get_item call. |
modification |
string | Yes | Natural language description of what to change. |
Returns: The same structure as create_item, with the updated data.
The server maintains conversation history for each item, so the language-specific AI has context from previous interactions. You can make incremental changes naturally: "add another concept," "change the theme to dark," "make the header row blue."
Retrieve an existing item by its ID.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
item_id |
string | Yes | The item ID to retrieve. |
Returns: The item's data, code, metadata, timestamps, and React rendering instructions.
L0169 creates interactive concept web assessment diagrams — educational tools where students engage with interconnected concept nodes arranged in a radial layout around a central hub. Students demonstrate understanding by dragging concepts from a tray into the correct positions on the web.
- Concept webs with a central topic and radiating connected concepts
- Drag-and-drop assessments where learners place concepts correctly
- Themed interfaces (light or dark)
- Image-augmented concepts with visual resources
- Configurable concept tray positions (right, left, top, bottom)
- Self-grading assessments that check learner responses
To create a concept web assessment, call create_item with language: "L0169" and describe your assessment in natural language:
create_item(
language: "L0169",
description: "Create a concept web assessment about photosynthesis.
The central concept is 'Light Reactions'. Connected concepts are
'Calvin Cycle', 'Chlorophyll', 'ATP Production', and 'Water Splitting'.
Each concept should be assessed by matching its label. Use a dark theme
with the concept tray on the right side."
)
The L0169 backend understands educational assessment terminology. Here are more examples of effective descriptions:
A simple concept web:
create_item(
language: "L0169",
description: "Create a concept web about the water cycle. The central
concept is Evaporation. Connected concepts: Condensation, Precipitation,
Collection, and Transpiration."
)
A complex assessment with images:
create_item(
language: "L0169",
description: "Create a concept web assessment about the solar system.
The Sun is the central concept. Connected concepts are Mercury, Venus,
Earth, Mars, Jupiter, Saturn, Uranus, and Neptune. Add a planet image
to each concept. Assess each concept by matching its name. Place the
concept tray at the bottom and use a light theme."
)
An assessment for younger learners:
create_item(
language: "L0169",
description: "Create a concept web for elementary students about
animal classification. The central concept is 'Animals'. Connected
concepts are 'Mammals', 'Birds', 'Fish', 'Reptiles', and 'Amphibians'.
Keep it simple with no assessment — just the web structure."
)
Use update_item to refine your assessment iteratively. You don't need to redescribe the entire web — just say what you want to change:
Adding concepts:
update_item(
item_id: "abc123",
modification: "Add a new connected concept called 'Carbon Fixation'
between Calvin Cycle and ATP Production."
)
Changing the theme:
update_item(
item_id: "abc123",
modification: "Switch to a light theme and move the concept tray
to the bottom."
)
Adjusting assessment configuration:
update_item(
item_id: "abc123",
modification: "Remove the assessment from the central concept but
keep it on all the connected concepts."
)
L0169 has 14 core operations that cover the full domain of concept web assessments:
| Capability | What It Means |
|---|---|
| Topic labeling | Give the overall concept web a title or subject label |
| Central concept (anchor) | Define the hub node that all other concepts radiate from |
| Connected concepts | Add peripheral nodes that connect to the central concept with auto-generated edges |
| Assessment configuration | Attach evaluation parameters to any concept node specifying what constitutes a correct response |
| Assessment methods | Define how responses are evaluated (e.g., by matching a value) |
| Concept tray | A draggable tray of concept labels that learners place onto the web |
| Tray alignment | Position the tray on any side: right, left, top, or bottom |
| Image attachment | Add visual resources (images) to any concept node |
| Theming | Switch between light and dark interface themes |
L0169 is specialized for radial concept webs. It does not support free-form quizzes, multiple-choice questions, text-input fields, hierarchical trees, or linear sequences. For tabular data and spreadsheets, use L0166. For flashcards and matching games, use L0159.
L0166 creates interactive spreadsheets with formatted cells, formulas, and structured tabular data.
create_item(
language: "L0166",
description: "Create a spreadsheet with two columns: Term and Definition.
Add three rows: Photosynthesis / The process by which plants convert
sunlight to energy, Mitosis / Cell division producing two identical
cells, Osmosis / Movement of water across a semipermeable membrane.
Make the header row bold with a light blue background."
)
update_item(
item_id: "xyz789",
modification: "Add a fourth row: Homeostasis / The body's ability to
maintain a stable internal environment. Make the header background
color light green instead of light blue."
)
L0159 creates interactive flashcard sets and card-matching games for learning and review.
create_item(
language: "L0159",
description: "Create a set of flashcards for Spanish vocabulary.
Include: House / Casa, Dog / Perro, Cat / Gato, Book / Libro,
Water / Agua. The front of each card shows the English word and
the back shows the Spanish translation."
)
Every Graffiticode language has a corresponding React component published on npm. When you call create_item, update_item, or get_item, the response includes a react_usage field with everything you need to embed the item in a React application.
Each language has its own npm package following the pattern @graffiticode/<language-id>:
npm install @graffiticode/l0169 # for concept web assessments
npm install @graffiticode/l0166 # for spreadsheets
npm install @graffiticode/l0159 # for flashcardsThe pattern is the same for every language. Use the data field from the MCP response to hydrate the component:
import React from 'react';
import { Form } from '@graffiticode/l0169';
import '@graffiticode/l0169/style.css';
function createState(initialData) {
let data = initialData;
return {
get data() { return data; },
apply(action) {
if (action.args) {
data = { ...data, ...action.args };
}
}
};
}
function ConceptWebAssessment({ itemData }) {
// itemData is the COMPLETE 'data' field from create_item / update_item / get_item
const [state] = React.useState(() => createState(itemData));
return <Form state={state} />;
}The state.data object must be the complete data field from the API response, including its validation.regions structure. Pass the whole object — don't extract subsets.
When using the MCP server with Claude or ChatGPT, items render automatically as interactive widgets directly in the chat interface. No additional setup is needed — the server provides widget resources that these platforms use to display your items inline.
| Issue | Solution |
|---|---|
| Multiple React versions error | Add resolve.dedupe: ['react', 'react-dom'] to your Vite/webpack config |
| Cannot read 'regions' of null | Pass the complete data object from the API response, not a subset |
| CSS not loading | Import styles from @graffiticode/<lang>/style.css |
The MCP server maintains conversation history for each item. When you call update_item, the server passes context from previous interactions to the language-specific AI, so it understands what you've already created and can make precise modifications.
This means you can work iteratively in a natural conversational style:
- "Create a concept web about the water cycle with Evaporation at the center"
- "Add Runoff as a connected concept"
- "Make it use a dark theme"
- "Add assessment to all the connected concepts"
Each step builds on the previous ones. The language AI remembers the full history.
For self-hosted deployments:
| Variable | Required | Default | Description |
|---|---|---|---|
GC_API_KEY_SECRET |
Yes (stdio) | — | Graffiticode API key for stdio transport |
GRAFFITICODE_CONSOLE_URL |
No | https://graffiticode.org/api |
API endpoint |
GRAFFITICODE_AUTH_URL |
No | https://auth.graffiticode.org |
Auth endpoint |
PORT |
No | 3001 |
HTTP server port (hosted mode) |
MCP_SERVER_URL |
No | https://mcp.graffiticode.org |
Public URL for OAuth metadata |
The MCP server is open source under the MIT license:
- Repository: github.com/graffiticode/graffiticode-mcp-server
- L0169 Language: github.com/graffiticode/l0169
- L0169 Specification: l0169.graffiticode.org/spec.html
All Graffiticode languages are accessed through the natural language interface provided by this MCP server or the Graffiticode console at console.graffiticode.org. Direct code authoring is neither required nor recommended.