Walkthrough: Describe the Work, Get a Governed Schema
Updated
Ask any chatbot for a discussion-board schema and you get one. The problem is never getting a schema — it is getting the schema your team already agreed on: the same abbreviations, the same suffixes, the same word for the same idea, in the tenth table as in the first.
This walkthrough goes from an empty project to standards-compliant DDL without leaving your agent. The example is a threaded discussion board — small enough to follow, and it forces the one case that trips most tools: a post that replies to another post.
Every output below is copied from a real run.
Before you start
- Node.js 22 or later, and
sqemo-mcp2.2.0 or later - The MCP server registered with your agent, and
npx sqemo-mcp logindone — see AI Integration (MCP) - A workspace with a team standard. This walkthrough uses one whose word list already contains Customer, Order, Number, Content, Flag, and a dozen more
1. Start the project on the standard
A new ERD has an empty word list, so physical names come out as the logical names verbatim. Bind the project to your standard at creation and the word list, naming rules, and domains come with it:
list_workspaces
// → [{ workspaceId: "…", name: "Engineering", role: "owner",
// glossary: { version: 4, … } }]
create_erd {
name: "Threaded Discussion Board",
workspaceId: "…",
target: { server: true }
}
get_erd_overview
// → { glossaryLinked: true, dictionaryEntryCount: 28, domainCount: 15, … }
That glossaryLinked: true is the whole point. From here on, every name
the agent creates is resolved through the standard rather than invented.
2. Describe the work in business terms
Now talk to your agent the way you would talk to a colleague:
Model a discussion board where members post articles, a post can be a reply to another post, and members comment on posts.
The agent calls upsert_entity and upsert_attribute with logical
names. You never type a column name:
upsert_entity { logicalName: "Post" }
// → { physicalName: "POST" }
upsert_attribute { logicalName: "Post Content", domain: "Content" }
// → { physicalName: "POST_CNTS" }
upsert_attribute { logicalName: "Delete Flag", domain: "Flag" }
// → { physicalName: "DELETE_YN" }
Content → CNTS and Flag → YN are not the agent’s taste. They are
your word list’s abbreviations, applied the same way they were applied in
every other table your team has modelled.
Domains carry the data type, so Content is varchar(1000) everywhere it
appears — nobody decides column widths per table.
3. The reply-to-a-post case
A self-referencing foreign key cannot reuse the primary key’s name, so it gets a role prefix. The prefix is a naming rule, and — like every other word — it is resolved through the word list:
upsert_relationship {
sourceEntityId: "<Post>", targetEntityId: "<Post>",
cardinality: "1:N", relationshipType: "nonIdentifying",
constraintName: "FK_POST_PARENT"
}
`PARENT_POST_NO` varchar(20) COMMENT 'Parent Post Number'
The prefix defaults to Parent, so the FK above comes out as
PARENT_POST_NO with no configuration. Change it once on your standard —
in the app, Naming tab → Edit → Self-reference prefix — and every
self-referencing FK in every connected project follows it.
Two things worth knowing before you change it:
- Register the prefix as a word first if you want it abbreviated
(
Parent→PRNTgivesPRNT_POST_NOinstead ofPARENT_POST_NO). - Unlike ordinary column names, existing self-referencing FKs are renamed on their next edit, because their names are derived rather than stored. Everything else keeps the name it already has.
Projects created before this default changed don’t shift underneath you.
When one loads, an unset prefix is written into its naming rules: 상위
(Korean for “parent”) if it already has a self-referencing relationship, so those
column names stay exactly as they were — Parent if it doesn’t.
4. Words the standard doesn’t have yet
Real modelling hits words nobody registered. Sqemo does not quietly invent an abbreviation — it flags them:
lint_erd
// → { code: "unknown-word", severity: "warning",
// objectName: "Delete Flag",
// message: "'Delete Flag' contains words not registered in the word list." }
Delete is missing, so DELETE_YN came out unabbreviated. The fix is not
to hardcode a name — it is to propose the word:
propose_dictionary_word {
logicalWord: "Delete", physicalWord: "DELETE", abbreviation: "DEL",
note: "Found while modelling the discussion board"
}
// → { proposalId: "…", status: "pending", baseVersion: 4 }
The standard’s owner approves it in the app, and the abbreviation propagates to every connected project. This is the part a chatbot cannot do: the agent is a proposer, not the authority.
5. Check compliance
check_naming compares a physical name you already have against the one
the standard would generate:
check_naming {
logicalName: "Customer Phone Number",
physicalName: "CUSTOMER_PHONE_NUMBER"
}
// → { generatedPhysicalName: "CUST_TEL_NO",
// compliant: false, providedMatches: false }
That is the check to put in CI. It works on a project file with no agent involved:
npx sqemo-mcp lint schema.erd.json # exit code 1 on violations
6. Export
export_sql { dialect: "mysql" }
CREATE TABLE `POST` (
`POST_NO` varchar(20) NOT NULL COMMENT 'Post Number',
`POST_CNTS` varchar(1000) NOT NULL COMMENT 'Post Content',
`DELETE_YN` char(1) NOT NULL DEFAULT 'N' COMMENT 'Delete Flag',
`PARENT_POST_NO` varchar(20) COMMENT 'Parent Post Number',
PRIMARY KEY (`POST_NO`)
) COMMENT='An article on a board; may reply to another post';
ALTER TABLE `POST` ADD CONSTRAINT `FK_POST_PARENT`
FOREIGN KEY (`PARENT_POST_NO`) REFERENCES `POST` (`POST_NO`);
Logical names survive as column comments, so the business meaning reaches the database instead of dying in the diagram.
One gotcha
defaultValue is raw SQL, emitted verbatim after DEFAULT. Quote string
literals yourself:
upsert_attribute { logicalName: "Delete Flag", defaultValue: "'N'" } // ✅
upsert_attribute { logicalName: "Delete Flag", defaultValue: "N" } // ❌ DEFAULT N
Unquoted values are treated as expressions, which is what you want for
0 or CURRENT_TIMESTAMP — and what you do not want for N.
What actually happened here
The agent did the typing. It did not decide anything that matters:
| Decision | Made by |
|---|---|
| Which tables and relationships exist | You, in business terms |
| Abbreviations, separators, case | Your team standard |
| Data types | Your domains |
| New words entering the standard | The standard’s owner, by approval |
That is the difference between generating a schema and growing one your team can keep. Next: Naming Standards for how the word list and rules work, or Team collaboration for the proposal queue and roles — the queue is also a two-minute video, if you would rather watch a word get approved than read about it.