Skip to main content
Glama

ArtifactSmith

ArtifactSmith is an open source artifact server for AI agents.

ArtifactSmith building the same page in five styles

Agents call it to turn their work into documents: web pages, PDFs, Word files, spreadsheets and Markdown. Each artifact is versioned and stays private until someone shares it, and a shared link can be revoked at any time.

It's for people who run their own agents and want artifacts like the ones in Muse on their own servers.

Try it before you install: https://artifactsmith.neuralami.ai/try. I run a copy you can point your MCP client at (email for a token, capped at 20 builds, runs on my server).

Examples, no sign-up: comparison page · status page with a chart · PDF checklist

What it does

ArtifactSmith gives agents tools to create, edit, export (PDF, Word, Excel, Markdown), share, and revoke documents. It works with any OpenAI-compatible model endpoint. The idea comes from Meta's Muse artifacts.

An example artifact: a one-page decision brief with a recommendation, a status scorecard and a bar chart

Requirements: Docker Engine with the Compose plugin. A model key (OpenAI-compatible API or local endpoint) is needed to build pages; publishing ready-made Markdown or HTML works without one.

Related MCP server: PasteAI

Quickstart

cp .env.example .env
# Set OPENAI_API_KEY (or AM_LLM_KEY). Change AM_LLM_BASE / AM_DEFAULT_MODEL
# only if you are not using the default OpenAI host and model.
# Object-store keys: leave them empty and run ./scripts/ensure-local-env.sh,
# or paste your own AM_STORE_KEY and AM_STORE_SECRET.
./scripts/ensure-local-env.sh
docker compose up -d --build

# Create a token (prints the raw value once; only its hash is stored).
# --workspace is a label that scopes every artifact the token can see
# (e.g. alpha, team-a). Tokens cannot cross workspaces.
docker compose exec server artifactsmith token add --name agent --workspace alpha

publish stores a page you already wrote and does not call the model, so the next step works when the key is empty. Paste the token printed above, then run this. It prints a share URL.

export AM_TOKEN='paste-the-token'
docker compose exec -T -e AM_TOKEN server python - <<'PY'
import asyncio
import json
import os
from datetime import timedelta

from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

URL = "http://127.0.0.1:8780/mcp"
PAGE = """# Pilot store brief

The pilot store code is HARBOR-17. It is the identifier used on inventory, support, and rollout reports.
"""


def parse(result):
    if result.structuredContent is not None:
        return result.structuredContent
    return json.loads(result.content[0].text)


async def main():
    headers = {"Authorization": "Bearer " + os.environ["AM_TOKEN"]}
    async with streamablehttp_client(URL, headers=headers) as (read, write, _session):
        async with ClientSession(read, write) as session:
            await session.initialize()
            created = parse(
                await session.call_tool(
                    "publish",
                    {"display_name": "Pilot store brief", "markdown": PAGE},
                    read_timeout_seconds=timedelta(seconds=120),
                )
            )
            done = parse(
                await session.call_tool(
                    "status",
                    {"job_id": created["job_id"], "wait": 90},
                    read_timeout_seconds=timedelta(seconds=120),
                )
            )
            if done.get("status") != "done":
                raise SystemExit(json.dumps(done))
            shared = parse(
                await session.call_tool(
                    "share",
                    {"artifact_id": created["artifact_id"]},
                    read_timeout_seconds=timedelta(seconds=120),
                )
            )
            print(shared["url"])


asyncio.run(main())
PY

The server reads AM_LLM_BASE, AM_LLM_KEY or OPENAI_API_KEY, and AM_DEFAULT_MODEL from .env. The default base is https://api.openai.com. A trailing /v1 on the base is fine; the client appends /v1/chat/completions itself. create needs a key or a local OpenAI-compatible endpoint. With an empty key, a failed create reports that OPENAI_API_KEY / AM_LLM_KEY is empty. Run docker compose up -d again so the change takes effect.

Host ports bind to 127.0.0.1:

  • http://127.0.0.1:8780/mcp is the MCP API (bearer token required)

  • http://127.0.0.1:8780/app/ is My pages (paste the same token; the browser gets a session cookie)

  • http://127.0.0.1:8781/ is the cookieless preview and share origin (/p/…, /s/…)

Use the published image

ghcr.io/vrijsinghani/artifactsmith:0.1.0 is published for linux/amd64 on each release. Building from source (the Compose steps above) stays the default.

Connect a client

Claude.ai: Customize → Connectors → + Add → Add custom connector. Paste https://artifactsmith.neuralami.ai/mcp, select Connect, and sign in with your email. The connector then works in the Claude desktop and phone apps too. Free plans allow one custom connector. On Team and Enterprise plans an owner adds it first.

github.com/vrijsinghani/Artifactsmith is the copy you run yourself. Claude Desktop, Claude Code, and Cursor use a token there. The MCP endpoint on this machine is http://127.0.0.1:8780/mcp. Send the token from artifactsmith token add as Authorization: Bearer YOUR_TOKEN. The header value starts with Bearer .

ChatGPT Free, Go, and Plus cannot add a custom MCP server. Pro can add one with read and fetch access only, so it cannot build pages. A ChatGPT Business, Enterprise, or Edu admin can add it on the web; it signs in with OAuth.

Claude Desktop stores the config in claude_desktop_config.json (Settings → Developer → Edit Config). The snippet runs npx and mcp-remote, which needs Node.js. Restart Claude Desktop after saving.

{
  "mcpServers": {
    "artifactsmith": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://127.0.0.1:8780/mcp", "--header", "Authorization:${AUTH_HEADER}"],
      "env": { "AUTH_HEADER": "Bearer YOUR_TOKEN" }
    }
  }
}

Claude Code stores .mcp.json, or run:

claude mcp add --transport http artifactsmith http://127.0.0.1:8780/mcp --header "Authorization: Bearer YOUR_TOKEN"
{
  "mcpServers": {
    "artifactsmith": {
      "type": "http",
      "url": "http://127.0.0.1:8780/mcp",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}

Cursor stores ~/.cursor/mcp.json, or .cursor/mcp.json in the project.

{
  "mcpServers": {
    "artifactsmith": {
      "url": "http://127.0.0.1:8780/mcp",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}

Example prompt: Create a one-page brief named Pilot store brief. Pass this as source_content: The pilot store code is HARBOR-17. It is the identifier used on inventory, support, and rollout reports.

artifactsmith stdio can list tools on stdin and stdout and does not listen. It cannot use the object store or the model, so it cannot build or publish a page.

Serving on your network or behind a proxy

Compose publishes API (8780) and preview/share (8781) on AM_BIND_ADDRESS (default 127.0.0.1). Cards and links use AM_API_URL, AM_PREVIEW_URL, and AM_SHARE_URL (empty AM_SHARE_URL falls back to AM_PREVIEW_URL). Exposing the ports without TLS puts bearer tokens and artifact content on the wire in cleartext.

LAN access

Bind on all interfaces (or a specific NIC IP) and point the public URL variables at the address clients will open:

# .env  (192.0.2.10 is documentation-range; substitute your LAN IP)
AM_BIND_ADDRESS=0.0.0.0
AM_API_URL=http://192.0.2.10:8780
AM_PREVIEW_URL=http://192.0.2.10:8781
AM_SHARE_URL=http://192.0.2.10:8781
AM_ALLOWED_HOSTS=192.0.2.10:8780,127.0.0.1:8780,localhost:8780

Then docker compose up -d --build. Clients open http://192.0.2.10:8780/mcp and preview/share links use 192.0.2.10, not 127.0.0.1. Browser-based MCP clients also need AM_ALLOWED_ORIGINS set to the page origin.

Share and preview URLs are built from AM_SHARE_URL / AM_PREVIEW_URL when the server returns them. Links you copied before switching to LAN serving still use the old base host; call share again (or copy the URL from inspect / status) after changing those settings.

Reverse proxy with TLS

Keep the host bind on loopback and terminate TLS on nginx or Caddy. Set the public URLs and allow-lists to the external hostname:

# .env
AM_BIND_ADDRESS=127.0.0.1
AM_API_URL=https://artifacts.example.com
AM_PREVIEW_URL=https://preview.artifacts.example.com
AM_SHARE_URL=https://preview.artifacts.example.com
AM_ALLOWED_HOSTS=artifacts.example.com
AM_ALLOWED_ORIGINS=https://artifacts.example.com

Minimal Caddy example (API on 443, preview on a second hostname):

artifacts.example.com {
  reverse_proxy 127.0.0.1:8780
}
preview.artifacts.example.com {
  reverse_proxy 127.0.0.1:8781
}

Minimal nginx snippet for the API host:

server {
  listen 443 ssl;
  server_name artifacts.example.com;
  # ssl_certificate / ssl_certificate_key omitted
  location / {
    proxy_pass http://127.0.0.1:8780;
    proxy_set_header Host $host;
    proxy_set_header Authorization $http_authorization;
    # status wait can be up to 90s; nginx defaults cut at 60s.
    proxy_read_timeout 120s;
    proxy_send_timeout 120s;
  }
}

When AM_ALLOWED_HOSTS lists only the public hostname, a local curl http://127.0.0.1:8780/mcp gets HTTP 421 (DNS-rebinding protection). Point clients at the proxied hostname instead. Browser-based MCP clients send an Origin header; set AM_ALLOWED_ORIGINS to that origin or the request is rejected.

MCP client on another machine

Point the client at {AM_API_URL}/mcp and send the token as Authorization: Bearer <token> (the value printed once by artifactsmith token add). The header value starts with Bearer . artifactsmith stdio can list tools on stdin and stdout and does not listen. It cannot use the object store or the model, so it cannot build or publish a page. Example:

URL:    http://192.0.2.10:8780/mcp
Header: Authorization: Bearer asmb_…

How it works

Agents talk to ArtifactSmith over MCP. They pass the user's request as verbatim_request and any research as source_content or source_files. create starts a private build and does not publish a share link. Want a styled page? Pass Markdown to publish and it is styled automatically (omit format when you pass markdown and no html; house style by default, applied when the page is opened). You can still pass format=markdown. Or call create with source_content. Raw HTML in publish is only for a page you designed yourself, and it is shown exactly as given.

The server calls an OpenAI-compatible endpoint (up to two attempts when checks fail). The model writes content. A fixed renderer turns that into HTML, Markdown, PDF, DOCX, or XLSX. publish skips the model and runs the same checks and renderer. Call status (or wait up to 90 seconds) to get a private preview.

share creates a public link for one exact version. unshare makes that link return 404. Tokens belong to one workspace and carry a permission set.

Documents may cite public http(s) sources as clickable links. The server strips scripts, rejects private-link hosts and likely secrets, and keeps images, CSS, fonts, and iframes self-contained so a download opens offline and does not phone home. Size limits apply. The process does not send usage data anywhere.

Output formats

Format

create(..., format=)

Primary file

Renderer

HTML

html (default)

index.html

sanitize, then store

Markdown

markdown

document.md

UTF-8 text

PDF

pdf

document.pdf

WeasyPrint (HTML/CSS to PDF)

DOCX

docx

document.docx

python-docx

XLSX

xlsx

document.xlsx

openpyxl

HTML uses a visual style (house by default) and stores a complete page. The other formats ask the model for Markdown, then a renderer converts it. publish accepts the finished HTML or Markdown and uses that same renderer. PDF, DOCX, and XLSX also store content.md so later edits have a text base.

PDF goes through WeasyPrint, which needs Pango and Cairo. The Docker image does not include Chromium. Details are in docs/formats.md.

Styles

create and edit take an optional style for HTML: house, bold, editorial, playful, terminal, or swiss. The default is AM_DEFAULT_STYLE (usually house). On markdown, PDF, DOCX, and XLSX the model prompt does not use that visual style. A published markdown page is still shown in the recorded style when it is opened. Omit style on edit to keep the last style.

create also takes mode: full (default, AM_BUILD_MODE) has the model write HTML. fast has the model write Markdown and the server render that style. fast is a first HTML build only. Edits and other formats stay on the full path. AM_FAST_STYLES (empty by default) lists styles whose omitted mode is fast; an explicit mode still wins. A complete HTML document in the source, or a request to publish that HTML as-is, stays on full when mode is omitted. Mentioning HTML beside "unchanged" does not.

  • house: today's quiet page — white, one accent, boldness in one place.

  • bold: dark navy-to-violet hero, huge white headline, saturated cards and chips.

Desktop and phone preview of the bold HTML style: a dark gradient hero with a huge headline, then colored step cards

  • editorial: magazine feature — off-white paper, large serif headline, thin rules, one deep red accent.

Desktop and phone preview of the editorial HTML style: a large serif headline, a red-ruled lead, and two columns

  • playful: cream page, candy-colored rounded cards, thick outlines and offset shadows.

Desktop and phone preview of the playful HTML style: a coral rounded banner, googly-eye accents, and mint chat cards

  • terminal: dark mode, monospace, neon green and cyan, sections as fake terminal windows.

Desktop and phone preview of the terminal HTML style: a green shell-prompt headline and dark windows with three title-bar dots

  • swiss: white grid, huge black grotesque type, signal-orange only on numbers and rules.

Desktop and phone preview of the swiss HTML style: a huge black headline, an orange rule, and oversized orange numerals

MCP tools

Tool

Permission

What it does

create

create

Queue a private build. Optional format, style, and mode. Does not create a share link.

publish

publish

Store Markdown to get a styled page, or store HTML you designed yourself. No model call.

edit

edit

New version from the latest done version. Refuses a stale base_version.

status

read

Poll, or wait (0 to 90 seconds), until done, failed, or needs_input.

list_artifacts

read

Catalog for the token's workspace.

inspect

read

Manifest, history, share state. Optional include_content returns the stored text of that version, capped at the publish character limit.

export

export

Download link for the rendered file, valid 15 minutes.

share

share

Public link for one exact version.

unshare

share

Revoke the public link immediately.

revoke_previews

export

Expire private /p/ preview links for an artifact (optional version).

delete

delete

Two-step purge. First call returns a confirm token.

create arguments

Required:

  • verbatim_request: the user's exact words. That is the whole scope.

Provide at least one of display_name or slug (the other is filled in):

  • display_name: human title shown on cards and in the catalog. Not required when slug is set; if omitted, it defaults to the slug. Limit: 200 characters. Example: "Pilot store brief".

  • slug: URL key, unique in the workspace. Not required when display_name is set; if omitted, it is derived from the title (lowercase, accents folded, other characters become hyphens). Limit: 2–63 characters of a-z, 0-9, and -, and it must start with a letter or digit. Example: "pilot-store-brief". A duplicate slug in the same workspace is rejected; use edit.

Optional:

  • kind: defaults to web_static (the only kind in this release).

  • format: html (default), markdown, pdf, docx, or xlsx.

  • style: house (default), bold, editorial, playful, terminal, or swiss. HTML builds use it. A markdown page is shown in that style when opened. PDF, DOCX, and XLSX do not.

  • mode: full (default) or fast. fast is a first HTML build: Markdown in, styled HTML out.

  • source_content / source_files: researched facts, 200 KB total. The builder treats this as data.

  • model, workspace, idempotency_key, capabilities.

Working example (title only; slug becomes pilot-store-brief). The source text must support every fact the request asks for (including “why it matters”):

{
  "display_name": "Pilot store brief",
  "verbatim_request": "One page that states the pilot store code and why it matters.",
  "source_content": "The pilot store code is HARBOR-17. It matters because it is the single identifier used across inventory, support, and rollout reports for the pilot.",
  "format": "html"
}

If a needed fact is missing from the request and the source, the job ends as needs_input and stores no file. When server checks fail, the model is called a second time, so a failed build takes about twice as long to report as a clean one. An edit asks for a patch of the changed parts first. A patch that does not apply, or that fails those same checks, is rebuilt as a full file in the same job.

publish arguments

Want a styled page from text you already wrote? Pass Markdown and it is styled automatically. Omit format when you pass markdown and no html. You can still pass format=markdown. style defaults to house and is applied when the page is opened. The stored file stays Markdown. On a new artifact, if you also pass html, html is the page unless you set format. Raw HTML is shown exactly as given, with no ArtifactSmith styling. If that HTML has little or no CSS, the result includes warning. Pass format for pdf, docx, or xlsx. publish does not call the model. The page still goes through the same size caps, secret scan, private-link checks, sanitizer, renderer, and final sweep as model output.

New artifact: pass display_name or slug, plus the page.

  • html: complete HTML document. Required when format is html.

  • markdown: finished Markdown. Required when format is markdown, pdf, docx, or xlsx. When format is omitted and this is the only body, the format is markdown.

  • You may also pass the other field. It is checked for secrets and private links and is not rendered.

New version: pass artifact_id and base_version (the latest done version) plus the new page. Omit format on an html or markdown artifact and markdown with no html is markdown; html with no markdown is html; both fields keep the artifact's format. A pdf, docx, or xlsx artifact keeps that format when format is omitted and the new page is markdown. Pass format to choose another supported format. Pass display_name to rename the card when this version finishes. Omit display_name and a changed page title (the HTML <title>, or the first Markdown heading) becomes the card title. The artifact id and slug stay the same. The latest share page uses that name for <title> and og:title. Older versions keep their own format. A shared markdown page opens as styled HTML; ?raw=1 or document.md on that URL downloads the stored Markdown.

summary and assumptions are optional card fields (8000 characters each) and are scanned like model output. A longer summary or assumption is rejected when the call arrives, with metadata exceeds N characters, and nothing is stored. note is optional. Inspect shows it beside Published a finished page. A note that is too long, or that fails the secret, private-link, or allow-list check, is rejected before a version row exists. On markdown, style changes the opened page (house by default). On HTML, style is recorded and the submitted page is not restyled. PDF, DOCX, and XLSX do not use the HTML style sheet.

Returns artifact_id, version, and job_id. Call status until done or failed. A rejected page is failed, and error names the check that failed. When the job is done, status and inspect include safety_changes, the list of safety edits that changed the submitted page. HTML publish does not turn a bare URL into a link. publish does not create a share link. It counts toward the per-token build quota and needs the publish permission.

export

Permission: export.

Arguments:

  • artifact_id (required): which artifact to download.

  • version (optional): a done version. Omit it to use the latest done version.

Returns download_url (valid 15 minutes), file, version, sha256, self_contained, and expires_in ("15m").

{ "artifact_id": "art_example", "version": 1 }

share

Permission: share.

Arguments:

  • artifact_id (required): which artifact to publish.

  • version (optional): the exact done version. Omit it to use the latest done version.

  • ttl_days (optional): lifetime in days. Defaults to AM_SHARE_TTL_DAYS. When AM_SHARE_TTL_DAYS is 0, ttl_days is ignored and the link lasts until revoked. Otherwise the lifetime is capped by AM_SHARE_TTL_MAX_DAYS.

Returns state ("shared"), url, version, expires_at, lifetime, and reused. Anyone with the URL can open that version.

{ "artifact_id": "art_example" }

unshare

Permission: share.

Arguments:

  • artifact_id (required): which artifact to revoke.

  • version (optional): revoke one version. Omit it, or pass 0, to revoke every version.

Returns artifact_id, revoked_links (how many links were revoked), and state. state is "shared" when a public link is still live, with url and follows ("latest" or "pinned"). It is "not_shared" only when no public link remains. Revoking one pin leaves the follow-latest link up. A revoked URL returns 404 immediately.

{ "artifact_id": "art_example" }

delete

Permission: delete.

Arguments:

  • artifact_id (required): which artifact to purge.

  • confirm_token (omit on the first call): token from the first response. It expires in 10 minutes.

The first call does not delete anything. It returns step ("confirm"), confirm_token, title, versions, expires_in ("10m"), and a warning. The second call purges every stored version, share link, and database row. It returns deleted (true), artifact_id, and purged_object_versions.

{ "artifact_id": "art_example" }
{ "artifact_id": "art_example", "confirm_token": "paste-the-token" }

Configuration

Every setting also has a NAME_FILE variant that reads the value from a file.

Variable

Default

Purpose

OPENAI_API_KEY / AM_LLM_KEY

empty

LLM credential for the configured endpoint.

AM_LLM_API

chat

chat (Chat Completions) or responses.

AM_LLM_BASE

https://api.openai.com

OpenAI-compatible host root. With or without a trailing /v1.

AM_LLM_TEMPERATURE

unset

Chat Completions temperature. Unset sends 0; none omits the field.

AM_DEFAULT_MODEL

gpt-4o-mini

Model name sent to the LLM.

AM_DEFAULT_STYLE

house

HTML visual style when create omits style.

AM_BUILD_MODE

full

full or fast when create omits mode. fast is first-build HTML only.

AM_FAST_STYLES

empty

Comma-separated styles (house, bold, …) that default to fast when create omits mode. Empty keeps AM_BUILD_MODE for every style. An explicit mode still wins. A finished HTML source, or a request to publish that HTML as-is, stays full when mode is omitted. Mentioning HTML beside "unchanged" does not. Other formats and edits stay full. An unknown name fails at startup.

AM_SHARE_TTL_DAYS

30

0 means until revoked.

AM_SHARE_TTL_MAX_DAYS

365

Cap on positive TTLs.

AM_STORE_ENDPOINT

(required)

S3-compatible object store.

AM_STORE_BUCKET

artifacts

Bucket name.

AM_STORE_KEY / AM_STORE_SECRET

(required)

Object-store credentials.

AM_BLOCK_PRIVATE_LINKS

true

Reject localhost, RFC1918, and link-local hosts in output.

AM_ALLOWED_LINK_DOMAINS

empty

If set, every remaining host must match.

AM_MAX_OUTPUT_BYTES

50 MiB

Rendered output cap.

AM_RENDER_TIMEOUT

120

Seconds for one renderer call.

AM_BUILD_TIMEOUT

900

Seconds for the whole job.

AM_EDIT_PATCH_TIMEOUT_S

45

Seconds for the edit patch call. A timeout falls back to a full rewrite.

AM_MAX_BUILDS

2

Concurrent workers. A hosted trial uses at most one of these slots.

AM_BUILDS_PER_HOUR

20

Per-token quota for internal tokens. Trial tokens use AM_TRIAL_BUILDS_PER_HOUR.

AM_TRIAL_ENABLED

false

Open the hosted try-it page (/try, old /trial) and allow its builds. false closes both immediately.

AM_TRIAL_EMAIL_VERIFY

true

Email a one-time link before showing the token. false shows it on submit and records the address as unverified.

AM_TRIAL_BUILD_LIMIT

20

Lifetime builds for one trial token.

AM_TRIAL_FREE_NEEDS_INPUT

5

needs_input jobs per hosted token that do not use a build. Further ones do.

AM_TRIAL_BUILDS_PER_HOUR

10

Hourly limit for a trial token. Does not change AM_BUILDS_PER_HOUR.

AM_TRIAL_DAILY_USD

10

Global trial spend cap per UTC day. Internal tokens are not capped.

AM_TRIAL_PRICE_IN / AM_TRIAL_PRICE_OUT

0.10 / 0.50

USD per 1M input / output tokens, used to price the hold.

AM_TRIAL_MODEL

anthropic/claude-haiku-5-5

Model for every trial build. The caller's model name is ignored.

AM_TRIAL_MAX_OUTPUT_TOKENS

16000

Output cap sent on trial model calls.

AM_TRIAL_SIGNUPS_PER_IP / AM_TRIAL_SIGNUP_WINDOW_S

3 / 86400

Sign-up attempts per IPv4 address or IPv6 /48 per window.

AM_TRIAL_SIGNUPS_PER_DAY

300

Minted sign-ups per UTC day. After that, /try says no more hosted tokens today, back at 00:00 UTC.

AM_TRIAL_LINK_TTL_S

1800

Magic-link lifetime, in seconds.

AM_TRIAL_RETENTION_DAYS

30

Delete a trial workspace this many days after its last build.

AM_TRIAL_ABUSE_EMAIL

empty

Mailto on the trial page footer. Empty omits the link.

AM_SMTP_HOST / AM_SMTP_PORT / AM_SMTP_FROM

empty / 587 / empty

Magic-link mail. Empty host or from fails closed.

AM_SMTP_USER / AM_SMTP_PASSWORD

empty

SMTP login. Password accepts AM_SMTP_PASSWORD_FILE.

AM_SMTP_TLS

true

Start TLS when sending the magic link.

AM_HOST

127.0.0.1

Listen address for artifactsmith serve. Compose/image set 0.0.0.0 in-container.

AM_BIND_ADDRESS

127.0.0.1

Host publish bind for ports 8780 and 8781 in compose.yaml.

AM_API_PORT / AM_PREVIEW_PORT

8780 / 8781

Listen ports.

AM_API_URL / AM_PREVIEW_URL / AM_SHARE_URL

http://127.0.0.1:8780 / :8781

URLs written into cards and links. Empty AM_SHARE_URL uses AM_PREVIEW_URL. An https:// API URL marks the My pages cookie Secure.

AM_COOKIE_SECURE

follows AM_API_URL

true or false forces the My pages cookie Secure flag. Leave it unset on local HTTP.

AM_TRUSTED_PROXIES

empty

Comma-separated IPs or CIDRs that may set X-Real-IP or X-Forwarded-For for My pages login limits and trial sign-up limits. Empty ignores those headers.

AM_LOGIN_LIMIT / AM_LOGIN_PREFIX_LIMIT / AM_LOGIN_WINDOW_S

8 / 8 / 900

Failed sign-ins per IPv4 address or IPv6 /64, and per hashed token prefix, inside the window (seconds).

AM_ALLOWED_HOSTS

127.0.0.1:<api_port>,localhost:<api_port>

Comma-separated Host values for MCP DNS-rebinding protection (on by default).

AM_ALLOWED_ORIGINS

empty

Comma-separated Origin values for MCP DNS-rebinding protection.

AM_DATA_DIR / AM_SECRETS_DIR

/data / /secrets

SQLite and the HMAC signing key.

Copy .env.example for a local file. ./scripts/ensure-local-env.sh fills empty AM_STORE_KEY and AM_STORE_SECRET. The storage image is rustfs/rustfs:1.0.1 (pinned by digest in compose.yaml).

Running a hosted try-it page

Set AM_TRIAL_ENABLED=true and point AM_API_URL at the public origin. With AM_TRIAL_EMAIL_VERIFY left at true, /try emails a one-time link (AM_SMTP_HOST and AM_SMTP_FROM are required; if either is empty, sign-up fails closed). GET /trial redirects to /try. POST /trial and /trial/verify stay so an open form or a link already sent still works. The Message-ID domain is the domain of AM_SMTP_FROM. Opening the link does not spend it. The "Show my token" button shows the workspace token once. Set AM_TRIAL_EMAIL_VERIFY=false to show that same page on submit and send no mail; the sign-up row stores verified=0. Either way the page includes config for Claude Desktop (mcp-remote), Claude Code (HTTP), Cursor, and a generic MCP client. The Desktop snippet puts the bearer value in AUTH_HEADER so a space is not an argument. Cursor uses a URL and headers, with no command arguments. Each snippet says YOUR_TOKEN. The page also names Claude Desktop's Customize → Connectors → Add custom connector route, with that same public MCP URL and an Authorization request header. The header value starts with Bearer. It names claude_desktop_config.json, .mcp.json, and ~/.cursor/mcp.json, the claude mcp add command for that public MCP URL, and one example prompt that passes notes as source_content. The page states the build cap from AM_TRIAL_BUILD_LIMIT. When AM_API_URL is a public https origin, that same page carries an Open Graph and Twitter card for {AM_API_URL}/try. The image is {AM_API_URL}/static/og-trial.png and needs no token. A loopback or private AM_API_URL omits the tags. The page stays noindex. One normalized address gets one token. Gmail addresses ignore dots and +tags. Disposable domains are rejected. Sign-up attempts from one IPv6 network share a /48. My pages login stays a /64. At most AM_TRIAL_SIGNUPS_PER_DAY tokens are minted per UTC day (default 300). After that, /try says no more hosted tokens today, back at 00:00 UTC.

Hosted builds always use AM_TRIAL_MODEL and cannot call publish. A hosted token's tool list omits publish. Responses include builds_remaining. Up to AM_TRIAL_FREE_NEEDS_INPUT (default 5) needs_input jobs per token do not use a build; after that they do. A failed job still does. The daily spend cap still counts that job's model tokens, and the hourly limit still counts the attempt. Deleting an artifact does not return those builds. When the daily spend cap is reached, the error is the hosted copy hit today's limit, back at 00:00 UTC. A used-up token points at the GitHub repo. Share links for a hosted token last at most 30 days. The served page adds noindex even when the page already has a robots meta that does not say noindex, and a footer that links the repo and stays visible if the page hides footers. Idle hosted workspaces are deleted after AM_TRIAL_RETENTION_DAYS (default 30). That sweep runs at startup and then hourly.

Security

Tokens are issued per workspace and stored as SHA-256 hashes. Knowing an artifact id does not grant access.

Permissions are create, read, edit, publish, export, share, and delete.

Builds stay private until you call share. That creates a public /s/… URL that follows the latest done version. Pass version or pin to freeze one version on its own URL. unshare makes it return 404. unshare with a version revokes only that pin.

Preview and share pages run on their own port, send no cookies, and set CSP script-src 'none' with sandbox allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation.

My pages (/app/ on the API origin) is a read-only catalog for the token's workspace. Sign-in pastes a token into a form. The response sets an HttpOnly, SameSite=Strict session cookie on path /app and does not put the token in the URL. The cookie is Secure when AM_API_URL is https (or when AM_COOKIE_SECURE=true). Each request loads the token again, so a revoke drops the session immediately. Another workspace's artifact id is a 404. The list shows 50 pages at a time. Times use America/New_York unless the viewer sets another IANA zone; the page names that zone. Behind a reverse proxy, set AM_TRUSTED_PROXIES to that proxy so a failed sign-in is counted for the client (X-Real-IP, or the rightmost X-Forwarded-For hop) and for a hash of the token prefix. IPv6 clients share a /64. Each IPv4 address is its own bucket, including an IPv4-mapped IPv6 address. Other peers cannot supply those headers. The preview origin does not serve /app/.

Scripts are stripped. Public http(s) links to global hosts are kept (with rel="noopener noreferrer nofollow" and target="_blank" in HTML). In-page # anchors stay on the page. Host checks use urllib plus a WHATWG-style approximation; they guarantee accepted links do not reach private hosts, not that every accepted link is browser-valid. Protocol-relative //host citations are upgraded to https:// only when the host is public; private or dangerous // destinations are neutralized, not upgraded. Private, loopback, and link-local hosts fail the build, as do javascript:, vbscript:, data:, and file:. A remote image URL becomes a clickable link to that URL (alt text as the label) in every format — nothing loads the image on open. CSS url(), fonts, and iframes stay local. Secrets fail the build. Size and time caps apply.

The process does not send usage data anywhere.

docs/threat-model.md and SECURITY.md have the rest.

Tokens

A workspace is a namespace for artifacts (for example alpha or team-a). Each token belongs to exactly one workspace and can only create or read artifacts in that workspace. Knowing an artifact id from another workspace does not grant access.

artifactsmith token add --name NAME --workspace WS [--perms create,read,edit,publish,export,share,delete]
artifactsmith token list
artifactsmith token revoke --id ID | --name NAME
artifactsmith token perms --id ID | --name NAME
artifactsmith token perms --name NAME --add publish
artifactsmith token perms --name NAME --remove publish

token perms changes one permission on an existing token. The token value stays the same. Each add or remove appends one line to the audit log. Listing permissions does not. If the name matches more than one token, pass --id.

Uninstall / reset

# Stop containers, delete compose volumes, and remove images created for this project
# (server, storage-init, and the rustfs/rustfs pull). Requires store keys in .env like other compose commands.
docker compose down --volumes --rmi all

Copy .env.example again and re-run the quickstart to start clean.

Development

python3 -m venv .venv && source .venv/bin/activate
make install
make ci          # lint, types, unit tests, denylist

Running the tests

make test runs the unit suite with the same pytest command CI uses. It does not start Docker.

End-to-end tests use compose.test.yaml, which adds a fake chat model from tests/support/.

./scripts/ensure-local-env.sh
docker compose -f compose.yaml -f compose.test.yaml up -d --build
python -m tests.e2e.test_end_to_end
python -m tests.e2e.test_formats
python -m tests.e2e.test_bind_address   # AM_BIND_ADDRESS=0.0.0.0 via non-loopback IP

make smoke runs the same stack and scripts. Compose commands need a .env with AM_STORE_KEY and AM_STORE_SECRET set (including docker compose down), because those variables are required by compose.yaml.

docs/architecture.md, docs/operations.md, CONTRIBUTING.md.

How this was built

Artifactsmith was vibe-coded. A person set the direction and made the calls, and AI agents wrote and checked the code:

  • Grok Bot ran the project. It planned the work, reviewed every pull request file by file, and decided what had to be fixed before anything merged.

  • Cursor cloud agents wrote the code, tests and docs, and fixed what the reviews found.

  • Hermes agents gave independent checks. One reviewed the code as a senior engineer and signed off on each round. Another installed it from scratch, following only this README, the way a new user would.

Before release it went through:

  • Several rounds of line-by-line code review, each one repeated until no blocking issues were left.

  • An independent security audit with Semgrep, Bandit, gitleaks, pip-audit and Trivy, plus a written threat model.

  • Hands-on attack testing of the HTML cleaner, link handling and export formats.

  • Real builds with a live model in all five formats, checking that links stay clickable and nothing loads from outside when a document is opened.

  • Clean installs on a separate machine, including serving it to other machines on a local network.

  • Automated checks on every change: lint, strict type checking, unit tests on Python 3.11 and 3.12, a Docker build, dependency scanning, and secret scanning.

The audit report is in docs/security-audit-2026-10-09.md. If you find something we missed, see SECURITY.md.

License

MIT. House-style writing and visual rules are adapted from Humanizer (MIT) and Anthropic frontend-design (Apache-2.0). Attribution is in NOTICE; third-party license texts are under licenses/. The Docker image ships those files at /app.

Available Tools

11 tools
createAInspect

Queue a new artifact. Returns artifact_id, version, and job_id immediately. Then call status(job_id, wait=90).

Want a styled page from text you already wrote? Publish that Markdown. It is styled automatically
when format is omitted and no html is passed (format=markdown; style defaults to house).
Want the server to write the page? Pass source_content here.
Raw HTML in publish is shown exactly as given, with no ArtifactSmith styling.

Required: verbatim_request (the user's exact words; the whole scope).

display_name is the human title on cards (optional if slug is set; defaults to the slug;
at most 200 characters; example "Pilot store brief"). slug is the workspace URL key
(optional if display_name is set; otherwise derived from the title; 2-63 chars of a-z, 0-9,
and hyphen, starting with a letter or digit; example "pilot-store-brief"). Provide at least one.
kind defaults to web_static (the only kind in this release).

format is html (default), markdown, pdf, docx, or xlsx. The model writes content. A fixed renderer
writes the bytes. html uses the visual style (house, bold, editorial, playful, terminal, swiss;
default AM_DEFAULT_STYLE, usually house). On markdown, pdf, docx, and xlsx the model prompt does
not use that visual style. A markdown page is still shown in the recorded style when it is opened.

mode is fast or full (default AM_BUILD_MODE, usually full). When mode is omitted and the
style is listed in AM_FAST_STYLES, the default is fast instead. fast applies to a first HTML
build: the model writes Markdown and the server renders it. full is the HTML build.
Other formats ignore fast. An explicit mode still wins. A complete HTML source, or a request
to publish that HTML as-is, stays full when mode is omitted. Mentioning HTML beside "unchanged" does not.

source_content (optional, up to 200 KB total with source_files) is researched material the page
is built from. The builder uses only the request plus this material for facts.

source_files (optional) is a list of {name, content} objects under the same 200 KB cap.

If a needed fact is missing, the job ends as needs_input with a short missing message and stores
no file. Call create again with the same slug after you supply the data.

create does not create a share link. When checks fail, the server retries the model once, so a
failed build takes about twice as long to report as a clean one.
ParametersJSON Schema
NameRequiredDescriptionDefault
kindNoweb_static
modeNoBuild mode for a first HTML page: fast or full. fast writes Markdown and the server renders the style. full is the HTML build. Default is AM_BUILD_MODE (full), or fast when the style is listed in AM_FAST_STYLES. A complete HTML source, or a request to publish that HTML as-is, stays full when mode is omitted. Mentioning HTML next to unchanged does not. Other formats ignore fast.
slugNoURL key unique in the workspace. Optional if display_name is set (then derived from the title: lowercase, accents folded, other characters become hyphens). 2-63 characters of a-z, 0-9, and hyphen; must start with a letter or digit. Example: "pilot-store-brief". Provide display_name or slug (at least one).
modelNo
styleNo
formatNo
workspaceNo
capabilitiesNo
display_nameNoHuman title shown on cards and in the catalog. Optional if slug is set (then it defaults to the slug). At most 200 characters. Example: "Pilot store brief". Provide display_name or slug (at least one).
source_filesNo
source_contentNo
idempotency_keyNo
verbatim_requestYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden and does well: it discloses the async pattern (job_id returned immediately), the needs_input failure mode that stores no file, the retry-once-on-check-failure penalty ("a failed build takes about twice as long to report"), the 200 KB cap, and the absence of a share link. Gaps remain around idempotency_key behavior, workspace scoping, and auth/permission requirements, which are behavioral concerns an agent may need.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The first sentence is correctly front-loaded, but the middle paragraphs digress into Markdown-vs-HTML and publish-vs-create asides, and the mode explanation largely restates the schema's own mode description verbatim. Sentences like "Mentioning HTML beside 'unchanged' does not" are edge-case clutter that cost more than they earn.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 13-parameter, async, job-producing tool this is a solidly complete brief covering defaults, format/mode interactions, failure handling, and the status follow-up; the output schema exists so return-shape explanation is not required. It falls short only on the undocumented idempotency_key/workspace/capabilities parameters, which an agent invoking repeatedly would want clarified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is only 23%, so the description must compensate, and it does for most high-traffic parameters: verbatim_request (the user's exact words, the whole scope), slug and display_name (formats, mutual fallback, examples), kind, format, mode, source_content, and source_files. It leaves model, workspace, capabilities, and idempotency_key entirely unexplained, which keeps it short of a 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence gives a specific verb and resource ("Queue a new artifact") plus the immediate return values, and later sentences explicitly separate create from publish ("Raw HTML in publish is shown exactly as given, with no ArtifactSmith styling") and from share ("create does not create a share link"). An agent can place this tool against its siblings without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It routes the agent through concrete decision points: use Markdown when the user already wrote text, pass source_content when the server should write the page, use publish when HTML must be shown verbatim. It also prescribes the follow-up call ("Then call status(job_id, wait=90)") and the recovery path after a needs_input failure ("Call create again with the same slug").

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

deleteAInspect

Two-step purge of an artifact. Warn the user before the second call.

Permission: delete.
Arguments: artifact_id (required), confirm_token (optional until the second call).
Returns: first call is step "confirm" with confirm_token, title, versions, expires_in "10m", and a warning.
Second call returns deleted true, artifact_id, and purged_object_versions.
Example: delete(artifact_id="art_example") then delete(artifact_id="art_example", confirm_token="...")
ParametersJSON Schema
NameRequiredDescriptionDefault
artifact_idYesArtifact to purge. Required.
confirm_tokenNoConfirmation from the first delete call. Optional on the first call (returns a token valid for 10 minutes). Required on the second call to purge. Example: "AbCdEf...".

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses the irreversible purge, the required delete permission, the 10-minute token expiry, and the returned warning. It stops short of explicitly stating permanence/irreversibility or any rate limits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the two-step nature, then uses short labeled sections (Permission, Arguments, Returns, Example) with zero filler. Every line contributes operational detail.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive, annotation-free tool this covers the essentials an agent needs: confirmation flow, permission, token lifetime, and response shapes. Explaining returns is mild redundancy given the output schema, but nothing required is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the schema already documents confirm_token's first-call-optional/second-call-required semantics and the 10-minute validity. The description restates that flow rather than adding meaning, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Two-step purge of an artifact') and immediately conveys the distinguishing mechanic (two-call confirmation). An agent can tell it apart from edit, revoke_previews, or unshare without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear procedural guidance: 'Warn the user before the second call,' and the example shows the exact call sequence. It does not, however, contrast this tool with siblings like revoke_previews or edit for partially-destructive scenarios, so it falls short of explicit alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

editAInspect

Build a new version from base_version (the latest done version) using the user's exact change request. Returns a conflict if base_version is stale. Inspect, then edit from the latest.

source_content and source_files (optional, 200 KB total) replace the base version's source.
If omitted, the base version's source is reused.

style (optional) is house, bold, editorial, playful, terminal, or swiss. Omit it to keep the
style the artifact was last built with. It applies to HTML only; other formats accept it and
ignore it.

format (optional) is html, markdown, pdf, docx, or xlsx. Omit it to keep the artifact's current
format. A different format builds this version in that format. When the version is done, the
artifact's format updates. The artifact id and slug stay the same. Older versions keep the format
they were built with.

display_name (optional) is the card title. When this version finishes, that name replaces the
artifact's display_name. Omit it and a changed page title (the HTML title, or the first Markdown
heading) becomes the card title instead. The slug stays the same.

Ends as needs_input with a missing message if the change needs facts that were not supplied.
ParametersJSON Schema
NameRequiredDescriptionDefault
modelNo
styleNo
formatNo
workspaceNo
artifact_idYes
base_versionYes
display_nameNo
source_filesNo
source_contentNo
idempotency_keyNo
verbatim_requestYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full load and does so well: it discloses the stale-base conflict, that source is replaced only when supplied, that style is HTML-only and silently ignored elsewhere, that format changes propagate to the artifact while old versions keep their format, and that it can end as needs_input. It omits idempotency_key, model, and workspace semantics.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and the stale-base caveat are front-loaded, then parameters are handled in short labeled blocks. It is on the long side but nearly every sentence adds behavior an agent could not infer from the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For an 11-parameter mutation tool with an output schema present, the description supplies the behavioral context an agent needs (conflict, override semantics, format propagation, needs_input terminal state). The gaps are the undocumented model/workspace/idempotency_key parameters, which matter less for correctness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate and largely does: it explains base_version, source_content/source_files (with the 200 KB cap), the style enum values, the format enum values, display_name fallback behavior, and the verbatim_request intent. It leaves artifact_id, model, workspace, and idempotency_key undefined, so it falls short of full coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence states a specific verb and resource: 'Build a new version from base_version ... using the user's exact change request.' It distinguishes the operation from the sibling 'create' by anchoring on a base version, though it never names create explicitly to draw the contrast.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Returns a conflict if base_version is stale. Inspect, then edit from the latest' gives a clear conditional workflow that routes the agent to the sibling 'inspect' tool before calling edit. It stops short of stating when-not to use edit or naming other alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

exportAInspect

Download link for one rendered file (html, md, pdf, docx, or xlsx).

Permission: export.
Arguments: artifact_id (required), version (optional; latest done version).
Returns: download_url (valid 15 minutes), file, version, sha256, self_contained, expires_in.
Example: export(artifact_id="art_example", version=1)
ParametersJSON Schema
NameRequiredDescriptionDefault
versionNoDone version to download. Optional; defaults to the latest done version.
artifact_idYesArtifact to download. Required.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses the required 'export' permission and that the returned download_url is valid for only 15 minutes (reinforced by expires_in). It stops short of describing failure behavior when the version is not done or whether repeat calls invalidate prior links.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core purpose, then cleanly sectioned into Permission/Arguments/Returns/Example. The Returns list largely duplicates the output schema, which is slight redundancy, but the 15-minute validity note earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter read-style tool with an output schema present, the description supplies the key extras an agent needs: required permission, default version behavior, and link expiration. Only error/failure-mode context is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so both parameters are already documented with the same semantics the description restates (artifact_id required; version optional, defaults to latest done version). The description adds no syntax, format, or edge-case detail beyond the schema, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource: producing a download link for one rendered file, and enumerates the supported output formats (html, md, pdf, docx, xlsx). This is clearly distinguishable from siblings like create, edit, publish, and share.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by 'Download link for one rendered file' and the permission note, but the description never states when to use export versus publish/share or any preconditions (e.g. version must be a 'done' version). No explicit exclusions or alternatives are given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

inspectAInspect

Manifest, files, build log, request history, and share state. Defaults to the latest done version.

include_content is optional and defaults to false. When true, the target done version adds
content with file, size_bytes, and text when that text is within the publish character cap.
PDF, DOCX, and XLSX return content.md and set primary_binary; they do not return the binary.
Over the cap, size_bytes is set and text is omitted. A store failure sets content_error to
store_read_failed. The call still requires read permission on this workspace.
ParametersJSON Schema
NameRequiredDescriptionDefault
versionNo
artifact_idYes
include_contentNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden and does so well: it discloses the include_content defaults, per-format handling (PDF/DOCX/XLSX returning content.md and setting primary_binary), the character-cap fallback (size_bytes set, text omitted), the failure mode (content_error=store_read_failed), and the read-permission requirement. This is exactly the extra context annotations would otherwise have to supply.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with what is returned before moving into parameter behavior, and each sentence carries information. It is somewhat dense in the middle section but contains no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read tool with an output schema, the description covers the non-obvious behaviors an agent needs: version defaulting, content-cap and format handling, error state, and permission prerequisites. The main gap is that artifact_id, the sole required parameter, receives no explanation anywhere.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It fully explains include_content (default false, what true adds, edge cases) and partially explains version via the 'latest done version' default, but artifact_id is never described. Partial compensation against three undocumented parameters justifies a middling score.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names the concrete payload the tool returns (manifest, files, build log, request history, share state) and states the default version target, so an agent knows this is an artifact-inspection read. It does not explicitly contrast itself with siblings like status or list_artifacts, which would have earned a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no when-to-use / when-not-to-use guidance and no alternative tool is named. The only contextual hint is 'Defaults to the latest done version,' which describes behavior rather than selection criteria against status, list_artifacts, or export.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_artifactsAInspect

Catalog of artifacts in your workspace, newest first. Optional kind filter and text query on slug or title.

ParametersJSON Schema
NameRequiredDescriptionDefault
kindNo
limitNo
queryNo
workspaceNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden, and it does disclose one real behavioral trait: results are returned newest first. However it says nothing about pagination, the default limit, workspace scoping behavior, or permissions – meaningful omissions for a list tool with a limit parameter.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences with zero filler; the core action and ordering are front-loaded, and the filter capabilities follow. Every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return values need no explanation, and the ordering plus two filter semantics are covered. But for a four-parameter tool with no annotations, the silence on limit/workspace semantics and pagination leaves the definition only partially complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It adds real meaning for two of four parameters (kind as a filter, query as text search over slug or title), but limit and workspace are entirely undocumented in both schema and description, leaving half the parameters opaque.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific read action (catalog artifacts) on a named resource (artifacts in your workspace) and adds the ordering (newest first), so an agent can immediately tell it apart from write-oriented siblings like create/edit/publish. It stops short of explicitly naming an alternative, but the purpose is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: the mention of an 'optional kind filter and text query' hints at when to narrow results, but there is no explicit when-to-use, when-not-to-use, or pointer to siblings such as inspect or status. Adequate but with clear gaps.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

publishAInspect

Store a page you have already finished. Use this when you already have the finished page. No model call. Returns artifact_id, version, and job_id immediately. Then call status(job_id, wait=90). When the job is done, status and inspect list safety_changes for each safety edit that changed the page. HTML publish does not turn bare URLs into links. The document title is stored as given and escaped in the page. note is optional. Inspect shows a non-empty note beside "Published a finished page." It is not the text a later edit sends to the model. A secret, private link, or over-long note is rejected before a row exists.

Want a styled page? Pass Markdown and it is styled automatically. Omit format when you pass
markdown and no html. style is house, bold, editorial, playful, terminal, or swiss (default house)
and is applied when the page is opened. The stored file stays Markdown. You can still pass
format=markdown. On a new artifact, if you also pass html, html is the page unless you set format.
Raw HTML is shown
exactly as given, with no ArtifactSmith styling. If that HTML has little or no CSS, the result
includes warning. Pass format for pdf, docx, or xlsx.

html is the complete HTML document when format is html. markdown is the finished Markdown when
format is markdown, pdf, docx, or xlsx. You may pass the other field as well: it is checked for
secrets and private links and is not rendered. PDF, DOCX, and XLSX do not use the HTML style sheet.

New artifact: pass display_name or slug (same rules as create). New version of an existing artifact:
pass artifact_id and base_version (the latest done version). Omit format on an html or markdown
artifact and markdown with no html is markdown; html with no markdown is html; both fields keep
the artifact's format. A pdf, docx, or xlsx artifact keeps that format when format is omitted and
the new page is markdown. Pass format to choose html, markdown, pdf, docx, or xlsx. Pass display_name
to rename the card when this version finishes. Omit display_name and a changed page title (the HTML
title, or the first Markdown heading) becomes the card title. The artifact id and slug stay the same.
Older versions keep the format they were published with.

The page runs the same safety pipeline as model output: size caps, secret scan, private-link checks,
the sanitizer, the renderer for format (including PDF, DOCX, and XLSX), and the final sweep. A rejected
page ends as failed, and error names the check that failed. publish does not create a share link.
It counts toward the per-token build quota and requires the publish permission.

summary and assumptions are optional card fields. Each is rejected when the call arrives if it is
longer than the metadata cap (the same 'metadata exceeds N characters' error the job would report).
They are also scanned like model output. On markdown, style changes the opened page. On HTML, style
is recorded and the submitted page is not restyled.
ParametersJSON Schema
NameRequiredDescriptionDefault
htmlNo
noteNo
slugNo
styleNo
formatNo
summaryNo
markdownNo
workspaceNo
artifact_idNo
assumptionsNo
base_versionNo
display_nameNo
idempotency_keyNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does so thoroughly: the complete safety pipeline (size caps, secret scan, private-link checks, sanitizer, renderer, final sweep), rejected pages ending as failed with the failing check in error, quota and permission requirements, that no share link is created, and the note/summary/assumptions rejection and scanning behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose and return values are correctly front-loaded, but the opening two sentences are redundant and the middle is a dense, unevenly chunked run of paragraph fragments ('Want a styled page?', format-omission rules, style-on-HTML rules) that takes real effort to parse. Length is partly justified by 13 parameters, but the organization costs readability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 13-parameter, annotation-free mutation tool this is close to complete: format/style interaction rules, new-vs-new-version requirements, permission/quota/failure behavior, and the follow-up status+inspect loop are all present. The output schema covers returns, and the remaining gap is the undocumented workspace and idempotency_key parameters.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must compensate, and it largely does: format values (html/markdown/pdf/docx/xlsx), style enum (house, bold, editorial, playful, terminal, swiss), html vs markdown field roles, artifact_id/base_version, display_name and slug. It leaves `workspace` and `idempotency_key` entirely unexplained, which matters for a publish call that counts against a quota.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

It states a specific verb and resource ('Store a page') and immediately scopes it ('you have already finished', 'No model call'), which cleanly separates it from the model-backed create/edit path. It also references `create` for slug/display_name rules, confirming which sibling it is parallel to rather than duplicating.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage context is strong: when to use it (finished page in hand), the immediate follow-up workflow (call status(job_id, wait=90), then inspect safety_changes), and how to pick format/style. It never states an explicit exclusion or names the alternative to reach for when the page is not yet finished, so route selection for the main purpose is only implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

revoke_previewsAInspect

Expire private /p/ preview links for an artifact (or one version). Token revocation and signing-key rotation do not expire these database-backed capabilities; call this explicitly.

ParametersJSON Schema
NameRequiredDescriptionDefault
versionNo
artifact_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. It usefully discloses that the links are database-backed capabilities unaffected by token/key rotation, which is non-obvious. But it omits whether the expiry is reversible, what permissions are required, and what the operation returns (the output schema partly covers the latter).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no filler, and the core action is front-loaded before the clarifying note about token revocation. Every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter mutation tool with an output schema already present, the description covers purpose, the non-obvious behavioral caveat, and the scope distinction adequately. Return-value explanation is correctly delegated to the output schema; only permission/irreversibility details are missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% for two parameters, so the description must compensate. The phrase 'for an artifact (or one version)' implicitly conveys that version narrows the scope to a single version while omitting it targets the whole artifact, which is meaningful. It adds no format or identifier details for artifact_id.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a precise verb+resource: expiring private /p/ preview links for an artifact or a single version. That is far more specific than the sibling verbs (share, unshare, delete) and an agent can tell what is being acted upon. It stops short of explicitly contrasting itself with unshare/delete, which would be needed for a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a clear conditional reason to invoke: token revocation and signing-key rotation do NOT expire these links, so call this explicitly. That is actionable context for when the tool is necessary. It does not, however, state when NOT to use it or name the alternative tool for related revocation needs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

shareAInspect

Publish a public link. Anyone with the link can open it.

Permission: share.
Arguments: artifact_id (required), version (optional; passing it pins that version), ttl_days (optional; ignored when AM_SHARE_TTL_DAYS is 0), pin (optional).
Returns: state, url, version, follows ("latest" or "pinned"), expires_at, lifetime, reused.
Omit version to keep serving the latest done version on the same URL.
Example: share(artifact_id="art_example")
unshare revokes it immediately.
ParametersJSON Schema
NameRequiredDescriptionDefault
pinNoFreeze one version. Optional. True with no version pins the latest done version now. False with no version follows later versions.
versionNoDone version to pin. Optional. Omit it to follow the latest done version. Passing a version freezes that version on its own link.
ttl_daysNoShare lifetime in days. Optional. Defaults to AM_SHARE_TTL_DAYS. Ignored when AM_SHARE_TTL_DAYS is 0 (the link lasts until revoked). Otherwise capped by AM_SHARE_TTL_MAX_DAYS.
artifact_idYesArtifact to publish. Required.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and largely meets it: it names the required permission, explains TTL behavior and the AM_SHARE_TTL_DAYS=0 exception, describes pin vs follow semantics, and notes immediate revocation via unshare. It does not state idempotency/reuse behavior for repeated calls, though the 'reused' return field hints at it.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded purpose, then Permission/Arguments/Returns/Example in a scannable block, and ends with the unshare note. Some argument text restates the schema, but it is compact and the example is useful.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return details are optional, yet the extra Returns list plus example make the tool callable end-to-end. The main remaining gap is sibling differentiation from 'publish' on a toolset that also has 'edit' and 'create'.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning on top of it: 'passing it pins that version', 'ignored when AM_SHARE_TTL_DAYS is 0', and the follow-vs-pin contrast for version/pin interaction.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a concrete verb+resource ('Publish a public link') and immediately defines the effect ('Anyone with the link can open it'), so the agent knows exactly what is produced. It does not, however, distinguish this from the sibling 'publish' tool, which the agent must resolve on its own.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear operating context: requires the share permission, omit version to follow the latest done version, and 'unshare revokes it immediately', which routes the agent to the sibling for teardown. It stops short of an explicit when-to-use-this-over-publish rule.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

statusBInspect

Build state (queued, building, done, failed, or needs_input). needs_input includes a missing message. Also returns progress and the presentation card. wait (0 to 90 seconds) holds the call open until the build finishes.

ParametersJSON Schema
NameRequiredDescriptionDefault
waitNo
job_idNo
artifact_idNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the burden and does disclose meaningful behavior: the enumerable states, what needs_input means, that progress and a presentation card are returned, and that wait blocks up to 90 seconds. It omits any auth/permission context and, critically, gives no indication of what happens if neither or both identifiers are supplied.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences with no filler, front-loaded with the returned state values before moving to the blocking behavior of wait. Every sentence carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return-value detail is not strictly required, and the description still adds useful state semantics. However, for a tool with three parameters, 0% schema coverage, and no annotations, the absence of any job_id/artifact_id guidance leaves a real gap an agent must guess around.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for all three parameters. It explains wait well (range and blocking semantics), but job_id and artifact_id are never mentioned, leaving the agent with no guidance on which identifier to pass or whether they are mutually exclusive.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States the specific resource (build state) and enumerates the exact states it can return, which lets an agent recognize this as a status-check tool. It does not explicitly differentiate itself from siblings like inspect or list_artifacts, but the purpose is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit when-to-use or alternative is named. The note that 'wait (0 to 90 seconds) holds the call open until the build finishes' implies the poll-versus-block choice, but the agent must infer when polling is appropriate versus blocking, and no sibling (e.g. inspect) is offered as an alternative.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unshareAInspect

Revoke public share links. Takes effect immediately; the old URL returns 404.

Permission: share.
Arguments: artifact_id (required), version (optional). A version revokes only pinned links for that version. Omit it, or pass 0, to revoke every public link, including the one that follows the latest version.
Returns: artifact_id, revoked_links (count), and state. state is "shared" when a public link is still live (with url and follows), and "not_shared" only when none is.
Example: unshare(artifact_id="art_example")
ParametersJSON Schema
NameRequiredDescriptionDefault
versionNoRevoke only this version. Optional. Omit it, or pass 0, to revoke every version of the artifact.
artifact_idYesArtifact whose public links should be revoked. Required.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden and largely meets it: it discloses immediate effect, that the old URL returns 404, the required 'share' permission, and the semantics of the returned state values. Gaps remain around idempotency (calling on an already-unshared artifact) and any rate or scope limits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action and its immediate effect, then structured into Permission, Arguments, Returns, and Example blocks. There is minor overlap between the argument prose and the schema descriptions, and the Returns block restates what the output schema already conveys, but nothing is padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a mutation tool with no annotations, the description covers effect timing, required permission, argument semantics with edge cases, and result-state interpretation. An agent has everything needed to invoke it correctly without opening the schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3, but the description adds meaning beyond the schema by explaining that omitting version also revokes the link that follows the latest version and that a versioned revoke targets only pinned links. That nuance about link types is not present in the schema text.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Revoke public share links'), which cleanly inverts the sibling 'share'. It does not explicitly disambiguate against adjacent siblings such as 'revoke_previews' or 'delete', so an agent must infer the boundary from the 'public share link' wording alone.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear conditional guidance for the version argument: a specific version revokes only pinned links for that version, while omitting it or passing 0 revokes everything including the latest-version link. It stops short of naming an alternative tool or stating when not to use this one.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 11 tool updatesv0.1.0
    • First observedcreate
    • First observeddelete
    • First observededit
    • First observedexport
    • First observedinspect
    • First observedlist_artifacts
    • First observedpublish
    • First observedrevoke_previews
    • First observedshare
    • First observedstatus
    • First observedunshare

TDQS

A3.9/5.0

Scored across 11 tools

Disambiguation4/5

The three authoring tools are clearly separated by who writes the content (create = model builds, publish = you supply finished page, edit = new version from base_version), and status vs inspect vs list_artifacts have distinct roles. Minor overlap remains between create/publish (both accept format/style/html and queue jobs) and between unshare/revoke_previews (both revoke link types), but descriptions disambiguate them.

Naming Consistency4/5

Mostly clean lowercase verbs (create, edit, publish, inspect, export, share, unshare, delete) with a couple of verb_noun names (list_artifacts, revoke_previews) and one noun (status). The pattern is readable and predictable despite the minor mix of single-verb and verb_noun forms.

Tool Count5/5

Eleven tools is well-scoped for an artifact lifecycle server. Every tool maps to a distinct stage (create/edit/publish/status/list/inspect/export/share/unshare/revoke_previews/delete) with no redundant entries.

Completeness5/5

Full lifecycle coverage: creation (create/publish), versioning (edit), monitoring (status), discovery (list_artifacts), inspection (inspect), distribution (export/share), revocation (unshare/revoke_previews), and cleanup (delete). No obvious dead ends for the stated artifact-hosting purpose.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers