EN | DE

Endpoint reference

Deutsch · English

Last updated: 2026-05-22 (D-054: list_learnings tool added; providerType updated to include learning-retrieval)

All externally reachable HTTP endpoints of Geef.Atelier — MCP, OAuth 2.1 and the web-UI/account endpoints. Base URL: https://geef.stefan-bechtel.de.


MCP endpoint

Endpoint Method Auth
/mcp POST Bearer token (static or OAuth)

The actual MCP endpoint. Clients send their JSON-RPC requests (tool calls) here. Supports the Streamable-HTTP transport (Stateless=true).

Auth options:

  • Claude Code CLI: Authorization: Bearer <ATELIER_MCP_TOKEN> (static token from .env)
  • Claude Desktop / Claude.ai: OAuth 2.1 access token (bearer), issued after the OAuth flow described below

If no token or an invalid token is sent, the server responds with 401 Unauthorized and the header:

WWW-Authenticate: Bearer resource_metadata="https://geef.stefan-bechtel.de/.well-known/oauth-protected-resource"

Through this, OAuth-capable clients discover the authorization server automatically.

MCP tools

The MCP server exposes the following tools callable via the /mcp endpoint. Run-related tools enforce the same run-user isolation as the web UI (D-042): non-admin users can only access their own runs.

list_run_artifacts

Lists all artifacts produced by a run.

Input:

{ "run_id": "<uuid>" }

Output: Array of artifact objects:

[
  {
    "artifact_id": "<uuid>",
    "finalizer_profile_name": "<string>",
    "artifact_type": "File|Url|Status",
    "filename": "<string|null>",
    "content_type": "<string|null>",
    "size_bytes": 12345,
    "storage_uri": "<string>",
    "status_message": "<string|null>",
    "created_at": "<ISO8601>"
  }
]

Returns an empty array if the run has no artifacts. Auth: owner check (same run isolation as other run tools).


list_grounding_provider_profiles

Lists all grounding-provider profiles (system + custom).

Input: { "includeSystem": bool (default true) }

Output: Array of grounding-provider profile objects:

[
  {
    "name": "tavily-refined",
    "displayName": "Tavily Refined",
    "description": "...",
    "providerType": "tavily",
    "maxQueriesPerRun": 3,
    "isSystem": true,
    "refinementEnabled": true,
    "refinementMode": "filter"
  },
  {
    "name": "tavily-news",
    "displayName": "Tavily News",
    "description": "...",
    "providerType": "news-search",
    "maxQueriesPerRun": 1,
    "isSystem": true,
    "refinementEnabled": true,
    "refinementMode": "filter"
  },
  {
    "name": "custom-my-provider",
    "displayName": "My Provider",
    "description": "...",
    "providerType": "vector-store",
    "maxQueriesPerRun": null,
    "isSystem": false,
    "refinementEnabled": false,
    "refinementMode": null
  }
]
Field Type Description
name string Profile identifier
displayName string Human-readable name
description string Purpose description
providerType string "tavily", "vector-store", "static-context", "url-fetch", "news-search", "academic-search", "rest-api", or "learning-retrieval"
maxQueriesPerRun int? Max queries per run (null = unlimited)
isSystem boolean Built-in system profile
refinementEnabled boolean Whether AI refinement is configured
refinementMode string | null "filter" or "synthesize" (null when not enabled)

download_run_artifact

Downloads the binary content of a File-type artifact, returned as Base64.

Input:

{ "run_id": "<uuid>", "artifact_id": "<uuid>" }

Output:

{
  "artifact_id": "<uuid>",
  "filename": "<string>",
  "content_type": "<string>",
  "size_bytes": 12345,
  "content_base64": "<base64-encoded file content>"
}

Only works for ArtifactType.File artifacts. Reads the file from the ExportPath on disk and returns the raw bytes as Base64. Auth: owner check.


list_learnings

Lists learning entries from the learning store (D-054).

Input:

{
  "status_filter": "Proposed|Approved|Rejected",
  "domain_filter": "<string>"
}

Both parameters are optional. Omit status_filter to return all statuses; omit domain_filter to return all domains.

Output: Array of learning entry objects:

[
  {
    "id": "<uuid>",
    "text": "<string, truncated to 300 chars>",
    "domain": "<string>",
    "status": "Proposed|Approved|Rejected",
    "source_run_id": "<uuid>",
    "learning_run_id": "<uuid|null>",
    "owner_username": "<string>",
    "created_at": "<ISO8601>",
    "approved_at": "<ISO8601|null>"
  }
]
Field Type Description
id uuid Learning entry identifier
text string The condensed learning text, truncated to 300 characters
domain string Template name of the source run (used for domain-boost retrieval)
status string "Proposed", "Approved", or "Rejected"
source_run_id uuid The standard run that triggered the extraction
learning_run_id uuid | null The learning-evaluation run that processed this entry (null while Proposed)
owner_username string Inherited from the source run's creating user
created_at ISO8601 Extraction timestamp
approved_at ISO8601 | null Approval timestamp (null while Proposed or Rejected)

Note: structured_facts_json is intentionally not exposed — the raw fact JSON is too verbose for MCP context and is only accessible via the web UI. Auth: standard run-user isolation; non-admin users see only their own entries.


OAuth endpoints

Discovery

GET /.well-known/oauth-authorization-server

Auth: None
RFC: 8414

Returns the server metadata as JSON. Clients use this endpoint to automatically discover all other OAuth endpoints.

{
  "issuer": "https://geef.stefan-bechtel.de",
  "authorization_endpoint": "https://geef.stefan-bechtel.de/oauth/authorize",
  "token_endpoint": "https://geef.stefan-bechtel.de/oauth/token",
  "registration_endpoint": "https://geef.stefan-bechtel.de/oauth/register",
  "revocation_endpoint": "https://geef.stefan-bechtel.de/oauth/revoke",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "code_challenge_methods_supported": ["S256"],
  "token_endpoint_auth_methods_supported": ["none"],
  "scopes_supported": ["mcp:full"]
}

GET /.well-known/oauth-protected-resource

Auth: None
RFC: Draft (MCP resource metadata)

Returns metadata about the protected resource (the MCP server).

{
  "resource": "https://geef.stefan-bechtel.de/mcp",
  "authorization_servers": ["https://geef.stefan-bechtel.de"],
  "bearer_methods_supported": ["header"],
  "scopes_supported": ["mcp:full"]
}

Client registration

POST /oauth/register

Auth: None (or an optional Authorization: Bearer <REGISTRATION_TOKEN> if configured)
RFC: 7591 — Dynamic Client Registration

Registers a new OAuth client. Typically called automatically by the MCP client.

Request (JSON):

{
  "client_name": "My Client",
  "redirect_uris": ["https://example.com/callback"],
  "client_id": "my-client-id",
  "logo_uri": null,
  "client_uri": null
}

client_id is optional — if omitted, the server generates a UUID. client_name and redirect_uris are mandatory fields.

Response (201):

{
  "client_id": "my-client-id",
  "client_id_issued_at": 1747390000,
  "redirect_uris": ["https://example.com/callback"],
  "client_name": "My Client",
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}

Authorization-code flow

GET /oauth/authorize

Auth: Cookie (Geef.Atelier login — redirected to /login if there is no session)

Starts the authorization-code flow. Shows the logged-in user the consent page with the client name and the requested permission.

Mandatory query parameters:

Parameter Description
response_type Must be code
client_id Registered client ID
redirect_uri Must exactly match a registered URI
code_challenge PKCE challenge (Base64Url-encoded SHA-256 hash of the verifier)
code_challenge_method Must be S256 (plain is rejected)

Optional parameters: scope, state

Example URL as Claude Desktop calls it:

https://geef.stefan-bechtel.de/oauth/authorize
  ?response_type=code
  &client_id=claude-ai
  &redirect_uri=https%3A%2F%2Fclaude.ai%2Fapi%2Fmcp%2Fauth_callback
  &code_challenge=Oaa_K782ehJ6ZNf-INVXFk1mEKtzQz7xOERSXZUiGXA
  &code_challenge_method=S256
  &state=<random-state>
  &scope=mcp%3Afull

The GET /oauth/authorize page is a server-rendered Blazor consent page ([Authorize] cookie). The approve/deny decision is sent via form POST to /oauth/consent (see below) — not to /oauth/authorize itself.


POST /oauth/consent

Auth: Cookie (Geef.Atelier login) + anti-forgery token
Content-Type: application/x-www-form-urlencoded

Submit target of the consent page. Processes the user's approval/denial, on approval creates the authorization code and performs the redirect.

On approval: redirect to redirect_uri?code=<auth_code>&state=<state>
On denial: redirect to redirect_uri?error=access_denied&state=<state>
On an invalid request: error page (no redirect — protects against open redirect)


Token endpoint

POST /oauth/token

Auth: None (public clients — authentication via PKCE instead of a client secret)
Content-Type: application/x-www-form-urlencoded

Exchanges an authorization code for tokens or renews via a refresh token.

Grant: authorization_code

grant_type=authorization_code
&code=<auth_code>
&client_id=<client_id>
&redirect_uri=<redirect_uri>
&code_verifier=<pkce_verifier>

Grant: refresh_token

grant_type=refresh_token
&refresh_token=<refresh_token>
&client_id=<client_id>

Response (200):

{
  "access_token": "<opaque_token>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "<opaque_token>",
  "scope": "mcp:full"
}

Response headers: Cache-Control: no-store, Pragma: no-cache (RFC 6749 §5.1).

Error behaviour:

  • Invalid or consumed code → 400 invalid_grant
  • Refresh token already used → 400 invalid_grant + immediate revocation of all of the user's tokens (theft detection per RFC 6819)

Revocation

POST /oauth/revoke

Auth: None
Content-Type: application/x-www-form-urlencoded
RFC: 7009

Revokes an access token or refresh token. Always returns 200 OK (even if the token is unknown).

token=<token>
&client_id=<client_id>

Web-UI & account endpoints

The web interface is Blazor Server (cookie auth). A selection of the relevant, externally reachable routes:

Endpoint Method Auth Purpose
/ GET Cookie Home page / Atelier overview
/health GET None Health check (Healthy) — for the reverse proxy/container lifecycle
/login GET/POST None Login page (static SSR)
/auth/logout POST Cookie Logout (anti-forgery, Minimal API)
/settings/theme POST None Theme-switch fallback (no-JS), redirect to the referer
/hubs/runs WS SignalR hub for live run updates
/admin/users GET Cookie (admin) User management
/admin/oauth-clients GET Cookie (admin) OAuth client management
/account/connected-clients GET Cookie Self-service for one's own connected OAuth clients
/crew, /crew/templates, /crew/profiles/*, /crew/studio, /crew/knowledge GET Cookie Crew/template/profile/Studio/knowledge-base management
/runs, /runs/{id}, /new GET Cookie Run list, run detail, new job

Run-related pages are subject to run-user isolation (D-042): each user sees only their own runs; the admin can see system-wide via an explicit toggle.


Artifact download endpoint

`GET /runs/

Auth: .RequireAuthorization() — requires an active session (cookie or bearer token)

Downloads an artifact file that was produced by a finalizer during a run. Only artifacts of type File can be downloaded; Url and Status artifacts return 404.

Owner check: Non-admin users may only download artifacts belonging to their own runs. The check is performed via IRunService.GetRunAsync with the requesting username. Admin users bypass the owner check.

Security note: A path containment guard (Path.GetFullPath comparison) prevents directory traversal attacks on the server-side file path.

Success response: 200 OK — file stream with Content-Disposition: attachment (filename from the artifact record).

Error responses:

Status Condition
401 Unauthorized Not authenticated
403 Forbidden Authenticated but not the run owner (and not admin)
404 Not Found runId or artifactId not found
404 Not Found Artifact exists but is type Url or Status (not File)
404 Not Found File not found on disk

Transform-Finalizer LLM-Binding

Custom Transform-Finalizer profiles can configure the LLM that powers the transformation. When creating or editing a finalizer profile via materialize_template_proposal or the UI, the settings object for Transform-type finalizers may include these optional keys:

Key Type Description
Provider string Provider name (e.g., "openrouter", "claude-cli"). Must match an active provider.
Model string Model identifier (e.g., "openai/gpt-4o-mini", "anthropic/claude-opus-4.7").
MaxTokens string Maximum output tokens. Must be ≥ 10000.
SystemPrompt string The transformation instruction (e.g., "Rewrite this in a formal tone").
Temperature string (optional) Temperature for sampling (0.0 to 2.0). If omitted, the provider default is used.

System Transform-Finalizer profiles are read-only; clone them as a custom profile to override LLM bindings.


Token design

Property Value
Format Opaque — 32 bytes of random data, Base64Url-encoded
Storage Only the SHA-256 hash in the database
Generation RandomNumberGenerator.GetBytes(32)
Comparison CryptographicOperations.FixedTimeEquals
Access-token lifetime 1 hour
Refresh-token lifetime 30 days, rotated on every refresh
Scope Only mcp:full (full access to the MCP server)

Full flow (summary)

Client                          Geef.Atelier                    Browser/user
  │                                 │                                 │
  │── GET /.well-known/oauth-... ──>│                                 │
  │<── server metadata ─────────────│                                 │
  │                                 │                                 │
  │── POST /oauth/register ────────>│                                 │
  │<── client_id ───────────────────│                                 │
  │                                 │                                 │
  │── open browser with GET ────────────────────────────────────────>│
  │   /oauth/authorize?...          │                                 │
  │                                 │<── login (if needed) ───────────│
  │                                 │<── consent "grant access" ──────│
  │                                 │── redirect ?code=... ──────────>│
  │<── code (via redirect_uri) ─────────────────────────────────────│
  │                                 │                                 │
  │── POST /oauth/token ───────────>│                                 │
  │   (code + code_verifier)        │                                 │
  │<── access_token + refresh_token─│                                 │
  │                                 │                                 │
  │── POST /mcp ───────────────────>│                                 │
  │   Authorization: Bearer <token> │                                 │
  │<── tool response ───────────────│                                 │

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.