Skip to main content
OmniScript 2.0

Agents: MCP, A2A and skills

osf speaks the two open agent protocols. An MCP client can read, query, edit and render your documents; a document with an @agent_card can itself run as an A2A agent.

MCP server

osf serve mcp serves a directory (default: the current one) or a list of .osf files over stdio. With --listen 127.0.0.1:8091 it serves streamable HTTP instead.

osf serve mcp ~/reports                         # stdio, read-only
osf serve mcp ~/reports --write                 # osf.set may write files
osf serve mcp ~/reports --listen 127.0.0.1:8091 # streamable HTTP

Tools

ToolDoes
osf.parseCanonical JSON of a document (blocks, properties, content, spans) with diagnostics
osf.lintDiagnostics: syntax, block schemas, ids, dangling references
osf.formatCanonical formatting; comments and content are kept
osf.queryBlocks matching a selector
osf.getOne block by id: its JSON, its exact source, and which blocks reference it
osf.setReplace a block by id (or append it if the id is new); the rest of the file is kept byte for byte. Writes only with --write, otherwise returns the new text
osf.renderhtml, md, json, typst as text; pdf, docx, pptx, xlsx as a base64 resource
osf.diffSemantic diff of two documents
osf.viewShow the rendered document in the chat (MCP App)

Each served document is also an MCP resource at osf://<path>.

MCP App view

osf.view is linked to the UI resource ui://osf/viewer (text/html;profile=mcp-app). Hosts that support MCP Apps show the rendered document inside the conversation; other hosts get a text summary. Ask the agent to "show the report" after editing it.

Client configuration

Claude Desktop (claude_desktop_config.json) and other clients that use the mcpServers format:

claude_desktop_config.json
{
  "mcpServers": {
    "osf": {
      "command": "osf",
      "args": ["serve", "mcp", "/path/to/documents"]
    }
  }
}

VS Code (.vscode/mcp.json) uses a servers key:

.vscode/mcp.json
{
  "servers": {
    "osf": {
      "type": "stdio",
      "command": "osf",
      "args": ["serve", "mcp", "${workspaceFolder}"]
    }
  }
}

Claude Code:

claude mcp add osf -- osf serve mcp /path/to/documents

A2A agent

A document with an @agent_card can run as an A2A agent. The card is served at /.well-known/agent-card.json; osf card report.osf prints it.

report.osf
@agent_card {
  name: "Funding report";
  description: "Queryable report with live programme data";
  url: "https://reports.example.org/funding";
  skills: [
    { id: "osf.query"; name: "Query"; description: "Selectors over the report"; },
    { id: "osf.export"; name: "Export"; description: "PDF or Office export"; },
    { id: "osf.resolve_decision"; name: "Decide"; description: "Record an outcome"; }
  ];
  security: [{ type: "bearer"; }];
}
OSF_A2A_TOKEN=s3cret osf serve a2a report.osf --write
# A2A agent on http://127.0.0.1:8090 (card: /.well-known/agent-card.json)

Built-in skills

A built-in skill runs only if the card lists it.

SkillInputDoes
osf.query{ selector }Blocks matching a selector
osf.get{ id }One block
osf.export{ target, selector?, theme? }The document, or the blocks a selector picks, as a file artifact
osf.update{ id, block }Replace one block with new OSF source
osf.resolve_decision{ id, outcome, rationale?, decided_by? }Set outcome, status: decided, decided_by and decided_on
osf.run_task{ assignee, input: { skill, … }, id? }Create a @task, send it to another A2A agent and record the result

A message picks a skill with a data part such as { "skill": "osf.query", "selector": "decision[status=pending]" }, or with text that starts with the skill id (osf.query finding[confidence>=0.8]). Any other text is answered with a keyword search over the document, so the agent is useful without a language model behind it.

Tokens and writes

  • If the card declares security (bearer or API key), the server refuses to start without --token or OSF_A2A_TOKEN, and every request except reading the card must carry the token.
  • osf.update, osf.resolve_decision and osf.run_task change the document and need --write. Changes are appended to .osf-audit.log.
  • The server listens on 127.0.0.1:8090 unless you pass --listen; --url sets the public URL advertised in the card.

Data sources

A @data_source block calls an MCP tool and feeds the result into a sheet, table or chart via source: @ref("id").

@data_source {
  id: "funding-live";
  mcp: "funding-db";
  tool: "search_programmes";
  arguments: { query: "energy transition"; };
  refresh: "daily";
}

@table { id: "live"; source: @ref("funding-live"); caption: "Live programmes"; }
  • Fetching is off by default. --allow-net allows every server; --allow-net=funding-db allows only the named ones. The flag exists on render, eval and the serve commands.
  • Results are cached in .osf-cache/ next to the document. Without permission the last cached result is used.
  • Servers are looked up in --mcp-config <file>, then ./.osf/mcp.json, ./.mcp.json and ~/.config/osf/mcp.json, in the usual mcpServers format.
.osf/mcp.json
{
  "mcpServers": {
    "funding-db": { "command": "funding-mcp", "args": ["--readonly"] }
  }
}
osf render report.osf --target pdf --allow-net=funding-db

Agent Skill

The osf skill teaches agents that support Agent Skills (SKILL.md) to write OSF, check it with osf lint, query it and export it. It contains the syntax, the block reference, the selector language and the command list.

osf skill install                 # ~/.claude/skills/osf
osf skill install --for project   # ./.claude/skills/osf
osf skill install --for agents    # ~/.agents/skills/osf
osf skill install --for ./skills  # ./skills/osf (any directory)
osf skill show                    # print SKILL.md
Servers bind to localhost by default. Documents never run code: @code is text, and nothing is fetched unless you allow it.