MCP server

Connect an MCP host to Artefaktum's hosted server and use its tools to find, upload and trust artifacts.

Connecting

Artefaktum runs one hosted MCP server over Streamable HTTP:

https://api.artefaktum.dev/mcp

No stdio entry point; a stdio-only host needs a bridge such as mcp-remote.

Claude Code:

claude mcp add --transport http artefaktum https://api.artefaktum.dev/mcp

Cursor, Windsurf, Cline, or any host that reads an mcp.json:

{ "mcpServers": { "artefaktum": { "url": "https://api.artefaktum.dev/mcp" } } }

What happens on first use

The host opens a browser tab for an OAuth 2.1 sign-in handled by Clerk: sign in with Google or GitHub, approve the connection once, and the host gets an access token it attaches to every call after that. There is no key to paste. Sign-in provisions a tenant the first time it happens, keyed to your personal account or Clerk organisation, with a default project already in place.

Authentication

The endpoint accepts two bearer credentials, checked by the same resolver chain as REST:

  • OAuth 2.1 access token: issued by Clerk after the browser sign-in above. The default for hosts with an OAuth-capable MCP client.
  • API key: the same key used against the REST API, created in the console, sent as Authorization: Bearer <key>. Use it for hosts that can’t complete a browser flow, or for unattended runs: pass --header on claude mcp add, or add a header next to url in mcp.json if the host allows one.

Two OAuth caveats: Clerk’s tokens carry no audience (aud) claim, so audience checking is relaxed rather than strict; and Clerk doesn’t know Artefaktum’s scope names, so every OAuth caller gets a fixed grant of read, write and search, never delete or admin. An API key carries exactly the scopes it was minted with. Neither matters for normal use, only for auditing what an OAuth agent can reach.

Tools

Every tool is scoped to a project. Call project_list first if you don’t have a project_id yet. This matters most after OAuth sign-in, which binds you to a tenant, not a project. List-shaped tools cap at 25 items and truncate long text; file bytes never enter a tool response.

Tool Purpose
artifact_search Find artifacts by meaning, text, or structured filters.
artifact_get Read one artifact’s metadata, lifecycle state, trust signals and relations.
artifact_get_download_url Get a short-lived signed URL for an artifact’s bytes.
project_list List the projects this credential can use.
artifact_list List artifacts in a project, with filters and a cursor.
artifact_create_upload Reserve a new artifact and get a signed upload URL.
artifact_complete_upload Confirm the bytes were uploaded so the server can verify and ready it.
artifact_resolve Reuse a fresh artifact by external key, or claim the right to create it.
artifact_update_metadata Change an artifact’s title, description, tags, metadata or TTL.
artifact_create_version Start a new immutable version of an existing artifact.
artifact_add_relation State a lineage relation between two artifacts.
artifact_delete Request permanent deletion of an artifact.

Finding artifacts

artifact_search matches descriptors (title, description, tags, metadata), never file contents. mode is exact (filters only), text (full-text), semantic (pgvector) or hybrid (fuses both; the default). Scores aren’t comparable across modes. Superseded artifacts are hidden unless asked for.

Parameter Type Default
project_id UUID required
query string ""
mode string "hybrid"
tags_all string[] none
content_types string[] none
exclude_superseded boolean true
limit integer 20 (capped at 25)
{ "project_id": "3f5c...", "query": "q3 churn analysis", "mode": "hybrid", "tags_all": ["churn"], "limit": 10 }

artifact_get returns one artifact plus its relations (up to 25). Argument: { "artifact_id": "0199198a-8f21-7c9e-9b1a-4e2f6d1a0c33" }.

artifact_list filters a project’s artifacts with a cursor, not a free-text query.

Parameter Type Default
project_id UUID required
status string[] none
tag string none
content_type string none
external_key string none
limit integer 20 (capped at 25)
cursor string none
{ "project_id": "3f5c...", "tag": "churn", "limit": 20 }

project_list takes no arguments; it returns every project the credential can use.

Caching by name

artifact_resolve turns “fetch only if nobody already has” into one call: hit (a fresh artifact exists, use it), create (you hold the reservation: PUT to upload.url, then artifact_complete_upload), or pending (retry after retry_after_seconds).

Parameter Type Default
project_id UUID required
external_key string required
filename string required
content_type string required
size_bytes integer required
title string required
max_age_seconds integer none (any age counts as fresh)
run_id string none
{
  "project_id": "3f5c...",
  "external_key": "company-api/acme/2026-09-17",
  "max_age_seconds": 86400,
  "filename": "acme.json",
  "content_type": "application/json",
  "size_bytes": 5120,
  "title": "Acme enrichment"
}

Uploading

artifact_create_upload reserves a new artifact and returns a signed, short-lived upload URL. PUT the bytes to upload.url with the given headers, then call artifact_complete_upload.

Parameter Type Default
project_id UUID required
filename string required
content_type string required
size_bytes integer required
title string required
description string ""
tags string[] none
metadata object none
external_key string none
run_id string none
infer_lineage boolean true

Field sizes are limited; see Limits.

{
  "project_id": "3f5c...",
  "filename": "report.pdf",
  "content_type": "application/pdf",
  "size_bytes": 88213,
  "title": "Q3 churn analysis",
  "tags": ["churn", "q3"]
}

artifact_complete_upload confirms the upload. The server verifies the object and marks it ready; the response says processing until that finishes, usually within a second or two; re-read with artifact_get. Safe to call more than once.

{ "artifact_id": "0199198a-...", "version_id": "0199198b-...", "sha256": "e3b0c4..." }

artifact_create_version starts a new immutable version and returns another signed upload URL. On completion the server writes a supersedes relation to the previous version.

Parameter Type Default
artifact_id UUID required
filename string required
content_type string required
size_bytes integer required
run_id string none
infer_lineage boolean true

Downloading

artifact_get_download_url returns a short-lived signed URL; fetch it yourself. Records a usage event.

Parameter Type Default
artifact_id UUID required
version_id UUID none (latest version)
run_id string none

Argument: { "artifact_id": "0199198a-8f21-7c9e-9b1a-4e2f6d1a0c33" }.

Lineage and lifecycle

artifact_add_relation states that one artifact relates to another: derived_from, supersedes, attachment_of, generated_by or related_to. Explicit relations replace inferred ones.

Parameter Type Default
artifact_id UUID required
to_artifact_id UUID required
relation_type string required
to_version_id UUID none
metadata object none
{ "artifact_id": "out-id", "to_artifact_id": "in-id", "relation_type": "derived_from" }

artifact_update_metadata changes title, description, tags, metadata or TTL. Bytes are immutable; use artifact_create_version for new content. Re-embeds when a searchable field changes.

Parameter Type Default
artifact_id UUID required
title string none
description string none
tags string[] none
metadata object none
expires_at string (date-time) none
clear_expires_at boolean false

artifact_delete moves the artifact to deleting; a worker removes the bytes. Not reversible. Argument: { "artifact_id": "0199198a-8f21-7c9e-9b1a-4e2f6d1a0c33" }.

The artifact resource

artefactai://artifacts/{artifact_id} is a pointer, not a data endpoint. A resource read carries no principal to authorize against, so it returns only the id you gave it plus a nudge to call artifact_get or artifact_get_download_url instead.

Typical agent flow

A research agent hands a report to a writing agent, neither knowing where the other runs.

  1. Research agent calls artifact_create_upload, then PUTs the bytes to upload.url.
  2. Research agent calls artifact_complete_upload, then polls artifact_get until status is ready.
  3. Writing agent, on a separate host, calls artifact_search (hybrid, same project_id) and picks a hit with superseded: false and stale_upstream: false.
  4. Writing agent calls artifact_get_download_url and fetches the bytes itself.
  5. Writing agent uploads its own output tagged with the same run_id; since it downloaded the first artifact in that run before uploading, the server infers a derived_from edge on its own, or either agent can call artifact_add_relation to state it explicitly.

This page as Markdown: /docs/mcp.md