If you're building web novel infrastructure with Claude or ChatGPT and need to manage serialized fiction at scale, this gives you structured endpoints for series setup, character databases, episode generation, and quality evaluation workflows. It's Korean-first but the MCP interface is language-agnostic. The remote transport means you don't run anything locally, you just point your client at app.seosa.ink/api/mcp and get access to worldbuilding tools, outline management, and multi-pass editorial feedback loops. Useful if you're prototyping narrative tools or need to maintain consistency across dozens of chapters without rebuilding context each time. The samples show it handles genre conventions (romance, fantasy, wuxia) with attention to pacing and character continuity.
Public tool metadata for what this MCP can expose to an agent.
seosa_list_seriesList the authenticated user's series (most recent first). Returns id, title, genre, language, platform, and creation time.2 paramsList the authenticated user's series (most recent first). Returns id, title, genre, language, platform, and creation time.
limitintegeroffsetintegerseosa_get_seriesGet one series by ID (must be owned by the authenticated user).1 paramsGet one series by ID (must be owned by the authenticated user).
idstringseosa_create_seriesCreate a new series for the authenticated user. Free of credit charge. Returns the created series object including its assigned id.9 paramsCreate a new series for the authenticated user. Free of credit charge. Returns the created series object including its assigned id.
genrestringtitlestringlanguagestringplatformstringsynopsisstringtotal_partsintegersystem_rulesstringworld_summarystringtotal_chaptersintegerseosa_update_seriesPartial-patch update for an existing series. Provide at least one field beyond `id`. Free of credit charge. Fields: title / genre / language / platform / synopsis / world_summary / system_rules / total_parts / total_chapters / visibility. Changing language/platform/genre also...11 paramsPartial-patch update for an existing series. Provide at least one field beyond `id`. Free of credit charge. Fields: title / genre / language / platform / synopsis / world_summary / system_rules / total_parts / total_chapters / visibility. Changing language/platform/genre also...
idstringgenrestringtitlestringlanguagestringplatformstringsynopsisstringvisibilitystringprivate · publictotal_partsintegersystem_rulesstringworld_summarystringtotal_chaptersintegerseosa_list_episodesList episodes of a given series in ascending order. Content body is omitted; use seosa_get_episode for full text.3 paramsList episodes of a given series in ascending order. Content body is omitted; use seosa_get_episode for full text.
limitintegeroffsetintegerseries_idstringseosa_get_episodeGet one episode (with full content) by series ID + episode number. Prefer `episode_number`; the legacy `number` alias remains for backward compatibility.3 paramsGet one episode (with full content) by series ID + episode number. Prefer `episode_number`; the legacy `number` alias remains for backward compatibility.
numberintegerseries_idstringepisode_numberintegerseosa_get_episode_statusLightweight status check for an episode: whether the body has been saved, the word count, the quality score (if evaluated), and whether an outline exists. Returns state ∈ { absent, body_ready, evaluated } plus `has_outline: boolean`. Free, read-only. Decision matrix for `state...2 paramsLightweight status check for an episode: whether the body has been saved, the word count, the quality score (if evaluated), and whether an outline exists. Returns state ∈ { absent, body_ready, evaluated } plus `has_outline: boolean`. Free, read-only. Decision matrix for `state...
series_idstringepisode_numberintegerseosa_wait_for_episodeServer-side long-poll that blocks until an episode reaches the target state. Use this AFTER calling seosa_generate_episode (which returns `status: "dispatched"` immediately while the body is written in the background) or seosa_evaluate_episode. Free, read-only. Behavior: - `un...4 paramsServer-side long-poll that blocks until an episode reaches the target state. Use this AFTER calling seosa_generate_episode (which returns `status: "dispatched"` immediately while the body is written in the background) or seosa_evaluate_episode. Free, read-only. Behavior: - `un...
untilstringbody_ready · evaluatedseries_idstringepisode_numberintegertimeout_secondsintegerseosa_update_episodeUpdate episode content, quality score, or publish state. Pass `content` to overwrite the body (the previous body is auto-snapshotted to `episode_version` with source 'mcp_edit' — rollback via `seosa_rollback_episode`). Pass `quality_score` (0-100) and/or `is_public` to update...5 paramsUpdate episode content, quality score, or publish state. Pass `content` to overwrite the body (the previous body is auto-snapshotted to `episode_version` with source 'mcp_edit' — rollback via `seosa_rollback_episode`). Pass `quality_score` (0-100) and/or `is_public` to update...
contentstringis_publicbooleanseries_idstringquality_scoreintegerepisode_numberintegerseosa_list_episode_versionsList all versions (revision history) of an episode in descending version order. Each item includes `version_num`, `word_count`, `source` ('edit' / 'mcp_edit' / 'api_edit' / 'generation' / 'generation_<provider>' / 'improve' / 'rollback'), and `created_at`. Body content is omit...2 paramsList all versions (revision history) of an episode in descending version order. Each item includes `version_num`, `word_count`, `source` ('edit' / 'mcp_edit' / 'api_edit' / 'generation' / 'generation_<provider>' / 'improve' / 'rollback'), and `created_at`. Body content is omit...
series_idstringepisode_numberintegerseosa_rollback_episodeRestore an episode body to a previous version. The current body is preserved as a new snapshot with source 'rollback' before the restore — so a rollback is itself reversible by another rollback. Use `seosa_list_episode_versions` first to find the target `version_num`. Free.3 paramsRestore an episode body to a previous version. The current body is preserved as a new snapshot with source 'rollback' before the restore — so a rollback is itself reversible by another rollback. Use `seosa_list_episode_versions` first to find the target `version_num`. Free.
series_idstringversion_numintegerepisode_numberintegerseosa_check_balanceReturn the authenticated user's credit balance, broken down by bucket, plus the current subscription plan.Return the authenticated user's credit balance, broken down by bucket, plus the current subscription plan.
No parameter schema in public metadata yet.
seosa_run_wizardGenerate and persist world, characters, relationships, and bible components for a series in one call. Costs up to 8 credits (2 per step, charged only for steps that actually run). Skips steps whose data already exists; pass force:true to re-run all targeted steps. Pass `steps`...3 paramsGenerate and persist world, characters, relationships, and bible components for a series in one call. Costs up to 8 credits (2 per step, charged only for steps that actually run). Skips steps whose data already exists; pass force:true to re-run all targeted steps. Pass `steps`...
forcebooleanstepsarrayseries_idstringseosa_get_charactersReturn the canonical character list (series.characters jsonb) plus the character_state mirror (character_name / class_name / level / last_updated_episode). Free, read-only.1 paramsReturn the canonical character list (series.characters jsonb) plus the character_state mirror (character_name / class_name / level / last_updated_episode). Free, read-only.
series_idstringseosa_get_bibleReturn bible_component rows (world / system_rules / locations / terminology / themes / etc) for a series. Pass component_name to fetch a single component. Free, read-only.2 paramsReturn bible_component rows (world / system_rules / locations / terminology / themes / etc) for a series. Pass component_name to fetch a single component. Free, read-only.
series_idstringcomponent_namestringseosa_get_relationshipsReturn the current relationship graph (narrative_state.relationships) plus the last_updated_episode marker. Set include_snapshots=true to also return per-episode relationship_snapshot rows (newest first). Free, read-only.3 paramsReturn the current relationship graph (narrative_state.relationships) plus the last_updated_episode marker. Set include_snapshots=true to also return per-episode relationship_snapshot rows (newest first). Free, read-only.
series_idstringmax_snapshotsintegerinclude_snapshotsbooleanseosa_update_bible_componentUpsert one bible_component (world / system_rules / locations / terminology / timeline / foreshadowing / themes / voice_profiles / scene_inventory) for a series. If no row exists for the given component_name it is created; if one exists it is fully replaced (no partial merge —...5 paramsUpsert one bible_component (world / system_rules / locations / terminology / timeline / foreshadowing / themes / voice_profiles / scene_inventory) for a series. If no row exists for the given component_name it is created; if one exists it is fully replaced (no partial merge —...
dataobjectseries_idstringchange_reasonstringcomponent_namestringepisode_contextintegerseosa_update_relationshipsUpsert the canonical character relationship graph (narrative_state.relationships) for a series. Replaces the entire graph — for partial edits, read with seosa_get_relationships first, modify, then write back. Free, no credit charge. Persists in the same shape as seosa_run_wiza...3 paramsUpsert the canonical character relationship graph (narrative_state.relationships) for a series. Replaces the entire graph — for partial edits, read with seosa_get_relationships first, modify, then write back. Free, no credit charge. Persists in the same shape as seosa_run_wiza...
series_idstringrelationshipsarrayepisode_contextintegerseosa_create_charactersManually create one or more canonical characters for a series, WITHOUT running seosa_run_wizard. Idempotent: characters whose name already exists in series.characters are skipped. Free, no credit charge. Persists to both `series.characters` (jsonb cast list) and the `character...2 paramsManually create one or more canonical characters for a series, WITHOUT running seosa_run_wizard. Idempotent: characters whose name already exists in series.characters are skipped. Free, no credit charge. Persists to both `series.characters` (jsonb cast list) and the `character...
series_idstringcharactersarrayseosa_update_characterPartially update a character_state row identified by canonical character_name. Only fields explicitly passed (class_name / level / stats / skills / fragments) are written; the rest are preserved. 404 if no character_state row exists with that name — create it via seosa_create_...7 paramsPartially update a character_state row identified by canonical character_name. Only fields explicitly passed (class_name / level / stats / skills / fragments) are written; the rest are preserved. 404 if no character_state row exists with that name — create it via seosa_create_...
levelintegerstatsobjectskillsarrayfragmentsarrayseries_idstringclass_namestringcharacter_namestringseosa_generate_outlineGenerate a draft outline for the next episode of the given series. Costs 2 credits. Returns title, emotional_tone, hook, key_events, character_focus plus a `conflicts` array (bible/narrative consistency warnings). May early-terminate with character proposals if the series need...6 paramsGenerate a draft outline for the next episode of the given series. Costs 2 credits. Returns title, emotional_tone, hook, key_events, character_focus plus a `conflicts` array (bible/narrative consistency warnings). May early-terminate with character proposals if the series need...
series_idstringinstructionstringskip_castingbooleanepisode_numberintegeraccepted_charactersarrayauto_accept_proposedbooleanseosa_update_outlinePartial-patch update for an existing episode outline. Free (0 credits). Use this AFTER seeing `conflicts` from seosa_generate_outline to fix the exact field at issue (e.g. tweak one `key_events` line) without paying for a new LLM call. Provide at least one of: title, emotional...8 paramsPartial-patch update for an existing episode outline. Free (0 credits). Use this AFTER seeing `conflicts` from seosa_generate_outline to fix the exact field at issue (e.g. tweak one `key_events` line) without paying for a new LLM call. Provide at least one of: title, emotional...
hookstringtitlestringseries_idstringkey_eventsarrayemotional_tonestringepisode_numberintegercharacter_focusarrayrecheck_conflictsbooleanseosa_regenerate_outlineRe-roll an existing episode outline by feeding the current outline plus fix hints (typically the `conflicts` from a previous generate_outline call) back into the LLM. Costs 2 credits. Skips the casting gate (existing series characters are reused as-is). Overwrites the existing...4 paramsRe-roll an existing episode outline by feeding the current outline plus fix hints (typically the `conflicts` from a previous generate_outline call) back into the LLM. Costs 2 credits. Skips the casting gate (existing series characters are reused as-is). Overwrites the existing...
series_idstringinstructionstringfix_conflictsarrayepisode_numberintegerseosa_get_outlineReturn a single episode_outline row for a given series + episode number (title / emotional_tone / hook / key_events / character_focus). 404 if the outline has not been generated yet. Free, read-only.2 paramsReturn a single episode_outline row for a given series + episode number (title / emotional_tone / hook / key_events / character_focus). 404 if the outline has not been generated yet. Free, read-only.
series_idstringepisode_numberintegerseosa_generate_episodeDispatch full-body generation for an episode. Costs 30 credits. Returns IMMEDIATELY with `status: "dispatched"` — there is NO transport timeout to handle. The actual writing runs in the background (typically 1m20s, up to 3 minutes). **Always follow this with `seosa_wait_for_ep...3 paramsDispatch full-body generation for an episode. Costs 30 credits. Returns IMMEDIATELY with `status: "dispatched"` — there is NO transport timeout to handle. The actual writing runs in the background (typically 1m20s, up to 3 minutes). **Always follow this with `seosa_wait_for_ep...
polishbooleanseries_idstringepisode_numberintegerseosa_rewrite_episode_selectionGenerate 3 alternative rewrites for a selected passage of an episode body. **Does NOT modify the episode** — returns `alternatives` array; the caller must pick one and apply it via `seosa_update_episode` with the new full content. Provide either a `preset` (vivid/concise/tensi...6 paramsGenerate 3 alternative rewrites for a selected passage of an episode body. **Does NOT modify the episode** — returns `alternatives` array; the caller must pick one and apply it via `seosa_update_episode` with the new full content. Provide either a `preset` (vivid/concise/tensi...
presetstringvivid · concise · tension · emotional · dialogue · expandcontextstringseries_idstringinstructionstringselected_textstringepisode_numberintegerseosa_evaluate_episodeEvaluate the quality of an existing episode from 3 perspectives: writer/reader/editor. Costs 5 credits. Returns IMMEDIATELY with `status: "dispatched"` — evaluation runs in the background (typically ~30s). Follow with `seosa_wait_for_episode` using `until: "evaluated"` to awai...2 paramsEvaluate the quality of an existing episode from 3 perspectives: writer/reader/editor. Costs 5 credits. Returns IMMEDIATELY with `status: "dispatched"` — evaluation runs in the background (typically ~30s). Follow with `seosa_wait_for_episode` using `until: "evaluated"` to awai...
series_idstringepisode_numberinteger