artifacty
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@artifactylist my recent artifacts"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Artifacty
Artifacty is a local, agent-to-agent artifact exchange for LLM workflows. Claude, Codex, Gemini, GitHub Copilot, Cursor, and other MCP-capable tools can publish an artifact once, then other agents can list, read, update, and continue from it without copying content through chat.

Why MCP
Claude Code artifacts are useful because they turn session output into shareable, versioned pages. Artifacty keeps that local and cross-agent: the browser server renders artifacts for people, while the MCP stdio server gives agents a common tool interface.
Related MCP server: Artifacta MCP Server
Installation
Artifacty requires Node.js 22.5 or newer.
Install the CLI globally from npm:
npm install -g artifacty
artifacty --helpRun it without a global install:
npx artifacty@latest serve --foregroundStart the local dashboard in the background:
artifacty serveOpen the url printed in the JSON response. Artifacty prefers http://127.0.0.1:8787; if that default port is busy and no explicit port was configured, it starts on the next available local port and records the actual URL for CLI and MCP responses.
Manage the background server:
artifacty status
artifacty stopartifacty start and artifacty serve --detach use the same lifecycle path as artifacty serve. Logs are written under ~/.artifacty/logs/.
These lifecycle commands use Node's detached process support and work on macOS, Linux, and Windows. artifacty stop uses process-group signals on macOS/Linux and Windows taskkill, falling back to /F when Windows requires forceful termination.
For foreground debugging, keep the process attached:
artifacty serve --foreground
npm startGenerate an API token at startup when you want to protect HTTP API and browser write routes:
artifacty serve --generate-token
artifacty serve --host 0.0.0.0 --share-mode lan --generate-token
artifacty serve --foreground --generate-tokenBackground serve returns the generated token and ready-to-open /new?token=... and /import?token=... URLs in JSON. Foreground serve prints the same values to stderr. For scripts or long-running services that need a stable token, generate one first:
artifacty token
artifacty start --api-token "$(artifacty token --raw)"Install MCP configuration for local agents:
artifacty install claude
artifacty install codex --dry-run
artifacty install gemini
artifacty install copilot
artifacty install cursor
artifacty install all
artifacty checkFor a central internal server, enable the HTTP MCP endpoint on the server and let users issue personal MCP/API tokens from the account page:
ARTIFACTY_BOOTSTRAP_TOKEN="$(artifacty token --raw)"
artifacty serve --host 10.0.0.50 --share-mode team --api-token "$ARTIFACTY_BOOTSTRAP_TOKEN" --mcp-http --foreground
# Open http://10.0.0.50:8787/login, create the first admin, then create a personal token at /account.
artifacty install all --mcp-url http://10.0.0.50:8787/mcp --api-token "$ARTIFACTY_PERSONAL_TOKEN"When running the central server as a Linux systemctl --user service, enable
lingering for the service account so Artifacty stays up after logout:
sudo loginctl enable-linger $USER.
Administrators can create users individually at /admin/users or import them
from CSV. Use email,name,role,password,password_reset_required headers. If
password is empty, Artifacty generates a temporary password, shows it once in
the import result, and requires the user to change it on first sign-in.
email,name,role
user@example.com,User,user
admin2@example.com,Admin Two,adminartifacty users import --file users.csvRun diagnostics for the local runtime, store, server, service definitions, and MCP discovery:
artifacty doctor
artifacty doctor --skip-mcpUse artifacty install codex --timeout 30000 or
artifacty install gemini --timeout 30000 to tune supported MCP client timeouts.
See docs/integrations.md for Claude Code, Codex, Gemini CLI, GitHub Copilot in VS Code, and Cursor setup.
Quick Start
Create an artifact in the browser:
http://127.0.0.1:8787/newFor local development from a checkout:
npm install
npm test
npm startRun the production-readiness check:
npm run release:checkPublish from the CLI:
artifacty publish --title "handoff note" --format markdown --source codex --content "# Next step\nReview the API plan."Import an artifact produced by another agent and convert it to Artifacty format:
artifacty import --agent claude --file ./deploy-failures.html --tag review
artifacty import --agent gemini --content '{"title":"Plan","returnDisplay":"# Plan\n- Ship it"}'
artifacty import --agent codex --content '{"agent":"codex","title":"Implementation Handoff","goal":"Continue Phase 3","changedFiles":[{"path":"src/lib/render.js","status":"modified"}],"nextSteps":["Add CodeMirror read-only viewer"]}'
artifacty import --agent copilot --content '{"agent":"github-copilot","title":"PR Review","findings":[{"severity":"medium","file":"src/app.js","line":42,"title":"Handle missing state"}]}'
artifacty import --agent cursor --content '{"sourceAgent":"cursor","title":"Cursor Handoff","summary":"Editor pass complete.","nextSteps":["Run visual QA."]}'Codex, GitHub Copilot, and Cursor structured payloads can become handoff,
bundle, diff-walkthrough, code-review, or test-report artifacts when
the payload explicitly identifies the agent through agent or sourceAgent.
Plain Markdown from these agents stays a normal
document unless you pass an explicit artifactType.
When format is omitted, Artifacty infers common formats from filename,
content type, and content. HTML files and HTML fragments such as
<section>...</section> are stored as html so the browser viewer renders
them as HTML instead of plain text.
Agent Handoff Example
One agent can publish a continuation artifact, then another agent can discover it, read the context, and append the next version.
Codex publishes the handoff:
artifacty import --agent codex --tag handoff --content '{
"agent": "codex",
"title": "Release Handoff",
"goal": "Prepare Artifacty for an npm release",
"changedFiles": [
{ "path": "src/lib/converters.js", "status": "modified", "summary": "Normalize agent outputs" }
],
"commands": [
{ "command": "npm run release:check", "status": "passed" }
],
"nextSteps": [
"Review README",
"Publish package"
]
}'Claude or Gemini finds the handoff and continues from the same artifact:
artifacty list --tag handoff
artifacty show release-handoff-abc12345 --raw
artifacty update release-handoff-abc12345 \
--format markdown \
--source claude \
--tag handoff \
--content "# Release Handoff\n\nReviewed README and prepared publish notes."List artifacts:
artifacty list
artifacty list --query review --limit 20 --offset 20Run the MCP server:
artifacty-mcp
ARTIFACTY_MCP_MODE=bridge ARTIFACTY_MCP_URL=http://10.0.0.50:8787/mcp ARTIFACTY_API_TOKEN=... artifacty-mcpMCP clients can create artifacts with artifacty_create. artifacty_publish remains as a backwards-compatible alias.
MCP clients that support resources or prompts can also read artifacty://recent,
artifacty://artifacts/{id}, artifacty://artifacts/{id}/raw{?version}, and
artifacty://schema/v1, or use prompt templates such as artifacty_handoff,
artifacty_review, and artifacty_release_notes.
Operational commands:
artifacty audit --limit 20
artifacty doctor
artifacty index rebuild
artifacty integrity
artifacty backup
artifacty backup --full
artifacty export --file ./artifacty-backup.json
artifacty export --file ./artifacty-backup-full.json --full
artifacty import-store --file ./artifacty-backup.json
artifacty import-store --file ./artifacty-backup-full.json --confirm replace-all [--force-users]
artifacty start
artifacty status
artifacty stop
artifacty service install --dry-run
artifacty service unit --dry-run
artifacty service task --dry-runWhen working from a source checkout without global installation, replace artifacty with node src/cli.js and artifacty-mcp with node src/mcp-server.js.
Storage
By default Artifacty stores files under ~/.artifacty.
ARTIFACTY_HOME=/path/to/shared/store artifacty serveArtifact metadata is stored in artifacty.sqlite; artifact content is stored as append-only version files under artifacts/ for normal create and update flows. Administrators can repair or delete individual bad versions from the browser, and those exceptional actions are recorded in the audit log. The current browser server URL is written to server.json so MCP tools can return the correct links when the default port falls back. Existing index.json stores are migrated automatically on first access.
Every artifact row carries a visibility (private/team, default team) and an ownerUserId, backfilled from the artifact's publisherUserId on migration. See the Security Model section below for the access rules these enforce.
Administrators can download and restore backups from /admin/backup. The
default artifacts scope contains artifact metadata and version contents,
but not users, sessions, API token records, or audit logs. Restoring an
artifacts-scope bundle replaces the target server's artifact records and
prunes unreferenced version files, leaving users, tokens, and audit history
untouched.
A full-scope backup (artifacty backup --full, ?scope=full on the
download, or the scope selector on /admin/backup) additionally includes
users (with password hashes), API tokens (hashed, never the raw token),
audit log entries, artifact relations, and webhooks. Webhook secrets are
never exported — restored webhooks come back disabled and must have their
secret re-issued. Sessions are never exported. Restoring a full bundle
requires explicit confirmation (confirm: "replace-all" over the API,
--confirm replace-all on the CLI, or the confirm checkbox in the browser)
and refuses to run when the target store already has users unless
forceUsers/--force-users is also set, since that would silently merge or
overwrite existing accounts. Restoring a bundle written before this feature
(no bundleVersion/scope header) is still supported and is always treated
as artifacts-only. For large migrations or scripted server moves, use
artifacty backup --full and
artifacty import-store --file ./artifacty-backup.json --confirm replace-all.
Search uses a SQLite FTS5 index when the local Node SQLite build supports it. The index covers the latest version body plus title, tags, source agent, artifact type, format, and metadata summary. If FTS5 is unavailable, Artifacty keeps working with metadata search. Rebuild or check the store when needed:
artifacty index rebuild
artifacty integritySemantic Search
?mode=keyword|semantic|hybrid on GET /api/artifacts, the dashboard, artifacty list --mode, and the MCP artifacty_list tool add optional natural-language search on top of FTS5, without any bundled model or mandatory dependency:
keywordis the existing FTS5/metadata path.semanticembeds the query and ranks artifacts by cosine similarity over stored vectors.hybrid(the default once a provider is configured andq/queryis set) merges keyword and semantic rankings with reciprocal rank fusion.
Without an embedding provider configured, semantic/hybrid requests fall back to keyword and the response's search.fallback is true. Configure one provider through the environment:
# openai-compatible: POSTs { model, input } to <url>/embeddings
export ARTIFACTY_EMBEDDINGS_URL=https://api.openai.com/v1
export ARTIFACTY_EMBEDDINGS_MODEL=text-embedding-3-small
export ARTIFACTY_EMBEDDINGS_API_KEY=sk-...
# or: command, a local executable reading/writing JSON lines on stdin/stdout
# (wire in Ollama or any local model without an Artifacty dependency)
export ARTIFACTY_EMBEDDINGS_COMMAND="python3 embed.py"The API key is read from the environment only; it is never logged, returned in an error, or persisted to the store (artifacty doctor reports the provider and model with the key redacted). New artifacts are embedded automatically in the background after create/update, and existing ones can be (re-)embedded in batches of 16 with:
artifacty index rebuild --embeddingsRelations
Artifacts can be linked with typed, directional relations so a handoff, its review, and follow-up work stay discoverable from one another:
Relation names are a closed set for v1:
derived-from,supersedes,reviews,references,part-of.Every relation has a computed inverse (
derived-from↔derives,supersedes↔superseded-by,reviews↔reviewed-by,references↔referenced-by,part-of↔contains) shown automatically on the other side.POST/GET/DELETE /api/artifacts/:id/relationsmanage links over HTTP;GET /api/artifactsacceptsrelatedToandrelationfilters.artifacty_createandartifacty_updateaccept arelationsarray so an agent can link an artifact the moment it is published;artifacty_getresponses includerelations: { outgoing, incoming }.A relation whose target was deleted is reported with
missing: trueinstead of being silently dropped.
artifacty link <from-id> derived-from <to-id>
artifacty relations <artifact-id>
artifacty show <artifact-id> --relations
artifacty unlink <from-id> derived-from <to-id>Comments and review status
Lightweight, version-anchored feedback without publishing a whole new version or a separate review artifact:
Comments are Markdown, rendered through the same sanitized Markdown pipeline as artifact content, and capped at 16 KB.
anchoris an optional, format-specific rendering hint —{ line: 42 }for text formats,{ path: "$.runs[0].results[3]" }for JSON/SARIF,{ row: 7 }for CSV — and is not validated against the artifact's actual content.Threads are one level deep:
parentIdon a new comment must point to a root comment (one with noparentIdof its own).Deleting a comment is a soft delete: it disappears from
artifacty comments/GET .../commentsbut its audit-log entry is kept. Pass--include-deleted/includeDeleted=trueto see it anyway.Every artifact carries a
reviewStatus:none,pending,changes-requested, orapproved, set explicitly and reset topendingautomatically (noted in that update's audit metadata) whenever a new version is appended after anapprovedstatus.GET/POST /api/artifacts/:id/comments,POST /api/artifacts/:id/comments/:commentId/resolve,DELETE /api/artifacts/:id/comments/:commentId, andPOST /api/artifacts/:id/review-statusmanage comments and review status over HTTP.artifacty_comment,artifacty_resolve_comment, andartifacty_set_review_statusare the MCP equivalents;artifacty_getacceptsincludeComments: trueto return the requested version's open comments alongside the artifact.The browser artifact page shows a comments panel grouped by version, with a reply form, a resolve button, and (for the artifact's owner or an admin) a review-status selector. Line-anchored comments are shown as plain text (e.g. "line 42") rather than a CodeMirror gutter marker.
artifacty comment release-handoff-abc12345 --body "Looks good, one nit" --line 42
artifacty comments release-handoff-abc12345
artifacty resolve-comment release-handoff-abc12345 <comment-id>
artifacty review-status release-handoff-abc12345 approvedDiff
artifacty diff <id> [--from N] [--to M] [--structured] [--json] compares two versions of an artifact, defaulting --to to the latest version and --from to --to - 1. JSON, SARIF, CSV, notebook, and bundle artifacts get a structured diff (JSON path entries, CSV row/cell entries, or per-file bundle entries) by default; other formats print a line diff. --structured forces the structured (word-highlighted line) diff for any format; --json prints the machine-readable form instead of the human-readable unified text.
artifacty diff release-handoff-abc12345
artifacty diff release-handoff-abc12345 --from 1 --to 3 --jsonRetention
Artifacts, audit rows, and events grow without bound by default. A declarative retention policy, stored in the meta table and edited through /admin/retention or artifacty retention set, lets an admin bound that growth:
archiveAfterDays(a default plus optional per-artifact-type overrides) auto-archives artifacts that have not been updated within the window, unless the artifact carries a tag inkeepTagsor hasreviewStatus: "approved".purgeArchivedAfterDayshard-deletes artifacts that have been archived longer than the window, including their version files, dependent rows that cascade via foreign keys (comments, relations, embeddings), and theirevents/search-index rows. Audit log rows for the artifact are deliberately kept (seeauditRetentionDaysbelow) as the historical record that the purge happened, even though the artifact itself is gone. This is the only retention action that deletes data outright, so it is refused unless the operator has setARTIFACTY_RETENTION_ALLOW_PURGE=truein the server environment;--allow-purge(or theallowPurgerequest field) only opts a single run in and never sets that environment variable itself. Always dry-run first (artifacty retention run --dry-run, or the report on/admin/retention) to review what would be purged.auditRetentionDaysprunes old audit log rows, exceptversion-repair,version-delete,retention-purge,retention-policy-update, andowner-change, which are kept indefinitely.eventRetentionRowscaps how many rows theeventschange-notification table keeps.
A background sweep runs every ARTIFACTY_RETENTION_INTERVAL_MS (default one hour) alongside the HTTP server, checking the current policy on every tick — it starts even when the policy is all-unset, so setting a policy later takes effect on the next tick without a restart. artifacty integrity/checkStoreIntegrity flags any files left behind if a purge is ever interrupted, and artifacty doctor reports the current retention policy summary.
artifacty retention show
artifacty retention set --archive-after-days 90 --archive-after-days-for test-report=30 --purge-archived-after-days 180 --audit-retention-days 365 --event-retention-rows 10000 --keep-tag pinned
artifacty retention run --dry-run
ARTIFACTY_RETENTION_ALLOW_PURGE=true artifacty retention run --allow-purgeDashboard Filters and Saved Views
listArtifactsPage (and every surface built on it — the / dashboard, GET /api/artifacts, artifacty list, and artifacty_list) accepts these filters in addition to q/tag/sourceAgent/relatedTo/relation/includeArchived:
artifactType— exact match against the artifact's type.publisher— matchespublisherId,publisherUserId, orownerUserId.createdAfter/createdBefore— ISO date or date-time bounds oncreatedAt. An unparseable value returns a400withcode: "invalid_filter".reviewStatus— the artifact-level review status:none,pending,changes-requested, orapproved.
A frequently used filter combination can be saved as a view:
POST /api/viewswith{ name, filters, shared }creates one;GET /api/viewslists views visible to the caller;DELETE /api/views/:idremoves one.filtersis validated against an allowlist (query,tag,sourceAgent,artifactType,publisher,createdAfter,createdBefore,reviewStatus,relatedTo,relation,includeArchived,mode) and rejects any other key withcode: "invalid_filter".In single-user mode (no user accounts) views are global. In team mode a view belongs to its creator and is visible only to them unless
shared: true, in which case every user sees it.?view=<name-or-id>on/orGET /api/artifactsexpands the saved filters first; any other query parameter passed alongsideviewoverrides that one filter.artifacty_listandartifacty list --view <name-or-id>work the same way.The dashboard sidebar lists views with delete buttons and a "Save current filters" form.
artifacty views,artifacty views save <name> [--shared] [filters...], andartifacty views delete <id>manage them from the CLI.groupBy=artifactType|sourceAgent|day(dashboard query param,--group-byon the CLI) renders the already-fetched page grouped under section headers. It is purely presentational and does not change which artifacts are fetched or their sort order.
artifacty list --type handoff --review-status pending --created-after 2025-01-01
artifacty views save open-handoffs --type handoff --review-status pending --shared
artifacty list --view open-handoffs --group-by sourceAgent
artifacty views delete <view-id>Events
Every mutation (artifact.created, artifact.updated, artifact.archived, artifact.restored, artifact.relation.added, artifact.version.repaired, artifact.version.deleted) publishes an event agents can react to instead of polling artifacty_list.
Watch from a shell:
artifacty watch --tag handoff --type artifact.updated --json
artifacty watch --artifact release-handoff-abc12345 --exec "./on-change.sh"
artifacty watch --once # exits 0 after the first match--exec runs the command with the event JSON on stdin and ARTIFACTY_EVENT_TYPE, ARTIFACTY_EVENT_ARTIFACT_ID, ARTIFACTY_EVENT_VERSION environment variables. watch reconnects automatically with Last-Event-ID if the connection drops.
Server-Sent Events:
curl -N -H "x-artifacty-token: $ARTIFACTY_API_TOKEN" \
"http://127.0.0.1:8787/api/events?tag=handoff"Filters: type, tag, artifactId, sourceAgent. Send Last-Event-ID (a request header, matching the id: field of the last event you saw) to replay missed events after a reconnect. Without Accept: text/event-stream, GET /api/events?since=<seq> returns one JSON page of events instead (useful for polling or tests). The server sends a heartbeat comment every 25 seconds and caps concurrent streams at ARTIFACTY_SSE_MAX_CLIENTS (default 64; excess connections get 503).
Webhooks deliver the same events to an external HTTP endpoint:
curl -s -X POST http://127.0.0.1:8787/api/webhooks \
-H 'content-type: application/json' \
-H "x-artifacty-token: $ARTIFACTY_API_TOKEN" \
-d '{ "url": "https://example.com/hooks/artifacty", "eventTypes": ["artifact.updated"] }'The response includes secret once — store it; it is never returned again. Deliveries are POST JSON with headers X-Artifacty-Event, X-Artifacty-Delivery, and X-Artifacty-Signature: sha256=<hmac>, retried up to 3 times (2s/10s/60s backoff) and disabled after 20 consecutive failures. Manage webhooks at /admin/webhooks in the browser, or GET/POST /api/webhooks, DELETE /api/webhooks/:id, POST /api/webhooks/:id/test. See docs/threat-model.md for the signature scheme and SSRF guard.
MCP: resources/subscribe on artifacty://artifacts/{id} or artifacty://recent delivers notifications/resources/updated over the stdio transport. The artifacty_wait tool ({ artifactId?, tag?, type?, timeoutMs }, max 120000ms) is a long-poll alternative that works over any transport, including the stateless /mcp HTTP transport where resources/subscribe cannot push notifications. See docs/mcp-public-api.md.
API Example
Start a protected server with a reusable shell token:
export ARTIFACTY_API_TOKEN="$(artifacty token --raw)"
artifacty serve --api-token "$ARTIFACTY_API_TOKEN"curl -s http://127.0.0.1:8787/api/artifacts \
-H 'content-type: application/json' \
-H "x-artifacty-token: $ARTIFACTY_API_TOKEN" \
-d '{
"title": "PR review dashboard",
"content": "<h1>Review</h1>",
"format": "html",
"sourceAgent": "claude",
"tags": ["review"]
}'Convert-and-save an external agent artifact:
curl -s http://127.0.0.1:8787/api/import \
-H 'content-type: application/json' \
-H "x-artifacty-token: $ARTIFACTY_API_TOKEN" \
-d '{
"agent": "claude",
"fileName": "deploy-failures.html",
"content": "<html><head><title>Deploy failures</title></head><body>...</body></html>",
"tags": ["review"]
}'Browser routes:
/: list artifacts with search, tag, and source filters./new: create an Artifacty-native artifact with the CodeMirror editor./import: paste an external agent artifact and convert it with automatic editor mode detection./artifacts/:id/edit: save a new version with Markdown, HTML, JSON, text, code, SVG, Mermaid, React, SARIF, CSV, image, or video syntax support. Browser edits that do not change the artifact are recorded asupdate-noopaudit events without creating a version./artifacts/:id/diff?from=&to=&view=structured|lines: compare versions.structuredis the default view for JSON-like formats (json,sarif,csv,notebook, bundle artifacts) and shows JSON path, CSV row/cell, or bundle per-file changes; other formats default to the line view with word-level highlighting on changed lines./api/artifacts/:id/diff?from=&to=&view=: the same diff as structured JSON./admin/artifacts/:id/versions: administrator-only repair/delete screen for individual versions./admin/backup: administrator-only backup download and restore screen, with anartifacts/fullscope selector./api/admin/backup?scope=artifacts|full: administrator-only backup JSON download./api/admin/backup/import: administrator-only backup restore. Accepts the raw bundle or{ bundle, confirm, forceUsers }./api/audit: list audit events./openapi.json: machine-readable OpenAPI 3.1 document for every/api/*route and/mcp;/docs/api: server-rendered human-readable reference for the same routes. Both require no authentication.
List APIs support pagination with limit and offset. Responses keep the top-level artifacts array and include pagination and search metadata:
curl -s "http://127.0.0.1:8787/api/artifacts?q=handoff&limit=20&offset=0" \
-H "x-artifacty-token: $ARTIFACTY_API_TOKEN"Optimistic Concurrency
Every artifact response carries latestVersion and an etag ("<id>:<latestVersion>"). When two agents read the same version and both try to save a new one, the second write can silently discard the first agent's work. To avoid that, pass back the version you read as expectedVersion in the update body, or send it as an If-Match header (a weak W/"..." prefix is accepted):
curl -s http://127.0.0.1:8787/api/artifacts/<id> \
-H 'content-type: application/json' \
-H "x-artifacty-token: $ARTIFACTY_API_TOKEN" \
-H 'If-Match: "<id>:3"' \
-d '{ "content": "updated content" }'If the artifact's latestVersion no longer matches, the update is rejected with 409 Version conflict:
{ "error": "Version conflict", "code": "version_conflict", "details": { "latestVersion": 4 } }GET /api/artifacts/:id sets an ETag header and returns 304 Not Modified when the request's If-None-Match matches the current version. expectedVersion is optional, so clients that don't send it keep working exactly as before. The browser editor sends its own hidden expectedVersion field and shows a banner with a link to the diff view if another agent updated the artifact first.
Interface Language
The browser UI defaults to English. Add ?lang=ko to any browser route to use Korean, for example http://127.0.0.1:8787/new?lang=ko. Forms and in-app links preserve the selected language. Documentation is maintained in English only.
Schema and storage:
Metadata lives in SQLite with
schemaVersion: 1,artifactType,publisherId, andarchivedAt.Archive hides artifacts from default lists without deleting versions. Admin version repair/delete is available for correcting accidental or sensitive historical versions and records
version-repairorversion-deleteaudit events.Bundle artifacts store multiple files or base64 assets as portable JSON. A
filesentry can also carry a binary document (PDF, DOCX, XLSX, or ZIP, base64-encoded, 32 MB per file) served atGET /artifacts/:id/raw?file=<path>; PDFs render in a sandboxed viewer iframe and other document types show a download link.Supported formats are
html,markdown,text,json,code,svg,mermaid,react,sarif,csv,image,video, andnotebook.Native create/import paths infer
htmlfrom HTML documents or fragments when no explicit format is supplied.sourceAgentis canonicalized before storage. Aliases such asclaude-code,Claude Code,github-copilot, andgemini-cliare stored asclaude,copilot, andgemini; legacyunknownrows are backfilled only when version metadata, audit data, or source-agent tags provide a known agent.Diagram, component, source snippet, analysis report, table, and media assets use
diagram,component,snippet,analysis-report,table, andassetartifact types.Copilot/Cursor examples cover PR reviews, screenshots, demo recordings, and visual evidence bundles.
See docs/mcp-public-api.md for MCP tools, resources, prompts, and compatibility notes.
See docs/central-team-deployment-design.md for central team deployment.
See docs/sarif-csv-artifact-plan.md for the SARIF/CSV output artifact roadmap.
See docs/roadmap-design.md for the design of planned features such as relations, change notifications, optimistic concurrency, comments, semantic search, visibility, token scopes, and retention.
Security Model
The HTTP server binds to
127.0.0.1by default.If
ARTIFACTY_API_TOKENis set, HTTP API routes requireAuthorization: Bearer <token>orx-artifacty-token; scripts should prefer headers over?token=...URLs.When users exist, personal API tokens issued from
/accountalso authenticate HTTP API and MCP requests. Created artifacts record the token owner's email aspublisherId, and audit logs record the same identity asactor.API token checks use timing-safe digest comparison.
Personal API tokens carry scopes (
read,write,admin; defaultread+write), settable as checkboxes when creating a token from/account.adminis only grantable to an admin user's token. Every/api/*route requires the matching scope on the authenticating token; the sharedARTIFACTY_API_TOKENand browser sessions always have full scopes. A denial responds403withcode: "scope_denied"and writes a rate-limitedtoken-scope-deniedaudit row. Over MCP,tools/listomits mutating tools for a token withoutwrite, and calling one anyway returns an error result withstructuredContent.code: "scope_denied".Once any user account exists, every artifact has a
visibility(privateorteam, defaulting toteam) and anownerUserId.teamis readable and writable by any authenticated principal (unlessARTIFACTY_TEAM_WRITE=ownerrestricts writes to the owner or an admin).privateis readable and writable only by its owner or an admin; reads by anyone else 404 instead of 403 so the artifact's existence isn't leaked. Archiving, restoring, and changing visibility or ownership always require the owner or an admin, regardless of visibility. A sharedARTIFACTY_API_TOKEN(or any request before the first user account is created) has no personal identity and is treated as an anonymous team principal: it can read and writeteamartifacts but never seesprivateones. In single-user mode (no user accounts at all) every check is bypassed, matching pre-visibility behavior. Change visibility and ownership withPOST /api/artifacts/:id/visibilityandPOST /api/artifacts/:id/owner, theartifacty visibility <id> private|teamCLI command, or theartifacty_set_visibilityMCP tool.HTTP and MCP-over-HTTP requests are rate limited per
(token or user or remote address, bucket)in fixed one-minute windows:write(mutating routes, default 120/min,ARTIFACTY_RATE_WRITE_PER_MIN),auth(/login, default 10/min per address,ARTIFACTY_RATE_AUTH_PER_MIN), andsearch(GET /api/artifacts?q=, default 300/min,ARTIFACTY_RATE_SEARCH_PER_MIN). Rate limiting is disabled on a loopback bind unlessARTIFACTY_RATE_LIMIT=always, and always disabled withARTIFACTY_RATE_LIMIT=off. Exceeding a limit returns429with aRetry-Afterheader,code: "rate_limited", and a rate-limitedrate-limitedaudit row.Binding outside localhost requires both
ARTIFACTY_SHARE_MODE=lanorteamandARTIFACTY_API_TOKEN.Non-local sharing is intended for trusted LAN or VPN sessions. Prefer a specific interface IP over
0.0.0.0, keep React rendering disabled, and see docs/network-sharing.md.Non-local binding prints a startup warning because Artifacty does not terminate TLS.
Artifact content is scanned for common API keys and private keys before storage. Use
--allow-secretsorARTIFACTY_ALLOW_SECRETS=trueonly for intentional exceptions.Creates, updates, reads, imports, archives, restores, no-op browser edits, and admin version repair/delete actions write audit events to SQLite. Legacy artifacts without a stored publisher are best-effort backfilled from their first
createorimportaudit actor.CodeMirror editor/viewer and renderer assets are served from local npm dependencies through a package allowlist, not from a public CDN. JavaScript asset routes answer
Origin: nullrequests withAccess-Control-Allow-Origin: nullso sandboxed renderer iframes can import local ESM withoutallow-same-origin.Mutating HTTP routes reject non-local browser origins.
HTML artifacts render in a sandboxed iframe.
SVG artifacts render in a scriptless sandboxed iframe and are sanitized for
<script>,on*attributes, andjavascript:links in the viewer. The raw source remains unchanged.Mermaid artifacts render with the vendored local Mermaid package in a sandboxed iframe without
allow-same-origin.React artifacts are source-only by default. Set
ARTIFACTY_ENABLE_REACT_RENDERER=trueto execute them in a sandboxed frame with a frame-scoped CSP that permits JSX transformation.SARIF artifacts render a bounded findings summary and keep the full formatted JSON behind a raw-source details panel. The viewer adds client-side level filter chips (error/warning/note), a rule id text filter, and sort-by-level/rule/location over the rendered set, all as progressive enhancement (the server output is already correct without JS).
CSV artifacts render as an escaped, bounded table;
/rawpreserves the original text. The viewer adds client-side numeric-aware column sort (click a header) and a per-column contains filter, with a visible row count.GET /artifacts/:id/export?format=csv&sort=<col>&dir=asc|desc&filter=<col>:<text>[,...]andGET /artifacts/:id/export?format=sarif&level=error,warning&rule=<text>re-parse the stored original (never/rawitself), apply the filter/sort, and stream a fresh file withContent-Disposition: attachment, capped at the max artifact byte size. Invalid parameters return 400 withcode: "invalid_export".Image and video artifacts store base64 media inline, render safe previews, and decode bytes through
/raw.Markdown artifacts render fenced code blocks with CodeMirror read-only syntax highlighting client-side (the server output stays plain escaped HTML, so no-JS contexts still work), render
mermaidfences through the same sandboxed Mermaid iframe as whole-document Mermaid artifacts (one lazily created iframe per diagram, capped byARTIFACTY_MAX_INLINE_DIAGRAMS, default 20; extra diagrams stay as escaped code), render task lists as disabled checkboxes, and wrap tables in a horizontally scrolling container. Inline HTML in Markdown is always escaped.Notebook (
.ipynb) artifacts store the original notebook JSON unchanged; the viewer renders cells in order (Markdown cells through the same embedded Markdown pipeline, code cells as highlighted escaped source with their execution count), with outputs limited totext/plain,text/markdown,image/png,image/jpeg,image/svg+xml(scriptless sandboxed iframe), andtext/html(sandboxed iframe); other output MIME types show a placeholder, outputs over 2 MB are replaced with a truncated notice, and rendering is bounded to the first 500 cells.Artifact content should still be treated as untrusted; use the raw view when handing content back to an agent.
npm releases are published with GitHub Actions OIDC Trusted Publishing after lint, test, and smoke checks pass.
See SECURITY.md, docs/threat-model.md, and docs/release-checklist.md before publishing or running a shared instance.
Environment Variables
Every ARTIFACTY_* variable Artifacty reads, most already covered in context above. node src/cli.js help prints the same list.
Variable | Purpose | Default |
| Storage directory |
|
| Public URL override; otherwise CLI/MCP read the last running server URL | — |
| Bind host for the HTTP server |
|
| Bind port for the HTTP server |
|
| Central MCP HTTP endpoint used by bridge mode | — |
|
|
|
| Set |
|
| Bridge-mode MCP HTTP request timeout |
|
| Shared token required for HTTP API and LAN/team sharing | — |
| Default UI locale (e.g. |
|
| openai-compatible embeddings endpoint base URL; enables semantic/hybrid search | — |
| Embeddings model name | provider default |
| API key for the openai-compatible provider; never logged or stored | — |
| Local command that reads JSON lines on stdin and writes vectors on stdout | — |
| Max content characters embedded per artifact |
|
| Timeout for the local embeddings command |
|
| Max embedding rows scored per semantic/hybrid search query |
|
| Set |
|
|
| — |
| Set | — |
| Set |
|
| Set |
|
| Background retention sweep interval |
|
| Set |
|
| Per-attempt webhook delivery timeout |
|
| Set |
|
| SSE poll interval for new events |
|
| Max in-memory event history retained for replay |
|
| Max concurrent SSE connections |
|
| Max concurrent |
|
| Max diff entries computed per |
|
| Max inline Mermaid diagrams rendered per Markdown artifact |
|
| Max comments retained per artifact |
|
| Max saved views retained per user |
|
|
| — |
| Rate limit for mutating routes |
|
| Rate limit for |
|
| Rate limit for |
|
This server cannot be deployed
Maintenance
Related MCP Connectors
- mcpOAuthio.artifacta
Artifact store for AI agents. Hosted OAuth at mcp.artifacta.io/mcp; local stdio via npm/PyPI.
Cross-agent artifact workspace with provenance across Claude Code, Codex, Cursor, LangGraph.
Shared, governed long-term memory for AI agents across tools and sessions via MCP and REST.
Artifact store for AI agents — read, write, and search files by path; share by rendered URL.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceMCP server for multi-agent collaboration enabling AI agents to communicate, delegate tasks, and share artifacts across clients and machines with federation support.21 npm1MIT
- AlicenseAqualityAmaintenanceThe artifact store for AI agents. Every output your agents produce — persisted, retrievable, shareable. Across runs, sessions, and tools. Session/agent metadata, content-hash dedup, and expiring share links; available on npm (@artifacta-mcp/mcp) and PyPI (artifacta-mcp).81MIT
- AlicenseAqualityAmaintenanceA local MCP-controlled artifact shelf for agents to publish generated content with stable preview URLs. Provides a shared SQLite registry and HTTP gallery for human browsing.311 npm1MIT
- AlicenseNot gradedqualityAmaintenanceThis MCP server enables AI agents to manage artifacts across sessions by providing tools for searching, retrieving, and registering entries in a persistent ledger, ensuring consistency and traceability of agent outputs.MIT