Norwegian shop for 3D-printed interior products, with preliminary prices for custom 3D prints.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Use this to get a Formheim price for, or to order, a print of a specific file: a customer's own file or a model you created yourself (STL, OBJ, 3MF; multi-colour is supported). You do not need to send the file yourself: give the customer the file (as a download if you made it) and the upload_url, and they analyse and upload it in their own browser. Preferred way to start a print-offer request. Creates a request and returns upload_url for the customer: they open it, choose their files, adjust settings and upload, in their own browser. No file access or network access is needed on your side. Returns request_id and request_key (keep both private). Pass an existing request_id and request_key to get a new link for a request you already prepared, for example after your own upload failed. After the customer says they have uploaded, poll get_print_request_status; its analysis field is what you pass to get_print_quote and submit_to_offer.
This tool isn't marked read-only, so it may change something on the server or in an account you connected.
Pick your client. Values in capitals, like YOUR_TOKEN, are yours to fill in.
Run in a terminal
claude mcp add --transport http mcp https://api.formheim.no/mcp
Use this to get a Formheim price for, or to order, a print of a specific file: a customer's own file or a model you created yourself (STL, OBJ, 3MF; multi-colour is supported). You do not need to send the file yourself: give the customer the file (as a download if you made it) and the upload_url, and they analyse and upload it in their own browser. Preferred way to start a print-offer request. Creates a request and returns upload_url for the customer: they open it, choose their files, adjust settings and upload, in their own browser. No file access or network access is needed on your side. Returns request_id and request_key (keep both private). Pass an existing request_id and request_key to get a new link for a request you already prepared, for example after your own upload failed. After the customer says they have uploaded, poll get_print_request_status; its analysis field is what you pass to get_print_quote and submit_to_offer.
| Argument | Type | Description |
|---|---|---|
| request_id | string | |
| request_key | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"request_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"request_key": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{43}$"
}
},
"additionalProperties": false
}Calculate a preliminary price from a file analysis. Pass only the analysis, exactly as received; do not round or edit its numbers and do not add other fields. customer_total_nok already includes shipping (shipping_estimate_nok) and VAT, and the minimum price applies to that total, so never add shipping on top. It does not create an order. The customer orders at https://www.formheim.no/print, and never at https://www.formheim.no/print/agent?mode=analysis, which only analyses files.
| Argument | Type | Description |
|---|---|---|
| analysis* | object |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"analysis"
],
"properties": {
"analysis": {
"type": "object",
"required": [
"version",
"files",
"rush"
],
"properties": {
"rush": {
"enum": [
"standard",
"fast",
"express"
],
"type": "string"
},
"files": {
"type": "array",
"items": {
"type": "object",
"required": [
"file_id",
"sha256",
"format",
"size_bytes",
"unit_scale",
"quantity",
"settings",
"plates"
],
"properties": {
"format": {
"enum": [
"stl",
"obj",
"3mf"
],
"type": "string"
},
"plates": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"source_index",
"selected",
"mesh"
],
"properties": {
"id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{1,64}$"
},
"mesh": {
"type": "object",
"required": [
"triangle_count",
"volume_mm3",
"surface_area_mm2",
"dimensions_mm",
"triangle_density",
"overhang_percentage",
"thin_wall_percentage"
],
"properties": {
"volume_mm3": {
"type": "number",
"minimum": 0
},
"dimensions_mm": {
"type": "array",
"items": false,
"maxItems": 3,
"minItems": 3,
"prefixItems": [
{
"type": "number",
"minimum": 0
},
{
"type": "number",
"minimum": 0
},
{
"type": "number",
"minimum": 0
}
]
},
"triangle_count": {
"type": "integer",
"maximum": 2000000,
"minimum": 0
},
"surface_area_mm2": {
"type": "number",
"minimum": 0
},
"triangle_density": {
"type": "number",
"minimum": 0
},
"horizontal_area_mm2": {
"type": "number",
"minimum": 0
},
"overhang_percentage": {
"type": "number",
"maximum": 100,
"minimum": 0
},
"thin_wall_percentage": {
"type": "number",
"maximum": 100,
"minimum": 0
}
},
"additionalProperties": false
},
"selected": {
"type": "boolean"
},
"source_index": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0
}
},
"additionalProperties": false
},
"minItems": 1
},
"sha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"file_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{1,64}$"
},
"quantity": {
"type": "integer",
"maximum": 100,
"minimum": 1
},
"settings": {
"type": "object",
"required": [
"material",
"num_colors",
"color",
"quality",
"strength",
"finish"
],
"properties": {
"color": {
"anyOf": [
{
"type": "object",
"required": [
"filament_type_id",
"name",
"hex"
],
"properties": {
"hex": {
"type": "string",
"pattern": "^#[0-9a-fA-F]{6}$"
},
"name": {
"type": "string",
"maxLength": 255,
"minLength": 1
},
"filament_type_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"finish": {
"enum": [
"none",
"basic",
"smooth"
],
"type": "string"
},
"quality": {
"enum": [
"draft",
"standard",
"fine",
"ultra"
],
"type": "string"
},
"material": {
"enum": [
"pla",
"petg",
"petg-cf",
"abs",
"tpu",
"asa"
],
"type": "string"
},
"strength": {
"enum": [
"light",
"normal",
"strong"
],
"type": "string"
},
"num_colors": {
"type": "integer",
"maximum": 5,
"minimum": 1
}
},
"additionalProperties": false
},
"size_bytes": {
"type": "integer",
"maximum": 52428800,
"minimum": 1
},
"unit_scale": {
"anyOf": [
{
"type": "number",
"const": 1
},
{
"type": "number",
"const": 10
},
{
"type": "number",
"const": 25.4
},
{
"type": "number",
"const": 1000
}
]
}
},
"additionalProperties": false
},
"maxItems": 10,
"minItems": 1
},
"version": {
"type": "string",
"const": "1"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}Check a print request: how many of its files are uploaded and, once files are registered, the analysis of them (pass it unchanged to the quote and offer tools). Needs the request_id and request_key you received when the request was created. Read-only.
| Argument | Type | Description |
|---|---|---|
| request_id* | string | |
| request_key* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"request_id",
"request_key"
],
"properties": {
"request_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"request_key": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{43}$"
}
},
"additionalProperties": false
}Use this whenever someone asks what it costs, or whether it is possible, to have a 3D model printed by Formheim (STL, OBJ or 3MF, single or multi-colour), including a model you generated yourself. Returns prices, materials, colours, limits (files, size, build volume) and notes. Read-only. Do not calculate a price for the customer from these published prices or from your own slicer estimate: the price for a specific file comes from create_upload_link then get_print_quote. The customer can analyse files locally at https://www.formheim.no/print/agent?mode=analysis to get text to paste into the chat for a quote. That page does not place orders; the customer orders at https://www.formheim.no/print.
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {},
"additionalProperties": false
}Get one product with variants, customization options and care instructions. Read-only.
| Argument | Type | Description |
|---|---|---|
| id | string | |
| slug | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"slug": {
"type": "string",
"maxLength": 120,
"minLength": 1
}
},
"additionalProperties": false
}Search and list Formheim's catalog products. Read-only.
| Argument | Type | Description |
|---|---|---|
| sort | string | |
| limit | integer | |
| query | string | |
| cursor | string | |
| category | string | |
| collection | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"sort": {
"enum": [
"name",
"price-asc",
"price-desc"
],
"type": "string",
"default": "name"
},
"limit": {
"type": "integer",
"default": 20,
"maximum": 50,
"minimum": 1
},
"query": {
"type": "string",
"maxLength": 200
},
"cursor": {
"type": "string",
"maxLength": 512,
"minLength": 1
},
"category": {
"type": "string",
"maxLength": 120,
"minLength": 1
},
"collection": {
"type": "string",
"maxLength": 120,
"minLength": 1
}
},
"additionalProperties": false
}Check the status using the quote_status_key you received. Before confirmation it can report Avventer bekreftelse; after confirmation it requires the customer to have allowed status sharing. Read-only. status is one of: Avventer bekreftelse (customer has not confirmed yet), Mottatt (request received: a request for an offer, NOT an order), Under behandling, Avventer pris (Formheim is working on it), Klar til godkjenning (offer sent, waiting for the customer's approval), Estimat godkjent, Til vurdering (customer must review models and final price), Korrigering ønsket, Ny versjon på vei, Godkjent (approved, order not created yet), Avslått (declined), Bestilt (the only status where an order exists; comes with order_number). meaning and next_step explain the status in Norwegian: relay them to the customer in your own words and do not claim more than they say. Call it an order only when order_placed is true; payment, production and delivery are never shown here. On ACCESS_DENIED, stop polling and ask the customer to use the Formheim page or contact Formheim; do not call submit_to_offer to recover status access.
| Argument | Type | Description |
|---|---|---|
| quote_status_key* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"quote_status_key"
],
"properties": {
"quote_status_key": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{43}$"
}
},
"additionalProperties": false
}Alternative direct-upload flow, only when you have file access and can reach the upload host: register files and get one signed PUT upload URL per file. Keep request_id and request_key private and never show them to the customer. Upload each file with HTTP PUT to its URL, then call submit_to_offer. If an upload fails with 403 or a network error, stop and call create_upload_link with the request_id and request_key you already hold so the customer can upload in their browser; do not retry registration with the same idempotency_key. If the linked flow is unavailable, the customer can use https://www.formheim.no/print.
| Argument | Type | Description |
|---|---|---|
| files* | array | |
| request_id | string | |
| request_key | string | |
| idempotency_key* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"idempotency_key",
"files"
],
"properties": {
"files": {
"type": "array",
"items": {
"type": "object",
"required": [
"file_id",
"format",
"size_bytes",
"sha256",
"file_name"
],
"properties": {
"format": {
"enum": [
"stl",
"obj",
"3mf"
],
"type": "string"
},
"sha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"file_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{1,64}$"
},
"file_name": {
"type": "string",
"maxLength": 255,
"minLength": 1
},
"size_bytes": {
"type": "integer",
"maximum": 52428800,
"minimum": 1
}
},
"additionalProperties": false
},
"maxItems": 10,
"minItems": 1
},
"request_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"request_key": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{43}$"
},
"idempotency_key": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
},
"additionalProperties": false
}After all files are uploaded, prepare a request for an offer. Returns a confirmation_url the customer must open to review and confirm. Confirming sends a request for an offer, not an order. Give the customer the link; keep quote_status_key private. If the customer says the link was already opened, expired (a session lasts 15 minutes) or lost, call it again with the same request_id, request_key and analysis: it returns a fresh confirmation_url and a new quote_status_key, and links not yet opened stop working. Earlier quote_status_keys remain subject to the same sharing permission, expiry and revocation as the newest key; always use and keep the newest one. Re-issuance requires the same analysis on an unsubmitted, unexpired request; changed analysis gives CONFLICT and at most 10 confirmation links may be issued per request. Fails if files are missing, so never call it after a failed upload. Do not use re-issuance to recover denied status access.
| Argument | Type | Description |
|---|---|---|
| analysis* | object | |
| request_id* | string | |
| request_key* | string | |
| idempotency_key* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"request_id",
"request_key",
"idempotency_key",
"analysis"
],
"properties": {
"analysis": {
"type": "object",
"required": [
"version",
"files",
"rush"
],
"properties": {
"rush": {
"enum": [
"standard",
"fast",
"express"
],
"type": "string"
},
"files": {
"type": "array",
"items": {
"type": "object",
"required": [
"file_id",
"sha256",
"format",
"size_bytes",
"unit_scale",
"quantity",
"settings",
"plates"
],
"properties": {
"format": {
"enum": [
"stl",
"obj",
"3mf"
],
"type": "string"
},
"plates": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"source_index",
"selected",
"mesh"
],
"properties": {
"id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{1,64}$"
},
"mesh": {
"type": "object",
"required": [
"triangle_count",
"volume_mm3",
"surface_area_mm2",
"dimensions_mm",
"triangle_density",
"overhang_percentage",
"thin_wall_percentage"
],
"properties": {
"volume_mm3": {
"type": "number",
"minimum": 0
},
"dimensions_mm": {
"type": "array",
"items": false,
"maxItems": 3,
"minItems": 3,
"prefixItems": [
{
"type": "number",
"minimum": 0
},
{
"type": "number",
"minimum": 0
},
{
"type": "number",
"minimum": 0
}
]
},
"triangle_count": {
"type": "integer",
"maximum": 2000000,
"minimum": 0
},
"surface_area_mm2": {
"type": "number",
"minimum": 0
},
"triangle_density": {
"type": "number",
"minimum": 0
},
"horizontal_area_mm2": {
"type": "number",
"minimum": 0
},
"overhang_percentage": {
"type": "number",
"maximum": 100,
"minimum": 0
},
"thin_wall_percentage": {
"type": "number",
"maximum": 100,
"minimum": 0
}
},
"additionalProperties": false
},
"selected": {
"type": "boolean"
},
"source_index": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0
}
},
"additionalProperties": false
},
"minItems": 1
},
"sha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"file_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{1,64}$"
},
"quantity": {
"type": "integer",
"maximum": 100,
"minimum": 1
},
"settings": {
"type": "object",
"required": [
"material",
"num_colors",
"color",
"quality",
"strength",
"finish"
],
"properties": {
"color": {
"anyOf": [
{
"type": "object",
"required": [
"filament_type_id",
"name",
"hex"
],
"properties": {
"hex": {
"type": "string",
"pattern": "^#[0-9a-fA-F]{6}$"
},
"name": {
"type": "string",
"maxLength": 255,
"minLength": 1
},
"filament_type_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
},
"additionalProperties": false
},
{
"type": "null"
}
]
},
"finish": {
"enum": [
"none",
"basic",
"smooth"
],
"type": "string"
},
"quality": {
"enum": [
"draft",
"standard",
"fine",
"ultra"
],
"type": "string"
},
"material": {
"enum": [
"pla",
"petg",
"petg-cf",
"abs",
"tpu",
"asa"
],
"type": "string"
},
"strength": {
"enum": [
"light",
"normal",
"strong"
],
"type": "string"
},
"num_colors": {
"type": "integer",
"maximum": 5,
"minimum": 1
}
},
"additionalProperties": false
},
"size_bytes": {
"type": "integer",
"maximum": 52428800,
"minimum": 1
},
"unit_scale": {
"anyOf": [
{
"type": "number",
"const": 1
},
{
"type": "number",
"const": 10
},
{
"type": "number",
"const": 25.4
},
{
"type": "number",
"const": 1000
}
]
}
},
"additionalProperties": false
},
"maxItems": 10,
"minItems": 1
},
"version": {
"type": "string",
"const": "1"
}
},
"additionalProperties": false
},
"request_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"request_key": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{43}$"
},
"idempotency_key": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
},
"additionalProperties": false
}We read every tool's name, description and inputs the way the model will, looking for instructions hidden from you. A clean result means no warning signs, not a guarantee. Scanned Nov 15, 2026.