Skip to content

Shortcuts

Shortcuts are named references that give stable, short identifiers to URIs. They follow CommonMark’s reference link pattern with an @ prefix.


Defines a shortcut in a single line:

[@id Label](url)
  • @id — the shortcut identifier
  • Label — human-readable display text
  • url — the topic URI

This is syntactic sugar. String decomposes it into a reference link and a reference definition internally.

Separates the display from the definition, following CommonMark’s reference link syntax:

[Label][@id]
[@id]: url

The reference definition ([@id]: url) MAY be placed anywhere in the document — typically grouped at the bottom.

# Inline form
[@docs Documentation](https://docs.example.com)
# Reference form — same result
[Documentation][@docs]
[@docs]: https://docs.example.com

The inline form is recommended for convenience. The reference form is useful when the same shortcut is referenced multiple times or when URLs are long and would clutter the content.


Regardless of which form the author uses, String always presents shortcuts to the AI as reference links:

Author writes (inline):

Visit the [@docs Documentation](https://docs.example.com) for details.

Author writes (reference):

Visit the [Documentation][@docs] for details.
[@docs]: https://docs.example.com

AI sees (both cases):

Visit the [Documentation][@docs] for details.

The URL never enters the AI’s context. The AI navigates with /open @docs. String resolves the actual URI.

In a standard CommonMark viewer, the inline form renders as a normal link. The reference form renders as a standard reference link (CommonMark-compatible).


Shortcut IDs MUST follow these rules:

RuleValidInvalid
Lowercase letters@docs@Docs
Numbers allowed@section2
Hyphens allowed@getting-started
Underscores allowed@api_ref
Must start with a letter@api@2api
MUST NOT start with link-@links@link-1

Pattern: @[a-z][a-z0-9_-]*

The link- prefix is reserved for auto-generated shortcuts. Named shortcuts MUST NOT use IDs starting with link-.


Visit the [@docs Documentation](https://docs.example.com) for
more details, or see the [@changelog Changelog](./changelog.md).

Menu files consist of shortcut lines:

[@home Home](../index.md)
[@api API Reference](../api/index.md)

Grouped at the bottom of a document or section:

[@docs]: https://docs.example.com
[@api]: https://api.example.com/v2
[@pricing]: ./pricing.md

Shortcuts are namespaced by their source.

Shortcuts defined in the document (inline or reference) belong to the page namespace. The prefix is implicit:

@docs = @page.docs

Both forms are valid. The short form @docs is preferred.

Shortcuts from nav menus are prefixed with the menu name:

Menu file (nav/main.md, menu name main):

[@home Home](../index.md)
[@api API Reference](../api/index.md)

AI sees:

[Home][@main.home]
[API Reference][@main.api]
SourceNamespaceAI seesFull form
Document [@docs ...]page[@docs][@page.docs]
Menu main [@home ...]main[@main.home][@main.home]
Menu api [@auth ...]api[@api.auth][@api.auth]

Regular Markdown links without explicit shortcut IDs are automatically assigned identifiers by the runtime.

Only links with long or external topics are converted:

Link topicConverted?Example
https:// or http:// URLYes[Docs](https://docs.example.com)
Relative path > 40 charsYes[Guide](./very/deep/nested/path/to/doc.md)
Short relative pathNo[Home](./index.md)

Short relative paths are already readable — no shortcut needed. Author-defined shortcuts ([@id Label](url)) are never converted; they keep their explicit ID.

The runtime attempts to derive a readable slug from the link label. A slug is used when the label meets all of these conditions:

  1. 20 characters or fewer
  2. Contains only letters, numbers, spaces, or hyphens
  3. Not a duplicate of an already-assigned slug in the same document

The slug is produced by: lowercase → remove non-word characters (except spaces and hyphens) → trim → replace spaces with hyphens.

GitHub → github
MDN Web Docs → mdn-web-docs
Example API → example-api

When the same slug appears more than once, a numeric suffix is appended starting from 2:

@docs ← first occurrence
@docs-2 ← second
@docs-3 ← third

The suffix is per-slug — duplicates of @docs and duplicates of @github maintain independent counters.

When the label does not qualify for a slug (too long, special characters, or empty after sanitizing), the runtime assigns @link-N where N increments from 1 per document:

ConditionLabelResult
Exceeds 20 charsA Very Long Label That Exceeds Limit@link-1
Special charactersC++ Reference (2024)@link-2
Empty slugLabel produces empty string after sanitizing@link-3

The link-N counter is independent from slug duplicate counters.

Source:

Visit [GitHub](https://github.com) for code.
Read the [MDN Web Docs](https://developer.mozilla.org) reference.
See [Docs](https://docs.first.com) and [Docs](https://docs.second.com).
And [Docs](https://docs.third.com) too.
Check [A Very Long Documentation Title Here](https://example.com/long).
Stay on [Home](./index.md).

AI view:

Visit [GitHub][@github] for code.
Read the [MDN Web Docs][@mdn-web-docs] reference.
See [Docs][@docs] and [Docs][@docs-2].
And [Docs][@docs-3] too.
Check [A Very Long Documentation Title Here][@link-1].
Stay on [Home](./index.md).
  • @github, @mdn-web-docs, @docs — label-derived slugs
  • @docs-2, @docs-3 — duplicate slug with suffix
  • @link-1 — non-sluggable fallback (label exceeds 20 chars)
  • [Home](./index.md) — short relative path, not converted
  • Slugs and @link-N IDs belong to the page namespace.
  • The link- prefix is reserved — named shortcuts MUST NOT use it.
  • Auto-shortcuts are scoped to the current document. When the document changes, previous auto-shortcuts are invalidated.
  • Same document content always produces the same auto-shortcuts (deterministic, top-to-bottom order).

Drill-in pattern ({@shortcut} = {value} in action responses)

Section titled “Drill-in pattern ({@shortcut} = {value} in action responses)”

Principle — dual-mode legibility. SFMD documents stay useful without the String runtime: an AI reading the raw file can parse the actions, follow the next: hints, and reason about what the app does. With String, the same document becomes interactive (shortcuts resolve, actions dispatch, navigation works). To preserve this dual mode:

  • /open targets (pages, external resources) MUST appear as valid markdown links in source — [Label](url) or [Label][@id]. A plain markdown viewer renders them as clickable links; an AI without String still parses them as navigation.
  • /act targets (action-only entities — no public URL, runtime binding required) can render as plain-text tokens (@post-1: Title). A markdown viewer can’t execute actions anyway, and an AI reading the source sees the action declarations plus the next: line and knows what would happen under String.

Don’t dress action dispatch in link form — it’s a broken link to any viewer that isn’t String, and it confuses the intent.

Action response templates register inline shortcuts that bind opaque ids (post ids, record keys, tuples) to short, human-grade names the agent then passes to subsequent action calls. Pattern:

for: p in Response.body.posts
{@post} = {p.id}
- {@post}: {p.title} — by {p.author.name} in /{p.submolt_name}
end:
next: /act.read @post-N · /act.upvote @post-N · /act.comment @post-N "..."

Each loop iteration registers @post-1, @post-2, … pointing at the post id. The agent sees @post-1: <title> in the output and runs /act.read @post-3 to drill in — the runtime resolves @post-3 back to the id when binding the action’s required field.

The next: line tells the agent which actions chain naturally from this result.

Why plain text is correct here: a plain markdown viewer can’t run /act.read anyway, so a [label](something) would just be a broken or misleading link. The token form (@post-1: Title) is honest about what the runtime is providing.

When one logical handle needs to carry more than one identifier — a comment addressed by (post_id, comment_id), a board cell addressed by (column, row), anything composite — register the shortcut as a tuple: comma-separated expressions inside parentheses on the right-hand side.

for: c in Response.body.comments
{@reply} = ({post}, {c.id})
- {@reply}: {c.author.name}: {c.content}
end:
next: /act.reply @reply-N "..."

{post} here is the parent post id taken from the calling action’s input fields. {c.id} is each comment’s id from the response. Iteration N registers @reply-N with value [post_id, comment_id_N].

The agent then runs /act.reply @reply-3 "thanks". The action’s field receives the whole tuple; its URI/body template addresses individual slots with {name[N]} indexing:

```act.reply
POST https://api.example.com/posts/{reply[0]}/comments/{reply[1]}/replies
reply, -r: tuple (required) "(post_id, comment_id) from /act.comments"
text, -t: string (required) "Reply body"
```

{reply[0]} substitutes the post id, {reply[1]} the comment id. The agent never sees either id directly — only the opaque @reply-3 handle. (Same indexing works inside body: templates; see Actions → Body template.)

  • One-element tuple normalizes to scalar. (x) and x are equivalent — useful when the same template sometimes produces a single id and sometimes a pair.
  • Empty tuple () is allowed (zero-element shortcut).
  • Nested braces are respected when splitting on commas. ({a}, {b}, {c}) is three elements; ({a, b}) is one element whose value happens to contain a literal comma.
  • {name} without an index emits the tuple joined by , — a debug fallback. In real templates, always address slots with {name[N]}.
  • {name[N]} returns empty string when N is out of range — fail-quiet rather than fail-loud. Validate at the API boundary.
  • /open @<tuple> is rejected with INVALID_TARGET: “Shortcut X is a value tuple, not a navigable target.” Tuples carry no URL, so navigation has nowhere to go. The error message suggests passing it to an action instead.
Section titled “/open navigation — standard markdown links”

Pages and external resources that an agent can /open MUST be written as standard markdown links so they remain followable in any viewer. Inline form:

[Home](string.md) · [Communities](communities.md) · [Profile](profile.md)

Or reference form when the URL is dynamic:

for: s in Response.body.submolts
- [{s.display_name}][@sub] — {s.subscriber_count} subscribers
[@sub]: https://www.moltbook.com/sub/{s.name}
end:
next: /open @sub-N to browse a community

The agent runs /open @sub-3 (or /open communities.md), and a plain markdown viewer renders the same line as a clickable link.

See cookbook/apps/moltbook for a complete reference: nav between pages uses markdown links (string.mdcommunities.md); drill-in into posts uses plain-text @post-N tokens.


  • Inline shortcut IDs MUST be unique within a document.
  • Menu shortcut IDs MUST be unique within a menu file.
  • Same IDs in different menus are allowed (namespacing prevents collision: @main.home vs @admin.home).
  • An inline shortcut MAY have the same ID as a menu shortcut. The inline shortcut takes priority during resolution.

When a shortcut reference like [@id] is resolved:

  1. Inline shortcuts — defined in the current document
  2. Auto-generated shortcuts — slugs, @slug-N duplicates, and @link-N fallbacks
  3. Menu shortcuts — namespaced entries from nav menus

If a reference includes a namespace (@main.home), it resolves directly to that menu. No priority lookup is needed.


  1. Inline form: [@id Label](url) — defines and displays in one line.
  2. Reference form: [Label][@id] + [@id]: url — separated.
  3. AI always sees: [Label][@id] — URL never in AI context.
  4. IDs: [a-z][a-z0-9_-]* — MUST NOT start with link-.
  5. Inline IDs MUST be unique within a document.
  6. Menu IDs MUST be unique within a menu file.
  7. Menu shortcuts are namespaced: @menu_name.id.
  8. Inline shortcuts use implicit page namespace.
  9. Auto-generated: external URLs and long paths only (short relative paths excluded).
  10. Slug from label if ≤ 20 chars, alphanumeric/space/hyphen only, and non-empty.
  11. Duplicate slugs: @slug-2, @slug-3 (per-slug counter).
  12. Non-sluggable fallback: @link-N (reserved prefix, global counter).
  13. Resolution order: inline > auto > menu.
  14. In CommonMark viewers, both forms render as standard links.